I have a javascript web application accessing a WebAPI using XMLHttpRequest (AJAX). The WebAPI (but not the HTML/JS) is secured with OAuth against a specific Office 365 Tenant.
The OAuth authentication code is as follows:
string clientId =GetSetting("ida:ClientId");string aadInstance =GetSetting("ida:AADInstance");string tenantId =GetSetting("ida:TenantId");string authority =String.Format(CultureInfo.InvariantCulture, aadInstance, tenantId);
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(newCookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(newOpenIdConnectAuthenticationOptions{ClientId= clientId,Authority= authority,PostLogoutRedirectUri="https://www.microsoft.com/",Notifications=newOpenIdConnectAuthenticationNotifications{AuthenticationFailed= context =>{
context.HandleResponse();
context.Response.Redirect("/Error?message="+ context.Exception.Message);returnTask.FromResult(0);}}});
In all my tests in the test tenant, if I access the app without a valid token, the XHR send
throws
an error in my browser, which I catch and change the browser window content to a controller, like this:
try{
xhr.send(null);catch(ex){
window.location = APIURI+'OAuthLogin';}
The OAuthLogin controller will only assure the user's logged in, otherwise forward to the login page, and once logged in, just redirects to the javascript application:
publicclassOAuthLoginController:ApiController{[HttpGet]publicHttpResponseMessageLogin(){var baseUri =HttpContext.Current.Request.Url.CutAtAPI();var query =HttpContext.Current.Request.Url.Query;var response =Request.CreateResponse(HttpStatusCode.Moved);
response.Headers.Location=newUri(baseUri +"/"+ query);return response;}}
In my test tenant, this works completely as expected:
- I call the controller via XHR.
- The controller tries to redirect and throws an exception.
- My code navigates the browser window to OAuthLogin controller.
- The forward of the unauthenticated user to login.microsoftonline works
- The user is logged in and redirected back to the the OAuthLogin controller
- The controller enforces its redirect back to the application.
So I have deployed in a production tenant, and at first, everything ran as it should. After some days, it stopped working, the problem exhibiting as follows:
- The XHR status is
(Aborted)
, but no exception is thrown. - In the network tab, I see that the microsoft login page is called, but it's not in
window.location
. - The microsoft login page seems to do a successful auto-login with the info still available in the browsercache.
- The login page returns some cookies and HTML containing a form submission. This HTML shows up in the XHR result, where the applications expects JSON, but since receives HTML, it throws up.
If I then completely reload the application again, the same procedure is tried (and fails) again; not sure why that is.
The HTML returned by the microsoft login page contains a form containing among others an access token and an autosubmit, something like:
<html><head><title>Working...</title></head><body><form action="...<noscript>Scripting is disabled in your browser. Please click submit to proceed: <input type="submit" text="Submit"></noscript></form><script type="text/javascript">document.forms[0].submit();</script></html>
What am I doing wrong here?