Skip Navigation

Build a multi-tenant SaaS web application that calls a web API using Azure AD

Last updated: 04-02-2019
Edit on GitHub

This sample shows how to build a multi-tenant .Net MVC web application that uses OpenID Connect to sign up and sign in users from any Azure Active Directory (AD) tenant, using the ASP.Net OpenID Connect OWIN middleware and the Active Directory Authentication Library (ADAL) for .NET. The sample also demonstrates how to leverage the authorization code received at sign in time to invoke the Graph API.

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

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

How To Run This Sample

Getting started is simple! To run this sample you will 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, please 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, so 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/active-directory-dotnet-webapp-webapi-multitenant-openidconnect.git

Step 2: Create an Organizational user account in your Azure Active Directory tenant

If you already have an Organizational user account in your Azure Active Directory tenant that you would like to use for consent and authentication, you can skip to the next step. This sample will not work with a Microsoft account, so 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. You can find instructions to do that here.

If you want to test both the Administrator and User consent flows discussed below, you will want to create two Organizational accounts: one assigned to the "User" role and one assigned to the "Global Administrator" role.

Step 3: Register the sample with your Azure Active Directory tenant

As a first step you'll need to:

  1. Sign in to the Azure portal using either a work or school account or a personal Microsoft 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 (Preview).

Register the service app (TodoListWebApp_MT)

  1. In App registrations (Preview) 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 and personal Microsoft accounts (e.g. Skype, Xbox, Outlook.com).
    • In the Redirect URI (optional) 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 Access tokens and ID tokens as this sample requires the Implicit grant flowto be enabled to sign-in the user, and call an API.
  6. Select Save.
  7. 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. Use the search box if necessary.
    • In the Application permissions section, ensure that the right permissions are checked: User.Read.All
    • Select the Add permissions button

Step 5: Configure the sample to use your Azure Active Directory 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

Note: if you used the setup scripts, the changes below will have been applied for you

  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.

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

This sample shows how to take advantage of the consent framework in Azure AD to enable an application to be multi-tenant aware, which allows authentication by user accounts from any Azure AD tenant. 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 registering the application. A great example of this type of scenario, is an application that needs to allow Office365 user accounts (which are homed in a separate Azure AD) to authenticate and consent access to their Office365 tenant. The simplest way of doing this 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 test users. 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 7: Run the sample

The sample implements two distinct tasks: the onboarding of a new customer (aka: Sign up), and regular sign in & use of the application.

Sign up

  1. Start the application. Click on Sign Up.
  2. You will be presented with a form that simulates an onboarding process. Here you can choose whether you want to follow the "admin consent" flow (the app gets provisioned for all the users in one organization - requires you to sign up using an administrator), or the "user consent" flow (the app gets provisioned for your user only).
  3. Click the SignUp button. You'll be transferred to the Azure AD portal. Sign in as the user you want to use for consenting. 4. If the user is from a tenant that is different from the one where the app was developed, you will be presented with a consent page. Click OK. You will be transported back to the app, where your registration will be finalized. #### 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 doing this 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).

How To Deploy This Sample to Azure

Coming soon.

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 SignUp 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 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 via OpenId Connect, according to the logic in App_Start/Startup.Auth.cs.

Notable code

The following code turns off the default Issuer validation, given that in the multitenant case the list of acceptable issuer values is dynamic and cannot be acquired via metadata (as it is instead the case for the single organization case).

TokenValidationParameters = new System.IdentityModel.Tokens.TokenValidationParameters
{
   ValidateIssuer = false,
}

The following 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 allows you to deploy the app to Azure Web Sites or any other location without having to change hardcoded address settings. Note that you do need to add the intended addresses to the Azure AD entry for your application.

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);
}

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.