1
0
mirror of https://github.com/BookStackApp/BookStack.git synced 2024-11-23 11:22:33 +01:00

Renamed OIDC files to all be aligned

This commit is contained in:
Dan Brown 2021-10-12 23:04:28 +01:00
parent 06a0d829c8
commit c167f40af3
No known key found for this signature in database
GPG Key ID: 46D9F943C24A2EF9
14 changed files with 112 additions and 112 deletions

View File

@ -1,11 +1,11 @@
<?php <?php
namespace BookStack\Auth\Access\OpenIdConnect; namespace BookStack\Auth\Access\Oidc;
use InvalidArgumentException; use InvalidArgumentException;
use League\OAuth2\Client\Token\AccessToken; use League\OAuth2\Client\Token\AccessToken;
class OpenIdConnectAccessToken extends AccessToken class OidcAccessToken extends AccessToken
{ {
/** /**
* Constructs an access token. * Constructs an access token.

View File

@ -1,8 +1,8 @@
<?php <?php
namespace BookStack\Auth\Access\OpenIdConnect; namespace BookStack\Auth\Access\Oidc;
class OpenIdConnectIdToken class OidcIdToken
{ {
/** /**
* @var array * @var array
@ -74,7 +74,7 @@ class OpenIdConnectIdToken
/** /**
* Validate all possible parts of the id token. * Validate all possible parts of the id token.
* @throws InvalidTokenException * @throws OidcInvalidTokenException
*/ */
public function validate(string $clientId): bool public function validate(string $clientId): bool
{ {
@ -105,35 +105,35 @@ class OpenIdConnectIdToken
/** /**
* Validate the structure of the given token and ensure we have the required pieces. * Validate the structure of the given token and ensure we have the required pieces.
* As per https://datatracker.ietf.org/doc/html/rfc7519#section-7.2 * As per https://datatracker.ietf.org/doc/html/rfc7519#section-7.2
* @throws InvalidTokenException * @throws OidcInvalidTokenException
*/ */
protected function validateTokenStructure(): void protected function validateTokenStructure(): void
{ {
foreach (['header', 'payload'] as $prop) { foreach (['header', 'payload'] as $prop) {
if (empty($this->$prop) || !is_array($this->$prop)) { if (empty($this->$prop) || !is_array($this->$prop)) {
throw new InvalidTokenException("Could not parse out a valid {$prop} within the provided token"); throw new OidcInvalidTokenException("Could not parse out a valid {$prop} within the provided token");
} }
} }
if (empty($this->signature) || !is_string($this->signature)) { if (empty($this->signature) || !is_string($this->signature)) {
throw new InvalidTokenException("Could not parse out a valid signature within the provided token"); throw new OidcInvalidTokenException("Could not parse out a valid signature within the provided token");
} }
} }
/** /**
* Validate the signature of the given token and ensure it validates against the provided key. * Validate the signature of the given token and ensure it validates against the provided key.
* @throws InvalidTokenException * @throws OidcInvalidTokenException
*/ */
protected function validateTokenSignature(): void protected function validateTokenSignature(): void
{ {
if ($this->header['alg'] !== 'RS256') { if ($this->header['alg'] !== 'RS256') {
throw new InvalidTokenException("Only RS256 signature validation is supported. Token reports using {$this->header['alg']}"); throw new OidcInvalidTokenException("Only RS256 signature validation is supported. Token reports using {$this->header['alg']}");
} }
$parsedKeys = array_map(function($key) { $parsedKeys = array_map(function($key) {
try { try {
return new JwtSigningKey($key); return new OidcJwtSigningKey($key);
} catch (InvalidKeyException $e) { } catch (OidcInvalidKeyException $e) {
return null; return null;
} }
}, $this->keys); }, $this->keys);
@ -141,27 +141,27 @@ class OpenIdConnectIdToken
$parsedKeys = array_filter($parsedKeys); $parsedKeys = array_filter($parsedKeys);
$contentToSign = $this->tokenParts[0] . '.' . $this->tokenParts[1]; $contentToSign = $this->tokenParts[0] . '.' . $this->tokenParts[1];
/** @var JwtSigningKey $parsedKey */ /** @var OidcJwtSigningKey $parsedKey */
foreach ($parsedKeys as $parsedKey) { foreach ($parsedKeys as $parsedKey) {
if ($parsedKey->verify($contentToSign, $this->signature)) { if ($parsedKey->verify($contentToSign, $this->signature)) {
return; return;
} }
} }
throw new InvalidTokenException('Token signature could not be validated using the provided keys'); throw new OidcInvalidTokenException('Token signature could not be validated using the provided keys');
} }
/** /**
* Validate the claims of the token. * Validate the claims of the token.
* As per https://openid.net/specs/openid-connect-basic-1_0.html#IDTokenValidation * As per https://openid.net/specs/openid-connect-basic-1_0.html#IDTokenValidation
* @throws InvalidTokenException * @throws OidcInvalidTokenException
*/ */
protected function validateTokenClaims(string $clientId): void protected function validateTokenClaims(string $clientId): void
{ {
// 1. The Issuer Identifier for the OpenID Provider (which is typically obtained during Discovery) // 1. The Issuer Identifier for the OpenID Provider (which is typically obtained during Discovery)
// MUST exactly match the value of the iss (issuer) Claim. // MUST exactly match the value of the iss (issuer) Claim.
if (empty($this->payload['iss']) || $this->issuer !== $this->payload['iss']) { if (empty($this->payload['iss']) || $this->issuer !== $this->payload['iss']) {
throw new InvalidTokenException('Missing or non-matching token issuer value'); throw new OidcInvalidTokenException('Missing or non-matching token issuer value');
} }
// 2. The Client MUST validate that the aud (audience) Claim contains its client_id value registered // 2. The Client MUST validate that the aud (audience) Claim contains its client_id value registered
@ -169,16 +169,16 @@ class OpenIdConnectIdToken
// if the ID Token does not list the Client as a valid audience, or if it contains additional // if the ID Token does not list the Client as a valid audience, or if it contains additional
// audiences not trusted by the Client. // audiences not trusted by the Client.
if (empty($this->payload['aud'])) { if (empty($this->payload['aud'])) {
throw new InvalidTokenException('Missing token audience value'); throw new OidcInvalidTokenException('Missing token audience value');
} }
$aud = is_string($this->payload['aud']) ? [$this->payload['aud']] : $this->payload['aud']; $aud = is_string($this->payload['aud']) ? [$this->payload['aud']] : $this->payload['aud'];
if (count($aud) !== 1) { if (count($aud) !== 1) {
throw new InvalidTokenException('Token audience value has ' . count($aud) . ' values, Expected 1'); throw new OidcInvalidTokenException('Token audience value has ' . count($aud) . ' values, Expected 1');
} }
if ($aud[0] !== $clientId) { if ($aud[0] !== $clientId) {
throw new InvalidTokenException('Token audience value did not match the expected client_id'); throw new OidcInvalidTokenException('Token audience value did not match the expected client_id');
} }
// 3. If the ID Token contains multiple audiences, the Client SHOULD verify that an azp Claim is present. // 3. If the ID Token contains multiple audiences, the Client SHOULD verify that an azp Claim is present.
@ -187,32 +187,32 @@ class OpenIdConnectIdToken
// 4. If an azp (authorized party) Claim is present, the Client SHOULD verify that its client_id // 4. If an azp (authorized party) Claim is present, the Client SHOULD verify that its client_id
// is the Claim Value. // is the Claim Value.
if (isset($this->payload['azp']) && $this->payload['azp'] !== $clientId) { if (isset($this->payload['azp']) && $this->payload['azp'] !== $clientId) {
throw new InvalidTokenException('Token authorized party exists but does not match the expected client_id'); throw new OidcInvalidTokenException('Token authorized party exists but does not match the expected client_id');
} }
// 5. The current time MUST be before the time represented by the exp Claim // 5. The current time MUST be before the time represented by the exp Claim
// (possibly allowing for some small leeway to account for clock skew). // (possibly allowing for some small leeway to account for clock skew).
if (empty($this->payload['exp'])) { if (empty($this->payload['exp'])) {
throw new InvalidTokenException('Missing token expiration time value'); throw new OidcInvalidTokenException('Missing token expiration time value');
} }
$skewSeconds = 120; $skewSeconds = 120;
$now = time(); $now = time();
if ($now >= (intval($this->payload['exp']) + $skewSeconds)) { if ($now >= (intval($this->payload['exp']) + $skewSeconds)) {
throw new InvalidTokenException('Token has expired'); throw new OidcInvalidTokenException('Token has expired');
} }
// 6. The iat Claim can be used to reject tokens that were issued too far away from the current time, // 6. The iat Claim can be used to reject tokens that were issued too far away from the current time,
// limiting the amount of time that nonces need to be stored to prevent attacks. // limiting the amount of time that nonces need to be stored to prevent attacks.
// The acceptable range is Client specific. // The acceptable range is Client specific.
if (empty($this->payload['iat'])) { if (empty($this->payload['iat'])) {
throw new InvalidTokenException('Missing token issued at time value'); throw new OidcInvalidTokenException('Missing token issued at time value');
} }
$dayAgo = time() - 86400; $dayAgo = time() - 86400;
$iat = intval($this->payload['iat']); $iat = intval($this->payload['iat']);
if ($iat > ($now + $skewSeconds) || $iat < $dayAgo) { if ($iat > ($now + $skewSeconds) || $iat < $dayAgo) {
throw new InvalidTokenException('Token issue at time is not recent or is invalid'); throw new OidcInvalidTokenException('Token issue at time is not recent or is invalid');
} }
// 7. If the acr Claim was requested, the Client SHOULD check that the asserted Claim Value is appropriate. // 7. If the acr Claim was requested, the Client SHOULD check that the asserted Claim Value is appropriate.
@ -225,7 +225,7 @@ class OpenIdConnectIdToken
// Custom: Ensure the "sub" (Subject) Claim exists and has a value. // Custom: Ensure the "sub" (Subject) Claim exists and has a value.
if (empty($this->payload['sub'])) { if (empty($this->payload['sub'])) {
throw new InvalidTokenException('Missing token subject value'); throw new OidcInvalidTokenException('Missing token subject value');
} }
} }

View File

@ -0,0 +1,8 @@
<?php
namespace BookStack\Auth\Access\Oidc;
class OidcInvalidKeyException extends \Exception
{
}

View File

@ -0,0 +1,10 @@
<?php
namespace BookStack\Auth\Access\Oidc;
use Exception;
class OidcInvalidTokenException extends Exception
{
}

View File

@ -0,0 +1,8 @@
<?php
namespace BookStack\Auth\Access\Oidc;
class OidcIssuerDiscoveryException extends \Exception
{
}

View File

@ -1,13 +1,13 @@
<?php <?php
namespace BookStack\Auth\Access\OpenIdConnect; namespace BookStack\Auth\Access\Oidc;
use phpseclib3\Crypt\Common\PublicKey; use phpseclib3\Crypt\Common\PublicKey;
use phpseclib3\Crypt\PublicKeyLoader; use phpseclib3\Crypt\PublicKeyLoader;
use phpseclib3\Crypt\RSA; use phpseclib3\Crypt\RSA;
use phpseclib3\Math\BigInteger; use phpseclib3\Math\BigInteger;
class JwtSigningKey class OidcJwtSigningKey
{ {
/** /**
* @var PublicKey * @var PublicKey
@ -20,7 +20,7 @@ class JwtSigningKey
* 'file:///var/www/cert.pem' * 'file:///var/www/cert.pem'
* ['kty' => 'RSA', 'alg' => 'RS256', 'n' => 'abc123...'] * ['kty' => 'RSA', 'alg' => 'RS256', 'n' => 'abc123...']
* @param array|string $jwkOrKeyPath * @param array|string $jwkOrKeyPath
* @throws InvalidKeyException * @throws OidcInvalidKeyException
*/ */
public function __construct($jwkOrKeyPath) public function __construct($jwkOrKeyPath)
{ {
@ -29,12 +29,12 @@ class JwtSigningKey
} else if (is_string($jwkOrKeyPath) && strpos($jwkOrKeyPath, 'file://') === 0) { } else if (is_string($jwkOrKeyPath) && strpos($jwkOrKeyPath, 'file://') === 0) {
$this->loadFromPath($jwkOrKeyPath); $this->loadFromPath($jwkOrKeyPath);
} else { } else {
throw new InvalidKeyException('Unexpected type of key value provided'); throw new OidcInvalidKeyException('Unexpected type of key value provided');
} }
} }
/** /**
* @throws InvalidKeyException * @throws OidcInvalidKeyException
*/ */
protected function loadFromPath(string $path) protected function loadFromPath(string $path)
{ {
@ -43,37 +43,37 @@ class JwtSigningKey
file_get_contents($path) file_get_contents($path)
)->withPadding(RSA::SIGNATURE_PKCS1); )->withPadding(RSA::SIGNATURE_PKCS1);
} catch (\Exception $exception) { } catch (\Exception $exception) {
throw new InvalidKeyException("Failed to load key from file path with error: {$exception->getMessage()}"); throw new OidcInvalidKeyException("Failed to load key from file path with error: {$exception->getMessage()}");
} }
if (!($this->key instanceof RSA)) { if (!($this->key instanceof RSA)) {
throw new InvalidKeyException("Key loaded from file path is not an RSA key as expected"); throw new OidcInvalidKeyException("Key loaded from file path is not an RSA key as expected");
} }
} }
/** /**
* @throws InvalidKeyException * @throws OidcInvalidKeyException
*/ */
protected function loadFromJwkArray(array $jwk) protected function loadFromJwkArray(array $jwk)
{ {
if ($jwk['alg'] !== 'RS256') { if ($jwk['alg'] !== 'RS256') {
throw new InvalidKeyException("Only RS256 keys are currently supported. Found key using {$jwk['alg']}"); throw new OidcInvalidKeyException("Only RS256 keys are currently supported. Found key using {$jwk['alg']}");
} }
if (empty($jwk['use'])) { if (empty($jwk['use'])) {
throw new InvalidKeyException('A "use" parameter on the provided key is expected'); throw new OidcInvalidKeyException('A "use" parameter on the provided key is expected');
} }
if ($jwk['use'] !== 'sig') { if ($jwk['use'] !== 'sig') {
throw new InvalidKeyException("Only signature keys are currently supported. Found key for use {$jwk['use']}"); throw new OidcInvalidKeyException("Only signature keys are currently supported. Found key for use {$jwk['use']}");
} }
if (empty($jwk['e'])) { if (empty($jwk['e'])) {
throw new InvalidKeyException('An "e" parameter on the provided key is expected'); throw new OidcInvalidKeyException('An "e" parameter on the provided key is expected');
} }
if (empty($jwk['n'])) { if (empty($jwk['n'])) {
throw new InvalidKeyException('A "n" parameter on the provided key is expected'); throw new OidcInvalidKeyException('A "n" parameter on the provided key is expected');
} }
$n = strtr($jwk['n'] ?? '', '-_', '+/'); $n = strtr($jwk['n'] ?? '', '-_', '+/');
@ -85,7 +85,7 @@ class JwtSigningKey
'n' => new BigInteger(base64_decode($n), 256), 'n' => new BigInteger(base64_decode($n), 256),
])->withPadding(RSA::SIGNATURE_PKCS1); ])->withPadding(RSA::SIGNATURE_PKCS1);
} catch (\Exception $exception) { } catch (\Exception $exception) {
throw new InvalidKeyException("Failed to load key from JWK parameters with error: {$exception->getMessage()}"); throw new OidcInvalidKeyException("Failed to load key from JWK parameters with error: {$exception->getMessage()}");
} }
} }

View File

@ -1,6 +1,6 @@
<?php <?php
namespace BookStack\Auth\Access\OpenIdConnect; namespace BookStack\Auth\Access\Oidc;
use League\OAuth2\Client\Grant\AbstractGrant; use League\OAuth2\Client\Grant\AbstractGrant;
use League\OAuth2\Client\Provider\AbstractProvider; use League\OAuth2\Client\Provider\AbstractProvider;
@ -16,7 +16,7 @@ use Psr\Http\Message\ResponseInterface;
* Credit to the https://github.com/steverhoades/oauth2-openid-connect-client * Credit to the https://github.com/steverhoades/oauth2-openid-connect-client
* project for the idea of extending a League\OAuth2 client for this use-case. * project for the idea of extending a League\OAuth2 client for this use-case.
*/ */
class OpenIdConnectOAuthProvider extends AbstractProvider class OidcOAuthProvider extends AbstractProvider
{ {
use BearerAuthorizationTrait; use BearerAuthorizationTrait;
@ -116,11 +116,11 @@ class OpenIdConnectOAuthProvider extends AbstractProvider
* *
* @param array $response * @param array $response
* @param AbstractGrant $grant * @param AbstractGrant $grant
* @return OpenIdConnectAccessToken * @return OidcAccessToken
*/ */
protected function createAccessToken(array $response, AbstractGrant $grant) protected function createAccessToken(array $response, AbstractGrant $grant)
{ {
return new OpenIdConnectAccessToken($response); return new OidcAccessToken($response);
} }

View File

@ -1,6 +1,6 @@
<?php <?php
namespace BookStack\Auth\Access\OpenIdConnect; namespace BookStack\Auth\Access\Oidc;
use GuzzleHttp\Psr7\Request; use GuzzleHttp\Psr7\Request;
use Illuminate\Contracts\Cache\Repository; use Illuminate\Contracts\Cache\Repository;
@ -13,7 +13,7 @@ use Psr\Http\Client\ClientInterface;
* Acts as a DTO for settings used within the oidc request and token handling. * Acts as a DTO for settings used within the oidc request and token handling.
* Performs auto-discovery upon request. * Performs auto-discovery upon request.
*/ */
class OpenIdConnectProviderSettings class OidcProviderSettings
{ {
/** /**
* @var string * @var string
@ -103,7 +103,7 @@ class OpenIdConnectProviderSettings
/** /**
* Discover and autoload settings from the configured issuer. * Discover and autoload settings from the configured issuer.
* @throws IssuerDiscoveryException * @throws OidcIssuerDiscoveryException
*/ */
public function discoverFromIssuer(ClientInterface $httpClient, Repository $cache, int $cacheMinutes) public function discoverFromIssuer(ClientInterface $httpClient, Repository $cache, int $cacheMinutes)
{ {
@ -114,12 +114,12 @@ class OpenIdConnectProviderSettings
}); });
$this->applySettingsFromArray($discoveredSettings); $this->applySettingsFromArray($discoveredSettings);
} catch (ClientExceptionInterface $exception) { } catch (ClientExceptionInterface $exception) {
throw new IssuerDiscoveryException("HTTP request failed during discovery with error: {$exception->getMessage()}"); throw new OidcIssuerDiscoveryException("HTTP request failed during discovery with error: {$exception->getMessage()}");
} }
} }
/** /**
* @throws IssuerDiscoveryException * @throws OidcIssuerDiscoveryException
* @throws ClientExceptionInterface * @throws ClientExceptionInterface
*/ */
protected function loadSettingsFromIssuerDiscovery(ClientInterface $httpClient): array protected function loadSettingsFromIssuerDiscovery(ClientInterface $httpClient): array
@ -130,11 +130,11 @@ class OpenIdConnectProviderSettings
$result = json_decode($response->getBody()->getContents(), true); $result = json_decode($response->getBody()->getContents(), true);
if (empty($result) || !is_array($result)) { if (empty($result) || !is_array($result)) {
throw new IssuerDiscoveryException("Error discovering provider settings from issuer at URL {$issuerUrl}"); throw new OidcIssuerDiscoveryException("Error discovering provider settings from issuer at URL {$issuerUrl}");
} }
if ($result['issuer'] !== $this->issuer) { if ($result['issuer'] !== $this->issuer) {
throw new IssuerDiscoveryException("Unexpected issuer value found on discovery response"); throw new OidcIssuerDiscoveryException("Unexpected issuer value found on discovery response");
} }
$discoveredSettings = []; $discoveredSettings = [];
@ -168,7 +168,7 @@ class OpenIdConnectProviderSettings
/** /**
* Return an array of jwks as PHP key=>value arrays. * Return an array of jwks as PHP key=>value arrays.
* @throws ClientExceptionInterface * @throws ClientExceptionInterface
* @throws IssuerDiscoveryException * @throws OidcIssuerDiscoveryException
*/ */
protected function loadKeysFromUri(string $uri, ClientInterface $httpClient): array protected function loadKeysFromUri(string $uri, ClientInterface $httpClient): array
{ {
@ -177,7 +177,7 @@ class OpenIdConnectProviderSettings
$result = json_decode($response->getBody()->getContents(), true); $result = json_decode($response->getBody()->getContents(), true);
if (empty($result) || !is_array($result) || !isset($result['keys'])) { if (empty($result) || !is_array($result) || !isset($result['keys'])) {
throw new IssuerDiscoveryException("Error reading keys from issuer jwks_uri"); throw new OidcIssuerDiscoveryException("Error reading keys from issuer jwks_uri");
} }
return $result['keys']; return $result['keys'];

View File

@ -1,4 +1,4 @@
<?php namespace BookStack\Auth\Access\OpenIdConnect; <?php namespace BookStack\Auth\Access\Oidc;
use BookStack\Auth\Access\LoginService; use BookStack\Auth\Access\LoginService;
use BookStack\Auth\Access\RegistrationService; use BookStack\Auth\Access\RegistrationService;
@ -20,7 +20,7 @@ use function url;
* Class OpenIdConnectService * Class OpenIdConnectService
* Handles any app-specific OIDC tasks. * Handles any app-specific OIDC tasks.
*/ */
class OpenIdConnectService class OidcService
{ {
protected $registrationService; protected $registrationService;
protected $loginService; protected $loginService;
@ -72,12 +72,12 @@ class OpenIdConnectService
} }
/** /**
* @throws IssuerDiscoveryException * @throws OidcIssuerDiscoveryException
* @throws ClientExceptionInterface * @throws ClientExceptionInterface
*/ */
protected function getProviderSettings(): OpenIdConnectProviderSettings protected function getProviderSettings(): OidcProviderSettings
{ {
$settings = new OpenIdConnectProviderSettings([ $settings = new OidcProviderSettings([
'issuer' => $this->config['issuer'], 'issuer' => $this->config['issuer'],
'clientId' => $this->config['client_id'], 'clientId' => $this->config['client_id'],
'clientSecret' => $this->config['client_secret'], 'clientSecret' => $this->config['client_secret'],
@ -104,15 +104,15 @@ class OpenIdConnectService
/** /**
* Load the underlying OpenID Connect Provider. * Load the underlying OpenID Connect Provider.
*/ */
protected function getProvider(OpenIdConnectProviderSettings $settings): OpenIdConnectOAuthProvider protected function getProvider(OidcProviderSettings $settings): OidcOAuthProvider
{ {
return new OpenIdConnectOAuthProvider($settings->arrayForProvider()); return new OidcOAuthProvider($settings->arrayForProvider());
} }
/** /**
* Calculate the display name * Calculate the display name
*/ */
protected function getUserDisplayName(OpenIdConnectIdToken $token, string $defaultValue): string protected function getUserDisplayName(OidcIdToken $token, string $defaultValue): string
{ {
$displayNameAttr = $this->config['display_name_claims']; $displayNameAttr = $this->config['display_name_claims'];
@ -135,7 +135,7 @@ class OpenIdConnectService
* Extract the details of a user from an ID token. * Extract the details of a user from an ID token.
* @return array{name: string, email: string, external_id: string} * @return array{name: string, email: string, external_id: string}
*/ */
protected function getUserDetails(OpenIdConnectIdToken $token): array protected function getUserDetails(OidcIdToken $token): array
{ {
$id = $token->getClaim('sub'); $id = $token->getClaim('sub');
return [ return [
@ -153,10 +153,10 @@ class OpenIdConnectService
* @throws UserRegistrationException * @throws UserRegistrationException
* @throws StoppedAuthenticationException * @throws StoppedAuthenticationException
*/ */
protected function processAccessTokenCallback(OpenIdConnectAccessToken $accessToken, OpenIdConnectProviderSettings $settings): User protected function processAccessTokenCallback(OidcAccessToken $accessToken, OidcProviderSettings $settings): User
{ {
$idTokenText = $accessToken->getIdToken(); $idTokenText = $accessToken->getIdToken();
$idToken = new OpenIdConnectIdToken( $idToken = new OidcIdToken(
$idTokenText, $idTokenText,
$settings->issuer, $settings->issuer,
$settings->keys, $settings->keys,
@ -168,7 +168,7 @@ class OpenIdConnectService
try { try {
$idToken->validate($settings->clientId); $idToken->validate($settings->clientId);
} catch (InvalidTokenException $exception) { } catch (OidcInvalidTokenException $exception) {
throw new OpenIdConnectException("ID token validate failed with error: {$exception->getMessage()}"); throw new OpenIdConnectException("ID token validate failed with error: {$exception->getMessage()}");
} }

View File

@ -1,8 +0,0 @@
<?php
namespace BookStack\Auth\Access\OpenIdConnect;
class InvalidKeyException extends \Exception
{
}

View File

@ -1,10 +0,0 @@
<?php
namespace BookStack\Auth\Access\OpenIdConnect;
use Exception;
class InvalidTokenException extends Exception
{
}

View File

@ -1,8 +0,0 @@
<?php
namespace BookStack\Auth\Access\OpenIdConnect;
class IssuerDiscoveryException extends \Exception
{
}

View File

@ -2,7 +2,7 @@
namespace BookStack\Http\Controllers\Auth; namespace BookStack\Http\Controllers\Auth;
use BookStack\Auth\Access\OpenIdConnect\OpenIdConnectService; use BookStack\Auth\Access\Oidc\OidcService;
use BookStack\Http\Controllers\Controller; use BookStack\Http\Controllers\Controller;
use Illuminate\Http\Request; use Illuminate\Http\Request;
@ -14,7 +14,7 @@ class OpenIdConnectController extends Controller
/** /**
* OpenIdController constructor. * OpenIdController constructor.
*/ */
public function __construct(OpenIdConnectService $oidcService) public function __construct(OidcService $oidcService)
{ {
$this->oidcService = $oidcService; $this->oidcService = $oidcService;
$this->middleware('guard:oidc'); $this->middleware('guard:oidc');

View File

@ -2,16 +2,16 @@
namespace Tests\Unit; namespace Tests\Unit;
use BookStack\Auth\Access\OpenIdConnect\InvalidTokenException; use BookStack\Auth\Access\Oidc\OidcInvalidTokenException;
use BookStack\Auth\Access\OpenIdConnect\OpenIdConnectIdToken; use BookStack\Auth\Access\Oidc\OidcIdToken;
use phpseclib3\Crypt\RSA; use phpseclib3\Crypt\RSA;
use Tests\TestCase; use Tests\TestCase;
class OpenIdConnectIdTokenTest extends TestCase class OidcIdTokenTest extends TestCase
{ {
public function test_valid_token_passes_validation() public function test_valid_token_passes_validation()
{ {
$token = new OpenIdConnectIdToken($this->idToken(), 'https://auth.example.com', [ $token = new OidcIdToken($this->idToken(), 'https://auth.example.com', [
$this->jwkKeyArray() $this->jwkKeyArray()
]); ]);
@ -20,20 +20,20 @@ class OpenIdConnectIdTokenTest extends TestCase
public function test_get_claim_returns_value_if_existing() public function test_get_claim_returns_value_if_existing()
{ {
$token = new OpenIdConnectIdToken($this->idToken(), 'https://auth.example.com', []); $token = new OidcIdToken($this->idToken(), 'https://auth.example.com', []);
$this->assertEquals('bscott@example.com', $token->getClaim('email')); $this->assertEquals('bscott@example.com', $token->getClaim('email'));
} }
public function test_get_claim_returns_null_if_not_existing() public function test_get_claim_returns_null_if_not_existing()
{ {
$token = new OpenIdConnectIdToken($this->idToken(), 'https://auth.example.com', []); $token = new OidcIdToken($this->idToken(), 'https://auth.example.com', []);
$this->assertEquals(null, $token->getClaim('emails')); $this->assertEquals(null, $token->getClaim('emails'));
} }
public function test_get_all_claims_returns_all_payload_claims() public function test_get_all_claims_returns_all_payload_claims()
{ {
$defaultPayload = $this->getDefaultPayload(); $defaultPayload = $this->getDefaultPayload();
$token = new OpenIdConnectIdToken($this->idToken($defaultPayload), 'https://auth.example.com', []); $token = new OidcIdToken($this->idToken($defaultPayload), 'https://auth.example.com', []);
$this->assertEquals($defaultPayload, $token->getAllClaims()); $this->assertEquals($defaultPayload, $token->getAllClaims());
} }
@ -52,7 +52,7 @@ class OpenIdConnectIdTokenTest extends TestCase
]; ];
foreach ($messagesAndTokenValues as [$message, $tokenValue]) { foreach ($messagesAndTokenValues as [$message, $tokenValue]) {
$token = new OpenIdConnectIdToken($tokenValue, 'https://auth.example.com', []); $token = new OidcIdToken($tokenValue, 'https://auth.example.com', []);
$err = null; $err = null;
try { try {
$token->validate('abc'); $token->validate('abc');
@ -60,43 +60,43 @@ class OpenIdConnectIdTokenTest extends TestCase
$err = $exception; $err = $exception;
} }
$this->assertInstanceOf(InvalidTokenException::class, $err, $message); $this->assertInstanceOf(OidcInvalidTokenException::class, $err, $message);
$this->assertEquals($message, $err->getMessage()); $this->assertEquals($message, $err->getMessage());
} }
} }
public function test_error_thrown_if_token_signature_not_validated_from_no_keys() public function test_error_thrown_if_token_signature_not_validated_from_no_keys()
{ {
$token = new OpenIdConnectIdToken($this->idToken(), 'https://auth.example.com', []); $token = new OidcIdToken($this->idToken(), 'https://auth.example.com', []);
$this->expectException(InvalidTokenException::class); $this->expectException(OidcInvalidTokenException::class);
$this->expectExceptionMessage('Token signature could not be validated using the provided keys'); $this->expectExceptionMessage('Token signature could not be validated using the provided keys');
$token->validate('abc'); $token->validate('abc');
} }
public function test_error_thrown_if_token_signature_not_validated_from_non_matching_key() public function test_error_thrown_if_token_signature_not_validated_from_non_matching_key()
{ {
$token = new OpenIdConnectIdToken($this->idToken(), 'https://auth.example.com', [ $token = new OidcIdToken($this->idToken(), 'https://auth.example.com', [
array_merge($this->jwkKeyArray(), [ array_merge($this->jwkKeyArray(), [
'n' => 'iqK-1QkICMf_cusNLpeNnN-bhT0-9WLBvzgwKLALRbrevhdi5ttrLHIQshaSL0DklzfyG2HWRmAnJ9Q7sweEjuRiiqRcSUZbYu8cIv2hLWYu7K_NH67D2WUjl0EnoHEuiVLsZhQe1CmdyLdx087j5nWkd64K49kXRSdxFQUlj8W3NeK3CjMEUdRQ3H4RZzJ4b7uuMiFA29S2ZhMNG20NPbkUVsFL-jiwTd10KSsPT8yBYipI9O7mWsUWt_8KZs1y_vpM_k3SyYihnWpssdzDm1uOZ8U3mzFr1xsLAO718GNUSXk6npSDzLl59HEqa6zs4O9awO2qnSHvcmyELNk31w' 'n' => 'iqK-1QkICMf_cusNLpeNnN-bhT0-9WLBvzgwKLALRbrevhdi5ttrLHIQshaSL0DklzfyG2HWRmAnJ9Q7sweEjuRiiqRcSUZbYu8cIv2hLWYu7K_NH67D2WUjl0EnoHEuiVLsZhQe1CmdyLdx087j5nWkd64K49kXRSdxFQUlj8W3NeK3CjMEUdRQ3H4RZzJ4b7uuMiFA29S2ZhMNG20NPbkUVsFL-jiwTd10KSsPT8yBYipI9O7mWsUWt_8KZs1y_vpM_k3SyYihnWpssdzDm1uOZ8U3mzFr1xsLAO718GNUSXk6npSDzLl59HEqa6zs4O9awO2qnSHvcmyELNk31w'
]) ])
]); ]);
$this->expectException(InvalidTokenException::class); $this->expectException(OidcInvalidTokenException::class);
$this->expectExceptionMessage('Token signature could not be validated using the provided keys'); $this->expectExceptionMessage('Token signature could not be validated using the provided keys');
$token->validate('abc'); $token->validate('abc');
} }
public function test_error_thrown_if_token_signature_not_validated_from_invalid_key() public function test_error_thrown_if_token_signature_not_validated_from_invalid_key()
{ {
$token = new OpenIdConnectIdToken($this->idToken(), 'https://auth.example.com', ['url://example.com']); $token = new OidcIdToken($this->idToken(), 'https://auth.example.com', ['url://example.com']);
$this->expectException(InvalidTokenException::class); $this->expectException(OidcInvalidTokenException::class);
$this->expectExceptionMessage('Token signature could not be validated using the provided keys'); $this->expectExceptionMessage('Token signature could not be validated using the provided keys');
$token->validate('abc'); $token->validate('abc');
} }
public function test_error_thrown_if_token_algorithm_is_not_rs256() public function test_error_thrown_if_token_algorithm_is_not_rs256()
{ {
$token = new OpenIdConnectIdToken($this->idToken([], ['alg' => 'HS256']), 'https://auth.example.com', []); $token = new OidcIdToken($this->idToken([], ['alg' => 'HS256']), 'https://auth.example.com', []);
$this->expectException(InvalidTokenException::class); $this->expectException(OidcInvalidTokenException::class);
$this->expectExceptionMessage("Only RS256 signature validation is supported. Token reports using HS256"); $this->expectExceptionMessage("Only RS256 signature validation is supported. Token reports using HS256");
$token->validate('abc'); $token->validate('abc');
} }
@ -133,7 +133,7 @@ class OpenIdConnectIdTokenTest extends TestCase
]; ];
foreach ($claimOverridesByErrorMessage as [$message, $overrides]) { foreach ($claimOverridesByErrorMessage as [$message, $overrides]) {
$token = new OpenIdConnectIdToken($this->idToken($overrides), 'https://auth.example.com', [ $token = new OidcIdToken($this->idToken($overrides), 'https://auth.example.com', [
$this->jwkKeyArray() $this->jwkKeyArray()
]); ]);
@ -144,7 +144,7 @@ class OpenIdConnectIdTokenTest extends TestCase
$err = $exception; $err = $exception;
} }
$this->assertInstanceOf(InvalidTokenException::class, $err, $message); $this->assertInstanceOf(OidcInvalidTokenException::class, $err, $message);
$this->assertEquals($message, $err->getMessage()); $this->assertEquals($message, $err->getMessage());
} }
} }
@ -154,7 +154,7 @@ class OpenIdConnectIdTokenTest extends TestCase
$file = tmpfile(); $file = tmpfile();
$testFilePath = 'file://' . stream_get_meta_data($file)['uri']; $testFilePath = 'file://' . stream_get_meta_data($file)['uri'];
file_put_contents($testFilePath, $this->pemKey()); file_put_contents($testFilePath, $this->pemKey());
$token = new OpenIdConnectIdToken($this->idToken(), 'https://auth.example.com', [ $token = new OidcIdToken($this->idToken(), 'https://auth.example.com', [
$testFilePath $testFilePath
]); ]);