-
Notifications
You must be signed in to change notification settings - Fork 217
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Question] How to handle the MsalUiRequiredException for incremental consent with AJAX calls #603
Comments
Is the [AuthorizeForScopes(Scopes = new[] { "https://ccbcc.sharepoint.com/AllSites.Read" })] |
@creativebrother : the AuthorizeForScopes attribute is explained here: https://github.com/AzureAD/microsoft-identity-web/wiki/Managing-incremental-consent-and-conditional-access It's an exception handling attribute (it handles the MsalUiRequiredException) You can also place this attribute on the controller/page if all the actions require consent for the same scopes |
Hi, jmprieur Chrome error before remove X-Requested-With header : Chrome error after remove X-Requested-With header : |
@creativebrother did you have a look at this article: Enable Cross-Origin Requests (CORS) in ASP.NET Core ? |
@jmprieur Yes I did. I tried and actually it did not work. Then I realized that I am solving a different problem. [AuthorizeForScope(Scope="xxx")] is good for redirect to the prompt page for user consent when user click on the action link. It seems [AuthorizeForScope(Scope="xxx")] need to return 200 and some custom header instead of 302 if the request header contains X-Requested-With: XMLHttpRequest so ajax has a chance to check the success result and issue a reload if there is the custom header indicating redirect url for consent prompt? Please let me know if I misunderstood anything. Thanks, |
@jmprieur In the mean time, I have to create another controller action without the [AuthorizeForScope(Scope="xxx")] for the ajax call and return the a 200 with custom data containg redirect url by using ConfidentialClientApplicationBuilder.CreateWithApplicationOptions to construct the authorization endpoint url so the ajax can use the window.location to bring up the consent prompt... |
@jmprieur I did the following and now my ajax call is working(kind of ...). Basically I need to remove the [AuthorizeForScopes] attribute. If the [AuthorizeForScopes] attribute can incorporate this feature would be great.
|
Hi, @jmprieur After looked at [authorize] source code and [authorizeforscopes] source code /// A delegate assigned to this property will be invoked when the related method is called. /// public Func<RedirectContext, Task> OnRedirectToReturnUrl { get; set; } = context => { if (IsAjaxRequest(context.Request)) { context.Response.Headers["Location"] = context.RedirectUri; } else { context.Response.Redirect(context.RedirectUri); } return Task.CompletedTask; };
in AuthorizeForScopeAttribute:
|
@jmprieur Or is it possible to do it in the Startup.cs
|
@creativebrother if this works for you, this looks good for me, but I'm not an expert |
Cookie auth already does that by default, not sure why you needed to modify it. |
@Tratcher Hi, Even thought it is using cookie authentication, but it is not behaving like it does when the ChallengeResult rendered the redirect result. It is still producing 302 instead of 401. When my ajax call the action method decorated with [AuthorizeForScopes(Scopes = new[] { "https://ccbcc.sharepoint.com/AllSites.Read" })], its response is NOT 401 but is still a 302 redirect(ChallengeResult) that caused the CORS issue because ajax is silently redirecting to Azure Authorization EndPoint. Is it possible in the AuthorizeForScopesAttribute to return ChallengeResult(302) or UnauthorizedObjectResult(/Here goes with the redirectUrl for Authorization URL/) based on whether the request is ajax or not ? I did try this, but can not get a redirect authorization url exactly like from login redirect is creating. I tried modify the attribute this way at the end of the AuthorizeForScopesAttribute class:
it does redirect to the authorization endpoint, can scope prompt and everything, but on the call back, I got this error: Exception: An error was encountered while handling the remote login. |
@Tratche Actually the problem I am facing is as simple as on web app page using Microsoft.Identity.Web 0.4.0-preview, an ajax call the action method decorated with AuthorizeForScopesAttribute will cause CORS issue with Azure Authorization endpoint because AuthorizeForScopesAttribute is not producing 401 but 302 result because of OpenId Authentication scheme. If challenge with cookie scheme, it will produce 401 as @Tratcher mentioned. But it is not useful in this case because we want to challenging OpenId Authentication with dynamic scopes to prompt user to consent. Is there a way to let OpenId scheme authentication handler to emit 401 if it is ajax request, similar to cookie scheme is doing? |
I have altered the AuthorizeForScopesAttribute a bit to let it return a cookie based ChallengeResult within the OnException override Now the ajax call will get 401 instead of 302 from OpenIdConnectDefaults.AuthenticationScheme as @Tratcher have suggested. 1 The response header gives |
@creativebrother : to enable the MicrosoftIdentity area, you need to add the 'Microsoft.Identity.Web.UI' NuGet package, and enable controllers in the startup.cs: For instance if you are using Razor pages: services.AddRazorPages().AddMvcOptions(options =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
}).AddMicrosoftIdentityUI(); |
@creativebrother any luck with your AJAX calls after steps taken in this thread? I am facing the same issues. |
@OnAzureCloud9 I tried to do the following in startup.cs and it solved the AJAX issue similar as Cookie Authentication scheme mentioned earlier by @Tratcher. Basically it detect AJAX request and return 401 in Response and in Header Location parameter pass the incremental consent page to get Auth Code. The AJAX has a chance to redirect the browser window by window.location = authcode url and avoid CORS errors.
But I have another problem after I introduced this OnRedirectToIdentityProvider OpenIdConnectEvent, which is now causing an infinite loop on this authcode page similar to #573, #531. It seems that the it is interfering with the following method I just upgraded the Microsoft.Identity.Web to the 1.0.0, will look if there is any luck. For my project to move ahead, I just admin consent them all and removed the incremental consent, sadly. |
@OnAzureCloud9 @Tratcher @jmprieur
|
@Tratcher After looking at the source code in OpenIdConnectHandler -> HandleChallengeAsync-> HandleChallengeAsyncInternal |
@creativebrother how are you handling the AJAX call client side after getting a 401? I setup a similar custom solution but instead I'm returning a 403 (not sure if 401 or 403 is more appropriate here, but really we're doing the same thing). On client side in the AJAX callback function, I check if I get an error and a 403. Then, I'm changing the window location direction to "/MicrosoftIdentity/Account/SignIn" (from the Can you explain how you are handling this client side? Are you able to redirect back to the original page the user was viewing before an MSAL exception occurred from an AJAX call? Are you able to save any state (such as if the user was filling out a form) on the page as well? I'm not happy with forcing the user back to the login screen every time the token needs to be refreshed, but at least I'm able to get my AJAX calls working now. |
@creativebrother. Thanks for sharing your learnings. |
@jmprieur Sure! Will do that. |
@OnAzureCloud9 For the state info like form data I would think Tempdata, but it perhaps won't work since there are two redirects here. But you can embed your custom data in the OpenIdMessage's state property as exiting auth properties like redirect url etc, since it will be protected when sending to authorize endpoint and on the way back. Specifically I just add a new dictionary item on the AuthenticationProperties.Items and you get it back after authentication. |
@creativebrother thanks for your contribution on this! |
cc: @OnAzureCloud9 |
Thanks for the tag @jmprieur. I will try out the 1.2.0 release this weekend with AJAX calls. Do you know if the 1.2.0 release still requires some custom implementation of OpenIdConnect? @creativebrother described that in this post. |
No, it shouldn't @OnAzureCloud9 : the startup.cs is pretty standard now.
|
@jmprieur great to hear. I will try this out this weekend and report back some results. |
@jmprieur looks like we still need to add some custom options like @creativebrother showed here? #603 (comment) I updated my started based on the format linked above with Open ID Connection defaults. When |
Looks like I'm having better luck after adding the After adding the To get redirect working properly, I had to update this to use the HTTP context request and fetch the
|
Which version of Microsoft Identity Web are you using?
v0.4.0-preview
Where is the issue?
Is this a new or an existing app?
c. This is a new app or an experiment.
Repro
Expected behavior
Expect the incremental consent prompt to come up when call the controller.
Actual behavior
When call the tokenAcquisition.GetAccessTokenForUserAsync(new[] { resource }) they exception is thrown: IDW10502
An MsalUiRequiredException was thrown due to a challenge for the user.
Inner Exeption:
AADSTS65001: The user or administrator has not consented to use the application with ID 'xxx' named 'xxxx'. Send an interactive authorization request for this user and resource.
Possible solution
Additional context / logs / screenshots
This code is trying to access sharepoint api with incremental scope.
The initial scope cause the initial consent prompt during authentication:
Cleared all the Permissions prior:
After initial consent:
The text was updated successfully, but these errors were encountered: