3 namespace BookStack\Access\Oidc;
5 use League\OAuth2\Client\Grant\AbstractGrant;
6 use League\OAuth2\Client\Provider\AbstractProvider;
7 use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
8 use League\OAuth2\Client\Provider\GenericResourceOwner;
9 use League\OAuth2\Client\Provider\ResourceOwnerInterface;
10 use League\OAuth2\Client\Token\AccessToken;
11 use League\OAuth2\Client\Tool\BearerAuthorizationTrait;
12 use Psr\Http\Message\ResponseInterface;
15 * Extended OAuth2Provider for using with OIDC.
16 * Credit to the https://github.com/steverhoades/oauth2-openid-connect-client
17 * project for the idea of extending a League\OAuth2 client for this use-case.
19 class OidcOAuthProvider extends AbstractProvider
21 use BearerAuthorizationTrait;
23 protected string $authorizationEndpoint;
24 protected string $tokenEndpoint;
27 * Scopes to use for the OIDC authorization call.
29 protected array $scopes = ['openid', 'profile', 'email'];
32 * Returns the base URL for authorizing a client.
34 public function getBaseAuthorizationUrl(): string
36 return $this->authorizationEndpoint;
40 * Returns the base URL for requesting an access token.
42 public function getBaseAccessTokenUrl(array $params): string
44 return $this->tokenEndpoint;
48 * Returns the URL for requesting the resource owner's details.
50 public function getResourceOwnerDetailsUrl(AccessToken $token): string
56 * Add another scope to this provider upon the default.
58 public function addScope(string $scope): void
60 $this->scopes[] = $scope;
61 $this->scopes = array_unique($this->scopes);
65 * Returns the default scopes used by this provider.
67 * This should only be the scopes that are required to request the details
68 * of the resource owner, rather than all the available scopes.
70 protected function getDefaultScopes(): array
76 * Returns the string that should be used to separate scopes when building
77 * the URL for requesting an access token.
79 protected function getScopeSeparator(): string
85 * Checks a provider response for errors.
86 * @throws IdentityProviderException
88 protected function checkResponse(ResponseInterface $response, $data): void
90 if ($response->getStatusCode() >= 400 || isset($data['error'])) {
91 throw new IdentityProviderException(
92 $data['error'] ?? $response->getReasonPhrase(),
93 $response->getStatusCode(),
94 (string) $response->getBody()
100 * Generates a resource owner object from a successful resource owner
103 protected function createResourceOwner(array $response, AccessToken $token): ResourceOwnerInterface
105 return new GenericResourceOwner($response, '');
109 * Creates an access token from a response.
111 * The grant that was used to fetch the response can be used to provide
112 * additional context.
114 protected function createAccessToken(array $response, AbstractGrant $grant): OidcAccessToken
116 return new OidcAccessToken($response);
120 * Get the method used for PKCE code verifier hashing, which is passed
121 * in the "code_challenge_method" parameter in the authorization request.
123 protected function getPkceMethod(): string
125 return static::PKCE_METHOD_S256;