4

I am attempting to retrieve some very basic information from Azure B2C, using the Built-In User Attributes and Claims.

I merely want to return

  1. Given Name
  2. Surname
  3. UserId
  4. Email

Its not totally obvious (to me) how B2C is storing this content... The SignIn/SignOut Policy (User Attirubtes) displays Email Address as a string Img 1

but the SignIn/SignOut Policy (Application Claims) displays Email Addresses as a stringCollection Img 2

Using the below code, I am attempting to return the 4 above Claims but only the

  • UserId
  • List item are coming through. enter image description here

I have used the JWT.IO to test the return Token and the Claims I'm looking for are there.

Lastly, just to make things even stranger, MS seems to store my Email in a UserName field but doesn't show me an Email Field(s)? enter image description here

I'm hoping to NOT have to make a seperate call to the Graph API in order to get these 2-3 fields I want.

I'm just hoping someone can help me clarify where my code is going wrong.

    var claimsIdentity = (ClaimsIdentity)HttpContext.User.Identity;
    var userIdClaim = claimsIdentity.Claims.SingleOrDefault(c => c.Type == ClaimTypes.NameIdentifier);
    if (userIdClaim != null)
    {
        userId = userIdClaim.Value;
        ViewData["userId"] = userId;
    }
    var GivenNameClaim = claimsIdentity.Claims.SingleOrDefault(c => c.Type == ClaimTypes.GivenName);
    if (GivenNameClaim != null)
    {
        GivenName = GivenNameClaim.Value;
        ViewData["GivenName"] = GivenName;
    }
    var SurNameClaim = claimsIdentity.Claims.SingleOrDefault(c => c.Type == ClaimTypes.Surname);
    if (SurName != null)
    {
        SurName = SurNameClaim.Value;
        ViewData["Surname"] = SurName;
    }
    var EmailClaim = claimsIdentity.Claims.SingleOrDefault(c => c.Type == ClaimTypes.Email);
    if (Email != null)
    {
        Email = EmailClaim.Value;
        ViewData["Email"] = Email;
    }

EDIT

Adding the below to my view helped..

  @foreach (Claim claim in User.Claims)
    {
     <tr>
     <td>@claim.Type @claim.Subject</td>
     <td>@claim.Value</td>
     </tr>
    }

It returns

So I have updated me email to the belwo, which now works for 3/4 files, it does not return the collection of emails.

var Claims = User.Claims;
var SurNameClaim = Claims.SingleOrDefault(c => c.Type == ClaimTypes.Surname);
ViewData["Surname"] = SurNameClaim.Value;

var GivenNameClaim = Claims.SingleOrDefault(c => c.Type == ClaimTypes.GivenName);
ViewData["GivenName"] = GivenNameClaim.Value;

var ClientIdClaim = Claims.SingleOrDefault(c => c.Type == ClaimTypes.NameIdentifier);
ViewData["ClientId"] = ClientIdClaim.Value;

var EmailClaim = Claims.SingleOrDefault(c => c.Type == ClaimTypes.Email);
if (EmailClaim != null)
{
    ViewData["Email"] = EmailClaim.Value;
}
else
{
    ViewData["Email"] = "Is Null";
}
Tim Cadieux
  • 447
  • 9
  • 21

1 Answers1

2

The user attributes is the information which aad B2C collects from user. So B2C collects only a single email, the 'email address' is a string. Claims is the information which B2C returns to the relying party app. since there can be more than one email here(coming from multiple resources, such as federated Idp, this is a collection.

you can see the sample app to see how to parse claims https://github.com/Azure-Samples/active-directory-b2c-dotnet-webapp-and-webapi/blob/master/TaskService/Controllers/TasksController.cs

How to read a claim whose value is array

List<string> emails = new List<string>();
  IEnumerable<Claim> emailClaims =  Claims.Where(c => c.Type == ClaimTypes.Email);

                if (emailClaims.Any())
                {
                    // get the roles' actual value
                    foreach (Claim claim in emailClaims)
                    {
                        emails.Add(claim.Value);
                    }                   
                }
Abhishek Agrawal
  • 2,183
  • 1
  • 17
  • 24
  • Correct. I will check the above example and come back to you. – Tim Cadieux Aug 27 '19 at 16:46
  • Ok, in that case, your claim types might be wrong. Which claims are you getting in code and which you are not. – Abhishek Agrawal Aug 27 '19 at 16:49
  • NameIdentifier and GivenNAme work, Surname and Email do not. When using JWT and test User Flow, i see my NameIdentifier appears as sub,I see given_name and family_name, as well as a collection called emails with my single email address. – Tim Cadieux Aug 27 '19 at 17:18
  • If I use foreach (Claim claim in User.Claims) in my View, I see all 4 claims that i want to see. – Tim Cadieux Aug 27 '19 at 17:32
  • you can see the claim type (key in the User.Claims) from foreach and see what is the key expected vs what were you passing. – Abhishek Agrawal Aug 27 '19 at 17:46
  • Posted some edit code, I now get 3/4 fields but I can't figure out how to return the Emails collection. – Tim Cadieux Aug 27 '19 at 18:06
  • Unfortunately, the response doesn't work for me as the IEnumerable cast doesn't work because the return type is Claim and not List – Tim Cadieux Aug 27 '19 at 20:21
  • I updated the code to var emailClaims = Claims.Where(c => c.Type == ClaimTypes.Email).ToList(); ViewData["Email"] = emailClaims.Count(); but that returns 0 – Tim Cadieux Aug 27 '19 at 20:29
  • Managed to get it working with this var EmailClaim = ((ClaimsIdentity)User.Identity).FindFirst("emails").Value; – Tim Cadieux Aug 28 '19 at 02:24