asp.net-coremicrosoft-graph-apitokenazure-ad-b2cclaims

How can I retrieve my Custom User Attributes (Claims) from B2C using Graph API?


I have successfully created an application that uses a new B2C Tenant for authentication and I can login and call APIs.

I have added new Attributes for the Users (Roles one of them). In my "User Flows" I have 1 flow for SignIn that has selected the Roles claim one to login and one to edit users. In the following images you can see the setup and the returned claims, after editing with the User Flow.

Added extended user attribute

Selected to return data as claim

Decoded Token with extension claims and values

However, when I query Graph PI for the users + extensions, the extensions are not returned.

I have also access to the users using Graph API and I'm able to add extensions to the users by code using the Graph API. I can Add extended attributes, update them, retrieve them... In the code below I can Add them. These created with the Graph API I can retrieve later, but not the ones that I created with the B2C User Flow.

    // Add extension without the guid from b2c_extensions_app        
    var extension = new OpenTypeExtension()
    {
        ExtensionName = $"extension_Roles",
        AdditionalData = new Dictionary<string, object>()
        {
            { "Roles", value}
        }
    };
    await _graphServiceUsers[userId].Extensions.Request().AddAsync(extension);


    // Add extension with the guid from b2c_extensions_app  
    var extension2= new OpenTypeExtension()
    {
        ExtensionName = $"extension_00000000-e062-0000-8ec8-800000000003_Roles",
        AdditionalData = new Dictionary<string, object>()
        {
            { "Roles", value}
        }
    };

    await _graphServiceUsers[userId].Extensions.Request().AddAsync(extension2);

Neither of the scenarios above are linked to the B2C User that is returned with the claims.

I can retrieve the user with the extensions with the following code. And I get the extensions created from Graph API, but not the ones that are shown in the claims of the Token in the images above.

_graphServiceUsers[userId].Request().Select(UserSelectExpression).Expand("extensions").GetAsync();

All the custom attributes seem to be disconnected. I would need to update the ones that are returned as Claims, as they are the ones that I want to modify with the Graph API.

If you see the image below, you can see how the Extended properties are coming, but not the ones that I added from B2C. In the token you can see the mapped claim having one value, different from the value I added and retrieved from Graph API.

Different values from Claims and retrieved data using Graph API

I think they just made it too difficult, when it reality should be a lot easier to achieve something as simple as adding a new attribute and retrieve the values and work with them using Graph API.


Additional Info:

I'm using the Graph API application to access using the api

B2C Applications

I added all the permissions that had been suggested

Graph API permissions

In Graph Explorer (same results as C#):

https://graph.microsoft.com/v1.0/users/600001c5-0000-49b9-89c5-0000c80000fc?$select=displayName,identities,extension_39b4801c-5782-48d4-be6a-1cae6a8a881a_Roles

Result (Exception): Result Graph Explorer

Result C#

----------- UPDATE WITH SOLUTION --------------

I just wanted to add a quick note to clarify that the real problem, just in case someone has the same problem, as it's a bit complicated to get started with Graph API.

The problem (as of today) is that you need to use the Beta version of the Nuget packages or the Beta (graph.microsoft.com/beta) graph api to retrieve all the extension attributes. The stable version doesn't work.

  1. The nuget package that worked for me is Microsoft.Graph.Beta 4.21.0-preview.
  2. The package Microsoft.Graph stable 4.11.0 published Nov 25 2021 doesn't retrieve the extension properties.

Note: Something that is important is that the extension extension_39b0000c570000d4be6a1cae7a9a000a_Roles doesn't have the - from the guid.

Also, an additional thing to remember is the use of the guid from the extension to load only the extension: The first one to load all the attributes for the user. The second one to load only the Custom extended attribute.

    var user = await _graphServiceClient.Users[userId].Request().GetAsync();
    
    
    var user2 = await _graphServiceClient.Users[userId].Request().Select("id,extension_39b0000c570000d4be6a1cae7a9a000a_Roles").GetAsync();

Solution

  • Per my test, I can use this request to get extension properity:

    https://graph.microsoft.com/v1.0/users/198axxxxx9ce1c?$select=id,country,extension_3d3xxxxx707e_tiny_custom_prop
    

    test result is

    enter image description here

    And with asp.net core code, following this document:

    var scopes = new[] { "https://graph.microsoft.com/.default" };
    var tenantId = "b2c_tenant_id_here";
    
    // Values from app registration
    var clientId = "application_clientid_that_registered_in_b2c";
    var clientSecret = "app_client_secret";
    var options = new TokenCredentialOptions
    {
                    AuthorityHost = AzureAuthorityHosts.AzurePublicCloud
    };
    var clientSecretCredential = new ClientSecretCredential(
                    tenantId, clientId, clientSecret, options);
    var graphClient = new GraphServiceClient(clientSecretCredential, scopes);
    var user = graphClient.Users["198xxxxx9ce1c"].Request()
                        .Select("id,country,extension_3dxxxxx7e_tiny_custom_prop")
                        .GetAsync();
    var res = user.Result;
    

    enter image description here

    ===========================Update============================

    You can see the claim is also a custom properity, and I got the value of this property of my specific user. The user I queried here is one which I signed up when redirect to the b2c sign up page, and the user flow is required to set this custom property.

    enter image description here

    I created an azure ad b2c application and set these api permission for this app.

    enter image description here enter image description here

    I used v1.0 version in my workaround, so I need to use $select feature to get extension property in my code, but if I used beta version here, I can get extension property directly.

    enter image description here

    enter image description here