-
Notifications
You must be signed in to change notification settings - Fork 345
Acquiring tokens with authorization codes on web apps
When users login to Web applications (web sites) using Open Id connect, the web application receives an authorization code which it can redeem to acquire a token for Web APIs.
You need to register a Reply URI so that Azure AD gets the authorization code and the token back to your application.
You should also register your application secrets either through the interactive experience in the Azure portal, or using command-line tools (like PowerShell)
The management of client credentials happens in the certificates & secrets page for an application:
- the application secret (also named client secret) is generated by Azure AD during the registration of the confidential client application when you select New client secret. At that point, you must copy the secret string in the clipboard for use in your app, before selecting Save. This string won't be presented any longer.
- the certificate is uploaded in the application registration using the Upload certificate button
The active-directory-dotnetcore-daemon-v2 sample shows how to register an application secret or a certificate with an Azure AD application:
- For details on how to register an application secret, see AppCreationScripts/Configure.ps1
- For details on how to register a certificate with the application, see AppCreationScripts-withCert/Configure.ps1
This flow is only available in the confidential client flow; therefore the protected Web API provides client credentials (client secret or certificate) to the ConfidentialClientApplicationBuilder via the or the WithClientSecret
or WithCertificate
methods respectively.
IConfidentialClientApplication app;
#if !VariationWithCertificateCredentials
app = ConfidentialClientApplicationBuilder.Create(config.ClientId)
.WithClientSecret(config.ClientSecret)
.Build();
#else
// Building the client credentials from a certificate
X509Certificate2 certificate = ReadCertificate(config.CertificateName);
app = ConfidentialClientApplicationBuilder.Create(config.ClientId)
.WithCertificate(certificate)
.Build();
#endif
To redeem an authorization code and get a token, and cache it, the IConfidentialClientApplication
contains a method called
AcquireTokenByAuthorizationCode(
IEnumerable<string> scopes,
string authorizationCode)
The principle is exactly the same for MSAL.NET as for ADAL.NET, and is illustrated in the active-directory-dotnet-webapp-openidconnect-v2 sample, in Startup.Auth.cs, Lines 70 to 87. ASP.NET triggers an authentication code flow because the scopes App_Start/Startup.Auth.cs#L53 contains open_id
Scope = "openid profile offline_access Mail.Read Mail.Send",
and the application subscribes to the notification when the authorization code get received App_Start/Startup.Auth.cs#L67-L72
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthorizationCodeReceived = OnAuthorization,
AuthenticationFailed = OnAuthenticationFailed
}
When this notification is processed it acquires a token from the authorization code by calling AcquireTokenByAuthorizationCode
.
private async Task OnAuthorization(AuthorizationCodeReceivedNotification context)
{
var code = context.Code;
string signedInUserID = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;
TokenCache userTokenCache = new MSALSessionCache(signedInUserID,
context.OwinContext.Environment["System.Web.HttpContextBase"] as HttpContextBase).GetMsalCacheInstance();
var cca = new ConfidentialClientApplication(clientId,
redirectUri,
new ClientCredential(appKey),
userTokenCache,
null);
string[] scopes = { "Mail.Read" };
try
{
// As AcquireTokenByAuthorizationCode is asynchronous we want to tell ASP.NET that
// we are handing the code even if it's not done yet, so that it does not concurrently
// call the Token endpoint itself, otherwise you'll get an error message:
// 'OAuth2 Authorization code was already redeemed' error message
context.HandleCodeRedemption();
// Redeem the code
AuthenticationResult result = await cca.AcquireTokenByAuthorizationCode(scopes, code)
.ExecuteAsync();
// Share the ID Token with ASP.NET, but not the Access Token, otherwise ASP.NET
// middleware could prevent a further call to AcquireTokenByAuthorizationCode to
// really get to AAD in the case of incremental consent (when the Web app requires more scopes)
context.HandleCodeRedemption(null, result.IdToken);
}
catch (Exception eee)
{
}
}
-
The code is usable only once to redeem a token.
AcquireTokenByAuthorizationCode
should not be called several times with the same authorization code (it's explicitly prohibited by the protocol standard spec). If you redeem the code several times (consciously, or because you are not aware that a framework also does it for you), you'll get an error:'invalid_grant', 'AADSTS70002: Error validating credentials. AADSTS54005: OAuth2 Authorization code was already redeemed, please retry with a new valid code or use an existing refresh token
-
In particular, if you are writing an ASP.NET / ASP.NET Core application, this might happen if you don't tell the ASP.NET/Core framework that you have already redeemed the code. For this you need to call
context.HandleCodeRedemption()
part of theAuthorizationCodeReceived
event handler -
Finally, avoid sharing the access token with ASP.NET otherwise this might prevent incremental consent happening correctly (for details see issue #693)
This very operation has the side effect of adding the token to the token cache, and therefore the controllers that will need a token later will be able to acquire a token silently, as does the SendMail() method of the HomeController.cs#L55-L76
For details about the protocol, see v2.0 Protocols - OAuth 2.0 authorization code flow
Sample | Description |
---|---|
active-directory-aspnetcore-webapp-openidconnect-v2 in branch aspnetcore2-2-signInAndCallGraph | Web application that handles sign on via the (AAD V2) unified Azure AD and MSA endpoint, so that users can sign in using both their work/school account or Microsoft account. The sample also shows how to use MSAL to obtain a token for invoking the Microsoft Graph, including how to handle incremental consent. |
active-directory-dotnet-webapp-openidconnect-v2 | Web application that handles sign on via the (AAD V2) unified Azure AD and MSA endpoint, so that users can sign in using both their work/school account or Microsoft account. The sample also shows how to use MSAL to obtain a token for invoking the Microsoft Graph. |
active-directory-dotnet-admin-restricted-scopes-v2 | An ASP.NET MVC application that shows how to use the Azure AD v2.0 endpoint to collect consent for permissions that require administrative consent. |
Vanity URL: https://aka.ms/msal-net-authorization-code
- 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
- Maui Docs
- 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