oAuthClientRepository = new OAuthClientRepository(); $this->oAuthTokenRepository = new OAuthTokenRepository(); $this->userRepository = new UserRepository(); } public function getToken(): ?IContent { $clientId = \Container::$request->post('client_id'); $clientSecret = \Container::$request->post('client_secret'); $code = \Container::$request->post('code'); if (!$clientId || !$clientSecret || !$code) { return new JsonContent([ 'error' => 'An invalid request was made.' ]); } $client = $this->oAuthClientRepository->getByClientId($clientId); if ($client === null || $client->getClientSecret() !== $clientSecret) { return new JsonContent([ 'error' => 'Client is not authorized.' ]); } $token = $this->oAuthTokenRepository->getByCode($code); if ($token === null || $token->getExpiresDate() < new DateTime()) { return new JsonContent([ 'error' => 'The provided code is invalid.' ]); } $payload = array_merge([ 'iss' => $_ENV['APP_URL'], 'iat' => (int)$token->getCreatedDate()->getTimestamp(), 'nbf' => (int)$token->getCreatedDate()->getTimestamp(), 'exp' => (int)$token->getExpiresDate()->getTimestamp(), 'aud' => $clientId, 'nonce' => $token->getNonce() ], $this->getUserInfoInternal( $this->userRepository->getById($token->getUserId()), $token->getScopeArray()) ); $privateKey = file_get_contents(ROOT . '/' . $_ENV['JWT_RSA_PRIVATE_KEY']); $jwt = JWT::encode($payload, $privateKey, 'RS256'); return new JsonContent([ 'access_token' => $token->getAccessToken(), 'expires_in' => $token->getExpiresDate()->getTimestamp() - (new DateTime())->getTimestamp(), 'scope' => $token->getScope(), 'id_token' => $jwt, 'token_type' => 'Bearer' ]); } public function getUserInfo() : IContent { $authorization = \Container::$request->header('Authorization'); if ($authorization === null) { return new JsonContent([ 'error' => 'No Authorization header was sent.' ]); } $accessToken = substr($authorization, strlen('Bearer ')); $token = $this->oAuthTokenRepository->getByAccessToken($accessToken); if ($token === null || $token->getExpiresDate() < new DateTime()) { return new JsonContent([ 'error' => 'The provided access token is invalid.' ]); } return new JsonContent( $this->getUserInfoInternal( $this->userRepository->getById($token->getUserId()), $token->getScopeArray() ) ); } public function getConfig(): IContent { return new JsonContent([ 'issuer' => $_ENV['APP_URL'], 'authorization_endpoint' => \Container::$request->getBase() . \Container::$routeCollection->getRoute('oauth.auth')->generateLink(), 'token_endpoint' => \Container::$request->getBase() . \Container::$routeCollection->getRoute('oauth.token')->generateLink(), 'userinfo_endpoint' => \Container::$request->getBase() . \Container::$routeCollection->getRoute('oauth.userinfo')->generateLink(), 'jwks_uri' => \Container::$request->getBase() . \Container::$routeCollection->getRoute('oauth.certs')->generateLink(), 'response_types_supported' => [ 'code', ], 'subject_types_supported' => [ 'public', ], 'id_token_signing_alg_values_supported' => [ 'RS256', ], 'scopes_supported' => [ 'openid', 'email', 'profile', ], 'token_endpoint_auth_methods_supported' => [ 'client_secret_post', ], 'claims_supported' => [ 'aud', 'email', 'exp', 'full_name', 'iat', 'id_number', 'iss', 'nickname', 'phone', 'picture', 'sub', 'username', ], 'code_challenge_methods_supported' => [ 'plain', 'S256', ], 'grant_types_supported' => [ 'authorization_code', ], ]); } public function getCerts(): IContent { $publicKey = file_get_contents(ROOT . '/' . $_ENV['JWT_RSA_PUBLIC_KEY']); $keyInfo = openssl_pkey_get_details(openssl_pkey_get_public($publicKey)); return new JsonContent(['keys' => [ [ 'kty' => 'RSA', 'alg' => 'RS256', 'use' => 'sig', 'kid' => '1', 'n' => str_replace(['+', '/'], ['-', '_'], base64_encode($keyInfo['rsa']['n'])), 'e' => str_replace(['+', '/'], ['-', '_'], base64_encode($keyInfo['rsa']['e'])), ] ]]); } private function getUserInfoInternal(User $user, array $scope): array { $userInfo = []; if (in_array('openid', $scope)) { $userInfo['sub'] = (string)$user->getId(); } if (in_array('email', $scope)) { $userInfo['email'] = $user->getEmail(); } if (in_array('profile', $scope)) { if ($user->getUsername() !== null) { $userInfo['preferred_username'] = $user->getUsername(); } $userInfo['name'] = $user->getFullName(); $userInfo['nickname'] = $user->getNickname(); $userInfo['phone_number'] = $user->getPhone(); $userInfo['id_number'] = $user->getIdNumber(); } return $userInfo; } }