io.camunda.identity.sdk.impl.auth0.authentication.Auth0Authentication Maven / Gradle / Ivy
/*
* Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH under
* one or more contributor license agreements. Licensed under a proprietary license. See the
* License.txt file for more information. You may not use this file except in compliance with the
* proprietary license.
*/
package io.camunda.identity.sdk.impl.auth0.authentication;
import com.auth0.client.auth.AuthAPI;
import com.auth0.exception.Auth0Exception;
import com.auth0.json.auth.TokenHolder;
import com.auth0.jwk.JwkProvider;
import com.auth0.jwk.JwkProviderBuilder;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.net.AuthRequest;
import com.auth0.net.Request;
import com.auth0.net.TokenRequest;
import io.camunda.identity.sdk.IdentityConfiguration;
import io.camunda.identity.sdk.authentication.AbstractAuthentication;
import io.camunda.identity.sdk.authentication.AuthorizeUriBuilder;
import io.camunda.identity.sdk.authentication.Tokens;
import io.camunda.identity.sdk.authentication.dto.AuthCodeDto;
import io.camunda.identity.sdk.authentication.dto.OrganizationDto;
import io.camunda.identity.sdk.authentication.exception.CodeExchangeException;
import io.camunda.identity.sdk.exception.IdentityException;
import io.camunda.identity.sdk.impl.dto.WellKnownConfiguration;
import io.camunda.identity.sdk.impl.rest.RestClient;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import org.apache.commons.lang3.NotImplementedException;
import org.apache.commons.lang3.Validate;
public class Auth0Authentication extends AbstractAuthentication {
private AuthAPI authApi;
private JwkProvider jwkProvider;
public Auth0Authentication(final IdentityConfiguration configuration,
final RestClient restClient) {
super(configuration, restClient);
}
private AuthAPI authApi() {
if (authApi == null) {
authApi = new AuthAPI(configuration.getIssuer(), configuration.getClientId(),
configuration.getClientSecret());
}
return authApi;
}
@Override
public AuthorizeUriBuilder authorizeUriBuilder(final String redirectUri) {
return new Auth0AuthorizeUriBuilder(configuration, authApi(), redirectUri);
}
@Override
public Tokens exchangeAuthCode(
final AuthCodeDto authCodeDto,
final String redirectUri
) throws CodeExchangeException {
Validate.notNull(authCodeDto, "authCodeDto must not be null");
Validate.notNull(redirectUri, "redirectUri must not be null");
if (authCodeDto.getError() != null && !authCodeDto.getError().isBlank()) {
throw new CodeExchangeException(authCodeDto.getError());
}
final AuthRequest request = authApi().exchangeCode(authCodeDto.getCode(), redirectUri)
.setAudience(configuration.getAudience());
try {
final TokenHolder tokenHolder = request.execute();
return fromTokenHolder(tokenHolder);
} catch (final Auth0Exception exception) {
throw new CodeExchangeException("Auth0 Code exchange failed", exception);
}
}
@Override
protected Tokens requestFreshToken(final String audience) {
final TokenRequest request = authApi().requestToken(audience);
try {
final TokenHolder tokenHolder = request.execute();
return fromTokenHolder(tokenHolder);
} catch (final Auth0Exception exception) {
throw new IdentityException("Auth0 token request failed", exception);
}
}
@Override
public Tokens renewToken(final String refreshToken) {
Validate.notEmpty(refreshToken, "refreshToken can not be empty");
final TokenRequest request = authApi().renewAuth(refreshToken);
try {
final TokenHolder tokenHolder = request.execute();
return fromTokenHolder(tokenHolder);
} catch (final Auth0Exception exception) {
throw new IdentityException("Auth0 refresh failed", exception);
}
}
@Override
public void revokeToken(final String refreshToken) {
final Request request = authApi().revokeToken(refreshToken);
try {
request.execute();
} catch (final Auth0Exception exception) {
throw new IdentityException("Auth0 token revocation failed", exception);
}
}
@Override
public boolean isM2MToken(final String token) {
final DecodedJWT decodedJwt = decodeJWT(token);
final Claim subClaim = decodedJwt.getClaim("sub");
return subClaim.asString().contains("@clients");
}
@Override
public String getClientId(final String token) {
final DecodedJWT decodedJwt = decodeJWT(token);
return decodedJwt.getClaim("azp").asString();
}
@Override
protected List getPermissions(final DecodedJWT token, final String audience) {
var permissionsClaim = token.getClaim("permissions");
if (permissionsClaim.isMissing()) {
return Collections.emptyList();
}
return permissionsClaim.asList(String.class);
}
@Override
public Map> getAssignedOrganizations(final DecodedJWT token) {
var orgClaim = token.getClaim("https://camunda.com/orgs");
if (orgClaim.isMissing()) {
return Collections.emptyMap();
}
return orgClaim.asList(OrganizationDto.class).stream()
.collect(Collectors.toMap(OrganizationDto::getId, OrganizationDto::getRoles));
}
@Override
protected JwkProvider jwkProvider() {
if (jwkProvider == null) {
jwkProvider = new JwkProviderBuilder(configuration.getIssuer())
.cached(JWKS_CACHE_SIZE, JWKS_CACHE_LIFETIME_DAYS, TimeUnit.DAYS)
.build();
}
return jwkProvider;
}
@Override
protected WellKnownConfiguration wellKnownConfiguration() {
throw new NotImplementedException();
}
private Tokens fromTokenHolder(final TokenHolder tokenHolder) {
return new Tokens(tokenHolder.getAccessToken(),
tokenHolder.getRefreshToken(),
tokenHolder.getExpiresIn(),
tokenHolder.getScope(),
tokenHolder.getTokenType());
}
@Override
protected boolean isRevokeAvailable() {
return true;
}
@Override
protected boolean isSingleSignOutAvailable() {
return false;
}
}