migrating from Azure Active Directory Graph Api to Microsoft Graph Api - azure-active-directory

I have an application I am signing in to using SSO office 365 to authenticate the user. I am also calling the azure active directory graph api to pull a list of all users in the organization. I want to stop using the azure active directory graph api (since it is being deprecated as of 2/2019) and move over to microsoft-graph api. If I use microsoft graph to pull users, will I also have to authenticate with diff way (not Azure)?
this is my current auth code in startup file:
public void ConfigureAuth(IAppBuilder app)
{
string strIssuers = ConfigurationManager.AppSettings["validIssuers"];
string[] validIssuers = strIssuers.Split(',');
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Tenant = ConfigurationManager.AppSettings["ida:Tenant"],
TokenValidationParameters = new TokenValidationParameters
{
ValidAudience = ConfigurationManager.AppSettings["ida:Audience"],
ValidIssuers = validIssuers
}
});
}
in graph call:
public async Task<List<User>> GetAdUsers(string tid, string path = "users")
{
var identity = HttpContext.Current.User.Identity as ClaimsIdentity;
string email = identity?.Name;
var selectvalues = "";//(path.ToLower() == "users" ? "$select=*" : "");
List<User> tmpUsers;
string skipToken;
string skipTokenResult;
int skipTokenIndex;
string strAuth = "https://login.microsoftonline.com/" + tid + "/oauth2/v2.0/token";
var client = ConfigurationManager.AppSettings["ida:Audience"];
var secret = ConfigurationManager.AppSettings["clientSecret"];
string clientId = client;
string clientSecret = secret;
Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationResult result = null;
AuthenticationContext _authContext = new AuthenticationContext(strAuth);
Microsoft.IdentityModel.Clients.ActiveDirectory.ClientCredential creds
= new Microsoft.IdentityModel.Clients.ActiveDirectory.ClientCredential(clientId, clientSecret);
result = await _authContext.AcquireTokenAsync("https://graph.microsoft.com", creds);
var _httpClient = new HttpClient();
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
HttpResponseMessage Res = await _httpClient.GetAsync("https://graph.microsoft.com/v1.0/" + path + "?$top=999" + selectvalues);
if (Res.IsSuccessStatusCode)
{
string strJson = Res.Content.ReadAsStringAsync().Result;
JavaScriptSerializer json = new JavaScriptSerializer();
RootObject rootObj = json.Deserialize<RootObject>(strJson);
List<User> adUsers = rootObj.Value;
var parseRes = JObject.Parse(strJson);
bool stop = false;
while (!stop)
{
try
{
skipTokenResult = parseRes["#odata.nextLink"].Value<string>();
skipTokenIndex = skipTokenResult.IndexOf("skiptoken=");
skipToken = skipTokenResult.Substring(skipTokenIndex + 10, skipTokenResult.Length - skipTokenIndex - 10);
Res = await _httpClient.GetAsync("https://graph.microsoft.com/v1.0/" + path + "?$top=999&$skiptoken=" + skipToken + selectvalues);
if (Res.IsSuccessStatusCode)
{
strJson = Res.Content.ReadAsStringAsync().Result;
rootObj = json.Deserialize<RootObject>(strJson);
tmpUsers = rootObj.Value;
adUsers.AddRange(tmpUsers);
parseRes = JObject.Parse(strJson);
}
else
{
stop = true;
}
}
catch (ArgumentNullException) // no skip token, stop looping !!!!
{
stop = true;
}
}
return adUsers;
}
else
{
// return null;
throw new Exception("GetAdUsers: Graph API failed for path: " + path + ", tid: " + tid + ". Reason: " + Res.ReasonPhrase);
}
}
//UPDATE: I was able to update the code to use SOAP Microsoft Graph API like this:
public GraphServiceClient AuthGraph(string tid, string groupId)
{
try
{
var clientId = ConfigurationManager.AppSettings["ida:Audience"];
var clientSecret = ConfigurationManager.AppSettings["ida:clientSecret"];
var tenantID = tid;
IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
.Create(clientId)
//.WithRedirectUri(redirectUri)
.WithTenantId(tenantID)
.WithClientSecret(clientSecret)
.Build();
ClientCredentialProvider authProvider = new ClientCredentialProvider(confidentialClientApplication);
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
return graphClient;
}
catch (Exception e)
{
throw e;
}
}
public async Task<List<User>> GetAdUsers(string tid, string groupId)
{
try
{
GraphServiceClient graphClient = AuthGraph(tid, groupId);
var graphUsers = await graphClient.Users
.Request()
.GetAsync();
List<User> users = graphUsers.Select(x => new User
{
Id = x.Id,
BusinessPhones = x.BusinessPhones.ToArray(),
DisplayName = x.DisplayName,
GivenName = x.GivenName,
JobTitle = x.JobTitle,
Mail = x.Mail,
MobilePhone = x.MobilePhone,
OfficeLocation = x.OfficeLocation,
PreferredLanguage = x.PreferredLanguage,
Surname = x.Surname,
UserPrincipalName = x.UserPrincipalName
}
).ToList();
if (!string.IsNullOrEmpty(groupId))
{
var membersInGroups = await GetNonSSOUsers(Globals.mghsTid, groupId);
users.AddRange(membersInGroups);
}
return users;
}
catch(Exception ex)
{
_errService.LogError("UserController.Update", tid, ex.HResult, ex.ToString().Substring(0, Math.Min(ex.ToString().Length, Globals.maxErrDescLen)), "getAdUsersService", 1, DateTime.Now.ToString());
throw ex;
}
}
public async Task<List<User>> GetNonSSOUsers(string tid, string groupId)
{
try
{
GraphServiceClient graphClient = AuthGraph(tid, groupId);
var members = await graphClient.Groups[groupId].Members
.Request()
.GetAsync();
List<User> users = new List<User>();
//while (members.NextPageRequest != null && (members = await members.NextPageRequest.GetAsync()).Count > 0)
//{
foreach (var member in members)
{
if (member is Microsoft.Graph.User)
{
var user = (Microsoft.Graph.User)member;
users.Add(new User
{
Id = user.Id,
BusinessPhones = user.BusinessPhones.ToArray(),
DisplayName = user.DisplayName,
GivenName = user.GivenName,
JobTitle = user.JobTitle,
Mail = user.Mail,
MobilePhone = user.MobilePhone,
OfficeLocation = user.OfficeLocation,
PreferredLanguage = user.PreferredLanguage,
Surname = user.Surname,
UserPrincipalName = user.UserPrincipalName
});
}
}
// }
return users;
}
catch (Exception e)
{
throw e;
}
}

The Microsoft Graph API is also protected under Azure AD. So, basically, you just need to add and grant necessary Graph API permissions to your application registered in Azure AD.
After that, you can call the Microsoft Graph API by adding an authorization header.

Related

How to pass UserInfo from Google OAuth to Requesting Website in WPF CefSharp Browser

So here's the question. I have a browser that I'm developing using CefSharp, and I'm trying to get the browser to properly authenticate and pass the UserInfo back to the website that's requesting a login. Below is the button that they are clicking on the website:
As it is written now, I catch this login event in the PopUpHandler class which implements the ILifeSpanHandler interface. I catch it specifically in the OnBeforePopup method, and run the default OAuth class that I got off of the internet. I've modified the OAuth class a bit to implement a custom cefsharp browser pop-up to get the user to input their credentials.
public bool OnBeforePopup(IWebBrowser browserControl, IBrowser browser, IFrame frame, string targetUrl, string targetFrameName,
WindowOpenDisposition targetDisposition, bool userGesture, IPopupFeatures popupFeatures, IWindowInfo windowInfo,
IBrowserSettings browserSettings, ref bool noJavascriptAccess, out IWebBrowser newBrowser)
{
foreach (var allowedSite in Helper.AllowedPopups)
{
if (targetUrl.Contains(allowedSite))
{
if (targetUrl.Contains("login") && targetUrl.Contains("oauth"))
{
OAuth that = new OAuth();
that.AuthorizeWithGoogle();
newBrowser = null;
return true;
}
Helper.OpenNewTab(targetUrl, true);
newBrowser = null;
return true;
}
}
string askingURL = Helper.GetWebsiteName(targetUrl);
AllowPopUpNotifier allowThis = new AllowPopUpNotifier(askingURL);
if (allowThis.ShowDialog() == true)
{
Helper.AllowedPopups.Add(askingURL);
if (targetUrl.Contains("login") && targetUrl.Contains("oauth"))
{
var newPopUpWindow = new CustomPopUpWindow(targetUrl);
newPopUpWindow.Show();
newBrowser = null;
return true;
}
Helper.OpenNewTab(targetUrl, true);
newBrowser = null;
return true;
}
else
{
newBrowser = null;
return true;
}
}
Then, the OAuth class that I've implemented looks like this:
public class OAuth
{
//client configuration;
const string clientID = "581786658708-elflankerquo1a6vsckabbhn25hclla0.apps.googleusercontent.com";
const string clientSecret = "3f6NggMbPtrmIBpgx-MK2xXK";
//const string clientID = "1020802206989-hsvquqebqgrj8kj4b387lcnosos4manc.apps.googleusercontent.com";
//const string clientSecret = "5k_uICJwwq25oV6-BM22310V";
const string authorizationEndpoint = "https://accounts.google.com/o/oauth2/v2/auth";
const string tokenEndpoint = "https://www.googleapis.com/oauth2/v4/token";
const string userInfoEndpoint = "https://www.googleapis.com/oauth2/v3/userinfo";
//ref http://stackoverflow.com/a/3978040
public static int GetRandomUnusedPort()
{
var listener = new TcpListener(IPAddress.Loopback, 0);
listener.Start();
var port = ((IPEndPoint)listener.LocalEndpoint).Port;
listener.Stop();
return port;
}
public async Task<UserCredential> getUserCredential()
{
UserCredential credential;
string[] scopes = new string[] { }; // user basic profile
//Read client id and client secret from Web config file
credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
new ClientSecrets
{
ClientId = "1020802206989-hsvquqebqgrj8kj4b387lcnosos4manc.apps.googleusercontent.com",
ClientSecret = "3f6NggMbPtrmIBpgx-MK2xXK"
}, scopes,
"user", CancellationToken.None, new FileDataStore("Auth.Api.Store"));
return credential;
}
public async void AuthorizeWithGoogle()
{
//Generates state and PKCE values.
string state = randomDataBase64url(32);
string code_verifier = randomDataBase64url(32);
string code_challenge = base64urlencodeNoPadding(sha256(code_verifier));
const string code_challenge_method = "S256";
//Creates a redirect URI using an available port on the loopback address.
string redirectURI = string.Format("http://{0}:{1}/", IPAddress.Loopback, GetRandomUnusedPort());
Debug.WriteLine("redirect URI: " + redirectURI);
//Creates an HttpListener to listen for requests on that redirect URI.
var http = new HttpListener();
http.Prefixes.Add(redirectURI);
Debug.WriteLine("Listening..");
http.Start();
//Creates the OAuth 2.0 authorization request.
string authorizationRequest = string.Format("{0}?response_type=code&scope=openid%20profile&redirect_uri={1}&client_id={2}&state={3}&code_challenge={4}&code_challenge_method={5}",
authorizationEndpoint,
Uri.EscapeDataString(redirectURI),
clientID,
state,
code_challenge,
code_challenge_method);
//Opens request in the browser.
//Process.Start(authorizationRequest);
//var newBrowser = new WebBrowser();
var that = new CustomPopUpWindow(authorizationRequest);
//that.browser.Address = authorizationRequest;
//newBrowser.Source = new Uri(authorizationRequest, UriKind.RelativeOrAbsolute);
//newBrowser.Load(authorizationRequest);
//that.Content = newBrowser;
that.Show();
//Waits for the OAuth authorization response.
var context = await http.GetContextAsync();
// Brings this app back to the foreground.
var mainWindow = Helper.GetMainWindow();
Application.Current.Dispatcher.Invoke(() =>
{
mainWindow.Activate();
});
//Sends an HTTP response to the browser.
var response = context.Response;
string responseString = string.Format("<html><head><meta http-equiv='refresh' content='10;url=https://google.com'></head><body>Please return to the app.</body></html>");
var buffer = System.Text.Encoding.UTF8.GetBytes(responseString);
response.ContentLength64 = buffer.Length;
var responseOutput = response.OutputStream;
Task responseTask = responseOutput.WriteAsync(buffer, 0, buffer.Length).ContinueWith((task) =>
{
responseOutput.Close();
http.Stop();
Console.WriteLine("HTTP server stopped.");
});
//Checks for errors.
if (context.Request.QueryString.Get("error") != null)
{
Debug.WriteLine(String.Format("OAuth authorization error: {0}.", context.Request.QueryString.Get("error")));
Helper.GoogleAuthorized = false;
CustomMessageBox.Show("Google login not authorized. Check your login credentials and try again.");
return;
}
if (context.Request.QueryString.Get("code") == null
|| context.Request.QueryString.Get("state") == null)
{
Debug.WriteLine("Malformed authorization response. " + context.Request.QueryString);
Helper.GoogleAuthorized = false;
CustomMessageBox.Show("Google login not authorized. Check your login credentials and try again.");
return;
}
//extracts the code
var code = context.Request.QueryString.Get("code");
var incoming_state = context.Request.QueryString.Get("state");
//Compares the receieved state to the expected value, to ensure that
//this app made the request which resulted in authorization.
if (incoming_state != state)
{
Debug.WriteLine(String.Format("Received request with invalid state ({0})", incoming_state));
Helper.GoogleAuthorized = false;
CustomMessageBox.Show("Google login not authorized. Check your login credentials and try again.");
return;
}
Debug.WriteLine("Authorization code: " + code);
//Starts the code exchange at the Token Endpoint.
performCodeExchange(code, code_verifier, redirectURI);
}
async void performCodeExchange(string code, string code_verifier, string redirectURI)
{
Debug.WriteLine("Exchanging code for tokens...");
//builds the request
string tokenRequestURI = "https://www.googleapis.com/oauth2/v4/token";
string tokenRequestBody = string.Format("code={0}&redirect_uri={1}&client_id={2}&code_verifier={3}&client_secret={4}&scope=&grant_type=authorization_code",
code,
System.Uri.EscapeDataString(redirectURI),
clientID,
code_verifier,
clientSecret
);
//sends the request
HttpWebRequest tokenRequest = (HttpWebRequest)WebRequest.Create(tokenRequestURI);
tokenRequest.Method = "POST";
tokenRequest.ContentType = "application/x-www-form-urlencoded";
tokenRequest.Accept = "Accept=text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
byte[] _byteVersion = Encoding.ASCII.GetBytes(tokenRequestBody);
tokenRequest.ContentLength = _byteVersion.Length;
Stream stream = tokenRequest.GetRequestStream();
await stream.WriteAsync(_byteVersion, 0, _byteVersion.Length);
stream.Close();
try
{
//gets the response
WebResponse tokenResponse = await tokenRequest.GetResponseAsync();
using (StreamReader reader = new StreamReader(tokenResponse.GetResponseStream()))
{
//reads response body
string responseText = await reader.ReadToEndAsync();
Debug.WriteLine(responseText);
//converts to dictionary
Dictionary<string, string> tokenEndpointDecoded = JsonConvert.DeserializeObject<Dictionary<string, string>>(responseText);
string access_token = tokenEndpointDecoded["access_token"];
userinfoCall(access_token);
}
}
catch (WebException ex)
{
if (ex.Status == WebExceptionStatus.ProtocolError)
{
var response = ex.Response as HttpWebResponse;
if (response != null)
{
Debug.WriteLine("HTTP: " + response.StatusCode);
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
//reads response body
string responseText = await reader.ReadToEndAsync();
Debug.WriteLine(responseText);
Helper.GoogleAuthorized = false;
CustomMessageBox.Show("Google login not authorized. Check your login credentials and try again.");
return;
}
}
}
}
}
async void userinfoCall(string access_token)
{
Debug.WriteLine("Making API Call to Userinfo...");
//builds the request
string userinfoRequestURI = "https://www.googleapis.com/oauth2/v3/userinfo";
//sends the request
HttpWebRequest userinfoRequest = (HttpWebRequest)WebRequest.Create(userinfoRequestURI);
userinfoRequest.Method = "GET";
userinfoRequest.Headers.Add(string.Format("Authorization: Bearer {0}", access_token));
userinfoRequest.ContentType = "application/x-www-form-urlencoded";
userinfoRequest.Accept = "Accept=text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
//gets the response
WebResponse userinfoResponse = await userinfoRequest.GetResponseAsync();
using (StreamReader userinfoResponseReader = new StreamReader(userinfoResponse.GetResponseStream()))
{
//reads response body
string userinfoResponseText = await userinfoResponseReader.ReadToEndAsync();
Debug.WriteLine(userinfoResponseText);
}
}
/// <summary>
/// Appends the given string to the on-screen log, and the debug console.
/// </summary>
/// <param name = "output" > string to be appended</param>
public void output(string output)
{
//textBoxOutput.Text = textBoxOutput.Text + output + Environment.NewLine;
Console.WriteLine(output);
}
/// <summary>
/// Returns URI-safe data with a given input length.
/// </summary>
/// <param name = "length" > Input length(nb.output will be longer)</param>
/// <returns></returns>
public static string randomDataBase64url(uint length)
{
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
byte[] bytes = new byte[length];
rng.GetBytes(bytes);
return base64urlencodeNoPadding(bytes);
}
/// <summary>
/// Returns the SHA256 hash of the input string.
/// </summary>
/// <param name = "inputStirng" ></ param >
/// < returns ></ returns >
public static byte[] sha256(string inputStirng)
{
byte[] bytes = Encoding.ASCII.GetBytes(inputStirng);
SHA256Managed sha256 = new SHA256Managed();
return sha256.ComputeHash(bytes);
}
/// <summary>
/// Base64url no-padding encodes the given input buffer.
/// </summary>
/// <param name = "buffer" ></ param >
/// < returns ></ returns >
public static string base64urlencodeNoPadding(byte[] buffer)
{
string base64 = Convert.ToBase64String(buffer);
//Converts base64 to base64url.
base64 = base64.Replace("+", "-");
base64 = base64.Replace("/", "_");
//Strips padding.
base64 = base64.Replace("=", "");
return base64;
}
}
}
This gets me the UserInfo, and writes it on the "Debug.WriteLine(userinfoResponseText);" line in the OAuth class. However, I don't know how to redirect it back to the website that initially called the pop-up. Can anybody help? I'm so lost.

OIDCClient LogoutAsync throwing SemaphoreFullException

I'm connecting to Identity Server 4 from a WPF application and I am trying to perform a logout. However, I get a SemaphoreFullException in my WpfEmbeddedBrowser when calling _oidcClient.LogoutAsync(). Initally I thought maybe I shouldn't use the browser for logging out, but I don't think that is possible.
Here is the part calling the login and logout:
//prompt login
var options = new OidcClientOptions()
{
Authority = Current.Properties["IdentityServerAPIAddress"].ToString(),
ClientId = "wpf",
ClientSecret = "secret",
Scope = "openid offline_access WebAPI",
RedirectUri = "http://localhost/signin-oidc",
Flow = OidcClientOptions.AuthenticationFlow.AuthorizationCode,
Browser = new WpfEmbeddedBrowser()
};
_oidcClient = new OidcClient(options);
try
{
loginResult = await _oidcClient.LoginAsync();
logoutResult = await _oidcClient.LogoutAsync();
}
...
Here is the WpfEmbeddedBrowser:
private BrowserOptions _options = null;
public WpfEmbeddedBrowser()
{
}
public async Task<BrowserResult> InvokeAsync(BrowserOptions options, CancellationToken cancellationToken = default)
{
_options = options;
var window = new Window()
{
Width = 450,
Height = 750,
Title = "SiteMonitor Desktop Application Login"
};
var webBrowser = new WebBrowser();
var signal = new SemaphoreSlim(0, 1);
window.Show();
var result = new BrowserResult()
{
ResultType = BrowserResultType.UserCancel
};
webBrowser.Navigating += (s, e) =>
{
if (BrowserIsNavigatingToRedirectUri(e.Uri))
{
e.Cancel = true;
result = new BrowserResult()
{
ResultType = BrowserResultType.Success,
Response = e.Uri.AbsoluteUri
};
signal.Release();
window.Close();
}
};
window.Closing += (s, e) =>
{
signal.Release();
};
window.Content = webBrowser;
window.Show();
webBrowser.Source = new Uri(_options.StartUrl);
await signal.WaitAsync();
return result;
}
private bool BrowserIsNavigatingToRedirectUri(Uri uri)
{
return uri.AbsoluteUri.StartsWith(_options.EndUrl);
}
Set maxCout to 2 in SemaphoreSlim, it's wroks for me.
var signal = new SemaphoreSlim(0, 2);

IdentityServer4 All Client Scopes in Token

I have a multitenant app secure with an IdentityServer4 implementation. I recently updated it to the latest ID4 and the behavior seems to have changed. Previously, I could make a request with the TokenClient inside of the IdentityModel package:
var parameters = new Dictionary<string, string>();
parameters.Add("username", loginModel.UserName);
parameters.Add("password", loginModel.Password);
var tokenClient = new TokenClient(new Uri(new Uri(accountsConfig.EndpointUrl), "/connect/token").ToString(), accountsConfig.ClientId, accountsConfig.Secret, null, AuthenticationStyle.PostValues);
var tokenResponse = await tokenClient.RequestCustomGrantAsync("AgentLogin", extra: parameters);
It would return all of the scopes defined for the client in the token. That is no longer the case. How do I configure ID4 to do that without explicitly requesting them inside of the TokenClient?
public class AgentLoginCustomGrantValidator : IExtensionGrantValidator
{
private readonly ILogger<AgentLoginCustomGrantValidator> _logger;
private readonly IAdminUserService _adminUserService;
public AgentLoginCustomGrantValidator(ILogger<AgentLoginCustomGrantValidator> logger, IAdminUserService adminUserService)
{
_logger = logger;
_adminUserService = adminUserService;
}
public async Task ValidateAsync(ExtensionGrantValidationContext context)
{
try
{
var username = context.Request.Raw.Get("username");
var password = context.Request.Raw.Get("password");
var userId = _adminUserService.AuthenticateUser(username, password);
if (userId != null)
{
var agencyUser = _adminUserService.GetUser(userId.Value);
context.Result = new GrantValidationResult($"{userId}", GrantType, agencyUser.Roles.Select(x => new Claim(JwtClaimTypes.Role, x.Name)).Concat(new List<Claim>() { new Claim(JwtClaimTypes.Name, agencyUser.UserName) { } }));
}
else
{
_logger.LogWarning($"Bum creds: {username} ");
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidClient, "Invalid credentials");
}
}
catch (Exception ex)
{
_logger.LogError(ex.ToString());
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidClient, ex.Message);
}
}
public string GrantType => "AgentLogin";
}
Looks like Identity Server 4 by default only returns the requested identity or api resources for each client. However, this behaviour can be easily overridden to return all the scopes regardless whether they were requested in the token request or not. You can create a CustomClaimsService which inherits from the DefaultClaimsService.
public class CustomClaimsService : DefaultClaimsService
{
public CustomClaimsService(IProfileService profile, ILogger<DefaultClaimsService> logger) : base(profile, logger)
{
}
public override async Task<IEnumerable<Claim>> GetAccessTokenClaimsAsync(ClaimsPrincipal subject,
Resources resources, ValidatedRequest request)
{
var baseResult = await base.GetAccessTokenClaimsAsync(subject, resources, request);
var outputClaims = baseResult.ToList();
//If there are any allowed scope claims that are not yet in the output claims - add them
foreach (var allowedClientScope in request.Client.AllowedScopes)
{
if (!outputClaims.Any(x => x.Type == JwtClaimTypes.Scope && x.Value == allowedClientScope))
{
outputClaims.Add(new Claim(JwtClaimTypes.Scope, allowedClientScope));
}
}
return outputClaims;
}
}
Then just register it with the IdentityServerBuilder service container.
var builder = services.AddIdentityServer(options =>
{
//Your identity server options
});
//Register the custom claims service with the service container
builder.Services.AddTransient<IClaimsService, CustomClaimsService>();
Each access token will now contain all the scopes that the given client is allowed.

Can't get Novell.Directory.Ldap.NETStandard library to query

I need to let the user query an Active Directory for names in .Net Core.
So I am building an Active Directory Search Web API Service.
I am able to connect with the bind statement.
But I am not able to get any results back with my query although there is no error.
Another programmer sent me some code he uses in other applications. But it uses the DirectoryEntry object which is not available in .Net Core.
So I am trying to use the Novell.Directory.Ldap.NetStandard library.
Here is the code the other developer sent me:
public static List<UserProfileModel> GetADUsers(string alias)
{
List<UserProfileModel> users = new List<UserProfileModel>();
if (alias == null || alias.Trim().Equals(""))
{
return users;
}
try
{
// Ad path LDAP://ourOrg.gov/CN=Users,DC=ourOrg,DC=gov
DirectoryEntry de2 = new DirectoryEntry(ConfigurationManager.AppSettings["AD_Path"], ConfigurationManager.AppSettings["AD_User"], ConfigurationManager.AppSettings["AD_Password"]);
de2.Path = ConfigurationManager.AppSettings["AD_Path"];
de2.AuthenticationType = AuthenticationTypes.Secure;
DirectorySearcher deSearch = new DirectorySearcher();
deSearch.SearchRoot = de2;
deSearch.Filter = "(samaccountname=*" + alias + "*)";
LOGGER.Debug(String.Format("Active Directory Search Filter {0}", deSearch.Filter));
SearchResultCollection results = deSearch.FindAll();
String raw = "";
LOGGER.Debug(String.Format("Active Directory Search Result Counts {0}", results.Count));
if (results.Count > 0)
{
foreach (SearchResult item in results)
{
UserProfileModel userProfileModel = new UserProfileModel();
userProfileModel.Name = GetADProperty("name", item);
userProfileModel.email = GetADProperty("mail", item);
userProfileModel.identity = GetADProperty("userPrincipalName", item);
userProfileModel.first_name = GetADProperty("givenName", item);
userProfileModel.last_name = GetADProperty("sn", item);
users.Add(userProfileModel);
raw = String.Format("{0}/n{1}", raw, userProfileModel.ToString());
}
LOGGER.Debug(String.Format("Active Directory Search Resuts ToString: {0}", raw));
}
}
catch (Exception e)
{
LOGGER.Error("Unable to Query Active Directory", e);
}
return users;
}
I need to translate this into Novell's LDAP library.
Here is my attempt:
[HttpGet]
public async Task<List<UserProfileModel>> GetByName(string alias)
{
int ldapPort = LdapConnection.DEFAULT_PORT;
string ldapHost = "ourOrg.gov";
string loginDn = #"ourOrg\myName";
string password = "myPass";
List<UserProfileModel> users = new List<UserProfileModel>();
if (alias == null || alias.Trim().Equals(""))
{
return users;
}
try
{
using (var con = new LdapConnection())
{
con.Connect(ldapHost, ldapPort);
con.Bind(loginDn, password);
LdapSearchResults results = con.Search(
"cn=users,dc=ourOrg,dc=gov",
LdapConnection.SCOPE_ONE,
"samaccountname=*",
null,
false);
// NO RESULTS:(
}
return users;
}
catch(Exception ex)
{
throw ex;
}
}
I don't get an error.
But there are 0 results.
I originally had this part:
"samaccountname=*",
like:
"samaccountname={alias}",
but I'm just trying to get back results at this point.
I got this working:
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Hrsa.Core.Web.App.Models.ViewModels;
using Novell.Directory.Ldap;
// For more information on enabling Web API for empty projects, visit http://go.microsoft.com/fwlink/?LinkID=397860
namespace Hrsa.Core.Web.App.Controllers.Api
{
[Route("api/[controller]")]
public class ActiveDirectoryController : Controller
{
private readonly AppSettings _appSettings;
public ActiveDirectoryController(IOptions<AppSettings> appSettings)
{
_appSettings = appSettings.Value;
}
[HttpGet]
public async Task<List<UserProfileModel>> GetByName(string alias)
{
int ldapPort = LdapConnection.DEFAULT_PORT;
string ldapHost = _appSettings.HrsaLdapHost; // ourOrgName.gov
string loginDn = _appSettings.AdUser;
string password = _appSettings.AdPassword;
string searchBase = _appSettings.HrsaAdSearchBase;
string searchFilter = $"(samaccountname=*{alias}*)";
string[] attributes = new string[] { "cn", "userPrincipalName", "st", "givenname", "samaccountname",
"description", "telephonenumber", "department", "displayname", "name", "mail", "givenName", "sn" };
List<UserProfileModel> users = new List<UserProfileModel>();
if (alias == null || alias.Trim().Equals(""))
{
return users;
}
try
{
using (var con = new LdapConnection())
{
con.Connect(ldapHost, ldapPort);
con.Bind(loginDn, password);
LdapSearchQueue queue = con.Search(
searchBase,
LdapConnection.SCOPE_SUB,
searchFilter,
attributes,
false,
(LdapSearchQueue)null,
(LdapSearchConstraints)null);
LdapMessage message;
while ((message = queue.getResponse()) != null)
{
if (message is LdapSearchResult)
{
LdapEntry entry = ((LdapSearchResult)message).Entry;
LdapAttributeSet attributeSet = entry.getAttributeSet();
users.Add(new UserProfileModel
{
Cn = attributeSet.getAttribute("cn")?.StringValue,
UserPrincipalName = attributeSet.getAttribute("userPrincipalName")?.StringValue,
St = attributeSet.getAttribute("st")?.StringValue,
Givenname = attributeSet.getAttribute("givenname")?.StringValue,
Samaccountname = attributeSet.getAttribute("samaccountname")?.StringValue,
Description = attributeSet.getAttribute("description")?.StringValue,
Telephonenumber = attributeSet.getAttribute("telephonenumber")?.StringValue,
Department = attributeSet.getAttribute("department")?.StringValue,
Displayname = attributeSet.getAttribute("displayname")?.StringValue,
Name = attributeSet.getAttribute("name")?.StringValue,
Mail = attributeSet.getAttribute("mail")?.StringValue,
GivenName = attributeSet.getAttribute("givenName")?.StringValue,
Sn = attributeSet.getAttribute("sn")?.StringValue
});
}
}
}
return users;
}
catch(Exception ex)
{
throw ex;
}
}
}
}

Google OAuth2.0 - Windows phone 7

Experts,
I'm new to OAuth,
I'm writing an small App in windows Phone 7, I'm using OAuth2 for google contacts, I need to get the initial URL API (1) to get the Token
1) https://accounts.google.com/o/oauth2/auth?response_type=code&scope=https://www.google.com/m8/feeds/&redirect_uri=REDIRECT_URI&client_id=CLIENT_ID&hl=en-US&from_login=1&as=NEW_AS&pli=1
I got the success code, and when I'm trying to use this
https://www.google.com/m8/feeds/contacts/default/full?access_token=TOKEN_CODE
but I'm getting 401 error back,
Can you please advice, what mistake i'm going.
I've taken Twitter OAuth example as base, and doing modifications.
CODE
var uri = new Uri(url);
var request = BuildOAuthWebRequest(url, null);
MakeGetRequest(callback, request);
private static HttpWebRequest BuildOAuthWebRequest( string url, string realm)
{
var header = new StringBuilder();
var request = (HttpWebRequest)WebRequest.Create(url);
return request;
}
private static void MakeGetRequest(EventHandler<OAuthEventArgs> callback, HttpWebRequest request)
{
var asyncState = request.BeginGetResponse(new AsyncCallback((asyncRes) =>
{
HttpWebResponse response = null;
try
{
//request has returned
response = (HttpWebResponse)request.EndGetResponse(asyncRes);
if (response.StatusCode == HttpStatusCode.OK)
{
using (StreamReader sr = new StreamReader(response.GetResponseStream()))
{
var token = sr.ReadToEnd();
callback(null, new OAuthEventArgs() { Response = token });
}
}
}
catch (WebException we)
{
string t = new StreamReader(we.Response.GetResponseStream()).ReadToEnd();
callback(null, new OAuthEventArgs() { Error = we, ErrorMessage = t, IsError = true });
}
catch (Exception e)
{
callback(null, new OAuthEventArgs() { Error = e, ErrorMessage = e.Message, IsError = true });
}
finally
{
if (response != null)
response.Close();
}
}), null);
}

Resources