예제 #1
0
 private String getActionCookie() {
   Cookie cookie = headers.getCookies().get(ACTION_COOKIE);
   AuthenticationManager.expireCookie(
       realm,
       ACTION_COOKIE,
       AuthenticationManager.getRealmCookiePath(realm, uriInfo),
       realm.getSslRequired().isRequired(clientConnection),
       clientConnection);
   return cookie != null ? cookie.getValue() : null;
 }
예제 #2
0
  /**
   * Impersonate the user
   *
   * @param id User id
   * @return
   */
  @Path("{id}/impersonation")
  @POST
  @NoCache
  @Produces(MediaType.APPLICATION_JSON)
  public Map<String, Object> impersonate(final @PathParam("id") String id) {
    auth.init(RealmAuth.Resource.IMPERSONATION);
    auth.requireManage();
    UserModel user = session.users().getUserById(id, realm);
    if (user == null) {
      throw new NotFoundException("User not found");
    }
    RealmModel authenticatedRealm = auth.getAuth().getRealm();
    // if same realm logout before impersonation
    boolean sameRealm = false;
    if (authenticatedRealm.getId().equals(realm.getId())) {
      sameRealm = true;
      UserSessionModel userSession =
          session
              .sessions()
              .getUserSession(authenticatedRealm, auth.getAuth().getToken().getSessionState());
      AuthenticationManager.expireIdentityCookie(realm, uriInfo, clientConnection);
      AuthenticationManager.expireRememberMeCookie(realm, uriInfo, clientConnection);
      AuthenticationManager.backchannelLogout(
          session, authenticatedRealm, userSession, uriInfo, clientConnection, headers, true);
    }
    EventBuilder event = new EventBuilder(realm, session, clientConnection);

    UserSessionModel userSession =
        session
            .sessions()
            .createUserSession(
                realm,
                user,
                user.getUsername(),
                clientConnection.getRemoteAddr(),
                "impersonate",
                false,
                null,
                null);
    AuthenticationManager.createLoginCookie(
        realm, userSession.getUser(), userSession, uriInfo, clientConnection);
    URI redirect = AccountService.accountServiceApplicationPage(uriInfo).build(realm.getName());
    Map<String, Object> result = new HashMap<>();
    result.put("sameRealm", sameRealm);
    result.put("redirect", redirect.toString());
    event
        .event(EventType.IMPERSONATE)
        .session(userSession)
        .user(user)
        .detail(Details.IMPERSONATOR_REALM, authenticatedRealm.getName())
        .detail(Details.IMPERSONATOR, auth.getAuth().getUser().getUsername())
        .success();

    return result;
  }
예제 #3
0
 /**
  * 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);
   }
 }
예제 #4
0
  /**
   * Revoke consent and offline tokens for particular client from user
   *
   * @param id User id
   * @param clientId Client id
   */
  @Path("{id}/consents/{client}")
  @DELETE
  @NoCache
  public void revokeConsent(
      final @PathParam("id") String id, final @PathParam("client") String clientId) {
    auth.requireManage();
    UserModel user = session.users().getUserById(id, realm);
    if (user == null) {
      throw new NotFoundException("User not found");
    }

    ClientModel client = realm.getClientByClientId(clientId);
    boolean revokedConsent = user.revokeConsentForClient(client.getId());
    boolean revokedOfflineToken = new UserSessionManager(session).revokeOfflineToken(user, client);

    if (revokedConsent) {
      // Logout clientSessions for this user and client
      AuthenticationManager.backchannelUserFromClient(
          session, realm, user, client, uriInfo, headers);
    }

    if (!revokedConsent && !revokedOfflineToken) {
      throw new NotFoundException("Consent nor offline token not found");
    }
    adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo).success();
  }
예제 #5
0
  @Path("sessions-logout")
  @GET
  public Response processSessionsLogout(@QueryParam("stateChecker") String stateChecker) {
    if (auth == null) {
      return login("sessions");
    }

    require(AccountRoles.MANAGE_ACCOUNT);
    csrfCheck(stateChecker);

    UserModel user = auth.getUser();
    List<UserSessionModel> userSessions = session.sessions().getUserSessions(realm, user);
    for (UserSessionModel userSession : userSessions) {
      AuthenticationManager.backchannelLogout(
          session, realm, userSession, uriInfo, clientConnection, headers, true);
    }

    UriBuilder builder =
        Urls.accountBase(uriInfo.getBaseUri()).path(AccountService.class, "sessionsPage");
    String referrer = uriInfo.getQueryParameters().getFirst("referrer");
    if (referrer != null) {
      builder.queryParam("referrer", referrer);
    }
    URI location = builder.build(realm.getName());
    return Response.seeOther(location).build();
  }
예제 #6
0
  protected Response processFlow(
      String execution,
      ClientSessionModel clientSession,
      String flowPath,
      AuthenticationFlowModel flow,
      String errorMessage) {
    AuthenticationProcessor processor = new AuthenticationProcessor();
    processor
        .setClientSession(clientSession)
        .setFlowPath(flowPath)
        .setFlowId(flow.getId())
        .setConnection(clientConnection)
        .setEventBuilder(event)
        .setProtector(authManager.getProtector())
        .setRealm(realm)
        .setSession(session)
        .setUriInfo(uriInfo)
        .setRequest(request);
    if (errorMessage != null)
      processor.setForwardedErrorMessage(new FormMessage(null, errorMessage));

    try {
      if (execution != null) {
        return processor.authenticationAction(execution);
      } else {
        return processor.authenticate();
      }
    } catch (Exception e) {
      return processor.handleBrowserException(e);
    }
  }
예제 #7
0
 /**
  * Remove a specific user session. Any client that has an admin url will also be told to
  * invalidate this particular session.
  *
  * @param sessionId
  */
 @Path("sessions/{session}")
 @DELETE
 public void deleteSession(@PathParam("session") String sessionId) {
   auth.init(RealmAuth.Resource.USER).requireManage();
   UserSessionModel userSession = session.sessions().getUserSession(realm, sessionId);
   if (userSession == null) throw new NotFoundException("Sesssion not found");
   AuthenticationManager.backchannelLogout(
       session, realm, userSession, uriInfo, connection, headers, true);
   adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo).success();
 }
예제 #8
0
 public Response redirectAccessCode(
     AccessCodeEntry accessCode,
     UserSessionModel session,
     String state,
     String redirect,
     boolean rememberMe) {
   String code = accessCode.getCode();
   UriBuilder redirectUri = UriBuilder.fromUri(redirect).queryParam(OAuth2Constants.CODE, code);
   log.debugv("redirectAccessCode: state: {0}", state);
   if (state != null) redirectUri.queryParam(OAuth2Constants.STATE, state);
   Response.ResponseBuilder location = Response.status(302).location(redirectUri.build());
   Cookie remember =
       request.getHttpHeaders().getCookies().get(AuthenticationManager.KEYCLOAK_REMEMBER_ME);
   rememberMe = rememberMe || remember != null;
   // refresh the cookies!
   authManager.createLoginCookie(realm, accessCode.getUser(), session, uriInfo, rememberMe);
   if (rememberMe) authManager.createRememberMeCookie(realm, uriInfo);
   return location.build();
 }
예제 #9
0
  @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);
    }
  }
예제 #10
0
 public static void createActionCookie(
     RealmModel realm, UriInfo uriInfo, ClientConnection clientConnection, String sessionId) {
   CookieHelper.addCookie(
       ACTION_COOKIE,
       sessionId,
       AuthenticationManager.getRealmCookiePath(realm, uriInfo),
       null,
       null,
       -1,
       realm.getSslRequired().isRequired(clientConnection),
       true);
 }
예제 #11
0
  /**
   * Remove all user sessions associated with the user
   *
   * <p>Also send notification to all clients that have an admin URL to invalidate the sessions for
   * the particular user.
   *
   * @param id User id
   */
  @Path("{id}/logout")
  @POST
  public void logout(final @PathParam("id") String id) {
    auth.requireManage();
    UserModel user = session.users().getUserById(id, realm);
    if (user == null) {
      throw new NotFoundException("User not found");
    }

    List<UserSessionModel> userSessions = session.sessions().getUserSessions(realm, user);
    for (UserSessionModel userSession : userSessions) {
      AuthenticationManager.backchannelLogout(
          session, realm, userSession, uriInfo, clientConnection, headers, true);
    }
    adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo).success();
  }
예제 #12
0
  @Path("revoke-grant")
  @POST
  public Response processRevokeGrant(final MultivaluedMap<String, String> formData) {
    if (auth == null) {
      return login("applications");
    }

    require(AccountRoles.MANAGE_ACCOUNT);
    csrfCheck(formData);

    String clientId = formData.getFirst("clientId");
    if (clientId == null) {
      return account.setError(Messages.CLIENT_NOT_FOUND).createResponse(AccountPages.APPLICATIONS);
    }
    ClientModel client = realm.getClientById(clientId);
    if (client == null) {
      return account.setError(Messages.CLIENT_NOT_FOUND).createResponse(AccountPages.APPLICATIONS);
    }

    // Revoke grant in UserModel
    UserModel user = auth.getUser();
    user.revokeConsentForClient(client.getId());
    OfflineTokenUtils.revokeOfflineToken(session, realm, user, client);

    // Logout clientSessions for this user and client
    AuthenticationManager.backchannelUserFromClient(session, realm, user, client, uriInfo, headers);

    event
        .event(EventType.REVOKE_GRANT)
        .client(auth.getClient())
        .user(auth.getUser())
        .detail(Details.REVOKED_CLIENT, client.getClientId())
        .success();
    setReferrerOnPage();

    UriBuilder builder =
        Urls.accountBase(uriInfo.getBaseUri()).path(AccountService.class, "applicationsPage");
    String referrer = uriInfo.getQueryParameters().getFirst("referrer");
    if (referrer != null) {
      builder.queryParam("referrer", referrer);
    }
    URI location = builder.build(realm.getName());
    return Response.seeOther(location).build();
  }
예제 #13
0
 @GET
 @Path("logout_response")
 public Response logoutResponse(@Context UriInfo uriInfo, @QueryParam("state") String state) {
   UserSessionModel userSession = session.sessions().getUserSession(realm, state);
   if (userSession == null) {
     logger.error("no valid user session");
     EventBuilder event = new EventBuilder(realm, session, clientConnection);
     event.event(EventType.LOGOUT);
     event.error(Errors.USER_SESSION_NOT_FOUND);
     return ErrorPage.error(session, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
   }
   if (userSession.getState() != UserSessionModel.State.LOGGING_OUT) {
     logger.error("usersession in different state");
     EventBuilder event = new EventBuilder(realm, session, clientConnection);
     event.event(EventType.LOGOUT);
     event.error(Errors.USER_SESSION_NOT_FOUND);
     return ErrorPage.error(session, Messages.SESSION_NOT_ACTIVE);
   }
   return AuthenticationManager.finishBrowserLogout(
       session, realm, userSession, uriInfo, clientConnection, headers);
 }
예제 #14
0
  /**
   * protocol independent registration page entry point
   *
   * @param code
   * @return
   */
  @Path(REGISTRATION_PATH)
  @GET
  public Response registerPage(
      @QueryParam("code") String code, @QueryParam("execution") String execution) {
    event.event(EventType.REGISTER);
    if (!realm.isRegistrationAllowed()) {
      event.error(Errors.REGISTRATION_DISABLED);
      return ErrorPage.error(session, Messages.REGISTRATION_NOT_ALLOWED);
    }

    Checks checks = new Checks();
    if (!checks.verifyCode(code, ClientSessionModel.Action.AUTHENTICATE.name())) {
      return checks.response;
    }
    event.detail(Details.CODE_ID, code);
    ClientSessionCode clientSessionCode = checks.clientCode;
    ClientSessionModel clientSession = clientSessionCode.getClientSession();

    authManager.expireIdentityCookie(realm, uriInfo, clientConnection);

    return processRegistration(execution, clientSession, null);
  }
예제 #15
0
  /**
   * OAuth grant page. You should not invoked this directly!
   *
   * @param formData
   * @return
   */
  @Path("consent")
  @POST
  @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
  public Response processConsent(final MultivaluedMap<String, String> formData) {
    event.event(EventType.LOGIN).detail(Details.RESPONSE_TYPE, "code");

    if (!checkSsl()) {
      return ErrorPage.error(session, Messages.HTTPS_REQUIRED);
    }

    String code = formData.getFirst("code");

    ClientSessionCode accessCode = ClientSessionCode.parse(code, session, realm);
    if (accessCode == null || !accessCode.isValid(ClientSessionModel.Action.OAUTH_GRANT.name())) {
      event.error(Errors.INVALID_CODE);
      return ErrorPage.error(session, Messages.INVALID_ACCESS_CODE);
    }
    ClientSessionModel clientSession = accessCode.getClientSession();
    event.detail(Details.CODE_ID, clientSession.getId());

    String redirect = clientSession.getRedirectUri();
    UserSessionModel userSession = clientSession.getUserSession();
    UserModel user = userSession.getUser();
    ClientModel client = clientSession.getClient();

    event
        .client(client)
        .user(user)
        .detail(Details.RESPONSE_TYPE, "code")
        .detail(Details.REDIRECT_URI, redirect);

    event.detail(Details.AUTH_METHOD, userSession.getAuthMethod());
    event.detail(Details.USERNAME, userSession.getLoginUsername());
    if (userSession.isRememberMe()) {
      event.detail(Details.REMEMBER_ME, "true");
    }

    if (!AuthenticationManager.isSessionValid(realm, userSession)) {
      AuthenticationManager.backchannelLogout(
          session, realm, userSession, uriInfo, clientConnection, headers, true);
      event.error(Errors.INVALID_CODE);
      return ErrorPage.error(session, Messages.SESSION_NOT_ACTIVE);
    }
    event.session(userSession);

    if (formData.containsKey("cancel")) {
      LoginProtocol protocol =
          session.getProvider(LoginProtocol.class, clientSession.getAuthMethod());
      protocol.setRealm(realm).setHttpHeaders(headers).setUriInfo(uriInfo);
      event.error(Errors.REJECTED_BY_USER);
      return protocol.consentDenied(clientSession);
    }

    UserConsentModel grantedConsent = user.getConsentByClient(client.getId());
    if (grantedConsent == null) {
      grantedConsent = new UserConsentModel(client);
      user.addConsent(grantedConsent);
    }
    for (RoleModel role : accessCode.getRequestedRoles()) {
      grantedConsent.addGrantedRole(role);
    }
    for (ProtocolMapperModel protocolMapper : accessCode.getRequestedProtocolMappers()) {
      if (protocolMapper.isConsentRequired() && protocolMapper.getConsentText() != null) {
        grantedConsent.addGrantedProtocolMapper(protocolMapper);
      }
    }
    user.updateConsent(grantedConsent);

    event.detail(Details.CONSENT, Details.CONSENT_VALUE_CONSENT_GRANTED);
    event.success();

    return authManager.redirectAfterSuccessfulFlow(
        session, realm, userSession, clientSession, request, uriInfo, clientConnection);
  }
예제 #16
0
  /**
   * Update account password
   *
   * <p>Form params:
   *
   * <p>password - old password password-new pasword-confirm
   *
   * @param formData
   * @return
   */
  @Path("password")
  @POST
  @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
  public Response processPasswordUpdate(final MultivaluedMap<String, String> formData) {
    if (auth == null) {
      return login("password");
    }

    require(AccountRoles.MANAGE_ACCOUNT);

    String action = formData.getFirst("submitAction");
    if (action != null && action.equals("Cancel")) {
      setReferrerOnPage();
      return account.createResponse(AccountPages.PASSWORD);
    }

    csrfCheck(formData);
    UserModel user = auth.getUser();

    boolean requireCurrent = isPasswordSet(user);
    account.setPasswordSet(requireCurrent);

    String password = formData.getFirst("password");
    String passwordNew = formData.getFirst("password-new");
    String passwordConfirm = formData.getFirst("password-confirm");

    if (requireCurrent) {
      if (Validation.isBlank(password)) {
        setReferrerOnPage();
        return account.setError(Messages.MISSING_PASSWORD).createResponse(AccountPages.PASSWORD);
      }

      UserCredentialModel cred = UserCredentialModel.password(password);
      if (!session.users().validCredentials(realm, user, cred)) {
        setReferrerOnPage();
        return account
            .setError(Messages.INVALID_PASSWORD_EXISTING)
            .createResponse(AccountPages.PASSWORD);
      }
    }

    if (Validation.isEmpty(passwordNew)) {
      setReferrerOnPage();
      return account.setError(Messages.MISSING_PASSWORD).createResponse(AccountPages.PASSWORD);
    }

    if (!passwordNew.equals(passwordConfirm)) {
      setReferrerOnPage();
      return account
          .setError(Messages.INVALID_PASSWORD_CONFIRM)
          .createResponse(AccountPages.PASSWORD);
    }

    try {
      session.users().updateCredential(realm, user, UserCredentialModel.password(passwordNew));
    } catch (ModelReadOnlyException mre) {
      setReferrerOnPage();
      return account.setError(Messages.READ_ONLY_PASSWORD).createResponse(AccountPages.PASSWORD);
    } catch (ModelException me) {
      logger.error("Failed to update password", me);
      setReferrerOnPage();
      return account
          .setError(me.getMessage(), me.getParameters())
          .createResponse(AccountPages.PASSWORD);
    } catch (Exception ape) {
      logger.error("Failed to update password", ape);
      setReferrerOnPage();
      return account.setError(ape.getMessage()).createResponse(AccountPages.PASSWORD);
    }

    List<UserSessionModel> sessions = session.sessions().getUserSessions(realm, user);
    for (UserSessionModel s : sessions) {
      if (!s.getId().equals(auth.getSession().getId())) {
        AuthenticationManager.backchannelLogout(
            session, realm, s, uriInfo, clientConnection, headers, true);
      }
    }

    event.event(EventType.UPDATE_PASSWORD).client(auth.getClient()).user(auth.getUser()).success();

    setReferrerOnPage();
    return account
        .setPasswordSet(true)
        .setSuccess(Messages.ACCOUNT_PASSWORD_UPDATED)
        .createResponse(AccountPages.PASSWORD);
  }
예제 #17
0
  public TokenValidation validateToken(
      KeycloakSession session,
      UriInfo uriInfo,
      ClientConnection connection,
      RealmModel realm,
      AccessToken oldToken,
      HttpHeaders headers)
      throws OAuthErrorException {
    UserModel user = session.users().getUserById(oldToken.getSubject(), realm);
    if (user == null) {
      throw new OAuthErrorException(
          OAuthErrorException.INVALID_GRANT, "Invalid refresh token", "Unknown user");
    }

    if (!user.isEnabled()) {
      throw new OAuthErrorException(
          OAuthErrorException.INVALID_GRANT, "User disabled", "User disabled");
    }

    UserSessionModel userSession =
        session.sessions().getUserSession(realm, oldToken.getSessionState());
    if (!AuthenticationManager.isSessionValid(realm, userSession)) {
      AuthenticationManager.backchannelLogout(
          session, realm, userSession, uriInfo, connection, headers, true);
      throw new OAuthErrorException(
          OAuthErrorException.INVALID_GRANT, "Session not active", "Session not active");
    }
    ClientSessionModel clientSession = null;
    for (ClientSessionModel clientSessionModel : userSession.getClientSessions()) {
      if (clientSessionModel.getId().equals(oldToken.getClientSession())) {
        clientSession = clientSessionModel;
        break;
      }
    }

    if (clientSession == null) {
      throw new OAuthErrorException(
          OAuthErrorException.INVALID_GRANT,
          "Client session not active",
          "Client session not active");
    }

    ClientModel client = clientSession.getClient();

    if (!client.getClientId().equals(oldToken.getIssuedFor())) {
      throw new OAuthErrorException(
          OAuthErrorException.INVALID_GRANT, "Unmatching clients", "Unmatching clients");
    }

    if (oldToken.getIssuedAt() < client.getNotBefore()) {
      throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Stale token");
    }
    if (oldToken.getIssuedAt() < realm.getNotBefore()) {
      throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Stale token");
    }

    // recreate token.
    Set<RoleModel> requestedRoles = TokenManager.getAccess(null, clientSession.getClient(), user);
    AccessToken newToken =
        createClientAccessToken(
            session, requestedRoles, realm, client, user, userSession, clientSession);
    verifyAccess(oldToken, newToken);

    return new TokenValidation(user, userSession, clientSession, newToken);
  }
예제 #18
0
  public Response processRequireAction(final String code, String action) {
    event.event(EventType.CUSTOM_REQUIRED_ACTION);
    event.detail(Details.CUSTOM_REQUIRED_ACTION, action);
    if (action == null) {
      logger.error("required action query param was null");
      event.error(Errors.INVALID_CODE);
      throw new WebApplicationException(ErrorPage.error(session, Messages.INVALID_CODE));
    }

    RequiredActionFactory factory =
        (RequiredActionFactory)
            session
                .getKeycloakSessionFactory()
                .getProviderFactory(RequiredActionProvider.class, action);
    if (factory == null) {
      logger.error("required action provider was null");
      event.error(Errors.INVALID_CODE);
      throw new WebApplicationException(ErrorPage.error(session, Messages.INVALID_CODE));
    }
    RequiredActionProvider provider = factory.create(session);
    Checks checks = new Checks();
    if (!checks.verifyCode(code, action)) {
      return checks.response;
    }
    final ClientSessionCode clientCode = checks.clientCode;
    final ClientSessionModel clientSession = clientCode.getClientSession();

    if (clientSession.getUserSession() == null) {
      logger.error("user session was null");
      event.error(Errors.USER_SESSION_NOT_FOUND);
      throw new WebApplicationException(ErrorPage.error(session, Messages.SESSION_NOT_ACTIVE));
    }

    initEvent(clientSession);
    event.event(EventType.CUSTOM_REQUIRED_ACTION);

    RequiredActionContextResult context =
        new RequiredActionContextResult(
            clientSession.getUserSession(),
            clientSession,
            realm,
            event,
            session,
            request,
            clientSession.getUserSession().getUser(),
            factory) {
          @Override
          public String generateAccessCode(String action) {
            String clientSessionAction = clientSession.getAction();
            if (action.equals(clientSessionAction)) {
              clientSession.setTimestamp(Time.currentTime());
              return code;
            }
            ClientSessionCode code = new ClientSessionCode(getRealm(), getClientSession());
            code.setAction(action);
            return code.getCode();
          }

          @Override
          public void ignore() {
            throw new RuntimeException("Cannot call ignore within processAction()");
          }
        };
    provider.processAction(context);
    if (context.getStatus() == RequiredActionContext.Status.SUCCESS) {
      event.clone().success();
      // do both
      clientSession.removeRequiredAction(factory.getId());
      clientSession.getUserSession().getUser().removeRequiredAction(factory.getId());
      event.event(EventType.LOGIN);
      return AuthenticationManager.nextActionAfterAuthentication(
          session,
          clientSession.getUserSession(),
          clientSession,
          clientConnection,
          request,
          uriInfo,
          event);
    }
    if (context.getStatus() == RequiredActionContext.Status.CHALLENGE) {
      return context.getChallenge();
    }
    if (context.getStatus() == RequiredActionContext.Status.FAILURE) {
      LoginProtocol protocol =
          context
              .getSession()
              .getProvider(LoginProtocol.class, context.getClientSession().getAuthMethod());
      protocol
          .setRealm(context.getRealm())
          .setHttpHeaders(context.getHttpRequest().getHttpHeaders())
          .setUriInfo(context.getUriInfo());
      event.detail(Details.CUSTOM_REQUIRED_ACTION, action).error(Errors.REJECTED_BY_USER);
      return protocol.consentDenied(context.getClientSession());
    }

    throw new RuntimeException("Unreachable");
  }