A (SharePoint Online) page has an iframe to a .NET MVC application. The MVC application lives in a separate domain. Both SharePoint Online and MVC app use AAD and allow access for the same users (AAD tenant)
Now when a user opens the SharePoint page (in Windows 7) the iframe fails to redirect user to the AAD login page (because this is disallowed - see OAuth not working inside an iframe or https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-23#section-10.13)
After unsuccessful googling I decided to play around with this a bit and came up with something that works but is ugly:
User signs in to SharePoint. The iframe on page is hidden
JavaScript (jquery) on page makes a jsonp call to the MVC app
If the call succeeds we show the iframe and are happy
If the call fails we redirect user to an MVC page (full redirect). We carry the current url in querystring
User authenticates against AAD
MVC page redirects user back to our SharePoint Online page
Now iframe successfully shows content and we are happy, except that the above solution has a lot of code to maintain:
JavaScript redirect logic
jsonp request (error handling not so straightforward. Actually this is timeout based)
extra MVC action to redirect user back
unoptimal ux (some waiting and redirects. Back button would cause trouble)
What would be a better solution or how these things are typically solved?
(another question would be why my home PC seems to show iframe content after only signing in once to SharePoint online)
Related
I have Single Page Application, browser only webapp (javascript, no server side).
The user interacts with the application and sets some variables.
Far after the creation of the main index.html page, when the user wants to upload a file, he needs to authenticate with an external service using oauth2 (using "token / implicit grant").
Once authenticated, the authentication service responds with a redirect, possibly to another page (page2).
What should the page2 do to continue the flow of the program?
Should it redirect back to the original main page (this time, with the access token)?
If so, wouldn't this reload the main page and reset the application and the variables that were set?
Thanks,
Avner
EDIT
Ricardo,
I don't use a specific web platform, just plain javascript.
#charlietfl
I found a general example here that does postMessage between 2 seperate windows and it works ok.
I'm straggling to customize it according to Ricardo's comments, i.e.
make the second window popup
set a separate HTML file or a "noop" route on your SPA as redirect URL (what is a noop route?)
poll for that URL in the popup
I found this example that create the popup in the main page (index.html), set the redirect, and poll in order to close the popup.
In my case I set the redirect URL to a new page (auth.html) that does postMessage with the access token back to the main page (index.html).
If the auth.html page is not openned in the browser, should I still expect to receive a message in the main page index.html ?
From what I read for postMessage to work both pages, index.html, auth.html should be opened in 2 separate tabs in the browser.
You can use a small popup, which will load the necessary oauth2 endpoint, setting a separate HTML file or a "noop" route on your SPA as redirect URL. You may then poll for that URL in the popup, so you know when to close it (user is done with oauth2 flow).
This strategy is used by libraries like vue-authenticate, react-oauth and satellizer.
Also, depending on the specific service your are trying to authenticate against, you can find official libraries and SDKs that will handle the oauth2 flow automatically.
Last but not least, you could also save the current state of you application in browser storage, redirect to the oauth2 endpoint, and point back to your app as redirect URL. When coming back to your app, the specific route should be able to restore the saved data and resume user experience, with the addition of the oauth2 tokens.
I've been using the Azure Active Directory Authentication Library for java
https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-authentication-scenarios#web-application-to-web-api
I have the sample code working (Authenticating a user in my own WebApp using my company's AD Cloud instance)
There's one part I don't understand in the sequence:
The browser loads my application's landing page and user clicks "Sign in with Microsoft"
The browser makes a request to my server, which is redirected to the Microsoft Log In page (https://login.microsoftonline.com/MyTenantId/oauth2/authorize?client_id=...etc)
User logs into their Microsoft account
After processing the log on, the Microsoft server redirects the browser back to a secured uri on my server - as a POST request
How can they do a Redirect with POST? I thought a WebServer (i.e. a Spring webapp) couldn't do this ? Are they doing it client-side (i.e. Javascript?)
Your guess is correct.
They return a small HTML page that includes a form, which is auto-submitted with JavaScript. If you disable JavaScript, you will see it.
I have a Single Page Application (SPA) built with DurandalJS v1.2 that integrates with a payment gateway. The SPA will go off to the payment gateway's page by doing a normal form POST to their page.
Once the user has finished on the payment gateways page, the payment gateway will then do a form POST back to my SPA.
Since it's a POST, I handle it server side in ASP.NET MVC by rendering out a require.js module definition for the various form parameters being sent, and this module is then used in the view model that needs to handle the result.
This is all working fine, I handle the result, and then stay on the same SPA page. This is different to the typical web pattern of Post-Redirect-Get, since the SPA doesn't do any redirects.
The issue now is when pressing the browser refresh button, it's bringing up the browser's retry post confirmation dialog (which I don't want).
Is there a way to avoid this by using JavaScript, or is the PRG pattern the only way?
Note: It appears that changing window.location, document.location, as well as using history.replaceState all do not work (at least in Chrome).
If you can modify the payment gateway, then intercept the form submission (<form onsubmit="...; return false">), process the form fields from JavaScript instead of letting the browser send them to the server.
If you can't modify the payment gateway, then modify the document.location from JavaScript just after the POST back (e.g. by adding a #). If the user presses reload after this, the browser won't ask for confirmation.
I'm building a Single Page Application, read that login page should not be on the same page. Should I have login as a separate html page, or can I have login also in the same page.
If I have login as a different page, depending on the first page that I load should have to redirect to the other in client side.
ie suppose I load the SPA first, and if the user is not logged in, I've to redirect to login page in client side. and suppose I load the login page first, and the user is already logged in, I've to redirect to SPA in client side.
What is the general solution for this problem?
I'd put login and verification in a separate page and then use ajax to make the calls.
User access index-file.
Index-file makes checks with server-side page to see if user is logged in or not.
Page displays content depending on if the json answer from the server-side page was true or false.
Then I'd do the same for logging in.
User provides login information
Checks with server-side file through ajax and json.
Page refreshes if succesful or throws error response if it's not.
The bottom line to my answer is that when creating single-page applications, ajax is the way to go. However, since you havent provided what language you're using, i'm unable to give you a more detailed answer.
Client side single page handling logins and content selection however is very bad practice and should be avoided all-together because of their lack of security (I cant stress this enough) since all elements will be available even to anonymous access and DOM manipulation will enable an unauthorized user to access restricted content. You'll have to use ajax to do backend serverside authorizations - as mentioned, as well as serverside code on the SPA that present different content depending on your authorization status.
The scenario you describe need to have server-side code for selecting what content to present and client-side code together with ajax for implementing features on the SPA.
What you CAN do however, is to - when for example pushing the login button - calling another file with ajax, remove the content of the wrapper div and append the ajax response to that div. Then you'll avoid client side redirections all together.
I am subscribing to Facebook's auth.login event in my ASP.NET MVC web application in order to reload my page to perform my own login process on the server and re-render the page in logged-in mode. I am using the fb:login-button and on the server I am using the C# Facebook SDK to extract Facebook user information.
I have found that if I do an immediate page reload (as almost every example I've found seems to show), the cookies which Facebook sets for my domain do not get sent, but if I introduce a 100ms timeout (possibly less) before reloading, they do. I would expect that the Facebook Javascript SDK should only fire the auth.login event once the cookies have definitely been written (and perhaps re-read back).
Am I doing something wrong?