Skip to content

Instantly share code, notes, and snippets.

@dzolnai
Last active August 18, 2021 07:23
Show Gist options
  • Save dzolnai/9710849 to your computer and use it in GitHub Desktop.
Save dzolnai/9710849 to your computer and use it in GitHub Desktop.
Retrofit OAuth2 refreshing tokens without modifying all requests.
package com.example.yourapp;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import com.example.yourapp.AuthenticationModel;
import retrofit.client.Header;
import retrofit.client.OkClient;
import retrofit.client.Request;
import retrofit.client.Response;
/**
* Extends the default Retrofit Http client with a check if the request got denied because the OAuth
* token expired. In this case
* To be entirely sure that there will be no StackOverFlowErrors using this implementation
* Don't let your authentication model use this as a client.
**/
public class OAuthClient extends OkClient {
private AuthenticationModel _authModel;
public OAuthClient(AuthenticationModel authModel){
_authModel = authModel;
}
/**
* Replaces the access token with the new one in the request.
* @param request Input request to modify
* @return An other request with the same parameters, but the replaced authorization header
*/
private Request changeTokenInRequest(Request request){
List<Header> tempHeaders = request.getHeaders(); // this one is an unmodifiable list.
List<Header> headers = new ArrayList<Header>();
headers.addAll(tempHeaders); // this one is modifiable
Iterator<Header> iter = headers.iterator();
boolean hadAuthHeader = false;
// we check if there was an authentication header in the original request
while(iter.hasNext()){
Header h = iter.next();
if (h.getName().equals("Authorization")){
iter.remove();
hadAuthHeader = true;
}
}
// if there was an authentication header, replace it with another one containing the new access token.
if (hadAuthHeader){
headers.add(new Header("Authorization", "Bearer " + _authModel.getAccessToken()));
}
// everything stays the same, except the headers
return new Request(request.getMethod(), request.getUrl(), headers, request.getBody());
}
@Override
public Response execute(Request request) throws IOException {
Response response = super.execute(request);
// 401: Forbidden, 403: Permission denied
if (response.getStatus() == 401 || response.getStatus() == 403) {
// the next call should be a synchronous call, otherwise it will immediately continue, and use the old token instead.
_authModel.refreshToken();
// the headers should be modified because the access token changed
Request newRequest = changeTokenInRequest(request);
return super.execute(newRequest);
} else {
return response;
}
}
}
// Create your RestAdapter this way
RestAdapter adapter = new Builder()
.setEndpoint(endpoint)
.setClient(new OAuthClient(yourAuthModel)
...
.build();
// Util to convert TypedInput to Java String
public static String stringFromTypedInput(TypedInput input) {
try {
InputStream istream = input.in();
java.util.Scanner s = new java.util.Scanner(istream).useDelimiter("\\A");
return s.hasNext() ? s.next() : "";
} catch (IOException e) {
return null;
}
}
// Example call
private interface OAuthInterface {
@FormUrlEncoded
@POST("/oauth2/access_token")
public void getAccessToken(@Field("username") String username, @Field("password") String password, @Field("client_id") String clientId, @Field("client_secret") String clientSecret, @Field("grant_type") String grantType,
Callback<TokenData> cb);
// NOTICE: This call is synchronous! This should only be called from the OAuthClient.
@FormUrlEncoded
@POST("/oauth2/access_token")
public String refreshToken(@Field("refresh_token") String refreshToken, @Field("client_id") String clientId, @Field("client_secret") String clientSecret, @Field("grant_type") String grantType);
}
@ZkHaider
Copy link

ZkHaider commented Jul 7, 2015

This method here: https://gist.github.com/dzolnai/9710849#file-oauthclient-java-L33

can be improved, I suggest not using Iterators to begin with.

@bahacan19
Copy link

What is your AuthenticationModel class?

@SaurabhSawarkar
Copy link

Can we make synchronous API call in android?

@hjmcoder
Copy link

Hi mr.Zolnai can you please update this to retrofit2 ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment