/** * Get offline sessions for client * * <p>Returns a list of offline user sessions associated with this client * * @param firstResult Paging offset * @param maxResults Maximum results size (defaults to 100) * @return */ @Path("offline-sessions") @GET @NoCache @Produces(MediaType.APPLICATION_JSON) public List<UserSessionRepresentation> getOfflineUserSessions( @QueryParam("first") Integer firstResult, @QueryParam("max") Integer maxResults) { auth.requireView(); if (client == null) { throw new NotFoundException("Could not find client"); } firstResult = firstResult != null ? firstResult : -1; maxResults = maxResults != null ? maxResults : Constants.DEFAULT_MAX_RESULTS; List<UserSessionRepresentation> sessions = new ArrayList<UserSessionRepresentation>(); List<UserSessionModel> userSessions = session .sessions() .getOfflineUserSessions(client.getRealm(), client, firstResult, maxResults); for (UserSessionModel userSession : userSessions) { UserSessionRepresentation rep = ModelToRepresentation.toRepresentation(userSession); // Update lastSessionRefresh with the timestamp from clientSession for (ClientSessionModel clientSession : userSession.getClientSessions()) { if (client.getId().equals(clientSession.getClient().getId())) { rep.setLastAccess(Time.toMillis(clientSession.getTimestamp())); break; } } sessions.add(rep); } return sessions; }
protected boolean isLogoutPostBindingForClient(ClientSessionModel clientSession) { ClientModel client = clientSession.getClient(); SamlClient samlClient = new SamlClient(client); String logoutPostUrl = client.getAttribute(SAML_SINGLE_LOGOUT_SERVICE_URL_POST_ATTRIBUTE); String logoutRedirectUrl = client.getAttribute(SAML_SINGLE_LOGOUT_SERVICE_URL_REDIRECT_ATTRIBUTE); if (logoutPostUrl == null) { // if we don't have a redirect uri either, return true and default to the admin url + POST // binding if (logoutRedirectUrl == null) return true; return false; } if (samlClient.forcePostBinding()) { return true; // configured to force a post binding and post binding logout url is not null } String bindingType = clientSession.getNote(SAML_BINDING); // if the login binding was POST, return true if (SAML_POST_BINDING.equals(bindingType)) return true; if (logoutRedirectUrl == null) return true; // we don't have a redirect binding url, so use post binding return false; // redirect binding }
protected AccessToken initToken( RealmModel realm, ClientModel client, UserModel user, UserSessionModel session, ClientSessionModel clientSession) { AccessToken token = new AccessToken(); if (clientSession != null) token.clientSession(clientSession.getId()); token.id(KeycloakModelUtils.generateId()); token.subject(user.getId()); token.audience(client.getClientId()); token.issuedNow(); token.issuedFor(client.getClientId()); token.issuer(clientSession.getNote(OIDCLoginProtocol.ISSUER)); if (session != null) { token.setSessionState(session.getId()); } if (realm.getAccessTokenLifespan() > 0) { token.expiration(Time.currentTime() + realm.getAccessTokenLifespan()); } Set<String> allowedOrigins = client.getWebOrigins(); if (allowedOrigins != null) { token.setAllowedOrigins(allowedOrigins); } return token; }
private ClientSessionCode parseClientSessionCode(String code) { ClientSessionCode clientCode = ClientSessionCode.parse(code, this.session, this.realm); if (clientCode != null && clientCode.isValid(AUTHENTICATE.name(), ClientSessionCode.ActionType.LOGIN)) { ClientSessionModel clientSession = clientCode.getClientSession(); if (clientSession != null) { ClientModel client = clientSession.getClient(); if (client == null) { throw new IdentityBrokerException("Invalid client"); } logger.debugf("Got authorization code from client [%s].", client.getClientId()); } logger.debugf("Authorization code is valid."); return clientCode; } throw new IdentityBrokerException( "Invalid code, please login again through your application."); }
/** * Get offline sessions associated with the user and client * * @param id User id * @return */ @Path("{id}/offline-sessions/{clientId}") @GET @NoCache @Produces(MediaType.APPLICATION_JSON) public List<UserSessionRepresentation> getSessions( final @PathParam("id") String id, final @PathParam("clientId") String clientId) { auth.requireView(); UserModel user = session.users().getUserById(id, realm); if (user == null) { throw new NotFoundException("User not found"); } ClientModel client = realm.getClientById(clientId); if (client == null) { throw new NotFoundException("Client not found"); } List<UserSessionModel> sessions = new UserSessionManager(session).findOfflineSessions(realm, client, user); List<UserSessionRepresentation> reps = new ArrayList<UserSessionRepresentation>(); for (UserSessionModel session : sessions) { UserSessionRepresentation rep = ModelToRepresentation.toRepresentation(session); // Update lastSessionRefresh with the timestamp from clientSession for (ClientSessionModel clientSession : session.getClientSessions()) { if (clientId.equals(clientSession.getClient().getId())) { rep.setLastAccess(Time.toMillis(clientSession.getTimestamp())); break; } } reps.add(rep); } return reps; }
private static String createAdminToken(String username, String realm) { KeycloakSession session = keycloakRule.startSession(); try { RealmManager manager = new RealmManager(session); RealmModel adminRealm = manager.getRealm(realm); ClientModel adminConsole = adminRealm.getClientByClientId(Constants.ADMIN_CLI_CLIENT_ID); TokenManager tm = new TokenManager(); UserModel admin = session.users().getUserByUsername(username, adminRealm); ClientSessionModel clientSession = session.sessions().createClientSession(adminRealm, adminConsole); clientSession.setNote(OIDCLoginProtocol.ISSUER, "http://localhost:8081/auth/realms/" + realm); UserSessionModel userSession = session .sessions() .createUserSession(adminRealm, admin, "admin", null, "form", false, null, null); AccessToken token = tm.createClientAccessToken( session, tm.getAccess(null, true, adminConsole, admin), adminRealm, adminConsole, admin, userSession, clientSession); return tm.encodeToken(adminRealm, token); } finally { keycloakRule.stopSession(session, true); } }
/** * Initiated by admin, not the user on login * * @param key * @return */ @Path("execute-actions") @GET public Response executeActions(@QueryParam("key") String key) { event.event(EventType.EXECUTE_ACTIONS); if (key != null) { Checks checks = new Checks(); if (!checks.verifyCode(key, ClientSessionModel.Action.EXECUTE_ACTIONS.name())) { return checks.response; } ClientSessionModel clientSession = checks.clientCode.getClientSession(); clientSession.setNote(AuthenticationManager.END_AFTER_REQUIRED_ACTIONS, "true"); clientSession.setNote(ClientSessionModel.Action.EXECUTE_ACTIONS.name(), "true"); return AuthenticationManager.nextActionAfterAuthentication( session, clientSession.getUserSession(), clientSession, clientConnection, request, uriInfo, event); } else { event.error(Errors.INVALID_CODE); return ErrorPage.error(session, Messages.INVALID_CODE); } }
/** * Endpoint for executing reset credentials flow. If code is null, a client session is created * with the account service as the client. Successful reset sends you to the account page. Note, * account service must be enabled. * * @param code * @param execution * @return */ @Path(RESET_CREDENTIALS_PATH) @GET public Response resetCredentialsGET( @QueryParam("code") String code, @QueryParam("execution") String execution) { // we allow applications to link to reset credentials without going through OAuth or SAML // handshakes // if (code == null) { if (!realm.isResetPasswordAllowed()) { event.event(EventType.RESET_PASSWORD); event.error(Errors.NOT_ALLOWED); return ErrorPage.error(session, Messages.RESET_CREDENTIAL_NOT_ALLOWED); } // set up the account service as the endpoint to call. ClientModel client = realm.getClientByClientId(Constants.ACCOUNT_MANAGEMENT_CLIENT_ID); ClientSessionModel clientSession = session.sessions().createClientSession(realm, client); clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE.name()); clientSession.setNote(ClientSessionCode.ACTION_KEY, KeycloakModelUtils.generateCodeSecret()); // clientSession.setNote(AuthenticationManager.END_AFTER_REQUIRED_ACTIONS, "true"); clientSession.setAuthMethod(OIDCLoginProtocol.LOGIN_PROTOCOL); String redirectUri = Urls.accountBase(uriInfo.getBaseUri()).path("/").build(realm.getName()).toString(); clientSession.setRedirectUri(redirectUri); clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE.name()); clientSession.setNote(ClientSessionCode.ACTION_KEY, KeycloakModelUtils.generateCodeSecret()); clientSession.setNote(OIDCLoginProtocol.RESPONSE_TYPE_PARAM, OAuth2Constants.CODE); clientSession.setNote(OIDCLoginProtocol.REDIRECT_URI_PARAM, redirectUri); clientSession.setNote( OIDCLoginProtocol.ISSUER, Urls.realmIssuer(uriInfo.getBaseUri(), realm.getName())); return processResetCredentials(null, clientSession, null); } return resetCredentials(code, execution); }
protected Response buildErrorResponse( ClientSessionModel clientSession, JaxrsSAML2BindingBuilder binding, Document document) throws ConfigurationException, ProcessingException, IOException { if (isPostBinding(clientSession)) { return binding.postBinding(document).response(clientSession.getRedirectUri()); } else { return binding.redirectBinding(document).response(clientSession.getRedirectUri()); } }
private ClientSessionModel createClientSession( UserModel user, String redirectUri, String clientId) { if (!user.isEnabled()) { throw new WebApplicationException( ErrorResponse.error("User is disabled", Response.Status.BAD_REQUEST)); } if (redirectUri != null && clientId == null) { throw new WebApplicationException( ErrorResponse.error("Client id missing", Response.Status.BAD_REQUEST)); } if (clientId == null) { clientId = Constants.ACCOUNT_MANAGEMENT_CLIENT_ID; } ClientModel client = realm.getClientByClientId(clientId); if (client == null || !client.isEnabled()) { throw new WebApplicationException( ErrorResponse.error(clientId + " not enabled", Response.Status.BAD_REQUEST)); } String redirect; if (redirectUri != null) { redirect = RedirectUtils.verifyRedirectUri(uriInfo, redirectUri, realm, client); if (redirect == null) { throw new WebApplicationException( ErrorResponse.error("Invalid redirect uri.", Response.Status.BAD_REQUEST)); } } else { redirect = Urls.accountBase(uriInfo.getBaseUri()).path("/").build(realm.getName()).toString(); } UserSessionModel userSession = session .sessions() .createUserSession( realm, user, user.getUsername(), clientConnection.getRemoteAddr(), "form", false, null, null); // audit.session(userSession); ClientSessionModel clientSession = session.sessions().createClientSession(realm, client); clientSession.setAuthMethod(OIDCLoginProtocol.LOGIN_PROTOCOL); clientSession.setRedirectUri(redirect); clientSession.setUserSession(userSession); return clientSession; }
@Path("email-verification") @GET public Response emailVerification( @QueryParam("code") String code, @QueryParam("key") String key) { event.event(EventType.VERIFY_EMAIL); if (key != null) { Checks checks = new Checks(); if (!checks.verifyCode(key, ClientSessionModel.Action.VERIFY_EMAIL.name())) { return checks.response; } ClientSessionCode accessCode = checks.clientCode; ClientSessionModel clientSession = accessCode.getClientSession(); UserSessionModel userSession = clientSession.getUserSession(); UserModel user = userSession.getUser(); initEvent(clientSession); user.setEmailVerified(true); user.removeRequiredAction(RequiredAction.VERIFY_EMAIL); event.event(EventType.VERIFY_EMAIL).detail(Details.EMAIL, user.getEmail()).success(); String actionCookieValue = getActionCookie(); if (actionCookieValue == null || !actionCookieValue.equals(userSession.getId())) { session.sessions().removeClientSession(realm, clientSession); return session .getProvider(LoginFormsProvider.class) .setSuccess(Messages.EMAIL_VERIFIED) .createInfoPage(); } event = event.clone().removeDetail(Details.EMAIL).event(EventType.LOGIN); return AuthenticationManager.nextActionAfterAuthentication( session, userSession, clientSession, clientConnection, request, uriInfo, event); } else { Checks checks = new Checks(); if (!checks.verifyCode(code, ClientSessionModel.Action.VERIFY_EMAIL.name())) { return checks.response; } ClientSessionCode accessCode = checks.clientCode; ClientSessionModel clientSession = accessCode.getClientSession(); UserSessionModel userSession = clientSession.getUserSession(); initEvent(clientSession); createActionCookie(realm, uriInfo, clientConnection, userSession.getId()); return session .getProvider(LoginFormsProvider.class) .setClientSessionCode(accessCode.getCode()) .setUser(userSession.getUser()) .createResponse(RequiredAction.VERIFY_EMAIL); } }
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; }
/** * Send a update account email to the user * * <p>An email contains a link the user can click to perform a set of required actions. The * redirectUri and clientId parameters are optional. The default for the redirect is the account * client. * * @param id User is * @param redirectUri Redirect uri * @param clientId Client id * @param actions required actions the user needs to complete * @return */ @Path("{id}/execute-actions-email") @PUT @Consumes(MediaType.APPLICATION_JSON) public Response executeActionsEmail( @PathParam("id") String id, @QueryParam(OIDCLoginProtocol.REDIRECT_URI_PARAM) String redirectUri, @QueryParam(OIDCLoginProtocol.CLIENT_ID_PARAM) String clientId, List<String> actions) { auth.requireManage(); UserModel user = session.users().getUserById(id, realm); if (user == null) { return ErrorResponse.error("User not found", Response.Status.NOT_FOUND); } if (user.getEmail() == null) { return ErrorResponse.error("User email missing", Response.Status.BAD_REQUEST); } ClientSessionModel clientSession = createClientSession(user, redirectUri, clientId); for (String action : actions) { clientSession.addRequiredAction(action); } ClientSessionCode accessCode = new ClientSessionCode(realm, clientSession); accessCode.setAction(ClientSessionModel.Action.EXECUTE_ACTIONS.name()); try { UriBuilder builder = Urls.executeActionsBuilder(uriInfo.getBaseUri()); builder.queryParam("key", accessCode.getCode()); String link = builder.build(realm.getName()).toString(); long expiration = TimeUnit.SECONDS.toMinutes(realm.getAccessCodeLifespanUserAction()); this.session .getProvider(EmailTemplateProvider.class) .setRealm(realm) .setUser(user) .sendExecuteActions(link, expiration); // audit.user(user).detail(Details.EMAIL, user.getEmail()).detail(Details.CODE_ID, // accessCode.getCodeId()).success(); adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo).success(); return Response.ok().build(); } catch (EmailException e) { logger.failedToSendActionsEmail(e); return ErrorResponse.error( "Failed to send execute actions email", Response.Status.INTERNAL_SERVER_ERROR); } }
public static void attachClientSession( UserSessionModel session, ClientSessionModel clientSession) { if (clientSession.getUserSession() != null) { return; } UserModel user = session.getUser(); clientSession.setUserSession(session); Set<String> requestedRoles = new HashSet<String>(); // todo scope param protocol independent for (RoleModel r : TokenManager.getAccess(null, clientSession.getClient(), user)) { requestedRoles.add(r.getId()); } clientSession.setRoles(requestedRoles); Set<String> requestedProtocolMappers = new HashSet<String>(); for (ProtocolMapperModel protocolMapper : clientSession.getClient().getProtocolMappers()) { if (protocolMapper.getProtocol().equals(clientSession.getAuthMethod())) { requestedProtocolMappers.add(protocolMapper.getId()); } } clientSession.setProtocolMappers(requestedProtocolMappers); Map<String, String> transferredNotes = clientSession.getUserSessionNotes(); for (Map.Entry<String, String> entry : transferredNotes.entrySet()) { session.setNote(entry.getKey(), entry.getValue()); } }
@Override public Response sendError(ClientSessionModel clientSession, Error error) { setupResponseTypeAndMode(clientSession); String redirect = clientSession.getRedirectUri(); String state = clientSession.getNote(OIDCLoginProtocol.STATE_PARAM); OIDCRedirectUriBuilder redirectUri = OIDCRedirectUriBuilder.fromUri(redirect, responseMode) .addParam(OAuth2Constants.ERROR, translateError(error)); if (state != null) redirectUri.addParam(OAuth2Constants.STATE, state); session.sessions().removeClientSession(realm, clientSession); RestartLoginCookie.expireRestartCookie(realm, session.getContext().getConnection(), uriInfo); return redirectUri.build(); }
public static void dettachClientSession( UserSessionProvider sessions, RealmModel realm, ClientSessionModel clientSession) { UserSessionModel userSession = clientSession.getUserSession(); if (userSession == null) { return; } clientSession.setUserSession(null); clientSession.setRoles(null); clientSession.setProtocolMappers(null); if (userSession.getClientSessions().isEmpty()) { sessions.removeUserSession(realm, userSession); } }
@Override public Response frontchannelLogout( UserSessionModel userSession, ClientSessionModel clientSession) { ClientModel client = clientSession.getClient(); SamlClient samlClient = new SamlClient(client); if (!(client instanceof ClientModel)) return null; try { if (isLogoutPostBindingForClient(clientSession)) { String bindingUri = getLogoutServiceUrl(uriInfo, client, SAML_POST_BINDING); SAML2LogoutRequestBuilder logoutBuilder = createLogoutRequest(bindingUri, clientSession, client); JaxrsSAML2BindingBuilder binding = createBindingBuilder(samlClient); return binding.postBinding(logoutBuilder.buildDocument()).request(bindingUri); } else { logger.debug("frontchannel redirect binding"); String bindingUri = getLogoutServiceUrl(uriInfo, client, SAML_REDIRECT_BINDING); SAML2LogoutRequestBuilder logoutBuilder = createLogoutRequest(bindingUri, clientSession, client); JaxrsSAML2BindingBuilder binding = createBindingBuilder(samlClient); return binding.redirectBinding(logoutBuilder.buildDocument()).request(bindingUri); } } catch (ConfigurationException e) { throw new RuntimeException(e); } catch (ProcessingException e) { throw new RuntimeException(e); } catch (IOException e) { throw new RuntimeException(e); } catch (ParsingException e) { throw new RuntimeException(e); } }
@Override public void backchannelLogout(UserSessionModel userSession, ClientSessionModel clientSession) { ClientModel client = clientSession.getClient(); SamlClient samlClient = new SamlClient(client); String logoutUrl = getLogoutServiceUrl(uriInfo, client, SAML_POST_BINDING); if (logoutUrl == null) { logger.warnv( "Can't do backchannel logout. No SingleLogoutService POST Binding registered for client: {1}", client.getClientId()); return; } SAML2LogoutRequestBuilder logoutBuilder = createLogoutRequest(logoutUrl, clientSession, client); String logoutRequestString = null; try { JaxrsSAML2BindingBuilder binding = createBindingBuilder(samlClient); logoutRequestString = binding.postBinding(logoutBuilder.buildDocument()).encoded(); } catch (Exception e) { logger.warn("failed to send saml logout", e); return; } HttpClient httpClient = session.getProvider(HttpClientProvider.class).getHttpClient(); for (int i = 0; i < 2; i++) { // follow redirects once try { List<NameValuePair> formparams = new ArrayList<NameValuePair>(); formparams.add( new BasicNameValuePair(GeneralConstants.SAML_REQUEST_KEY, logoutRequestString)); formparams.add( new BasicNameValuePair("BACK_CHANNEL_LOGOUT", "BACK_CHANNEL_LOGOUT")); // for Picketlink // todo remove // this UrlEncodedFormEntity form = new UrlEncodedFormEntity(formparams, "UTF-8"); HttpPost post = new HttpPost(logoutUrl); post.setEntity(form); HttpResponse response = httpClient.execute(post); try { int status = response.getStatusLine().getStatusCode(); if (status == 302 && !logoutUrl.endsWith("/")) { String redirect = response.getFirstHeader(HttpHeaders.LOCATION).getValue(); String withSlash = logoutUrl + "/"; if (withSlash.equals(redirect)) { logoutUrl = withSlash; continue; } } } finally { HttpEntity entity = response.getEntity(); if (entity != null) { InputStream is = entity.getContent(); if (is != null) is.close(); } } } catch (IOException e) { logger.warn("failed to send saml logout", e); } break; } }
private void setupResponseTypeAndMode(ClientSessionModel clientSession) { String responseType = clientSession.getNote(OIDCLoginProtocol.RESPONSE_TYPE_PARAM); String responseMode = clientSession.getNote(OIDCLoginProtocol.RESPONSE_MODE_PARAM); this.responseType = OIDCResponseType.parse(responseType); this.responseMode = OIDCResponseMode.parse(responseMode, this.responseType); this.event.detail(Details.RESPONSE_TYPE, responseType); this.event.detail(Details.RESPONSE_MODE, this.responseMode.toString().toLowerCase()); }
@Override public Response authenticated(UserSessionModel userSession, ClientSessionCode accessCode) { ClientSessionModel clientSession = accessCode.getClientSession(); setupResponseTypeAndMode(clientSession); String redirect = clientSession.getRedirectUri(); OIDCRedirectUriBuilder redirectUri = OIDCRedirectUriBuilder.fromUri(redirect, responseMode); String state = clientSession.getNote(OIDCLoginProtocol.STATE_PARAM); logger.debugv("redirectAccessCode: state: {0}", state); if (state != null) redirectUri.addParam(OAuth2Constants.STATE, state); // Standard or hybrid flow if (responseType.hasResponseType(OIDCResponseType.CODE)) { accessCode.setAction(ClientSessionModel.Action.CODE_TO_TOKEN.name()); redirectUri.addParam(OAuth2Constants.CODE, accessCode.getCode()); } // Implicit or hybrid flow if (responseType.isImplicitOrHybridFlow()) { TokenManager tokenManager = new TokenManager(); AccessTokenResponse res = tokenManager .responseBuilder( realm, clientSession.getClient(), event, session, userSession, clientSession) .generateAccessToken() .generateIDToken() .build(); if (responseType.hasResponseType(OIDCResponseType.ID_TOKEN)) { redirectUri.addParam("id_token", res.getIdToken()); } if (responseType.hasResponseType(OIDCResponseType.TOKEN)) { redirectUri.addParam("access_token", res.getToken()); redirectUri.addParam("token_type", res.getTokenType()); redirectUri.addParam("session-state", res.getSessionState()); redirectUri.addParam("expires_in", String.valueOf(res.getExpiresIn())); } redirectUri.addParam("not-before-policy", String.valueOf(res.getNotBeforePolicy())); } return redirectUri.build(); }
protected String getNameIdFormat(SamlClient samlClient, ClientSessionModel clientSession) { String nameIdFormat = clientSession.getNote(GeneralConstants.NAMEID_FORMAT); boolean forceFormat = samlClient.forceNameIDFormat(); String configuredNameIdFormat = samlClient.getNameIDFormat(); if ((nameIdFormat == null || forceFormat) && configuredNameIdFormat != null) { nameIdFormat = configuredNameIdFormat; } if (nameIdFormat == null) return SAML_DEFAULT_NAMEID_FORMAT; return nameIdFormat; }
@Override public Response performLogin(AuthenticationRequest request) { try { Twitter twitter = new TwitterFactory().getInstance(); twitter.setOAuthConsumer(getConfig().getClientId(), getConfig().getClientSecret()); URI uri = new URI(request.getRedirectUri() + "?state=" + request.getState()); RequestToken requestToken = twitter.getOAuthRequestToken(uri.toString()); ClientSessionModel clientSession = request.getClientSession(); clientSession.setNote("twitter_token", requestToken.getToken()); clientSession.setNote("twitter_tokenSecret", requestToken.getTokenSecret()); URI authenticationUrl = URI.create(requestToken.getAuthenticationURL()); return Response.temporaryRedirect(authenticationUrl).build(); } catch (Exception e) { throw new IdentityBrokerException("Could send authentication request to twitter.", e); } }
@Override public Response sendError(ClientSessionModel clientSession, Error error) { try { if ("true".equals(clientSession.getClient().getAttribute(SAML_IDP_INITIATED_LOGIN))) { if (error == Error.CANCELLED_BY_USER) { UriBuilder builder = RealmsResource.protocolUrl(uriInfo).path(SamlService.class, "idpInitiatedSSO"); Map<String, String> params = new HashMap<>(); params.put("realm", realm.getName()); params.put("protocol", LOGIN_PROTOCOL); params.put( "client", clientSession.getClient().getAttribute(SAML_IDP_INITIATED_SSO_URL_NAME)); URI redirect = builder.buildFromMap(params); return Response.status(302).location(redirect).build(); } else { return ErrorPage.error(session, translateErrorToIdpInitiatedErrorMessage(error)); } } else { SAML2ErrorResponseBuilder builder = new SAML2ErrorResponseBuilder() .destination(clientSession.getRedirectUri()) .issuer(getResponseIssuer(realm)) .status(translateErrorToSAMLStatus(error).get()); try { JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder() .relayState(clientSession.getNote(GeneralConstants.RELAY_STATE)); Document document = builder.buildDocument(); return buildErrorResponse(clientSession, binding, document); } catch (Exception e) { return ErrorPage.error(session, Messages.FAILED_TO_PROCESS_RESPONSE); } } } finally { RestartLoginCookie.expireRestartCookie(realm, session.getContext().getConnection(), uriInfo); session.sessions().removeClientSession(realm, clientSession); } }
/** * Attempts to retrieve the persistent type NameId as follows: * * <ol> * <li>saml.persistent.name.id.for.$clientId user attribute * <li>saml.persistent.name.id.for.* user attribute * <li>G-$randomUuid * </ol> * * If a randomUuid is generated, an attribute for the given saml.persistent.name.id.for.$clientId * will be generated, otherwise no state change will occur with respect to the user's attributes. * * @return the user's persistent NameId */ protected String getPersistentNameId( final ClientSessionModel clientSession, final UserSessionModel userSession) { // attempt to retrieve the UserID for the client-specific attribute final UserModel user = userSession.getUser(); final String clientNameId = String.format( "%s.%s", SAML_PERSISTENT_NAME_ID_FOR, clientSession.getClient().getClientId()); String samlPersistentNameId = user.getFirstAttribute(clientNameId); if (samlPersistentNameId != null) { return samlPersistentNameId; } // check for a wildcard attribute final String wildcardNameId = String.format("%s.*", SAML_PERSISTENT_NAME_ID_FOR); samlPersistentNameId = user.getFirstAttribute(wildcardNameId); if (samlPersistentNameId != null) { return samlPersistentNameId; } // default to generated. "G-" stands for "generated" samlPersistentNameId = "G-" + UUID.randomUUID().toString(); user.setSingleAttribute(clientNameId, samlPersistentNameId); return samlPersistentNameId; }
private void initEvent(ClientSessionModel clientSession) { event .event(EventType.LOGIN) .client(clientSession.getClient()) .user(clientSession.getUserSession().getUser()) .session(clientSession.getUserSession().getId()) .detail(Details.CODE_ID, clientSession.getId()) .detail(Details.REDIRECT_URI, clientSession.getRedirectUri()) .detail( Details.USERNAME, clientSession.getNote(AbstractUsernameFormAuthenticator.ATTEMPTED_USERNAME)) .detail(Details.RESPONSE_TYPE, "code"); UserSessionModel userSession = clientSession.getUserSession(); if (userSession != null) { event.detail(Details.AUTH_METHOD, userSession.getAuthMethod()); event.detail(Details.USERNAME, userSession.getLoginUsername()); if (userSession.isRememberMe()) { event.detail(Details.REMEMBER_ME, "true"); } } }
@GET @Path("clients/{client}") @Produces(MediaType.TEXT_HTML) public Response idpInitiatedSSO( @PathParam("client") String clientUrlName, @QueryParam("RelayState") String relayState) { event.event(EventType.LOGIN); CacheControlUtil.noBackButtonCacheControlHeader(); ClientModel client = null; for (ClientModel c : realm.getClients()) { String urlName = c.getAttribute(SamlProtocol.SAML_IDP_INITIATED_SSO_URL_NAME); if (urlName == null) continue; if (urlName.equals(clientUrlName)) { client = c; break; } } if (client == null) { event.error(Errors.CLIENT_NOT_FOUND); return ErrorPage.error(session, Messages.CLIENT_NOT_FOUND); } if (client.getManagementUrl() == null && client.getAttribute(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_POST_ATTRIBUTE) == null && client.getAttribute(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_REDIRECT_ATTRIBUTE) == null) { logger.error("SAML assertion consumer url not set up"); event.error(Errors.INVALID_REDIRECT_URI); return ErrorPage.error(session, Messages.INVALID_REDIRECT_URI); } String bindingType = SamlProtocol.SAML_POST_BINDING; if (client.getManagementUrl() == null && client.getAttribute(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_POST_ATTRIBUTE) == null && client.getAttribute(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_REDIRECT_ATTRIBUTE) != null) { bindingType = SamlProtocol.SAML_REDIRECT_BINDING; } String redirect = null; if (bindingType.equals(SamlProtocol.SAML_REDIRECT_BINDING)) { redirect = client.getAttribute(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_REDIRECT_ATTRIBUTE); } else { redirect = client.getAttribute(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_POST_ATTRIBUTE); } if (redirect == null) { redirect = client.getManagementUrl(); } ClientSessionModel clientSession = session.sessions().createClientSession(realm, client); clientSession.setAuthMethod(SamlProtocol.LOGIN_PROTOCOL); clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE.name()); clientSession.setNote(SamlProtocol.SAML_BINDING, SamlProtocol.SAML_POST_BINDING); clientSession.setNote(SamlProtocol.SAML_IDP_INITIATED_LOGIN, "true"); clientSession.setRedirectUri(redirect); if (relayState == null) { relayState = client.getAttribute(SamlProtocol.SAML_IDP_INITIATED_SSO_RELAY_STATE); } if (relayState != null && !relayState.trim().equals("")) { clientSession.setNote(GeneralConstants.RELAY_STATE, relayState); } return newBrowserAuthentication(clientSession, false, false); }
protected Response logoutRequest( LogoutRequestType logoutRequest, ClientModel client, String relayState) { SamlClient samlClient = new SamlClient(client); // validate destination if (logoutRequest.getDestination() != null && !uriInfo.getAbsolutePath().equals(logoutRequest.getDestination())) { event.detail(Details.REASON, "invalid_destination"); event.error(Errors.INVALID_SAML_LOGOUT_REQUEST); return ErrorPage.error(session, Messages.INVALID_REQUEST); } // authenticate identity cookie, but ignore an access token timeout as we're logging out // anyways. AuthenticationManager.AuthResult authResult = authManager.authenticateIdentityCookie(session, realm, false); if (authResult != null) { String logoutBinding = getBindingType(); if ("true".equals(samlClient.forcePostBinding())) logoutBinding = SamlProtocol.SAML_POST_BINDING; String bindingUri = SamlProtocol.getLogoutServiceUrl(uriInfo, client, logoutBinding); UserSessionModel userSession = authResult.getSession(); userSession.setNote(SamlProtocol.SAML_LOGOUT_BINDING_URI, bindingUri); if (samlClient.requiresRealmSignature()) { userSession.setNote( SamlProtocol.SAML_LOGOUT_SIGNATURE_ALGORITHM, samlClient.getSignatureAlgorithm().toString()); } if (relayState != null) userSession.setNote(SamlProtocol.SAML_LOGOUT_RELAY_STATE, relayState); userSession.setNote(SamlProtocol.SAML_LOGOUT_REQUEST_ID, logoutRequest.getID()); userSession.setNote(SamlProtocol.SAML_LOGOUT_BINDING, logoutBinding); userSession.setNote( SamlProtocol.SAML_LOGOUT_CANONICALIZATION, samlClient.getCanonicalizationMethod()); userSession.setNote( AuthenticationManager.KEYCLOAK_LOGOUT_PROTOCOL, SamlProtocol.LOGIN_PROTOCOL); // remove client from logout requests for (ClientSessionModel clientSession : userSession.getClientSessions()) { if (clientSession.getClient().getId().equals(client.getId())) { clientSession.setAction(ClientSessionModel.Action.LOGGED_OUT.name()); } } logger.debug("browser Logout"); return authManager.browserLogout( session, realm, userSession, uriInfo, clientConnection, headers); } else if (logoutRequest.getSessionIndex() != null) { for (String sessionIndex : logoutRequest.getSessionIndex()) { ClientSessionModel clientSession = session.sessions().getClientSession(realm, sessionIndex); if (clientSession == null) continue; UserSessionModel userSession = clientSession.getUserSession(); if (clientSession.getClient().getClientId().equals(client.getClientId())) { // remove requesting client from logout clientSession.setAction(ClientSessionModel.Action.LOGGED_OUT.name()); // Remove also other clientSessions of this client as there could be more in this // UserSession if (userSession != null) { for (ClientSessionModel clientSession2 : userSession.getClientSessions()) { if (clientSession2.getClient().getId().equals(client.getId())) { clientSession2.setAction(ClientSessionModel.Action.LOGGED_OUT.name()); } } } } try { authManager.backchannelLogout( session, realm, userSession, uriInfo, clientConnection, headers, true); } catch (Exception e) { logger.warn("Failure with backchannel logout", e); } } } // default String logoutBinding = getBindingType(); String logoutBindingUri = SamlProtocol.getLogoutServiceUrl(uriInfo, client, logoutBinding); String logoutRelayState = relayState; SAML2LogoutResponseBuilder builder = new SAML2LogoutResponseBuilder(); builder.logoutRequestID(logoutRequest.getID()); builder.destination(logoutBindingUri); builder.issuer(RealmsResource.realmBaseUrl(uriInfo).build(realm.getName()).toString()); JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder().relayState(logoutRelayState); if (samlClient.requiresRealmSignature()) { SignatureAlgorithm algorithm = samlClient.getSignatureAlgorithm(); KeyManager.ActiveKey keys = session.keys().getActiveKey(realm); binding .signatureAlgorithm(algorithm) .signWith(keys.getPrivateKey(), keys.getPublicKey(), keys.getCertificate()) .signDocument(); } try { if (SamlProtocol.SAML_POST_BINDING.equals(logoutBinding)) { return binding.postBinding(builder.buildDocument()).response(logoutBindingUri); } else { return binding.redirectBinding(builder.buildDocument()).response(logoutBindingUri); } } catch (Exception e) { throw new RuntimeException(e); } }
protected Response loginRequest( String relayState, AuthnRequestType requestAbstractType, ClientModel client) { SamlClient samlClient = new SamlClient(client); // validate destination if (requestAbstractType.getDestination() != null && !uriInfo.getAbsolutePath().equals(requestAbstractType.getDestination())) { event.detail(Details.REASON, "invalid_destination"); event.error(Errors.INVALID_SAML_AUTHN_REQUEST); return ErrorPage.error(session, Messages.INVALID_REQUEST); } String bindingType = getBindingType(requestAbstractType); if (samlClient.forcePostBinding()) bindingType = SamlProtocol.SAML_POST_BINDING; String redirect = null; URI redirectUri = requestAbstractType.getAssertionConsumerServiceURL(); if (redirectUri != null && !"null".equals(redirectUri)) { // "null" is for testing purposes redirect = RedirectUtils.verifyRedirectUri(uriInfo, redirectUri.toString(), realm, client); } else { if (bindingType.equals(SamlProtocol.SAML_POST_BINDING)) { redirect = client.getAttribute(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_POST_ATTRIBUTE); } else { redirect = client.getAttribute(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_REDIRECT_ATTRIBUTE); } if (redirect == null) { redirect = client.getManagementUrl(); } } if (redirect == null) { event.error(Errors.INVALID_REDIRECT_URI); return ErrorPage.error(session, Messages.INVALID_REDIRECT_URI); } ClientSessionModel clientSession = session.sessions().createClientSession(realm, client); clientSession.setAuthMethod(SamlProtocol.LOGIN_PROTOCOL); clientSession.setRedirectUri(redirect); clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE.name()); clientSession.setNote(SamlProtocol.SAML_BINDING, bindingType); clientSession.setNote(GeneralConstants.RELAY_STATE, relayState); clientSession.setNote(SamlProtocol.SAML_REQUEST_ID, requestAbstractType.getID()); // Handle NameIDPolicy from SP NameIDPolicyType nameIdPolicy = requestAbstractType.getNameIDPolicy(); if (nameIdPolicy != null && !samlClient.forceNameIDFormat()) { String nameIdFormat = nameIdPolicy.getFormat().toString(); // TODO: Handle AllowCreate too, relevant for persistent NameID. if (isSupportedNameIdFormat(nameIdFormat)) { clientSession.setNote(GeneralConstants.NAMEID_FORMAT, nameIdFormat); } else { event.detail(Details.REASON, "unsupported_nameid_format"); event.error(Errors.INVALID_SAML_AUTHN_REQUEST); return ErrorPage.error(session, Messages.UNSUPPORTED_NAME_ID_FORMAT); } } // Reading subject/nameID in the saml request SubjectType subject = requestAbstractType.getSubject(); if (subject != null) { SubjectType.STSubType subType = subject.getSubType(); if (subType != null) { BaseIDAbstractType baseID = subject.getSubType().getBaseID(); if (baseID != null && baseID instanceof NameIDType) { NameIDType nameID = (NameIDType) baseID; clientSession.setNote(OIDCLoginProtocol.LOGIN_HINT_PARAM, nameID.getValue()); } } } return newBrowserAuthentication( clientSession, requestAbstractType.isIsPassive(), redirectToAuthentication); }
@Path("federated-identity-update") @GET public Response processFederatedIdentityUpdate( @QueryParam("action") String action, @QueryParam("provider_id") String providerId, @QueryParam("stateChecker") String stateChecker) { if (auth == null) { return login("identity"); } require(AccountRoles.MANAGE_ACCOUNT); csrfCheck(stateChecker); UserModel user = auth.getUser(); if (Validation.isEmpty(providerId)) { setReferrerOnPage(); return account .setError(Messages.MISSING_IDENTITY_PROVIDER) .createResponse(AccountPages.FEDERATED_IDENTITY); } AccountSocialAction accountSocialAction = AccountSocialAction.getAction(action); if (accountSocialAction == null) { setReferrerOnPage(); return account .setError(Messages.INVALID_FEDERATED_IDENTITY_ACTION) .createResponse(AccountPages.FEDERATED_IDENTITY); } boolean hasProvider = false; for (IdentityProviderModel model : realm.getIdentityProviders()) { if (model.getAlias().equals(providerId)) { hasProvider = true; } } if (!hasProvider) { setReferrerOnPage(); return account .setError(Messages.IDENTITY_PROVIDER_NOT_FOUND) .createResponse(AccountPages.FEDERATED_IDENTITY); } if (!user.isEnabled()) { setReferrerOnPage(); return account .setError(Messages.ACCOUNT_DISABLED) .createResponse(AccountPages.FEDERATED_IDENTITY); } switch (accountSocialAction) { case ADD: String redirectUri = UriBuilder.fromUri( Urls.accountFederatedIdentityPage(uriInfo.getBaseUri(), realm.getName())) .build() .toString(); try { ClientSessionModel clientSession = auth.getClientSession(); ClientSessionCode clientSessionCode = new ClientSessionCode(realm, clientSession); clientSessionCode.setAction(ClientSessionModel.Action.AUTHENTICATE.name()); clientSession.setRedirectUri(redirectUri); clientSession.setNote(OIDCLoginProtocol.STATE_PARAM, UUID.randomUUID().toString()); return Response.temporaryRedirect( Urls.identityProviderAuthnRequest( this.uriInfo.getBaseUri(), providerId, realm.getName(), clientSessionCode.getCode())) .build(); } catch (Exception spe) { setReferrerOnPage(); return account .setError(Messages.IDENTITY_PROVIDER_REDIRECT_ERROR) .createResponse(AccountPages.FEDERATED_IDENTITY); } case REMOVE: FederatedIdentityModel link = session.users().getFederatedIdentity(user, providerId, realm); if (link != null) { // Removing last social provider is not possible if you don't have other possibility to // authenticate if (session.users().getFederatedIdentities(user, realm).size() > 1 || user.getFederationLink() != null || isPasswordSet(user)) { session.users().removeFederatedIdentity(realm, user, providerId); logger.debugv( "Social provider {0} removed successfully from user {1}", providerId, user.getUsername()); event .event(EventType.REMOVE_FEDERATED_IDENTITY) .client(auth.getClient()) .user(auth.getUser()) .detail(Details.USERNAME, link.getUserId() + "@" + link.getIdentityProvider()) .success(); setReferrerOnPage(); return account .setSuccess(Messages.IDENTITY_PROVIDER_REMOVED) .createResponse(AccountPages.FEDERATED_IDENTITY); } else { setReferrerOnPage(); return account .setError(Messages.FEDERATED_IDENTITY_REMOVING_LAST_PROVIDER) .createResponse(AccountPages.FEDERATED_IDENTITY); } } else { setReferrerOnPage(); return account .setError(Messages.FEDERATED_IDENTITY_NOT_ACTIVE) .createResponse(AccountPages.FEDERATED_IDENTITY); } default: throw new IllegalArgumentException(); } }
public void init() { eventStore = session.getProvider(EventStoreProvider.class); account = session .getProvider(AccountProvider.class) .setRealm(realm) .setUriInfo(uriInfo) .setHttpHeaders(headers); AuthenticationManager.AuthResult authResult = authManager.authenticateBearerToken(session, realm, uriInfo, clientConnection, headers); if (authResult != null) { auth = new Auth( realm, authResult.getToken(), authResult.getUser(), client, authResult.getSession(), false); } else { authResult = authManager.authenticateIdentityCookie(session, realm); if (authResult != null) { auth = new Auth( realm, authResult.getToken(), authResult.getUser(), client, authResult.getSession(), true); updateCsrfChecks(); account.setStateChecker(stateChecker); } } String requestOrigin = UriUtils.getOrigin(uriInfo.getBaseUri()); // don't allow cors requests unless they were authenticated by an access token // This is to prevent CSRF attacks. if (auth != null && auth.isCookieAuthenticated()) { String origin = headers.getRequestHeaders().getFirst("Origin"); if (origin != null && !requestOrigin.equals(origin)) { throw new ForbiddenException(); } if (!request.getHttpMethod().equals("GET")) { String referrer = headers.getRequestHeaders().getFirst("Referer"); if (referrer != null && !requestOrigin.equals(UriUtils.getOrigin(referrer))) { throw new ForbiddenException(); } } } if (authResult != null) { UserSessionModel userSession = authResult.getSession(); if (userSession != null) { boolean associated = false; for (ClientSessionModel c : userSession.getClientSessions()) { if (c.getClient().equals(client)) { auth.setClientSession(c); associated = true; break; } } if (!associated) { ClientSessionModel clientSession = session.sessions().createClientSession(realm, client); clientSession.setUserSession(userSession); auth.setClientSession(clientSession); } } account.setUser(auth.getUser()); } boolean eventsEnabled = eventStore != null && realm.isEventsEnabled(); // todo find out from federation if password is updatable account.setFeatures(realm.isIdentityFederationEnabled(), eventsEnabled, true); }