Skip to content

Latest commit

 

History

History
314 lines (215 loc) · 18.9 KB

File metadata and controls

314 lines (215 loc) · 18.9 KB
services platforms author level client service endpoint
active-directory
dotnet
kalyankrishna1
300
ASP.NET Core 2.x Web App
Microsoft Graph
Microsoft identity platform

Add authorization using app roles & roles claims to an ASP.NET Core web app thats signs-in users with the Microsoft identity platform

About this sample

Overview

This sample shows how a .NET Core 2.2 MVC Web app that uses OpenID Connect to sign in users and use Azure AD Application Roles (app roles) for authorization. App roles, along with Security groups are popular means to implement authorization.

This application implements RBAC using Azure AD's Application Roles & Role Claims feature. Another approach is to use Azure AD Groups and Group Claims, as shown in WebApp-GroupClaims. Azure AD Groups and Application Roles are by no means mutually exclusive; they can be used in tandem to provide even finer grained access control.

Build status

Using RBAC with Application Roles and Role Claims, developers can securely enforce authorization policies with minimal effort on their part.

For more information about how the protocols work in this scenario and other scenarios, see Authentication Scenarios for Azure AD.

Scenario

This sample first leverages the ASP.NET Core OpenID Connect middleware to sign in the user. On the home page it displays the various claims that the user's ID Token contained. The ID token is used by the asp.net security middleware to build the ClaimsPrincipal, accessible via HttpContext.User in the code.

This web application allows users to list all users in their tenant or a list of all the roles and groups the signed in user is assigned to depending on the app role they have been assigned to. The idea is to provide an example of how, within an application, access to certain functionality is restricted to subsets of users depending on which role they belong to.

This kind of authorization is implemented using role-based access control (RBAC). When using RBAC, an administrator grants permissions to roles, not to individual users or groups. The administrator can then assign roles to different users and groups to control who has then access to certain content and functionality.

This sample application defines the following two Application Roles:

  • DirectoryViewers: Have the ability to view any directory user's roles and security group assignments.
  • UserReaders: Have the ability to view a list of users in the directory.

These application roles are defined in the Azure portal in the application's registration manifest. When a user signs into the application, Azure AD emits a roles claim for each role that the user has been granted individually to the user in the from of role membership. Assignment of users and groups to roles can be done through the portal's UI, or programmatically using the Microsoft Graph and Azure AD PowerShell. In this sample, application role management is done through the Azure portal or using PowerShell.

NOTE: Role claims will not be present for guest users in a tenant if the /common endpoint is used as the authority.

Sign in with the Microsoft identity platform

This is the sixth chapter of a set of tutorials. In the chapter before this one, you learned how to receive the group memberships in a user's claims. In this one you will learn about how to use the App roles in an app using the Microsoft Identity Platform to authenticate users.

How to run this sample

To run this sample:

Pre-requisites:

go through the previous phase of the tutorial showing how the Using the Microsoft identity platform to call the Microsoft Graph API from an An ASP.NET Core 2.x Web App. This page shows the incremental change needed to set up application roles and retrieve them in your app when a user signs in.

To run this sample, you'll need:

  • Visual Studio 2017 or just the .NET Core SDK
  • An Internet connection
  • A Windows machine (necessary if you want to run the app on Windows)
  • An OS X machine (necessary if you want to run the app on Mac)
  • A Linux machine (necessary if you want to run the app on Linux)
  • An Azure Active Directory (Azure AD) tenant. For more information on how to get an Azure AD tenant, see How to get an Azure AD tenant
  • A user account in your Azure AD tenant. This sample will not work with a Microsoft account (formerly Windows Live account). Therefore, if you signed in to the Azure portal with a Microsoft account and have never created a user account in your directory before, you need to do that now.

Step 1: Clone or download this repository

From your shell or command line:

git clone https://github.com/Azure-Samples/microsoft-identity-platform-aspnetcore-webapp-tutorial.git

or download and extract the repository .zip file.

Given that the name of the sample is quiet long, and so are the names of the referenced NuGet packages, you might want to clone it in a folder close to the root of your hard drive, to avoid file size limitations on Windows.

Navigate to the "5-WebApp-AuthZ" folder

 cd "5-1-Roles"

Step 2: Configure your application to receive the roles claims

Note: To receive the roles claim with the name of the app roles this user is assigned to, make sure that the user accounts you plan to sign-in to this app is assigned to the app roles of this app. The guide, Assign a user or group to an enterprise app in Azure Active Directory provides step by step instructions.

Step 3: Define your Application Roles

  1. In the blade for your application in Azure Portal, click Manifest.
  2. Edit the manifest by locating the appRoles setting and adding the two Application Roles. The role definitions are provided in the JSON code block below. Leave the allowedMemberTypes to User only. Each role definition in this manifest must have a different valid Guid for the "id" property. Note that the "value" property of each role is set to the exact strings DirectoryViewers and UserReaders (as these strings are used in the code in the application).
  3. Save the manifest.

The content of appRoles should be the following (the id should be a unique Guid)

{
  ...
    "appRoles": [
        {
            "allowedMemberTypes": [
                "User"
            ],
            "description": "User readers can read basic profiles of all users in the directory",
            "displayName": "UserReaders",
            "id": "a816142a-2e8e-46c4-9997-f984faccb625",
            "isEnabled": true,
            "lang": null,
            "origin": "Application",
            "value": "UserReaders"
        },
        {
            "allowedMemberTypes": [
                "User"
            ],
            "description": "Directory viewers can view objects in the whole directory.",
            "displayName": "DirectoryViewers",
            "id": "72ff9f52-8011-49e0-a4f4-cc1bb26206fa",
            "isEnabled": true,
            "lang": null,
            "origin": "Application",
            "value": "DirectoryViewers"
        }
    ],
 ...
}
  1. Follow the steps in the document Assign users and groups to an application in Azure Active Directory to assign users to these roles.
  • You can also use PowerShell scripts that automatically creates these two roles for your app. They additionally create two users in your tenant and assign them to these two roles. If you want to use this automation:

    1. In PowerShell run the following command to create the two roles and users.

      .\AppCreationScripts\CreateUsersAndRoles.ps1
    2. Run the following command to remove the two roles and users once you done testing.

      .\AppCreationScripts\CleanupUsersAndRoles.ps1

Remember to make changes in the following line (the app name) in the powershell scripts to ensure that the scripts target the app you intended to use.

     $app = Get-AzureADApplication -Filter "DisplayName eq 'WebApp-RolesClaims'"

More details on how to run the scripts are described in App Creation Scripts

Step 4: Run the sample

  1. Clean the solution, rebuild the solution, and run it.

  2. Open your web browser and make a request to the app. The app immediately attempts to authenticate you via the Microsoft identity platform endpoint. Sign in using a user account of that tenant.

First time Consent

  1. On the home page, the app lists the various claims it obtained from your ID token. You'd notice a claim named roles. There will be one roles claim for each app role the signed-in use is assigned to.

  2. There also are two links provided on the home page under the Try one of the following Azure App Role driven operations heading. These links will result in an access denied error if the signed-in user is not present in the expected role. Sign-out and sign-in with a user account with the correct role assignment to view the contents of these pages.

Note: You need to be a tenant admin to view the page that lists all the groups and roles the signed-in user is assigned to. It requires the Directory.Read.All permission to work. If you run into the AADSTS65001: The user or administrator has not consented to use the application error, provide admin consent to your app in the portal. Sign-out and sign-in again to make the page work as expected.

When you click on the page that fetches the signed-in user's roles and group assignments, the sample will attempt to obtain consent from you for the Directory.Read.All permission using incremental consent.

Did the sample not work for you as expected? Did you encounter issues trying this sample? Then please reach out to us using the GitHub Issues page.

Support in ASP.NET Core middleware libraries

The asp.net middleware supports roles populated from claims by specifying the claim in the RoleClaimType property of TokenValidationParameters.

// Startup.cs
public static IServiceCollection AddMicrosoftIdentityPlatformAuthentication(this IServiceCollection services, IConfiguration configuration, X509Certificate2 tokenDecryptionCertificate = null)
{
            // [removed for] brevity

            // This is required to be instantiated before the OpenIdConnectOptions starts getting configured.
            // By default, the claims mapping will map claim names in the old format to accommodate older SAML applications.
            // 'http://schemas.microsoft.com/ws/2008/06/identity/claims/role' instead of 'roles'
            // This flag ensures that the ClaimsIdentity claims collection will be built from the claims in the token
            JwtSecurityTokenHandler.DefaultMapInboundClaims = false;

            // The following lines code instruct the asp.net core middleware to use the data in the "groups" claim in the Authorize attribute and User.IsInrole()
            // See https://docs.microsoft.com/aspnet/core/security/authorization/roles?view=aspnetcore-2.2 for more info.
            services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options =>
            {
                // Use the groups claim for populating roles
                options.TokenValidationParameters.RoleClaimType = "roles";
            });
        // [removed for] brevity
}

// In code..(Controllers & elsewhere)
[Authorize(Roles = DirectoryViewers")] // In controllers
// or
User.IsInRole("UserReaders"); // In methods

About the code

The following files have the code that would be of interest to you.

  1. HomeController.cs

    1. Passes the HttpContext.User (the signed-in user) to the view. 1 Services\GraphServiceClientFactory.cs
    2. Uses the Microsoft Graph SDK to carry out various operations with Microsoft Graph.
  2. Home\Index.cshtml

    1. This has some code to print the current user's claims
  3. Startup.cs

    - at the top of the file, add the following using directive:
    
     using Microsoft.Identity.Web;
    • in the ConfigureServices method, replace the two following lines:

       services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
               .AddAzureAD(options => Configuration.Bind("AzureAd", options));
      
      // by this line:
      
      //This enables your application to use the Microsoft identity platform endpoint. This endpoint is capable of signing-in users both with their Work and School and Microsoft Personal accounts.
             services.AddMicrosoftIdentityPlatformAuthentication(Configuration)
                     .AddMsal(Configuration, new string[] { "User.Read", "Directory.Read.All" }) // Adds support for the MSAL library with the permissions necessary to retrieve the signed-in user's group info in case of a token overage
                     .AddInMemoryTokenCaches(); // Adds aspnetcore MemoryCache as Token cache provider for MSAL.
      
         services.AddMSGraphService(Configuration);    // Adds the IMSGraphService as an available service for this app.
  4. In the HomeController.cs, the following method is added with the Authorize attribute with the name of the app role UserReaders, that permits listing of users in the tenant.

        [Authorize(Roles = AppRoles.UserReaders )]
        public async Task<IActionResult> Users()
        {
  5. In the ConfigureServices method of `Startup.cs', the following line instructs the asp.net security middleware to use the roles claim to fetch roles for authorization:

               // The claim in the Jwt token where App roles are available.
               options.TokenValidationParameters.RoleClaimType = "roles";
  6. A new class called AccountController.cs is introduced. This contains the code to intercept the default AccessDenied error's route and present the user with an option to sign-out and sign-back in with a different account that has access to the required role.

        [AllowAnonymous]
        public IActionResult AccessDenied()
        {
  7. The following method is also added with the Authorize attribute with the name of the app role DirectoryViewers, that permits listing of roles and groups the signed-in user is assigned to.

        [Authorize(Roles = AppRoles.DirectoryViewers)]
        public async Task<IActionResult> Groups()
        {
  8. The views, Users.cshtml and Groups.cshtml have the code to display the users in a tenant and roles and groups the signed-in user is assigned to respectively.

Next steps

Learn more

Community Help and Support

Use Stack Overflow to get support from the community. Ask your questions on Stack Overflow first and browse existing issues to see if someone has asked your question before. Make sure that your questions or comments are tagged with [azure-active-directory adal msal dotnet].

If you find a bug in the sample, please raise the issue on GitHub Issues.

To provide a recommendation, visit the following User Voice page.

More information

To understand more about app registration, see:

To understand more about groups roles and the various claims in tokens, see:

Contributing

If you'd like to contribute to this sample, see CONTRIBUTING.MD.

This project has adopted the Microsoft Open Source Code of Conduct. For more information, see the Code of Conduct FAQ or contact [email protected] with any additional questions or comments.