I'm implementing a client-server system. The client logs in with username and password and (initially) the server responds with a token that expires in some hours and is used in the rest of services where the authentication is required.
When the token expires, how should I refresh it?
Saving the username/password persistently (ciphered) and calling the login again
Saving some kind of hash of the password?
Any other option?
How should I send the password to the server?
The client apply a hash and the server just stores it and validates hash (and never know the real password)
The client send the raw password through a secure channel and the server validates (lenght,strength,etc) and store a hash?
Any other option?
Check out this link. -> https://developers.hubspot.com/docs/methods/auth/refresh_token
From what I understand, when you first login with the password/username you should get a refresh token back which you save (to be used on refresh), along with the access token. Every so often you make a call to refresh your token to the server, you provide it with the refresh token and your access token and it gives you a new access token and refresh token.
I think the idea is that if someone sees one of your requests they can only impersonate you for a short while. They don't know the refresh token so assuming they don't see your request to refresh that token they would be cut off when you refresh and get a new access token.
I think sending the password through a secure channel would be fine.
Anyone else feel free to correct me here - I'm just trying to help out.
Related
When you log in, you store the Access Token in the client header and the Refresh Token in Redis. And when the validity period of the AccessToken expires, the logic is used to check the expiration period of the RefreshToken by using the AccessToken as a KeyValue and reissue the AccessToken if it is valid. At this time, if the attacker steals the AccessToken, the attacker will also be able to obtain a new AccessToken even if the AccessToken expires, and it is expected that the attack will continue until the RefreshToken expires. When renewing AccessToken, I wonder if other conditions should be added instead of just checking the key value or if it is okay as it is. Also I wonder why this is ok
the logic is used to check the expiration period of the RefreshToken by using the AccessToken as a KeyValue and reissue the AccessToken if it is valid.
This part is wrong. The client needs to send the refresh token to the Authorization Server in order to get a new access token. Ideally, the refresh token endpoint should require client authentication, e.g. the client has to send a secret or present a concrete certificate in a TLS connection. Unfortunately, this can't be achieved when you have public clients, like SPAs (unless you use some kind of middleware, like the Token Handler.
Anyway, in order to get new access tokens, the attacker would have to get hold of the refresh token, not just the access token. If the client is confidential, then the attacker would also have to steal the client's credentials (like a secret or a certificate's private key)
The idea is that you will make the lifetime of an access token very short and share it across different APIs without worrying. However, for refresh tokens you would be more careful and don't use it any where except the authorization server. (Do not store in the browser nor in other APIs).
This will ensure the safety of the refresh token and the short lifespan of the access token will ensure its safety.
I had the same concern before and I got clear explanations that you can check here
how is using JWT refresh token secure?
Using Java OAuth2 client library: scribe 1.2.0 (https://github.com/scribejava/scribejava)
I am able to get refresh token from the authorization code (i.e; by making POST call to https://accounts.google.com/o/oauth2/token with client_id, client_secret, code, scope, grant_type (authorization_code), redirect_uri parameters). And I have persisted the refresh token in DB.
And we support drive and calendar scopes => so, I do store two refresh token per user (email)
And then clients will be invoking API to get access token (then I am making POST call to https://accounts.google.com/o/oauth2/token with refresh_token, grant_type (refresh_token), client_id and client_secret). And the call is successful. i.e; happy normal path works.
But eventually getting new access token from refresh token is failing with invalid_grant error code (with Bad Request OR Token has been expired or revoked as errors) (like in 2 days or 3 days etc)
Please do note that the refresh token is not revoked or invalidated explicitly by user or code. Password is not changed. Code is not changed. Client ID and secrets didn't change. I am kind of lost.
Questions
Since refresh token supposed to be a long lasting token, why my application is not able to get new access token from refresh token? Its just failing in like in 2 to 3 days - and its happening regularly in stage and production environments.
Is storing two refresh tokens based on scope (drive and calendar) - per user (email) problem (i.e; as soon as second refresh token is issued the previous refresh token expire)? [Shouldn't be the case - I do know there are limitations per user and client, per user for all clients. But, 2 is too low to reach that limit.]
Answer
Finally was able to resolve it, please see the answer below comment(s) - its related to having two refresh tokens of same email for different scopes, and invalidating one of them.
Getting access token from Refresh token - PostMan
Getting refresh token from authorization code - PostMan
Relevent questions: Google token refresh returns "Token has been expired or revoked."
Since couple of days Refresh token has been automatically expired
Changed password
There are serval reasons why your refresh token maybe expiring. The first one we can lockout as the cause is the user changing their password if you are using a gmail scope and the user changes their password this will cause all outstanding refresh tokens to expire.
User revoked access
If the user revokes your access directly though their Google account this will also revoke your refresh token.
Application status.
Now is your application still in testing on Google cloud console? Have you moved it to published has it been though the verification process? If not then your refresh tokens will probably be expiring after about two weeks although the time frame may have changed as this seams to be something that Google has been working on for the last serval months and there is no official word on it.
Refresh access token gives refresh token.
Another cause which actually could be the case, when you refresh the access token does it return a new refresh token. sometimes I will do this. Always check that this is the same refresh token that you use before if note then its a new one and you should store the new one. See next point for more info on why.
Max number of outstanding refresh tokens.
When a user authorizes your application using offline access you are given a refresh token, if the user authorizes your application again you are given another refresh tokens. you can keep doing this up to fifty times and all fifty refresh tokens will continue to work. As soon as you go over the magic number of fifty then the first one that was created will be expired. This is why it is important to ensure that you are always storing the most recent refresh token for a user in your database.
Basically, if your app has multiple refresh tokens for the same user (Gmail) with different scopes, invalidating one of them will invalidate all the tokens.
that turned out to be the issue as we maintain different refresh tokens based on scopes (say drive, calendar, contacts etc)
I follow the steps in the Spotify Web Api Tutorial using the authorization-code from the Spotify Accounts Authentication Examples. Everything is okey, I register an application with Spotify, authenticate a user and get authorization to access user data and when the page show me the user data the refresh token is different each time I authenticate myself. I think refresh token shuld not change.
The only modification I did in the example code was replace the client id, client secret and the redirect uri with the correct values from my application.
Any advices?
For the Authorisation Code Flow you just need to do that initial authentication you're already doing. It will always return a new refresh token however that token can be used over an over with 4. Requesting a refreshed access token; Spotify returns a new access token to your app from Authorisation Guide that will return you a new access token for subsequent calls and you won't need to do the Authentication Step you're doing multiple times just keep doing that one.
I'm trying to implement token authentication for a REST service based on Spring MVC.
I'm followiong this SO answer: https://stackoverflow.com/a/10864088/1061499 as guideline, but now I need to understand some server-side detail.
When an user is successfully authenticated (first time via username + password) I return a token that stores some information.
When the same user sent his token in a request header, I need to identify the associated user and define if is "session" is still alive.
So the way are basically two:
encrypt the token with an algorithm (which one?) I can also use to decrypt on server side and extract user information
store token-user association info in application DB also storing session info.
Most suggest the first solution, without storing any info about authentication in DB, but this solution seems to me less secure.
Any suggestion?
This question is in some way related to the below linked question. However, I need a little more clarity on some aspects and some additional information. Refer:
REST Web Service authentication token implementation
Background:
I need to implement security for a REST Web Service using token
The webservice is intended for use with Java client. Hence, form
authentication and popups for credentials are not useful.
I'm new to REST security and encryption
This is what I have understood till now:
For first request:
User establishes https connection (or container ensures https using
301)
User POSTs username and password to login service
If credentials are valid we:
Generate a random temporary token
Store the random token on server mapping it to actual username
Encrypt the token using a symmetric key only known to server
Hash the encrypted token
Send the encrypted token and the hash to the client
For subsequent requests:
Client sends this encrypted token and hash combination (using
username field of basic?)
We make sure the encrypted token is not tampered using the hash and
then decrypt it
We check the decrypted token in the session-tracking-table for a
not-expired entry and get the actual username (expiry to be managed
by code?)
If the username is found, based on allowed roles, allowed operations
are configured
More details:
Since client is a java client, the first request can be a POST
containing the credentials. However, this looks like it may expose
the credentials before the https gets established. Hence should
there be a dummy GET to a secured resource so that https is
established first?
Assuming above is required, the second request is a LoginAction POST
with credentials. This request is handled manually (not using
container's authorisation). Is this right?
The above LoginAction returns the user the combination of encrypted
token + hash
User sets it to the header that is used by BASIC authentication
mechanism (field username)
We implement a JAASRealm to decrypt and validate the token, and find
the roles allowed
The rest of authorisation process is taken care of by the container
with the WebResourceCollection defined in the web.xml
Is this the correct approach?
Why not simplify it to the following?
For first request:
User establishes HTTPS connection to server (service does not listen on any
other ports) and POSTs credentials to login service.
Server replies with HSTS header to ensure all further communication
is HTTPS.
If credentials are valid we:
Generate a random temporary token which is securely generated using a CSPRNG. Make this long enough to be secure (128 bit).
Store the random token on server mapping it to actual username.
Send the random token to the client
For subsequent requests:
Client sends token in a custom HTTP header over HTTPS.
Token is located in the DB and mapped to the username. If found access is configured based on allowed roles and allowed operations.
If not found user is considered unauthenticated and will have to authenticate with the login service again to get a new token.
On the server side the token will be stored with an expiry date. On each access to the service this date will be updated to create a sliding expiration. There will be a job that will run every few minutes to delete expired tokens and the query that checks the token for a valid session will only check those that have not deemed to have expired (to prevent permanent sessions if the scheduled job fails for any reason).
There is no need to hash and encrypt the tokens within the database - it adds no real value apart from a touch of security through obscurity. You could just hash though. This would prevent an attacker that managed to get at the session data table from hijacking existing user sessions.
The approach looks ok. Not very secure.
Let me highlight some of the attacks possible with the request.
Man-In-the-middle attack in a POST request, the user can tamper with the request and server does not have any way to ensure the data is not tampered.
Replay attack: In this, the attacker does not tamper with the request. The attacker taps the request and sends it to the server multiple times in a short duration, though it is a valid request, the server processes the request multiple times, which is not needed
Please read about Nonce.
In the first step, the user sends his credentials i.e username and password to the login service and if you have a web based application that also uses the same password it might be dangerous. If in case password in compromised, API and web everything is exposed, please use a different PIN for API access. Also, ensure decrypted token as specified by you, expires after a certain time.
Ensure the service (application server) tomcat. jboss never returns a server page in case of internal error, this gives the attacker extra information of the server where the app is deployed.
-- MODIFIED BASED ON SECOND POST --
Yes your correct if your using mutual SSL, but in case its a one way access you don't have the client certificates. It would be good if you just double ensured everything in the request, just like signed (signature) SOAP, one of the strong data transfer mechanism. But replay attack is a possibility with HTTPS, just handle that. Rest use tokens encryption is good. And why not ask the client to decrypt the token with the password and return the output of the decryption by this you can validate the output, if it is present in your database ? This approach the user does not send the password over the wire even if it is HTTPS ?