My React app uses Firebase Authentication and the Real-Time Database. I now need to access 3rd party services using OAuth 2.0. To do this securely, I am using the OAuth 2.0 Authorization Code Flow with Firebase Functions as my back-end:
React app gets an Authorization Code from the 3rd party.
Authorization code is passed to a Firebase function to get an access token.
But this is where I am stuck. How do I save the access token in the backend so that the React app can access the 3rd party resources? I don't want to store the access token in the React app because that's not secure.
How do I maintain a session in the back-end using Firebase Functions?
Should the session be tied to the React app instance or the Firebase user? The former approach would require a session cleanup. The latter has the advantage that new access tokens can be obtained using a refresh token. This way the user never has to login to the 3rd party again!
After trying bunch of different approaches, I ended up storing the access token & the refresh token in the back-end, both tied to the firebase user. The tokens are only accessible through firebase functions. This approach has the following advantages:
Secure
I don't have to worry about sessions and session cleanup.
I can refresh the access token whenever I want using the refresh token.
OVERALL FACTORS
Typically these are the areas that your SPA Security choices influence, and there are trade offs:
Type of App
Strong Security
Hosting and Global Performance
Technical Simplicity
IN ALL CASES
Your app should:
Maintain a token for each user separately
You will want to identify users from tokens after login
OPTION 1: FRONT END MODEL / COOKIELESS
This involves using tokens in the browser and has these benefits:
Back end is static content only
Web resources can be deployed close to users via a CDN
You can use the OIDC Client library to manage security
This is widely used and should only be exploitable if your app has cross site scripting vulnerabilities. Some resources of mine:
SPA Goals
In Memory Authentication Code
Code Sample Write Up
OPTION 2: BACK END MODEL / AUTH COOKIES
This involves proxying via a back end and carrying tokens around in authentication cookies. I would use this model if developing an online banking app but for medium security apps it can add a lot of overhead. You may have to write more security code and it is possible to end up with a less secure solution than option 1:
Requires a proxy back end that runs code
The back end code may need to be clustered and globally distributed, to achieve good performance
You need to protect the auth cookie and deal with cross site request forgery risks
Out of interest the respected mod_auth_openidc library can be helpful for this type of solution.
BEST OF BOTH WORLDS
If using tokens in a browser is deemed unacceptable I would aim to follow option 1 as much as possible. Then implement option 2 as an additive step:
Deploy web static content separately to the code that does back end token management
Continue to use the OIDC Client library, and make the Authorization Code Exchange call your back end
The only difference to option 1 is that tokens are not available to browser code
Related
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 am trying Firebase to authenticate users for a website that was initially built on Flask (using the flask login workflow with a postgres DB). However, I am not sure that I have a correct understanding of what would be considered best practices when using Firebase.
I read through this article, which I think has led me down a suboptimal path when it comes to actually managing users.
My questions are:
Should all the Firebase authentication be handled in the javascript?
If so, should I use the request.headers on the backend to verify the identity of the user?
Any tutorials (aside from the Firenotes one, which I am working through) much appreciated.
Should all the Firebase authentication be handled in the javascript?
No, it doesn't have to be JavaScript. But in general, you'll find that most apps using one of the existing Firebase Authentication providers handle the sign-in of the user in their client-side code, with calls to the authentication server.
If so, should I use the request.headers on the backend to verify the identity of the user?
When calling REST APIs Firebase itself passes the ID token of the authenticated user in the Authorization header, so that's a valid approach indeed. On the server you can then verify that the ID token is valid, and decide what data this user has access to.
This is my first GAE app so please let me know if my approach is wrong. I have knowledge of Flask and have used Flask-Login before for my authentication needs. With GAE, it seems they suggest to use Firebase. Mine is not a SPA but I wanted to use Firebase UI and let it handle all the user authentication part.
Looking at the examples here https://github.com/GoogleCloudPlatform/python-docs-samples/blob/master/appengine/standard_python3/building-an-app/building-an-app-3/static/script.js#L42 , i can verify the token server side using the Python Admin SDK. https://firebase.google.com/docs/auth/admin/verify-id-tokens#python or a more developed example https://firebase.google.com/docs/auth/admin/manage-cookies#python . I am able to do this but I am a bit confused about the flow later on.
In the same example, they suggest to store the token (or some part of claims) as secured cookie. This is similar to how Flask Login also stores the cookie and then we verify the cookie in every request - but since in the case the backend is local REDIS or any storage, the validation is not expensive. But when using Firebase, it seems we will have to call Firebase for every api call to validate the token.
Otherwise, it could that be that user has changed their password or reset something in their Google/Facebook provider and we wont know at our server till we validate things at the provider end. This also prevents us from developing locally offline (or we write some logic to specially handle local development).
Whats the best way to solve this?
You can use the Firebase Admin SDK to manage users or to manage authentication tokens, according to the official documentation – marian.vladoi
Reading this question, #Pinpoint's answer and the further discussion on comments, I'm well aware that natively we can't add an identity provider to our apps developed with ASP.NET 5. One possible replacement for the legacy OAuthAuthorizationServerMiddleware is then provided by the AspNet.Security.OpenIdConnect.Server as I've found in many places.
Now, there is one point that I'm still unsure about all this because I'm really not an expert in security, so my knowledge about OAuth is not very deep. My doubt is the following: is it possible to use an external identity provider when using OAuth to protect one RESTful API?
Notice that I'm not talking about adding social login to one website, I'm talking about using one external identity provider in one RESTful API.
My point is, this makes me a little confused yet, because I always thought this should be a concern of my app.
So my question here is: when using OAuth and ASP.NET 5, is it possible to use an external identity provider, other than implementing one? If it is possible, how this works in short? I mean, my app still needs to be able to manage the identities of users, in the sense that it needs to manage claims and so on.
In that case, if it is really possible, how the flow would be? The external identity provider should issue the tokens? But how my app would be able to verify those tokens and manage users identities?
EDIT: One of the reasons I feel unsure about that is that when we use the UseOAuthAuthentication extension method, we set up one callback path which is described as
The request path within the application's base path where the user-agent will be returned. The middleware will process this request when it arrives.
Now, if we are developing a site, then this really does make sense. The person goes there, click a button to login with a provider like Facebook. The user is redirected to Facebook's page and then after he logs in, he is redirected to some page of the site.
On the other hand, with a RESTful API this is meaningless. There is no notion of being redirected.
This makes it seems that the usage of external providers is only for sites and not for RESTful API's. This is the main point of my question.
My doubt is the following: is it possible to use an external identity provider when using OAuth to protect one RESTful API?
Yes, it's definitely possible. This is exactly what you do when you use Azure Active Directory to protect your API endpoints:
app.UseOAuthBearerAuthentication(options => {
options.AutomaticAuthenticate = true;
options.Authority = "https://login.windows.net/tushartest.onmicrosoft.com";
options.Audience = "https://TusharTest.onmicrosoft.com/TodoListService-ManualJwt";
});
The next legitimate question is: if you can use the tokens issued by AAD to protect your API, why couldn't you do the same thing with Facebook or Google tokens?
Unlike Facebook or Google, AAD issues completely standardized tokens named JWT tokens that the OAuth2 bearer middleware can "read" and "verify" to determine whether the token is still valid and was really issued for your API (i.e if the audience attached with the token corresponds to your API. You can control this value using the resource parameter when making your authorization request).
You can't do something similar with FB or Google tokens, since they are totally opaque. Actually, it's not really surprising since these tokens have only one objective: allowing you to query FB or Google APIs, not your own ones (these social providers don't allow to set the audience of the access token).
Since you can't read the token yourself, the only option is to ask FB or Google whether it is still valid to make sure your API doesn't accept invalid tokens. That's something you can (easily) do with Facebook as they offer a "token inspection endpoint" you can query for that: https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow (see the Inspecting access tokens chapter). This way, you can ensure the token is not expired and determine the user corresponding to the token.
Sadly, this approach has two downsides:
You have to make an extra HTTP call to the Facebook endpoint to validate the access token, which implies caching received tokens to avoid flooding Facebook with too many requests.
As the access token is not issued for your own API, you MUST absolutely ensure that the access token was issued to a client application you fully trust, or it will allow any third party developer to use his own FB/Google tokens with your API without having to request user's consent. This is - obviously - a major security concern.
You can find more information in the last part of this SO answer (it's for Katana and about Dropbox, but you should get the idea): OWIN/OAuth2 3rd party login: Authentication from Client App, Authorization from Web API
So my question here is: when using OAuth and ASP.NET 5, is it possible to use an external identity provider, other than implementing one? If it is possible, how this works in short? I mean, my app still needs to be able to manage the identities of users, in the sense that it needs to manage claims and so on.
In that case, if it is really possible, how the flow would be? The external identity provider should issue the tokens? But how my app would be able to verify those tokens and manage users identities?
To work around the limitations mentioned in the previous part, the best option is - as you've already figured out - to create your own authorization/authentication server. This way, your API doesn't (directly) accept FB or Google tokens but the tokens issued by your own server, that can possibly redirect your users to FB or Google for authentication.
This is exactly what this sample does: https://github.com/aspnet-contrib/AspNet.Security.OpenIdConnect.Server/tree/vNext/samples/Mvc
The user is invited by the client application (Mvc.Client) to authenticate with your authorization server (Mvc.Server) so he can get an access token to later query the API (also in Mvc.Server). For that, the user is redirected to your authorization server, which itself offers you to authenticate with Google or Twitter.
When this external authentication step is done, the user is redirected back to your authorization server (Mvc.Server), where he's asked to give his consent for the client app (Mvc.Client) to access his personal data.
When the consent is given, the user is redirected back to the client application with the access token you can use to query the API endpoint.
I have a RESTful API written in ASP.Net that implements OAuth 2 for authentication, and it's currently accessed through a web application. I've also got a legacy desktop client that accesses the same resources directly (not through the RESTful API and without OAuth, but using the same login credentials and hitting the same database). The requirement I'm trying to meet right now is to allow a user to click a link in the desktop application in order to open the web app to a specific screen, and when they do, to have the web app authenticate automatically so that they don't have to manually log into it (since they've already logged into the desktop app).
I'm trying to work out how I can handle this within the constraints of the framework. I'm not too familiar with OAuth 2 in general, but from what I understand I shouldn't share tokens between clients and there are no flows specifically for this kind of hand-off (unless I'm missing something). Worst case scenario, I could generate a temporary token outside of OAuth that's used by the web client to authenticate rather than a username and password, but I'm hoping to avoid stepping outside of what's already in the framework to do what I need to do.
So the question is this: is there some decent way built into the OAuth 2.0 framework to handle this sort of "handshake" between two applications, or should I just build my own method of dealing with it?
Using temporary one-time tokens is actually part of OAuth spec (authorization_code grant type). In this case this short-lived code can be exchanged for access_token (and refresh_token). You will have to implemenent generating and validating of this authorization_code.
If you are using OWIN OAuth middleware:
You can generate the code at separate API endpoint accessed by your desktop client app.
After receiving token, pass it to your browser and direct it to auth endpoint with grant_type=authorization_code over secure connection. Example: call Process.Start("https://example.com/ExternalLogin/authorization_code_goes_here"). At the webpage redirect user to your OAuth Token endpoint with grant_type=authorization_code.
AuthenticationTokenProvider.Receive will be called, in which you will validate your token. (Example code here).
After successful validation OAuthAuthorizationServerProvider.GrantAuthorizationCode will be called, in which you will process the authenticated user in the same way you process it with grant_type=password.
Remember that your token validation logic should ensure that your tokens are short-lived, usable only once and transmitted over secure connection.
This is sometimes called "single sign-on" if you want to research this topic further.