All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.gematik.ti.epa.vzd.client.invoker.auth.RetryingOAuth Maven / Gradle / Ivy

There is a newer version: 1.0.1
Show newest version
/*
 * Copyright (c) 2020 gematik GmbH
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *    http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package de.gematik.ti.epa.vzd.client.invoker.auth;

import de.gematik.ti.epa.vzd.client.invoker.Pair;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.util.List;
import java.util.Map;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.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;

public class RetryingOAuth extends OAuth implements Interceptor {

  private final 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,
      OAuthFlow flow,
      String clientSecret,
      Map parameters
  ) {
    this(OAuthClientRequest.tokenLocation(tokenUrl)
        .setClientId(clientId)
        .setClientSecret(clientSecret));
    setFlow(flow);
    if (parameters != null) {
      for (String paramName : parameters.keySet()) {
        tokenRequestBuilder.setParameter(paramName, parameters.get(paramName));
      }
    }
  }

  public void setFlow(OAuthFlow flow) {
    switch (flow) {
      case accessCode:
        tokenRequestBuilder.setGrantType(GrantType.AUTHORIZATION_CODE);
        break;
      case implicit:
        tokenRequestBuilder.setGrantType(GrantType.IMPLICIT);
        break;
      case password:
        tokenRequestBuilder.setGrantType(GrantType.PASSWORD);
        break;
      case application:
        tokenRequestBuilder.setGrantType(GrantType.CLIENT_CREDENTIALS);
        break;
      default:
        break;
    }
  }

  @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.url().toString()).
                setAccessToken(requestAccessToken).
                buildHeaderMessage();
      } catch (OAuthSystemException e) {
        throw new IOException(e);
      }

      Map 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;
  }

  // Applying authorization to parameters is performed in the retryingIntercept method
  @Override
  public void applyToParams(List queryParams, Map headerParams, Map cookieParams) {
    // No implementation necessary
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy