
com.trimble.id.AuthorizationCodeGrantTokenProvider Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of trimble-id Show documentation
Show all versions of trimble-id Show documentation
Trimble Identity OAuth Client library holds the client classes that are used for communicating with Trimble Identity Service
The newest version!
package com.trimble.id;
import static java.net.URLEncoder.encode;
import java.net.URI;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import static com.trimble.id.AuthenticationConstants.AMPERSAND;
import static com.trimble.id.AuthenticationConstants.AUTHORIZATION_CODE_GRANT;
import static com.trimble.id.AuthenticationConstants.AUTH_CODE_REDIRECT_ERROR;
import static com.trimble.id.AuthenticationConstants.CLIENT_ID;
import static com.trimble.id.AuthenticationConstants.CLIENT_ID_PLACEHOLDER;
import static com.trimble.id.AuthenticationConstants.CODE;
import static com.trimble.id.AuthenticationConstants.CODE_CHALLENGE;
import static com.trimble.id.AuthenticationConstants.CODE_CHALLENGE_METHOD;
import static com.trimble.id.AuthenticationConstants.CODE_CHALLENGE_METHOD_PARAM;
import static com.trimble.id.AuthenticationConstants.CODE_CHALLENGE_PARAM;
import static com.trimble.id.AuthenticationConstants.CODE_VERIFIER;
import static com.trimble.id.AuthenticationConstants.EQUAL_TO;
import static com.trimble.id.AuthenticationConstants.GRANT_TYPE;
import static com.trimble.id.AuthenticationConstants.IDENTITY_PROVIDER;
import static com.trimble.id.AuthenticationConstants.OPENID;
import static com.trimble.id.AuthenticationConstants.QUERY_STRING;
import static com.trimble.id.AuthenticationConstants.QUERY_STRING_MARKER;
import static com.trimble.id.AuthenticationConstants.REDIRECT_URI;
import static com.trimble.id.AuthenticationConstants.REDIRECT_URI_PLACEHOLDER;
import static com.trimble.id.AuthenticationConstants.S256;
import static com.trimble.id.AuthenticationConstants.SCOPE_PLACEHOLDER;
import static com.trimble.id.AuthenticationConstants.SDK_VARIANT;
import static com.trimble.id.AuthenticationConstants.SDK_VERSION;
import static com.trimble.id.AuthenticationConstants.SPACE;
import static com.trimble.id.AuthenticationConstants.STATE;
import static com.trimble.id.CryptographicHelper.createCodeChallenge;
import static com.trimble.id.CryptographicHelper.generateCodeVerifier;
import static com.trimble.id.CryptographicHelper.generateState;
import com.trimble.id.analytics.AnalyticsHttpClient;
/** A token provider based on the OAuth Authorization Code grant type */
public class AuthorizationCodeGrantTokenProvider implements ITokenProvider {
private IEndpointProvider endpointProvider;
private String clientId;
private String clientSecret;
private List scopes;
private String state;
private String identityProvider;
private String codeVerifier;
private String codeChallenge;
private URI redirectUri;
private URI logoutRedirectUri;
private IHttpTokenClient httpTokenClient;
private RefreshableTokenProvider refreshableTokenProvider;
/**
* Public constructor of AuthorizationCodeGrantTokenProvider class
*
* @param endpointProvider The endpoint provider
* @param clientId The client id of the application
* @param redirectUri The redirect URI
*/
public AuthorizationCodeGrantTokenProvider(IEndpointProvider endpointProvider, String clientId, URI redirectUri) {
this.endpointProvider = endpointProvider;
this.clientId = clientId;
this.scopes = new ArrayList();
this.scopes.add(OPENID);
this.redirectUri = redirectUri;
this.httpTokenClient = new HttpTokenClient(endpointProvider);
AnalyticsHttpClient.sendInitEvent(this.getClass().getSimpleName(), this.getClass().getPackage().getName(),
SDK_VERSION, clientId);
}
/**
* Fluent extension to add client secret to the token provider
*
* @param clientSecret The client secret of the application
* @return The token provider with the client secret added
*/
public AuthorizationCodeGrantTokenProvider withClientSecret(String clientSecret) {
this.clientSecret = clientSecret;
return this;
}
/**
* Fluent extension to add scopes to the token provider
*
* @param scopes The scopes of the application
* @return The token provider with the scopes added
*/
public AuthorizationCodeGrantTokenProvider withScopes(String[] scopes) {
this.scopes.addAll(Arrays.asList(scopes));
return this;
}
/**
* Fluent extension to add code verifier to the token provider
*
* @param codeVerifier The code verifier
* @return The token provider with the code verifier added
*/
public AuthorizationCodeGrantTokenProvider withCodeVerifier(String codeVerifier) {
this.codeVerifier = codeVerifier;
return this;
}
/**
* Fluent extension to add code challenge to the token provider
*
* @param codeChallenge The code challenge
* @return The token provider with the code challenge added
*/
public AuthorizationCodeGrantTokenProvider withCodeChallenge(String codeChallenge) {
this.codeChallenge = codeChallenge;
return this;
}
/**
* Fluent extension to use PKCE flow for the token provider
*
* @return The token provider with the PKCE flow enabled
*/
public AuthorizationCodeGrantTokenProvider withProofKeyForCodeExchange() {
this.codeVerifier = generateCodeVerifier();
this.codeChallenge = createCodeChallenge(this.codeVerifier);
return this;
}
/**
* Fluent extension to add state to the token provider
*
* @param state The state
* @return The token provider with the state added
*/
public AuthorizationCodeGrantTokenProvider withState(String state) {
this.state = state;
return this;
}
/**
* Fluent extension to add random state to the token provider
*
* @return The token provider with random state added
*/
public AuthorizationCodeGrantTokenProvider withRandomState() {
this.state = generateState();
return this;
}
/**
* Fluent extension to add logout redirect URI to the token provider
*
* @param logoutRedirectUri The logout redirect URI
* @return The token provider with the logout redirect URI added
*/
public AuthorizationCodeGrantTokenProvider withLogoutRedirect(URI logoutRedirectUri) {
this.logoutRedirectUri = logoutRedirectUri;
return this;
}
/**
* Fluent extension to add identity provider to the token provider
*
* @param identityProvider The identity provider
* @return The token provider with the identity provider added
*/
public AuthorizationCodeGrantTokenProvider withIdentityProvider(String identityProvider) {
this.identityProvider = identityProvider;
return this;
}
/**
* Retrieves an access token for the authenticated user
*
* @return A completable future that contains the access token
*/
@Override
public CompletableFuture getAccessToken() throws SDKClientException {
AnalyticsHttpClient.sendMethodEvent("getAccessToken", SDK_VARIANT, this.getClass().getPackage().getName(),
SDK_VERSION, clientId);
return getRefreshableTokenProvider().getAccessToken();
}
/**
* Retrieves a refresh token for the authenticated user
*
* @return A completable future that contains the refresh token
*/
@Override
public CompletableFuture getRefreshToken() throws SDKClientException {
AnalyticsHttpClient.sendMethodEvent("getRefreshToken", SDK_VARIANT, this.getClass().getPackage().getName(),
SDK_VERSION, clientId);
return getRefreshableTokenProvider().getRefreshToken();
}
/**
* Retrieves an ID token for the authenticated user
*
* @return A completable future that contains the ID token
*/
@Override
public CompletableFuture getIdToken() throws SDKClientException {
AnalyticsHttpClient.sendMethodEvent("getIdToken", SDK_VARIANT, this.getClass().getPackage().getName(),
SDK_VERSION, clientId);
return getRefreshableTokenProvider().getIdToken();
}
/**
* Revokes the refresh token
*
* @return A completable future that contains the status code
*/
@Override
public CompletableFuture revokeRefreshToken() throws SDKClientException {
AnalyticsHttpClient.sendMethodEvent("revokeRefreshToken", SDK_VARIANT, this.getClass().getPackage().getName(),
SDK_VERSION, clientId);
return getRefreshableTokenProvider().revokeRefreshToken();
}
/**
* Create an authorization URI with the provided parameters
*
* @return A completable future that contains the authorization URI
*/
public CompletableFuture createAuthorizationUri() {
AnalyticsHttpClient.sendMethodEvent("createAuthorizationUri", SDK_VARIANT,
this.getClass().getPackage().getName(), SDK_VERSION, clientId);
return getEndpointProvider().getAuthorizationEndpoint().thenApply((endpoint) -> {
try {
String queryString = QUERY_STRING
.replace(SCOPE_PLACEHOLDER,
encode(String.join(SPACE, this.scopes), StandardCharsets.UTF_8.name()))
.replace(REDIRECT_URI_PLACEHOLDER,
encode(this.redirectUri.toString(), StandardCharsets.UTF_8.name()))
.replace(CLIENT_ID_PLACEHOLDER,
encode(this.clientId.toString(), StandardCharsets.UTF_8.name()));
if (this.codeChallenge != null)
queryString = queryString.concat(CODE_CHALLENGE_METHOD).concat(S256).concat(CODE_CHALLENGE)
.concat(this.codeChallenge);
if (this.state != null)
queryString = queryString.concat(STATE).concat(encode(this.state, StandardCharsets.UTF_8.name()));
if (this.identityProvider != null)
queryString = queryString.concat(IDENTITY_PROVIDER)
.concat(encode(this.identityProvider, StandardCharsets.UTF_8.name()));
return URI.create(endpoint.toString().concat(QUERY_STRING_MARKER).concat(queryString));
} catch (Exception e) {
throw new SDKClientException(e, e.getCause(), e.getMessage());
}
});
}
/**
* Checks if the authorization was successful
*
* @param queryString The query string
* @return A completable future that contains the status of the authorization
*/
public CompletableFuture isAuthorizationSuccessful(String queryString) throws SDKClientException {
AnalyticsHttpClient.sendMethodEvent("isAuthorizationSuccessful", SDK_VARIANT,
this.getClass().getPackage().getName(), SDK_VERSION, clientId);
Map paramMap = decodeQueryString(queryString);
if (paramMap.get(CODE) == null) {
AnalyticsHttpClient.sendExceptionEvent("isAuthorizationSuccessful", SDK_VARIANT,
this.getClass().getPackage().getName(), SDK_VERSION, AUTH_CODE_REDIRECT_ERROR, clientId);
throw new SDKClientException(new IllegalArgumentException(AUTH_CODE_REDIRECT_ERROR));
}
// For PKCE flow, call setAccessTokenUsingAuthCodeAndPKCE() method instead of
// setAccessTokenUsingAuthCode() method
if (this.codeVerifier != null) {
return setAccessTokenUsingAuthCodeAndPKCE(paramMap.get(CODE)).thenApply((isSuccessful) -> {
return isSuccessful;
});
} else {
return setAccessTokenUsingAuthCode(paramMap.get(CODE)).thenApply((isSuccessful) -> {
return isSuccessful;
});
}
}
private Map decodeQueryString(String queryString) {
Map paramMap = new HashMap<>();
queryString = queryString.split(" ")[0];
for (String param : queryString.split(AMPERSAND)) {
String[] keyValue = param.split(EQUAL_TO);
if (keyValue.length == 1) {
paramMap.put(keyValue[0], null);
} else {
try {
paramMap.put(keyValue[0], URLDecoder.decode(keyValue[1], StandardCharsets.UTF_8.name()));
} catch (Exception e) {
throw new SDKClientException(e, e.getCause(), e.getMessage());
}
}
}
return paramMap;
}
private CompletableFuture setAccessTokenUsingAuthCode(String authCode) {
List nameValuePairs = new ArrayList();
nameValuePairs.add(new NameValuePair(GRANT_TYPE, AUTHORIZATION_CODE_GRANT));
nameValuePairs.add(new NameValuePair(REDIRECT_URI, this.redirectUri.toString()));
nameValuePairs.add(new NameValuePair(CLIENT_ID, this.clientId));
nameValuePairs.add(new NameValuePair(CODE, authCode));
if (this.codeVerifier != null) {
nameValuePairs.add(new NameValuePair(CODE_VERIFIER, this.codeVerifier));
}
return getHttpTokenClient().getOauthTokens(new TokenRequest(this.clientId, this.clientSecret, nameValuePairs))
.thenApply((tokenResponse) -> {
if (tokenResponse.error_description != null) {
AnalyticsHttpClient.sendExceptionEvent("setAccessTokenUsingAuthCode", SDK_VARIANT,
this.getClass().getPackage().getName(), SDK_VERSION, tokenResponse.error_description,
clientId);
throw new SDKClientException(tokenResponse.error, tokenResponse.error_description);
}
setRefreshableTokenProvider(new RefreshableTokenProvider(this.endpointProvider, this.clientId,
this.clientSecret, tokenResponse.accessToken,
(Long.valueOf(new Date().getTime()) + (tokenResponse.expiresIn * 1000)),
tokenResponse.refreshToken, tokenResponse.idToken, null));
return true;
});
}
private CompletableFuture setAccessTokenUsingAuthCodeAndPKCE(String authCode) {
List nameValuePairs = new ArrayList();
nameValuePairs.add(new NameValuePair(GRANT_TYPE, AUTHORIZATION_CODE_GRANT));
nameValuePairs.add(new NameValuePair(REDIRECT_URI, this.redirectUri.toString()));
nameValuePairs.add(new NameValuePair(CLIENT_ID, this.clientId));
nameValuePairs.add(new NameValuePair(CODE, authCode));
nameValuePairs.add(new NameValuePair(CODE_VERIFIER, this.codeVerifier));
String codeVerifierForSerialRefresh = generateCodeVerifier();
nameValuePairs.add(new NameValuePair(CODE_CHALLENGE_PARAM, createCodeChallenge(codeVerifierForSerialRefresh)));
nameValuePairs.add(new NameValuePair(CODE_CHALLENGE_METHOD_PARAM, S256));
return getHttpTokenClient().getOauthTokens(new TokenRequest(this.clientId, nameValuePairs))
.thenApply((tokenResponse) -> {
if (tokenResponse.error_description != null) {
AnalyticsHttpClient.sendExceptionEvent("setAccessTokenUsingAuthCodeAndPKCE", SDK_VARIANT,
this.getClass().getPackage().getName(), SDK_VERSION, tokenResponse.error_description,
clientId);
throw new SDKClientException(tokenResponse.error, tokenResponse.error_description);
}
setRefreshableTokenProvider(new RefreshableTokenProvider(this.endpointProvider, this.clientId,
this.clientSecret, tokenResponse.accessToken,
(Long.valueOf(new Date().getTime()) + (tokenResponse.expiresIn * 1000)),
tokenResponse.refreshToken, tokenResponse.idToken, codeVerifierForSerialRefresh));
return true;
});
}
/**
* Create a logout URI with the provided parameters
*
* @return A completable future that contains the logout URI
*/
public CompletableFuture createLogoutUri() {
AnalyticsHttpClient.sendMethodEvent("createLogoutUri", SDK_VARIANT, this.getClass().getPackage().getName(),
SDK_VERSION);
return getEndpointProvider().getEndSessionEndpoint().thenApply((endpoint) -> {
String idToken;
try {
idToken = getIdToken().get();
} catch (InterruptedException | ExecutionException e) {
AnalyticsHttpClient.sendExceptionEvent("createLogoutUri", SDK_VARIANT,
this.getClass().getPackage().getName(), SDK_VERSION, e.getMessage(), clientId);
throw new SDKClientException(e, e.getCause(), e.getMessage());
}
String url = endpoint + "?id_token_hint=" + idToken + "&post_logout_redirect_uri=" + logoutRedirectUri;
if (state != null)
url += "&state=" + state;
return URI.create(url);
});
}
/**
* Retrieves the endpoint provider
*
* @return The endpoint provider
*/
public IEndpointProvider getEndpointProvider() {
return endpointProvider;
}
/**
* Retrieves the http token client
*
* @return The http token client
*/
public IHttpTokenClient getHttpTokenClient() {
return httpTokenClient;
}
/**
* Retrieves the refreshable token provider
*
* @return The refreshable token provider
*/
public RefreshableTokenProvider getRefreshableTokenProvider() {
return refreshableTokenProvider;
}
public void setRefreshableTokenProvider(RefreshableTokenProvider refreshableTokenProvider) {
this.refreshableTokenProvider = refreshableTokenProvider;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy