Best way to handle 401 errors with spring android - android

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.

Related

Android login via Google Sign-In with a Spring Boot backend

As the title says, I'm trying to use the Google Sign-In API with a Spring Boot backend server, as described here.
Just to describe the context, the Spring backend is basically a resource+authentication server, that is currently providing Oauth2 authentication to a second spring boot application containing the frontend website, via Google SSO or simple form login (similar to what's described here).
My original idea was to mimic the #EnableOauth2Sso annotation by simply providing an access token to the android app and attach it to every request as "Bearer ".
Using the user credentials for this was pretty straightforward: I simply make a request to the server at "/oauth/token", using those credentials inserted by the user as authentication and I correctly receive the access token.
Now, I have absolutely no idea on how to build a similar procedure with the Google API in Android. The tutorial page I linked before describes how to get a token ID and how the server should validate it, but after that I don't know what to do.
So far I've managed to add a filter to the security chain that simply checks the token like this:
private Authentication attemptOpenIDAuthentication(#NonNull String tokenString){
String clientId = authServices.getClientId();
GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(transport, factory)
.setAudience(Arrays.asList(clientId, androidClient))
.build();
try {
GoogleIdToken token = verifier.verify(tokenString);
if (token != null) {
return authServices.loadAuthentication(token.getPayload());
} else {
throw new InvalidTokenException("ID token is null");
}
} catch (GeneralSecurityException | IOException e) {
throw new BadCredentialsException("Could not validate ID token");
}
}
This manages indeed to create an Authentication object, but how can I generate an access token after the authentication filtering?
To recap, so far I've got:
The Android app successfully retrieves the Google token ID and sends it to the server
The server sucessfully intercepts the request and validates the token
I'm basically missing the third point where I return a proper access token to the Android client.
Here you are a simple scheme to better understand the situation:
Is there any other way to validate the token and get an access token from the server, or should I completely change the authentication procedure on Android?
As far as I can tell: Yes, you need an access token from the server. If I understand this correctly, a webapp is already authenticated via Oauth on your backend, so the procedure is similar here: Load the user with the google-ID and generate a token. In my application I used a JWT which is valid for 30 days. If the token expires, the Google authentication in the app is usually still valid, so the token can be renewed using the Google ID. With Oauth you can also send a refresh-token directly.
It is important that the app always checks the Google authentication first and only in a second step that of the backend.
For the Authentication process on the backend u may need to manually implement a dedicated securityConfiguration for this. Have a look at the jhipster project, they implemented a custom jwt-authentication which may give you an idea how it works.

Oauth2 refresh token renewal in Android

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.

How to re use access token for future request and how to refresh access token Android Outlook API

In the example OutlookQuickStart for Android works fine in the first request after logon().Now I want to keep connect my app to that user and continue checking for new emails.. How can I re use the access token and build the request to check for new emails? Do I have to save the access token, refresh token ?
How I can refresh the token in Android if it is expired.
According to the documentation for the auth library at https://github.com/AzureAD/azure-activedirectory-library-for-android, the library caches the token and refresh token for you. So you would just use acquireTokenSilentSync to get the token each time you need it. That function would return the current token from the cache if it is still valid, and would refresh it if it is expired.
UPDATE: I've taken a closer look at the sample you're using and the Outlook SDK that it uses. The key thing here is the DependencyResolver object. You pass that object to the OutlookClient constructor. Then anytime you make an API call with that OutlookClient, it just calls the getCredentials override that you supply when you create the DependencyResolver.
So as the sample stands, you should be able to make multiple calls through that OutlookClient without having to change it at all. However, after an hour, when the access token expires, calls will start to fail. The fix for that would be to change the getCredentials override to always call acquireTokenSilentSync. Something like:
#Override
public Credentials getCredentials() {
logger.debug("getCredentials in resolver called");
AuthenticationResult result = mAuthContext.acquireTokenSilentSync(
scopes,
getResources().getString(R.string.AADClientId),
UserIdentifier.getAnyUser());
logger.debug("AcquireTokenSilentSync SUCCESS");
logger.debug("Token expires: ", result.getExpiresOn());
logger.debug("Token: ", result.getAccessToken());
return new OAuthCredentials(result.getAccessToken());
}
Caveat: I'm unable to run this code to validate it due to problems getting the Android emulator running on my dev machine :(.

Reauthenticate after Session has expired in volley

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.

Dealing with expired auth token android account manager

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.

Categories

Resources