-
Notifications
You must be signed in to change notification settings - Fork 340
SPA Authorization Code
This flow enables confidential client applications to request an additional "spa auth code" from the eSTS /token endpoint, and this authorization code can be redeemed silently by the front end running in the browser. This feature is intended for applications that perform server-side (web apps) and client-side (SPA) authentication, using a confidential client SDK such as MSAL.net or MSAL Node server-side, and MSAL.js client side (e.g., an ASP.net web application hosting a React single-page application). In these scenarios, the application will likely need authentication both client-side (e.g., a public client using MSAL.js) and server-side (e.g., a confidential client using MSAL.net), and each application context will need to acquire its own tokens.
Today, applications using this architecture will first interactively authenticate the user via the confidential client application, and then attempt to silently authenticate the user a second time with the public client. Unfortunately, this process is both relatively slow, and the silent network request made client-side (in a hidden iframe) will deterministically fail if third-party cookies are disabled/blocked. By acquiring a second authorization code server-side, MSAL.js can skip hidden iframe step, and immediately redeem the authorization code against the /token endpoint. This mitigates issued caused by third-party cookie blocking, and is also more performant
MSAL 4.40+ supports confidential clients to request an additional "spa auth code" from the eSTS / token endpoint
The redirect_uri used to acquire the spa auth code must be of type web.
The authorization code flow begins with the client directing the user to the /authorize
endpoint. In this request, the client requests the openid
, offline_access
, and https://graph.microsoft.com/mail.read
permissions from the user.
// Line breaks for legibility only
https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize?
&response_type=code
&redirect_uri=https://localhost:44320/
&state=12345
&scope=https%3A%2F%2Fgraph.microsoft.com%2Fuser.read
&client_id=639982ad-d26c-40a5-852a-80817e3fdae6
Parameter | Required/optional | Description |
---|---|---|
tenant |
required | The {tenant} value in the path of the request can be used to control who can sign into the application. The allowed values are common , organizations , consumers , and tenant identifiers. For more detail, see protocol basics. Critically, for guest scenarios where you sign a user from one tenant into another tenant, you must provide the tenant identifier to correctly sign them into the resource tenant. |
client_id |
required | The Application (client) ID that the Azure portal – App registrations experience assigned to your app. |
response_type |
required | Must include code for the authorization code flow. Can also include id_token or token if using the hybrid flow. |
redirect_uri |
required | The redirect_uri of your app, where authentication responses can be sent and received by your app. It must exactly match one of the redirect_uris you registered in the portal, except it must be URL-encoded. For native & mobile apps, you should use one of the recommended values - https://login.microsoftonline.com/common/oauth2/nativeclient (for apps using embedded browsers) or http://localhost (for apps that use system browsers). |
scope |
required | A space-separated list of scopes that you want the user to consent to. For the /authorize leg of the request, this can cover multiple resources, allowing your app to get consent for multiple web APIs you want to call. |
state |
recommended | A value included in the request that will also be returned in the token response. It can be a string of any content that you wish. A randomly generated unique value is typically used for preventing cross-site request forgery attacks. The value can also encode information about the user's state in the app before the authentication request occurred, such as the page or view they were on. |
At this point, the user will be asked to enter their credentials and complete the authentication. The Microsoft identity platform will also ensure that the user has consented to the permissions indicated in the scope
query parameter. If the user has not consented to any of those permissions, it will ask the user to consent to the required permissions.
Once the user authenticates and grants consent, the Microsoft identity platform will return a response to your app at the indicated redirect_uri
, using the method specified in the response_mode
parameter.
A successful response using response_mode=query
looks like:
GET https://localhost:44320/
?code=0.ARwAkq1F9o3jGk21ENGwmnSoyq2CmWN
&state=12345
&session_state=a0ae5f98-92b
Parameter | Description |
---|---|
code |
The authorization_code that the app requested. The app can use the authorization code to request an access token for the target resource. Authorization_codes are short lived, typically they expire after about 10 minutes. |
state |
If a state parameter is included in the request, the same value should appear in the response. The app should verify that the state values in the request and response are identical. |
All confidential clients have a choice of using client secrets (symmetric shared secrets generated by the Microsoft identity platform) and certificate credentials(asymmetric keys uploaded by the developer). For best security, we recommend using certificate credentials. Public clients (native applications and single page apps) must not use secrets or certificates when redeeming an authorization code - always ensure that your redirect URIs correctly indicate the type of application and are unique.
Now that you've acquired an authorization_code and have been granted permission by the user, you can redeem the code
for an access_token
to the desired resource. Do this by sending a POST
request to the /token
endpoint:
// Line breaks for legibility only
POST /{tenant}/oauth2/v2.0/token HTTP/1.1
Host: https://login.microsoftonline.com
Content-Type: application/x-www-form-urlencoded
client_id=639982ad-d26c-40a5-852a-80817e3fdae6
&scope=https://graph.microsoft.com/user.read
&code=OAAABAAAAiL9Kn2Z27UubvWFPbm0gLWQJVzCTE9UkP3pSx1aXxUjq3n8b2JRLk4OxVXr...
&redirect_uri=https://localhost:44320/
&grant_type=authorization_code
&client_secret=JqQX2PNo.........
&return_spa_code=1
Parameter | Required/optional | Description |
---|---|---|
tenant |
required | The {tenant} value in the path of the request can be used to control who can sign into the application. The allowed values are common , organizations , consumers , and tenant identifiers. For more detail, see protocol basics. |
client_id |
required | The Application (client) ID that the Azure portal – App registrations page assigned to your app. |
scope |
optional | A space-separated list of scopes. The scopes must all be from a single resource, along with OIDC scopes (profile , openid , email ). For a more detailed explanation of scopes, refer to permissions, consent, and scopes. This is a Microsoft extension to the authorization code flow, intended to allow apps to declare the resource they want the token for during token redemption. |
code |
required | The authorization_code that you acquired in the first leg of the flow. |
redirect_uri |
required | The same redirect_uri value that was used to acquire the authorization_code. |
grant_type |
required | Must be authorization_code for the authorization code flow. |
client_secret |
required for confidential web apps | The application secret that you created in the app registration portal for your app. You shouldn't use the application secret in a native app or single page app because client_secrets can't be reliably stored on devices or web pages. It's required for web apps and web APIs, which have the ability to store the client_secret securely on the server side. Like all parameters discussed here, the client secret must be URL-encoded before being sent, a step usually performed by the SDK. For more information on URI encoding, see the URI Generic Syntax specification. The Basic auth pattern of instead providing credentials in the Authorization header, per RFC 6749 is also supported. |
return_spa_code |
required | This will force eSTS to return the SPA Authorization Code in the response |
A successful response using return_spa_code=1
looks like:
{
"token_type": "Bearer",
"scope": "email openid profile https://graph.microsoft.com/Mail.Read https://graph.microsoft.com/User.Read",
"expires_in": 4558,
"ext_expires_in": 4558,
"access_token": "eyJ0eXAiOiJKV1QiLCJub25jZSI6Ik5jX1NMLWZCajV0T1hPaExic1BWRkFZTHhWbF9td3dqdGRHaGw0Z...............",
"spa_code": "0.ARwAkq1F9o3jGk21ENGwmnSoyq2CmWNs0qVAhSqAgX4................."
}
Parameter | Description |
---|---|
token type |
Security token type. |
scope |
If a state parameter is included in the request, the same value should appear in the response. The app should verify that the state values in the request and response are identical. |
expires_in |
Lifetime of the token. |
ext_expires_in |
Extended Lifetime of the token. |
access_token |
An access token is a security token that's issued by an authorization server as part of an OAuth 2.0 flow. It contains information about the user and the resource for which the token is intended. |
spa_code |
The SPA Authorization Code sent from eSTS. |
First, configure a new PublicClientApplication from MSAL.js in your single-page application:
const msalInstance = new msal.PublicClientApplication({
auth: {
clientId: "{{clientId}}",
redirectUri: "http://localhost:3000/auth/client-redirect",
authority: "{{authority}}"
}
})
Next, render the code that was acquired server-side, and provide it to the acquireTokenByCode API on the MSAL.js PublicClientApplication instance. Be sure to not include any additional scopes that were not included in the first login request, otherwise the user may be prompted for consent.
The application should also render any account hints, as they will be needed for any interactive requests to ensure the same user is used for both requests
const code = "{{code}}";
const loginHint = "{{loginHint}}";
const scopes = [ "user.read" ];
return msalInstance.acquireTokenByCode({
code,
scopes
})
.catch(error => {
if (error instanceof msal.InteractionRequiredAuthError) {
// Use loginHint/sid from server to ensure same user
return msalInstance.loginRedirect({
loginHint,
scopes
})
}
});
Once the Access Token is retrieved using the new MSAL.js acquireTokenByCode
api, the token is then used to read the user's profile
function callMSGraph(endpoint, token, callback) {
const headers = new Headers();
const bearer = `Bearer ${token}`;
headers.append("Authorization", bearer);
const options = {
method: "GET",
headers: headers
};
console.log('request made to Graph API at: ' + new Date().toString());
fetch(endpoint, options)
.then(response => response.json())
.then(response => callback(response, endpoint))
.then(result => {
console.log('Successfully Fetched Data from Graph API:', result);
})
.catch(error => console.log(error))
}
ASP.NET MVC project that uses the SPA Authorization Code in the Front End
- Home
- Why use MSAL.NET
- Is MSAL.NET right for me
- Scenarios
- Register your app with AAD
- Client applications
- Acquiring tokens
- MSAL samples
- Known Issues
- AcquireTokenInteractive
- WAM - the Windows broker
- .NET Core
- Xamarin Docs
- UWP
- Custom Browser
- Applying an AAD B2C policy
- Integrated Windows Authentication for domain or AAD joined machines
- Username / Password
- Device Code Flow for devices without a Web browser
- ADFS support
- Acquiring a token for the app
- Acquiring a token on behalf of a user in Web APIs
- Acquiring a token by authorization code in Web Apps
- High Availability
- Token cache serialization
- Logging
- Exceptions in MSAL
- Provide your own Httpclient and proxy
- Extensibility Points
- Clearing the cache
- Client Credentials Multi-Tenant guidance
- Performance perspectives
- Differences between ADAL.NET and MSAL.NET Apps
- PowerShell support
- Testing apps that use MSAL
- Experimental Features
- Proof of Possession (PoP) tokens
- Using in Azure functions
- Extract info from WWW-Authenticate headers
- SPA Authorization Code