Java tutorial
/* * Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com * The software in this package is published under the terms of the CPAL v1.0 * license, a copy of which has been included with this distribution in the * LICENSE.txt file. */ package org.mule.service.oauth.internal; import static java.lang.String.format; import static java.lang.Thread.currentThread; import static java.util.concurrent.CompletableFuture.completedFuture; import static org.apache.commons.codec.binary.Base64.encodeBase64String; import static org.mule.runtime.api.i18n.I18nMessageFactory.createStaticMessage; import static org.mule.runtime.oauth.api.state.ResourceOwnerOAuthContext.DEFAULT_RESOURCE_OWNER_ID; import static org.mule.service.oauth.internal.OAuthConstants.CLIENT_ID_PARAMETER; import static org.mule.service.oauth.internal.OAuthConstants.CLIENT_SECRET_PARAMETER; import static org.mule.service.oauth.internal.OAuthConstants.GRANT_TYPE_CLIENT_CREDENTIALS; import static org.mule.service.oauth.internal.OAuthConstants.GRANT_TYPE_PARAMETER; import static org.mule.service.oauth.internal.OAuthConstants.SCOPE_PARAMETER; import static org.slf4j.LoggerFactory.getLogger; import org.mule.runtime.api.el.MuleExpressionLanguage; import org.mule.runtime.api.exception.MuleException; import org.mule.runtime.api.lifecycle.LifecycleException; import org.mule.runtime.api.lifecycle.Startable; import org.mule.runtime.api.lock.LockFactory; import org.mule.runtime.http.api.client.HttpClient; import org.mule.runtime.oauth.api.ClientCredentialsOAuthDancer; import org.mule.runtime.oauth.api.exception.RequestAuthenticationException; import org.mule.runtime.oauth.api.exception.TokenNotFoundException; import org.mule.runtime.oauth.api.exception.TokenUrlResponseException; import org.mule.runtime.oauth.api.state.DefaultResourceOwnerOAuthContext; import org.mule.runtime.oauth.api.state.ResourceOwnerOAuthContext; import org.mule.service.oauth.internal.state.TokenResponse; import java.nio.charset.Charset; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.function.Function; import org.slf4j.Logger; /** * Provides OAuth dance support for client-credentials grant-type. * * @since 1.0 */ public class DefaultClientCredentialsOAuthDancer extends AbstractOAuthDancer implements Startable, ClientCredentialsOAuthDancer { private static final Logger LOGGER = getLogger(DefaultClientCredentialsOAuthDancer.class); private final boolean encodeClientCredentialsInBody; public DefaultClientCredentialsOAuthDancer(String clientId, String clientSecret, String tokenUrl, String scopes, boolean encodeClientCredentialsInBody, Charset encoding, String responseAccessTokenExpr, String responseRefreshTokenExpr, String responseExpiresInExpr, Map<String, String> customParametersExprs, Function<String, String> resourceOwnerIdTransformer, LockFactory lockProvider, Map<String, DefaultResourceOwnerOAuthContext> tokensStore, HttpClient httpClient, MuleExpressionLanguage expressionEvaluator) { super(clientId, clientSecret, tokenUrl, encoding, scopes, responseAccessTokenExpr, responseRefreshTokenExpr, responseExpiresInExpr, customParametersExprs, resourceOwnerIdTransformer, lockProvider, tokensStore, httpClient, expressionEvaluator); this.encodeClientCredentialsInBody = encodeClientCredentialsInBody; } @Override public void start() throws MuleException { super.start(); try { refreshToken().get(); } catch (ExecutionException e) { super.stop(); throw new LifecycleException(e.getCause(), this); } catch (InterruptedException e) { super.stop(); currentThread().interrupt(); throw new LifecycleException(e, this); } } @Override public CompletableFuture<String> accessToken() throws RequestAuthenticationException { final String accessToken = getContext().getAccessToken(); if (accessToken == null) { throw new RequestAuthenticationException(createStaticMessage(format("No access token found. " + "Verify that you have authenticated before trying to execute an operation to the API."))); } // TODO MULE-11858 proactively refresh if the token has already expired based on its 'expiresIn' parameter return completedFuture(accessToken); } @Override public CompletableFuture<Void> refreshToken() { final Map<String, String> formData = new HashMap<>(); formData.put(GRANT_TYPE_PARAMETER, GRANT_TYPE_CLIENT_CREDENTIALS); if (scopes != null) { formData.put(SCOPE_PARAMETER, scopes); } String authorization = null; if (encodeClientCredentialsInBody) { formData.put(CLIENT_ID_PARAMETER, clientId); formData.put(CLIENT_SECRET_PARAMETER, clientSecret); } else { authorization = "Basic " + encodeBase64String(format("%s:%s", clientId, clientSecret).getBytes()); } try { TokenResponse tokenResponse = invokeTokenUrl(tokenUrl, formData, authorization, false, encoding); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Retrieved access token, refresh token and expires from token url are: %s, %s, %s", tokenResponse.getAccessToken(), tokenResponse.getRefreshToken(), tokenResponse.getExpiresIn()); } final DefaultResourceOwnerOAuthContext defaultUserState = (DefaultResourceOwnerOAuthContext) getContext(); defaultUserState.setAccessToken(tokenResponse.getAccessToken()); defaultUserState.setExpiresIn(tokenResponse.getExpiresIn()); for (Entry<String, Object> customResponseParameterEntry : tokenResponse.getCustomResponseParameters() .entrySet()) { defaultUserState.getTokenResponseParameters().put(customResponseParameterEntry.getKey(), customResponseParameterEntry.getValue()); } updateResourceOwnerOAuthContext(defaultUserState); return completedFuture(null); } catch (TokenUrlResponseException | TokenNotFoundException e) { final CompletableFuture<Void> exceptionFuture = new CompletableFuture<>(); exceptionFuture.completeExceptionally(e); return exceptionFuture; } } @Override public void invalidateContext() { invalidateContext(DEFAULT_RESOURCE_OWNER_ID); } @Override public ResourceOwnerOAuthContext getContext() { return getContextForResourceOwner(DEFAULT_RESOURCE_OWNER_ID); } }