protected SAML2LogoutRequestBuilder createLogoutRequest( String logoutUrl, ClientSessionModel clientSession, ClientModel client) { // build userPrincipal with subject used at login SAML2LogoutRequestBuilder logoutBuilder = new SAML2LogoutRequestBuilder() .assertionExpiration(realm.getAccessCodeLifespan()) .issuer(getResponseIssuer(realm)) .sessionIndex(clientSession.getId()) .userPrincipal( clientSession.getNote(SAML_NAME_ID), clientSession.getNote(SAML_NAME_ID_FORMAT)) .destination(logoutUrl); return logoutBuilder; }
@Override public Response authenticated(UserSessionModel userSession, ClientSessionCode accessCode) { ClientSessionModel clientSession = accessCode.getClientSession(); ClientModel client = clientSession.getClient(); SamlClient samlClient = new SamlClient(client); String requestID = clientSession.getNote(SAML_REQUEST_ID); String relayState = clientSession.getNote(GeneralConstants.RELAY_STATE); String redirectUri = clientSession.getRedirectUri(); String responseIssuer = getResponseIssuer(realm); String nameIdFormat = getNameIdFormat(samlClient, clientSession); String nameId = getNameId(nameIdFormat, clientSession, userSession); // 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); SAML2LoginResponseBuilder builder = new SAML2LoginResponseBuilder(); builder .requestID(requestID) .destination(redirectUri) .issuer(responseIssuer) .assertionExpiration(realm.getAccessCodeLifespan()) .subjectExpiration(realm.getAccessTokenLifespan()) .sessionIndex(clientSession.getId()) .requestIssuer(clientSession.getClient().getClientId()) .nameIdentifier(nameIdFormat, nameId) .authMethod(JBossSAMLURIConstants.AC_UNSPECIFIED.get()); if (!samlClient.includeAuthnStatement()) { builder.disableAuthnStatement(true); } List<ProtocolMapperProcessor<SAMLAttributeStatementMapper>> attributeStatementMappers = new LinkedList<>(); List<ProtocolMapperProcessor<SAMLLoginResponseMapper>> loginResponseMappers = new LinkedList<>(); ProtocolMapperProcessor<SAMLRoleListMapper> roleListMapper = null; Set<ProtocolMapperModel> mappings = accessCode.getRequestedProtocolMappers(); for (ProtocolMapperModel mapping : mappings) { ProtocolMapper mapper = (ProtocolMapper) session .getKeycloakSessionFactory() .getProviderFactory(ProtocolMapper.class, mapping.getProtocolMapper()); if (mapper == null) continue; if (mapper instanceof SAMLAttributeStatementMapper) { attributeStatementMappers.add( new ProtocolMapperProcessor<SAMLAttributeStatementMapper>( (SAMLAttributeStatementMapper) mapper, mapping)); } if (mapper instanceof SAMLLoginResponseMapper) { loginResponseMappers.add( new ProtocolMapperProcessor<SAMLLoginResponseMapper>( (SAMLLoginResponseMapper) mapper, mapping)); } if (mapper instanceof SAMLRoleListMapper) { roleListMapper = new ProtocolMapperProcessor<SAMLRoleListMapper>((SAMLRoleListMapper) mapper, mapping); } } Document samlDocument = null; try { ResponseType samlModel = builder.buildModel(); final AttributeStatementType attributeStatement = populateAttributeStatements( attributeStatementMappers, session, userSession, clientSession); populateRoles(roleListMapper, session, userSession, clientSession, 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, clientSession); samlDocument = builder.buildDocument(samlModel); } catch (Exception e) { logger.error("failed", e); return ErrorPage.error(session, Messages.FAILED_TO_PROCESS_RESPONSE); } JaxrsSAML2BindingBuilder bindingBuilder = new JaxrsSAML2BindingBuilder(); bindingBuilder.relayState(relayState); KeyManager keyManager = session.keys(); KeyManager.ActiveKey keys = keyManager.getActiveKey(realm); if (samlClient.requiresRealmSignature()) { String canonicalization = samlClient.getCanonicalizationMethod(); if (canonicalization != null) { bindingBuilder.canonicalizationMethod(canonicalization); } bindingBuilder .signatureAlgorithm(samlClient.getSignatureAlgorithm()) .signWith(keys.getPrivateKey(), keys.getPublicKey(), keys.getCertificate()) .signDocument(); } if (samlClient.requiresAssertionSignature()) { String canonicalization = samlClient.getCanonicalizationMethod(); if (canonicalization != null) { bindingBuilder.canonicalizationMethod(canonicalization); } bindingBuilder .signatureAlgorithm(samlClient.getSignatureAlgorithm()) .signWith(keys.getPrivateKey(), keys.getPublicKey(), keys.getCertificate()) .signAssertions(); } if (samlClient.requiresEncryption()) { PublicKey publicKey = null; try { publicKey = SamlProtocolUtils.getEncryptionValidationKey(client); } catch (Exception e) { logger.error("failed", e); return ErrorPage.error(session, Messages.FAILED_TO_PROCESS_RESPONSE); } bindingBuilder.encrypt(publicKey); } try { return buildAuthenticatedResponse(clientSession, redirectUri, samlDocument, bindingBuilder); } catch (Exception e) { logger.error("failed", e); return ErrorPage.error(session, Messages.FAILED_TO_PROCESS_RESPONSE); } }