/active-directory-dotnet-webapp-multitenant-openidconnect

A sample .NET 4.5 MVC web app that signs-up and signs-in users from any Azure AD tenant using OpenID Connect.

Primary LanguageJavaScriptMIT LicenseMIT

services platforms author level client service endpoint
active-directory
dotnet
jmprieur
200
.NET Web App (MVC)
Microsoft Graph
AAD v1.0

Build a multi-tenant SaaS web application using Azure AD & OpenID Connect

Build status

About this sample

This sample shows how to build an ASP.NET MVC web application that uses Azure AD for sign-in using the OpenID Connect protocol. Additionally it also introduces developers to the concept of a multi-tenant Azure Active Directory application.

Overview

When it comes to developing apps, developers can choose to configure their app to be either single-tenant or multi-tenant during app registration in the Azure portal.

  • Single-tenant apps are only available in the tenant they were registered in, also known as their home tenant.
  • Multi-tenant apps are available to users in both their home tenant and other tenants where they are provisioned.

For more information about apps and tenancy, see Tenancy in Azure Active Directory

This sample shows how to build a multi-tenant ASP.Net MVC web application that uses OpenID Connect to sign up and sign in users from any Azure Active Directory tenant, using the ASP.Net OpenID Connect OWIN middleware and the Active Directory Authentication Library (ADAL) for .NET.

Looking for previous versions of this code sample? Check out the tags on the releases GitHub page.

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

Overview

Scenario

This sample demonstrates a multi-tenant .NET Web App (MVC) application signing in users from multiple Azure AD tenants and calling Microsoft Graph.

  1. The web app allows the tenant admins to sign-up (and provision this app in their tenant) by signing them in using the ASP.Net OpenID Connect OWIN middleware.
  2. The users of these signed-up tenants can then sign-in themselves and create a Todo list for themselves.
  3. The application also uses the Active Directory Authentication Library (ADAL) for .NET library to obtain a JWT access token from Azure Active Directory (Azure AD) for Microsoft Graph.
  4. The access token is used as a bearer token to authenticate the user when calling Microsoft Graph to fetch the signed-in user's details.

How to run this sample

To run this sample, you'll need:

  • Visual Studio 2017
  • An Internet connection.
  • 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.

[!Note] If you want to run this sample in a Microsoft National Cloud, navigate to the README-National-Cloud.md.

Step 1: Clone or download this repository

From your shell or command line:

git clone https://github.com/Azure-Samples/active-directory-dotnet-webapp-multitenant-openidconnect.git

or download and extract the repository .zip file.

Given that the name of the sample is pretty long, and so are the name 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.

Step 2: Register the sample application with your Azure Active Directory tenant

There is one project in this sample. To register it, you can:

If you want to use this automation, read the instructions in App Creation Scripts

Choose the Azure AD tenant where you want to create your applications

As a first step you'll need to:

  1. Sign in to the Azure portal using either a work or school account.
  2. If your account gives you access to more than one tenant, select your account in the top-right corner, and set your portal session to the desired Azure AD tenant (using Switch Directory).
  3. In the left-hand navigation pane, select the Azure Active Directory service, and then select App registrations.

Register the service app (TodoListWebApp_MT)

  1. Navigate to the Azure portal - App registrations page select New registration.

  2. When the Register an application page appears, enter your application's registration information:

    • In the Name section, enter a meaningful application name that will be displayed to users of the app, for example TodoListWebApp_MT.
    • In the Supported account types section, select Accounts in any organizational directory.
    • In the Redirect URI section, select Web in the combo-box and enter the following redirect URIs.
      • https://localhost:44302/
      • https://localhost:44302/Onboarding/ProcessCode
  3. Select Register to create the application.

  4. On the app Overview page, find the Application (client) ID value and record it for later. You'll need it to configure the Visual Studio configuration file for this project.

  5. In the list of pages for the app, select Authentication.

    • In the Advanced settings section set Logout URL to https://localhost:44302/Account/EndSession
    • In the Advanced settings | Implicit grant section, check ID tokens as this sample requires the Implicit grant flow to be enabled to sign-in the user, and call an API.
  6. Select Save.

  7. The new customer onboarding process implemented by the sample requires the application to perform an OAuth2 request, which in turn requires to associate a key to the app in your tenant. From the Certificates & secrets page, in the Client secrets section, choose New client secret:

    • Type a key description (of instance app secret),
    • Select a key duration of either In 1 year, In 2 years, or Never Expires.
    • When you press the Add button, the key value will be displayed, copy, and save the value in a safe location.
    • You'll need this key later to configure the project in Visual Studio. This key value will not be displayed again, nor retrievable by any other means, so record it as soon as it is visible from the Azure portal.
  8. In the list of pages for the app, select API permissions

    • Click the Add a permission button and then,
    • Ensure that the Microsoft APIs tab is selected
    • In the Commonly used Microsoft APIs section, click on Microsoft Graph
    • In the Delegated permissions section, ensure that the right permissions are checked: User.Read, User.Read.All. Use the search box if necessary.
    • Select the Add permissions button

Step 3: Configure the sample to use your Azure AD tenant

In the steps below, "ClientID" is the same as "Application ID" or "AppId".

Open the solution in Visual Studio to configure the projects

Configure the service project

  1. Open the TodoListWebApp\Web.Config file
  2. Find the app key ida:ClientId and replace the existing value with the application ID (clientId) of the TodoListWebApp_MT application copied from the Azure portal.
  3. Find the app key ida:ClientSecret and replace the existing value with the key you saved during the creation of the TodoListWebApp_MT app, in the Azure portal.
  4. Find the app key ida:RedirectUri and replace the existing value with the base address of the TodoListWebApp_MT project (by default https://localhost:44302/).

Step 4: [optional] Create an Azure Active Directory test tenant

This sample shows how to take advantage of the consent model in Azure AD to make an application available to any user from any organization with a tenant in Azure AD. To see that part of the sample in action, you need to have access to user accounts from a tenant that is different from the one you used for developing the application. The simplest way of doing that is to create a new directory tenant in your Azure subscription (just navigate to the main Active Directory page in the portal and click Add) and add a few test user accounts.

This step is optional as you can also use accounts from the same directory, but if you do you will not see the consent prompts as the app is already approved.

Step 5: Run the sample

Clean the solution, rebuild the solution, and run it. The sample implements two distinct tasks: the onboarding of a new customer and regular sign in & use of the application.

Sign up

When running the app for the very first time, you'd need to sign-in as an administrator first. Click the Sign Up link on the top bar.

Sign Up link

You will be presented with a form that simulates an onboarding process. Check the checkbox and Click the SignUp button.

Sign Up form

Now you are going through the admin consent flow. In this flow, the app gets provisioned for all the users in one organization. You'll be transferred to the Azure AD portal. Sign in as the administrator and you'd be presented with the following screen to consent on behalf of all users.

Admin Consent

Click Accept to provision a service principal of this app in the tenant. You will be transported back to the app, where your registration will be finalized.

If the app is not provisioned in your tenant by the tenant administrator using the steps laid out above, the sign-up process will result in the following error.

Need admin approval

This step uses the `prompt=admin_consent' option provided in the OAuth 2.0 authorization code grant to provide the administrator an option to consent for the entire tenant.

You can also use the admin consent endpoint to provision the app in the chosen tenant.

Sign in

Once you signed up, you can either click on the Todo tab or the sign in link to gain access to the application. Note that if you are selecting sign in in the same session in which you signed up, you will automatically sign in with the same account you used for signing up.

If you are signing in during a new session, you will be presented with Azure AD's credentials prompt: sign in using an account compatible with the sign up option you chose earlier (the exact same account if you used user consent, any user form the same tenant if you used admin consent).

If you try to sign-in before the tenant administrator has provisioned the app in the tenant using the Sign up link above, you will see the following error.

AADSTS700016, App not found

About The Code

The application is subdivided in three main functional areas:

  1. Common assets
  2. Sign up
  3. Todo editor

Let's briefly list the noteworthy elements in each area. For more details, please refer to the comments in the code.

Common assets

The application relies on models defined in Models/AppModels.cs, stored via entities as described by the context and initializer classes in the DAL folder. The Home controller provides the basis for the main experience, listing all the actions the user can perform and providing conditional UI elements for explicit sign in and sign out (driven by the Account controller).

Sign Up

The sign-up operations are handled by the Onboarding controller. The Sign-up action and corresponding view simulate a simple onboarding experience, which results in an OAuth2 code grant request that triggers the consent flow. The ProcessCode action receives authorization codes from Azure AD and, if they appear valid (see the code comments for details) it creates entries in the application store for the new customer organization/user.

Todo editor

This component is the application proper. Its core resource is the Todo controller, a CRUD editor, which leverages claims and the entity framework to manage a personalized list of Todo items for the currently signed in user. The Todo controller is secured using OpenId Connect, according to the logic in App_Start/Startup.Auth.cs.

Notable code:

     TokenValidationParameters validationParameters = new TokenValidationParameters
            {
                // Since this is a multi-tenant app, you should ideally only accept users from a list of tenants that you want to.
                // * Instead of using the default validation (validating against a single issuer value, as we do in line of business apps), you can inject your own multi-tenant validation logic through a IssuerValidator.
                // * Or you can provide a static list of acceptable tenantIds, as detailed below
                // ValidIssuers = new List<string>()
                // {
                //     "https://sts.windows.net/6d9c0c36-c30e-442b-b60a-ca22d8994d14/",
                //     "https://sts.windows.net/f69b5f46-9a0d-4a5c-9e25-54e42bbbd4c3/",
                //     "https://sts.windows.net/fb674642-8965-493d-beee-2703caa74f9a/"
                // }
                ValidateIssuer = true,
                IssuerValidator = AadIssuerValidator.ValidateAadIssuer
            };

That code assigns a Issuer validator, given that in the case of a multitenant app, the list of acceptable issuer values is dynamic and cannot be acquired via metadata (as it is in the case of a single tenant).

    RedirectToIdentityProvider = (context) =>
    {
       string appBaseUrl = context.Request.Scheme + "://" + context.Request.Host + context.Request.PathBase;
       context.ProtocolMessage.Redirect_Uri = appBaseUrl;
       context.ProtocolMessage.Post_Logout_Redirect_Uri = appBaseUrl;
       return Task.FromResult(0);
    }

That handler for RedirectToIdentityProvider assigns to the Redirect_Uri and Post_Logout_Redirect_Uri (properties used for sign in and sign out locations) URLs that reflect the current address of the application. This assignment allows you to deploy the app to Azure Web Sites or any other location without having to change hardcoded address settings. You do need to add the intended addresses to the Azure AD entry for your application.

Finally: the implementation of SecurityTokenValidated contains the custom caller validation logic, comparing the incoming token with the database of trusted tenants and registered users and interrupting the authentication sequence if a match is not found.

All of the OWIN middleware in this project is created as a part of the open-source Katana project. You can read more about OWIN here.

Create and publish the TodoListWebApp_MT to an Azure Web Site

  1. Sign in to the Azure portal.
  2. Click Create a resource in the top left-hand corner, select Web + Mobile --> Web App, select the hosting plan and region, and give your web site a name, for example, TodoListWebApp_MT-contoso.azurewebsites.net. Click Create Web Site.
  3. Choose SQL Database, click on "Create a new database", enter DefaultConnection as the DB Connection String Name.
  4. Select or create a database server, and enter server login credentials.
  5. Once the web site is created, click on it to manage it. For this set of steps, download the publish profile by clicking Get publish profile and save it. Other deployment mechanisms, such as from source control, can also be used.
  6. Switch to Visual Studio and go to the TodoListWebApp_MT project. Right click on the project in the Solution Explorer and select Publish. Click Import Profile on the bottom bar, and import the publish profile that you downloaded earlier.
  7. Click on Settings and in the Connection tab, update the Destination URL so that it is https, for example https://TodoListWebApp_MT-contoso.azurewebsites.net. Click Next.
  8. On the Settings tab, make sure Enable Organizational Authentication is NOT selected. Click Save. Click on Publish on the main screen.
  9. Visual Studio will publish the project and automatically open a browser to the URL of the project. If you see the default web page of the project, the publication was successful.

Update the Active Directory tenant application registration for TodoListWebApp_MT

  1. Navigate to the Azure portal.
  2. On the top bar, click on your account and under the Directory list, choose the Active Directory tenant containing the TodoListWebApp_MT application.
  3. On the applications tab, select the TodoListWebApp_MT application.
  4. In the Settings | page for your application, update the Logout URL fields with the address of your service, for example https://TodoListWebApp_MT-contoso.azurewebsites.net
  5. From the Settings -> Properties menu, update the Home page URL, to the address of your service, for example https://TodoListWebApp_MT-contoso.azurewebsites.net. Save the configuration.
  6. Add the same URL in the list of values of the Settings -> Reply URLs menu

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 [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.

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 opencode@microsoft.com with any additional questions or comments.

More information

For more information, see the following links:

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