Search in sources :

Example 21 with BrokeredIdentityContext

use of org.keycloak.broker.provider.BrokeredIdentityContext in project keycloak by keycloak.

the class OIDCIdentityProvider method extractIdentity.

protected BrokeredIdentityContext extractIdentity(AccessTokenResponse tokenResponse, String accessToken, JsonWebToken idToken) throws IOException {
    String id = idToken.getSubject();
    BrokeredIdentityContext identity = new BrokeredIdentityContext(id);
    String name = (String) idToken.getOtherClaims().get(IDToken.NAME);
    String givenName = (String) idToken.getOtherClaims().get(IDToken.GIVEN_NAME);
    String familyName = (String) idToken.getOtherClaims().get(IDToken.FAMILY_NAME);
    String preferredUsername = (String) idToken.getOtherClaims().get(getusernameClaimNameForIdToken());
    String email = (String) idToken.getOtherClaims().get(IDToken.EMAIL);
    if (!getConfig().isDisableUserInfoService()) {
        String userInfoUrl = getUserInfoUrl();
        if (userInfoUrl != null && !userInfoUrl.isEmpty()) {
            if (accessToken != null) {
                SimpleHttp.Response response = executeRequest(userInfoUrl, SimpleHttp.doGet(userInfoUrl, session).header("Authorization", "Bearer " + accessToken));
                String contentType = response.getFirstHeader(HttpHeaders.CONTENT_TYPE);
                MediaType contentMediaType;
                try {
                    contentMediaType = MediaType.valueOf(contentType);
                } catch (IllegalArgumentException ex) {
                    contentMediaType = null;
                }
                if (contentMediaType == null || contentMediaType.isWildcardSubtype() || contentMediaType.isWildcardType()) {
                    throw new RuntimeException("Unsupported content-type [" + contentType + "] in response from [" + userInfoUrl + "].");
                }
                JsonNode userInfo;
                if (MediaType.APPLICATION_JSON_TYPE.isCompatible(contentMediaType)) {
                    userInfo = response.asJson();
                } else if (APPLICATION_JWT_TYPE.isCompatible(contentMediaType)) {
                    JWSInput jwsInput;
                    try {
                        jwsInput = new JWSInput(response.asString());
                    } catch (JWSInputException cause) {
                        throw new RuntimeException("Failed to parse JWT userinfo response", cause);
                    }
                    if (verify(jwsInput)) {
                        userInfo = JsonSerialization.readValue(jwsInput.getContent(), JsonNode.class);
                    } else {
                        throw new RuntimeException("Failed to verify signature of userinfo response from [" + userInfoUrl + "].");
                    }
                } else {
                    throw new RuntimeException("Unsupported content-type [" + contentType + "] in response from [" + userInfoUrl + "].");
                }
                id = getJsonProperty(userInfo, "sub");
                name = getJsonProperty(userInfo, "name");
                givenName = getJsonProperty(userInfo, IDToken.GIVEN_NAME);
                familyName = getJsonProperty(userInfo, IDToken.FAMILY_NAME);
                preferredUsername = getUsernameFromUserInfo(userInfo);
                email = getJsonProperty(userInfo, "email");
                AbstractJsonUserAttributeMapper.storeUserProfileForMapper(identity, userInfo, getConfig().getAlias());
            }
        }
    }
    identity.getContextData().put(VALIDATED_ID_TOKEN, idToken);
    identity.setId(id);
    if (givenName != null) {
        identity.setFirstName(givenName);
    }
    if (familyName != null) {
        identity.setLastName(familyName);
    }
    if (givenName == null && familyName == null) {
        identity.setName(name);
    }
    identity.setEmail(email);
    identity.setBrokerUserId(getConfig().getAlias() + "." + id);
    if (preferredUsername == null) {
        preferredUsername = email;
    }
    if (preferredUsername == null) {
        preferredUsername = id;
    }
    identity.setUsername(preferredUsername);
    if (tokenResponse != null && tokenResponse.getSessionState() != null) {
        identity.setBrokerSessionId(getConfig().getAlias() + "." + tokenResponse.getSessionState());
    }
    if (tokenResponse != null)
        identity.getContextData().put(FEDERATED_ACCESS_TOKEN_RESPONSE, tokenResponse);
    if (tokenResponse != null)
        processAccessTokenResponse(identity, tokenResponse);
    return identity;
}
Also used : SimpleHttp(org.keycloak.broker.provider.util.SimpleHttp) MediaType(javax.ws.rs.core.MediaType) JWSInputException(org.keycloak.jose.jws.JWSInputException) JsonNode(com.fasterxml.jackson.databind.JsonNode) JWSInput(org.keycloak.jose.jws.JWSInput) BrokeredIdentityContext(org.keycloak.broker.provider.BrokeredIdentityContext)

Example 22 with BrokeredIdentityContext

use of org.keycloak.broker.provider.BrokeredIdentityContext in project keycloak by keycloak.

the class LoginActionsService method brokerLoginFlow.

protected Response brokerLoginFlow(String authSessionId, String code, String execution, String clientId, String tabId, String flowPath) {
    boolean firstBrokerLogin = flowPath.equals(FIRST_BROKER_LOGIN_PATH);
    EventType eventType = firstBrokerLogin ? EventType.IDENTITY_PROVIDER_FIRST_LOGIN : EventType.IDENTITY_PROVIDER_POST_LOGIN;
    event.event(eventType);
    SessionCodeChecks checks = checksForCode(authSessionId, code, execution, clientId, tabId, flowPath);
    if (!checks.verifyActiveAndValidAction(AuthenticationSessionModel.Action.AUTHENTICATE.name(), ClientSessionCode.ActionType.LOGIN)) {
        return checks.getResponse();
    }
    event.detail(Details.CODE_ID, code);
    final AuthenticationSessionModel authSession = checks.getAuthenticationSession();
    processLocaleParam(authSession);
    String noteKey = firstBrokerLogin ? AbstractIdpAuthenticator.BROKERED_CONTEXT_NOTE : PostBrokerLoginConstants.PBL_BROKERED_IDENTITY_CONTEXT;
    SerializedBrokeredIdentityContext serializedCtx = SerializedBrokeredIdentityContext.readFromAuthenticationSession(authSession, noteKey);
    if (serializedCtx == null) {
        ServicesLogger.LOGGER.notFoundSerializedCtxInClientSession(noteKey);
        throw new WebApplicationException(ErrorPage.error(session, authSession, Response.Status.BAD_REQUEST, "Not found serialized context in authenticationSession."));
    }
    BrokeredIdentityContext brokerContext = serializedCtx.deserialize(session, authSession);
    final String identityProviderAlias = brokerContext.getIdpConfig().getAlias();
    String flowId = firstBrokerLogin ? brokerContext.getIdpConfig().getFirstBrokerLoginFlowId() : brokerContext.getIdpConfig().getPostBrokerLoginFlowId();
    if (flowId == null) {
        ServicesLogger.LOGGER.flowNotConfigForIDP(identityProviderAlias);
        throw new WebApplicationException(ErrorPage.error(session, authSession, Response.Status.BAD_REQUEST, "Flow not configured for identity provider"));
    }
    AuthenticationFlowModel brokerLoginFlow = realm.getAuthenticationFlowById(flowId);
    if (brokerLoginFlow == null) {
        ServicesLogger.LOGGER.flowNotFoundForIDP(flowId, identityProviderAlias);
        throw new WebApplicationException(ErrorPage.error(session, authSession, Response.Status.BAD_REQUEST, "Flow not found for identity provider"));
    }
    event.detail(Details.IDENTITY_PROVIDER, identityProviderAlias).detail(Details.IDENTITY_PROVIDER_USERNAME, brokerContext.getUsername());
    AuthenticationProcessor processor = new AuthenticationProcessor() {

        @Override
        public Response authenticateOnly() throws AuthenticationFlowException {
            Response challenge = super.authenticateOnly();
            if (challenge != null) {
                if ("true".equals(authenticationSession.getAuthNote(FORWARDED_PASSIVE_LOGIN))) {
                    // forwarded passive login is incompatible with challenges created by the broker flows.
                    logger.errorf("Challenge encountered when executing %s flow. Auth requests with prompt=none are incompatible with challenges", flowPath);
                    LoginProtocol protocol = session.getProvider(LoginProtocol.class, authSession.getProtocol());
                    protocol.setRealm(realm).setHttpHeaders(headers).setUriInfo(session.getContext().getUri()).setEventBuilder(event);
                    return protocol.sendError(authSession, Error.PASSIVE_INTERACTION_REQUIRED);
                }
            }
            return challenge;
        }

        @Override
        protected Response authenticationComplete() {
            if (firstBrokerLogin) {
                authSession.setAuthNote(AbstractIdpAuthenticator.FIRST_BROKER_LOGIN_SUCCESS, identityProviderAlias);
            } else {
                String authStateNoteKey = PostBrokerLoginConstants.PBL_AUTH_STATE_PREFIX + identityProviderAlias;
                authSession.setAuthNote(authStateNoteKey, "true");
            }
            return redirectToAfterBrokerLoginEndpoint(authSession, firstBrokerLogin);
        }
    };
    return processFlow(checks.isActionRequest(), execution, authSession, flowPath, brokerLoginFlow, null, processor);
}
Also used : Response(javax.ws.rs.core.Response) AuthenticationSessionModel(org.keycloak.sessions.AuthenticationSessionModel) RootAuthenticationSessionModel(org.keycloak.sessions.RootAuthenticationSessionModel) WebApplicationException(javax.ws.rs.WebApplicationException) EventType(org.keycloak.events.EventType) AuthenticationFlowModel(org.keycloak.models.AuthenticationFlowModel) SerializedBrokeredIdentityContext(org.keycloak.authentication.authenticators.broker.util.SerializedBrokeredIdentityContext) AuthenticationProcessor(org.keycloak.authentication.AuthenticationProcessor) OIDCLoginProtocol(org.keycloak.protocol.oidc.OIDCLoginProtocol) LoginProtocol(org.keycloak.protocol.LoginProtocol) BrokeredIdentityContext(org.keycloak.broker.provider.BrokeredIdentityContext) SerializedBrokeredIdentityContext(org.keycloak.authentication.authenticators.broker.util.SerializedBrokeredIdentityContext)

Example 23 with BrokeredIdentityContext

use of org.keycloak.broker.provider.BrokeredIdentityContext in project keycloak by keycloak.

the class AdvancedAttributeToRoleMapper method applies.

protected boolean applies(final IdentityProviderMapperModel mapperModel, final BrokeredIdentityContext context) {
    Map<String, String> attributes = mapperModel.getConfigMap(ATTRIBUTE_PROPERTY_NAME);
    boolean areAttributeValuesRegexes = Boolean.parseBoolean(mapperModel.getConfig().get(ARE_ATTRIBUTE_VALUES_REGEX_PROPERTY_NAME));
    AssertionType assertion = (AssertionType) context.getContextData().get(SAMLEndpoint.SAML_ASSERTION);
    Set<AttributeStatementType> attributeAssertions = assertion.getAttributeStatements();
    if (attributeAssertions == null) {
        return false;
    }
    for (Map.Entry<String, String> attribute : attributes.entrySet()) {
        String attributeKey = attribute.getKey();
        List<Object> attributeValues = attributeAssertions.stream().flatMap(statements -> statements.getAttributes().stream()).filter(choiceType -> attributeKey.equals(choiceType.getAttribute().getName()) || attributeKey.equals(choiceType.getAttribute().getFriendlyName())).flatMap(choiceType -> choiceType.getAttribute().getAttributeValue().stream()).collect(Collectors.toList());
        boolean attributeValueMatch = areAttributeValuesRegexes ? valueMatchesRegex(attribute.getValue(), attributeValues) : attributeValues.contains(attribute.getValue());
        if (!attributeValueMatch) {
            return false;
        }
    }
    return true;
}
Also used : RegexUtils.valueMatchesRegex(org.keycloak.utils.RegexUtils.valueMatchesRegex) Arrays(java.util.Arrays) SAMLEndpoint(org.keycloak.broker.saml.SAMLEndpoint) IdentityProviderMapperModel(org.keycloak.models.IdentityProviderMapperModel) IdentityProviderSyncMode(org.keycloak.models.IdentityProviderSyncMode) ProviderConfigProperty(org.keycloak.provider.ProviderConfigProperty) Set(java.util.Set) BrokeredIdentityContext(org.keycloak.broker.provider.BrokeredIdentityContext) ConfigConstants(org.keycloak.broker.provider.ConfigConstants) AssertionType(org.keycloak.dom.saml.v2.assertion.AssertionType) Collectors(java.util.stream.Collectors) ArrayList(java.util.ArrayList) AttributeStatementType(org.keycloak.dom.saml.v2.assertion.AttributeStatementType) HashSet(java.util.HashSet) List(java.util.List) SAMLIdentityProviderFactory(org.keycloak.broker.saml.SAMLIdentityProviderFactory) Map(java.util.Map) AttributeStatementType(org.keycloak.dom.saml.v2.assertion.AttributeStatementType) AssertionType(org.keycloak.dom.saml.v2.assertion.AssertionType) Map(java.util.Map)

Example 24 with BrokeredIdentityContext

use of org.keycloak.broker.provider.BrokeredIdentityContext in project keycloak by keycloak.

the class FreeMarkerLoginFormsProvider method createResponse.

@SuppressWarnings("incomplete-switch")
protected Response createResponse(LoginFormsPages page) {
    Theme theme;
    try {
        theme = getTheme();
    } catch (IOException e) {
        logger.error("Failed to create theme", e);
        return Response.serverError().build();
    }
    Locale locale = session.getContext().resolveLocale(user);
    Properties messagesBundle = handleThemeResources(theme, locale);
    handleMessages(locale, messagesBundle);
    // for some reason Resteasy 2.3.7 doesn't like query params and form params with the same name and will null out the code form param
    UriBuilder uriBuilder = prepareBaseUriBuilder(page == LoginFormsPages.OAUTH_GRANT);
    createCommonAttributes(theme, locale, messagesBundle, uriBuilder, page);
    attributes.put("login", new LoginBean(formData));
    if (status != null) {
        attributes.put("statusCode", status.getStatusCode());
    }
    switch(page) {
        case LOGIN_CONFIG_TOTP:
            attributes.put("totp", new TotpBean(session, realm, user, uriInfo.getRequestUriBuilder()));
            break;
        case LOGIN_UPDATE_PROFILE:
            UpdateProfileContext userCtx = (UpdateProfileContext) attributes.get(LoginFormsProvider.UPDATE_PROFILE_CONTEXT_ATTR);
            attributes.put("user", new ProfileBean(userCtx, formData));
            break;
        case LOGIN_IDP_LINK_CONFIRM:
        case LOGIN_IDP_LINK_EMAIL:
            BrokeredIdentityContext brokerContext = (BrokeredIdentityContext) this.attributes.get(IDENTITY_PROVIDER_BROKER_CONTEXT);
            String idpAlias = brokerContext.getIdpConfig().getAlias();
            idpAlias = ObjectUtil.capitalize(idpAlias);
            String displayName = idpAlias;
            if (!ObjectUtil.isBlank(brokerContext.getIdpConfig().getDisplayName())) {
                displayName = brokerContext.getIdpConfig().getDisplayName();
            }
            attributes.put("brokerContext", brokerContext);
            attributes.put("idpAlias", idpAlias);
            attributes.put("idpDisplayName", displayName);
            break;
        case LOGIN_TOTP:
            attributes.put("otpLogin", new TotpLoginBean(session, realm, user, (String) this.attributes.get(OTPFormAuthenticator.SELECTED_OTP_CREDENTIAL_ID)));
            break;
        case REGISTER:
            if (isDynamicUserProfile()) {
                page = LoginFormsPages.REGISTER_USER_PROFILE;
            }
            RegisterBean rb = new RegisterBean(formData, session);
            // legacy bean for static template
            attributes.put("register", rb);
            // bean for dynamic template
            attributes.put("profile", rb);
            break;
        case OAUTH_GRANT:
            attributes.put("oauth", new OAuthGrantBean(accessCode, client, clientScopesRequested));
            break;
        case CODE:
            attributes.put(OAuth2Constants.CODE, new CodeBean(accessCode, messageType == MessageType.ERROR ? getFirstMessageUnformatted() : null));
            break;
        case X509_CONFIRM:
            attributes.put("x509", new X509ConfirmBean(formData));
            break;
        case SAML_POST_FORM:
            attributes.put("samlPost", new SAMLPostFormBean(formData));
            break;
        case UPDATE_USER_PROFILE:
            attributes.put("profile", new VerifyProfileBean(user, formData, session));
            break;
        case IDP_REVIEW_USER_PROFILE:
            UpdateProfileContext idpCtx = (UpdateProfileContext) attributes.get(LoginFormsProvider.UPDATE_PROFILE_CONTEXT_ATTR);
            attributes.put("profile", new IdpReviewProfileBean(idpCtx, formData, session));
            break;
        case FRONTCHANNEL_LOGOUT:
            attributes.put("logout", new FrontChannelLogoutBean(session));
            break;
    }
    return processTemplate(theme, Templates.getTemplate(page), locale);
}
Also used : Locale(java.util.Locale) ProfileBean(org.keycloak.forms.login.freemarker.model.ProfileBean) IdpReviewProfileBean(org.keycloak.forms.login.freemarker.model.IdpReviewProfileBean) VerifyProfileBean(org.keycloak.forms.login.freemarker.model.VerifyProfileBean) OAuthGrantBean(org.keycloak.forms.login.freemarker.model.OAuthGrantBean) CodeBean(org.keycloak.forms.login.freemarker.model.CodeBean) RegisterBean(org.keycloak.forms.login.freemarker.model.RegisterBean) IOException(java.io.IOException) FrontChannelLogoutBean(org.keycloak.forms.login.freemarker.model.FrontChannelLogoutBean) Properties(java.util.Properties) BrokeredIdentityContext(org.keycloak.broker.provider.BrokeredIdentityContext) VerifyProfileBean(org.keycloak.forms.login.freemarker.model.VerifyProfileBean) UpdateProfileContext(org.keycloak.authentication.requiredactions.util.UpdateProfileContext) UserUpdateProfileContext(org.keycloak.authentication.requiredactions.util.UserUpdateProfileContext) TotpBean(org.keycloak.forms.login.freemarker.model.TotpBean) TotpLoginBean(org.keycloak.forms.login.freemarker.model.TotpLoginBean) X509ConfirmBean(org.keycloak.forms.login.freemarker.model.X509ConfirmBean) LoginBean(org.keycloak.forms.login.freemarker.model.LoginBean) TotpLoginBean(org.keycloak.forms.login.freemarker.model.TotpLoginBean) SAMLPostFormBean(org.keycloak.forms.login.freemarker.model.SAMLPostFormBean) Theme(org.keycloak.theme.Theme) IdpReviewProfileBean(org.keycloak.forms.login.freemarker.model.IdpReviewProfileBean) UriBuilder(javax.ws.rs.core.UriBuilder)

Example 25 with BrokeredIdentityContext

use of org.keycloak.broker.provider.BrokeredIdentityContext in project keycloak by keycloak.

the class FreeMarkerEmailTemplateProvider method sendConfirmIdentityBrokerLink.

@Override
public void sendConfirmIdentityBrokerLink(String link, long expirationInMinutes) throws EmailException {
    Map<String, Object> attributes = new HashMap<>(this.attributes);
    attributes.put("user", new ProfileBean(user));
    addLinkInfoIntoAttributes(link, expirationInMinutes, attributes);
    attributes.put("realmName", getRealmName());
    BrokeredIdentityContext brokerContext = (BrokeredIdentityContext) this.attributes.get(IDENTITY_PROVIDER_BROKER_CONTEXT);
    String idpAlias = brokerContext.getIdpConfig().getAlias();
    String idpDisplayName = brokerContext.getIdpConfig().getDisplayName();
    idpAlias = ObjectUtil.capitalize(idpAlias);
    if (idpDisplayName != null) {
        idpAlias = ObjectUtil.capitalize(idpDisplayName);
    }
    attributes.put("identityProviderContext", brokerContext);
    attributes.put("identityProviderAlias", idpAlias);
    List<Object> subjectAttrs = Arrays.asList(idpAlias);
    send("identityProviderLinkSubject", subjectAttrs, "identity-provider-link.ftl", attributes);
}
Also used : ProfileBean(org.keycloak.email.freemarker.beans.ProfileBean) HashMap(java.util.HashMap) BrokeredIdentityContext(org.keycloak.broker.provider.BrokeredIdentityContext)

Aggregations

BrokeredIdentityContext (org.keycloak.broker.provider.BrokeredIdentityContext)40 IOException (java.io.IOException)12 IdentityBrokerException (org.keycloak.broker.provider.IdentityBrokerException)12 JsonNode (com.fasterxml.jackson.databind.JsonNode)11 SerializedBrokeredIdentityContext (org.keycloak.authentication.authenticators.broker.util.SerializedBrokeredIdentityContext)6 ErrorResponseException (org.keycloak.services.ErrorResponseException)6 OAuthErrorException (org.keycloak.OAuthErrorException)5 AuthenticationSessionModel (org.keycloak.sessions.AuthenticationSessionModel)5 Map (java.util.Map)3 AuthenticationFlowException (org.keycloak.authentication.AuthenticationFlowException)3 SimpleHttp (org.keycloak.broker.provider.util.SimpleHttp)3 HashMap (java.util.HashMap)2 Set (java.util.Set)2 Collectors (java.util.stream.Collectors)2 WebApplicationException (javax.ws.rs.WebApplicationException)2 MediaType (javax.ws.rs.core.MediaType)2 Response (javax.ws.rs.core.Response)2 Test (org.junit.Test)2 IdentityProvider (org.keycloak.broker.provider.IdentityProvider)2 JWSInput (org.keycloak.jose.jws.JWSInput)2