Closed keggster101020 closed 3 years ago
I'm working on the same service with @keggster101020 and want to provide a screenshot of the form in the response when we make the prompt=none
request in an <iframe>
.
Since we specified prompt=none
and on the request in an <iframe>
, we were hoping we can silently handle the error callback in the frame instead of navigating the top window.
@keggster101020, @jeffreyrivor. I'm a bit confused. Is this an issue with Microsoft.Identity.Web ? which kind of application are you building? a web app or a single page application?
@jmprieur I don't think it is an issue with Microsoft.Identity.Web, but I'm also not sure where to send these kinds of issues for AzureAD. We're making an ASP.NET Core web app that uses the OIDC and OAuth2 flow for id_token + code with a POST callback. We want to passively authenticate users using an iframe with a challenge to AAD with prompt=none
specified, but the expected error that is returned when there is no user logged into AAD breaks out of the iframe.
@jeffreyrivor can you send no prompt at all, so no value for prompt?
@jennyf19 I can't request without prompt=none
because that would generate a response from AzureAD with X-Frame-Options: DENY
since login prompts are not allowed in iframes.
@jmprieur I believe this is a regression between AADv1 and Microsoft Identity Platform (AADv2) because I just looked at a v1 app and the form response for prompt=none
does not force a target="_top"
.
@jeffreyrivor - are you running this within a sandboxed iframe by chance? We've filed an incident internally to investigate, as you're correct to call out that this should not be occurring.
Thanks @hpsin ...
@jeffreyrivor going to close, as this is not related to identity web, but will update here when we have information about the incident. Thanks for letting us know.
cc: @jmprieur
@hpsin We were planning to add the sandbox parameters to the iframe as a workaround, but I figured that it was worth bringing up since v1 apps don't have the same problem. Do you have a suggestion on the set of values that would need to be set if we have to go that route?
Unfortunately the error still won't make it back to you even with sandboxing, due to our incorrect navigationin respone to interaction_required errors. We wanted to make sure that sandboxing wasn't triggering this. Sandboxing won't really help here, I'm afraid - it'll preserve your app, but you'll need some way to watchdog the iframe to ensure that it hasn't gotten lodged on this error.
Seeing similar behavior in SPA app that is using iFrame to refresh authentication token. AAD login page seems to want to bust out and redirect the main app window in some browsers.
let iframe = document.createElement("iframe");
iframe.id = "authIFrame";
iframe.style =
"width: 0; height: 0; border: 0; border: none; position: absolute; visibility: hidden;";
iframe.src = `/.auth/login/aad?prompt=none&domain_hint=<fakeappdomain.com>`;
document.body.appendChild(iframe);
Chrome 90: Properly stays within iframe, successfully refreshing token Firefox 87: AAD login page busts out of iFrame, redirecting parent window. Safari 14.1: Break/hangs with
Unsafe JavaScript attempt to initiate navigation for frame with URL 'https://fakeappdomain.azurewebsites.net' from frame with URL 'https://login.microsoftonline.com/common/oauth2/authorize?response_type=id_token&redirect_uri=https%3A%2F%2Ffakeappdomain.azurewebsites.net%2F.auth%2Flogin%2Faad%2Fcallback&client_id=fake-fake-fake-fake-fake&scope=openid+profile+email&response_mode=form_post&domain_hint=fakeappdomain.com&prompt=none&nonce=blahblahfake&state=redir%3D'. The frame attempting navigation of the top-level window is cross-origin or untrusted and the user has never interacted with the frame.
Troubleshooting further.
allow-top-navigation
//Inject iFrame that will call endpoint to refresh token/cookie
console.log("Refreshing auth token...");
try {
let iframe = document.createElement("iframe");
iframe.id = "authIFrame";
iframe.style ="width: 0; height: 0; border: 0; border: none; position: absolute; visibility: hidden;";
iframe.sandbox = "allow-same-origin allow-forms allow-scripts allow-popups"; //Block allow-top-navigation
iframe.src = `/.auth/login/aad?prompt=none&domain_hint=fakedomain.com`;
document.body.appendChild(iframe);
} catch (error) {
console.log("Caught error", error);
resolve();
}
try/catch was not able to catch the Unsafe Javascript error with the iframe. Didn't think it would but if someone knows how to do it would be helpful for handling the iframe errors (now and in the future).
Chrome 90: Works properly, token refreshed (it worked previously as well)
Firefox 88: With sandbox parameters, busting out of iframe contained, BUT token not refreshed.
An error (Uncaught DOMException: The operation is insecure.
) was logged in the console pointing to code line 85 of https://login.microsoftonline.com/common/oauth2/authorize
!function(){var e=window,o=e.document,i=e.$Config||{};if(e.self===e.top){o&&o.body&&(o.body.style.display="block")}else if(!i.allowFrame){var s=e.self.location.href,l=s.indexOf("#"),n=-1!==l,t=s.indexOf("?"),f=n?l:s.length,d=-1===t||n&&t>l?"?":"&";s=s.substr(0,f)+d+"iframe-request-id="+i.sessionId+s.substr(f),e.top.location=s}}();
Grabbing the URL from the message and opening it in a new firefox tab -> "You've successfully signed in". Odd because I was expecting an error (as below in safari) - if its working why is it trying to navigate parent window....
Safari 14.01: Breaking but in different manner
Unsafe JavaScript attempt to initiate navigation[...]
is logged, but now ends with The frame attempting navigation of the top-level window is sandboxed, but the 'allow-top-navigation' flag is not set.
. To attempt handling the error I wrapped the iframe creation in try/catch block but that didn't work.https://login.microsoftonline.com/common/oauth2/authorize?response_type=id_token&redirect_uri=https%3A%2F%2Ffakeapp.azurewebsites.net%2F.auth%2Flogin%2Faad%2Fcallback&client_id=fake&scope=openid+profile+email&response_mode=form_post&domain_hint=fakedomain.com&prompt=none&nonce=blahblahfake&state=redir%3D
Resulted in an error that doesn't make sense:
Request Id: b6791602-aa28-snipped Correlation Id: afa4927d-3bbc-snipped Timestamp: 2021-04-30T00:06:11Z Message: AADSTS50059: No tenant-identifying information found in either the request or implied by any provided credentials.
tenant-identifying information found
) is being thrown is a different question - there is some talk of Safari discarding cookies in an iframe, which may explain the why AAD login page seems to be telling me that it doesn't know which tenant because it doesn't have a cookie to refresh. Should the iframe src url provide more than just a domain hint?What I'm ultimately trying to do is refresh the token/session of an SPA hosted behind Azure Web App Authentication (also sometimes called "EasyAuth"). Initial session launches fine, but once the token expires (~1 hour, 5 minutes in) app starts throwing 401s.
Ensure app state is stored. Add mechanism to reload/rehydrate app state (e.g querystring restoreState=true
)
post_login_redirect_url
param (maybe/probably a URL encoding issue). I ended up instead setting a flag in localStorage and resulting redirect ended up being: window.location.replace("/.auth/login/aad?post_login_redirect_url=/")
Check browser:
post_login_redirect_url
parameter that should redirect on login:
/.auth/login/aad?post_login_redirect_url=/
App should now successfully refresh token in all cases.
The experience is clearly nicer for chromium based browser, bit more ugly for iOS users but hey, SPA PWA should function now. Not particularly proud of it but something something "perfect enemy of good". It would be nice if cross browser implementation of silent updating of token could be had in this scenario.
refreshAuthToken() {
//Chrome based browsers work with silent iFrame based token reAuth
if (this.browserChromium()) {
//Remove existing iframe (if exists) #trick to not fill up history/back button
let existingFrame = document.getElementById("authIFrame");
if (existingFrame) {
existingFrame.remove();
}
//Inject iFrame that will call endpoint to refresh token/cookie
console.log("Refreshing auth token (quietly)...");
let iframe = document.createElement("iframe");
iframe.id = "authIFrame";
iframe.style =
"width: 0; height: 0; border: 0; border: none; position: absolute; visibility: hidden;";
iframe.src = `/.auth/login/aad?prompt=none&domain_hint=fakedomain.com`;
document.body.appendChild(iframe);
new Promise(r => setTimeout(r, 2000)); //Hacky method of "waiting" for iframe to finish
} else {
console.log("Refreshing auth token (via page reload)...");
window.location.replace("/.auth/login/aad?post_login_redirect_url=/");
}
}
Update here - this is an unfixed bug in the server side, that is still being worked on. All apologies, and we'll update here when it's fixed.
Which version of Microsoft Identity Web are you using? Using AAD v2 and ASP.NET Core 3.1
Nuget packages and versions:
Where is the issue?
Is this a new or an existing app? New experience and testing experience
Repro I'm calling an auth API on my service by pointing an iframe to an API on my service which calls
Challenge()
withprompt=none
Whenever there is a remote login failure, the identity server responds with a form post and the target attribute is set to_top
which is causing the redirect to bust out of the iframe.This is separate from the successful login flow, which remains contained in the iframe.
Expected behavior All redirects are contained within the iframe.
Actual behavior Identity server errors are busting out of the iframe and are redirecting the parent window.
Possible solution Alter the response form post target away from
_top
Additional context / logs / screenshots Add any other context about the problem here, such as logs and screenshots.