/** * {@inheritdoc} */ public function validateAuthorization(\Phalcon\Http\RequestInterface $request) { if (!$request->getHeader('authorization')) { throw OAuthServerException::accessDenied('Missing "Authorization" header'); } $header = $request->getHeader('authorization'); $jwt = trim(preg_replace('/^(?:\\s+)?Bearer\\s/', '', $header)); try { // Attempt to parse and validate the JWT $token = (new Parser())->parse($jwt); if ($token->verify(new Sha256(), $this->publicKey->getKeyPath()) === false) { throw OAuthServerException::accessDenied('Access token could not be verified'); } // Ensure access token hasn't expired $data = new ValidationData(); $data->setCurrentTime(time()); if ($token->validate($data) === false) { throw OAuthServerException::accessDenied('Access token is invalid'); } // Check if token has been revoked if ($this->accessTokenRepository->isAccessTokenRevoked($token->getClaim('jti'))) { throw OAuthServerException::accessDenied('Access token has been revoked'); } // Return the response with additional attributes $response = ['oauth_access_token_id' => $token->getClaim('jti'), 'oauth_client_id' => $token->getClaim('aud'), 'oauth_user_id' => $token->getClaim('sub'), 'oauth_scopes' => $token->getClaim('scopes')]; return $response; } catch (\InvalidArgumentException $exception) { // JWT couldn't be parsed so return the request as is throw OAuthServerException::accessDenied($exception->getMessage()); } }
/** * check if client have right scopes to access the route. * * @param $neededScopes * @param $requestedScopes * * @throws OAuthServerException * * @return bool */ protected function validateScopes($neededScopes, $requestedScopes) { if (empty($neededScopes)) { return true; } foreach ($requestedScopes as $requestedScope) { if (in_array($requestedScope->getIdentifier(), $neededScopes)) { return true; } } throw OAuthServerException::accessDenied('you need right scope to access this resource'); }
/** * @param \Psr\Http\Message\ServerRequestInterface $request * @param \League\OAuth2\Server\Entities\ClientEntityInterface $client * * @throws \League\OAuth2\Server\Exception\OAuthServerException * * @return \League\OAuth2\Server\Entities\UserEntityInterface */ protected function validateUser(ServerRequestInterface $request, ClientEntityInterface $client) { $username = $this->getRequestParameter('username', $request); if (is_null($username)) { throw OAuthServerException::invalidRequest('username'); } $password = $this->getRequestParameter('password', $request); if (is_null($password)) { throw OAuthServerException::invalidRequest('password'); } $user = $this->userRepository->getUserEntityByUserCredentials($username, $password, $this->getIdentifier(), $client); if (!$user instanceof UserEntityInterface) { $this->getEmitter()->emit(new RequestEvent(RequestEvent::USER_AUTHENTICATION_FAILED, $request)); throw OAuthServerException::invalidCredentials(); } return $user; }
/** * @param \lcon\Http\RequestInterface $request * @param \League\OAuth2\Server\Entities\ClientEntityInterface $client * * @throws \League\OAuth2\Server\Exception\OAuthServerException * * @return \League\OAuth2\Server\Entities\UserEntityInterface **/ protected function validateUser(\Phalcon\Http\RequestInterface $request, ClientEntityInterface $client) { $username = $this->getRequestParameter('username', $request); if (is_null($username)) { throw OAuthServerException::invalidRequest('username', '`%s` parameter is missing'); } $password = $this->getRequestParameter('password', $request); if (is_null($password)) { throw OAuthServerException::invalidRequest('password', '`%s` parameter is missing'); } $user = $this->userRepository->getUserEntityByUserCredentials($username, $password, $this->getIdentifier(), $client); if (!$user instanceof UserEntityInterface) { $this->getEmitter()->emit(new RequestEvent('user.authentication.failed', $request)); throw OAuthServerException::invalidCredentials(); } return $user; }
/** * {@inheritdoc} */ public function respondToAccessTokenRequest(ServerRequestInterface $request, ResponseTypeInterface $responseType, DateInterval $accessTokenTTL) { // Validate request $client = $this->validateClient($request); $scopes = $this->validateScopes($this->getRequestParameter('scope', $request)); // Finalize the requested scopes $scopes = $this->scopeRepository->finalizeScopes($scopes, $this->getIdentifier(), $client); $userIdentifier = $this->getRequestParameter('user_id', $request); if (is_null($userIdentifier)) { throw OAuthServerException::invalidRequest('user_id'); } $this->tokenName = $this->getRequestParameter('token_name', $request); if (is_null($this->tokenName)) { throw OAuthServerException::invalidRequest('token_name'); } // Issue and persist access token $accessToken = $this->issueAccessToken(new DateInterval('P5Y'), $client, $userIdentifier, $scopes); // Inject access token into response type $responseType->setAccessToken($accessToken); return $responseType; }
public function getUserEntityByUserCredentials($username, $password, $grantType, ClientEntityInterface $clientEntity) { $builder = (new Builder())->columns(['User.id', 'User.username', 'User.password'])->addFrom(\Ivyhjk\OAuth2\Server\Adapter\Phalcon\Model\User::class, 'User')->where('User.username = :username:'******'username'))->limit(1); if ($this->getConfig()->limit_users_to_clients === true) { $builder->innerJoin(\Ivyhjk\OAuth2\Server\Adapter\Phalcon\Model\UserClient::class, 'UserClient.user_id = User.id', 'UserClient')->innerJoin(\Ivyhjk\OAuth2\Server\Adapter\Phalcon\Model\Client::class, 'Client.id = UserClient.client_id', 'Client')->andWhere('Client.id = :client_id:', ['client_id' => $clientEntity->getIdentifier()]); } if ($this->getConfig()->limit_users_to_grants === true) { $builder->innerJoin(\Ivyhjk\OAuth2\Server\Adapter\Phalcon\Model\UserGrant::class, 'UserGrant.user_id = User.id', 'UserGrant')->innerJoin(\Ivyhjk\OAuth2\Server\Adapter\Phalcon\Model\Grant::class, 'Grant.id = UserGrant.grant_id', 'Grant')->andWhere('Grant.id = :grantType:', compact('grantType')); } $query = $builder->getQuery(); $result = $query->getSingleResult(); if (!$result) { throw OAuthServerException::invalidCredentials(); } $security = new Security(); if ($security->checkHash($password, $result->password) !== true) { throw OAuthServerException::invalidCredentials(); } $user = new UserEntity(); $user->setIdentifier($result->id); return $user; }
/** * Get a client. * * @param string $clientIdentifier The client's identifier * @param string $grantType The grant type used * @param string $clientSecret The client's secret (if sent) * * @return \League\OAuth2\Server\Entities\Interfaces\ClientEntityInterface **/ public function getClientEntity($clientIdentifier, $grantType, $clientSecret = null, $mustValidateSecret = true) { $builder = (new Builder())->columns(['Client.id', 'Client.secret', 'Client.name'])->addFrom(\Ivyhjk\OAuth2\Server\Adapter\Phalcon\Model\Client::class, 'Client')->where('Client.id = :clientIdentifier:', compact('clientIdentifier'))->limit(1); if ($mustValidateSecret === true) { $builder->andWhere('Client.secret = :clientSecret:', compact('clientSecret')); } // if ($this->getConfig()->limit_clients_to_grants === true) { $builder->innerJoin(\Ivyhjk\OAuth2\Server\Adapter\Phalcon\Model\ClientGrant::class, 'ClientGrant.client_id = Client.id', 'ClientGrant')->innerJoin(\Ivyhjk\OAuth2\Server\Adapter\Phalcon\Model\Grant::class, 'Grant.id = ClientGrant.grant_id', 'Grant')->andWhere('Grant.id = :grantType:', compact('grantType')); } $query = $builder->getQuery(); $result = $query->getSingleResult(); if (!$result) { throw OAuthServerException::invalidClient(); } // Get one endpoint? // $builder = $this->getDatabase()->createBuilder(); // // $builder // ->columns([ // 'ClientEndpoint.redirect_uri' // ]) // ->addFrom(\Ivyhjk\OAuth2\Server\Adapter\Phalcon\Model\ClientEndpoint::class, 'ClientEndpoint') // ->where('ClientEndpoint.client_id = :client_id:', [ // 'client_id' => $result->id // ]) // ->limit(1); // // $endpoint = $builder->getQuery()->getSingleResult(); $client = new ClientEntity(); $client->setName($result->name); $client->setIdentifier($result->id); // if ($endpoint) { // $client->setRedirectUri($endpoint->redirect_uri); // } return $client; }
/** * {@inheritdoc} */ public function validateAuthorization(ServerRequestInterface $request) { if ($request->hasHeader('authorization') === false) { throw OAuthServerException::accessDenied('Missing "Authorization" header'); } $header = $request->getHeader('authorization'); $accessTokenId = trim($header[0]); try { $accessTokenEntity = $this->accessTokenRepository->findAccessToken($accessTokenId); // Check if token has been revoked if (is_null($accessTokenEntity)) { throw OAuthServerException::accessDenied('Access token has been revoked'); } // Ensure access token hasn't expired if ($accessTokenEntity->getExpiryDateTime()->lt(Carbon::now())) { throw OAuthServerException::accessDenied('Access token is invalid'); } // Return the request with additional attributes return $request->withAttribute('oauth_access_token_id', $accessTokenEntity->getIdentifier())->withAttribute('oauth_client_id', $accessTokenEntity->getClient()->getIdentifier())->withAttribute('oauth_user_id', $accessTokenEntity->getUserIdentifier())->withAttribute('oauth_scopes', $accessTokenEntity->getScopes()); } catch (\InvalidArgumentException $exception) { // JWT couldn't be parsed so return the request as is throw OAuthServerException::accessDenied($exception->getMessage()); } }
/** * Return an access token response. * * @param ServerRequestInterface $request * @param ResponseInterface $response * * @throws OAuthServerException * * @return ResponseInterface */ public function respondToAccessTokenRequest(ServerRequestInterface $request, ResponseInterface $response) { foreach ($this->enabledGrantTypes as $grantType) { if ($grantType->canRespondToAccessTokenRequest($request)) { $tokenResponse = $grantType->respondToAccessTokenRequest($request, $this->getResponseType(), $this->grantTypeAccessTokenTTL[$grantType->getIdentifier()]); if ($tokenResponse instanceof ResponseTypeInterface) { return $tokenResponse->generateHttpResponse($response); } } } throw OAuthServerException::unsupportedGrantType(); }
/** * Generate a new unique identifier. * * @param int $length * * @throws \League\OAuth2\Server\Exception\OAuthServerException * * @return string */ protected function generateUniqueIdentifier($length = 40) { try { return bin2hex(random_bytes($length)); // @codeCoverageIgnoreStart } catch (\TypeError $e) { throw OAuthServerException::serverError('An unexpected error has occurred'); } catch (\Error $e) { throw OAuthServerException::serverError('An unexpected error has occurred'); } catch (\Exception $e) { // If you get this message, the CSPRNG failed hard. throw OAuthServerException::serverError('Could not generate a random string'); } // @codeCoverageIgnoreEnd }
/** * @param ServerRequestInterface $request * @param string $clientId * * @throws OAuthServerException * * @return array */ protected function validateOldRefreshToken(ServerRequestInterface $request, $clientId) { $encryptedRefreshToken = $this->getRequestParameter('refresh_token', $request); if (is_null($encryptedRefreshToken)) { throw OAuthServerException::invalidRequest('refresh_token'); } // Validate refresh token try { $refreshToken = $this->decrypt($encryptedRefreshToken); } catch (\LogicException $e) { throw OAuthServerException::invalidRefreshToken('Cannot decrypt the refresh token'); } $refreshTokenData = json_decode($refreshToken, true); if ($refreshTokenData['client_id'] !== $clientId) { $this->getEmitter()->emit(new RequestEvent(RequestEvent::REFRESH_TOKEN_CLIENT_FAILED, $request)); throw OAuthServerException::invalidRefreshToken('Token is not linked to client'); } if ($refreshTokenData['expire_time'] < time()) { throw OAuthServerException::invalidRefreshToken('Token has expired'); } if ($this->refreshTokenRepository->isRefreshTokenRevoked($refreshTokenData['refresh_token_id']) === true) { throw OAuthServerException::invalidRefreshToken('Token has been revoked'); } return $refreshTokenData; }
/** * {@inheritdoc} */ public function completeAuthorizationRequest(AuthorizationRequest $authorizationRequest) { if ($authorizationRequest->getUser() instanceof UserEntityInterface === false) { throw new \LogicException('An instance of UserEntityInterface should be set on the AuthorizationRequest'); } $finalRedirectUri = $authorizationRequest->getRedirectUri() === null ? is_array($authorizationRequest->getClient()->getRedirectUri()) ? $authorizationRequest->getClient()->getRedirectUri()[0] : $authorizationRequest->getClient()->getRedirectUri() : $authorizationRequest->getRedirectUri(); // The user approved the client, redirect them back with an auth code if ($authorizationRequest->isAuthorizationApproved() === true) { $authCode = $this->issueAuthCode($this->authCodeTTL, $authorizationRequest->getClient(), $authorizationRequest->getUser()->getIdentifier(), $authorizationRequest->getRedirectUri(), $authorizationRequest->getScopes()); $response = new RedirectResponse(); $response->setRedirectUri($this->makeRedirectUri($finalRedirectUri, ['code' => $this->encrypt(json_encode(['client_id' => $authCode->getClient()->getIdentifier(), 'redirect_uri' => $authCode->getRedirectUri(), 'auth_code_id' => $authCode->getIdentifier(), 'scopes' => $authCode->getScopes(), 'user_id' => $authCode->getUserIdentifier(), 'expire_time' => (new \DateTime())->add($this->authCodeTTL)->format('U'), 'code_challenge' => $authorizationRequest->getCodeChallenge(), 'code_challenge_method ' => $authorizationRequest->getCodeChallengeMethod()])), 'state' => $authorizationRequest->getState()])); return $response; } // The user denied the client, redirect them back with an error throw OAuthServerException::accessDenied('The user denied the request', $this->makeRedirectUri($finalRedirectUri, ['state' => $authorizationRequest->getState()])); }
/** * Return an access token response. * * @param \Phalcon\Http\RequestInterface $request * @param \Ivyhjk\OAuth2\Server\Contract\Http\Response $response * * @throws \League\OAuth2\Server\Exception\OAuthServerException * * @return \Ivyhjk\OAuth2\Server\Contract\Http\Response **/ public function respondToAccessTokenRequest(RequestContract $request, ResponseContract $response) { $tokenResponse = null; while ($tokenResponse === null && ($grantType = array_shift($this->enabledGrantTypes))) { /** @var \League\OAuth2\Server\Grant\GrantTypeInterface $grantType */ if ($grantType->canRespondToAccessTokenRequest($request)) { $tokenResponse = $grantType->respondToAccessTokenRequest($request, $this->getResponseType(), $this->grantTypeAccessTokenTTL[$grantType->getIdentifier()]); } } if ($tokenResponse instanceof ResponseTypeContract) { return $tokenResponse->generateHttpResponse($response); } throw OAuthServerException::unsupportedGrantType(); }
public function __construct($resource, $identifier) { $message = sprintf(ERROR_MESSAGE, $resource, $identifier); $resource = strtolower(str_replace(' ', '_', $resource)); parent::__construct($message, 100, sprintf('%s_not_found', $resource), 404); }
/** * {@inheritdoc} */ public function completeAuthorizationRequest(AuthorizationRequest $authorizationRequest) { if ($authorizationRequest->getUser() instanceof UserEntityInterface === false) { throw new \LogicException('An instance of UserEntityInterface should be set on the AuthorizationRequest'); } $finalRedirectUri = $authorizationRequest->getRedirectUri() === null ? is_array($authorizationRequest->getClient()->getRedirectUri()) ? $authorizationRequest->getClient()->getRedirectUri()[0] : $authorizationRequest->getClient()->getRedirectUri() : $authorizationRequest->getRedirectUri(); // The user approved the client, redirect them back with an access token if ($authorizationRequest->isAuthorizationApproved() === true) { $accessToken = $this->issueAccessToken($this->accessTokenTTL, $authorizationRequest->getClient(), $authorizationRequest->getUser()->getIdentifier(), $authorizationRequest->getScopes()); $response = new RedirectResponse(); $response->setRedirectUri($this->makeRedirectUri($finalRedirectUri, ['access_token' => (string) $accessToken->convertToJWT($this->privateKey), 'token_type' => 'bearer', 'expires_in' => $accessToken->getExpiryDateTime()->getTimestamp() - (new \DateTime())->getTimestamp(), 'state' => $authorizationRequest->getState()], '#')); return $response; } // The user denied the client, redirect them back with an error throw OAuthServerException::accessDenied('The user denied the request', $this->makeRedirectUri($finalRedirectUri, ['state' => $authorizationRequest->getState()])); }