AWS Cognito Identity Service Provider appears to store access token in local storage. Is this safe? - reactjs

We are developing an application that uses a React front end website hosted on AWS using Amplify. This communicates with a .NET Core 3.1 Web API running on EC2 / Elastic Beanstalk. Cognito is used for user authentication with the Web API configured to use JWT tokens.
It works OK, but we have noticed that the Cognito provider stores the JWT access token in the browser local storage. This is what we see using F12 in Chrome and inspecting local storage.
From what we have read, storing access tokens in local storage is not advised as it makes the application susceptible to XSS attacks. Strange then, that the Cognito identity provider chooses to store sensitive information here.
If this approach is not considered safe, can the provider be configured to store this information elsewhere, such as cookies?
Alternatively, as we control both front and back ends, is there an alternative method that can be used to secure the API that does not involve tokens? Obviously the API needs to know which user is logged on to the web application in order to perform authorization checks. [Note authorization in the application is record level and defined in database tables, so it goes beyond simple user profile attributes.]
Many thanks in advance for your advice.
Doug

Security is a spectrum not a feature so it really depends on your appetite for risk vs effort. Amplify is not a particularly nice codebase, it has 500+ issues and if you look at the code you might be fairly shocked at the quality of it.
If you are using Hosted-UI then you can write code to manage the tokens yourself rather than using amplify, although you will need to learn a bit about OAuth grants and OIDC.
Be aware that the Hosted UI lacks a huge amount of features, so if you are going to use it make sure you are happy with it. Off the top of my head
no silent refresh capability in the hosted UI, so no safe way to store the refresh token.
no support for custom auth flow in the hosted UI
no passwordless support in the hosted UI
no ability to pre-populate a field in the hosted UI (e.g. username)
no ability to customise the plethora of obscure error messages in the custom UI
fixed now, but for years the email addresses were case sensitive!
An alternative is also to just use the AWS SDK to get tokens directly using cognito-idp but this also has a bunch of issues:
no code/PKCE/nonce capability so insecure in a mobile authsession
no ability to set oauth scopes, so can't use them
consequently not possible to use for OIDC
the SRP implementation is bananas and so far off spec
if you make device registration mandatory, it will deliver a working access tokens before the device is registered! (allows invisible devices for malicious logins)
We were using auth0 which was leagues ahead but we had to move to Cognito because of SMS OTP cost (min $25k per year at auth0).
I have been using AWS for over a decade now, Cognito is by far the worst service I have used, and I have used a lot! If you can avoid it, do so.
To answer the original question, yeah it's insecure. The best you can probably do is keep them in memory. If you wanted to you could probably put the hosted UI behind a cloudfront and use an lambda#edge to transform the token into a cookie instead. This has now opened you up to CSRF attacks though.

answering the original question: no, it is not safe at all.
Storing refreshtoken in any local storage accessable to any local app/script is not secure. So, the best way would be to store the refreshoten (and also the access token) in an httponly cookie or even better to store a one-time session token in httponly secure cookie could be used to get new access and refresh cookies - similarly as it is made by cognito hosted ui with XSRF-TOKEN.
See below how I would solve (and plan to solve) this issue:
Some background:
Due to GDPR regulations I think I can not use the cognito hosted ui - I have to make sure users read and accept the general terms and conditions (giving clear and auditable consent) and can review and accept cookie policies as well before they type in any user data for sign up. Nevertheless the built in hosted ui design is quite outdated and unflexible. I have an SPA website where I want to manage users, secure endpoints, etc.
So I have the following idea which is still not super secure but I think it is more secure one if you want to use js and ampify sdk and which also might answer your question:
I'll use amplify javascript sdk to let users sign up, change psw and log in (get tokenid, access token and refresh token), will make my own "hosted ui". I'll store the access token in memory only (not in local cookies and not in localstorage for sure). Access token will be used in header (bearer) to access apiGW endpoints. Access tokens will have very short expire dates. (I'd also use httponly secure cookies sent back by the apigw, as well as in the body.., then compare at BE side..)
And here comes the trick: I'd cut the refresh token into two. (Don't forget it is just a string.) I'd store the first part of the string in a local cookie (javascript can read it, if browser is closed and opened again it will be still there) and will send the other half of the refresh token to an apiGW endpoint (accessable without authentication) which will store it in a dynamoDB table (with TTL) and will send back an httponly secure cookie to the browser with a randomly generated "storagetoken" in it (which will be a key in dynamodb). There will be another unauthenticated apigw endpoint which will be called by the client whenever the client needs the full refresh token. Calling this endpoint the browser will send in the httponly secure cookie as well (same domain), so the backend will get it. As it is issued by the BE and available only in the given browser it can not be stolen so the backend will send back the stored half refreshtoken. The other half part of refreshtoken is stored in a simple cookie in the browser.
If the browser is closed and opened again client checks if there is any valid accesstoken and if not it checks if there is a half refeshtoken stored as cookie. Then ask the other part of refreshtoken assuming there is a httponlycookie also stored and it will get back the other part of the refreshtoken from the BE. In case of success the client tries to use the full refreshtoken to renew/get access token from cognito, in case of failure it will pop up the login screen.
Whenever refreshtoken is not in use it is deleted from the memory.
I know this is still not supersecure but might be a better solution than storing refresh token in localstorage.

Alternatively, as we control both front and back ends, is there an alternative method that can be used to secure the API that does not involve tokens?
I don't know anything of Amplify but in AWS Cognito what you describe is the Implicit grant OAuth flow. In AWS Cognito it is possible to use Authorization code grant where you instead of the token get a code which you in the backend can exchange for a user pool token.
https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-configuring-app-integration.html

Related

How to keep authentication token safe in js web apps?

I am not sure if I should create it here on StackOverflow or another stackexchange channel, but let's try here.
We have a web api made in asp.net core which uses the basic authentication where another web app post some login data to the api and it respond a token for the next requests. The client app stores this token and the next request to get/post data uses this token key for authentication. It works fine from the api perspective.
The point here is our web app. We are building it using react.js and the point how to keep the authentication token safe. We store the token on the current app (which is executed in a web browser). We have a feeling about store it on the browser because bad users can access the console on devTools and investigate how to to get the token from the global variables on the react app (just a sample). Given this point the questions are: How to deal with it to keep the back-end and front-end safe? How make sure the users cannot get the auth token and use it on another apps?
We were thinking in creating a kind of server-side channel just to store the authentication token like the picture bellow:
The web browser app make requests to server-side channel to get/post some data;
The server-side channel make a new request the API defining the authentication token and repassing the get/post data;
The api process the request and respond;
The server-side channel get a response from api and send it to the browser;
The web browser app get the final response.
It could work and keep it safe, but another question is: How computationally expensive is that?
Is there any possible solutions or suggestions how to deal with it?
Thank you.
Use JWT access tokens against your API and authenticate your SPA with an identity provider using an Open ID Connect flow (OIDC).
https://www.ubisecure.com/single-sign-on/single-page-application-and-openid-connect/
There are lots of examples of this, Identity Server is a common OIDC implementation with examples, http://docs.identityserver.io/en/latest/quickstarts/6_javascript_client.html
Once you've gone through the OIDC flow and acquired an access token for the user, you can store this client side safely, as
The access token has an inbuilt lifetime and once it's expired can no longer be used. Good practice is to keep this lifetime short (thus limiting the attack vector) and provide some sort of token refresh logic to automatically keep the user working against the API, as long as they keep the SPA open.
Your netcore web api has all the libraries it needs to do token validation / lifetime valdiation etc. This has been made very simple at the API layer
NB: I mention safely as there is still an attack vector, someone who acquires the JWT can act as that user for the lifetime of the token against your API, they are the bearer of the token. It's up to you to make sure the lifetimes of your tokens are sane and the the process for acquiring a new token is as secure as possible
There are a lot of examples on how to implement this, and whether or not you want to use your own Identity Server or use a solution such as Auth0.
Don't try and roll your own security solution. Stick to the specs and standards and adhere to all the industry best practices, making use of battle-tested libraries.
store token in local storage in web browser in encrypted form

ReactJs security

I have just started learning authorization and authentication in react, and I'm writing this after finishing my first simple login system using JWT, as most of you know you store a token in the browser and then you compare it with the saved tokens in your backend now when that validation is done truly I set Authenticated Boolean to true and gain access to the website, after finishing that simple system I checked react dev tools and I found out that I can just change the boolean to true and bypass all of the authentication work!
And I have searched online for resources and tutorials to fix that massive problem but didn't find what was I looking for all I found is how to setup Authentication or protect a router similar to the way I did, but not deeply secured.
So can anyone recommend a course or tutorial paid or free to learn more about security and authentication?
Since React apps are single page apps (if you are doing client-side rendering), the entire application (all html/css/js files) is sent to the client in the initial request. Generally authentication works in the way you have stated where the authentication status of the user is stored in the application state. This, of course, means that someone familiar with web applications would be able to set the variable isAuthenticated to true. If you have sensitive information kept statically (written literally in html/css/js) then this would be an issue.
The reason this scenario is not generally seen as an issue is because React apps usually do not hold any data. And data is usually the sensitive stuff in an app. Data can be tied to the user and should not be exposed to those who are not properly authenticated or do not have the required permissions. Data is held by the server, which can control what it sends out (checking for verified JWTs) via the API to the app. So your server should check for a valid JWT on any request that returns sensitive information—generally all requests except those for the app itself and authentication requests.
In short: Yes, someone can get access to the "authenticated" side of your app, but any requests to the API for data by the app at this point would (or should) be blocked as unauthorized (i.e. status 401). So they can see what the app looks like to an authenticated user, but would not be able to see any sensitive information.
(Note: if you do store sensitive information statically (as mentioned above), consider storing it on the server and having the app request that info via the API and requiring a valid authentication token).
If you would like to block the possibility of an unauthenticated user gaining access to the authenticated side of your app, you could make the login page its own app or HTML doc and only send the full/authenticated version of the app to authenticated users.

Is it good practice to store google tokens in local storage

I am bit confused after reading few articles, basically assume you have a web app which uses gmail login for authentication. Is it a good practice to store the google access token in the local storage and send it via headers to verify in back end APIs ? or should I use separate mechanism to handle api level access ?
It does not matter where will you save tokens at front-end, because even in a case when you will be exchanging them witch back-end - you will need to send them with the request.
(all data which is placed at front-end is unsecured)
So everyone will have access to them inside the inspector network tab.
When a question is going about security, the first rule:
Research possibility to implement that at back-end by using environment variables.
P.S: I just opened Authorizing Your App with Gmail and there are says:
Get started:
To get started, see Implementing Server-side Authorization.
So you need to handle all of the Authorization actions at back-end where all the tokes will be secured.

Can I just create and store list of auth tokens instead of using JWT and similar?

I'm creating my first SPA using NodeJS stack for development and I came to a point where I should design authentication and secure some parts of the app.
I read a lot about auth techniques including JWT, OAuth, etc. but I still didn't find something like «a real world example».
Let's assume that my task is just to secure some parts of app from public. My app isn't designed to work with 3rd party services so I see no need to use something like Google or Facebook auth. I want to use login/password and store all this data using my own database server.
I don't understand the point of having authentication stateless. I came to a simple conclusion that I can design authentication in this way:
I store users logins and passwords in my database.
User auth means that user enters his credentials, server checks it and creates token. Then server saves this token into database. User saves token using browser local storage or somewhere else if it's not browser environment.
On each request client sends this token, server checks that this token exists and responds appropriately.
User can login from different devices, we just create multiple tokens for him.
We can end specific session or all sessions by just deleting user tokens from database.
We can manage tokens in a way we want, for example server can check expiration time and invalidate (delete) token.
Is it OK? JWT require additional implementation if we need to invalidate token, I saw different examples, all of them were based on storing invalid tokens but what's the point of that if we can just store valid tokens? We already lose statelessness by implementing this storage.
I see that I can just use cookies instead of implementation described above but I don't like an idea to use cookies in RESTful app because it really depends on browser-like client environment.
What are the disadvantages of just storing tokens on server?
I have implemented one project in which user authentication is required and token is stored into database.
You can find that example here.
You are describing the traditional authentication approach using session cookies. The server stores a session for each connected user identified by a sessionid. The sessionid is stored by client in a cookie and sent in each following request to identify user.
You can use this method perfectly, both in browsers or mobile devices (using the device storage instead of a cookie)
The drawback is that it requires many server resources to keep sessions open and requires database queries at each authentication to retrieve user data. This is what JWT solves. The user information goes in the token itself, and is reliable because it is signed with the secret key of the server.
JWT has its own drawbacks, for example it is not useful if you need to have a revocation list, because them will require server storage.

Why is token based authentication better for Single Page Applications?

Okay so this might be a very rookie-ish or naive question but I tried searching the internet and have resorted to stack overflow only after not finding anything fruitful. I have been reading about Token based authentication as well as Cookie based authentication. I have come across the opinion that token based authentication is better for Single page web applications but cannot clearly understand why. I will be using nodejs and angularjs to accomplish the same.
I guess that with Token based authentication as well as Cookie based authentication you mean Token authentication vs Session authentication because a token can be stored in a cookie
See this
With session based authentication the server maintains a sessions per each connected user. Client authenticates with its credentials and receives a session_id (which can be stored in a cookie) and attaches this to every subsequent outgoing request. So this could be considered a "token" as it is the equivalent of a set of credentials. This approach requires heavy server resources
Token based authentication is stateless and does not require server storage because the issued token (mainly JWT is used) contains the relevant user info and is signed with the server private key, so it is non-falsifiable. The token is stored in client side (cookie, localStorage, etc), attached to every request and validated by the server. Tokens are also suitable for REST APIs that do not require to maintain the state between each request
Forms based applications use session based authentication, and SPA often use token based authentication by the inherent advantages.
Note also that a SPA with session based authentication only will attach cookies to the outgoing request if the applicacion is located in the same domain that the server
SPAs tend to have many faces: the logged in view, the logged out view, or the restricted view. It’s all about access control. Your users are all getting the same app but they may not have the same levels of access. You’ll find yourself building access control logic for your front end and your back end.
Because tokens contain all this information, they are very portable: they can be used by your UI and your backend to make decisions. You can share them with partner services as a means of building Single Sign On services that delegate users to the correct application.
Hope this link will give you more information..
Token Based Authentication for Single Page Apps (SPAs)

Resources