Loading classes/ClientAuthChecker.php +30 −10 Original line number Diff line number Diff line Loading @@ -17,6 +17,35 @@ class ClientAuthChecker { public function validateClientAuth(): void { $basic = $this->getBasicAuthArray(); $clientId = $basic[0]; $clientSecret = $basic[1]; $client = $this->locator->getOAuth2ClientDAO()->getOAuth2ClientByClientId($clientId); if ($client === null) { throw new UnauthorizedException("Client '$clientId' not configured"); } if ($clientSecret !== $client->secret) { throw new UnauthorizedException("Invalid client secret"); } } public function validateCliClientAuth(): CliClient { $basic = $this->getBasicAuthArray(); $clientId = $basic[0]; $clientSecret = $basic[1]; $client = $this->locator->getOAuth2ClientDAO()->getCliClient($clientId, $clientSecret); if ($client === null) { throw new UnauthorizedException("Client '$clientId' not configured or wrong password"); } return $client; } private function getBasicAuthArray(): array { $headers = apache_request_headers(); if (!isset($headers['Authorization'])) { Loading @@ -29,16 +58,7 @@ class ClientAuthChecker { if (count($basic) !== 2) { throw new BadRequestException("Malformed Basic-Auth header"); } $clientId = $basic[0]; $clientSecret = $basic[1]; $client = $this->locator->getOAuth2ClientDAO()->getOAuth2ClientByClientId($clientId); if ($client === null) { throw new UnauthorizedException("Client '$clientId' not configured"); } if ($clientSecret !== $client->secret) { throw new UnauthorizedException("Invalid client secret"); } return $basic; } else { throw new UnauthorizedException("Expected Basic authorization header"); } Loading classes/JWKSHandler.php +1 −5 Original line number Diff line number Diff line Loading @@ -107,11 +107,7 @@ class JWKSHandler { $dao->updatePublicJWK($jwk); } } else { $errorMessage = 'Error while retrieving JWKS: ' . curl_error($conn); error_log($result); curl_close($conn); http_response_code(500); die($errorMessage); error_log('Error while retrieving JWKS from ' . $url); } curl_close($conn); Loading classes/OAuth2RequestHandler.php +18 −3 Original line number Diff line number Diff line Loading @@ -91,7 +91,7 @@ class OAuth2RequestHandler { return $redirectUrl; } public function handleAccessTokenRequest($params): array { public function handleGetTokenFromCodeRequest($params): array { $this->locator->getClientAuthChecker()->validateClientAuth(); Loading Loading @@ -123,6 +123,19 @@ class OAuth2RequestHandler { return $response; } public function handleClientCredentialsRequest($params): array { $client = $this->locator->getClientAuthChecker()->validateCliClientAuth(); $accessTokenData = new AccessTokenData(); $accessTokenData->clientId = $client->id; $accessTokenData->userId = $client->id; $accessTokenData->scope = $client->scope; $accessTokenData->audience = $client->audience; return $this->getAccessTokenResponse($accessTokenData, false); } public function handleRefreshTokenRequest($params): array { $this->locator->getClientAuthChecker()->validateClientAuth(); Loading Loading @@ -182,14 +195,16 @@ class OAuth2RequestHandler { return $scope; } private function getAccessTokenResponse(AccessTokenData $tokenData) { private function getAccessTokenResponse(AccessTokenData $tokenData, bool $refreshToken = true) { $result = []; $result['access_token'] = $this->locator->getTokenBuilder()->getAccessToken($tokenData); $result['token_type'] = 'Bearer'; $result['expires_in'] = $tokenData->expirationTime - time(); if ($refreshToken) { $result['refresh_token'] = $this->buildRefreshToken($tokenData); } if ($tokenData->scope !== null && in_array('openid', $tokenData->scope)) { $result['id_token'] = $this->locator->getTokenBuilder()->getIdToken($tokenData); Loading classes/TokenBuilder.php +19 −1 Original line number Diff line number Diff line Loading @@ -58,10 +58,16 @@ class TokenBuilder { $keyPair = $this->locator->getJWKSDAO()->getNewestKeyPair(); $user = $this->locator->getUserDAO()->findUserById($tokenData->userId); if ($user === null) { // CLI client $sub = $tokenData->clientId; } else { $sub = $user->id; } $payload = array( 'iss' => $this->locator->config->jwtIssuer, 'sub' => strval($user->id), 'sub' => strval($sub), 'iat' => intval($tokenData->creationTime), 'exp' => intval($tokenData->expirationTime), 'aud' => $this->getAudience($tokenData), Loading @@ -77,7 +83,15 @@ class TokenBuilder { private function getAudience(AccessTokenData $tokenData) { if ($tokenData->audience !== null) { return $this->getAudienceClaim($tokenData->audience); } $client = $this->locator->getOAuth2ClientDAO()->getOAuth2ClientByClientId($tokenData->clientId); if ($client === null) { // CLI client without audience return null; } $audiences = [$tokenData->clientId]; Loading @@ -90,6 +104,10 @@ class TokenBuilder { } } return $this->getAudienceClaim($audiences); } private function getAudienceClaim($audiences) { if (count($audiences) === 1) { // according to RFC 7519 audience can be a single value or an array return $audiences[0]; Loading classes/datalayer/OAuth2ClientDAO.php +2 −0 Original line number Diff line number Diff line Loading @@ -20,4 +20,6 @@ interface OAuth2ClientDAO { * the secret, not the database id). */ function getOAuth2ClientByClientId($clientId): ?OAuth2Client; function getCliClient(string $clientId, string $secret): ?CliClient; } Loading
classes/ClientAuthChecker.php +30 −10 Original line number Diff line number Diff line Loading @@ -17,6 +17,35 @@ class ClientAuthChecker { public function validateClientAuth(): void { $basic = $this->getBasicAuthArray(); $clientId = $basic[0]; $clientSecret = $basic[1]; $client = $this->locator->getOAuth2ClientDAO()->getOAuth2ClientByClientId($clientId); if ($client === null) { throw new UnauthorizedException("Client '$clientId' not configured"); } if ($clientSecret !== $client->secret) { throw new UnauthorizedException("Invalid client secret"); } } public function validateCliClientAuth(): CliClient { $basic = $this->getBasicAuthArray(); $clientId = $basic[0]; $clientSecret = $basic[1]; $client = $this->locator->getOAuth2ClientDAO()->getCliClient($clientId, $clientSecret); if ($client === null) { throw new UnauthorizedException("Client '$clientId' not configured or wrong password"); } return $client; } private function getBasicAuthArray(): array { $headers = apache_request_headers(); if (!isset($headers['Authorization'])) { Loading @@ -29,16 +58,7 @@ class ClientAuthChecker { if (count($basic) !== 2) { throw new BadRequestException("Malformed Basic-Auth header"); } $clientId = $basic[0]; $clientSecret = $basic[1]; $client = $this->locator->getOAuth2ClientDAO()->getOAuth2ClientByClientId($clientId); if ($client === null) { throw new UnauthorizedException("Client '$clientId' not configured"); } if ($clientSecret !== $client->secret) { throw new UnauthorizedException("Invalid client secret"); } return $basic; } else { throw new UnauthorizedException("Expected Basic authorization header"); } Loading
classes/JWKSHandler.php +1 −5 Original line number Diff line number Diff line Loading @@ -107,11 +107,7 @@ class JWKSHandler { $dao->updatePublicJWK($jwk); } } else { $errorMessage = 'Error while retrieving JWKS: ' . curl_error($conn); error_log($result); curl_close($conn); http_response_code(500); die($errorMessage); error_log('Error while retrieving JWKS from ' . $url); } curl_close($conn); Loading
classes/OAuth2RequestHandler.php +18 −3 Original line number Diff line number Diff line Loading @@ -91,7 +91,7 @@ class OAuth2RequestHandler { return $redirectUrl; } public function handleAccessTokenRequest($params): array { public function handleGetTokenFromCodeRequest($params): array { $this->locator->getClientAuthChecker()->validateClientAuth(); Loading Loading @@ -123,6 +123,19 @@ class OAuth2RequestHandler { return $response; } public function handleClientCredentialsRequest($params): array { $client = $this->locator->getClientAuthChecker()->validateCliClientAuth(); $accessTokenData = new AccessTokenData(); $accessTokenData->clientId = $client->id; $accessTokenData->userId = $client->id; $accessTokenData->scope = $client->scope; $accessTokenData->audience = $client->audience; return $this->getAccessTokenResponse($accessTokenData, false); } public function handleRefreshTokenRequest($params): array { $this->locator->getClientAuthChecker()->validateClientAuth(); Loading Loading @@ -182,14 +195,16 @@ class OAuth2RequestHandler { return $scope; } private function getAccessTokenResponse(AccessTokenData $tokenData) { private function getAccessTokenResponse(AccessTokenData $tokenData, bool $refreshToken = true) { $result = []; $result['access_token'] = $this->locator->getTokenBuilder()->getAccessToken($tokenData); $result['token_type'] = 'Bearer'; $result['expires_in'] = $tokenData->expirationTime - time(); if ($refreshToken) { $result['refresh_token'] = $this->buildRefreshToken($tokenData); } if ($tokenData->scope !== null && in_array('openid', $tokenData->scope)) { $result['id_token'] = $this->locator->getTokenBuilder()->getIdToken($tokenData); Loading
classes/TokenBuilder.php +19 −1 Original line number Diff line number Diff line Loading @@ -58,10 +58,16 @@ class TokenBuilder { $keyPair = $this->locator->getJWKSDAO()->getNewestKeyPair(); $user = $this->locator->getUserDAO()->findUserById($tokenData->userId); if ($user === null) { // CLI client $sub = $tokenData->clientId; } else { $sub = $user->id; } $payload = array( 'iss' => $this->locator->config->jwtIssuer, 'sub' => strval($user->id), 'sub' => strval($sub), 'iat' => intval($tokenData->creationTime), 'exp' => intval($tokenData->expirationTime), 'aud' => $this->getAudience($tokenData), Loading @@ -77,7 +83,15 @@ class TokenBuilder { private function getAudience(AccessTokenData $tokenData) { if ($tokenData->audience !== null) { return $this->getAudienceClaim($tokenData->audience); } $client = $this->locator->getOAuth2ClientDAO()->getOAuth2ClientByClientId($tokenData->clientId); if ($client === null) { // CLI client without audience return null; } $audiences = [$tokenData->clientId]; Loading @@ -90,6 +104,10 @@ class TokenBuilder { } } return $this->getAudienceClaim($audiences); } private function getAudienceClaim($audiences) { if (count($audiences) === 1) { // according to RFC 7519 audience can be a single value or an array return $audiences[0]; Loading
classes/datalayer/OAuth2ClientDAO.php +2 −0 Original line number Diff line number Diff line Loading @@ -20,4 +20,6 @@ interface OAuth2ClientDAO { * the secret, not the database id). */ function getOAuth2ClientByClientId($clientId): ?OAuth2Client; function getCliClient(string $clientId, string $secret): ?CliClient; }