Authentication - proper way of handling session with tokens - authentication

I have a ReactJS SPA application which connects to the ASP.NET Core WebAPI. The API is also an authorization server thanks to OpenIddict. I am using PasswordFlow and RefreshTokenFlow to handle authentication, which means that the server returns an AccessToken and optionally a RefreshToken. At this moment I struggle with handling Remember me functionality properly. When a user wants to rememebered it is no big deal - the server returns an AccessToken and a RefreshToken which the client stores is LocalStorage, so it can refresh the AccessToken when it's about to expire or is expired by using RefreshToken and it is fine - there is a lot of articles and other helpful resources on the Web. The problem comes when the user does not want to be remembered. How to handle authentication is this scenario? The two solutions I see are:
the server only issues an AccessToken, which the client stores in SessionStorage. If it expires - the client forces the user to re-enter his credentials to get a new AccessToken. AccessTokens should be short-lived (according to what I've learned so far, it can be from one up to several hours, but the more hours it is valid, the less secure it is in a situation when it gets stolen. If it lasts for an hour and after that time the user still uses the application, it seems a bit odd to force him/her to login again.
the server returns an AccessToken and a RefreshToken and the client stores it in SessionStorage. During the session, if the AccessToken is about to expire, the client can get a new one by using the RefreshToken. This is risky as well as the server will never know that the user closed his browser and that his/hers RefreshToken should be revoked. Assuming that RefreshTokens have a way longer lifetime, this seems really risky.
I will be grateful for any thoughts, suggestions and insights on this topic.
Thank you!

If you are new this stuff is tricky to understand, but most implementations work like this:
The SPA uses the Open Id Connect Implicit Flow via a 3rd party library
Users are redirected from the SPA to a 3rd party Authorization Server to login
The Login Process is completely externalized from your app
After login the Authorization Server returns a token to your SPA
The SPA calls the API with an access token
PS: I have a code sample and some written guidance that you might find useful.
Further posts describe how silent token renewal works for an SPA, and as John points out above, an SPA does not use refresh tokens.

Related

What are the benefits of implementing OAuth (JWT access tokens with refresh tokens)

Current Setup
I'm currently building an application, and have just finished implementing an OAuth authentication server. This is my first "real" attempt at a project that has a legitimate user authentication and management component, so much of this is new to me.
I've implemented the OAuth specification. This means the following:
The authentication is deployed on a separate webserver with a separate DB than the application server.
The auth server logs in a user, and issues them two tokens. One is a JWT access token that contains information about their user identity. This has a short-lived 15 minutes expiry. The second token is a refresh-token. This is a single-use token with a 7 day expiry. It is used to fetch another access token with the application server returns a 401 (the access token has expired).
The application server shares a secret key with the auth server, and has middleware that verifies the JWT access token.
Concerns
My concern is that I've over-engineered a project that didn't need this much complexity. Since the auth/app are distinct, this means that my client code needs to be aware of, and handle refresh token rotation. It needs to interpret 401s, and fetch another access token.
As far as I can tell from my reading, this wouldn't be necessary with simple session cookies that persist. The cookie is just included in requests to my domain. If I killed this approach, and just had my server do a DB lookup on a signed session cookie - I would avoid a lot of this client complexity.
One consideration is that I’m using a React SPA frontend client. This is also new territory for me, so I’m not sure whether or not this changes the situation.
As I said above, I'm new to a lot of this so could use some advice from people who have worked in this space for longer and have the experience. Is OAuth worth the hassle?
Options
OAuth with access tokens (JWTs) and refresh tokens:
Pros: Provides a secure and scalable way to handle user sessions.
Cons: Client needs to be aware of refresh token rotation procedure and handle 401 responses by using the refresh token to request a new access token, which can add complexity to the client-side code. Also adds complexity (and cost) to the AWS infra stuff. I need to know how to get the auth server and app server on the same base URI. Also makes my local dev environment a little more involved.
Server-side sessions using signed cookies:
Pros: Simpler for the client, as it does not need to handle refresh tokens or 401 responses.
Cons: Server needs to manage session state, which can be more resource-intensive. This means a DB call on every request.

Need help regarding refresh token mechanims

I have a client facing React native application.
I'm using JWT for authentication with quickly expiring access tokens(10m) & long lasting refresh tokens (7d).
I have two servers, one for auth & one for fetching other things.
ex: server.com/auth, server.com/activities
My question is, how to have a good refresh mechanism here ?
Ex: If a user logs in after 2 days and queries an API through my app, how should I send him the new access token back (using his refresh token) - keeping in mind my auth server is at a separate location ?
The obvious solution I see here is to contact /auth from /activities, get the new tokens and send the new tokens in the header of the response, while on the client side keep checking for new tokens in the header & saving them if present.
Is there a better solution than this ? Is this even a good solution ?
An perhaps better option is to look at using the Backend For Frontend (a.k.a BFF) pattern to secure SPA applications.
See
Backend For Frontend Authentication Pattern with Auth0 and ASP.NET Core
The BFF Pattern (Backend for Frontend): An Introduction
Securing SPAs using the BFF Pattern (once and for all)
What I would do, and as far as I know is what most people do is to have your API (/activities) respond with 403 if the access token is expired. The frontend should catch these responses and then ask the authorization server for a new access token based on the refresh token you have. The AS will respond with a fresh access token, which the frontend can now use to send to the API. Usually this exchange will happen automatically and the frontend will eventually retry calling the API with the new access token.
If the refresh token happens to be expired, then the authorization server will respond with a 403, which is a signal to the frontend app that it should ask the user to log in again.

API Authentication for PWA

The Setup
We’re building a PWA (progressive web app). The main components are the app shell (SPA) and the API. The REST API will supply the data needed for the app, while the SPA will handle the rest (as per Google recommendation).
The Problem
Authentication of the end-user seems problematic because the web browser needs to be accounted for. We want the user login to persist through closing down the browser.
We’ve done the research about the possible ways of going about it, however we’d like to ensure that we’re not going in the wrong direction.
Solutions we’ve considered
Session based authentication - the user sends username and password to /accounts/auth and receives a HTTP only cookie with the session ID. The session needs to be stored in a database or Redis. The issue with this option is that cookies are automatically sent by the browser therefore we need a CSRF protection in place. Using the Synchronizer Token Pattern a new token would be generated every time a state changing request has been made e.g. POST. This means that the application needs to supply a CSRF token with every request so that the PWA can send it via AJAX. We determined that it’s not ideal as the user can send multiple post requests in a quick succession making some of them fail and resulting in a bad user experience.
We could also use this method without the CSRF by limiting the CORS policy to same domain and adding a header requirement which technically should stop all CSRF, however we're unsure how secure it would be.
JWT token based authentication - the user sends username and password to /accounts/auth and a new JWT token is issued. The JWT then needs to be stored in localstorage or a cookie. Using localstorage means that JWT is XSS vulnerable and if the token is stolen, an attacker can impersonate the user completely. Using cookies we will still have a CSRF issue to resolve. We considered a double submit cookie method but the CSRF would only refresh every time the JWT is reissued which creates a window for the attacker to find out what the CSRF is. It is not clear which method is best to use.
Session based authentication + JWT token authentication - the user sends username and password to /accounts/auth, a session is created, a HTTP only cookie is set in the browser and a JWT token is sent back to the user. The PWA can authenticate requests with the JWT and whenever the JWT expires the app calls /accounts/auth again to acquire a new one. The /accounts/auth endpoint would still need to be CSRF protected, however the impact of it on usability would be minimised.
There seems to be a large amount of articles claiming that localStorage is insecure and shouldn't be used so why are high profile organisations like Amazon still recommending it? https://github.com/aws/amazon-cognito-auth-js - this SDK uses localStorage to store the token.
You don't need to generate new CSRF token each time a client make a request. It's much easier to use a scheme like token = hash(id + secret + current_day). You only need to update it once a day, or even employ mixed scheme (if the token is invalid today, but is okay for the previous day, the server accepts the operation and returns new token in a predefined header for client to renew it). You may also use the cookie as an id, making the token totally stateless and much easier to check, no need to store them in the database.
Here is how I look at it.
JWT token authentication : with this approach, you can always use a time-bound token with its expiration set to say 2 hours or something?
Or another approach would also be to try and see how you could use some of the approaches the Credentials Management API suggests for example, auto-sign-in of users whenever they come back.
Stuff like 2-step verification with OTPs for instance; for very important features in your web app can be a choice. In this case basic stuff are tied to whichever one time authentication method you have.
Actually, you can also use user-defined pins or short codes (seen a lot in banking apps) to grant access to some features in your web app.
Hope this helps, or sparks some ideation.

Multiple SPA with JWT Authentication with Refresh Tokens

I have a weird scenario. I want to have a single backend API with multiple SPAs as frontend (all under the same domain but different subdomains). Here was the original idea for using JWT and Refresh tokens.
User logs in using USN/PWD.
Server Authenticates the request and sends back a JWT with an expiration and permission in claims.
The server sets a refresh token (that is stored in a data store for sessions) in a cookie for the domain and all subdomains.
Now all request send the JWT and cookie to the server.
If the JWT is expired then check that the refresh cookie is still valid and send a new JWT
Now if the user is in a different SPA under the same domain we can send a request to /token?grant_type=refresh_token which will send a JWT token if refresh token is stored in a cookie.
I would like to know drawbacks to this approach.
Another approach I read (https://stackoverflow.com/a/29088725/4664675) looks interesting as the JTI is basically the sessions ID.
I am not too worried about 15 minutes expiration tokens and a refresh token of about 1-week sliding session. All connection from the SPA to API are over HTTPS as well.
What would someone recommend the best way to handle this situation (One login that is used for many apps like Google does for mail, drive, photos, youtube, etc.)? I like the idea of JWT so that I don't have to hit the datastore on every request.
A little further down the road, I would like to make native apps that consume the same API so an authentication and authorization process that can handle both would be ideal! Seems like I need a Single Sign On approach?
PS. This is all from research and this will be the first authentication/authorization workflow that I will be writing.

How to make Google sign-in token valid for longer than 1 hour?

I have implemented google sign-in successfully.
I am able to authenticate user and in response I receive token. However the token expires in 1 hour.
expires_in: "3600"
I tried searching in the docs - https://developers.google.com/identity/sign-in/web/reference - but cannot find a paramenter to extend the lifespan of the token.
What I'm actually trying to do?
https://developers.google.com/identity/sign-in/web/backend-auth
after a user successfully signs in, send the user's ID token to your server using HTTPS
I'm sending token with each request to the server:
endpoint/get?access_token=" + access_token
And then on the server I'm calling https://www.googleapis.com/oauth2/v3/tokeninfo
So I have a token, every request is authenticated, but after 1 hour of working the tokeninfo method returns false and I need to re-authenticate the user.
In my code I circumvented that by storing all the historical access_tokens and if client uses old token I check against historical data and manually issue new token using refresh_token (one of my permissions is to grant offline access)
So yes, I'd be very interested to know:
How to expand lifespan of the access_token?
OR
Given the limited lifespan how to ensure requests are authenticated on the backend?
As #DaImTo noted, you can't extend the life of an access_token. You can get a new one using a refresh_token, but often if you're trying to do this client side and have a server, you should re-think your approach.
It sounds like there are two "authentications" that you're doing here - the client authenticating against the server, and the server authenticating against the Google service. Right now, the server should be holding onto the refresh token - so it can always re-authenticate against Google. It sounds like you're wrestling with how to authenticate your client against the server after the auth_token timeout.
In general, the client shouldn't send the access_token to the server, nor the refresh_token. What it does is during the first sign-in, the client gets a one-time code (from Google) which it hands to the server. The server uses this to talk to Google and get the access_token and refresh_token, confirming the user has authenticated themselves, and then sends something (usually a cookie) back to the client saying "ok, I've authenticated you. Here is how you keep authenticating yourself for the rest of our conversation."
That later action is pretty standard and is unrelated to oauth itself. The client and server then communicate as they always do - no oauth stuff is exchanged at all, you're relying on the cookie (or equivalent) to keep up the client-server authentication. The server continues to use the auth token and refresh token to talk to Google.
https://developers.google.com/identity/sign-in/web/server-side-flow I think is the best guide to this at the moment. Or at least it is the best one I can find at the moment. It has a good diagram, at least.
The key point is that you're exchanging the brilliantly named "code" with the server (what I was calling the "one-time code"). Once you have done that, the server authenticates you with Google - and it then has the access/refresh tokens and you communicate with the server without having to pass those.
Access tokens are short lived and only last for one hour this is not something you can extend.
What you need to do is take the refresh token and get a new access token.
example:
You take the refresh_token that you got from your initial request and HTTP Post it to: Note: grant_type=refresh_token
https://accounts.google.com/o/oauth2/token
client_id={ClientId}.apps.googleusercontent.com&client_secret={ClientSecret}&refresh_token=1/ffYmfI0sjR54Ft9oupubLzrJhD1hZS5tWQcyAvNECCA&grant_type=refresh_token
response
{
"access_token" : "ya29.1.AADtN_XK16As2ZHlScqOxGtntIlevNcasMSPwGiE3pe5ANZfrmJTcsI3ZtAjv4sDrPDRnQ",
"token_type" : "Bearer",
"expires_in" : 3600
}