-
Notifications
You must be signed in to change notification settings - Fork 372
Description
Which Version of MSAL are you using ?*
MSAL.NET 2.4.1-preview
Which platform has the issue?
net4.5, netcore 2.1 (Web)
What authentication flow has the issue?
- Desktop
- Interactive
- Integrated Windows Auth
- Username / Password
- Device code flow (browserless)
- Mobile
- Xamarin.iOS
- Xamarin.Android
- UWP
- Web App
- [ x] Authorization code
- OBO
- Web API
- OBO
- Daemon App
- Client credentials
What is the identity provider ?
- [ x] Azure AD
- Azure AD B2C
Repro
-
Get the code from https://github.com/Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2
-
Checkout to branch
jmprieur/addIncrementalConsent
-
Comment the lines 121-125 in Extensions\TokenAcquisition.cs. These lines are a work around
121 var account = await application.GetAccountAsync(context.Principal.GetMsalAccountId()); 122 if (account!=null) 123 { 124 await application.RemoveAsync(account); 125 } 126 var result = await application.AcquireTokenByAuthorizationCodeAsync(context.ProtocolMessage.Code, scopes.Except(scopesRequestedByMsalNet));
1. Run the Web site under the debugger
1. sign-in
1. Select **Contact**
=> you should see information about youself (from the graph)
1. Put a breakpoint in the HomeController.Contact() method
```CSharp
public async Task<IActionResult> Contact()
{
var scopes = new string[] { "user.read" };
. . .
- In the web site click on contact again.
- Now in edit and continue mode, change the scope to "Mail.Send"
- Continue the debugger
Expected behavior
If the code above is not commented, you'll notice the step-up experience. you are asked for incremental consent (for Mail.Send) and the Contact page is displayed again.
Actual behavior
with the workaround code commented, AcquireTokenByAuthorizationCodeAsync returns from the cache a token that does not have all the scopes, and therefore you have an infinite loop of selecting your account (once you have consented once) because the HomeController.Contact() method attempts to get a token with the new scopes, fails, sends a challenge to get a token with the new scopes, the user selects the account/consents if needed, and then the code is redeemed, but since AcquireTokenByAuthorizationCodeAsync returns the previous token, it does not contain the required claim (Mail.Send), and therefore when controller.Contact() is called, the same thing happens forever until the user is tired ...
Possible Solution
I don't think that AcquireTokenByAuthorizationCodeAsync should even hit the cache. If developers really want to attempt the cache they should call AcquireTokenSilent before AcquireTokenByAuthorizationCodeAsync but I don't see for which scenario.
** Work around for the moment **
remove the account before calling AcquireTokenByAuthorizationCodeAsync
var account = await application.GetAccountAsync(context.Principal.GetMsalAccountId());
if (account!=null)
{
await application.RemoveAsync(account);
}
var result = await application.AcquireTokenByAuthorizationCodeAsync(context.ProtocolMessage.Code, scopes.Except(scopesRequestedByMsalNet));