]> BookStack Code Mirror - bookstack/blob - app/Auth/Access/Oidc/OidcOAuthProvider.php
Added OIDC group sync functionality
[bookstack] / app / Auth / Access / Oidc / OidcOAuthProvider.php
1 <?php
2
3 namespace BookStack\Auth\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     /**
24      * @var string
25      */
26     protected $authorizationEndpoint;
27
28     /**
29      * @var string
30      */
31     protected $tokenEndpoint;
32
33     /**
34      * Scopes to use for the OIDC authorization call
35      */
36     protected array $scopes = ['openid', 'profile', 'email'];
37
38     /**
39      * Returns the base URL for authorizing a client.
40      */
41     public function getBaseAuthorizationUrl(): string
42     {
43         return $this->authorizationEndpoint;
44     }
45
46     /**
47      * Returns the base URL for requesting an access token.
48      */
49     public function getBaseAccessTokenUrl(array $params): string
50     {
51         return $this->tokenEndpoint;
52     }
53
54     /**
55      * Returns the URL for requesting the resource owner's details.
56      */
57     public function getResourceOwnerDetailsUrl(AccessToken $token): string
58     {
59         return '';
60     }
61
62     /**
63      * Add an additional scope to this provider upon the default.
64      */
65     public function addScope(string $scope): void
66     {
67         $this->scopes[] = $scope;
68         $this->scopes = array_unique($this->scopes);
69     }
70
71     /**
72      * Returns the default scopes used by this provider.
73      *
74      * This should only be the scopes that are required to request the details
75      * of the resource owner, rather than all the available scopes.
76      */
77     protected function getDefaultScopes(): array
78     {
79         return $this->scopes;
80     }
81
82     /**
83      * Returns the string that should be used to separate scopes when building
84      * the URL for requesting an access token.
85      */
86     protected function getScopeSeparator(): string
87     {
88         return ' ';
89     }
90
91     /**
92      * Checks a provider response for errors.
93      *
94      * @param ResponseInterface $response
95      * @param array|string      $data     Parsed response data
96      *
97      * @throws IdentityProviderException
98      *
99      * @return void
100      */
101     protected function checkResponse(ResponseInterface $response, $data)
102     {
103         if ($response->getStatusCode() >= 400 || isset($data['error'])) {
104             throw new IdentityProviderException(
105                 $data['error'] ?? $response->getReasonPhrase(),
106                 $response->getStatusCode(),
107                 (string) $response->getBody()
108             );
109         }
110     }
111
112     /**
113      * Generates a resource owner object from a successful resource owner
114      * details request.
115      *
116      * @param array       $response
117      * @param AccessToken $token
118      *
119      * @return ResourceOwnerInterface
120      */
121     protected function createResourceOwner(array $response, AccessToken $token)
122     {
123         return new GenericResourceOwner($response, '');
124     }
125
126     /**
127      * Creates an access token from a response.
128      *
129      * The grant that was used to fetch the response can be used to provide
130      * additional context.
131      *
132      * @param array         $response
133      * @param AbstractGrant $grant
134      *
135      * @return OidcAccessToken
136      */
137     protected function createAccessToken(array $response, AbstractGrant $grant)
138     {
139         return new OidcAccessToken($response);
140     }
141 }