Get the user's email address from Azure AD via OpenID Connect
I struggled with the same problem for a few days before arriving at a solution. In answer to your question: yes, you should be able to get the e-mail address back in your claims as long as you:
- Include the
profile
oremail
scope in your request, and - Configure your application in the Azure Portal Active Directory section to include Sign in and read user profile under Delegated Permissions.
Note that the e-mail address may not be returned in an email
claim: in my case (once I got it working) it's coming back in a name
claim.
However, not getting the e-mail address back at all could be caused by one of the following issues:
No e-mail address associated with the Azure AD account
As per this guide to Scopes, permissions, and consent in the Azure Active Directory v2.0 endpoint, even if you include the email
scope you may not get an e-mail address back:
The
If you're getting other profile-related claims back (like given_name
and family_name
), this might be the problem.
Claims discarded by middleware
This was the cause for me. I wasn't getting any profile-related claims back (first name, last name, username, e-mail, etc.).
In my case, the identity-handling stack looks like this:
- IdentityServer3
- IdentityServer3.AspNetIdentity
- A custom Couchbase storage provider based on couchbase-aspnet-identity
The problem was in the IdentityServer3.AspNetIdentity AspNetIdentityUserService
class: the InstantiateNewUserFromExternalProviderAsync()
method looks like this:
protected virtual Task<TUser> InstantiateNewUserFromExternalProviderAsync(
string provider,
string providerId,
IEnumerable<Claim> claims)
{
var user = new TUser() { UserName = Guid.NewGuid().ToString("N") };
return Task.FromResult(user);
}
Note it passes in a claims collection then ignores it. My solution was to create a class derived from this and override the method to something like this:
protected override Task<TUser> InstantiateNewUserFromExternalProviderAsync(
string provider,
string providerId,
IEnumerable<Claim> claims)
{
var user = new TUser
{
UserName = Guid.NewGuid().ToString("N"),
Claims = claims
};
return Task.FromResult(user);
}
I don't know exactly what middleware components you're using, but it's easy to see the raw claims returned from your external provider; that'll at least tell you they're coming back OK and that the problem is somewhere in your middleware. Just add a Notifications
property to your OpenIdConnectAuthenticationOptions
object, like this:
// Configure Azure AD as a provider
var azureAdOptions = new OpenIdConnectAuthenticationOptions
{
AuthenticationType = Constants.Azure.AuthenticationType,
Caption = Resources.AzureSignInCaption,
Scope = Constants.Azure.Scopes,
ClientId = Config.Azure.ClientId,
Authority = Constants.Azure.AuthenticationRootUri,
PostLogoutRedirectUri = Config.Identity.RedirectUri,
RedirectUri = Config.Azure.PostSignInRedirectUri,
AuthenticationMode = AuthenticationMode.Passive,
TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false
},
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthorizationCodeReceived = context =>
{
// Log all the claims returned by Azure AD
var claims = context.AuthenticationTicket.Identity.Claims;
foreach (var claim in claims)
{
Log.Debug("{0} = {1}", claim.Type, claim.Value);
}
return null;
}
},
SignInAsAuthenticationType = signInAsType // this MUST come after TokenValidationParameters
};
app.UseOpenIdConnectAuthentication(azureAdOptions);
See also
- This article by Scott Brady contains a section on Claims Transformation which may be useful if neither of the above fixes it.
- This discussion on the IdentityServer3 GitHub account was a huge help to me, especially this response.
Is it an option for you to pass &resource=https://graph.windows.net in the sign-in request to the authorization endpoint, then query the Azure AD Graph API for the authenticated organizational user's Office 365 email address? For example, GET https://graph.windows.net/me/mail?api-version=1.5
For additional reference, see the WebApp-WebAPI-MultiTenant-OpenIdConnect-DotNet code sample on the AzureADSamples GitHub.
I was struggling with the same issue for days... I was getting the email address from users with personal Microsoft accounts but not for those with company Microsoft accounts.
For personal accounts, the email address is returned in an email
field like one would expect.
For company accounts, the email address is returned in a preferred_username
field.
Keeping my fingers crossed that there isn't another Microsoft variation that I haven't discovered yet...