]> BookStack Code Mirror - bookstack/blob - app/Access/Oidc/OidcOAuthProvider.php
Opensearch: Fixed XML declaration when php short tags enabled
[bookstack] / app / Access / Oidc / OidcOAuthProvider.php
1 <?php
2
3 namespace BookStack\Access\Oidc;
4
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;
13
14 /**
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.
18  */
19 class OidcOAuthProvider extends AbstractProvider
20 {
21     use BearerAuthorizationTrait;
22
23     protected string $authorizationEndpoint;
24     protected string $tokenEndpoint;
25
26     /**
27      * Scopes to use for the OIDC authorization call.
28      */
29     protected array $scopes = ['openid', 'profile', 'email'];
30
31     /**
32      * Returns the base URL for authorizing a client.
33      */
34     public function getBaseAuthorizationUrl(): string
35     {
36         return $this->authorizationEndpoint;
37     }
38
39     /**
40      * Returns the base URL for requesting an access token.
41      */
42     public function getBaseAccessTokenUrl(array $params): string
43     {
44         return $this->tokenEndpoint;
45     }
46
47     /**
48      * Returns the URL for requesting the resource owner's details.
49      */
50     public function getResourceOwnerDetailsUrl(AccessToken $token): string
51     {
52         return '';
53     }
54
55     /**
56      * Add another scope to this provider upon the default.
57      */
58     public function addScope(string $scope): void
59     {
60         $this->scopes[] = $scope;
61         $this->scopes = array_unique($this->scopes);
62     }
63
64     /**
65      * Returns the default scopes used by this provider.
66      *
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.
69      */
70     protected function getDefaultScopes(): array
71     {
72         return $this->scopes;
73     }
74
75     /**
76      * Returns the string that should be used to separate scopes when building
77      * the URL for requesting an access token.
78      */
79     protected function getScopeSeparator(): string
80     {
81         return ' ';
82     }
83
84     /**
85      * Checks a provider response for errors.
86      * @throws IdentityProviderException
87      */
88     protected function checkResponse(ResponseInterface $response, $data): void
89     {
90         if ($response->getStatusCode() >= 400 || isset($data['error'])) {
91             throw new IdentityProviderException(
92                 $data['error'] ?? $response->getReasonPhrase(),
93                 $response->getStatusCode(),
94                 (string) $response->getBody()
95             );
96         }
97     }
98
99     /**
100      * Generates a resource owner object from a successful resource owner
101      * details request.
102      */
103     protected function createResourceOwner(array $response, AccessToken $token): ResourceOwnerInterface
104     {
105         return new GenericResourceOwner($response, '');
106     }
107
108     /**
109      * Creates an access token from a response.
110      *
111      * The grant that was used to fetch the response can be used to provide
112      * additional context.
113      */
114     protected function createAccessToken(array $response, AbstractGrant $grant): OidcAccessToken
115     {
116         return new OidcAccessToken($response);
117     }
118
119     /**
120      * Get the method used for PKCE code verifier hashing, which is passed
121      * in the "code_challenge_method" parameter in the authorization request.
122      */
123     protected function getPkceMethod(): string
124     {
125         return static::PKCE_METHOD_S256;
126     }
127 }