Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Azure AD B2C - Local IDP with "Username" not providing username claim

Tags:

azure-ad-b2c

I've configured an identity provider to use local accounts with usernames rather than passwords.

I have created a user with a Display Name, User Name, and Email.

I am able to select "Display Name" and "Email Address" application claims for all policies, but "User Name" is not an option. I have also confirmed that a username claim is not being provided to my application.

How can I configure Azure AD B2C such that it provides the username claim?

IDP Config

enter image description here

enter image description here

like image 768
Kevin Ortman Avatar asked Feb 12 '17 03:02

Kevin Ortman


1 Answers

Unfortunately, the username isn't available yet for selection as a claim to pass down in the token. You should vote for this ask in the Azure AD B2C UserVoice forum to help prioritize it: Include username in JWT claims

Your only option as this time is to retrieve it yourself via the Graph.

Here's a quick & dirty snippet of .Net code that you can use for this:

    private async Task OnSecurityTokenValidated(SecurityTokenValidatedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
    {
        try
        {
            var userObjectId = notification.AuthenticationTicket.Identity.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier")?.Value;

            // You'll need to register a separate app for this.
            // This app will need APPLICATION (not Delegated) Directory.Read permissions
            // Check out this link for more info:
            // https://docs.microsoft.com/en-us/azure/active-directory-b2c/active-directory-b2c-devquickstarts-graph-dotnet 
            var authContext = new Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext(string.Format(graphAuthority, tenant));
            var t = await authContext.AcquireTokenAsync(graphResource, new ClientCredential(graphClientId, graphClientSecret));

            string result;
            using (var client = new HttpClient())
            {
                client.DefaultRequestHeaders.Add("Authorization", "Bearer " + t.AccessToken);

                var url = graphResource + tenant + "/users/" + userObjectId + "/?api-version=1.6";
                result = await client.GetStringAsync(url);
            }

            var jsonResult = JObject.Parse(result);
            var username = jsonResult["signInNames"].FirstOrDefault(j => j["type"].ToString() == "userName")?["value"]?.ToString();
            notification.AuthenticationTicket.Identity.AddClaim(new Claim("username", username));
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.ToString());
        }
    }

You'll reference this method when you setup your OpenIdConnectAuthenticationOptions like so:

    new OpenIdConnectAuthenticationOptions
        {
            // (...)
            Notifications = new OpenIdConnectAuthenticationNotifications
            {
                AuthenticationFailed = OnAuthenticationFailed,
                SecurityTokenValidated = OnSecurityTokenValidated,
            },
            // (...)
        };
like image 122
Saca Avatar answered Oct 01 '22 12:10

Saca