All checks were successful
rvr-nextgen/pipeline/pr-master This commit looks good
198 lines
6.6 KiB
PHP
198 lines
6.6 KiB
PHP
<?php namespace RVR\Controller;
|
|
|
|
use DateTime;
|
|
use Firebase\JWT\JWT;
|
|
use RVR\Repository\OAuthTokenRepository;
|
|
use RVR\Repository\UserRepository;
|
|
use RVR\PersistentData\Model\User;
|
|
use RVR\Repository\OAuthClientRepository;
|
|
use SokoWeb\Interfaces\Response\IContent;
|
|
use SokoWeb\Response\JsonContent;
|
|
|
|
class OAuthController
|
|
{
|
|
private OAuthClientRepository $oAuthClientRepository;
|
|
|
|
private OAuthTokenRepository $oAuthTokenRepository;
|
|
|
|
private UserRepository $userRepository;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->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;
|
|
}
|
|
}
|