I created this web app completely in go-lang, which uses Google Drive API to authenticate users. Once user authenticated, it saves the token in a <user-email>_token.json file so the app can operate for 24 hours without the users involvement. It works fine. But now I want to separate the front-end from (Go-Lang)back-end and convert it to AngularJS.
So I have this problem with authentication. Because I should keep the authentication in server-side. But then how would Angular know that the user is authenticated or not? Because I cannot use sessions.
Should I need to use JWT to this? If it is, then how should I do it?
Your token does not have to be on the server side.
Why? Because if you have many clients connected to your server, it would mean that all of these clients are sharing the same token and therefore have access to the Google Drive linked to this token. It does not make sense.
The token has to be on the client side. You should save the token as a cookie, maybe by using JWT, I let you read the documentation of JWT to know why it would be interesting to use it in your case or not.
Then on your Angular, you have to say something like "hey, this client has a cookie called "my-google-drive-token", let's check if that is a good one... Mmmmh, okay seems to be good, I display the Google Drive content".
Think about using good practices about security (using an encrypted token in your cookie, making the connection between the front and the back safe, keep your API key safe...).
Your backend is only a gateway between your front-end and the Google Drive API.
Also, check the usefulness of your server. I think that in your case, a simple frontend connected to the Google API is enough.
Related
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
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
I've been developing RESTful API server communicating with cross-platform clients such as Android, iOS, Web browser, and so on.
When a user login successfully by username and password, this server issue an access token(JWT, 5 minutes) and a refresh token(GUID, 20 days).
When we develop Android client app communicating with server, we just can store this tokens in mobile device and I believe it will not be a problem in terms of security( using SharedPreferences).
But when it comes to Web browsers, (React App) I had to tackle where to store these tokens. Finally, I decided HttpOnly Cookie, because I can manage easily CSRF attacks rather than XSS.
Soon, I doubt this is a typical design. For example, web browser users cannot logout whenever they want. So I determinate change the wrapper server(Node.js) between the React app and the RESTful API server.
In my second design, the React App and the wrapper server authenticate session-cookie model, using passport.js for exmaple. And when the wrapper recognize the request is authenticated, then the wrapper issue a short term access token(1 minute JWT) and reorganize the request by inserting the access token just issued in the header sent to the RESTful API server.
Is this reasonable process? Thank you in advance.
You could simplify your solution by removing the JWT access token altogether. The refresh token could be used as a session id. Every time a client issues an API call to the server the session id is sent in an HTTP header, so you can check if the request is legitimate.
Your approach of using a JWT token with a short expiration time is ok, but it brings some complexity to your system. In my opinion this approach is best suited for systems where you have an authentication service and a resource owner service. So the client would request an access token to the authentication service and use that token to communicate with the resource owner service. Then the resource owner can check the validity of the access token by just checking whether the signature matches the authentication service's.
I hope this helps you, let me know if I'm missing something.
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.
I'm working on a webapp that will allow users to authenticate using simpleauth. For now I will be supporting Google and Facebook. Other than logging in and out (using webapp2), the webapp will consist of Cloud Endpoint APIs. The clients will be web, Android, and iOS.
My questions is, using Endpoints Proto Datastore, can I have user_required=True and call endpoints.get_current_user() to get my user from an #Model.method if the user's auth provider is Facebook (or any of the other supported OAuth2 providers? If it is not possible, does that mean I should not have user_required=True, and should instead get a permanent user id from the provider given the OAuth2 token and keep it in the datastore, generate my own auth token for that user, and then pass that token to each request?
EDIT: Instead of passing the auth token around, would it make sense to have an authenticated user request an "API token" that they can pass to the API methods? Would this token have to be included in the POST or GET body, or can it be put in a header/cookie (I saw some questions elsewhere on SO regarding headers and cookies with Cloud Endpoints, but it's been some time since then). This is all assuming that non-Google auth won't work.
This answer is not going to directly answer your question but should give you a good idea how you can implement authentication in a safe way. I've implemented something similar recently and spent quite some time figuring out which is the best way to do authentication with Google AppEngine environment.
Google supports OpenId Connect protocol. Facebook's implementation should be very similar according to Getting Started with OAuth 2.0 book. I will focus more on Google's implementation, as I am more familiar with it but the concepts should be very similar when using other OAuth providers.
OpenId Connect will give you an id_token, after successfully authenticating the user. It will also give you an access token. Access token is something that should be kept a secret. Never send it over the wire. Id token on the other hand is very handy. It contains some information about your user, and it's encrypted so it doesn't expose any information about the user "just like that". You'd have to crack the id_token to find out anything about user. In the unlikely event that it gets cracked it doesn't expose anything critical about the user. What you can do you can store it as a cookie and then use it in all subsequent requests to verify it the user by checking if there's an access token that matches the id_token. The only drawback is that id_token is quite long - it takes around 650bytes. That would mean that every http request carries that payload. If sending that much information is too much for your use case you can send only first few characters, maybe 12 or so and then match just the first part. The id_token has can also be useful when analysing your data. It will show up when analysing http requests but will not reveal any information about the user and you can still differentiate requests that came from different users.
Also on a side note, don't try using AppEngine's users service as it doesn't work very well with any kind of custom authentication.
Hope this gives you an idea and puts you on the right track.