This article describes how to configure code for a web app that signs in users.
Code snippets in this article and the following are extracted from the ASP.NET web app sample.
You might want to refer to this sample for full implementation details.
Code snippets in this article and the following are extracted from the Node.js web application signing users in sample in MSAL Node.
You might want to refer to this sample for full implementation details.
Web applications that sign in users by using the Microsoft identity platform are configured through configuration files. Those files must specify the following values:
In ASP.NET Core, these settings are located in the appsettings.json file, in the "Microsoft Entra ID" section.
{
"AzureAd": {
"Instance": "https://login.partner.microsoftonline.cn/",
"TenantId": "[Enter the tenantId here]",
// Client ID (application ID) obtained from the Azure portal
"ClientId": "[Enter the Client Id here]",
"CallbackPath": "/signin-oidc",
"SignedOutCallbackPath": "/signout-oidc"
}
}
In ASP.NET Core, another file (properties\launchSettings.json) contains the URL (applicationUrl
) and the TLS/SSL port (sslPort
) for your application and various profiles.
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:3110/",
"sslPort": 44321
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"webApp": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:3110/"
}
}
}
In the Azure portal, the redirect URIs that you register on the Authentication page for your application need to match these URLs. For the two preceding configuration files, they would be https://localhost:44321/signin-oidc
. The reason is that applicationUrl
is http://localhost:3110
, but sslPort
is specified (44321
). CallbackPath
is /signin-oidc
, as defined in appsettings.json
.
In the same way, the sign-out URI would be set to https://localhost:44321/signout-oidc
.
Note
SignedOutCallbackPath should set either to portal or the application to avoid conflict while handling the event.
In ASP.NET, the application is configured through the appsettings.json file, lines 12 to 15.
{
"AzureAd": {
"Instance": "https://login.partner.microsoftonline.cn/",
"TenantId": "[Enter the tenantId here]",
"ClientId": "[Enter the Client Id here]",
}
}
In the Azure portal, the reply URIs that you register on the Authentication page for your application need to match these URLs. That is, they should be https://localhost:44326/
.
In Java, the configuration is located in the application.properties file located under src/main/resources
.
aad.clientId=Enter_the_Application_Id_here
aad.authority=https://login.partner.microsoftonline.cn/Enter_the_Tenant_Info_Here/
aad.secretKey=Enter_the_Client_Secret_Here
aad.redirectUriSignin=http://localhost:8080/msal4jsample/secure/aad
aad.redirectUriGraph=http://localhost:8080/msal4jsample/graph/me
In the Azure portal, the reply URIs that you register on the Authentication page for your application need to match the redirectUri
instances that the application defines. That is, they should be http://localhost:8080/msal4jsample/secure/aad
and http://localhost:8080/msal4jsample/graph/me
.
Here, the configuration parameters reside in .env.dev as environment variables:
CLOUD_INSTANCE="Enter_the_Cloud_Instance_Id_Here" # cloud instance string should end with a trailing slash
TENANT_ID="Enter_the_Tenant_Info_Here"
CLIENT_ID="Enter_the_Application_Id_Here"
CLIENT_SECRET="Enter_the_Client_Secret_Here"
REDIRECT_URI="http://localhost:3000/auth/redirect"
POST_LOGOUT_REDIRECT_URI="http://localhost:3000"
GRAPH_API_ENDPOINT="Enter_the_Graph_Endpoint_Here" # graph api endpoint string should end with a trailing slash
EXPRESS_SESSION_SECRET="Enter_the_Express_Session_Secret_Here"
These parameters are used to create a configuration object in authConfig.js file, which will eventually be used to initialize MSAL Node:
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
require('dotenv').config({ path: '.env.dev' });
/**
* Configuration object to be passed to MSAL instance on creation.
* For a full list of MSAL Node configuration parameters, visit:
* https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-node/docs/configuration.md
*/
const msalConfig = {
auth: {
clientId: process.env.CLIENT_ID, // 'Application (client) ID' of app registration in Azure portal - this value is a GUID
authority: process.env.CLOUD_INSTANCE + process.env.TENANT_ID, // Full directory URL, in the form of https://login.partner.microsoftonline.cn/<tenant>
clientSecret: process.env.CLIENT_SECRET // Client secret generated from the app registration in Azure portal
},
system: {
loggerOptions: {
loggerCallback(loglevel, message, containsPii) {
console.log(message);
},
piiLoggingEnabled: false,
logLevel: 3,
}
}
}
const REDIRECT_URI = process.env.REDIRECT_URI;
const POST_LOGOUT_REDIRECT_URI = process.env.POST_LOGOUT_REDIRECT_URI;
const GRAPH_ME_ENDPOINT = process.env.GRAPH_API_ENDPOINT + "v1.0/me";
module.exports = {
msalConfig,
REDIRECT_URI,
POST_LOGOUT_REDIRECT_URI,
GRAPH_ME_ENDPOINT
};
In the Azure portal, the reply URIs that you register on the Authentication page for your application need to match the redirectUri instances that the application defines (http://localhost:3000/auth/redirect
).
For simplicity in this article, the client secret is stored in the configuration file. In the production app, consider using a key vault or an environment variable. An even better option is to use a certificate.
The configuration parameters are set in .env as environment variables:
# Note: If you are using Azure App Service, go to your app's Configuration,
# and then set the following values into your app's "Application settings".
CLIENT_ID=<client id>
CLIENT_SECRET=<client secret>
# Expects a full tenant id such as "contoso.partner.onmschina.cn", or its GUID
# Or leave it undefined if you are building a multi-tenant app
#TENANT_ID=<tenant id>
Those environment variables are referenced in app_config.py:
import os
# Application (client) ID of app registration
CLIENT_ID = os.getenv("CLIENT_ID")
# Application's generated client secret: never check this into source control!
CLIENT_SECRET = os.getenv("CLIENT_SECRET")
# AUTHORITY = "https://login.partner.microsoftonline.cn/common" # For multi-tenant app
AUTHORITY = f"https://login.partner.microsoftonline.cn/{os.getenv('TENANT_ID', 'common')}"
REDIRECT_PATH = "/getAToken" # Used for forming an absolute URL to your redirect URI.
# The absolute URL must match the redirect URI you set
# in the app's registration in the Azure portal.
ENDPOINT = 'https://microsoftgraph.chinacloudapi.cn/v1.0/users' # This resource requires no admin consent
# You can find the proper permission names from this document
# https://docs.microsoft.com/en-us/graph/permissions-reference
SCOPE = ["User.ReadBasic.All"]
# Tells the Flask-session extension to store sessions in the filesystem
SESSION_TYPE = "filesystem"
# Using the file system will not work in most production systems,
# it's better to use a database-backed session store instead.
The .env file should never be checked into source control, since it contains secrets. The quickstart sample includes a .gitignore file that prevents the .env file from being checked in.
# Environments
.env
The initialization code differences are platform dependent. For ASP.NET Core and ASP.NET, signing in users is delegated to the OpenID Connect middleware. The ASP.NET or ASP.NET Core template generates web applications for the Azure AD v1.0 endpoint. Some configuration is required to adapt them to the Microsoft identity platform.
In ASP.NET Core web apps (and web APIs), the application is protected because you have a Authorize
attribute on the controllers or the controller actions. This attribute checks that the user is authenticated. Prior to the release of .NET 6, the code initialization was in the Startup.cs file. New ASP.NET Core projects with .NET 6 no longer contain a Startup.cs file. Taking its place is the Program.cs file. The rest of this tutorial pertains to .NET 5 or lower.
Note
If you want to start directly with the new ASP.NET Core templates for Microsoft identity platform, that leverage Microsoft.Identity.Web, you can download a preview NuGet package containing project templates for .NET 5.0. Then, once installed, you can directly instantiate ASP.NET Core web applications (MVC or Blazor). See Microsoft.Identity.Web web app project templates for details. This is the simplest approach as it will do all the following steps for you.
If you prefer to start your project with the current default ASP.NET Core web project within Visual Studio or by using dotnet new mvc --auth SingleOrg
or dotnet new webapp --auth SingleOrg
, you'll see code like the following:
services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
.AddAzureAD(options => Configuration.Bind("AzureAd", options));
This code uses the legacy Microsoft.AspNetCore.Authentication.AzureAD.UI NuGet package which is used to create an Azure Active Directory v1.0 application. This article explains how to create a Microsoft identity platform v2.0 application which replaces that code.
Add the Microsoft.Identity.Web and Microsoft.Identity.Web.UI NuGet packages to your project. Remove the Microsoft.AspNetCore.Authentication.AzureAD.UI
NuGet package if it's present.
Update the code in ConfigureServices
so that it uses the AddMicrosoftIdentityWebApp
and AddMicrosoftIdentityUI
methods.
public class Startup
{
...
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(Configuration, "AzureAd");
services.AddRazorPages().AddMvcOptions(options =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
}).AddMicrosoftIdentityUI();
In the Configure
method in Startup.cs, enable authentication with a call to app.UseAuthentication();
and app.MapControllers();
.
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// more code here
app.UseAuthentication();
app.UseAuthorization();
app.MapRazorPages();
app.MapControllers();
// more code here
}
In that code:
The AddMicrosoftIdentityWebApp
extension method is defined in Microsoft.Identity.Web, which;
- Configures options to read the configuration file (here from the "Microsoft Entra ID" section)
- Configures the OpenID Connect options so that the authority is the Microsoft identity platform.
- Validates the issuer of the token.
- Ensures that the claims corresponding to name are mapped from the
preferred_username
claim in the ID token.
In addition to the configuration object, you can specify the name of the configuration section when calling AddMicrosoftIdentityWebApp
. By default, it's AzureAd
.
AddMicrosoftIdentityWebApp
has other parameters for advanced scenarios. For example, tracing OpenID Connect middleware events can help you troubleshoot your web application if authentication doesn't work. Setting the optional parameter subscribeToOpenIdConnectMiddlewareDiagnosticsEvents
to true
will show you how information is processed by the set of ASP.NET Core middleware as it progresses from the HTTP response to the identity of the user in HttpContext.User
.
The AddMicrosoftIdentityUI
extension method is defined in Microsoft.Identity.Web.UI. It provides a default controller to handle sign-in and sign-out.
For more information about how Microsoft.Identity.Web enables you to create web apps, see Web Apps in microsoft-identity-web.
The code related to authentication in an ASP.NET web app and web APIs is located in the App_Start/Startup.Auth.cs file.
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
OwinTokenAcquirerFactory factory = TokenAcquirerFactory.GetDefaultInstance<OwinTokenAcquirerFactory>();
factory.Build();
app.AddMicrosoftIdentityWebApi(factory);
}
The Java sample uses the Spring framework. The application is protected because you implement a filter, which intercepts each HTTP response. In the quickstart for Java web apps, this filter is AuthFilter
in src/main/java/com/microsoft/azure/msalwebsample/AuthFilter.java
.
The filter processes the OAuth 2.0 authorization code flow and checks if the user is authenticated (isAuthenticated()
method). If the user isn't authenticated, it computes the URL of the Microsoft Entra authorization endpoints, and redirects the browser to this URI.
When the response arrives, containing the authorization code, it acquires the token by using MSAL Java. When it finally receives the token from the token endpoint (on the redirect URI), the user is signed in.
For details, see the doFilter()
method in AuthFilter.java.
Note
The code of the doFilter()
is written in a slightly different order, but the flow is the one described.
For details about the authorization code flow that this method triggers, see the Microsoft identity platform and OAuth 2.0 authorization code flow.
The Node sample uses the Express framework. MSAL is initialized in auth route handler:
var express = require('express');
const authProvider = require('../auth/AuthProvider');
const { REDIRECT_URI, POST_LOGOUT_REDIRECT_URI } = require('../authConfig');
const router = express.Router();
router.get('/signin', authProvider.login({
scopes: [],
redirectUri: REDIRECT_URI,
successRedirect: '/'
The Python sample is built with the Flask framework, though other frameworks like Django could be used as well. The Flask app is initialized with the app configuration at the top of app.py:
import identity
import identity.web
import requests
from flask import Flask, redirect, render_template, request, session, url_for
from flask_session import Session
import app_config
app = Flask(__name__)
app.config.from_object(app_config)
Session(app)
Then the code constructs an auth
object using the identity package.
auth = identity.web.Auth(
session=session,
authority=app.config.get("AUTHORITY"),
client_id=app.config["CLIENT_ID"],
client_credential=app.config["CLIENT_SECRET"],
)