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?
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,
},
// (...)
};