self::OAuth2Protocol_ResponseType_Code, self::OAuth2Protocol_ResponseType_Token => self::OAuth2Protocol_ResponseType_Token ); // http://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication const TokenEndpoint_AuthMethod_ClientSecretBasic = 'client_secret_basic'; const TokenEndpoint_AuthMethod_ClientSecretPost = 'client_secret_post'; const TokenEndpoint_AuthMethod_ClientSecretJwt = 'client_secret_jwt'; const TokenEndpoint_AuthMethod_PrivateKeyJwt = 'private_key_jwt'; const TokenEndpoint_AuthMethod_None = 'none'; const OAuth2Protocol_ClientAssertionType = 'client_assertion_type'; const OAuth2Protocol_ClientAssertion = 'client_assertion'; public static $token_endpoint_auth_methods = array ( self::TokenEndpoint_AuthMethod_ClientSecretBasic, self::TokenEndpoint_AuthMethod_ClientSecretPost, self::TokenEndpoint_AuthMethod_ClientSecretJwt, self::TokenEndpoint_AuthMethod_PrivateKeyJwt, // PKCE only self::TokenEndpoint_AuthMethod_None, ); const OpenIdConnect_Scope = 'openid'; const OfflineAccess_Scope = 'offline_access'; public static $supported_signing_algorithms = array ( // MAC SHA2 JSONWebSignatureAndEncryptionAlgorithms::HS256, JSONWebSignatureAndEncryptionAlgorithms::HS384, JSONWebSignatureAndEncryptionAlgorithms::HS512, // RSA JSONWebSignatureAndEncryptionAlgorithms::RS256, JSONWebSignatureAndEncryptionAlgorithms::RS384, JSONWebSignatureAndEncryptionAlgorithms::RS512, JSONWebSignatureAndEncryptionAlgorithms::PS256, JSONWebSignatureAndEncryptionAlgorithms::PS384, JSONWebSignatureAndEncryptionAlgorithms::PS512, JSONWebSignatureAndEncryptionAlgorithms::None ); public static $supported_signing_algorithms_hmac_sha2 = array ( JSONWebSignatureAndEncryptionAlgorithms::HS256, JSONWebSignatureAndEncryptionAlgorithms::HS384, JSONWebSignatureAndEncryptionAlgorithms::HS512, ); public static $supported_signing_algorithms_rsa = array ( JSONWebSignatureAndEncryptionAlgorithms::RS256, JSONWebSignatureAndEncryptionAlgorithms::RS384, JSONWebSignatureAndEncryptionAlgorithms::RS512, JSONWebSignatureAndEncryptionAlgorithms::PS256, JSONWebSignatureAndEncryptionAlgorithms::PS384, JSONWebSignatureAndEncryptionAlgorithms::PS512, ); // https://tools.ietf.org/html/rfc7518#page-12 public static $supported_key_management_algorithms = array ( JSONWebSignatureAndEncryptionAlgorithms::RSA1_5, JSONWebSignatureAndEncryptionAlgorithms::RSA_OAEP, JSONWebSignatureAndEncryptionAlgorithms::RSA_OAEP_256, JSONWebSignatureAndEncryptionAlgorithms::Dir, JSONWebSignatureAndEncryptionAlgorithms::None, ); // https://tools.ietf.org/html/rfc7518#page-22 public static $supported_content_encryption_algorithms = array ( JSONWebSignatureAndEncryptionAlgorithms::A128CBC_HS256, JSONWebSignatureAndEncryptionAlgorithms::A192CBC_HS384, JSONWebSignatureAndEncryptionAlgorithms::A256CBC_HS512, JSONWebSignatureAndEncryptionAlgorithms::None, ); /** * @see http://tools.ietf.org/html/rfc6749#appendix-A * VSCHAR = %x20-7E */ const VsChar = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.-_~'; /** * PKCE * @see https://tools.ietf.org/html/rfc7636 **/ // auth request new params const PKCE_CodeChallenge = 'code_challenge'; const PKCE_CodeChallengeMethod = 'code_challenge_method'; const PKCE_CodeChallengeMethodPlain = 'plain'; const PKCE_CodeChallengeMethodSHA256 = 'S256'; const PKCE_ValidCodeChallengeMethods = [self::PKCE_CodeChallengeMethodPlain, self::PKCE_CodeChallengeMethodSHA256]; // token request new params const PKCE_CodeVerifier = 'code_verifier'; //services /** * @var ILogService */ private $log_service; /** * @var ICheckPointService */ private $checkpoint_service; /** * @var IClientService */ private $client_service; /** * @var IClientRepository */ private $client_repository; /** * @var IAuthService */ private $auth_service; /** * @var IPrincipalService */ private $principal_service; /** * @var ITokenService */ private $token_service; //endpoints /** * @var AuthorizationEndpoint */ private $authorize_endpoint; /** * @var TokenEndpoint */ private $token_endpoint; /** * @var TokenRevocationEndpoint */ private $revoke_endpoint; /** * @var TokenIntrospectionEndpoint */ private $introspection_endpoint; /** * grant types * @var array */ private $grant_types = []; /** * @var IServerPrivateKeyRepository */ private $server_private_keys_repository; /** * @var IOpenIDProviderConfigurationService */ private $oidc_provider_configuration_service; /** * @var IMementoOAuth2SerializerService */ private $memento_service; /** * OAuth2Protocol constructor. * @param ILogService $log_service * @param IClientService $client_service * @param IClientRepository $client_repository * @param ITokenService $token_service * @param IAuthService $auth_service * @param IOAuth2AuthenticationStrategy $auth_strategy * @param ICheckPointService $checkpoint_service * @param IApiScopeService $scope_service * @param IUserConsentService $user_consent_service * @param IServerPrivateKeyRepository $server_private_keys_repository * @param IOpenIDProviderConfigurationService $oidc_provider_configuration_service * @param IMementoOAuth2SerializerService $memento_service * @param ISecurityContextService $security_context_service * @param IPrincipalService $principal_service * @param IServerPrivateKeyRepository $server_private_key_repository * @param IClientJWKSetReader $jwk_set_reader_service * @param UserIPHelperProvider $ip_helper */ public function __construct ( ILogService $log_service, IClientService $client_service, IClientRepository $client_repository, ITokenService $token_service, IAuthService $auth_service, IOAuth2AuthenticationStrategy $auth_strategy, ICheckPointService $checkpoint_service, IApiScopeService $scope_service, IUserConsentService $user_consent_service, IServerPrivateKeyRepository $server_private_keys_repository, IOpenIDProviderConfigurationService $oidc_provider_configuration_service, IMementoOAuth2SerializerService $memento_service, ISecurityContextService $security_context_service, IPrincipalService $principal_service, IServerPrivateKeyRepository $server_private_key_repository, IClientJWKSetReader $jwk_set_reader_service, UserIPHelperProvider $ip_helper ) { $this->server_private_keys_repository = $server_private_keys_repository; $this->oidc_provider_configuration_service = $oidc_provider_configuration_service; $this->memento_service = $memento_service; $authorization_code_grant_type = new AuthorizationCodeGrantType ( $scope_service, $client_service, $client_repository, $token_service, $auth_service, $auth_strategy, $log_service, $user_consent_service, $this->memento_service, $security_context_service, $principal_service, $server_private_key_repository, $jwk_set_reader_service ); $implicit_grant_type = new ImplicitGrantType ( $scope_service, $client_service, $client_repository, $token_service, $auth_service, $auth_strategy, $log_service, $user_consent_service, $this->memento_service, $security_context_service, $principal_service, $server_private_key_repository, $jwk_set_reader_service ); $hybrid_grant_type = new HybridGrantType ( $scope_service, $client_service, $client_repository, $token_service, $auth_service, $auth_strategy, $log_service, $user_consent_service, $this->memento_service, $security_context_service, $principal_service, $server_private_key_repository, $jwk_set_reader_service ); $refresh_bearer_token_grant_type = new RefreshBearerTokenGrantType ( $client_service, $client_repository, $token_service, $log_service ); $client_credential_grant_type = new ClientCredentialsGrantType ( $scope_service, $client_service, $client_repository, $token_service, $log_service ); $this->grant_types[$authorization_code_grant_type->getType()] = $authorization_code_grant_type; $this->grant_types[$implicit_grant_type->getType()] = $implicit_grant_type; $this->grant_types[$refresh_bearer_token_grant_type->getType()] = $refresh_bearer_token_grant_type; $this->grant_types[$client_credential_grant_type->getType()] = $client_credential_grant_type; $this->grant_types[$hybrid_grant_type->getType()] = $hybrid_grant_type; $this->log_service = $log_service; $this->checkpoint_service = $checkpoint_service; $this->client_service = $client_service; $this->client_repository = $client_repository; $this->auth_service = $auth_service; $this->principal_service = $principal_service; $this->token_service = $token_service; $this->authorize_endpoint = new AuthorizationEndpoint($this); $this->token_endpoint = new TokenEndpoint($this); $this->revoke_endpoint = new TokenRevocationEndpoint($this, $client_service, $client_repository, $token_service, $log_service); $this->introspection_endpoint = new TokenIntrospectionEndpoint ( $this, $client_service, $client_repository, $token_service, $auth_service, $log_service, $ip_helper ); } /** * @param OAuth2Request $request * @return OAuth2Response * @throws Exception * @throws UriNotAllowedException */ public function authorize(OAuth2Request $request = null) { try { $this->last_request = $request; if (is_null($this->last_request)) throw new InvalidOAuth2Request; if(!$this->last_request->isValid()) { // then check if we have a memento .... if (!$this->memento_service->exists()) throw new InvalidOAuth2Request($this->last_request->getLastValidationError()); $this->last_request = OAuth2AuthorizationRequestFactory::getInstance()->build ( OAuth2Message::buildFromMemento($this->memento_service->load()) ); if(!$this->last_request->isValid()) throw new InvalidOAuth2Request($this->last_request->getLastValidationError()); } return $this->authorize_endpoint->handle($this->last_request); } catch (UriNotAllowedException $ex1) { $this->log_service->warning($ex1); $this->checkpoint_service->trackException($ex1); throw $ex1; } catch(OAuth2BaseException $ex2) { $this->log_service->warning($ex2); $this->checkpoint_service->trackException($ex2); $redirect_uri = $this->validateRedirectUri($this->last_request); if (is_null($redirect_uri)) throw $ex2; return OAuth2IndirectErrorResponseFactoryMethod::buildResponse ( $this->last_request, $ex2->getError(), $ex2->getMessage(), $redirect_uri ); } catch (AbsentClientException $ex3){ $this->log_service->warning($ex3); $this->checkpoint_service->trackException($ex3); $redirect_uri = $this->validateRedirectUri($this->last_request); if (is_null($redirect_uri)) throw $ex3; return OAuth2IndirectErrorResponseFactoryMethod::buildResponse ( $this->last_request, OAuth2Protocol::OAuth2Protocol_Error_ServerError, $ex3->getMessage(), $redirect_uri ); } catch (AbsentCurrentUserException $ex4){ $this->log_service->warning($ex4); $this->checkpoint_service->trackException($ex4); $redirect_uri = $this->validateRedirectUri($this->last_request); if (is_null($redirect_uri)) throw $ex4; return OAuth2IndirectErrorResponseFactoryMethod::buildResponse ( $this->last_request, OAuth2Protocol::OAuth2Protocol_Error_ServerError, $ex4->getMessage(), $redirect_uri ); } catch (Exception $ex) { $this->log_service->error($ex); $this->checkpoint_service->trackException($ex); $redirect_uri = $this->validateRedirectUri($this->last_request); if (is_null($redirect_uri)) throw $ex; return OAuth2IndirectErrorResponseFactoryMethod::buildResponse ( $this->last_request, OAuth2Protocol::OAuth2Protocol_Error_ServerError, $ex->getMessage(), $redirect_uri ); } } /** * @param OAuth2Request $request * @return OAuth2Response */ public function token(OAuth2Request $request = null) { try { $this->last_request = $request; if (is_null($this->last_request)) throw new InvalidOAuth2Request; if(!$this->last_request->isValid()) throw new InvalidOAuth2Request($this->last_request->getLastValidationError()); return $this->token_endpoint->handle($this->last_request); } catch(OAuth2BaseException $ex1) { $this->log_service->warning($ex1); $this->checkpoint_service->trackException($ex1); return new OAuth2DirectErrorResponse($ex1->getError(), $ex1->getMessage()); } catch (Exception $ex) { $this->log_service->error($ex); $this->checkpoint_service->trackException($ex); return new OAuth2DirectErrorResponse ( OAuth2Protocol::OAuth2Protocol_Error_ServerError, $ex->getMessage() ); } } /** * Revoke Token Endpoint * @see http://tools.ietf.org/html/rfc7009 * @param OAuth2Request $request * @return OAuth2Response */ public function revoke(OAuth2Request $request = null){ try { $this->last_request = $request; if (is_null($this->last_request)) throw new InvalidOAuth2Request; if(!$this->last_request->isValid()) throw new InvalidOAuth2Request($this->last_request->getLastValidationError()); return $this->revoke_endpoint->handle($this->last_request); } catch (Exception $ex) { $this->log_service->error($ex); $this->checkpoint_service->trackException($ex); //simple say "OK" and be on our way ... return new OAuth2TokenRevocationResponse; } } /** * Introspection Token Endpoint * @see http://tools.ietf.org/html/draft-richer-oauth-introspection-04 * @param OAuth2Request $request * @return OAuth2Response */ public function introspection(OAuth2Request $request = null) { try { $this->last_request = $request; if (is_null($this->last_request)) throw new InvalidOAuth2Request; if(!$this->last_request->isValid()) throw new InvalidOAuth2Request($this->last_request->getLastValidationError()); return $this->introspection_endpoint->handle($this->last_request); } catch(ExpiredAccessTokenException $ex1) { $this->log_service->warning($ex1); return new OAuth2DirectErrorResponse($ex1->getError(), $ex1->getMessage()); } catch(OAuth2BaseException $ex2) { $this->log_service->warning($ex2); $this->checkpoint_service->trackException($ex2); return new OAuth2DirectErrorResponse($ex2->getError(), $ex2->getMessage()); } catch (Exception $ex) { $this->log_service->error($ex); $this->checkpoint_service->trackException($ex); return new OAuth2DirectErrorResponse ( OAuth2Protocol::OAuth2Protocol_Error_InvalidRequest, OAuth2Protocol::OAuth2Protocol_Error_InvalidRequest ); } } public function getAvailableGrants() { return $this->grant_types; } /** * @param IClient $client * @return bool */ static public function isClientAllowedToUseTokenEndpointAuth(IClient $client) { return $client->getClientType() === IClient::ClientType_Confidential || $client->getApplicationType() === IClient::ApplicationType_Native; } static public function getTokenEndpointAuthMethodsPerClientType(IClient $client) { if($client->getClientType() == IClient::ClientType_Public) { return ArrayUtils::convert2Assoc ( array ( self::TokenEndpoint_AuthMethod_PrivateKeyJwt, self::TokenEndpoint_AuthMethod_None ) ); } return ArrayUtils::convert2Assoc ( array_merge ( self::$token_endpoint_auth_methods, array ( self::TokenEndpoint_AuthMethod_None ) ) ); } /** * @param IClient $client * @return array */ static public function getSigningAlgorithmsPerClientType(IClient $client) { if($client->getClientType() == IClient::ClientType_Public) { return ArrayUtils::convert2Assoc ( array_merge ( self::$supported_signing_algorithms_rsa, array ( JSONWebSignatureAndEncryptionAlgorithms::None ) ) ); } return ArrayUtils::convert2Assoc ( array_merge ( self::$supported_signing_algorithms_hmac_sha2, self::$supported_signing_algorithms_rsa, array ( JSONWebSignatureAndEncryptionAlgorithms::None ) ) ); } /** * @param IClient $client * @return array */ static public function getKeyManagementAlgorithmsPerClientType(IClient $client) { if($client->getClientType() == IClient::ClientType_Public) { return ArrayUtils::convert2Assoc ( array_diff ( self::$supported_key_management_algorithms, array ( JSONWebSignatureAndEncryptionAlgorithms::Dir ) ) ); } return ArrayUtils::convert2Assoc ( self::$supported_key_management_algorithms ); } /** * @return string */ public function getJWKSDocument() { $keys = $this->server_private_keys_repository->getActives(); $set = []; foreach($keys as $private_key) { $jwk = RSAJWKFactory::build ( new RSAJWKPEMPrivateKeySpecification ( $private_key->getPEM(), $private_key->getPassword() ) ); $jwk->setVisibility(JSONWebKeyVisibility::PublicOnly); $jwk ->setId($private_key->getKeyId()) ->setKeyUse($private_key->getUse()) ->setType($private_key->getType()) ->setAlgorithm($private_key->getAlg()->getName()); array_push($set, $jwk); } $jkws = new JWKSet($set); return $jkws->toJson(); } /** * @see http://openid.net/specs/openid-connect-discovery-1_0.html * @return string */ public function getDiscoveryDocument() { $builder = new DiscoveryDocumentBuilder(); return $builder ->setIssuer($this->oidc_provider_configuration_service->getIssuerUrl()) ->setAuthEndpoint($this->oidc_provider_configuration_service->getAuthEndpoint()) ->setTokenEndpoint($this->oidc_provider_configuration_service->getTokenEndpoint()) ->setUserInfoEndpoint($this->oidc_provider_configuration_service->getUserInfoEndpoint()) ->setJWKSUrl($this->oidc_provider_configuration_service->getJWKSUrl()) ->setRevocationEndpoint($this->oidc_provider_configuration_service->getRevocationEndpoint()) ->setIntrospectionEndpoint($this->oidc_provider_configuration_service->getIntrospectionEndpoint()) // session management http://openid.net/specs/openid-connect-session-1_0.html ->setEndSessionEndpoint($this->oidc_provider_configuration_service->getEndSessionEndpoint()) ->setCheckSessionIframe($this->oidc_provider_configuration_service->getCheckSessionIFrame()) // response types ->addResponseTypeSupported('code') ->addResponseTypeSupported('token') ->addResponseTypeSupported('code token') ->addResponseTypeSupported('token id_token') ->addResponseTypeSupported('code token id_token') // claims ->addClaimSupported('aud') ->addClaimSupported('exp') ->addClaimSupported('iat') ->addClaimSupported('iss') ->addClaimSupported('sub') ->addClaimSupported(StandardClaims::Email) ->addClaimSupported(StandardClaims::EmailVerified) ->addClaimSupported(StandardClaims::Name) ->addClaimSupported(StandardClaims::GivenName) ->addClaimSupported(StandardClaims::FamilyName) ->addClaimSupported(StandardClaims::NickName) ->addClaimSupported(StandardClaims::Picture) ->addClaimSupported(StandardClaims::Birthdate) ->addClaimSupported(StandardClaims::Locale) ->addClaimSupported(StandardClaims::Gender) ->addClaimSupported(StandardClaims::Address) // scopes ->addScopeSupported(self::OpenIdConnect_Scope) ->addScopeSupported(IUserService::UserProfileScope_Address) ->addScopeSupported(IUserService::UserProfileScope_Email) ->addScopeSupported(IUserService::UserProfileScope_Profile) // id token signing alg ->addIdTokenSigningAlgSupported(JSONWebSignatureAndEncryptionAlgorithms::HS256) ->addIdTokenSigningAlgSupported(JSONWebSignatureAndEncryptionAlgorithms::HS384) ->addIdTokenSigningAlgSupported(JSONWebSignatureAndEncryptionAlgorithms::HS512) ->addIdTokenSigningAlgSupported(JSONWebSignatureAndEncryptionAlgorithms::RS256) ->addIdTokenSigningAlgSupported(JSONWebSignatureAndEncryptionAlgorithms::RS384) ->addIdTokenSigningAlgSupported(JSONWebSignatureAndEncryptionAlgorithms::RS512) ->addIdTokenSigningAlgSupported(JSONWebSignatureAndEncryptionAlgorithms::PS256) ->addIdTokenSigningAlgSupported(JSONWebSignatureAndEncryptionAlgorithms::PS384) ->addIdTokenSigningAlgSupported(JSONWebSignatureAndEncryptionAlgorithms::PS512) // id token enc alg ->addIdTokenEncryptionAlgSupported(JSONWebSignatureAndEncryptionAlgorithms::RSA1_5) ->addIdTokenEncryptionAlgSupported(JSONWebSignatureAndEncryptionAlgorithms::RSA_OAEP) ->addIdTokenEncryptionAlgSupported(JSONWebSignatureAndEncryptionAlgorithms::RSA_OAEP_256) ->addIdTokenEncryptionAlgSupported(JSONWebSignatureAndEncryptionAlgorithms::Dir) // id token enc enc ->addIdTokenEncryptionEncSupported(JSONWebSignatureAndEncryptionAlgorithms::A128CBC_HS256) ->addIdTokenEncryptionEncSupported(JSONWebSignatureAndEncryptionAlgorithms::A192CBC_HS384) ->addIdTokenEncryptionEncSupported(JSONWebSignatureAndEncryptionAlgorithms::A256CBC_HS512) // user info signing alg ->addUserInfoSigningAlgSupported(JSONWebSignatureAndEncryptionAlgorithms::HS256) ->addUserInfoSigningAlgSupported(JSONWebSignatureAndEncryptionAlgorithms::HS384) ->addUserInfoSigningAlgSupported(JSONWebSignatureAndEncryptionAlgorithms::HS512) ->addUserInfoSigningAlgSupported(JSONWebSignatureAndEncryptionAlgorithms::RS256) ->addUserInfoSigningAlgSupported(JSONWebSignatureAndEncryptionAlgorithms::RS384) ->addUserInfoSigningAlgSupported(JSONWebSignatureAndEncryptionAlgorithms::RS512) ->addUserInfoSigningAlgSupported(JSONWebSignatureAndEncryptionAlgorithms::PS256) ->addUserInfoSigningAlgSupported(JSONWebSignatureAndEncryptionAlgorithms::PS384) ->addUserInfoSigningAlgSupported(JSONWebSignatureAndEncryptionAlgorithms::PS512) // user info enc alg ->addUserInfoEncryptionAlgSupported(JSONWebSignatureAndEncryptionAlgorithms::RSA1_5) ->addUserInfoEncryptionAlgSupported(JSONWebSignatureAndEncryptionAlgorithms::RSA_OAEP) ->addUserInfoEncryptionAlgSupported(JSONWebSignatureAndEncryptionAlgorithms::RSA_OAEP_256) ->addUserInfoEncryptionAlgSupported(JSONWebSignatureAndEncryptionAlgorithms::Dir) // user info enc enc ->addUserInfoEncryptionAlgSupported(JSONWebSignatureAndEncryptionAlgorithms::A128CBC_HS256) ->addUserInfoEncryptionAlgSupported(JSONWebSignatureAndEncryptionAlgorithms::A192CBC_HS384) ->addUserInfoEncryptionAlgSupported(JSONWebSignatureAndEncryptionAlgorithms::A256CBC_HS512) ->addSubjectTypeSupported(IClient::SubjectType_Public) ->addSubjectTypeSupported(IClient::SubjectType_Pairwise) ->addTokenEndpointAuthMethodSupported(self::TokenEndpoint_AuthMethod_ClientSecretBasic) ->addTokenEndpointAuthMethodSupported(self::TokenEndpoint_AuthMethod_ClientSecretPost) ->addTokenEndpointAuthMethodSupported(self::TokenEndpoint_AuthMethod_PrivateKeyJwt) ->addTokenEndpointAuthMethodSupported(self::TokenEndpoint_AuthMethod_ClientSecretJwt) ->addResponseModeSupported(self::OAuth2Protocol_ResponseMode_FormPost) ->addResponseModeSupported(self::OAuth2Protocol_ResponseMode_Query) ->addResponseModeSupported(self::OAuth2Protocol_ResponseMode_Fragment) ->addDisplayValueSupported(self::OAuth2Protocol_Display_Page) ->addDisplayValueSupported(self::OAuth2Protocol_Display_PopUp) ->addDisplayValueSupported(self::OAuth2Protocol_Display_Touch) ->addDisplayValueSupported(self::OAuth2Protocol_Display_Wap) ->addDisplayValueSupported(self::OAuth2Protocol_Display_Native) ->addAvailableThirdPartyIdentityProviders() ->render(); } /** * @see http://openid.net/specs/openid-connect-session-1_0.html#RPLogout * @param OAuth2Request $request * @return OAuth2Response */ public function endSession(OAuth2Request $request = null) { try { $this->log_service->debug_msg("OAuth2Protocol::endSession"); $this->last_request = $request; if (is_null($this->last_request)) { $this->log_service->debug_msg("OAuth2Protocol::endSession last request is null"); throw new InvalidOAuth2Request; } if(!$this->last_request->isValid()) { $this->log_service->debug_msg(sprintf("OAuth2Protocol::endSession last request is invalid error %s", $this->last_request->getLastValidationError())); throw new InvalidOAuth2Request($this->last_request->getLastValidationError()); } if(!$this->last_request instanceof OAuth2LogoutRequest) throw new InvalidOAuth2Request; $id_token_hint = $this->last_request->getIdTokenHint(); $client_id = null; $user_id = null; $user = null; if(!empty($id_token_hint)){ $jwt = BasicJWTFactory::build($id_token_hint); if((!$jwt instanceof IJWT)) { $this->log_service->debug_msg("OAuth2Protocol::endSession invalid id_token_hint!"); throw new InvalidOAuth2Request('invalid id_token_hint!'); } $client_id = $jwt->getClaimSet()->getAudience()->getString(); $user_id = $jwt->getClaimSet()->getSubject(); } if(empty($client_id)){ $client_id = $this->last_request->getClientId(); } if(is_null($client_id)) { $this->log_service->debug_msg("OAuth2Protocol::endSession client_id can not be inferred."); throw new InvalidClientException('client_id can not be inferred.'); } $client = $this->client_repository->getClientById($client_id); if(is_null($client)){ $this->log_service->debug_msg("OAuth2Protocol::endSession client not found!"); throw new InvalidClientException('Client not found!'); } $redirect_logout_uri = $this->last_request->getPostLogoutRedirectUri(); $state = $this->last_request->getState(); if(!empty($redirect_logout_uri) && !$client->isPostLogoutUriAllowed($redirect_logout_uri)) { $this->log_service->debug_msg("OAuth2Protocol::endSession post_logout_redirect_uri not allowed!"); throw new InvalidOAuth2Request('post_logout_redirect_uri not allowed!'); } if(!is_null($user_id)){ // try to get the user from id token ( if its set ) $user_id = $this->auth_service->unwrapUserId(intval($user_id->getString())); $user = $this->auth_service->getUserById($user_id); if(is_null($user)){ $this->log_service->debug_msg("OAuth2Protocol::endSession user not found!"); throw new InvalidOAuth2Request('user not found!'); } } $logged_user = $this->auth_service->getCurrentUser(); if(!is_null($logged_user) && !is_null($user) && $logged_user->getId() !== $user->getId()) { Log::warning(sprintf("OAuth2Protocol::endSession user does not match with current session! logged user id %s - user id %s", $logged_user->getId(), $user->getId())); } if(!is_null($logged_user)) $this->auth_service->logout(); if(!empty($redirect_logout_uri)) { return new OAuth2LogoutResponse($redirect_logout_uri, $state); } return null; } catch (UriNotAllowedException $ex1) { $this->log_service->warning($ex1); $this->checkpoint_service->trackException($ex1); return new OAuth2DirectErrorResponse(OAuth2Protocol::OAuth2Protocol_Error_UnauthorizedClient); } catch(OAuth2BaseException $ex2) { $this->log_service->warning($ex2); $this->checkpoint_service->trackException($ex2); return new OAuth2DirectErrorResponse($ex2->getError(), $ex2->getMessage()); } catch (Exception $ex) { $this->log_service->error($ex); $this->checkpoint_service->trackException($ex); return new OAuth2DirectErrorResponse ( OAuth2Protocol::OAuth2Protocol_Error_ServerError, $ex->getMessage() ); } } /** * @return OAuth2Request */ public function getLastRequest() { return $this->last_request; } /** * @param OAuth2Request|null $request * @return null */ private function validateRedirectUri(OAuth2Request $request = null) { if (is_null($request)) return null; $redirect_uri = $request->getRedirectUri(); if (is_null($redirect_uri)) return null; $client_id = $request->getClientId(); if (is_null($client_id)) return null; $client = $this->client_repository->getClientById($client_id); if (is_null($client)) return null; if (!$client->isUriAllowed($redirect_uri)) return null; return $redirect_uri; } }