Search in sources :

Example 1 with KeycloakKeySamlExtensionGenerator

use of org.keycloak.saml.processing.core.util.KeycloakKeySamlExtensionGenerator in project keycloak by keycloak.

the class SamlProtocol method authenticated.

@Override
public Response authenticated(AuthenticationSessionModel authSession, UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
    AuthenticatedClientSessionModel clientSession = clientSessionCtx.getClientSession();
    ClientModel client = clientSession.getClient();
    SamlClient samlClient = new SamlClient(client);
    String requestID = authSession.getClientNote(SAML_REQUEST_ID);
    String relayState = authSession.getClientNote(GeneralConstants.RELAY_STATE);
    String redirectUri = authSession.getRedirectUri();
    String responseIssuer = getResponseIssuer(realm);
    String nameIdFormat = getNameIdFormat(samlClient, authSession);
    int assertionLifespan = samlClient.getAssertionLifespan();
    SAML2LoginResponseBuilder builder = new SAML2LoginResponseBuilder();
    builder.requestID(requestID).destination(redirectUri).issuer(responseIssuer).assertionExpiration(assertionLifespan <= 0 ? realm.getAccessCodeLifespan() : assertionLifespan).subjectExpiration(assertionLifespan <= 0 ? realm.getAccessTokenLifespan() : assertionLifespan).sessionExpiration(realm.getSsoSessionMaxLifespan()).requestIssuer(clientSession.getClient().getClientId()).authMethod(JBossSAMLURIConstants.AC_UNSPECIFIED.get());
    String sessionIndex = SamlSessionUtils.getSessionIndex(clientSession);
    builder.sessionIndex(sessionIndex);
    if (!samlClient.includeAuthnStatement()) {
        builder.disableAuthnStatement(true);
    }
    builder.includeOneTimeUseCondition(samlClient.includeOneTimeUseCondition());
    List<ProtocolMapperProcessor<SAMLAttributeStatementMapper>> attributeStatementMappers = new LinkedList<>();
    List<ProtocolMapperProcessor<SAMLLoginResponseMapper>> loginResponseMappers = new LinkedList<>();
    AtomicReference<ProtocolMapperProcessor<SAMLRoleListMapper>> roleListMapper = new AtomicReference<>(null);
    List<ProtocolMapperProcessor<SAMLNameIdMapper>> samlNameIdMappers = new LinkedList<>();
    ProtocolMapperUtils.getSortedProtocolMappers(session, clientSessionCtx).forEach(entry -> {
        ProtocolMapperModel mapping = entry.getKey();
        ProtocolMapper mapper = entry.getValue();
        if (mapper instanceof SAMLAttributeStatementMapper) {
            attributeStatementMappers.add(new ProtocolMapperProcessor<>((SAMLAttributeStatementMapper) mapper, mapping));
        }
        if (mapper instanceof SAMLLoginResponseMapper) {
            loginResponseMappers.add(new ProtocolMapperProcessor<>((SAMLLoginResponseMapper) mapper, mapping));
        }
        if (mapper instanceof SAMLRoleListMapper) {
            roleListMapper.set(new ProtocolMapperProcessor<>((SAMLRoleListMapper) mapper, mapping));
        }
        if (mapper instanceof SAMLNameIdMapper) {
            samlNameIdMappers.add(new ProtocolMapperProcessor<>((SAMLNameIdMapper) mapper, mapping));
        }
    });
    Document samlDocument = null;
    ResponseType samlModel = null;
    KeyManager keyManager = session.keys();
    KeyManager.ActiveRsaKey keys = keyManager.getActiveRsaKey(realm);
    boolean postBinding = isPostBinding(authSession);
    String keyName = samlClient.getXmlSigKeyInfoKeyNameTransformer().getKeyName(keys.getKid(), keys.getCertificate());
    String nameId = getSAMLNameId(samlNameIdMappers, nameIdFormat, session, userSession, clientSession);
    if (nameId == null) {
        return samlErrorMessage(null, samlClient, isPostBinding(authSession), redirectUri, JBossSAMLURIConstants.STATUS_INVALID_NAMEIDPOLICY, relayState);
    }
    builder.nameIdentifier(nameIdFormat, nameId);
    // save NAME_ID and format in clientSession as they may be persistent or
    // transient or email and not username
    // we'll need to send this back on a logout
    clientSession.setNote(SAML_NAME_ID, nameId);
    clientSession.setNote(SAML_NAME_ID_FORMAT, nameIdFormat);
    try {
        if ((!postBinding) && samlClient.requiresRealmSignature() && samlClient.addExtensionsElementWithKeyInfo()) {
            builder.addExtension(new KeycloakKeySamlExtensionGenerator(keyName));
        }
        samlModel = builder.buildModel();
        final AttributeStatementType attributeStatement = populateAttributeStatements(attributeStatementMappers, session, userSession, clientSession);
        populateRoles(roleListMapper.get(), session, userSession, clientSessionCtx, attributeStatement);
        // SAML Spec 2.7.3 AttributeStatement must contain one or more Attribute or EncryptedAttribute
        if (attributeStatement.getAttributes().size() > 0) {
            AssertionType assertion = samlModel.getAssertions().get(0).getAssertion();
            assertion.addStatement(attributeStatement);
        }
        samlModel = transformLoginResponse(loginResponseMappers, samlModel, session, userSession, clientSessionCtx);
    } catch (Exception e) {
        logger.error("failed", e);
        return ErrorPage.error(session, null, Response.Status.BAD_REQUEST, Messages.FAILED_TO_PROCESS_RESPONSE);
    }
    JaxrsSAML2BindingBuilder bindingBuilder = new JaxrsSAML2BindingBuilder(session);
    bindingBuilder.relayState(relayState);
    if ("true".equals(clientSession.getNote(JBossSAMLURIConstants.SAML_HTTP_ARTIFACT_BINDING.get()))) {
        try {
            return buildArtifactAuthenticatedResponse(clientSession, redirectUri, samlModel, bindingBuilder);
        } catch (Exception e) {
            logger.error("failed", e);
            return ErrorPage.error(session, null, Response.Status.BAD_REQUEST, Messages.FAILED_TO_PROCESS_RESPONSE);
        }
    }
    if (samlClient.requiresRealmSignature() || samlClient.requiresAssertionSignature()) {
        String canonicalization = samlClient.getCanonicalizationMethod();
        if (canonicalization != null) {
            bindingBuilder.canonicalizationMethod(canonicalization);
        }
        bindingBuilder.signatureAlgorithm(samlClient.getSignatureAlgorithm()).signWith(keyName, keys.getPrivateKey(), keys.getPublicKey(), keys.getCertificate());
        if (samlClient.requiresRealmSignature())
            bindingBuilder.signDocument();
        if (samlClient.requiresAssertionSignature())
            bindingBuilder.signAssertions();
    }
    if (samlClient.requiresEncryption()) {
        PublicKey publicKey = null;
        try {
            publicKey = SamlProtocolUtils.getEncryptionKey(client);
        } catch (Exception e) {
            logger.error("failed", e);
            return ErrorPage.error(session, null, Response.Status.BAD_REQUEST, Messages.FAILED_TO_PROCESS_RESPONSE);
        }
        bindingBuilder.encrypt(publicKey);
    }
    try {
        samlDocument = builder.buildDocument(samlModel);
        return buildAuthenticatedResponse(clientSession, redirectUri, samlDocument, bindingBuilder);
    } catch (Exception e) {
        logger.error("failed", e);
        return ErrorPage.error(session, null, Response.Status.BAD_REQUEST, Messages.FAILED_TO_PROCESS_RESPONSE);
    }
}
Also used : ProtocolMapper(org.keycloak.protocol.ProtocolMapper) SAMLNameIdMapper(org.keycloak.protocol.saml.mappers.SAMLNameIdMapper) Document(org.w3c.dom.Document) KeycloakKeySamlExtensionGenerator(org.keycloak.saml.processing.core.util.KeycloakKeySamlExtensionGenerator) SAMLAttributeStatementMapper(org.keycloak.protocol.saml.mappers.SAMLAttributeStatementMapper) SAMLLoginResponseMapper(org.keycloak.protocol.saml.mappers.SAMLLoginResponseMapper) SAML2LoginResponseBuilder(org.keycloak.saml.SAML2LoginResponseBuilder) KeyManager(org.keycloak.models.KeyManager) SAMLRoleListMapper(org.keycloak.protocol.saml.mappers.SAMLRoleListMapper) PublicKey(java.security.PublicKey) AttributeStatementType(org.keycloak.dom.saml.v2.assertion.AttributeStatementType) AuthenticatedClientSessionModel(org.keycloak.models.AuthenticatedClientSessionModel) AtomicReference(java.util.concurrent.atomic.AtomicReference) AssertionType(org.keycloak.dom.saml.v2.assertion.AssertionType) LinkedList(java.util.LinkedList) ParsingException(org.keycloak.saml.common.exceptions.ParsingException) ProcessingException(org.keycloak.saml.common.exceptions.ProcessingException) ConfigurationException(org.keycloak.saml.common.exceptions.ConfigurationException) IOException(java.io.IOException) ProtocolMapperModel(org.keycloak.models.ProtocolMapperModel) ArtifactResponseType(org.keycloak.dom.saml.v2.protocol.ArtifactResponseType) ResponseType(org.keycloak.dom.saml.v2.protocol.ResponseType) StatusResponseType(org.keycloak.dom.saml.v2.protocol.StatusResponseType) ClientModel(org.keycloak.models.ClientModel)

Example 2 with KeycloakKeySamlExtensionGenerator

use of org.keycloak.saml.processing.core.util.KeycloakKeySamlExtensionGenerator in project keycloak by keycloak.

the class SamlProtocol method finishLogout.

@Override
public Response finishLogout(UserSessionModel userSession) {
    logger.debug("finishLogout");
    String logoutBindingUri = userSession.getNote(SAML_LOGOUT_BINDING_URI);
    if (logoutBindingUri == null) {
        logger.error("Can't finish SAML logout as there is no logout binding set.  Please configure the logout service url in the admin console for your client applications.");
        return ErrorPage.error(session, null, Response.Status.BAD_REQUEST, Messages.FAILED_LOGOUT);
    }
    String logoutRelayState = userSession.getNote(SAML_LOGOUT_RELAY_STATE);
    SAML2LogoutResponseBuilder builder = new SAML2LogoutResponseBuilder();
    builder.logoutRequestID(userSession.getNote(SAML_LOGOUT_REQUEST_ID));
    builder.destination(logoutBindingUri);
    builder.issuer(getResponseIssuer(realm));
    JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder(session);
    binding.relayState(logoutRelayState);
    String signingAlgorithm = userSession.getNote(SAML_LOGOUT_SIGNATURE_ALGORITHM);
    boolean postBinding = isLogoutPostBindingForInitiator(userSession);
    if (signingAlgorithm != null) {
        SignatureAlgorithm algorithm = SignatureAlgorithm.valueOf(signingAlgorithm);
        String canonicalization = userSession.getNote(SAML_LOGOUT_CANONICALIZATION);
        if (canonicalization != null) {
            binding.canonicalizationMethod(canonicalization);
        }
        KeyManager.ActiveRsaKey keys = session.keys().getActiveRsaKey(realm);
        XmlKeyInfoKeyNameTransformer transformer = XmlKeyInfoKeyNameTransformer.from(userSession.getNote(SAML_SERVER_SIGNATURE_KEYINFO_KEY_NAME_TRANSFORMER), SamlClient.DEFAULT_XML_KEY_INFO_KEY_NAME_TRANSFORMER);
        String keyName = transformer.getKeyName(keys.getKid(), keys.getCertificate());
        binding.signatureAlgorithm(algorithm).signWith(keyName, keys.getPrivateKey(), keys.getPublicKey(), keys.getCertificate()).signDocument();
        boolean addExtension = (!postBinding) && Objects.equals("true", userSession.getNote(SamlProtocol.SAML_LOGOUT_ADD_EXTENSIONS_ELEMENT_WITH_KEY_INFO));
        if (addExtension) {
            // Only include extension if REDIRECT binding and signing whole SAML protocol message
            builder.addExtension(new KeycloakKeySamlExtensionGenerator(keyName));
        }
    }
    Response response;
    try {
        response = buildLogoutResponse(userSession, logoutBindingUri, builder, binding);
    } catch (ConfigurationException | ProcessingException | IOException e) {
        throw new RuntimeException(e);
    }
    if (logoutBindingUri != null) {
        event.detail(Details.REDIRECT_URI, logoutBindingUri);
    }
    event.event(EventType.LOGOUT).detail(Details.AUTH_METHOD, userSession.getAuthMethod()).client(session.getContext().getClient()).user(userSession.getUser()).session(userSession).detail(Details.USERNAME, userSession.getLoginUsername()).detail(Details.RESPONSE_MODE, postBinding ? SamlProtocol.SAML_POST_BINDING : SamlProtocol.SAML_REDIRECT_BINDING).detail(SamlProtocol.SAML_LOGOUT_REQUEST_ID, userSession.getNote(SAML_LOGOUT_REQUEST_ID)).success();
    return response;
}
Also used : SAML2LogoutResponseBuilder(org.keycloak.saml.SAML2LogoutResponseBuilder) XmlKeyInfoKeyNameTransformer(org.keycloak.saml.common.util.XmlKeyInfoKeyNameTransformer) SignatureAlgorithm(org.keycloak.saml.SignatureAlgorithm) IOException(java.io.IOException) KeycloakKeySamlExtensionGenerator(org.keycloak.saml.processing.core.util.KeycloakKeySamlExtensionGenerator) Response(javax.ws.rs.core.Response) CloseableHttpResponse(org.apache.http.client.methods.CloseableHttpResponse) ConfigurationException(org.keycloak.saml.common.exceptions.ConfigurationException) KeyManager(org.keycloak.models.KeyManager) ProcessingException(org.keycloak.saml.common.exceptions.ProcessingException)

Example 3 with KeycloakKeySamlExtensionGenerator

use of org.keycloak.saml.processing.core.util.KeycloakKeySamlExtensionGenerator in project keycloak by keycloak.

the class SamlProtocol method frontchannelLogout.

@Override
public Response frontchannelLogout(UserSessionModel userSession, AuthenticatedClientSessionModel clientSession) {
    ClientModel client = clientSession.getClient();
    SamlClient samlClient = new SamlClient(client);
    try {
        boolean postBinding = isLogoutPostBindingForClient(clientSession);
        String bindingUri = getLogoutServiceUrl(session, client, postBinding ? SAML_POST_BINDING : SAML_REDIRECT_BINDING, false);
        if (bindingUri == null) {
            logger.warnf("Failed to logout client %s, skipping this client.  Please configure the logout service url in the admin console for your client applications.", client.getClientId());
            return null;
        }
        NodeGenerator[] extensions = new NodeGenerator[] {};
        if (!postBinding) {
            if (samlClient.requiresRealmSignature() && samlClient.addExtensionsElementWithKeyInfo()) {
                KeyManager.ActiveRsaKey keys = session.keys().getActiveRsaKey(realm);
                String keyName = samlClient.getXmlSigKeyInfoKeyNameTransformer().getKeyName(keys.getKid(), keys.getCertificate());
                extensions = new NodeGenerator[] { new KeycloakKeySamlExtensionGenerator(keyName) };
            }
        }
        LogoutRequestType logoutRequest = createLogoutRequest(bindingUri, clientSession, client, extensions);
        JaxrsSAML2BindingBuilder binding = createBindingBuilder(samlClient, "true".equals(clientSession.getNote(JBossSAMLURIConstants.SAML_HTTP_ARTIFACT_BINDING.get())));
        // If this session uses artifact binding, send an artifact instead of the LogoutRequest
        if ("true".equals(clientSession.getNote(JBossSAMLURIConstants.SAML_HTTP_ARTIFACT_BINDING.get())) && useArtifactForLogout(client)) {
            clientSession.setAction(CommonClientSessionModel.Action.LOGGING_OUT.name());
            return buildArtifactAuthenticatedResponse(clientSession, bindingUri, logoutRequest, binding);
        }
        Document samlDocument = SAML2Request.convert(logoutRequest);
        if (postBinding) {
            // This is POST binding, hence KeyID is included in dsig:KeyInfo/dsig:KeyName, no need to add <samlp:Extensions> element
            return binding.postBinding(samlDocument).request(bindingUri);
        } else {
            logger.debug("frontchannel redirect binding");
            return binding.redirectBinding(samlDocument).request(bindingUri);
        }
    } catch (ConfigurationException | ProcessingException | IOException | ParsingException e) {
        throw new RuntimeException(e);
    }
}
Also used : LogoutRequestType(org.keycloak.dom.saml.v2.protocol.LogoutRequestType) IOException(java.io.IOException) KeycloakKeySamlExtensionGenerator(org.keycloak.saml.processing.core.util.KeycloakKeySamlExtensionGenerator) Document(org.w3c.dom.Document) ClientModel(org.keycloak.models.ClientModel) ConfigurationException(org.keycloak.saml.common.exceptions.ConfigurationException) ParsingException(org.keycloak.saml.common.exceptions.ParsingException) KeyManager(org.keycloak.models.KeyManager) NodeGenerator(org.keycloak.saml.SamlProtocolExtensionsAwareBuilder.NodeGenerator) ProcessingException(org.keycloak.saml.common.exceptions.ProcessingException)

Example 4 with KeycloakKeySamlExtensionGenerator

use of org.keycloak.saml.processing.core.util.KeycloakKeySamlExtensionGenerator in project keycloak by keycloak.

the class SAMLIdentityProvider method performLogin.

@Override
public Response performLogin(AuthenticationRequest request) {
    try {
        UriInfo uriInfo = request.getUriInfo();
        RealmModel realm = request.getRealm();
        String issuerURL = getEntityId(uriInfo, realm);
        String destinationUrl = getConfig().getSingleSignOnServiceUrl();
        String nameIDPolicyFormat = getConfig().getNameIDPolicyFormat();
        if (nameIDPolicyFormat == null) {
            nameIDPolicyFormat = JBossSAMLURIConstants.NAMEID_FORMAT_PERSISTENT.get();
        }
        String protocolBinding = JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.get();
        String assertionConsumerServiceUrl = request.getRedirectUri();
        if (getConfig().isPostBindingResponse()) {
            protocolBinding = JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get();
        }
        SAML2RequestedAuthnContextBuilder requestedAuthnContext = new SAML2RequestedAuthnContextBuilder().setComparison(getConfig().getAuthnContextComparisonType());
        for (String authnContextClassRef : getAuthnContextClassRefUris()) requestedAuthnContext.addAuthnContextClassRef(authnContextClassRef);
        for (String authnContextDeclRef : getAuthnContextDeclRefUris()) requestedAuthnContext.addAuthnContextDeclRef(authnContextDeclRef);
        Integer attributeConsumingServiceIndex = getConfig().getAttributeConsumingServiceIndex();
        String loginHint = getConfig().isLoginHint() ? request.getAuthenticationSession().getClientNote(OIDCLoginProtocol.LOGIN_HINT_PARAM) : null;
        Boolean allowCreate = null;
        if (getConfig().getConfig().get(SAMLIdentityProviderConfig.ALLOW_CREATE) == null || getConfig().isAllowCreate())
            allowCreate = Boolean.TRUE;
        SAML2AuthnRequestBuilder authnRequestBuilder = new SAML2AuthnRequestBuilder().assertionConsumerUrl(assertionConsumerServiceUrl).destination(destinationUrl).issuer(issuerURL).forceAuthn(getConfig().isForceAuthn()).protocolBinding(protocolBinding).nameIdPolicy(SAML2NameIDPolicyBuilder.format(nameIDPolicyFormat).setAllowCreate(allowCreate)).attributeConsumingServiceIndex(attributeConsumingServiceIndex).requestedAuthnContext(requestedAuthnContext).subject(loginHint);
        JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder(session).relayState(request.getState().getEncoded());
        boolean postBinding = getConfig().isPostBindingAuthnRequest();
        if (getConfig().isWantAuthnRequestsSigned()) {
            KeyManager.ActiveRsaKey keys = session.keys().getActiveRsaKey(realm);
            String keyName = getConfig().getXmlSigKeyInfoKeyNameTransformer().getKeyName(keys.getKid(), keys.getCertificate());
            binding.signWith(keyName, keys.getPrivateKey(), keys.getPublicKey(), keys.getCertificate()).signatureAlgorithm(getSignatureAlgorithm()).signDocument();
            if (!postBinding && getConfig().isAddExtensionsElementWithKeyInfo()) {
                // Only include extension if REDIRECT binding and signing whole SAML protocol message
                authnRequestBuilder.addExtension(new KeycloakKeySamlExtensionGenerator(keyName));
            }
        }
        AuthnRequestType authnRequest = authnRequestBuilder.createAuthnRequest();
        for (Iterator<SamlAuthenticationPreprocessor> it = SamlSessionUtils.getSamlAuthenticationPreprocessorIterator(session); it.hasNext(); ) {
            authnRequest = it.next().beforeSendingLoginRequest(authnRequest, request.getAuthenticationSession());
        }
        if (authnRequest.getDestination() != null) {
            destinationUrl = authnRequest.getDestination().toString();
        }
        // Save the current RequestID in the Auth Session as we need to verify it against the ID returned from the IdP
        request.getAuthenticationSession().setClientNote(SamlProtocol.SAML_REQUEST_ID_BROKER, authnRequest.getID());
        if (postBinding) {
            return binding.postBinding(authnRequestBuilder.toDocument()).request(destinationUrl);
        } else {
            return binding.redirectBinding(authnRequestBuilder.toDocument()).request(destinationUrl);
        }
    } catch (Exception e) {
        throw new IdentityBrokerException("Could not create authentication request.", e);
    }
}
Also used : SAML2RequestedAuthnContextBuilder(org.keycloak.saml.SAML2RequestedAuthnContextBuilder) JaxrsSAML2BindingBuilder(org.keycloak.protocol.saml.JaxrsSAML2BindingBuilder) KeycloakKeySamlExtensionGenerator(org.keycloak.saml.processing.core.util.KeycloakKeySamlExtensionGenerator) ConfigurationException(org.keycloak.saml.common.exceptions.ConfigurationException) IdentityBrokerException(org.keycloak.broker.provider.IdentityBrokerException) ParserConfigurationException(javax.xml.parsers.ParserConfigurationException) RealmModel(org.keycloak.models.RealmModel) AuthnRequestType(org.keycloak.dom.saml.v2.protocol.AuthnRequestType) IdentityBrokerException(org.keycloak.broker.provider.IdentityBrokerException) SamlAuthenticationPreprocessor(org.keycloak.protocol.saml.preprocessor.SamlAuthenticationPreprocessor) SAML2AuthnRequestBuilder(org.keycloak.saml.SAML2AuthnRequestBuilder) KeyManager(org.keycloak.models.KeyManager) UriInfo(javax.ws.rs.core.UriInfo)

Aggregations

KeyManager (org.keycloak.models.KeyManager)4 ConfigurationException (org.keycloak.saml.common.exceptions.ConfigurationException)4 KeycloakKeySamlExtensionGenerator (org.keycloak.saml.processing.core.util.KeycloakKeySamlExtensionGenerator)4 IOException (java.io.IOException)3 ProcessingException (org.keycloak.saml.common.exceptions.ProcessingException)3 ClientModel (org.keycloak.models.ClientModel)2 ParsingException (org.keycloak.saml.common.exceptions.ParsingException)2 Document (org.w3c.dom.Document)2 PublicKey (java.security.PublicKey)1 LinkedList (java.util.LinkedList)1 AtomicReference (java.util.concurrent.atomic.AtomicReference)1 Response (javax.ws.rs.core.Response)1 UriInfo (javax.ws.rs.core.UriInfo)1 ParserConfigurationException (javax.xml.parsers.ParserConfigurationException)1 CloseableHttpResponse (org.apache.http.client.methods.CloseableHttpResponse)1 IdentityBrokerException (org.keycloak.broker.provider.IdentityBrokerException)1 AssertionType (org.keycloak.dom.saml.v2.assertion.AssertionType)1 AttributeStatementType (org.keycloak.dom.saml.v2.assertion.AttributeStatementType)1 ArtifactResponseType (org.keycloak.dom.saml.v2.protocol.ArtifactResponseType)1 AuthnRequestType (org.keycloak.dom.saml.v2.protocol.AuthnRequestType)1