pl.edu.icm.unity.oauth.client.OAuth2Verificator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of unity-server-oauth Show documentation
Show all versions of unity-server-oauth Show documentation
Client and server OAuth support
The newest version!
/*
* Copyright (c) 2014 ICM Uniwersytet Warszawski All rights reserved.
* See LICENCE.txt file for licensing information.
*/
package pl.edu.icm.unity.oauth.client;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import org.apache.hc.core5.net.URIBuilder;
import org.apache.logging.log4j.Logger;
import org.eclipse.jetty.ee10.servlet.ServletHolder;
import org.eclipse.jetty.util.MultiMap;
import org.eclipse.jetty.util.UrlEncoded;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.google.common.base.Strings;
import com.nimbusds.jwt.JWT;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.oauth2.sdk.AccessTokenResponse;
import com.nimbusds.oauth2.sdk.AuthorizationCode;
import com.nimbusds.oauth2.sdk.AuthorizationCodeGrant;
import com.nimbusds.oauth2.sdk.AuthorizationRequest;
import com.nimbusds.oauth2.sdk.ParseException;
import com.nimbusds.oauth2.sdk.ResponseType;
import com.nimbusds.oauth2.sdk.Scope;
import com.nimbusds.oauth2.sdk.TokenRequest;
import com.nimbusds.oauth2.sdk.auth.ClientAuthentication;
import com.nimbusds.oauth2.sdk.auth.ClientAuthenticationMethod;
import com.nimbusds.oauth2.sdk.auth.ClientSecretBasic;
import com.nimbusds.oauth2.sdk.auth.ClientSecretPost;
import com.nimbusds.oauth2.sdk.auth.Secret;
import com.nimbusds.oauth2.sdk.http.HTTPRequest;
import com.nimbusds.oauth2.sdk.http.HTTPResponse;
import com.nimbusds.oauth2.sdk.id.ClientID;
import com.nimbusds.oauth2.sdk.id.State;
import com.nimbusds.oauth2.sdk.token.AccessToken;
import com.nimbusds.oauth2.sdk.token.AccessTokenType;
import com.nimbusds.oauth2.sdk.token.BearerAccessToken;
import com.nimbusds.openid.connect.sdk.AuthenticationRequest;
import com.nimbusds.openid.connect.sdk.AuthenticationRequest.Builder;
import com.nimbusds.openid.connect.sdk.OIDCTokenResponse;
import com.nimbusds.openid.connect.sdk.claims.UserInfo;
import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
import eu.unicore.util.configuration.ConfigurationException;
import io.imunity.vaadin.auth.CommonWebAuthnProperties;
import jakarta.ws.rs.core.MediaType;
import net.minidev.json.JSONObject;
import pl.edu.icm.unity.base.authn.ExpectedIdentity;
import pl.edu.icm.unity.base.authn.ExpectedIdentity.IdentityExpectation;
import pl.edu.icm.unity.base.exceptions.EngineException;
import pl.edu.icm.unity.base.exceptions.InternalException;
import pl.edu.icm.unity.base.message.MessageSource;
import pl.edu.icm.unity.base.translation.TranslationProfile;
import pl.edu.icm.unity.base.utils.Log;
import pl.edu.icm.unity.engine.api.PKIManagement;
import pl.edu.icm.unity.engine.api.authn.AbstractCredentialVerificatorFactory;
import pl.edu.icm.unity.engine.api.authn.AuthenticationException;
import pl.edu.icm.unity.engine.api.authn.AuthenticationResult;
import pl.edu.icm.unity.engine.api.authn.AuthenticationResult.ResolvableError;
import pl.edu.icm.unity.engine.api.authn.AuthenticationStepContext;
import pl.edu.icm.unity.engine.api.authn.IdPInfo;
import pl.edu.icm.unity.engine.api.authn.RememberMeToken.LoginMachineDetails;
import pl.edu.icm.unity.engine.api.authn.RemoteAuthenticationException;
import pl.edu.icm.unity.engine.api.authn.RemoteAuthenticationResult;
import pl.edu.icm.unity.engine.api.authn.remote.AbstractRemoteVerificator;
import pl.edu.icm.unity.engine.api.authn.remote.AuthenticationTriggeringContext;
import pl.edu.icm.unity.engine.api.authn.remote.RedirectedAuthnState;
import pl.edu.icm.unity.engine.api.authn.remote.RemoteAuthnResultTranslator;
import pl.edu.icm.unity.engine.api.authn.remote.RemotelyAuthenticatedInput;
import pl.edu.icm.unity.engine.api.authn.remote.SharedRemoteAuthenticationContextStore;
import pl.edu.icm.unity.engine.api.endpoint.SharedEndpointManagement;
import pl.edu.icm.unity.engine.api.server.AdvertisedAddressProvider;
import pl.edu.icm.unity.engine.api.utils.PrototypeComponent;
import pl.edu.icm.unity.engine.api.utils.URIBuilderFixer;
import pl.edu.icm.unity.oauth.client.config.CustomProviderProperties;
import pl.edu.icm.unity.oauth.client.config.CustomProviderProperties.AccessTokenFormat;
import pl.edu.icm.unity.oauth.client.config.CustomProviderProperties.ClientAuthnMode;
import pl.edu.icm.unity.oauth.client.config.OAuthClientProperties;
import pl.edu.icm.unity.oauth.client.profile.ProfileFetcherUtils;
import pl.edu.icm.unity.oauth.oidc.metadata.OAuthDiscoveryMetadataCache;
/**
* Binding independent OAuth 2 logic. Creates authZ requests, validates response (OAuth authorization grant)
* performs subsequent call to AS to get resource owner's (authenticated user) information.
*
* @author K. Benedyczak
*/
@PrototypeComponent
public class OAuth2Verificator extends AbstractRemoteVerificator implements OAuthExchange
{
private static final Logger log = Log.getLogger(Log.U_SERVER_OAUTH, OAuth2Verificator.class);
public static final String NAME = "oauth2";
public static final String DESC = "Handles OAuth2 tokens obtained from remote OAuth providers. "
+ "Queries about additional user information.";
public static final String DEFAULT_TOKEN_EXPIRATION = "3600";
private OAuthClientProperties config;
private final String responseConsumerAddress;
private final OAuthContextsManagement contextManagement;
private final PKIManagement pkiManagement;
private final MessageSource msg;
private final OAuthDiscoveryMetadataCache metadataManager;
private final OAuthRemoteAuthenticationInputAssembler remoteAuthenticationInputAssembler;
@Autowired
public OAuth2Verificator(MessageSource msg, AdvertisedAddressProvider advertisedAddrProvider,
SharedEndpointManagement sharedEndpointManagement,
OAuthContextsManagement contextManagement,
PKIManagement pkiManagement,
RemoteAuthnResultTranslator processor,
OAuthDiscoveryMetadataCache metadataManager, OAuthRemoteAuthenticationInputAssembler remoteAuthenticationInputAssembler)
{
super(NAME, DESC, OAuthExchange.ID, processor);
URL baseAddress = advertisedAddrProvider.get();
String baseContext = sharedEndpointManagement.getBaseContextPath();
this.responseConsumerAddress = baseAddress + baseContext + ResponseConsumerServlet.PATH;
this.contextManagement = contextManagement;
this.pkiManagement = pkiManagement;
this.msg = msg;
this.metadataManager = metadataManager;
this.remoteAuthenticationInputAssembler = remoteAuthenticationInputAssembler;
}
@Override
public String getSerializedConfiguration()
{
StringWriter sbw = new StringWriter();
try
{
config.getProperties().store(sbw, "");
} catch (IOException e)
{
throw new InternalException("Can't serialize OAuth2 verificator configuration", e);
}
return sbw.toString();
}
@Override
public void setSerializedConfiguration(String source)
{
try
{
Properties properties = new Properties();
properties.load(new StringReader(source));
config = new OAuthClientProperties(properties, pkiManagement);
} catch(ConfigurationException e)
{
throw new InternalException("Invalid configuration of the OAuth2 verificator", e);
} catch (IOException e)
{
throw new InternalException("Invalid configuration of the OAuth2 verificator(?)", e);
}
}
@Override
public OAuthClientProperties getSettings()
{
return config;
}
@Override
public OAuthContext createRequest(String providerKey, Optional expectedIdentity,
AuthenticationStepContext authnStepContext,
LoginMachineDetails initialLoginMachine,
String ultimateReturnURL,
AuthenticationTriggeringContext authnTriggeringContext)
throws URISyntaxException, ParseException, IOException
{
CustomProviderProperties providerCfg = config.getProvider(providerKey);
String clientId = providerCfg.getValue(CustomProviderProperties.CLIENT_ID);
String authzEndpoint = providerCfg.getValue(CustomProviderProperties.PROVIDER_LOCATION);
String scopes = providerCfg.getValue(CustomProviderProperties.SCOPES);
boolean openidMode = providerCfg.getBooleanValue(CustomProviderProperties.OPENID_CONNECT);
RedirectedAuthnState baseAuthnContext = new RedirectedAuthnState(authnStepContext, this::processResponse,
initialLoginMachine, ultimateReturnURL,
authnTriggeringContext);
OAuthContext context = new OAuthContext(baseAuthnContext);
AuthorizationRequest req;
if (openidMode)
{
if (Strings.isNullOrEmpty(authzEndpoint))
{
OIDCProviderMetadata providerMeta = metadataManager.getMetadata(providerCfg.generateMetadataRequest());
if (providerMeta.getAuthorizationEndpointURI() == null)
throw new ConfigurationException("The authorization endpoint address is not set and"
+ " it is not available in the discovered OpenID Provider metadata.");
authzEndpoint = providerMeta.getAuthorizationEndpointURI().toString();
}
Builder builder = new AuthenticationRequest.Builder(new ResponseType(ResponseType.Value.CODE),
Scope.parse(scopes), new ClientID(clientId),
new URI(responseConsumerAddress));
builder.state(new State(context.getRelayState()))
.endpointURI(new URI(authzEndpoint));
if (expectedIdentity.isPresent())
{
builder.loginHint(expectedIdentity.get().getIdentity());
context.setExpectedIdentity(expectedIdentity.get());
}
req = builder.build();
} else
{
Scope scope = scopes == null ? null : Scope.parse(scopes);
req = new AuthorizationRequest(
new URI(authzEndpoint),
new ResponseType(ResponseType.Value.CODE),
null,
new ClientID(clientId),
new URI(responseConsumerAddress),
scope,
new State(context.getRelayState()));
}
URIBuilder uriBuilder = URIBuilderFixer.newInstance(req.toURI());
uriBuilder.addParameters(providerCfg.getAdditionalAuthzParams());
context.setRequest(req, uriBuilder.build(), providerKey);
contextManagement.addAuthnContext(context);
return context;
}
private AuthenticationResult processResponse(RedirectedAuthnState remoteAuthnState)
{
try
{
return verifyOAuthAuthzResponse((OAuthContext) remoteAuthnState);
} catch (Exception e)
{
log.error("Runtime error during OAuth2 response processing or principal mapping", e);
return RemoteAuthenticationResult.failed(null, e, new ResolvableError("OAuth2Retrieval.authnFailedError"));
}
}
/**
* The real OAuth workhorse. The authz code response verification needs not to be done: the state is
* correct as otherwise there would be no match with the {@link OAuthContext}. However we need to
* use the authz code to retrieve access token. The access code may include everything we need. But it
* may also happen that we need to perform one more query to obtain additional profile information.
*/
private AuthenticationResult verifyOAuthAuthzResponse(OAuthContext context)
{
try
{
RemotelyAuthenticatedInput input = getRemotelyAuthenticatedInput(context);
verifyExpectedIdentity(input, context.getExpectedIdentity());
CustomProviderProperties providerProps = config.getProvider(context.getProviderConfigKey());
TranslationProfile profile = getTranslationProfile(
providerProps,
CommonWebAuthnProperties.TRANSLATION_PROFILE,
CommonWebAuthnProperties.EMBEDDED_TRANSLATION_PROFILE);
String regFormForUnknown = providerProps.getValue(CommonWebAuthnProperties.REGISTRATION_FORM);
boolean enableAssociation = providerProps.isSet(CommonWebAuthnProperties.ENABLE_ASSOCIATION) ?
providerProps.getBooleanValue(CommonWebAuthnProperties.ENABLE_ASSOCIATION) :
config.getBooleanValue(CommonWebAuthnProperties.DEF_ENABLE_ASSOCIATION);
return getResult(input,
profile,
context.getAuthenticationTriggeringContext().isSandboxTriggered(),
regFormForUnknown,
enableAssociation);
} catch (UnexpectedIdentityException uie)
{
return RemoteAuthenticationResult.failed(null, uie,
new ResolvableError("OAuth2Retrieval.unexpectedUser", uie.expectedIdentity));
} catch (RemoteAuthenticationException e)
{
log.info("OAuth2 authorization code verification or processing failed", e);
return RemoteAuthenticationResult.failed(e.getResult().getRemotelyAuthenticatedPrincipal(), e,
new ResolvableError("OAuth2Retrieval.authnFailedError"));
}
}
private void verifyExpectedIdentity(RemotelyAuthenticatedInput input, ExpectedIdentity expectedIdentity)
{
if (expectedIdentity == null)
return;
if (expectedIdentity.getExpectation() == IdentityExpectation.HINT)
return;
String identity = expectedIdentity.getIdentity();
if (input.getIdentities().values().stream()
.filter(ri -> ri.getName().equals(identity))
.findAny().isPresent())
return;
if (input.getAttributes().values().stream()
.filter(ra -> ra.getName().equals(UserInfo.EMAIL_CLAIM_NAME))
.filter(ra -> ra.getValues().contains(identity))
.findAny().isPresent())
return;
log.warn("Failing OAuth authentication as expected&mandatory identity {} was not found "
+ "in received user data: {}", identity, input.getTextDump());
throw new UnexpectedIdentityException(identity);
}
private RemotelyAuthenticatedInput getRemotelyAuthenticatedInput(OAuthContext context)
throws RemoteAuthenticationException
{
String error = context.getErrorCode();
if (error != null)
{
throw new RemoteAuthenticationException("OAuth provider returned an error: " +
error + (context.getErrorDescription() != null ?
" " + context.getErrorDescription() : ""));
}
boolean openIdConnectMode = config.getProvider(context.getProviderConfigKey()).getBooleanValue(
CustomProviderProperties.OPENID_CONNECT);
AttributeFetchResult attributes;
try
{
attributes = openIdConnectMode ? getAccessTokenAndProfileOpenIdConnect(context) :
getAccessTokenAndProfilePlain(context);
} catch (Exception e)
{
throw new RemoteAuthenticationException("Problem during user information retrieval", e);
}
return remoteAuthenticationInputAssembler.convertInput(config.getProvider(context.getProviderConfigKey()), context, attributes, openIdConnectMode);
}
private AccessTokenFormat getAccessTokenFormat(OAuthContext context)
{
CustomProviderProperties providerCfg = config
.getProvider(context.getProviderConfigKey());
return providerCfg.getEnumValue(CustomProviderProperties.ACCESS_TOKEN_FORMAT,
AccessTokenFormat.class);
}
private HTTPResponse retrieveAccessTokenGeneric(OAuthContext context, String tokenEndpoint,
ClientAuthnMode mode)
throws IOException, URISyntaxException
{
String clientId = config.getProvider(context.getProviderConfigKey()).getValue(
CustomProviderProperties.CLIENT_ID);
String clientSecret = config.getProvider(context.getProviderConfigKey()).getValue(
CustomProviderProperties.CLIENT_SECRET);
ClientAuthentication clientAuthn = getClientAuthentication(clientId, clientSecret, mode);
AuthorizationCodeGrant authzCodeGrant = new AuthorizationCodeGrant(
new AuthorizationCode(context.getAuthzCode()),
new URI(responseConsumerAddress));
TokenRequest request = new TokenRequest(
new URI(tokenEndpoint),
clientAuthn,
authzCodeGrant);
CustomProviderProperties providerCfg = config.getProvider(context.getProviderConfigKey());
HTTPRequest httpRequest = new HttpRequestConfigurer()
.secureRequest(request.toHTTPRequest(), providerCfg.getValidator(), providerCfg.getHostNameCheckingMode());
if (getAccessTokenFormat(context) == AccessTokenFormat.standard)
httpRequest.setAccept(MediaType.APPLICATION_JSON);
if (log.isTraceEnabled())
{
String notSecretQuery = httpRequest.getQuery().replaceFirst(
"client_secret=[^&]*", "client_secret=xxxxxx");
log.trace("Exchanging authorization code for access token with request to: " +
httpRequest.getURL() + "?" + notSecretQuery);
} else if (log.isDebugEnabled())
{
log.debug("Exchanging authorization code for access token with request to: " +
httpRequest.getURL());
}
HTTPResponse response = httpRequest.send();
log.debug("Received answer: {}", response.getStatusCode());
if (response.getStatusCode() != 200)
log.warn("Error received. Contents: {}", response.getContent());
else
log.trace("Received token: {}", response.getContent().trim());
return response;
}
private ClientAuthentication getClientAuthentication(String clientId, String clientSecret,
ClientAuthnMode mode)
{
switch (mode)
{
case secretPost:
return new ClientSecretPost(new ClientID(clientId), new Secret(clientSecret));
case secretBasic:
default:
return new ClientSecretBasic(new ClientID(clientId), new Secret(clientSecret));
}
}
private AttributeFetchResult getAccessTokenAndProfileOpenIdConnect(OAuthContext context) throws Exception
{
CustomProviderProperties providerCfg = config.getProvider(context.getProviderConfigKey());
OIDCProviderMetadata providerMeta = metadataManager.getMetadata(providerCfg.generateMetadataRequest());
String tokenEndpoint = providerCfg.getValue(CustomProviderProperties.ACCESS_TOKEN_ENDPOINT);
if (tokenEndpoint == null)
{
if (providerMeta.getTokenEndpointURI() != null)
tokenEndpoint = providerMeta.getTokenEndpointURI().toString();
else
throw new AuthenticationException("The access token endpoint is not set "
+ "in provider's metadata and it is not configured manually");
}
ClientAuthnMode selectedMethod = establishOpenIDAuthnMode(providerMeta, providerCfg);
HTTPResponse response = retrieveAccessTokenGeneric(context, tokenEndpoint, selectedMethod);
OIDCTokenResponse acResponse = OIDCTokenResponse.parse(response);
BearerAccessToken accessToken = extractAccessToken(acResponse);
JWT idToken = acResponse.getOIDCTokens().getIDToken();
if (idToken == null)
throw new AuthenticationException("Id token was not returned by the authorization server");
JWTClaimsSet accessTokenClaimsSet = idToken.getJWTClaimsSet();
Map> accessTokenAttributes = ProfileFetcherUtils.convertToAttributes(
new JSONObject(accessTokenClaimsSet.getClaims()));
List userInfoEndpoints = providerCfg.getUserInfoEndpoints();
if (userInfoEndpoints.isEmpty() && providerMeta.getUserInfoEndpointURI() != null)
userInfoEndpoints.add(providerMeta.getUserInfoEndpointURI().toString());
return fetchUserAttributes(providerCfg, accessToken, accessTokenAttributes, userInfoEndpoints);
}
private ClientAuthnMode establishOpenIDAuthnMode(OIDCProviderMetadata providerMeta,
CustomProviderProperties providerCfg) throws AuthenticationException
{
ClientAuthnMode selectedMethod = ClientAuthnMode.secretBasic;
if (providerCfg.isSet(CustomProviderProperties.CLIENT_AUTHN_MODE))
{
selectedMethod = providerCfg.getEnumValue(CustomProviderProperties.CLIENT_AUTHN_MODE,
ClientAuthnMode.class);
} else
{
List supportedMethods = providerMeta.getTokenEndpointAuthMethods();
if (supportedMethods != null)
{
selectedMethod = null;
for (ClientAuthenticationMethod sm: supportedMethods)
{
if (ClientAuthenticationMethod.CLIENT_SECRET_POST.equals(sm))
{
selectedMethod = ClientAuthnMode.secretPost;
break;
} else if (ClientAuthenticationMethod.CLIENT_SECRET_BASIC.equals(sm))
{
selectedMethod = ClientAuthnMode.secretBasic;
break;
}
}
if (selectedMethod == null)
throw new AuthenticationException("Client authentication metods supported by"
+ " the provider (" + supportedMethods + ") do not include "
+ "any of methods supported by Unity.");
}
}
return selectedMethod;
}
private AttributeFetchResult getAccessTokenAndProfilePlain(OAuthContext context) throws Exception
{
CustomProviderProperties providerCfg = config.getProvider(context.getProviderConfigKey());
String tokenEndpoint = providerCfg.getValue(CustomProviderProperties.ACCESS_TOKEN_ENDPOINT);
ClientAuthnMode selectedMethod = providerCfg.getEnumValue(CustomProviderProperties.CLIENT_AUTHN_MODE,
ClientAuthnMode.class);
HTTPResponse response = retrieveAccessTokenGeneric(context, tokenEndpoint, selectedMethod);
Map> accessTokenAttributes = new HashMap<>();
BearerAccessToken accessToken;
if (getAccessTokenFormat(context) == AccessTokenFormat.standard)
{
JSONObject jsonResp = response.getContentAsJSONObject();
if (!jsonResp.containsKey("token_type"))
jsonResp.put("token_type", AccessTokenType.BEARER.getValue());
AccessTokenResponse atResponse = AccessTokenResponse.parse(jsonResp);
accessToken = extractAccessToken(atResponse);
extractUserInfoFromStandardAccessToken(atResponse, accessTokenAttributes);
} else
{
if (response.getStatusCode() != 200)
throw new AuthenticationException("Exchange of authorization code for access "
+ "token failed: " + response.getContent());
MultiMap map = new MultiMap<>();
UrlEncoded.decodeTo(response.getContent().trim(), map, StandardCharsets.UTF_8);
String accessTokenVal = map.getString("access_token");
if (accessTokenVal == null)
throw new AuthenticationException("Access token answer received doesn't contain "
+ "'access_token' parameter.");
String lifetimeStr = map.getString("expires");
if (lifetimeStr == null)
lifetimeStr = map.getString("expires_in");
if (lifetimeStr == null)
{
log.debug("AS didn't provide expiration time, assuming default value " +
DEFAULT_TOKEN_EXPIRATION);
lifetimeStr = DEFAULT_TOKEN_EXPIRATION;
}
accessToken = new BearerAccessToken(accessTokenVal, Long.parseLong(lifetimeStr), null);
extractUserInfoFromHttpParamsAccessToken(map, accessTokenAttributes);
}
List userInfoEndpoints = providerCfg.getUserInfoEndpoints();
return fetchUserAttributes(providerCfg, accessToken, accessTokenAttributes, userInfoEndpoints);
}
private AttributeFetchResult fetchUserAttributes(CustomProviderProperties providerCfg,
BearerAccessToken accessToken, Map> baseAttributes,
List userInfoEndpoints) throws Exception
{
UserProfileFetcher userAttributesFetcher = providerCfg.getUserAttributesResolver();
AttributeFetchResult fetchRet = new AttributeFetchResult();
if (userAttributesFetcher != null)
{
for (String userInfoEndpoint: userInfoEndpoints)
{
AttributeFetchResult fetchSingle = userAttributesFetcher.fetchProfile(accessToken,
userInfoEndpoint, providerCfg, baseAttributes);
fetchRet = fetchRet.mergeWith(fetchSingle);
}
}
fetchRet.getAttributes().putAll(baseAttributes); //minor bug - those won't be visible as rawAttribtues.
log.debug("Received the following attributes from the OAuth provider: {}", fetchRet.getAttributes());
return fetchRet;
}
private void extractUserInfoFromStandardAccessToken(AccessTokenResponse atResponse, Map> ret)
{
Map customParameters = atResponse.getCustomParameters();
for (Map.Entry e: customParameters.entrySet())
{
if (attributeIgnored(e.getKey()))
continue;
ret.put(e.getKey(), Arrays.asList(e.getValue().toString()));
}
}
private void extractUserInfoFromHttpParamsAccessToken(MultiMap params, Map> ret)
{
for (Map.Entry> param: params.entrySet())
{
String key = param.getKey();
List values = param.getValue();
if (attributeIgnored(key) || values.isEmpty())
continue;
ret.put(key, values);
}
}
private boolean attributeIgnored(String key)
{
return key.equals("access_token") || key.equals("expires") || key.equals("expires_in") ||
key.equals("id_token");
}
private BearerAccessToken extractAccessToken(AccessTokenResponse atResponse) throws AuthenticationException
{
AccessToken accessTokenGeneric = atResponse.getTokens().getAccessToken();
if (!(accessTokenGeneric instanceof BearerAccessToken))
{
throw new AuthenticationException("OAuth provider returned an access token which is not "
+ "the bearer token, it is unsupported and most probably a problem on "
+ "the provider side. The received token type is: " +
accessTokenGeneric.getType().toString());
}
return (BearerAccessToken) accessTokenGeneric;
}
@Override
public VerificatorType getType()
{
return VerificatorType.Remote;
}
@Override
public List getIdPs()
{
List providers = new ArrayList<>();
Set keys = config.getStructuredListKeys(OAuthClientProperties.PROVIDERS);
for (String key : keys)
{
CustomProviderProperties providerProps = config.getProvider(key);
String idpKey = key.substring(OAuthClientProperties.PROVIDERS.length(), key.length() - 1);
if (config.getProvider(key).getBooleanValue(CustomProviderProperties.OPENID_CONNECT))
{
extractIdPInfoFromOIDCProvider(key, idpKey, providerProps).ifPresent(i -> providers.add(i));
} else
{
IdPInfo providerInfo = IdPInfo.builder()
.withId(config.getProvider(key).getValue(CustomProviderProperties.ACCESS_TOKEN_ENDPOINT))
.withConfigId(idpKey)
.withDisplayedName(config.getProvider(key).getLocalizedStringWithoutFallbackToDefault(msg,
CustomProviderProperties.PROVIDER_NAME))
.build();
providers.add(providerInfo);
}
}
return providers;
}
private Optional extractIdPInfoFromOIDCProvider(String key, String idpKey,
CustomProviderProperties providerProps)
{
OIDCProviderMetadata metadata;
try
{
metadata = metadataManager.getMetadata(providerProps.generateMetadataRequest());
} catch (Exception e)
{
log.warn("Can't obtain OIDC metadata", e);
return Optional.empty();
}
return Optional.of(IdPInfo.builder().withId(metadata.getTokenEndpointURI().toString()).withConfigId(idpKey)
.withDisplayedName(config.getProvider(key).getLocalizedStringWithoutFallbackToDefault(msg,
CustomProviderProperties.PROVIDER_NAME))
.build());
}
@Component
public static class Factory extends AbstractCredentialVerificatorFactory
{
@Autowired
public Factory(ObjectFactory factory,
SharedEndpointManagement sharedEndpointManagement,
OAuthContextsManagement contextManagement,
SharedRemoteAuthenticationContextStore remoteAuthnContextStore) throws EngineException
{
super(NAME, DESC, factory);
ServletHolder servlet = new ServletHolder(new ResponseConsumerServlet(contextManagement,
remoteAuthnContextStore));
sharedEndpointManagement.deployInternalEndpointServlet(
ResponseConsumerServlet.PATH, servlet, false);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy