|
4 | 4 |
|
5 | 5 | namespace Tempest\Auth\OAuth; |
6 | 6 |
|
| 7 | +use BackedEnum; |
| 8 | +use Closure; |
7 | 9 | use League\OAuth2\Client\Provider\AbstractProvider; |
8 | 10 | use League\OAuth2\Client\Provider\Exception\IdentityProviderException; |
9 | 11 | use League\OAuth2\Client\Token\AccessToken; |
| 12 | +use Tempest\Auth\Authentication\Authenticatable; |
| 13 | +use Tempest\Auth\Authentication\Authenticator; |
| 14 | +use Tempest\Auth\Exceptions\OAuthStateWasInvalid; |
10 | 15 | use Tempest\Auth\Exceptions\OAuthTokenCouldNotBeRetrieved; |
11 | 16 | use Tempest\Auth\Exceptions\OAuthUserCouldNotBeRetrieved; |
| 17 | +use Tempest\Http\Request; |
| 18 | +use Tempest\Http\Session\Session; |
12 | 19 | use Tempest\Mapper\ObjectFactory; |
13 | 20 | use Tempest\Router\UriGenerator; |
| 21 | +use UnitEnum; |
14 | 22 |
|
15 | | -final readonly class GenericOAuthClient implements OAuthClient |
| 23 | +final class GenericOAuthClient implements OAuthClient |
16 | 24 | { |
17 | 25 | private AbstractProvider $provider; |
18 | 26 |
|
19 | 27 | public function __construct( |
20 | | - private(set) OAuthConfig $config, |
21 | | - private UriGenerator $uri, |
22 | | - private ObjectFactory $factory, |
| 28 | + private(set) readonly OAuthConfig $config, |
| 29 | + private readonly UriGenerator $uri, |
| 30 | + private readonly ObjectFactory $factory, |
| 31 | + private readonly Session $session, |
| 32 | + private readonly Authenticator $authenticator, |
23 | 33 | ?AbstractProvider $provider = null, |
24 | | - ) { |
| 34 | + ) |
| 35 | + { |
25 | 36 | $this->provider = $provider ?? $this->config->createProvider(); |
26 | 37 | } |
27 | 38 |
|
| 39 | + public string $sessionKey { |
| 40 | + get { |
| 41 | + $tag = $this->config->tag; |
| 42 | + |
| 43 | + $key = match (true) { |
| 44 | + is_string($tag) => $tag, |
| 45 | + $tag instanceof BackedEnum => $tag->value, |
| 46 | + $tag instanceof UnitEnum => $tag->name, |
| 47 | + default => 'default', |
| 48 | + }; |
| 49 | + |
| 50 | + return "oauth:{$key}"; |
| 51 | + } |
| 52 | + } |
| 53 | + |
28 | 54 | public function getAuthorizationUrl(array $scopes = [], array $options = []): string |
29 | 55 | { |
| 56 | + $this->session->set($this->sessionKey, $this->provider->getState()); |
| 57 | + |
30 | 58 | return $this->provider->getAuthorizationUrl([ |
31 | 59 | 'scope' => $scopes ?? $this->config->scopes, |
32 | 60 | 'redirect_uri' => $this->uri->createUri($this->config->redirectTo), |
@@ -69,4 +97,19 @@ public function fetchUser(string $code): OAuthUser |
69 | 97 | token: $this->getAccessToken($code), |
70 | 98 | ); |
71 | 99 | } |
| 100 | + |
| 101 | + public function authenticate(Request $request, Closure $authenticate): Authenticatable |
| 102 | + { |
| 103 | + if ($this->session->get($this->sessionKey) !== $request->get('state')) { |
| 104 | + throw new OAuthStateWasInvalid(); |
| 105 | + } |
| 106 | + |
| 107 | + $user = $this->fetchUser($request->get('code')); |
| 108 | + |
| 109 | + $authenticable = $authenticate($user); |
| 110 | + |
| 111 | + $this->authenticator->authenticate($authenticable); |
| 112 | + |
| 113 | + return $authenticable; |
| 114 | + } |
72 | 115 | } |
0 commit comments