Say Goodbye to the OAuth implicit flow with MSAL 2
TL;DR If you want to skip the blurbs, just go straight to steps to reproduce or checkout the code on GitHub.
Authentication in SPAs using OAuth 2 authorisation code flow with MSAL.js 2.x and consuming a .NET Core 3.1 API
This post provides the steps to secure your ASP.NET core API using OAuth2, authenticating a Single Page App against Azure AD using Microsoft Authentication Library for JavaScript (MSAL.js) 2.x and the authorisation code flow with PKCE and calling the API with an access token.
MSAL 1.x, Implicit Flow and Security Considerations
Authentication in single page apps using v1.x involves the use of the implicit flow. The OAuth security best practices have been updated and advise against using the implicit flow due to a few vulnerabilities most of which result from the fact that the access token is part of the return URL (and therefore could be intercepted before arriving in the app it is intended for).
Instead, the authorisation code flow is recommended.
MSAL 2, Authorisation Code Flow and Azure AD
Using MSAL.js 2.x, SPAs can now authenticate against Azure AD using the authorisation code flow (Azure AD B2C was not supported at the time this blog was written). Let’s get started…
Steps to reproduce
- Prerequisites
- An app registration in AzureAD, set up for authentication with the platform ‘Single-page Application’ as described here.
- What I used:
- Windows 10
- Visual Studio 2019
- dotnet sdk v3.1.401
- Commit 1: Creating an ASP.NET Core 3.1 web app with React
- File->New project
- ASP.NET Core Web Application (name it i.e. “Msal2”) -> Create
- Choose .NET Core 3.1, React.js (the auth code won’t be React-specific)
- Choose ‘No Authentication’
- Create (the app will be scaffolded)
- Commit 2: Let’s modify
Home.js
to fetch and display today’s weather forecast. Authetication is not yet enabled in the API so we can get away with an unauthenticated call - here is the link to the complete Home.js file for this step. The App will now display tomorrow’s weather.//... fetchWeather() { fetch("/weatherforecast").then(response => { response.json().then(result => { this.setState(result); }) }); } //...
- Commit 3: Secure the API by adding middleware to handle OpenID Connect Bearer token.
- Install nuget package
Microsoft.AspNetCore.Authentication.JwtBearer
-
Add the following lines to the beginning of the
Startup.ConfigureServices
method:services.AddAuthentication(options => { options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(jwtOptions => { var instance = Configuration["AzureAd:Instance"]; var domain = Configuration["AzureAd:Domain"]; jwtOptions.Authority = $"{instance}/{domain}/v2.0/"; jwtOptions.Audience = Configuration["AzureAd:ClientId"]; });
- Add the following lines to the
Startup.Configure
method to configure the pipeline to use the auth middleware:app.UseAuthentication(); app.UseAuthorization();
- Open
appsettings.json
and replace the values forTenantId
,ClientId
andDomain
with valid values from your Azure AD app registration. - Finally, we add the
[Authorize]
attribute to the weather forecast endpoint. - Now the
fetch('/weatherforecast')
call will result in a 401 (Unauthorized) response due to a missing valid JWT.
- Install nuget package
- Commit 4: Finally, we add the package
@azure/msal-browser
to our client code and acquire an access token from Azure AD that we then use to call the API.- Install msal-browser by typing
npm install --save @azure/msal-browser
- The call to
myMSALObj.loginRedirect(loginRequest)
will request an authorisation code (response_type: code
) which then will be used to request an access token. - We then use that access token to call the API:
// ... auth.getToken().then(accessToken => { fetch("/weatherforecast", { headers: { Authorization: `Bearer ${accessToken}` } }).then(response => { response.json().then(result => { this.setState(result); }) }); }); // ...
- Install msal-browser by typing
Conclusion
MSAL.js 2.x supports the authorisation code flow with PKCE as opposed to MSAL.js 1.x (and ADAL.js), which uses the implicit flow. With the updates OAuth security best practices advising against using the implicit flow it’s a good idea to choose MSAL.js 2.x over version 1.x for new projects and update existing projects to use the most recent version.