예제 #1
0
 boolean verifyCode(String code, String requiredAction) {
   if (!verifyCode(code)) {
     return false;
   } else if (!clientCode.isValidAction(requiredAction)) {
     event.client(clientCode.getClientSession().getClient());
     event.error(Errors.INVALID_CODE);
     response = ErrorPage.error(session, Messages.INVALID_CODE);
     return false;
   } else if (!clientCode.isActionActive(requiredAction)) {
     event.client(clientCode.getClientSession().getClient());
     event.clone().error(Errors.EXPIRED_CODE);
     if (clientCode
         .getClientSession()
         .getAction()
         .equals(ClientSessionModel.Action.AUTHENTICATE.name())) {
       AuthenticationProcessor.resetFlow(clientCode.getClientSession());
       response =
           processAuthentication(null, clientCode.getClientSession(), Messages.LOGIN_TIMEOUT);
       return false;
     }
     response = ErrorPage.error(session, Messages.EXPIRED_CODE);
     return false;
   } else {
     return true;
   }
 }
예제 #2
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);
    }
  }
예제 #3
0
    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.");
    }
예제 #4
0
  protected Response resetCredentials(String code, String execution) {
    event.event(EventType.RESET_PASSWORD);
    Checks checks = new Checks();
    if (!checks.verifyCode(code, ClientSessionModel.Action.AUTHENTICATE.name())) {
      return checks.response;
    }
    final ClientSessionCode clientCode = checks.clientCode;
    final ClientSessionModel clientSession = clientCode.getClientSession();

    if (!realm.isResetPasswordAllowed()) {
      event.client(clientCode.getClientSession().getClient());
      event.error(Errors.NOT_ALLOWED);
      return ErrorPage.error(session, Messages.RESET_CREDENTIAL_NOT_ALLOWED);
    }

    return processResetCredentials(execution, clientSession, null);
  }
예제 #5
0
  /**
   * URL called after login page. YOU SHOULD NEVER INVOKE THIS DIRECTLY!
   *
   * @param code
   * @return
   */
  @Path(AUTHENTICATE_PATH)
  @POST
  public Response authenticateForm(
      @QueryParam("code") String code, @QueryParam("execution") String execution) {
    event.event(EventType.LOGIN);
    Checks checks = new Checks();
    if (!checks.verifyCode(code, ClientSessionModel.Action.AUTHENTICATE.name())) {
      return checks.response;
    }
    final ClientSessionCode clientCode = checks.clientCode;
    final ClientSessionModel clientSession = clientCode.getClientSession();

    return processAuthentication(execution, clientSession, null);
  }
예제 #6
0
  @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();
  }
예제 #7
0
  /**
   * Registration
   *
   * @param code
   * @return
   */
  @Path(REGISTRATION_PATH)
  @POST
  public Response processRegister(
      @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;
    }

    ClientSessionCode clientCode = checks.clientCode;
    ClientSessionModel clientSession = clientCode.getClientSession();

    return processRegistration(execution, clientSession, null);
  }
예제 #8
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);
  }
예제 #9
0
  @Override
  public Response authenticated(UserSessionModel userSession, ClientSessionCode accessCode) {
    ClientSessionModel clientSession = accessCode.getClientSession();
    ClientModel client = clientSession.getClient();
    SamlClient samlClient = new SamlClient(client);
    String requestID = clientSession.getNote(SAML_REQUEST_ID);
    String relayState = clientSession.getNote(GeneralConstants.RELAY_STATE);
    String redirectUri = clientSession.getRedirectUri();
    String responseIssuer = getResponseIssuer(realm);
    String nameIdFormat = getNameIdFormat(samlClient, clientSession);
    String nameId = getNameId(nameIdFormat, clientSession, userSession);

    // save NAME_ID and format in clientSession as they may be persistent or transient or email and
    // not username
    // we'll need to send this back on a logout
    clientSession.setNote(SAML_NAME_ID, nameId);
    clientSession.setNote(SAML_NAME_ID_FORMAT, nameIdFormat);

    SAML2LoginResponseBuilder builder = new SAML2LoginResponseBuilder();
    builder
        .requestID(requestID)
        .destination(redirectUri)
        .issuer(responseIssuer)
        .assertionExpiration(realm.getAccessCodeLifespan())
        .subjectExpiration(realm.getAccessTokenLifespan())
        .sessionIndex(clientSession.getId())
        .requestIssuer(clientSession.getClient().getClientId())
        .nameIdentifier(nameIdFormat, nameId)
        .authMethod(JBossSAMLURIConstants.AC_UNSPECIFIED.get());
    if (!samlClient.includeAuthnStatement()) {
      builder.disableAuthnStatement(true);
    }

    List<ProtocolMapperProcessor<SAMLAttributeStatementMapper>> attributeStatementMappers =
        new LinkedList<>();
    List<ProtocolMapperProcessor<SAMLLoginResponseMapper>> loginResponseMappers =
        new LinkedList<>();
    ProtocolMapperProcessor<SAMLRoleListMapper> roleListMapper = null;

    Set<ProtocolMapperModel> mappings = accessCode.getRequestedProtocolMappers();
    for (ProtocolMapperModel mapping : mappings) {

      ProtocolMapper mapper =
          (ProtocolMapper)
              session
                  .getKeycloakSessionFactory()
                  .getProviderFactory(ProtocolMapper.class, mapping.getProtocolMapper());
      if (mapper == null) continue;
      if (mapper instanceof SAMLAttributeStatementMapper) {
        attributeStatementMappers.add(
            new ProtocolMapperProcessor<SAMLAttributeStatementMapper>(
                (SAMLAttributeStatementMapper) mapper, mapping));
      }
      if (mapper instanceof SAMLLoginResponseMapper) {
        loginResponseMappers.add(
            new ProtocolMapperProcessor<SAMLLoginResponseMapper>(
                (SAMLLoginResponseMapper) mapper, mapping));
      }
      if (mapper instanceof SAMLRoleListMapper) {
        roleListMapper =
            new ProtocolMapperProcessor<SAMLRoleListMapper>((SAMLRoleListMapper) mapper, mapping);
      }
    }

    Document samlDocument = null;
    try {
      ResponseType samlModel = builder.buildModel();
      final AttributeStatementType attributeStatement =
          populateAttributeStatements(
              attributeStatementMappers, session, userSession, clientSession);
      populateRoles(roleListMapper, session, userSession, clientSession, attributeStatement);

      // SAML Spec 2.7.3 AttributeStatement must contain one or more Attribute or EncryptedAttribute
      if (attributeStatement.getAttributes().size() > 0) {
        AssertionType assertion = samlModel.getAssertions().get(0).getAssertion();
        assertion.addStatement(attributeStatement);
      }

      samlModel =
          transformLoginResponse(
              loginResponseMappers, samlModel, session, userSession, clientSession);
      samlDocument = builder.buildDocument(samlModel);
    } catch (Exception e) {
      logger.error("failed", e);
      return ErrorPage.error(session, Messages.FAILED_TO_PROCESS_RESPONSE);
    }

    JaxrsSAML2BindingBuilder bindingBuilder = new JaxrsSAML2BindingBuilder();
    bindingBuilder.relayState(relayState);

    KeyManager keyManager = session.keys();
    KeyManager.ActiveKey keys = keyManager.getActiveKey(realm);

    if (samlClient.requiresRealmSignature()) {
      String canonicalization = samlClient.getCanonicalizationMethod();
      if (canonicalization != null) {
        bindingBuilder.canonicalizationMethod(canonicalization);
      }
      bindingBuilder
          .signatureAlgorithm(samlClient.getSignatureAlgorithm())
          .signWith(keys.getPrivateKey(), keys.getPublicKey(), keys.getCertificate())
          .signDocument();
    }
    if (samlClient.requiresAssertionSignature()) {
      String canonicalization = samlClient.getCanonicalizationMethod();
      if (canonicalization != null) {
        bindingBuilder.canonicalizationMethod(canonicalization);
      }
      bindingBuilder
          .signatureAlgorithm(samlClient.getSignatureAlgorithm())
          .signWith(keys.getPrivateKey(), keys.getPublicKey(), keys.getCertificate())
          .signAssertions();
    }
    if (samlClient.requiresEncryption()) {
      PublicKey publicKey = null;
      try {
        publicKey = SamlProtocolUtils.getEncryptionValidationKey(client);
      } catch (Exception e) {
        logger.error("failed", e);
        return ErrorPage.error(session, Messages.FAILED_TO_PROCESS_RESPONSE);
      }
      bindingBuilder.encrypt(publicKey);
    }
    try {
      return buildAuthenticatedResponse(clientSession, redirectUri, samlDocument, bindingBuilder);
    } catch (Exception e) {
      logger.error("failed", e);
      return ErrorPage.error(session, Messages.FAILED_TO_PROCESS_RESPONSE);
    }
  }
예제 #10
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");
  }
예제 #11
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);
  }
예제 #12
0
 public boolean verifyCode(String code) {
   if (!checkSsl()) {
     event.error(Errors.SSL_REQUIRED);
     response = ErrorPage.error(session, Messages.HTTPS_REQUIRED);
     return false;
   }
   if (!realm.isEnabled()) {
     event.error(Errors.REALM_DISABLED);
     response = ErrorPage.error(session, Messages.REALM_NOT_ENABLED);
     return false;
   }
   ClientSessionCode.ParseResult result = ClientSessionCode.parseResult(code, session, realm);
   clientCode = result.getCode();
   if (clientCode == null) {
     if (result.isClientSessionNotFound()) { // timeout
       try {
         ClientSessionModel clientSession =
             RestartLoginCookie.restartSession(session, realm, code);
         if (clientSession != null) {
           event
               .clone()
               .detail(Details.RESTART_AFTER_TIMEOUT, "true")
               .error(Errors.EXPIRED_CODE);
           response =
               processFlow(
                   null,
                   clientSession,
                   AUTHENTICATE_PATH,
                   realm.getBrowserFlow(),
                   Messages.LOGIN_TIMEOUT);
           return false;
         }
       } catch (Exception e) {
         logger.error("failed to parse RestartLoginCookie", e);
       }
     }
     event.error(Errors.INVALID_CODE);
     response = ErrorPage.error(session, Messages.INVALID_CODE);
     return false;
   }
   ClientSessionModel clientSession = clientCode.getClientSession();
   if (clientSession == null) {
     event.error(Errors.INVALID_CODE);
     response = ErrorPage.error(session, Messages.INVALID_CODE);
     return false;
   }
   event.detail(Details.CODE_ID, clientSession.getId());
   ClientModel client = clientSession.getClient();
   if (client == null) {
     event.error(Errors.CLIENT_NOT_FOUND);
     response = ErrorPage.error(session, Messages.UNKNOWN_LOGIN_REQUESTER);
     session.sessions().removeClientSession(realm, clientSession);
     return false;
   }
   if (!client.isEnabled()) {
     event.error(Errors.CLIENT_NOT_FOUND);
     response = ErrorPage.error(session, Messages.LOGIN_REQUESTER_NOT_ENABLED);
     session.sessions().removeClientSession(realm, clientSession);
     return false;
   }
   session.getContext().setClient(client);
   return true;
 }