forked from loafle/openapi-generator-original
[okhttp-gson] Add support for OAuth access token retry (#1058)
* Add support for access token retry in okhttp-gson lib * Update expected number of generated files in test * Update samples * Update security samples * Fix default user-agent and update samples
This commit is contained in:
committed by
William Cheng
parent
1b2f3fbfb6
commit
0e045bee1b
@@ -23,6 +23,9 @@ import org.threeten.bp.LocalDate;
|
||||
import org.threeten.bp.OffsetDateTime;
|
||||
import org.threeten.bp.format.DateTimeFormatter;
|
||||
|
||||
import org.apache.oltu.oauth2.client.request.OAuthClientRequest.TokenRequestBuilder;
|
||||
import org.apache.oltu.oauth2.common.message.types.GrantType;
|
||||
|
||||
import javax.net.ssl.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -49,6 +52,7 @@ import org.openapitools.client.auth.Authentication;
|
||||
import org.openapitools.client.auth.HttpBasicAuth;
|
||||
import org.openapitools.client.auth.ApiKeyAuth;
|
||||
import org.openapitools.client.auth.OAuth;
|
||||
import org.openapitools.client.auth.RetryingOAuth;
|
||||
|
||||
public class ApiClient {
|
||||
|
||||
@@ -77,6 +81,39 @@ public class ApiClient {
|
||||
* Constructor for ApiClient
|
||||
*/
|
||||
public ApiClient() {
|
||||
init();
|
||||
|
||||
// Setup authentications (key: authentication name, value: authentication).
|
||||
authentications.put("api_key", new ApiKeyAuth("header", "api_key"));
|
||||
authentications.put("api_key_query", new ApiKeyAuth("query", "api_key_query"));
|
||||
authentications.put("http_basic_test", new HttpBasicAuth());
|
||||
authentications.put("petstore_auth", new OAuth());
|
||||
// Prevent the authentications from being modified.
|
||||
authentications = Collections.unmodifiableMap(authentications);
|
||||
}
|
||||
|
||||
/*
|
||||
* Constructor for ApiClient to support access token retry on 401/403
|
||||
*/
|
||||
public ApiClient(
|
||||
String clientId,
|
||||
String clientSecret,
|
||||
Map<String, String> parameters
|
||||
) {
|
||||
init();
|
||||
|
||||
RetryingOAuth retryingOAuth = new RetryingOAuth("", clientId, GrantType.valueOf("implicit"), clientSecret, parameters);
|
||||
authentications.put(
|
||||
"petstore_auth",
|
||||
retryingOAuth
|
||||
);
|
||||
httpClient.interceptors().add(retryingOAuth);
|
||||
|
||||
// Prevent the authentications from being modified.
|
||||
authentications = Collections.unmodifiableMap(authentications);
|
||||
}
|
||||
|
||||
private void init() {
|
||||
httpClient = new OkHttpClient();
|
||||
|
||||
|
||||
@@ -87,14 +124,7 @@ public class ApiClient {
|
||||
// Set default User-Agent.
|
||||
setUserAgent("OpenAPI-Generator/1.0.0/java");
|
||||
|
||||
// Setup authentications (key: authentication name, value: authentication).
|
||||
authentications = new HashMap<String, Authentication>();
|
||||
authentications.put("api_key", new ApiKeyAuth("header", "api_key"));
|
||||
authentications.put("api_key_query", new ApiKeyAuth("query", "api_key_query"));
|
||||
authentications.put("http_basic_test", new HttpBasicAuth());
|
||||
authentications.put("petstore_auth", new OAuth());
|
||||
// Prevent the authentications from being modified.
|
||||
authentications = Collections.unmodifiableMap(authentications);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -484,6 +514,20 @@ public class ApiClient {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to configure the token endpoint of the first oauth found in the apiAuthorizations (there should be only one)
|
||||
* @return Token request builder
|
||||
*/
|
||||
public TokenRequestBuilder getTokenEndPoint() {
|
||||
for (Authentication apiAuth : authentications.values()) {
|
||||
if (apiAuth instanceof RetryingOAuth) {
|
||||
RetryingOAuth retryingOAuth = (RetryingOAuth) apiAuth;
|
||||
return retryingOAuth.getTokenRequestBuilder();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the given parameter object into string.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
package org.openapitools.client.auth;
|
||||
|
||||
import com.squareup.okhttp.OkHttpClient;
|
||||
import com.squareup.okhttp.MediaType;
|
||||
import com.squareup.okhttp.Request;
|
||||
import com.squareup.okhttp.RequestBody;
|
||||
import com.squareup.okhttp.Response;
|
||||
|
||||
import org.apache.oltu.oauth2.client.HttpClient;
|
||||
import org.apache.oltu.oauth2.client.request.OAuthClientRequest;
|
||||
import org.apache.oltu.oauth2.client.response.OAuthClientResponse;
|
||||
import org.apache.oltu.oauth2.client.response.OAuthClientResponseFactory;
|
||||
import org.apache.oltu.oauth2.common.exception.OAuthProblemException;
|
||||
import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
public class OAuthOkHttpClient implements HttpClient {
|
||||
private OkHttpClient client;
|
||||
|
||||
public OAuthOkHttpClient() {
|
||||
this.client = new OkHttpClient();
|
||||
}
|
||||
|
||||
public OAuthOkHttpClient(OkHttpClient client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends OAuthClientResponse> T execute(OAuthClientRequest request, Map<String, String> headers,
|
||||
String requestMethod, Class<T> responseClass)
|
||||
throws OAuthSystemException, OAuthProblemException {
|
||||
|
||||
MediaType mediaType = MediaType.parse("application/json");
|
||||
Request.Builder requestBuilder = new Request.Builder().url(request.getLocationUri());
|
||||
|
||||
if(headers != null) {
|
||||
for (Entry<String, String> entry : headers.entrySet()) {
|
||||
if (entry.getKey().equalsIgnoreCase("Content-Type")) {
|
||||
mediaType = MediaType.parse(entry.getValue());
|
||||
} else {
|
||||
requestBuilder.addHeader(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RequestBody body = request.getBody() != null ? RequestBody.create(mediaType, request.getBody()) : null;
|
||||
requestBuilder.method(requestMethod, body);
|
||||
|
||||
try {
|
||||
Response response = client.newCall(requestBuilder.build()).execute();
|
||||
return OAuthClientResponseFactory.createCustomResponse(
|
||||
response.body().string(),
|
||||
response.body().contentType().toString(),
|
||||
response.code(),
|
||||
responseClass);
|
||||
} catch (IOException e) {
|
||||
throw new OAuthSystemException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
// Nothing to do here
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
package org.openapitools.client.auth;
|
||||
|
||||
import com.squareup.okhttp.Interceptor;
|
||||
import com.squareup.okhttp.OkHttpClient;
|
||||
import com.squareup.okhttp.Request;
|
||||
import com.squareup.okhttp.Response;
|
||||
|
||||
import org.apache.oltu.oauth2.client.OAuthClient;
|
||||
import org.apache.oltu.oauth2.client.request.OAuthBearerClientRequest;
|
||||
import org.apache.oltu.oauth2.client.request.OAuthClientRequest;
|
||||
import org.apache.oltu.oauth2.client.request.OAuthClientRequest.TokenRequestBuilder;
|
||||
import org.apache.oltu.oauth2.client.response.OAuthJSONAccessTokenResponse;
|
||||
import org.apache.oltu.oauth2.common.exception.OAuthProblemException;
|
||||
import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
|
||||
import org.apache.oltu.oauth2.common.message.types.GrantType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.util.Map;
|
||||
|
||||
public class RetryingOAuth extends OAuth implements Interceptor {
|
||||
private OAuthClient oAuthClient;
|
||||
|
||||
private TokenRequestBuilder tokenRequestBuilder;
|
||||
|
||||
public RetryingOAuth(OkHttpClient client, TokenRequestBuilder tokenRequestBuilder) {
|
||||
this.oAuthClient = new OAuthClient(new OAuthOkHttpClient(client));
|
||||
this.tokenRequestBuilder = tokenRequestBuilder;
|
||||
}
|
||||
|
||||
public RetryingOAuth(TokenRequestBuilder tokenRequestBuilder) {
|
||||
this(new OkHttpClient(), tokenRequestBuilder);
|
||||
}
|
||||
|
||||
public RetryingOAuth(
|
||||
String tokenUrl,
|
||||
String clientId,
|
||||
GrantType grantType,
|
||||
String clientSecret,
|
||||
Map<String, String> parameters
|
||||
) {
|
||||
this(OAuthClientRequest.tokenLocation(tokenUrl)
|
||||
.setClientId(clientId)
|
||||
.setGrantType(grantType)
|
||||
.setClientSecret(clientSecret));
|
||||
|
||||
if (parameters != null) {
|
||||
for (String paramName : parameters.keySet()) {
|
||||
tokenRequestBuilder.setParameter(paramName, parameters.get(paramName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response intercept(Chain chain) throws IOException {
|
||||
return retryingIntercept(chain, true);
|
||||
}
|
||||
|
||||
private Response retryingIntercept(Chain chain, boolean updateTokenAndRetryOnAuthorizationFailure) throws IOException {
|
||||
Request request = chain.request();
|
||||
|
||||
// If the request already has an authorization (e.g. Basic auth), proceed with the request as is
|
||||
if (request.header("Authorization") != null) {
|
||||
return chain.proceed(request);
|
||||
}
|
||||
|
||||
// Get the token if it has not yet been acquired
|
||||
if (getAccessToken() == null) {
|
||||
updateAccessToken(null);
|
||||
}
|
||||
|
||||
OAuthClientRequest oAuthRequest;
|
||||
if (getAccessToken() != null) {
|
||||
// Build the request
|
||||
Request.Builder requestBuilder = request.newBuilder();
|
||||
|
||||
String requestAccessToken = getAccessToken();
|
||||
try {
|
||||
oAuthRequest =
|
||||
new OAuthBearerClientRequest(request.urlString()).
|
||||
setAccessToken(requestAccessToken).
|
||||
buildHeaderMessage();
|
||||
} catch (OAuthSystemException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
|
||||
Map<String, String> headers = oAuthRequest.getHeaders();
|
||||
for (String headerName : headers.keySet()) {
|
||||
requestBuilder.addHeader(headerName, headers.get(headerName));
|
||||
}
|
||||
requestBuilder.url(oAuthRequest.getLocationUri());
|
||||
|
||||
// Execute the request
|
||||
Response response = chain.proceed(requestBuilder.build());
|
||||
|
||||
// 401/403 response codes most likely indicate an expired access token, unless it happens two times in a row
|
||||
if (
|
||||
response != null &&
|
||||
( response.code() == HttpURLConnection.HTTP_UNAUTHORIZED ||
|
||||
response.code() == HttpURLConnection.HTTP_FORBIDDEN ) &&
|
||||
updateTokenAndRetryOnAuthorizationFailure
|
||||
) {
|
||||
try {
|
||||
if (updateAccessToken(requestAccessToken)) {
|
||||
response.body().close();
|
||||
return retryingIntercept(chain, false);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
response.body().close();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
return response;
|
||||
}
|
||||
else {
|
||||
return chain.proceed(chain.request());
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if the access token has been updated
|
||||
*/
|
||||
public synchronized boolean updateAccessToken(String requestAccessToken) throws IOException {
|
||||
if (getAccessToken() == null || getAccessToken().equals(requestAccessToken)) {
|
||||
try {
|
||||
OAuthJSONAccessTokenResponse accessTokenResponse =
|
||||
oAuthClient.accessToken(tokenRequestBuilder.buildBodyMessage());
|
||||
if (accessTokenResponse != null && accessTokenResponse.getAccessToken() != null) {
|
||||
setAccessToken(accessTokenResponse.getAccessToken());
|
||||
return !getAccessToken().equals(requestAccessToken);
|
||||
}
|
||||
} catch (OAuthSystemException | OAuthProblemException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public TokenRequestBuilder getTokenRequestBuilder() {
|
||||
return tokenRequestBuilder;
|
||||
}
|
||||
|
||||
public void setTokenRequestBuilder(TokenRequestBuilder tokenRequestBuilder) {
|
||||
this.tokenRequestBuilder = tokenRequestBuilder;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user