active-directory-aspnetcore-webapp-openidconnect-v2
active-directory-aspnetcore-webapp-openidconnect-v2 copied to clipboard
[Tutorial] Advice sought: Handling expired tokens during an javascript fetch request
Please provide us with the following information:
This issue is for a: (mark with an x)
- [x] request for help
- [ ] bug report -> please search issues before submitting
- [ ] feature request
- [ ] documentation issue or request
- [ ] regression (a behavior that used to work and stopped in a new release)
The issue was found for the following scenario:
Please add an 'x' for the scenario(s) where you found an issue
- [x] Other (please describe)
- [x] Handling the an Expired Access Token during an AJAX based call
- [x] Microsoft.Identity.Web
- [x] Using TokenAcquisition to retrieve a token to be used for calling the graph method checkMemberGroups
- [x] Making a AJAX based call to an ActionMethod requiring Azure AD authorization
- [x] Handling the an Expired Access Token during an AJAX based call
Do you have any advise on how to handle the following situation?
When using the fetch javascript method to call an ActionMethod that requires an AccessToken, if the access token has expired, then a 302 redirect response is received. The redirect to login.microsoftonline.com then appears in the Network traffic with a 200 OK status response. However, the final redirection back to the client app (localhost) fails.
Chrome reports these errors/warnings duing this process
Access to fetch at 'https://login.microsoftonline.com/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx/oauth2/v2.0/authorize?client_id=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx&redirect_uri=https%3A%2F%2Flocalhost%3A44306%2Fsignin-oidc&response_type=code%20id_token&scope=openid%20profile%20offline_access%20User.Read&response_mode=form_post&nonce=12345&state=6789&x-client-SKU=ID_NETSTANDARD2_0&x-client-ver=5.3.0.0' (redirected from 'https://localhost:44306/Home/CheckMembership?groups=3c479d63-xxxx-xxxx-xxxx-9ce22ce90d13') from origin 'https://localhost:44306' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
Cross-Origin Read Blocking (CORB) blocked cross-origin response https://login.microsoftonline.com/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx/oauth2/v2.0/authorize?client_id=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx&redirect_uri=https%3A%2F%2Flocalhost%3A44306%2Fsignin-oidc&response_type=code%20id_token&scope=openid%20profile%20offline_access%20User.Read&response_mode=form_post&nonce=12345&state=6789&x-client-SKU=ID_NETSTANDARD2_0&x-client-ver=5.3.0.0 with MIME type text/html. See https://www.chromestatus.com/feature/5629709824032768 for more details.
The error received in the fetch request contains no details other than "TypeError: Failed to fetch".
Here's the javascript for the fetch
let response = await fetch(`/Home/CheckMembership?groups=${checkGroupIds}`)
.catch(err => console.log({ method: "CheckMembership", error: err }));
I'm guessing that a MsalUIRedirectionException is being thrown under the hood and this needs to be appropriately handled in the fetch method, however, I cannot see how to do it. I have checked various sample projects but none of them make requests via an AJAX style call.
Please let me know if you need more information.
@jmprieur will respond.
cc: @anasimao
@jadjare : did you try to remove the AuthorizeForScopes attribute on the action? you could process the MsalUiRequiredException yourself in the Action?
https://github.com/Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2/blob/f84854eb0c2876afa63edc349c16e1cb3f23f32e/2-WebApp-graph-user/2-1-Call-MSGraph/Controllers/HomeController.cs#L34
cc: @anasimao @jennyf19
@jmprieur : I had a filter on to handle the AuthorizeForScopesAttribute site wide
public class DefaultAuthorizeForScopeFilter: AuthorizeForScopesAttribute
{
public DefaultAuthorizeForScopeFilter(string[] scopes) => base.Scopes = scopes;
}
and in startup.cs
services.AddMvc(options =>
{
options.Filters.Add(new DefaultAuthorizeForScopeFilter(new string[] {"User.Read", "Directory.Read.All"}));
}).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
I have removed this so that so that there shouldn't be any automatic handling of the MsalUiRequiredException.
Now, when the page first loads it authorizes the user and presents my basic sample page, which includes a button to perform the AJAX request. Clicking on the button successfully calls the ActionMethod, acquires an AccessToken, and calls the Microsoft graph. I then invalidate the Access Token. Note, rather than waiting an hour for it to expire I'm able to invalidate it by running the sample web app in "5-WebApp-AuthZ\5-2-Groups" and navigating to the User Profile page. After this step I return to my app and try and make the AJAX request. The request is automatically redirected, it doesn't even hit my Action Method or any breakpoints I've placed in the AuthorizeForScopesAttribute.
Ultimately, even if I could process the MsalUIRequiredException myself, because the call to the Action Method originated from javascript, as an AJAX request, is there actually anything I can do with it without generating a CORS error?
@jadjare What do you mean by "invalidating the token"?
@jadjare What do you mean by "invalidating the token"?
@jennyf19 By "invalidating the token" I mean taking an action that causes a request for an Access Token to be redirected to login.microsoftonline.com/.../oath2.
Normally this happens when you first log in to an application or after an Access Token has expired. I don't have permissions to change the expiry period of the token I'm receiving and can't wait an hour between requests during development/debugging. However, I've found that if I have two different Visual Studio applications running on different ports but with the same AD Tenant Id, Client Id and Client Secret then I can mimic the behaviour. In this situation, if I refresh the page in one application and then go to the other application, when I refresh the page on the other application it redirects to login.microsoftonline.com. If I then return to the 1st application instance and refresh the page the Access Token it originally retrieved is no longer "valid" and it once again redirects to login.microsoftonline.com. I believe under the hood a "MsalUIRequiredException" is occurring, hence the redirect to login.microsoft.com.
@jmprieur Title question is exactly what i am looking for..
Following the tutorials on signin user to org, call graph and distributed token cache I now have most of what I am looking for
services.AddMicrosoftIdentityPlatformAuthentication(Configuration) .AddMsal(Configuration, new string[] { Constants.ScopeUserRead }) .AddDistributedTokenCaches();
Now on my action I want to save using jquery Ajax
[Authorize] public JsonResult Save([FromBody] SomeViewModel data)
If the application user lets the page sit how do I get it to go away and look for a refresh token when needed when the $.ajax({some work}) begins? Currently, after an hour, I get a Cors error when it attemps to go to login.microsoft
Is this in scope for what you can answer here or would I need to ask this elsewhere?
I see this is assigned and just wanted to check if the plan is to include/produce a tutorial that will including handling expired access tokens when making xhr requests from the web app and to get refresh tokens when using authorize policies? (or indeed whatever the correct approach should be)
For the moment, the plan is to understand the problem, and come-up with the best solution, (possibly with an incremental step). Out of curiosity would you also be interested in Blazor?
What I would like to do is 1. A Regular ASP.Net Core Web App in AAD. In a returned View, have sections that have Js/Jquery calls to an Action to bring back some Json to update sections of the page. These Actions will have [Authorize] or [Authorize Policy()] so if the access token has expired I would see how we could use the refresh token (possibly in the db token cache) to restore the access and then update the section of the page where an event happened and make sure if further events had happened during login.microsoft stuff that they were updated as well. (Found stuff in stack that just sends off a standard call every 20mins to keep the token alive. I would prefer not to do that)
-
Again same idea but instead of a contained web app. An API with a client on the server checking the person is authorized for called actions and managing token lifetime and refresh. (ASP.Net Core Client to consume, Some API, all protected in AAD)
-
Seeing how to build this up to a Blazor app would be great as I would like to start this later in the year. 2 and 3 would be a great bonus but need to understand more about using the access and refresh tokens in core, particularly using Javascript/jQuery or perhaps KO to update page sections.
Happy to expand or clarify details. Still have a hard time finding tutorials/recipes for a number of scenarios on ASP.Net Core with Azure and MSAL/ADAL and Calling MS Graph etc. Microsoft.Identity.Web helps a lot but it would be good to see more examples as much of what is can do is not always clear.
N.B Here is an example of keeping the session alive https://stackoverflow.com/questions/1431733/keeping-asp-net-session-open-alive Without this you get a cors message when it goes away to login.microsoft.com A normal action request will allow $.get/post to start working again until the token expires again.
Thanks
From: Jean-Marc Prieur [email protected] Sent: 05 March 2020 15:27 To: Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2 active-directory-aspnetcore-webapp-openidconnect-v2@noreply.github.com Cc: dxm38 [email protected]; Comment [email protected] Subject: Re: [Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2] [Tutorial] Advice sought: Handling expired tokens during an javascript fetch request (#264)
For the moment, the plan is to understand the problem, and come-up with the best solution, (possibly with an incremental step). Out of curiosity would you also be interested in Blazor?
— You are receiving this because you commented. Reply to this email directly, view it on GitHubhttps://github.com/Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2/issues/264?email_source=notifications&email_token=AGMPVZHC5EYOH2DMQBQ4USLRF7AFHA5CNFSM4KIJBVC2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEN5WFCA#issuecomment-595288712, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AGMPVZDZ6GPF44JNF5JTON3RF7AFHANCNFSM4KIJBVCQ.
- A Regular ASP.Net Core Web App in AAD. In a returned View, have sections that have Js/Jquery calls to an Action to bring back some Json to update sections of the page. These Actions will have [Authorize] or [Authorize Policy()] so if the access token has expired I would see how we could use the refresh token (possibly in the db token cache) to restore the access and then update the section of the page where an event happened and make sure if further events had happened during login.microsoft stuff that they were updated as well. (Found stuff in stack that just sends off a standard call every 20mins to keep the token alive. I would prefer not to do that)
@dxm38 's first requirement matches what I'm looking to achieve also.
Out of curiosity would you also be interested in Blazor?
I'm only interested if it's the only way that the problem can be solved. We currently use VueJs and don't intend to move to Blazor in the immediate term.
Thanks for your continued assistance.
Changing this issue to an enhancement to add a new phase to the tutorial cc: @kalyankrishna1 @TiagoBrenck
Changing this issue to an enhancement to add a new phase to the tutorial cc: @kalyankrishna1 @TiagoBrenck
@jmprieur This is great news and thanks for taking the feeback and requests on-board. Looking forward to seeing more on this. Hope you can include all 3 scenarios over time. Cheers
@djmx38 I've been temporarily moved onto some other work, so not looked into this much further. Interesting that you found that old article pointing out the exact same problem years ago! When I originally posted this question I suspected that the refresh of the access token during an Ajax call would simply not be possible, and was simply seeking to confirm that. I didn't expect it to become this bigger thing!
Anyway, in the back of my mind I've been assuming the answer will have to be something along these lines:
- User attempts to access your site 1.1 Request received by the Controller, which redirects due to [Authorize] attribute
- Site redirects to Microsoft Azure AD to login and authenticate and redirects back 2.1 Request is now returned back to Controller "Index" action method
- You'll now have a cookie containing AD details and a newly issued access token
- Before returning the "Index" view, in the action method create your own JWT token. Either by calling the MS Graph or using the details in the cookie, add claims to the JWT token for the relevant permissions.
- Issue your own JWT token back to the client with the rest of the Index page (you can just embed it in some
<script>tags inside the "index" view markup, e.g.let token = "@Html.Raw(Model.MyCustomToken)") - When calling you APIs from the client pass back your own JWT token in the header of the request.
- Configure the application (in
startup.cs) to use your JWT token (e.g. provide details of the JWT secret and add policies that require the claims you used earlier) - Use the
[Authorize]attribute on your API methods utilising the policies linked to your own JWT token's claims.
Note: I'm not near a computer at the moment, so I've written that off the top of my head! Hopefully it makes sense. I can follow up with more details / example code if need be.
Yes, that sounds promising actually. I have found some other stuff as well, so when I get time to try it out and have success, I will get back to you. Cheers
From: jadjare [email protected] Sent: 30 March 2020 00:38 To: Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2 active-directory-aspnetcore-webapp-openidconnect-v2@noreply.github.com Cc: dxm38 [email protected]; Mention [email protected] Subject: Re: [Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2] [Tutorial] Advice sought: Handling expired tokens during an javascript fetch request (#264)
@djmx38https://github.com/djmx38 I've been temporarily moved onto some other work, so not looked into this much further. Interesting that you found that old article pointing out the exact same problem years ago! When I originally posted this question I suspected that the refresh of the access token during an Ajax call would simply not be possible, and was simply seeking to confirm that. I didn't expect it to become this bigger thing!
Anyway, in the back of my mind I've been assuming the answer will have to be something along these lines:
- User attempts to access your site 1.1 Request received by the Controller, which redirects due to [Authorize] attribute
- Site redirects to Microsoft Azure AD to login and authenticate and redirects back 2.1 Request is now returned back to Controller "Index" action method
- You'll now have a cookie containing AD details and a newly issued access token
- Before returning the "Index" view, in the action method create your own JWT token. Either by calling the MS Graph or using the details in the cookie, add claims to the JWT token for the relevant permissions.
- Issue your own JWT token back to the client with the rest of the Index page (you can just embed it in some
- When calling you APIs from the client pass back your own JWT token in the header of the request.
- Configure the application (in startup.cs) to use your JWT token (e.g. provide details of the JWT secret and add policies that require the claims you used earlier)
- Use the [Authorize] attribute on your API methods utilising the policies linked to your own JWT token's claims.
Note: I'm not near a computer at the moment, so I've written that off the top of my head! Hopefully it makes sense. I can follow up with more details / example code if need be.
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHubhttps://github.com/Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2/issues/264#issuecomment-605719594, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AGMPVZFREFVSZ57JLUUR7S3RJ7LVLANCNFSM4KIJBVCQ.
Changing this issue to an enhancement to add a new phase to the tutorial @jmprieur @kalyankrishna1 @TiagoBrenck
Hi, I might have missed it but have there been any updates on the tutotial on how to handled expired tokens with JS when you have logged in using the 2-2-TokenCache or similar example (Mixed C# and Javascript Ajax calls environment)?
As I reminder, what I was looking for, in the first instance, was an enhancement to the 2-2-TokenCache (or depending on whatever is the most convenient 2-1-Call-MSGraph or 4-1-MyOrg) where a user can login with the Identity Platform.
Scenario: Normally, if the user stays on the page for about 30mins and clicks on a new page link you will see the address bar go off to login.microsoft.com to refresh the session. However, if you have a Javascript call on the page to update a select list for example this will fail due to the expired token. Please can you provide the correct approach to use the existing token cache token to carry on working as normal?
Currently, when using Knockout and JS calls, various online sources suggest using a timed Javascript Call to a fake [Authorize] decorated action that keeps the token active. This seems like a poor approach.
Primarily, I want to solve this problem with either a controller in the same project (api or standard) that can handle the AJAX type calls when it has been decorated with [Authorize(Policy ="")] However, this seems like a fairly common use case so would love to see the same when calling a separate decorated authorize API like in tutorial 4-1-MyOrg. Additionally, jmprieur asked if we would like this as a Blazor tutorial. Yes indeed I would. I hope our organization will start using Blazor soon.
I would love to provide more feedback if interested but hoped you might be able to help with this as I would love to stop making fake calls to keep the token active. Many Thanks
@jmprieur @jennyf19 @pmaytak Does Microsoft.Identity.Web got this feature implemented?
Any update on this issue @jmprieur ? I have a web and API application setup using Microsoft.Identity.Web which follows this structure from your examples: https://github.com/Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2/tree/master/4-WebApp-your-API/4-1-MyOrg
My web app is .NET Core MVC. Everything has been working fine until I started attempting to load partial views using the jQuery .load() function. It was working for a while, but then it's no longer working which I believe is due to the token expiring. My situation is essentially exactly how the OP described it. For example, after a complete sign-out/sign-in, the AJAX load functions then succeed again and no redirection is attempted to https://login.microsoftonline.com.
Is there an updated example or guidance for how to resolve this issue? Also, I have just upgraded to use the first GA version (1.0.0) of Microsoft.Identity.Web.
I setup my web app to return a 401 to the AJAX call when a MicrosoftIdentityWebChallengeUserException is thrown. So my goal is to also return the redirect URL (not sure if I need anything else as well) so that the AJAX can catch the 401 and navigate to the redirect URL directly. This should avoid the CORS issue to my knowledge.
As it turns out, after inspecting the source code when the token acquisition methods are called and a MsalUiRequiredException is thrown, this is then caught and a MicrosoftIdentityWebChallengeUserException is thrown. I found this out since I was unable to catch any MsalUiRequiredException exceptions (I even tried to catch the base class), since this exception type is caught and another exception type is thrown.
So, similar to how the AuthorizeForScopes exception handling attribute is setup, I created my own exception handling attribute which on methods which I expect to be called by AJAX I use instead of the AuthorizeForAttributes to avoid redirection and the CORS issue. My exception filter catches the MicrosoftIdentityWebChallengeUserException exception and returns a 401 response. But I'm still needing to process the redirect URL to include this in the response so AJAX can detect the 401, extract the redirect URL, and finally navigate to the appropriate URL. Currently, I'm trying to replicate (to an extent) the HandleException method used in Blazor apps to process the exception. But instead of redirecting, it will just process and create the redirect URL and this will be passed in the 401 response to the client. Are there any currently integrated ways in Microsoft.Identity.Web to accomplish this? Since many of the methods in the HandleException method are internal this means I need to re-construct quite a bit to create the redirect URL and I don't see any public methods I can use. It would be great if there was a method which accepted the exception and returned the appropriate redirect URL. This is essentially what I'm trying to create now. Thoughts?
So, my best work around for making AJAX calls with this library so far is:
- Removing
AuthorizeForScopesattribute on actions which are expected to be called via AJAX. Otherwise, a redirect will occur and a CORS issue will arise. - Catch the MSAL exceptions yourself. If you're calling a method like
GetAccessTokenForUserAsync, aMsalUiRequiredExceptionwill be thrown but this will be caught and aMicrosoftIdentityWebChallengeUserExceptionwill then be thrown instead. So you'll actually need to catch an exception of typeMicrosoftIdentityWebChallengeUserException. This could be done in a number of ways, but the method I'm using is by my own custom exception filter attribute. - After catching the
MicrosoftIdentityWebChallengeUserException, you need to return a response to the client (AJAX call) to inform that the request is forbidden. So, in my case I'm returning a custom response with a brief response message along with a 403 status code. - On client side after retrieving response from AJAX call in callback function, check status codes. If a 403 is returned, then you know you'll need to refresh the user token.
- Assuming you're using
Microsoft.Identity.UI, replace window location with "/MicrosoftIdentity/Account/SignIn". I believe based on reply URLs, this will always redirect you back to the homepage after the user has re-signed in. Therefore, I also included a second window replace directly afterwards to direct back to the page the user was viewing where the MSAL exception occured.
After following these steps, you should have re-signed in the user and be located back on the original page the viewer was viewer. However, I think as a result of the windows replacements you'll lose all state of your page. For example, if the user was filling out a form. I suppose this is a different issue but another thing to fix. Anyways, this is my "solution" for now. I'd greatly appreciate any feedback on more optimal or elegant solutions. I have a feeling it will be really tedious and annoying, but next step I see is to somehow save the state of the page so after the re-sign in and window replacements, the user's state is back to what they expected.
That is awesome and would love to see an implemented repo with your solution :) Thanks for the effort. Much better than timed calls to keep the page alive. Would love to hear from the team how to avoid losing state as well though as much of what I am doing means that operators will take a very long time to fill in the form (nature of the work, not the size of the form)
@OnAzureCloud9 Hi, In your case I am wondering why the refresh token in the cache haven't helped to get you a new access token silently without causing the Msl exception? Refresh token will not expire. I assume you used GetAccessTokenForUserAsync from ITokenAqusition from the Microsoft.Identity.Web. to retrieve the token from the token cache and in your startup.cs you applied the extention method EnableTokenAcquisitionToCallDownstreamApi to setup the user token cache?
@creativebrother I think I have seen that the issue is I have restarted the app many times. I am mainly in local development still, but it's also occurred in Azure as well. But the app has been restarted in Azure as well due to fact that I've been deploying app updates during my development and I assume that restarts the app service as well. Here's how I have configured token cache:
services.AddMicrosoftIdentityWebAppAuthentication(Configuration)
.EnableTokenAcquisitionToCallDownstreamApi(new string[] { Configuration["API:Scope"] })
.AddInMemoryTokenCaches();
I have only done a brief investigation of token cache options. I think there are some options which persist the tokens even beyond app restart? Although once I have finished my app I won't be re-deploying to production very often, it will still occur periodically. So while the alert/redirect to home page to re-sign in the user is not optimal, if it only occurs when the app restarts and token cache is lost that's not the biggest deal. I will do some investigating into the token cache options, but if you have any additional info that would be great.
Edit: Won't this issue occur anyways when token has expired? I thought that is the original OP's issue as well. So while token cache is lost with my in memory option, are you saying this should never occur if the app doesn't restart? I am investigating some options in the Microsoft.Identity.Web Wiki here regarding the token cache options.
Hi, @OnAzureCloud9 I understand OP's original issue.
But you need not manually challenge the user to get an access token simply because it expires, that is refresh token is used for in confidential applications such as web apps.
There are of course cases where token lost due to App restart and app is not using persistent cache, and even app does not lose the cache, user still may facing challenge/redirect for authentication to renew token due to for instance, no refresh token was in the cache, or the user needs to consent (this is my issue), or re-sign-in (for instance if the password expired), or the user needs to perform two factor authentication. But other than those mentioned, and if you used .AddDistributedTokenCaches(); extention method and injected e.g.
services.AddDistributedSqlServerCache(options =>
{
options.ConnectionString = Configuration.GetConnectionString("TokenCacheDbConnStr");
options.SchemaName = "dbo";
options.TableName = "TokenCache";
});
you should only need to acquire access token once to fill in the cache with refresh token in it, the rest of the time MSAL will take care of it using refresh token in the background, until it is invalidated due to aforementioned changes. But this of course still present us the original issue of AJAX CORS, which always exist. But your user will only experience it once, whatever method you implemented is ok. That's what I am trying to say.
@OnAzureCloud9, After I looked again at your previous comments -
My web app is .NET Core MVC. Everything has been working fine until I started attempting to load partial views using the jQuery .load() function. It was working for a while, but then it's no longer working which I believe is due to the token expiring
I realized your may refer to two different scenarios when you mentioned the access token expiring issue. Access token is really for downstream API access, IdToken is you use to create the auth ticket/cookie for your web app authentication.
The AddMicrosoftIdentityWebApp extension method from the Mircosoft.Identity.Web essentially wrapped both cookie and openidconnect schemes under hood.
A) For challenge raised from [Authorization], you may be experiencing authentication cookie (authentication ticket) expiring under the hood since the cookie essentially is created from IdToken and cookie expiration is controlled by half time sliding window mechanism by imbedded cookie authentication scheme.
B) For challenge raised from [AuthorizeForScope] exception, you are experiencing the access token issue.
B) For web app call wep api using Access Tokens, refresh token will take care of the renewal of access token, as I mentioned in the previous comment.
A) But for auth session expiration issue on the web app itself, the framework itself has already handled that in cookieauthentication events for action with Authorization Filter enabled and will optionally emit a 401 instead of 302 redirect for the sake of Ajax...
public Func<RedirectContext<CookieAuthenticationOptions>, Task> OnRedirectToLogin { get; set; } = context =>
{
if (IsAjaxRequest(context.Request))
{
context.Response.Headers[HeaderNames.Location] = context.RedirectUri;
context.Response.StatusCode = 401;
}
else
{
context.Response.Redirect(context.RedirectUri);
}
return Task.CompletedTask;
};
So for case A, you may just need to catch the 401 response and window replace that in ajax callback to avoid the CORS in you ajax error handling but keep the AuthorizeForScope Attribute for access token exception. Edit: This may not be true, it seems to use OpenIdConnect Handler to handle the challenge. Sorry for the confusion.
Of course your second window replacement is still needed to redirect user back to whatever previous page where the trouble causing ajax resides, but not the AJAX calling url itself by default from the challenge, (I am curious how do you obtain that last page url where the AJAX resides?). If you want to save the form data, you may use the state parameter sending to Authroization endpoint and get back, but first you need to somehow find a way to override the Authorize filter to retain the submitted data first.
My case is due to incremental consent for the API access, so if no consent existed, the app is redirecting to Azure Authorization Endpoint which the challenge handler in the core framework emitted a 302, instead of conditionally emit 302 or 401(ajax) thus ended with CORS for my AJAX.
@djmx38 well, you can use Javascript with the library even when integrated with a .NET Core MVC app (such as in my case). However, you will need to handle re-authorization client side to avoid redirection errors (CORS) since the AuthorizeForScopes attribute provided by the library redirects to Microsoft login site.
I am using both standard Ajax as well as Jquery Unobtrusive Ajax for post requests which is working just fine. But it only works without flaw when the user's token is active. When it's expired or invalid and needs to be replaced this requires navigating to the sign in page again. I also don't see any built in support for Ajax re-authorization in the library. You will need to implement a custom solution to handle re-authorization in Ajax requests. In my case, I check whether the request is an Ajax request and if the MsalUIRedirectionException is thrown, I return a 403 to the client instead of using the library's built in redirect functionality to avoid CORS issues. Then I process the 403 client side and redirect the user to the login page. However, with this soliton it will always navigate the user back to the home page after re-authentication. I'm not sure this is very optimal and I would like to see what suggestions the owners of the library have to address this. Using Ajax in this matter is very common and I'm sure many would like to see their recommended examples of how to handle this. I am closely watching this thread.
@OnAzureCloud9 @djmx38, did you try with Microsoft.Identity.Web 1.2.0 ? (not released yet, but is in master) @creativebrother has done a contribution to improve the Ajax case.
(including a sample here: https://github.com/AzureAD/microsoft-identity-web/tree/master/tests/AjaxCallActionsWithDynamicConsent)
@jmprieur I am still using Ms.Id.Web 1.1.0.
I can look into how to try it out. I'm not familiar with using packages that are in a Git repo and not on NuGet, but that is unrelated to your project in specific so I can investigate how to do that. Any ideas when it will be officially released?
I will investigate your sample link provided (it looks new and I hadn't seen it yet). It looks like it directly addresses the AJAX and CORS issue I am facing. Looking forward to testing it out and I will report back results afterwards.
@djmx38 I would check out this example (https://github.com/Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2/issues/264#issuecomment-707263309) that @jmprieur just linked. It's going to be a part of a soon to be release (1.2.0). However, before I was working on the MVC app that I'm working on now I was using Blazor. But in Blazor since it's a stream over SignalR, in a sense it's "kind of like using Ajax", since it's not requiring a full page refresh to update parts of the page. So even with Blazor I had the same data loss issue you are talking about, since even though I wasn't using Ajax at the time and the Microsoft login redirect worked correctly, I still lost page/form data when it redirected back to the page after authentication. I am still looking for a solution for that as well, but I think that's a whole separate issue unrelated to this library.
@javiercn : you might be interested in the comment just above
@creativebrother could you elaborate a bit more on the OpenIdConnect requirements for your pull request to work?
2) in OpenIdConnentHandler, after the redirect url for Identity Provider is calculated, for ajax request, the handler will return a 401 with this redirect url in the header location.
Added a test project to test modified AuthorizeForScopes Attribute to handle Ajax call. It depends on a locally built microsoft.aspnetcore.authentication.openidconnect dev nupkg within which associate change on ajax call redirection is handled differently (OpenIdConnectHandler need to be updated at Asp.net Core side).
I suppose there's a missing piece for me here. It seems that not only does the AuthorizeForScopes attribute need to be updated, but some custom implementation of OpenIdConnect needs to be included? I wasn't seeing that anywhere in the PR so that's where I'm a bit lost.