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;
}
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);
}
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;
}
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);
}
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);
}
Aggregations