Reputation: 63
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.
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.
I'm using the Graph API application to access using the api
I added all the permissions that had been suggested
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
----------- 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.
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();
Upvotes: 4
Views: 9539
Reputation: 3402
Graph.Client
v5 request to the https://graph.microsoft.com/v1.0/users
endpoint looks like this in C#:
var phoneNumberClaim = $"extension_{b2cClientId}_PhoneNumber";
var graphResponse = await _graphServiceClient.Users[request.UserId].GetAsync(options =>
{
options.QueryParameters.Select = new string[] { "id", "displayName", "identities", phoneNumberClaim };
}, cancellationToken: cancellationToken);
var user = new GetUserResponse.UserItem
{
FullName = graphResponse!.DisplayName!,
Email = graphResponse.Identities!.First(i => i.SignInType == "emailAddress").IssuerAssignedId!,
PhoneNumber = graphResponse.AdditionalData[phoneNumberClaim]?.ToString(),
};
var response = new GetUserResponse
{
User = user
};
return response;
If you're testing it out using the Graph Explorer, make sure you're signed into the b2c tenant with a user. If you're in the wrong tenant, sign out and then append ?tenantId={tenantId}
onto the url (i.e. https://developer.microsoft.com/en-us/graph/graph-explorer?tenant=myb2ctenant.onmicrosoft.com
).
Example raw graph request on the graph explorer
https://graph.microsoft.com/v1.0/users/61000000-0000-0000-0000-00000000f297?$select=id,displayname,extension_480000000000000000000000000000a6_PhoneNumber,identities
Where the first id is the user object id and the extension id is the client id of the b2c application registration (not your application registration client id! - starts with b2c-extensions-app
). For example, I had a Phone Number attribute.
Permissions I had on my application registration (not the b2c one) were as follows:
Upvotes: 0
Reputation: 56
I could access custom properties using the standard .NET SDK for Microsoft.Graph API (not beta) in late 2022. I found the trick was to specify the property in the select clause.
var users = await graphServiceClient.Users.Request()
.Select($"extension_{b2cExtensionsAppClientId}_Roles")
.GetAsync();
The other trick was to get the Application (Client) Id of the b2c extensions app for including in the select clause. I could get this by navigating to App Registrations in the Azure Portal and clicking 'All Applications'. There I was shown the b2c-extensions-app where I could retrieve the app Id.
Upvotes: 1
Reputation: 63
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 (https://graph.microsoft.com/beta) graph api to retrieve all the extension attributes. The stable version doesn't work.
Upvotes: 1
Reputation: 15961
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
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;
===========================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.
I created an azure ad b2c application and set these api permission for this app.
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.
Upvotes: 2