I have an api that returns this after authentication
"token_type":"Bearer",
"expires_in":86400,
"access_token":"XXXXXXXXX",
"refresh_token":"XXXXXXXXXXX"
I have successfully saved the access token, refresh token and the expires_in in an sqlite database. How can i check if the token has expired ?
Here is what i have so far
if(new Date().after(expiresAt)){
Toast.makeText(this, "Token expired", Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(this, "You still have time", Toast.LENGTH_SHORT).show();
}
expiresAt contains the expires_in value
I would suggest you to use framework such as retrofit to help you manage the authentication without checking the expiresAt in every single API call.
In Retrofit, or its HTTP client OkHttp. You can handle authentication by using new Authenticator API, designed specifically for your refresh token/auto login scenario. You may refer to this link for implementation details refreshing-oauth-token-using-retrofit-without-modifying-all-calls
Whenever your API call return a "401 unauthorized" respond, it will trigger authenticator method, run your refresh token API and resend the original request with new access token.
Related
I'm developing an Android app which is using Oauth2 tokens to get authorization in order to access secured resources. I'm using a third party platform as the authentication server (using OpenId Connect). Basically my problem is that I want to deal with an expired refresh token.
Current scenario
I've got a NetUtils class which acts like a singleton and manages all my requests using a secured rest template. That rest template injects the required Authorization header for each request using a request wrapper. The NetUtils class deals whith tokens and timeouts, saving them in user preferences and refreshing them when it's needed.
However, the problem comes when the refresh token itself expires. As I'm using the Authorization code flow, I need to open a WebView and redirect the user to the login page, but I notice it when the NetUtils class determinates the refresh token has expired. Ideally, the app would launch a WebView, the user would login again and the stored request would be executed. Here it is my code to refresh the access token:
private AccessToken refreshToken(String idClient, String clientSecret, AccessToken accessToken) {
MultiValueMap<String, String> clientAuthenticationForm = new LinkedMultiValueMap<>();
clientAuthenticationForm.add("grant_type", "refresh_token");
clientAuthenticationForm.add("refresh_token", accessToken.getRefreshToken());
clientAuthenticationForm.add("client_id", idClient);
clientAuthenticationForm.add("client_secret", clientSecret);
try {
long lastClientRefresh = mPrefs.getLong(Preferences.LAST_LOGIN_TIME, Long.MIN_VALUE);
boolean refreshTokenExpired = lastClientRefresh
+ TimeUnit.SECONDS.toMillis(accessToken.getRefreshExpiresIn()) < System
.currentTimeMillis();
if (!refreshTokenExpired) {
return regularRestTemplate
.postForEntity(tokenUrl(), clientAuthenticationForm, AccessToken.class)
.getBody();
}else{
//How to cope with this?
return null;
}
} catch (Exception ex) {
Log.e(TAG, ex.getMessage(), ex);
throw ex;
}
}
Other choice
Other choice would be to make the refresh token long lived and refresh it each time the app starts, for example. I have to mention that client_id and client_secret are currently being hardcoded in the app (although client credential grants are not meant to be enabled in production, so there's still the need to provide a username and password to retrieve a token).
What would be the best practice here?
I think I can't suggest you how to code in Java, but I also had some troubles with refresh_token while creating application in PHP so maybe my thoughts will help you with something.
At first I was looking for refresh_token which never expires (like in Google API) so I can even hardcode it and use whenever I want to create a new access_token. Anyway it's really hard to do in oAuth2. So I have found a interesting look on this problem here:
Why do access tokens expire?
It showed me a bit other way to work with refresh_token. I have set on my oAuth service that it generates and returns a new refresh_token everytime I use refresh_token to obtain a new access_token. That part helped me most:
https://bshaffer.github.io/oauth2-server-php-docs/grant-types/refresh-token/
And there we got something like:
$server = new OAuth2\Server($storage, array(
'always_issue_new_refresh_token' => true, // this part
'refresh_token_lifetime' => 2419200,
));
In this case I have a long live refresh_token which I can store somewhere and when I need it I will use it to get a new access_token, but response will also provide me a new refresh_token which I can store again and use it later for obtaining a new access_token.
So in your case I think the best way is to keep generating refresh_token everytime you ask for access_token with refresh_token. And if user will not use your APP for longer time, I think he should authorize himself again.
I'm using GoogleAuthUtil in Google Play Services on Android. After calling GoogleAuthUtil.getToken(context, userName, scope), I got a token like this:
ya29.wQBWztab5kcgMLcMbAI0LwFzHC_DPrxauSWbX4P6KOnBEOgjcm9V7OI9AFr6JGxDY54gP00RemzzgML56_gWRHn8Q5jK16BLY-0y83Gc5vfe3xN-QpyM4d7z
This is an access_token, which can be used in calling Google Apis. Then, how can I get a refresh token to refresh this access_token, because I also use Google oauth java library and YouTube Java Library in my Android project, I want to use these two libraries to maintain/manage the access_token, refresh token and expires_in values. (When using Google oauth java library, the TokenResponse it returned contains access_token, refresh token and expires_in)
Thanks in advance.
You cannot directly get a refreshToken using GoogleAuthUtil.getToken() but if you call getToken() each time you get a 401 error, GoogleAuthUtil will return you a new valid token if needed.
In order to get a refresh token, make sure that your scope is in the following format:
Account account = new Account(mEmail, GoogleAuthUtil.GOOGLE_ACCOUNT_TYPE);
mScope="oauth2:server:client_id:"+ OAUTH_WEBCOMPONENT_ID+":api_scope:"+"https://www.googleapis.com/auth/userinfo.email";
return GoogleAuthUtil.getToken(mActivity, account, mScope);
This will give you an authorization code, which can be sent to your web component.
Your webcomponent than can use this authorization code only once to get an access token and refresh token with this code. You have to save the refresh token in your database, so that when the access code is no longer valid you can get a new access token when needed.
POST /oauth2/v3/token HTTP/1.1
Host: www.googleapis.com
Content-length: 233
content-type: application/x-www-form-urlencoded
user-agent: google-oauth-playground
code=4%2FVL2YMuPMheOP2-0vyKBSfGd-4er5GsMY17Ecp8ITK4U&redirect_uri=https%3A%2F%2Fdevelopers.google.com%2Foauthplayground&client_id=407408718192.apps.googleusercontent.com&client_secret=************&scope=&grant_type=authorization_code
You can simulate how this works here:
https://developers.google.com/oauthplayground/
Call requestServerAuthCode(String, true) instead requestServerAuthCode(String) which forces the request to include a refresh_token when it succeeds.
https://developers.google.com/android/reference/com/google/android/gms/auth/api/signin/GoogleSignInOptions.Builder.html#requestServerAuthCode(java.lang.String, boolean)
val task = GoogleSignIn.getSignedInAccountFromIntent(data);
task.addOnSuccessListener {
val account = task.getResult(ApiException::class.java)
val authCode = account!!.serverAuthCode
// Send authcode to server to exchange access and refresh tokens.
exchangeAuthCodeForAccessToken(authCode)
}
I have an android app which uses the spring android library to communicate with my restful api. I'm not sure how to handle the scenario when the token for my client expires. What I'd like to to is to capture any 401 error and simply fetch a new token and retry the request.
I've created a ResponseErrorHandler and wired that up to my rest template:
public class UnauthorizedErrorHandler implements ResponseErrorHandler {
....
public void handleError(ClientHttpResponse response) throws IOException {
if (response.getStatusCode().value() == HttpStatus.SC_UNAUTHORIZED) { // 401 error
// fetch a new token
// Retry request now that we have a new token????
}
}
}
My problem is that I have no access to the originating request in the Response error handler. Unless I'm missing something, this strategy seems reasonable, but I'm not sure how to make it work. This also seems like a typical scenario for any client that is working with OAuth tokens, so hopefully someone out there can point me in the right direction.
If the token has expired then you should ask the user to login again.
Think about a user removing your OAuth app access, your app will received an expired token or similar error, and you should have the user login and give your app access again.
If you are not referring to an OAuth token, but your own API, then your should create some sort of mechanism to update the token to be used by the client. For example, you can send a header with the new token on your response asking the user to start using the new value from that point onwards, or as part of the response body or a push notification requesting a token exchange, etc.
I am new to the volley library and try to figure out what is the best way to do the following.
My REST Api uses Basic Authentication first and if succeed they return a Access Token to use from that point. Because my Acces Token can expire, this is a requirement.
I want to call my api method http://myserver/test
I get back a 401 (Unauthorized).
I want to call http://myserver/auth using basic authentication
I get back a Access Token
Set the header to "Authentication: Session " + AccessToken
I want to "retry" the request to http://myserver/test.
Update
So basically what i want to do is. If a request failed with a given status code, i want to do a other request and after that retry the first one.
Thanks in advance!
I am using a similar approach using Volley.
Have listener from where the request is sent., Everytime when you get response check for session, if session is expired, save existing listeners temporarily.
Create new listeners & get Token, if success, resend request with oldTemp Listeners, so the request is sent back to original request.
Working app in PlayStore with similar approach.
Should I be invalidating and requesting a new token every time I need to make a request using a Google auth token from the AccountManager or is there an 'expired-at' time stamp I can use to see if it is still valid.
There is no expiry time provided in the HTTP response from the Google service, so I think you need to ensure that if the auth-token fails to provide access, you use that as the trigger to get a new auth token. Or you could acquire a new token each time the application starts, or create your own timeout.
http://code.google.com/apis/accounts/docs/AuthForInstalledApps.html
Looking at the HTTP response, the status code is 302 (it's redirecting you to provide auth token) and the "Set-Cookie" field in the header is not present. You could key off that.
if (res.getStatusLine().getStatusCode() == 302 && res.getHeaders("Set-Cookie").length == 0) {
// we need a new token
// invalidate account manager logic here
}
Failing to get that cookie from Google seems to signify it's time to grab a new token from AccountManager.
As there is no accepted answer yet:
I do it by firing my requests in a try block, then catching any exceptions and check if it's a 401 with if (e.getMessage().equals("401 Unauthorized")) { ... }.
Then, invalidate the auth token, request a new one and retry the request.
You need to call invalidateAuthToken(String, String) when you know the token has expired. i.e. when the request fails with an authentication error.