28

I have an Asp.NET MVC Application connected with Azure AD B2C.

In the Administrator settings I've created an Administrators Group:

enter image description here

In my code I would like to use [Authorize(Roles = "Administrator")]

With regular Azure Active Directory it was easy to add (just 3 lines of code). But for the Azure AD B2C I cannot find any tutorial or example in the web which is working. Maybe you can tell me what i need to modify.

Here is the ConfigureAuth method of my Startup.Auth.cs

public void ConfigureAuth(IAppBuilder app)
{
    app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);

    app.UseCookieAuthentication(new CookieAuthenticationOptions());

    app.UseOpenIdConnectAuthentication(
        new OpenIdConnectAuthenticationOptions
        {
            // Generate the metadata address using the tenant and policy information
            MetadataAddress = String.Format(AadInstance, Tenant, DefaultPolicy),

            // These are standard OpenID Connect parameters, with values pulled from web.config
            ClientId = ClientId,
            RedirectUri = RedirectUri,
            PostLogoutRedirectUri = RedirectUri,

            // Specify the callbacks for each type of notifications
            Notifications = new OpenIdConnectAuthenticationNotifications
            {
                RedirectToIdentityProvider = OnRedirectToIdentityProvider,
                AuthorizationCodeReceived = OnAuthorizationCodeReceived,
                AuthenticationFailed = OnAuthenticationFailed,
            },

            // Specify the claims to validate
            TokenValidationParameters = new TokenValidationParameters
            {
                NameClaimType = "name"
            },

            // Specify the scope by appending all of the scopes requested into one string (separated by a blank space)
            Scope = $"openid profile offline_access {ReadTasksScope} {WriteTasksScope}"
        }
    );
}
adiga
  • 34,372
  • 9
  • 61
  • 83
DarkWing89
  • 319
  • 2
  • 4
  • 10

1 Answers1

28

Azure AD B2C does not yet include Group claims in the token it sends to the application thus you can't follow the same approach as you outlined with Azure AD (which does include group claims in the token).

You can support this feature ask by voting for it in the Azure AD B2C feedback forum: Get user membership groups in the claims with Azure AD B2C

That being said, you can do some extra work in this application to have it manually retrieve these claims the group claims and inject them into the token.

First, register a separate application that'll call the Microsoft Graph to retrieve the group claims.

  1. Go to https://apps.dev.microsoft.com
  2. Create an app with Application Permissions : Directory.Read.All.
  3. Add an application secret by clicking on Generate new password
  4. Add a Platform and select Web and give it any redirect URI, (e.g. https://yourtenant.onmicrosoft.com/groups)
  5. Consent to this application by navigating to: https://login.microsoftonline.com/YOUR_TENANT.onmicrosoft.com/adminconsent?client_id=YOUR_CLIENT_ID&state=12345&redirect_uri=YOUR_REDIRECT_URI

Then, you'll need to add code the following code inside of the OnAuthorizationCodeReceived handler, right after redeeming the code:

var authority = $"https://login.microsoftonline.com/{Tenant}";
var graphCca = new ConfidentialClientApplication(GraphClientId, authority, GraphRedirectUri, new ClientCredential(GraphClientSecret), userTokenCache, null);
string[] scopes = new string[] { "https://graph.microsoft.com/.default" };

try
{
    AuthenticationResult authenticationResult = await graphCca.AcquireTokenForClientAsync(scopes);
    string token = authenticationResult.AccessToken;

    using (var client = new HttpClient())
    {
        string requestUrl = $"https://graph.microsoft.com/v1.0/users/{signedInUserID}/memberOf?$select=displayName";

        HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, requestUrl);
        request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);

        HttpResponseMessage response = await client.SendAsync(request);
        var responseString = await response.Content.ReadAsStringAsync();

        var json = JObject.Parse(responseString);

        foreach (var group in json["value"])
            notification.AuthenticationTicket.Identity.AddClaim(new System.Security.Claims.Claim(System.Security.Claims.ClaimTypes.Role, group["displayName"].ToString(), System.Security.Claims.ClaimValueTypes.String, "Graph"));

        //TODO: Handle paging. 
        // https://developer.microsoft.com/en-us/graph/docs/concepts/paging
        // If the user is a member of more than 100 groups, 
        // you'll need to retrieve the next page of results.
    }
} catch (Exception ex)
{
    //TODO: Handle
    throw;
}
Saca
  • 10,355
  • 1
  • 34
  • 47
  • First big thanks for your answer! I have just 2 questions left about it. Where should i add that URL (Step 4) and what is the Redirect uri (is this the reply uri of the b2c?) ? Another question to the Code: What sould i fill in the variables: - GraphClientId - GraphRedirectUri - GraphClientSecret - userTokenCache And VisualStudio is calling an error message at: new c.Claim Big thanks for your help :-) – DarkWing89 Aug 27 '17 at 09:41
  • Made updates to further clarify app registration instructions and address the c.Claim issues. – Saca Aug 27 '17 at 09:49
  • 1
    GraphClientID = the Application ID of the app you registered, GraphSecret = the Application Secret, GraphRedirectUri = the Redirect URI you specified, userTokenCache should already be defined from the code that's already in that OnAuthorizationCodeReceived from the sample. – Saca Aug 27 '17 at 09:50
  • Step 5 returns me a URL including this error message: error=access_denied&error_description=AADSTS50020%3a+We+are+unable+to+issue+tokens+from+this+api+version+for+a+Microsoft+account.+Please+contact+the+application+vendor+as+they+need+to+use+version+2.0+of+the+protocol+to+support+this.%0D%0ATrace+ID – DarkWing89 Aug 27 '17 at 10:49
  • The 2 things im not sure about: The Plattform Redirect Uri: Should this be the Website URL where the MVC Application is hosted or the AzureB2C Domain? The Application Permissions show: Directory.Read.All (Only Administrators) I cannot delete the "Only Administrators" – DarkWing89 Aug 27 '17 at 11:11
  • 1
    Finally get it working - If anybody else need the answers: Redirect URI is the same as in the azure portal. In the link you need to change "common" to your b2c tenant ID. Thanks again for your help Saca – DarkWing89 Aug 27 '17 at 14:21
  • [seems like the same question/issue here](https://stackoverflow.com/questions/41438417/azure-b2c-how-do-i-get-group-claim-in-jwt-token) – spottedmahn Sep 05 '17 at 18:55
  • Thank you so much for this post. I googled the heck out of this problem and tried several other solutions, but none worked. This one does. One issue I have is with the Graph Client app. I need my Web API to also get group membership, so I tried to register another app as a Web API. I couldn't get the consent step to work. I used that app's client ID etc, but the consent page shows an error that the reply URL is wrong. If you have any ideas, I'd appreciate them. E.g., do I even need another app, or can my API just use this one somehow? Regardless, thank you very much for the helpful info. – Eric C. Berridge Sep 06 '17 at 04:26
  • Wouldn't modifying the token break the signature of the JWT? These groups are being added to the token, right? – spottedmahn Nov 10 '17 at 19:17
  • 2
    You are not modifying the actual token. You are adding claims to the .Net abstraction of the token that gets generated in memory after the token is validated. – Saca Nov 11 '17 at 18:44
  • How do you add these claims if you are retrieving the access token with msal.js in an Angular frontend and sending it to a .net core backend? – afriedman111 Aug 08 '19 at 20:38
  • At the current time, I believe the link "https://apps.dev.microsoft.com" is no longer valid. I believe the new system is https://portal.azure.com/. However, I am not sure how to do step 2 at that location: "Create an app with Application Permissions : Directory.Read.All." Can any one give guidance on this? – nejohannsen Nov 11 '20 at 21:31