make oauth endpoints openid compliant

This commit is contained in:
bence committed 2023-04-11 19:06:22 +02:00
1 parent db757de71c
commit 6eb54a6f11
7 files changed
+306 -134

No files matched your search

+182
View File
@@ -0,0 +1,182 @@
<?php namespace RVR\Controller;
use DateTime;
use Firebase\JWT\JWT;
use RVR\Repository\OAuthTokenRepository;
use RVR\Repository\UserRepository;
use RVR\PersistentData\Model\User;
use SokoWeb\Interfaces\Request\IRequest;
use SokoWeb\Interfaces\Response\IContent;
use SokoWeb\Response\JsonContent;
class OAuthController
{
private IRequest $request;
private OAuthTokenRepository $oAuthTokenRepository;
private UserRepository $userRepository;
public function __construct(IRequest $request)
{
$this->request = $request;
$this->oAuthTokenRepository = new OAuthTokenRepository();
$this->userRepository = new UserRepository();
}
public function getToken(): ?IContent
{
$token = $this->oAuthTokenRepository->getByCode($this->request->post('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(),
'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
{
//TODO: headers should be set by soko-web
$headers = getallheaders();
if (!isset($headers['Authorization'])) {
return new JsonContent([
'error' => 'No Authorization header was sent.'
]);
}
$accessToken = substr($headers['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' => $this->request->getBase() . '/oauth/auth',
'token_endpoint' => $this->request->getBase() . '/oauth/token',
'userinfo_endpoint' => $this->request->getBase() . '/oauth/userinfo',
'jwks_uri' => $this->request->getBase() . '/oauth/certs',
'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;
}
}