I am using Microsoft Azure Active Directory login for my MVC 5 application. Can somebody give me an idea how I can check if a username already exists in Microsoft Azure Active Directory?
What is the general approach to do this?
You can use the Graph API and query for the user you want. For information about Graph API read: http://msdn.microsoft.com/en-us/library/azure/hh974476.aspx
The common queries page (http://msdn.microsoft.com/en-us/library/azure/jj126255.aspx) has a query for a user given the userPrincipalName. You should use this query and check if it returns a 404 (not found).
Basically the query you are looking for is: "https://graph.windows.net/contoso.com/users/admin#contoso.com?api-version=2013-04-05" where you need to replace contoso.com with your domain and admin#contoso.com with the upn you want to search for.
You should also look at the Azure AD samples on GitHub. In this case, you're probably interested in how to use the Graph API: https://github.com/AzureADSamples/WebApp-GraphAPI-DotNet
This sample MVC web application demonstrates how to query Azure Active Directory using the Graph API. To facilitate application development, it includes showing how to use included Graph Library, and uses OpenID Connect to authorize users to conenct to their directory data.
An alternative solution :
public async Task<User> FindByAlias(string alias, GraphServiceClient graphClient)
{
List<QueryOption> queryOptions = new List<QueryOption>
{
new QueryOption("$filter", $#"mailNickname eq '{alias}'")
};
var userResult = await graphClient.Users.Request(queryOptions).GetAsync();
if (userResult.Count != 1) throw new ApplicationException($"Unable to find a user with the alias {alias}");
return userResult[0];
}
Related
I have been trying to implement a solution for this for days. It's my first experiment with Microsoft Graph. I had our network admin register the app and went through the quick start code in console-app-quickstart.
I looked at active-directory-dotnetcore-daemon-v2 and active-directory-dotnet-iwa-v2.
var App = PublicClientApplicationBuilder
.Create("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")
.WithTenantId("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")
.Build();
The PublicClientApplication has the AcquireTokenByIntegratedWindowsAuth function. This sounds good because we can launch the console app as whatever user we want to use with a scheduled task. But it errors out with WS-Trust endpoint not found. Where's WS-Trust endpoint defined?
The sample also includes the line var accounts = await App.GetAccountsAsync() but that always returns zero accounts. Some responses to searches for this say that we have to use the global tenant admin. The company doesn't like that idea at all. How can that be safe? Do we create a new user as an admin tenant just for that?
The other option is this
var App = ConfidentialClientApplicationBuilder.Create("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")
.WithClientSecret("aeiou~XXXXXXXXXXX")
.WithAuthority(new Uri("https://login.microsoftonline.com/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"))
.Build();
The ConfidentialClientApplication doesn't have the integrated windows auth version. I can get connected and get MailFolders and Messages and process those, but it seems to work only when we use App.AcquireTokenForClient(scopes) and API permissions that allow the app to read everyone's email. Security doesn't like that much either.
I also looked at impersonation-and-ews-in-exchange. I read in some places that ExchangeWebService is deprecated and use MS Graph instead. Is the MS Graph API permissions in the EWS category mean that it's going to be around?
Can anyone out there show me the right combination of pieces needed to do this? (api permissions, client application type, scopes, authority, etc). It needs to be unattended (launched by scheduled task), needs to have permissions to read only one email box, and save the attachments.
(sorry so long)
Thanks, Mike
WS-Trust endpoint not found
The WS-Trust endpoint is your ADFS endpoint, if you have ADFS 2019 then MSAL does support that using WithAdfsAuthority see https://github.com/MicrosoftDocs/azure-docs/blob/main/articles/active-directory/develop/msal-net-initializing-client-applications.md
There are some other restriction around using WIA that are listed at the top of https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/Integrated-Windows-Authentication-in-MSAL-2.x . If the constraints don't affect you it should work okay.
With the Client Credentials flow which is what your using above you can restrict the scope of the mailboxes it can access see https://learn.microsoft.com/en-us/graph/auth-limit-mailbox-access
I would stick with the Graph rather then EWS as the later is being phased out and requires more permissions as its a legacy API.
The tutorial you shared in the question is an asp.net core console app. Since you want to have a console app and use it to read exchange mails.
Therefore, what we can confirm is that: We need to use MS Graph API to read the exchange mails. Graph API required an Azure AD application with correct API permissions to generate Access token to call the API. API permissions have 2 types, Delegated for Web app because it required users to sign in to obtain the token, Application for daemon app like console application which don't require an user-sign-in.
Since you are using the asp.net core console application, you can only using Application API permission. Using Application permission means the console app has the permission to query messages of any email address in your tenant. You can't control the Graph API itself to query some specific users only. But you can write your own business logic to set authorization.
Then we can make the console application authorized to access the API, we can generate an Access token and use it in the HTTP request header to call the API, we can also use the Graph SDK. Using SDK will help to troubleshoot when met error.
using Microsoft.Graph;
using Azure.Identity;
var scopes = new[] { "https://graph.microsoft.com/.default" };
var tenantId = "tenant_id";
var clientId = "Azure_AD_app_id";
var clientSecret = "Azure_AD_client_secret";
var clientSecretCredential = new ClientSecretCredential(
tenantId, clientId, clientSecret);
var graphClient = new GraphServiceClient(clientSecretCredential, scopes);
var messages = await graphClient.Users["{email_address/user_account/user_id}"].Messages.Request().Select("sender,subject").GetAsync();
I keep getting the above error when try to sort the list on displayName, however sorting is clearly supported according the example below: https://learn.microsoft.com/en-us/graph/api/user-list?view=graph-rest-1.0&tabs=csharp#request-3
I tried adding ConsistencyLevel headers and count query param with no luck.
var users = await GraphHelper.client.Users
.Request() //new List<QueryOption>() { new QueryOption("$count","true") }
.Header("ConsistencyLevel", "eventual")
.Filter($"accountEnabled eq true and extension_{AppConfig.variable["B2C:ExtensionId"]}_BankId eq '{bankId}'")
.OrderBy("displayName")
.Select($"id,displayName,identities,extension_{AppConfig.variable["B2C:ExtensionId"]}_BankId").GetAsync();
Same query works in graph explorer but not in the REST API.
I suspect that you are seeing this discrepancy because of incorrect setup in POSTMAN and code.
You can setup POSTMAN by following this step-by-step guide. A quick glance of steps is below:
Registering the Azure AD App
Get admin consent for the app
Get access token using the app
Make Microsoft Graph API call using the access token as bearer token
Similarly, please check similar setup of C# code as well.
Edit:
It seems there are some rules that we need to follow while using filter and orderby:
Hence, when I followed the rules and ran https://graph.microsoft.com/beta/users?$count=true&$filter=startsWith(displayName,'B')&$orderby=displayName desc query, it works:
I saw a reference in your code sample to B2C-ExtensionID. Not all of the core Microsoft Graph calls play nicely with B2C tenants.
The most recent MS documentation I've found is from 1/26/21 and can be viewed here
The documentation does not make clear some information I consider rather important for those of us working with B2C. Little comments like this one, found in the Optional query parameters section of this document :
The $count and $search parameters are currently not available in Azure AD B2C tenants.
It is also worth noting that, since B2C doesn't support $count, the presently MS endorsed workaround (which requires using $count, along with a header of "ConsistencyLevel: eventual") for using $orderby cannot be used against a list of B2C users.
So, if you're using Microsoft Graph to work with B2C tenant data you cannot implement useful features like counting your api resposne results, nor can you order them.
I need to create a webservice which will accept a file from a user, parse the file for data, assemble a collection of files stored on Sharepoint, and the return a zip file containing those files to the user.
The user may not have direct access to Sharepoint and in the future there might not even be a human user involved as the file may be pulled automatically from another system on a timer.
I am creating the solution in C# and am struggling to create a client which can successfully authenticate against Sharepoint.
Currently, I am trying to use the CSOM Authentication Manager in the Microsoft documentation, but using the Visual Studio debugger, I observe a failure in this method:
private async Task<string> AcquireTokenAsync(Uri resourceUri, string username, string password)
{
string resource = $"{resourceUri.Scheme}://{resourceUri.DnsSafeHost}";
var clientId = defaultAADAppId;
var body = $"resource={resource}&client_id={clientId}&grant_type=password&username={HttpUtility.UrlEncode(username)}&password={HttpUtility.UrlEncode(password)}";
using (var stringContent = new StringContent(body, Encoding.UTF8, "application/x-www-form-urlencoded"))
{
var result = await httpClient.PostAsync(tokenEndpoint, stringContent).ContinueWith((response) =>
{
return response.Result.Content.ReadAsStringAsync().Result;
}).ConfigureAwait(false);
var tokenResult = JsonSerializer.Deserialize<JsonElement>(result);
var token = tokenResult.GetProperty("access_token").GetString();
return token;
}
}
The value of result is:
"{"error":"unauthorized_client","error_description":"AADSTS700016: Application with identifier '986002f6-c3f6-43ab-913e-78cca185c392' was not found in the directory 'enpal.de'. This can happen if the application has not been installed by the administrator of the tenant or consented to by any user in the tenant. You may have sent your authentication request to the wrong tenant.\r\nTrace ID: d3b47a0b-54bc-40fd-a731-b99732326200\r\nCorrelation ID: 158621cc-3052-4c7b-9532-e2bab5c3cc09\r\nTimestamp: 2020-11-18 13:46:39Z","error_codes":[700016],"timestamp":"2020-11-18 13:46:39Z","trace_id":"d3b47a0b-54bc-40fd-a731-b99732326200","correlation_id":"158621cc-3052-4c7b-9532-e2bab5c3cc09","error_uri":"https://login.microsoftonline.com/error?code=700016"}"
The resourceUri, username, and password values correctly correspond to the values I would directly use to log into the website. My administrator has assured me the user is an admin of the site and therefore should have all the possible permissions it should need to do anything, including connect.
Does anyone know how to make this work?
Or know a different solution which is more likely to work?
Or know of anything which must be configured on the SharePoint side to allow this solution to work?
It looks like you took the code sample from the Microsoft website and skipped on reading an important section: Configuring an application in Azure AD ;)
At the moment, you are trying to connect to your tenant as an app with an id of '986002f6-c3f6-43ab-913e-78cca185c392', which is a placeholder from the code sample. Register your app in your tenant, and insert the proper app id in your defaultAADAppId constant.
I have a requirement to get the AD samAccountName in an MVC C# application that's deployed to an Azure Web App.
The app is Windows Authenticated against Azure AD which is synced with our local on premise AD servers using ADConnect.
When we run the Web App locally (Visual Studio 2017), the value that's returned from:
User.Identity.Name
is returned in the format DOMAIN\UserName
But when looking at the WebApp in Azure, the same code returns it in the format firstname.lastname#domain.co.uk
I appreciate that we won't be able to use User.Identity.Name to achieve a consistent result, but we do need a way of getting this information when the site is running Azure.
We've looked various ways of achieving this using Claims Descriptions and Extended Properties but have had no luck so far.
I've tried to give as much information as possible, but I'm working in conjunction with our infrastructure team so may not have provided enough, please let me know if more info is required.
The "firstname.lastname#domain.co.uk" format would be the userPrincipalName attribute of the account. So if you see an "#" in the name, you can connect to AD and search for the account and pull the sAMAccountName. Something like this:
var searcher =
new DirectorySearcher(
new DirectoryEntry("LDAP://domain.co.uk"),
$"(&(objectClass=user)(userPrincipalName={User.Identity.Name}))");
searcher.PropertiesToLoad.Add("sAMAccountName");
var result = searcher.FindOne();
var sAmAccountName = result.Properties["sAMAccountName"][0] as string;
Issue:
Using C# library, trying to get user's mailboxsettings but it is always null as a property of User class
I try to get the mailboxsetting like below:
return (await client.Users["userid"].Request().GetAsync()).MailboxSettings;
But it always returns null.
But when I try to use the rest API by sending request /beta/users/{id}/mailboxSettings, it works.
Is it that the mailboxsettings hasn't been exposed in User class?
You should build your request like:
var result = await client.Users["userid"].Request().Select("MailboxSetting").GetAsync().Result;
var mailboxSettings = result.MailboxSettings;
So you will get the MailboxSettings.
Appears to be an issue in the Graph SDK. You should file an issue on their GitHub repository https://github.com/microsoftgraph/msgraph-sdk-dotnet/issues.
The problem seems to be that getting mailbox settings requires another request, but the SDK treats it like a normal property on the User object.
I know this is an old thread but I had this problem as well. After fiddling with permissions I got the code posted by Larissa to work.
Microsoft Graph Application Permissions
Read all user's full profiles
Read and write all users mailbox settings
Windows Azure Active Directory Delegated Permissions
Read all user's basic profiles