mirror of
https://github.com/keycloak/keycloak.git
synced 2026-01-09 23:12:06 -03:30
Extract related methods from IdentityProvider to UserIdentityProvider (#43535)
Closes #43534 Signed-off-by: stianst <stianst@gmail.com>
This commit is contained in:
parent
6080f21c64
commit
84a161d4dd
@ -21,6 +21,16 @@ This prevents problems with client IDs or passwords that contain, for example, a
|
||||
|
||||
To revert to the old behavior, change the client authentication to the deprecated option *Client secret sent as HTTP Basic authentication without URL encoding* (`client_secret_basic_unencoded`).
|
||||
|
||||
=== Identity Provider refactoring
|
||||
|
||||
The private SPI for identity providers have been refactored. This is to allow identity providers to support more use
|
||||
-cases than federated user authentication.
|
||||
|
||||
For anyone implementing a custom federated user authentication identity provider and are not extending one provided
|
||||
by Keycloak or `AbstractIdentityProvider` you need to update your implementation to implement
|
||||
the new `UserAuthenticationIdentityProvider` interface (all methods remain the same, they have just been moved).
|
||||
|
||||
|
||||
// ------------------------ Notable changes ------------------------ //
|
||||
== Notable changes
|
||||
|
||||
|
||||
@ -43,7 +43,7 @@ import java.util.UUID;
|
||||
/**
|
||||
* @author Pedro Igor
|
||||
*/
|
||||
public abstract class AbstractIdentityProvider<C extends IdentityProviderModel> implements IdentityProvider<C> {
|
||||
public abstract class AbstractIdentityProvider<C extends IdentityProviderModel> implements UserAuthenticationIdentityProvider<C> {
|
||||
|
||||
protected static final Logger logger = Logger.getLogger(AbstractIdentityProvider.class);
|
||||
|
||||
@ -67,11 +67,6 @@ public abstract class AbstractIdentityProvider<C extends IdentityProviderModel>
|
||||
return this.config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response export(UriInfo uriInfo, RealmModel realm, String format) {
|
||||
return Response.noContent().build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
// no-op
|
||||
|
||||
@ -48,7 +48,7 @@ public class BrokeredIdentityContext {
|
||||
private String brokerUserId;
|
||||
private String token;
|
||||
private IdentityProviderModel idpConfig;
|
||||
private IdentityProvider idp;
|
||||
private UserAuthenticationIdentityProvider<?> idp;
|
||||
private Map<String, Object> contextData = new HashMap<>();
|
||||
private AuthenticationSessionModel authenticationSession;
|
||||
|
||||
@ -152,11 +152,11 @@ public class BrokeredIdentityContext {
|
||||
return idpConfig;
|
||||
}
|
||||
|
||||
public IdentityProvider getIdp() {
|
||||
public UserAuthenticationIdentityProvider<?> getIdp() {
|
||||
return idp;
|
||||
}
|
||||
|
||||
public void setIdp(IdentityProvider idp) {
|
||||
public void setIdp(UserAuthenticationIdentityProvider<?> idp) {
|
||||
this.idp = idp;
|
||||
}
|
||||
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
package org.keycloak.broker.provider;
|
||||
|
||||
import org.keycloak.authentication.ClientAuthenticationFlowContext;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
|
||||
public interface ClientAssertionIdentityProvider {
|
||||
public interface ClientAssertionIdentityProvider<C extends IdentityProviderModel> extends IdentityProvider<C> {
|
||||
|
||||
boolean verifyClientAssertion(ClientAuthenticationFlowContext context) throws Exception;
|
||||
|
||||
|
||||
@ -16,15 +16,9 @@
|
||||
*/
|
||||
package org.keycloak.broker.provider;
|
||||
|
||||
import org.keycloak.events.EventBuilder;
|
||||
import org.keycloak.models.FederatedIdentityModel;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.provider.Provider;
|
||||
import org.keycloak.sessions.AuthenticationSessionModel;
|
||||
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import jakarta.ws.rs.core.UriInfo;
|
||||
@ -37,122 +31,24 @@ import java.util.List;
|
||||
*/
|
||||
public interface IdentityProvider<C extends IdentityProviderModel> extends Provider {
|
||||
|
||||
String EXTERNAL_IDENTITY_PROVIDER = "EXTERNAL_IDENTITY_PROVIDER";
|
||||
String FEDERATED_ACCESS_TOKEN = "FEDERATED_ACCESS_TOKEN";
|
||||
|
||||
interface AuthenticationCallback {
|
||||
|
||||
/**
|
||||
* Common method to return current authenticationSession and verify if it is not expired
|
||||
*
|
||||
* @param encodedCode
|
||||
* @return see description
|
||||
*/
|
||||
AuthenticationSessionModel getAndVerifyAuthenticationSession(String encodedCode);
|
||||
|
||||
/**
|
||||
* This method should be called by provider after the JAXRS callback endpoint has finished authentication
|
||||
* with the remote IDP. There is an assumption that authenticationSession is set in the context when this method is called
|
||||
*
|
||||
* @param context
|
||||
* @return see description
|
||||
*/
|
||||
Response authenticated(BrokeredIdentityContext context);
|
||||
|
||||
/**
|
||||
* Called when user cancelled authentication on the IDP side - for example user didn't approve consent page on the IDP side.
|
||||
* Assumption is that authenticationSession is set in the {@link org.keycloak.models.KeycloakContext} when this method is called
|
||||
*
|
||||
* @param idpConfig identity provider config
|
||||
* @return see description
|
||||
*/
|
||||
Response cancelled(IdentityProviderModel idpConfig);
|
||||
|
||||
/**
|
||||
* Indicates that login with the particular IDP should be retried
|
||||
*
|
||||
* @param identityProvider provider to retry login
|
||||
* @param authSession authentication session
|
||||
* @return see description
|
||||
*/
|
||||
Response retryLogin(IdentityProvider<?> identityProvider, AuthenticationSessionModel authSession);
|
||||
|
||||
/**
|
||||
* Called when error happened on the IDP side.
|
||||
* Assumption is that authenticationSession is set in the {@link org.keycloak.models.KeycloakContext} when this method is called
|
||||
*
|
||||
* @return see description
|
||||
*/
|
||||
Response error(IdentityProviderModel idpConfig, String message);
|
||||
}
|
||||
|
||||
C getConfig();
|
||||
|
||||
|
||||
void preprocessFederatedIdentity(KeycloakSession session, RealmModel realm, BrokeredIdentityContext context);
|
||||
void authenticationFinished(AuthenticationSessionModel authSession, BrokeredIdentityContext context);
|
||||
void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, BrokeredIdentityContext context);
|
||||
void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, BrokeredIdentityContext context);
|
||||
|
||||
/**
|
||||
* JAXRS callback endpoint for when the remote IDP wants to callback to keycloak.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Object callback(RealmModel realm, AuthenticationCallback callback, EventBuilder event);
|
||||
|
||||
/**
|
||||
* <p>Initiates the authentication process by sending an authentication request to an identity provider. This method is called
|
||||
* only once during the authentication.</p>
|
||||
*
|
||||
* @param request The initial authentication request. Contains all the contextual information in order to build an authentication request to the
|
||||
* identity provider.
|
||||
* @return
|
||||
*/
|
||||
Response performLogin(AuthenticationRequest request);
|
||||
|
||||
/**
|
||||
* <p>Returns a {@link jakarta.ws.rs.core.Response} containing the token previously stored during the authentication process for a
|
||||
* specific user.</p>
|
||||
*
|
||||
* @param identity
|
||||
* @return
|
||||
*/
|
||||
Response retrieveToken(KeycloakSession session, FederatedIdentityModel identity);
|
||||
|
||||
void backchannelLogout(KeycloakSession session, UserSessionModel userSession, UriInfo uriInfo, RealmModel realm);
|
||||
|
||||
/**
|
||||
* Called when a Keycloak application initiates a logout through the browser. This is expected to do a logout
|
||||
* with the IDP
|
||||
*
|
||||
* @param userSession
|
||||
* @param uriInfo
|
||||
* @param realm
|
||||
* @return null if this is not supported by this provider
|
||||
*/
|
||||
Response keycloakInitiatedBrowserLogout(KeycloakSession session, UserSessionModel userSession, UriInfo uriInfo, RealmModel realm);
|
||||
|
||||
/**
|
||||
* Export a representation of the IdentityProvider in a specific format. For example, a SAML EntityDescriptor
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Response export(UriInfo uriInfo, RealmModel realm, String format);
|
||||
|
||||
/**
|
||||
* Implementation of marshaller to serialize/deserialize attached data to Strings, which can be saved in clientSession
|
||||
* @return
|
||||
*/
|
||||
IdentityProviderDataMarshaller getMarshaller();
|
||||
default Response export(UriInfo uriInfo, RealmModel realm, String format) {
|
||||
return Response.noContent().build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a mapper is supported for this Identity Provider.
|
||||
*/
|
||||
default boolean isMapperSupported(IdentityProviderMapper mapper) {
|
||||
List<String> compatibleIdps = Arrays.asList(mapper.getCompatibleProviders());
|
||||
List<String> compatibleIdps = Arrays.asList(mapper.getCompatibleProviders());
|
||||
return compatibleIdps.contains(IdentityProviderMapper.ANY_PROVIDER)
|
||||
|| compatibleIdps.contains(getConfig().getProviderId());
|
||||
|| compatibleIdps.contains(getConfig().getProviderId());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -163,11 +59,4 @@ public interface IdentityProvider<C extends IdentityProviderModel> extends Provi
|
||||
default boolean reloadKeys() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if identity provider supports long value of "state" parameter (or "RelayState" parameter), which can hold relatively big amount of context data
|
||||
*/
|
||||
default boolean supportsLongStateParameter() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,122 @@
|
||||
package org.keycloak.broker.provider;
|
||||
|
||||
import org.keycloak.events.EventBuilder;
|
||||
import org.keycloak.models.FederatedIdentityModel;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.sessions.AuthenticationSessionModel;
|
||||
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import jakarta.ws.rs.core.UriInfo;
|
||||
|
||||
public interface UserAuthenticationIdentityProvider<C extends IdentityProviderModel> extends IdentityProvider<C> {
|
||||
|
||||
String EXTERNAL_IDENTITY_PROVIDER = "EXTERNAL_IDENTITY_PROVIDER";
|
||||
String FEDERATED_ACCESS_TOKEN = "FEDERATED_ACCESS_TOKEN";
|
||||
|
||||
interface AuthenticationCallback {
|
||||
|
||||
/**
|
||||
* Common method to return current authenticationSession and verify if it is not expired
|
||||
*
|
||||
* @param encodedCode
|
||||
* @return see description
|
||||
*/
|
||||
AuthenticationSessionModel getAndVerifyAuthenticationSession(String encodedCode);
|
||||
|
||||
/**
|
||||
* This method should be called by provider after the JAXRS callback endpoint has finished authentication
|
||||
* with the remote IDP. There is an assumption that authenticationSession is set in the context when this method is called
|
||||
*
|
||||
* @param context
|
||||
* @return see description
|
||||
*/
|
||||
Response authenticated(BrokeredIdentityContext context);
|
||||
|
||||
/**
|
||||
* Called when user cancelled authentication on the IDP side - for example user didn't approve consent page on the IDP side.
|
||||
* Assumption is that authenticationSession is set in the {@link org.keycloak.models.KeycloakContext} when this method is called
|
||||
*
|
||||
* @param idpConfig identity provider config
|
||||
* @return see description
|
||||
*/
|
||||
Response cancelled(IdentityProviderModel idpConfig);
|
||||
|
||||
/**
|
||||
* Indicates that login with the particular IDP should be retried
|
||||
*
|
||||
* @param identityProvider provider to retry login
|
||||
* @param authSession authentication session
|
||||
* @return see description
|
||||
*/
|
||||
Response retryLogin(UserAuthenticationIdentityProvider<?> identityProvider, AuthenticationSessionModel authSession);
|
||||
|
||||
/**
|
||||
* Called when error happened on the IDP side.
|
||||
* Assumption is that authenticationSession is set in the {@link org.keycloak.models.KeycloakContext} when this method is called
|
||||
*
|
||||
* @return see description
|
||||
*/
|
||||
Response error(IdentityProviderModel idpConfig, String message);
|
||||
}
|
||||
|
||||
void preprocessFederatedIdentity(KeycloakSession session, RealmModel realm, BrokeredIdentityContext context);
|
||||
void authenticationFinished(AuthenticationSessionModel authSession, BrokeredIdentityContext context);
|
||||
void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, BrokeredIdentityContext context);
|
||||
void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, BrokeredIdentityContext context);
|
||||
|
||||
/**
|
||||
* JAXRS callback endpoint for when the remote IDP wants to callback to keycloak.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Object callback(RealmModel realm, AuthenticationCallback callback, EventBuilder event);
|
||||
|
||||
/**
|
||||
* <p>Initiates the authentication process by sending an authentication request to an identity provider. This method is called
|
||||
* only once during the authentication.</p>
|
||||
*
|
||||
* @param request The initial authentication request. Contains all the contextual information in order to build an authentication request to the
|
||||
* identity provider.
|
||||
* @return
|
||||
*/
|
||||
Response performLogin(AuthenticationRequest request);
|
||||
|
||||
/**
|
||||
* <p>Returns a {@link jakarta.ws.rs.core.Response} containing the token previously stored during the authentication process for a
|
||||
* specific user.</p>
|
||||
*
|
||||
* @param identity
|
||||
* @return
|
||||
*/
|
||||
Response retrieveToken(KeycloakSession session, FederatedIdentityModel identity);
|
||||
|
||||
void backchannelLogout(KeycloakSession session, UserSessionModel userSession, UriInfo uriInfo, RealmModel realm);
|
||||
|
||||
/**
|
||||
* Called when a Keycloak application initiates a logout through the browser. This is expected to do a logout
|
||||
* with the IDP
|
||||
*
|
||||
* @param userSession
|
||||
* @param uriInfo
|
||||
* @param realm
|
||||
* @return null if this is not supported by this provider
|
||||
*/
|
||||
Response keycloakInitiatedBrowserLogout(KeycloakSession session, UserSessionModel userSession, UriInfo uriInfo, RealmModel realm);
|
||||
|
||||
/**
|
||||
* Implementation of marshaller to serialize/deserialize attached data to Strings, which can be saved in clientSession
|
||||
* @return
|
||||
*/
|
||||
IdentityProviderDataMarshaller getMarshaller();
|
||||
|
||||
/**
|
||||
* @return true if identity provider supports long value of "state" parameter (or "RelayState" parameter), which can hold relatively big amount of context data
|
||||
*/
|
||||
default boolean supportsLongStateParameter() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -16,11 +16,11 @@
|
||||
*/
|
||||
package org.keycloak.broker.social;
|
||||
|
||||
import org.keycloak.broker.provider.IdentityProvider;
|
||||
import org.keycloak.broker.provider.UserAuthenticationIdentityProvider;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
|
||||
/**
|
||||
* @author Pedro Igor
|
||||
*/
|
||||
public interface SocialIdentityProvider<C extends IdentityProviderModel> extends IdentityProvider<C> {
|
||||
public interface SocialIdentityProvider<C extends IdentityProviderModel> extends UserAuthenticationIdentityProvider<C> {
|
||||
}
|
||||
|
||||
@ -20,8 +20,8 @@ package org.keycloak.authentication.authenticators.broker.util;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import org.keycloak.authentication.requiredactions.util.UpdateProfileContext;
|
||||
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
||||
import org.keycloak.broker.provider.IdentityProvider;
|
||||
import org.keycloak.broker.provider.IdentityProviderDataMarshaller;
|
||||
import org.keycloak.broker.provider.UserAuthenticationIdentityProvider;
|
||||
import org.keycloak.common.util.Base64Url;
|
||||
import org.keycloak.common.util.reflections.Reflections;
|
||||
import org.keycloak.models.Constants;
|
||||
@ -282,7 +282,7 @@ public class SerializedBrokeredIdentityContext implements UpdateProfileContext {
|
||||
ctx.setBrokerUserId(getBrokerUserId());
|
||||
ctx.setToken(getToken());
|
||||
|
||||
IdentityProvider idp = IdentityBrokerService.getIdentityProvider(session, idpConfig.getAlias());
|
||||
UserAuthenticationIdentityProvider<?> idp = IdentityBrokerService.getIdentityProvider(session, idpConfig.getAlias());
|
||||
ctx.setIdp(idp);
|
||||
|
||||
IdentityProviderDataMarshaller serializer = idp.getMarshaller();
|
||||
|
||||
@ -7,7 +7,6 @@ import org.keycloak.authentication.AuthenticationFlowError;
|
||||
import org.keycloak.authentication.ClientAuthenticationFlowContext;
|
||||
import org.keycloak.authentication.ConfigurableAuthenticatorFactory;
|
||||
import org.keycloak.broker.provider.ClientAssertionIdentityProvider;
|
||||
import org.keycloak.broker.provider.IdentityProvider;
|
||||
import org.keycloak.broker.spiffe.SpiffeConstants;
|
||||
import org.keycloak.cache.AlternativeLookupProvider;
|
||||
import org.keycloak.common.Profile;
|
||||
@ -92,12 +91,7 @@ public class FederatedJWTClientAuthenticator extends AbstractClientAuthenticator
|
||||
if (identityProviderModel == null) {
|
||||
return null;
|
||||
}
|
||||
IdentityProvider<?> identityProvider = IdentityBrokerService.getIdentityProvider(session, identityProviderModel);
|
||||
if (identityProvider instanceof ClientAssertionIdentityProvider clientAssertionProvider) {
|
||||
return clientAssertionProvider;
|
||||
} else {
|
||||
throw new RuntimeException("Provider does not support client assertions");
|
||||
}
|
||||
return IdentityBrokerService.getIdentityProvider(session, identityProviderModel, ClientAssertionIdentityProvider.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -39,7 +39,7 @@ import org.keycloak.broker.provider.BrokeredIdentityContext;
|
||||
import org.keycloak.broker.provider.ExchangeExternalToken;
|
||||
import org.keycloak.broker.provider.ExchangeTokenToIdentityProviderToken;
|
||||
import org.keycloak.broker.provider.IdentityBrokerException;
|
||||
import org.keycloak.broker.provider.IdentityProvider;
|
||||
import org.keycloak.broker.provider.UserAuthenticationIdentityProvider;
|
||||
import org.keycloak.broker.provider.util.IdentityBrokerState;
|
||||
import org.keycloak.common.ClientConnection;
|
||||
import org.keycloak.common.util.SecretGenerator;
|
||||
@ -360,7 +360,7 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
|
||||
if (!getConfig().isStoreToken()) {
|
||||
// if token isn't stored, we need to see if this session has been linked
|
||||
String brokerId = tokenUserSession.getNote(Details.IDENTITY_PROVIDER);
|
||||
brokerId = brokerId == null ? tokenUserSession.getNote(IdentityProvider.EXTERNAL_IDENTITY_PROVIDER) : brokerId;
|
||||
brokerId = brokerId == null ? tokenUserSession.getNote(UserAuthenticationIdentityProvider.EXTERNAL_IDENTITY_PROVIDER) : brokerId;
|
||||
if (brokerId == null || !brokerId.equals(getConfig().getAlias())) {
|
||||
event.detail(Details.REASON, "requested_issuer has not linked");
|
||||
event.error(Errors.INVALID_REQUEST);
|
||||
|
||||
@ -94,7 +94,7 @@ import java.util.Optional;
|
||||
/**
|
||||
* @author Pedro Igor
|
||||
*/
|
||||
public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIdentityProviderConfig> implements ExchangeExternalToken, ClientAssertionIdentityProvider {
|
||||
public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIdentityProviderConfig> implements ExchangeExternalToken, ClientAssertionIdentityProvider<OIDCIdentityProviderConfig> {
|
||||
protected static final Logger logger = Logger.getLogger(OIDCIdentityProvider.class);
|
||||
|
||||
public static final String SCOPE_OPENID = "openid";
|
||||
|
||||
@ -22,7 +22,7 @@ import org.jboss.resteasy.reactive.NoCache;
|
||||
|
||||
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
||||
import org.keycloak.broker.provider.IdentityBrokerException;
|
||||
import org.keycloak.broker.provider.IdentityProvider;
|
||||
import org.keycloak.broker.provider.UserAuthenticationIdentityProvider;
|
||||
import org.keycloak.common.ClientConnection;
|
||||
import org.keycloak.common.VerificationException;
|
||||
import org.keycloak.dom.saml.v2.assertion.AssertionType;
|
||||
@ -148,7 +148,7 @@ public class SAMLEndpoint {
|
||||
protected final RealmModel realm;
|
||||
protected EventBuilder event;
|
||||
protected final SAMLIdentityProviderConfig config;
|
||||
protected final IdentityProvider.AuthenticationCallback callback;
|
||||
protected final UserAuthenticationIdentityProvider.AuthenticationCallback callback;
|
||||
protected final SAMLIdentityProvider provider;
|
||||
private final DestinationValidator destinationValidator;
|
||||
|
||||
@ -159,7 +159,7 @@ public class SAMLEndpoint {
|
||||
private final HttpHeaders headers;
|
||||
|
||||
|
||||
public SAMLEndpoint(KeycloakSession session, SAMLIdentityProvider provider, SAMLIdentityProviderConfig config, IdentityProvider.AuthenticationCallback callback, DestinationValidator destinationValidator) {
|
||||
public SAMLEndpoint(KeycloakSession session, SAMLIdentityProvider provider, SAMLIdentityProviderConfig config, UserAuthenticationIdentityProvider.AuthenticationCallback callback, DestinationValidator destinationValidator) {
|
||||
this.realm = session.getContext().getRealm();
|
||||
this.config = config;
|
||||
this.callback = callback;
|
||||
|
||||
@ -1,30 +1,18 @@
|
||||
package org.keycloak.broker.spiffe;
|
||||
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import jakarta.ws.rs.core.UriInfo;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.authentication.ClientAuthenticationFlowContext;
|
||||
import org.keycloak.authentication.authenticators.client.AbstractJWTClientValidator;
|
||||
import org.keycloak.authentication.authenticators.client.FederatedJWTClientValidator;
|
||||
import org.keycloak.broker.provider.AuthenticationRequest;
|
||||
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
||||
import org.keycloak.broker.provider.ClientAssertionIdentityProvider;
|
||||
import org.keycloak.broker.provider.IdentityProvider;
|
||||
import org.keycloak.broker.provider.IdentityProviderDataMarshaller;
|
||||
import org.keycloak.crypto.KeyWrapper;
|
||||
import org.keycloak.crypto.SignatureProvider;
|
||||
import org.keycloak.events.EventBuilder;
|
||||
import org.keycloak.jose.jws.JWSHeader;
|
||||
import org.keycloak.jose.jws.JWSInput;
|
||||
import org.keycloak.keys.PublicKeyStorageProvider;
|
||||
import org.keycloak.keys.PublicKeyStorageUtils;
|
||||
import org.keycloak.models.FederatedIdentityModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.representations.JsonWebToken;
|
||||
import org.keycloak.sessions.AuthenticationSessionModel;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
@ -40,7 +28,7 @@ import java.nio.charset.StandardCharsets;
|
||||
* <li>Keys are fetched from a SPIFFE bundle endpoint, where the JWKS has additional SPIFFE specific fields (<code>spiffe_sequence</code> and <code>spiffe_refresh_hint</code>, the JWK does not set the <code>alg></code></li>
|
||||
* </ul>
|
||||
*/
|
||||
public class SpiffeIdentityProvider implements IdentityProvider<SpiffeIdentityProviderConfig>, ClientAssertionIdentityProvider {
|
||||
public class SpiffeIdentityProvider implements ClientAssertionIdentityProvider<SpiffeIdentityProviderConfig> {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(SpiffeIdentityProvider.class);
|
||||
|
||||
@ -103,58 +91,4 @@ public class SpiffeIdentityProvider implements IdentityProvider<SpiffeIdentityPr
|
||||
public void close() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preprocessFederatedIdentity(KeycloakSession session, RealmModel realm, BrokeredIdentityContext context) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void authenticationFinished(AuthenticationSessionModel authSession, BrokeredIdentityContext context) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, BrokeredIdentityContext context) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, BrokeredIdentityContext context) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object callback(RealmModel realm, AuthenticationCallback callback, EventBuilder event) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response performLogin(AuthenticationRequest request) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response retrieveToken(KeycloakSession session, FederatedIdentityModel identity) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void backchannelLogout(KeycloakSession session, UserSessionModel userSession, UriInfo uriInfo, RealmModel realm) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response keycloakInitiatedBrowserLogout(KeycloakSession session, UserSessionModel userSession, UriInfo uriInfo, RealmModel realm) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response export(UriInfo uriInfo, RealmModel realm, String format) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IdentityProviderDataMarshaller getMarshaller() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,6 +28,7 @@ import org.keycloak.broker.provider.IdentityBrokerException;
|
||||
import org.keycloak.broker.provider.IdentityProvider;
|
||||
import org.keycloak.broker.provider.IdentityProviderMapper;
|
||||
import org.keycloak.broker.provider.IdentityProviderMapperSyncModeDelegate;
|
||||
import org.keycloak.broker.provider.UserAuthenticationIdentityProvider;
|
||||
import org.keycloak.common.ClientConnection;
|
||||
import org.keycloak.events.Details;
|
||||
import org.keycloak.events.Errors;
|
||||
@ -300,8 +301,8 @@ public abstract class AbstractTokenExchangeProvider implements TokenExchangeProv
|
||||
externalExchangeContext.provider().exchangeExternalComplete(userSession, context, formParams);
|
||||
|
||||
// this must exist so that we can obtain access token from user session if idp's store tokens is off
|
||||
userSession.setNote(IdentityProvider.EXTERNAL_IDENTITY_PROVIDER, externalExchangeContext.idpModel().getAlias());
|
||||
userSession.setNote(IdentityProvider.FEDERATED_ACCESS_TOKEN, subjectToken);
|
||||
userSession.setNote(UserAuthenticationIdentityProvider.EXTERNAL_IDENTITY_PROVIDER, externalExchangeContext.idpModel().getAlias());
|
||||
userSession.setNote(UserAuthenticationIdentityProvider.FEDERATED_ACCESS_TOKEN, subjectToken);
|
||||
|
||||
context.addSessionNotesToUserSession(userSession);
|
||||
|
||||
|
||||
@ -23,7 +23,7 @@ import jakarta.ws.rs.core.Response;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.OAuthErrorException;
|
||||
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
||||
import org.keycloak.broker.provider.IdentityProvider;
|
||||
import org.keycloak.broker.provider.UserAuthenticationIdentityProvider;
|
||||
import org.keycloak.events.Details;
|
||||
import org.keycloak.events.Errors;
|
||||
import org.keycloak.models.UserModel;
|
||||
@ -101,8 +101,8 @@ public class ExternalToInternalTokenExchangeProvider extends StandardTokenExchan
|
||||
externalExchangeContext.provider().exchangeExternalComplete(userSession, context, formParams);
|
||||
|
||||
// this must exist so that we can obtain access token from user session if idp's store tokens is off
|
||||
userSession.setNote(IdentityProvider.EXTERNAL_IDENTITY_PROVIDER, externalExchangeContext.idpModel().getAlias());
|
||||
userSession.setNote(IdentityProvider.FEDERATED_ACCESS_TOKEN, subjectToken);
|
||||
userSession.setNote(UserAuthenticationIdentityProvider.EXTERNAL_IDENTITY_PROVIDER, externalExchangeContext.idpModel().getAlias());
|
||||
userSession.setNote(UserAuthenticationIdentityProvider.FEDERATED_ACCESS_TOKEN, subjectToken);
|
||||
|
||||
context.addSessionNotesToUserSession(userSession);
|
||||
|
||||
|
||||
@ -35,7 +35,7 @@ import org.keycloak.authentication.RequiredActionContextResult;
|
||||
import org.keycloak.authentication.RequiredActionFactory;
|
||||
import org.keycloak.authentication.RequiredActionProvider;
|
||||
import org.keycloak.authentication.authenticators.browser.AbstractUsernameFormAuthenticator;
|
||||
import org.keycloak.broker.provider.IdentityProvider;
|
||||
import org.keycloak.broker.provider.UserAuthenticationIdentityProvider;
|
||||
import org.keycloak.common.ClientConnection;
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.common.VerificationException;
|
||||
@ -457,7 +457,7 @@ public class AuthenticationManager {
|
||||
if (logoutBroker) {
|
||||
String brokerId = userSession.getNote(Details.IDENTITY_PROVIDER);
|
||||
if (brokerId != null) {
|
||||
IdentityProvider<?> identityProvider = null;
|
||||
UserAuthenticationIdentityProvider<?> identityProvider = null;
|
||||
try {
|
||||
identityProvider = IdentityBrokerService.getIdentityProvider(session, brokerId);
|
||||
} catch (IdentityBrokerException e) {
|
||||
@ -700,7 +700,7 @@ public class AuthenticationManager {
|
||||
String brokerId = userSession.getNote(Details.IDENTITY_PROVIDER);
|
||||
String initiatingIdp = logoutAuthSession.getAuthNote(AuthenticationManager.LOGOUT_INITIATING_IDP);
|
||||
if (brokerId != null && !brokerId.equals(initiatingIdp)) {
|
||||
IdentityProvider<?> identityProvider = IdentityBrokerService.getIdentityProvider(session, brokerId);
|
||||
UserAuthenticationIdentityProvider<?> identityProvider = IdentityBrokerService.getIdentityProvider(session, brokerId);
|
||||
Response response = identityProvider.keycloakInitiatedBrowserLogout(session, userSession, uriInfo, realm);
|
||||
if (response != null) {
|
||||
return response;
|
||||
|
||||
@ -22,6 +22,7 @@ import org.keycloak.authentication.RequiredActionContext;
|
||||
import org.keycloak.authentication.authenticators.broker.IdpConfirmOverrideLinkAuthenticator;
|
||||
import org.keycloak.broker.provider.ExchangeTokenToIdentityProviderToken;
|
||||
import org.keycloak.broker.provider.IdpLinkAction;
|
||||
import org.keycloak.broker.provider.UserAuthenticationIdentityProvider;
|
||||
import org.keycloak.http.HttpRequest;
|
||||
import org.keycloak.OAuthErrorException;
|
||||
import org.keycloak.authentication.AuthenticationProcessor;
|
||||
@ -131,7 +132,7 @@ import static org.keycloak.broker.provider.AbstractIdentityProvider.BROKER_REGIS
|
||||
*
|
||||
* @author Pedro Igor
|
||||
*/
|
||||
public class IdentityBrokerService implements IdentityProvider.AuthenticationCallback {
|
||||
public class IdentityBrokerService implements UserAuthenticationIdentityProvider.AuthenticationCallback {
|
||||
|
||||
// Authentication session note, which references identity provider that is currently linked
|
||||
public static final String LINKING_IDENTITY_PROVIDER = "LINKING_IDENTITY_PROVIDER";
|
||||
@ -340,7 +341,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
||||
|
||||
public Response performClientInitiatedAccountLogin(String providerAlias, ClientSessionCode<AuthenticationSessionModel> clientSessionCode) {
|
||||
try {
|
||||
IdentityProvider<?> identityProvider = getIdentityProvider(session, providerAlias);
|
||||
UserAuthenticationIdentityProvider<?> identityProvider = getIdentityProvider(session, providerAlias);
|
||||
Response response = identityProvider.performLogin(createAuthenticationRequest(identityProvider, providerAlias, clientSessionCode));
|
||||
|
||||
if (response != null) {
|
||||
@ -402,7 +403,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
||||
clientSessionCode.getClientSession().setClientNote(OIDCLoginProtocol.LOGIN_HINT_PARAM, loginHint);
|
||||
}
|
||||
|
||||
IdentityProvider<?> identityProvider = getIdentityProvider(session, identityProviderModel.getAlias());
|
||||
UserAuthenticationIdentityProvider<?> identityProvider = getIdentityProvider(session, identityProviderModel.getAlias());
|
||||
Response response = identityProvider.performLogin(createAuthenticationRequest(identityProvider, providerAlias, clientSessionCode));
|
||||
|
||||
if (response != null) {
|
||||
@ -423,7 +424,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response retryLogin(IdentityProvider<?> identityProvider, AuthenticationSessionModel authSession) {
|
||||
public Response retryLogin(UserAuthenticationIdentityProvider<?> identityProvider, AuthenticationSessionModel authSession) {
|
||||
ClientSessionCode<AuthenticationSessionModel> clientSessionCode = new ClientSessionCode<>(session, realmModel, authSession);
|
||||
clientSessionCode.setAction(AuthenticationSessionModel.Action.AUTHENTICATE.name());
|
||||
Response response = identityProvider.performLogin(createAuthenticationRequest(identityProvider, identityProvider.getConfig().getAlias(), clientSessionCode));
|
||||
@ -443,7 +444,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
||||
|
||||
@Path("{provider_alias}/endpoint")
|
||||
public Object getEndpoint(@PathParam("provider_alias") String providerAlias) {
|
||||
IdentityProvider identityProvider;
|
||||
UserAuthenticationIdentityProvider<?> identityProvider;
|
||||
|
||||
try {
|
||||
identityProvider = getIdentityProvider(session, providerAlias);
|
||||
@ -501,7 +502,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
||||
|
||||
}
|
||||
|
||||
IdentityProvider identityProvider = getIdentityProvider(session, providerAlias);
|
||||
UserAuthenticationIdentityProvider<?> identityProvider = getIdentityProvider(session, providerAlias);
|
||||
IdentityProviderModel identityProviderConfig = getIdentityProviderConfig(providerAlias);
|
||||
|
||||
if (identityProviderConfig.isStoreToken()) {
|
||||
@ -755,7 +756,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
||||
|
||||
logger.debugf("Registered new user '%s' after first login with identity provider '%s'. Identity provider username is '%s' . ", federatedUser.getUsername(), providerAlias, context.getUsername());
|
||||
|
||||
IdentityProvider idp = context.getIdp();
|
||||
UserAuthenticationIdentityProvider<?> idp = context.getIdp();
|
||||
idp.importNewUser(session, realmModel, federatedUser, context);
|
||||
KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
|
||||
session.identityProviders().getMappersByAliasStream(providerAlias).forEach(mapper -> {
|
||||
@ -1255,7 +1256,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
||||
return null;
|
||||
}
|
||||
|
||||
private AuthenticationRequest createAuthenticationRequest(IdentityProvider<?> identityProvider, String providerAlias, ClientSessionCode<AuthenticationSessionModel> clientSessionCode) {
|
||||
private AuthenticationRequest createAuthenticationRequest(UserAuthenticationIdentityProvider<?> identityProvider, String providerAlias, ClientSessionCode<AuthenticationSessionModel> clientSessionCode) {
|
||||
AuthenticationSessionModel authSession = null;
|
||||
IdentityBrokerState encodedState = null;
|
||||
|
||||
@ -1342,19 +1343,20 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
||||
throw ErrorResponse.error(message, Response.Status.NOT_FOUND);
|
||||
}
|
||||
|
||||
public static IdentityProvider<?> getIdentityProvider(KeycloakSession session, String alias) {
|
||||
public static UserAuthenticationIdentityProvider<?> getIdentityProvider(KeycloakSession session, String alias) {
|
||||
IdentityProviderModel identityProviderModel = session.identityProviders().getByAlias(alias);
|
||||
IdentityProvider<?> identityProvider = getIdentityProvider(session, identityProviderModel);
|
||||
UserAuthenticationIdentityProvider<?> identityProvider = getIdentityProvider(session, identityProviderModel, UserAuthenticationIdentityProvider.class);
|
||||
if (identityProvider == null) {
|
||||
throw new IdentityBrokerException("Identity Provider [" + alias + "] not found.");
|
||||
}
|
||||
return identityProvider;
|
||||
}
|
||||
|
||||
public static IdentityProvider<?> getIdentityProvider(KeycloakSession session, IdentityProviderModel identityProviderModel) {
|
||||
public static <T extends IdentityProvider<?>> T getIdentityProvider(KeycloakSession session, IdentityProviderModel identityProviderModel, Class<T> type) {
|
||||
if (identityProviderModel != null) {
|
||||
IdentityProviderFactory<?> providerFactory = getIdentityProviderFactory(session, identityProviderModel);
|
||||
return providerFactory != null ? providerFactory.create(session, identityProviderModel) : null;
|
||||
IdentityProvider<?> idp = providerFactory.create(session, identityProviderModel);
|
||||
return type.isInstance(idp) ? type.cast(idp) : null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -24,7 +24,7 @@ import org.keycloak.broker.provider.AuthenticationRequest;
|
||||
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
||||
import org.keycloak.broker.provider.ExchangeTokenToIdentityProviderToken;
|
||||
import org.keycloak.broker.provider.IdentityBrokerException;
|
||||
import org.keycloak.broker.provider.IdentityProvider;
|
||||
import org.keycloak.broker.provider.UserAuthenticationIdentityProvider;
|
||||
import org.keycloak.broker.provider.util.IdentityBrokerState;
|
||||
import org.keycloak.broker.social.SocialIdentityProvider;
|
||||
import org.keycloak.common.ClientConnection;
|
||||
@ -147,7 +147,7 @@ public class TwitterIdentityProvider extends AbstractIdentityProvider<OAuth2Iden
|
||||
}
|
||||
|
||||
protected Response exchangeSessionToken(UriInfo uriInfo, ClientModel authorizedClient, UserSessionModel tokenUserSession, UserModel tokenSubject) {
|
||||
String accessToken = tokenUserSession.getNote(IdentityProvider.FEDERATED_ACCESS_TOKEN);
|
||||
String accessToken = tokenUserSession.getNote(UserAuthenticationIdentityProvider.FEDERATED_ACCESS_TOKEN);
|
||||
if (accessToken == null) {
|
||||
return exchangeTokenExpired(uriInfo, authorizedClient, tokenUserSession, tokenSubject);
|
||||
}
|
||||
@ -242,7 +242,7 @@ public class TwitterIdentityProvider extends AbstractIdentityProvider<OAuth2Iden
|
||||
if (providerConfig.isStoreToken()) {
|
||||
identity.setToken(token);
|
||||
}
|
||||
identity.getContextData().put(IdentityProvider.FEDERATED_ACCESS_TOKEN, token);
|
||||
identity.getContextData().put(UserAuthenticationIdentityProvider.FEDERATED_ACCESS_TOKEN, token);
|
||||
|
||||
identity.setAuthenticationSession(authSession);
|
||||
|
||||
@ -271,7 +271,7 @@ public class TwitterIdentityProvider extends AbstractIdentityProvider<OAuth2Iden
|
||||
|
||||
@Override
|
||||
public void authenticationFinished(AuthenticationSessionModel authSession, BrokeredIdentityContext context) {
|
||||
authSession.setUserSessionNote(IdentityProvider.FEDERATED_ACCESS_TOKEN, (String)context.getContextData().get(IdentityProvider.FEDERATED_ACCESS_TOKEN));
|
||||
authSession.setUserSessionNote(UserAuthenticationIdentityProvider.FEDERATED_ACCESS_TOKEN, (String) context.getContextData().get(UserAuthenticationIdentityProvider.FEDERATED_ACCESS_TOKEN));
|
||||
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user