skip to Main Content

I am learning / experimenting with Azure Active Directory B2C (AADB2C). I have created a web application that can sign-in or sign-up users (not using custom user flows) with OpenID Connect.

Once a user has signed in, I would like to make a call to Microsoft Graph to get information about the signed in user.

According to the docs, I initialize a client credential auth provider as follows:

var scopes = new[] { "https://graph.microsoft.com/.default" };

var clientSecretCredential = new ClientSecretCredential(config.TenantId, config.AppId, config.ClientSecret);

var graphClient = new GraphServiceClient(clientSecretCredential, scopes);

I then try to make a request, as follows:

var user = await graphClient.Me.Request().GetAsync();

The above results in the exception:

Code: BadRequest
Message: /me request is only valid with delegated authentication flow.

When I try the following:

var user = await graphClient.Users[ '' ].Request().GetAsync();

The above results in the exception:

Code: Authorization_RequestDenied
Message: Insufficient privileges to complete the operation.

Many of the examples in the MS documentation state that the "User.Read" permission must be granted, but when using the Azure Portal the "User.Read" permission is not found, below is a screen snapshot of the Request API Permissions blade within the Azure portal – I am still not clear on the Note (highlighted in the image below).

Azure Portal trying to add User.Read permission

When I do try to enter the "User.Read" permission, the following is displayed:

User.Read permission not found

I also found this tutorial regarding how to Register a Microsoft Graph application, but it seems to be geared to administrators who want to access the graph, not "regular" signed-in users per se.

My question – Is there any working example that illustrates how (after a user is signed in) to read the users information from Microsoft Graph?

2

Answers


  1. Chosen as BEST ANSWER

    In order to make calls to Microsoft Graph with an application registered with the option "Accounts in any identity provider or organizational directory (for authenticating users with user flows)", the following must be done (additional reference at: https://learn.microsoft.com/en-us/azure/active-directory-b2c/microsoft-graph-get-started?tabs=app-reg-ga):

    Add the "User.ReadWrite.All" or "User.Read.All" application permission (depending on the application needs).

    Note: When you add any of the above permissions via the Azure Portal, the status will initially be shown as "Not granted". Click on the line containing the permission, and then click on the "Grant admin consent for ..." button - (it is not intuitive at all), as shown below:

    enter image description here

    a popup window will appear to confirm, as shown below (click Yes to confirm):

    enter image description here

    Once confirmed, the permissions should appear as follows:

    enter image description here

    A few more gotcha's to be aware of:

    The .Me method will not work, for example, using the following code:

    var user = await graphClient.Me.Request().GetAsync();
    

    ... will still result in the exception:

    Code: BadRequest
    Message: /me request is only valid with delegated authentication flow.
    

    however, you can make a request using the user ID; which may be obtained from the authenticated users claim(s), for example:

    var user = await graphClient.Users[ "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" ].Request().GetAsync();
    

    The above call will work, however, be aware that some Graph properties are only returned if .Select() is used, for example, the "companyName" property is only returned if .Select is used, i.e.:

    var user = await graphClient.Users[ "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" ]
               .Request()
               .Select( u => new {
                  u.CompanyName
               } )
               .GetAsync();
    

    The above threw me for a loop as some properties seemed to be missing after the call when .Select() was not used, see the following for more information:


  2. Note that, web applications registered with supported account type as Accounts in any identity provider or organizational directory (for authenticating users with user flows) supports only Microsoft Graph offline_access and openid delegated permissions.

    I tried to reproduce the same in my environment and got below results:

    I registered one application in my B2C tenant by selecting supported account type as below:

    enter image description here

    When I used same code to get signed-in user profile from client credentials flow, I got same error as this flow won’t support Delegated permissions.

    using Microsoft.Graph;
    using Azure.Identity;
    
    var scopes = new[] { "https://graph.microsoft.com/.default" };
    var tenantId = "srib2corg.onmicrosoft.com";
    var clientId = "7427462d-b6e1-4ab0-ba0c-xxxxxxxx";
    var clientSecret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxx";
    var clientSecretCredential = new ClientSecretCredential(
                    tenantId, clientId, clientSecret);
    var graphClient = new GraphServiceClient(clientSecretCredential, scopes);
    var user = await graphClient.Me.Request().GetAsync();
    

    Response:

    enter image description here

    The following command is to fetch the list of users where I got same error as I don’t have required permissions as below:

    var user = await graphClient.Users["userID"].Request().GetAsync();
    

    Response:

    enter image description here

    To get signed-in user profile, you need to add Delegated User.Read permission in your B2C application which is not available because of your supported account type:

    enter image description here

    To resolve it, you need to create an app registration with supported account type as Single tenant/Multitenant below:

    enter image description here

    In the above application, you can add Delegated User.Read permission like below:

    enter image description here

    As client credentials flow won’t work with Delegated permissions, you need to change your flow to Delegated flows like Authorization code, username password etc….

    In my case, I changed to Username Password flow and used below code to get user profile:

    using Microsoft.Graph;
    using Azure.Identity;
    
    var scopes = new[] { "https://graph.microsoft.com/.default" };
    var tenantId = "srib2corg.onmicrosoft.com";
    var clientId = "7d983e16-5296-4056-8e72-xxxxxxxx";
    
    var options = new TokenCredentialOptions
    {
        AuthorityHost = AzureAuthorityHosts.AzurePublicCloud
    };
    
    var userName = "[email protected]";
    var password = "xxxxxxxxx";
    
    var userNamePasswordCredential = new UsernamePasswordCredential(
        userName, password, tenantId, clientId, options);
    
    var graphClient = new GraphServiceClient(userNamePasswordCredential, scopes);
    
    var user = await graphClient.Me.Request().GetAsync();
    
    Console.WriteLine("nName: " + user.DisplayName);
    Console.WriteLine("nID: " + user.Id);
    Console.WriteLine("nUPN: " + user.UserPrincipalName);
    

    Response:

    enter image description here

    If your use case is to get profile after user sign in the web application, you can check below SO thread that I recently answered.

    Signed-in user Profile page in ASP.NET Core Web App calling the Microsoft Graph – Stack Overflow

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search