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.
Related
I have seen this solved in popular platforms like Android & iOS using client SDKs. My question is
I have a RESTful server
I have a mobile client
How to
Create a signup which uses a federated OAuth (Google, FB, Microsoft etc) and use that to further authenticate the subsequent API calls.
This is what I am thinking
Client application calls the OAuth dialog of the login provider, and receives (after user consent), access token and user ID.
This is stored on the client and also passed to the server.
Server can validate (retreive) user info using the accessToken.
Sever can return IDtoken/Refreshtokens which client can use in subsequent API calls.
My question here is
Is this the right approach.
Can clients store the accesstoken (best practice?)
Can client pass the accessToken to backend (best practice?)
Is there an example, how this can be implemented for Google Auth (for a client and Webserver) without using SDKs.
The standard option is to implement the AppAuth pattern in your mobile app, meaning it signs in and uses tokens from your Authorization Server (AS). The mobile app then sends access tokens to your APIs.
Once this is done, signing in via Google, Facebook etc requires only config changes in the AS. Adding a new login / identity provider requires no code changes in either your UIs or APIs.
Here are Android and iOS samples of mine that use this approach and which you can run and maybe borrow some ideas from.
They use AWS Cognito as the AS - and I could configure Cognito to use Google or Facebook logins if I wanted to. Also I am in full control of scopes and claims added to access tokens - which my APIs can use to authorize requests.
ANSWERS
Almost right - your client app redirects to the AS and not Google / Facebook directly
Yes - mobile clients can store a refresh token in OS secure storage private to the app so that users do not need to login on every app restart. My samples do that.
Yes - mobile clients use access tokens as API message credentials
LIBRARIES
I agree with you here - avoid Google / Facebook libraries in your app. However it is recommended to use the respected AppAuth libraries - once integrated your app is compliant with any AS and will support all of its authentication flows.
Out of interest the AppAuth pattern even potentially enables future advanced scenarios such as App2App, since the AS can federate to an AS from another company (though I doubt that is relevant to you right now).
LEARNING CURVE
Finally it's worth mentioning that it is tricky to implement AppAuth - there are annoyances - but once done the architecture is in a good place.
Firstly, Any application that calls Google APIs needs to enable those APIs in the API Console.
Now, Any application that uses OAuth 2.0 to access Google APIs must have authorization credentials that identify the application to Google's OAuth 2.0 server.
For the credentials go to the Credentials Page and then fill the form according to your Application.
Note: Google recommends that you design your app's auth endpoints so that your application does not expose authorization codes to other resources on the page.
After getting your credentials, download the client_secret.json file from the API Console and securely store the file in a location that only your application can access.
For HTTP/REST, there is no need to install any libraries to call oAuth 2.0
Google's OAuth 2.0 endpoint is at https://accounts.google.com/o/oauth2/v2/auth. This endpoint is accessible only over HTTPS. Plain HTTP connections are refused.
As a client, the only thing you need to do for Basic authentication is to include an Authorization header in an HTTP request, composed of the username and password, separated by a colon and then Base64 encoded. E.g., in Ruby (1.9) using RestClient:
require 'restclient'
require 'base64'
auth = "Basic " + Base64::strict_encode64("#{username}:#{password}")
response = RestClient.get("https://myhost/resource",:authorization=>auth)
The token value is opaque to a client, but can be decoded by a Resource Server so it can check that the Client and User have permission to access the requested resource.
Authorization: Bearer <TOKEN_VALUE>
Send user to Google's OAuth 2.0 server. Example URL:
https://accounts.google.com/o/oauth2/v2/auth?
scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive.metadata.readonly&
access_type=offline&
include_granted_scopes=true&
state=state_parameter_passthrough_value&
redirect_uri=http%3A%2F%2Foauth2.example.com%2Fcallback&
response_type=code&
client_id=client_id
Request access token. Example:
POST /oauth2/v4/token HTTP/1.1
Host: www.googleapis.com
Content-Type: application/x-www-form-urlencoded
code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7&
client_id=your_client_id&
client_secret=your_client_secret&
redirect_uri=https://oauth2.example.com/code&
grant_type=authorization_code
Use API. Example:
GET /drive/v2/files HTTP/1.1
Authorization: Bearer <access_token>
Host: www.googleapis.com/
Is this the right approach.
Yes, pretty much. You're describing standard bearer token authorization. Access & ID tokens expire after a short time frame, and you have to use a long-lived refresh token to get new ones. The access token allows you to gain access, the refresh token is only useful for bootstrapping tokens.
Can clients store the accesstoken (best practice?)
Yes. You must store an OAuth refresh token. You may store the ID and access tokens (as opposed to keeping them in memory.)
On the web, you should not use Local Storage. Instead, use an httpOnly cookie.
On mobile, use platform features like Android's AccountManager or iOS' Keychain Services.
Can client pass the accessToken to backend (best practice?)
Yes, that's its purpose.
Is there an example, how this can be implemented for Google Auth (for a client and Webserver) without using SDKs.
I do not recommend this for several reasons. Firstly, your implementation will be less secure than Google's, who has a team devoted to ensuring their SDK is the most secure it can be. Secondly, you're not ready to try this implementation, yet. Start with the SDK, get it working, and then come back to this.
Using a solution like Auth0 will be the easiest way to start.
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 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.
I'm building a SPA with AngularJS with communication to a Laravel PHP backend. The Authentication method uses JWT, with the tymon/jwt-auth PHP library, which seems to me like a great library.
When user sends his username/password, the service sends back the JWT, which is stored in localStorage on the client. This works fine. However, I want to use a refresh token to continue issuing fresh JWTs to the client, to keep the user logged in as long as he is using the application. Where are these refresh tokens supposed to be issued? Should they be issued when a user sends his username/password? If so, there doesn't seem to be a way in the tymon/jwt-auth library to send the refresh token to the client. Please help, I'm having a lot of trouble conceptualizing how this is supposed to work.
Whether or not you get issued a refresh token when you authenticate with an OAuth 2.0 authorization server depends on which OAuth grant you're using.
When your client is a SPA (an untrusted client), you're probably using the implicit grant and that grant does not support refresh tokens.
Some companies have implemented libraries that are able to refresh access tokens issued by the authorization server using requests in a hidden IFRAME. I'm not familiar with the library you are using.
See also this question for more info.
I have been looking around for this issue and haven't found anything yet.
I want to create a webapp that requests offline access to google api, and the my main concern is that I'm using angular as a front-end and I'm consuming a api created in nancy.
I have been able to get access but only for a couple of hours, but I need the user to grand offline access to the app because it's going to have a task running every amount of time needed.
Have anyone already accomplish this one? Tried? Experience? I've tried to call the google oauth api to request the token but it returns 405 method not allowed.
You can use Simpleauthentication, which has Nancy specific NuGet package. Just follow the instruction to set ip SimpleAuthentication in a Nancy app.
I've never used nancy. But in general, your OAUTH concerns should be handled server-side. The user validates against the google oauth, that google oauth flow returns to the back-end server, and the back-end server gives up a key, cookie, or something like that, which your front-end now uses in its request.
That's at least how I handle it. I use a token-based scheme for all my $http requests from my back-end API, which returns 401 if there's no valid token on the request and 403 if there is a token, but that token doesn't authorize that resource. I stuck a httpProvider interceptor in angular that, in the event of a 401, initiates client-side login flow.
Specifically, the token just is a randomly generated series of bits turned into hexadecimal, and then I stash it in redis as a key whose value is the user_id associated with this login.
Of course all of this requires a real https stack to be secure, since I'm just pushing what amount to plain-text one-time keys around, but that's about all you can do about that.