diff --git a/config/oauth.php b/config/oauth.php
index 2b5b931b1a..d7e3fd2488 100644
--- a/config/oauth.php
+++ b/config/oauth.php
@@ -15,6 +15,44 @@
'callback' => 'oauth/{provider}/callback',
],
+ /*
+ |--------------------------------------------------------------------------
+ | Create User
+ |--------------------------------------------------------------------------
+ |
+ | Whether or not a user account should be created upon authentication
+ | with an OAuth provider. If disabled, a user account will be need
+ | to be explicitly created ahead of time.
+ |
+ */
+
+ 'create_user' => true,
+
+ /*
+ |--------------------------------------------------------------------------
+ | Merge User Data
+ |--------------------------------------------------------------------------
+ |
+ | When authenticating with an OAuth provider, the user data returned
+ | such as their name will be merged with the existing user account.
+ |
+ */
+
+ 'merge_user_data' => true,
+
+ /*
+ |--------------------------------------------------------------------------
+ | Unauthorized Redirect
+ |--------------------------------------------------------------------------
+ |
+ | This controls where the user is taken after authenticating with
+ | an OAuth provider but their account is unauthorized. This may
+ | happen when the create_user option has been set to false.
+ |
+ */
+
+ 'unauthorized_redirect' => null,
+
/*
|--------------------------------------------------------------------------
| Remember Me
diff --git a/resources/views/auth/unauthorized.blade.php b/resources/views/auth/unauthorized.blade.php
index 3502faa78b..d781cefc79 100644
--- a/resources/views/auth/unauthorized.blade.php
+++ b/resources/views/auth/unauthorized.blade.php
@@ -10,7 +10,11 @@
diff --git a/src/Http/Controllers/OAuthController.php b/src/Http/Controllers/OAuthController.php
index 880a6ea922..ac98813dfa 100644
--- a/src/Http/Controllers/OAuthController.php
+++ b/src/Http/Controllers/OAuthController.php
@@ -36,14 +36,24 @@ public function handleProviderCallback(Request $request, string $provider)
return $this->redirectToProvider($request, $provider);
}
- $user = $oauth->findOrCreateUser($providerUser);
+ if ($user = $oauth->findUser($providerUser)) {
+ if (config('statamic.oauth.merge_user_data', true)) {
+ $user = $oauth->mergeUser($user, $providerUser);
+ }
+ } elseif (config('statamic.oauth.create_user', true)) {
+ $user = $oauth->createUser($providerUser);
+ }
+
+ if ($user) {
+ session()->put('oauth-provider', $provider);
- session()->put('oauth-provider', $provider);
+ Auth::guard($request->session()->get('statamic.oauth.guard'))
+ ->login($user, config('statamic.oauth.remember_me', true));
- Auth::guard($request->session()->get('statamic.oauth.guard'))
- ->login($user, config('statamic.oauth.remember_me', true));
+ return redirect()->to($this->successRedirectUrl());
+ }
- return redirect()->to($this->successRedirectUrl());
+ return redirect()->to($this->unauthorizedRedirectUrl());
}
protected function successRedirectUrl()
@@ -60,4 +70,31 @@ protected function successRedirectUrl()
return Arr::get($query, 'redirect', $default);
}
+
+ protected function unauthorizedRedirectUrl()
+ {
+ // If a URL has been explicitly defined, use that.
+ if ($url = config('statamic.oauth.unauthorized_redirect')) {
+ return $url;
+ }
+
+ // We'll check the redirect to see if they were intending on
+ // accessing the CP. If they were, we'll redirect them to
+ // the unauthorized page in the CP. Otherwise, to home.
+
+ $default = '/';
+ $previous = session('_previous.url');
+
+ if (! $query = Arr::get(parse_url($previous), 'query')) {
+ return $default;
+ }
+
+ parse_str($query, $query);
+
+ if (! $redirect = Arr::get($query, 'redirect')) {
+ return $default;
+ }
+
+ return $redirect === '/'.config('statamic.cp.route') ? cp_route('unauthorized') : $default;
+ }
}
diff --git a/src/OAuth/Provider.php b/src/OAuth/Provider.php
index b200052228..0aeafdf8fe 100644
--- a/src/OAuth/Provider.php
+++ b/src/OAuth/Provider.php
@@ -45,15 +45,31 @@ public function getUserId(string $id): ?string
}
public function findOrCreateUser($socialite): StatamicUser
+ {
+ if ($user = $this->findUser($socialite)) {
+ return config('statamic.oauth.merge_user_data', true)
+ ? $this->mergeUser($user, $socialite)
+ : $user;
+ }
+
+ return $this->createUser($socialite);
+ }
+
+ /**
+ * Find a Statamic user by a Socialite user.
+ *
+ * @param SocialiteUser $socialite
+ */
+ public function findUser($socialite): ?StatamicUser
{
if (
($user = User::findByOAuthId($this, $socialite->getId())) ||
($user = User::findByEmail($socialite->getEmail()))
) {
- return $this->mergeUser($user, $socialite);
+ return $user;
}
- return $this->createUser($socialite);
+ return null;
}
/**
diff --git a/tests/OAuth/ProviderTest.php b/tests/OAuth/ProviderTest.php
index e5d2edecd9..917aba151e 100644
--- a/tests/OAuth/ProviderTest.php
+++ b/tests/OAuth/ProviderTest.php
@@ -74,6 +74,8 @@ public function it_merges_data()
$user = $this->user()->save();
+ $this->assertEquals(['name' => 'foo', 'extra' => 'bar'], $user->data()->all());
+
$provider->mergeUser($user, $this->socialite());
$this->assertEquals(['name' => 'Foo Bar', 'extra' => 'bar'], $user->data()->all());
@@ -122,20 +124,91 @@ public function it_creates_a_user()
}
#[Test]
- public function it_finds_an_existing_user_by_email()
+ public function it_finds_an_existing_user_via_find_user_method()
+ {
+ $provider = $this->provider();
+
+ $savedUser = $this->user()->save();
+
+ $this->assertCount(1, UserFacade::all());
+ $this->assertEquals([$savedUser], UserFacade::all()->all());
+
+ $foundUser = $provider->findUser($this->socialite());
+
+ $this->assertCount(1, UserFacade::all());
+ $this->assertEquals([$savedUser], UserFacade::all()->all());
+ $this->assertEquals($savedUser, $foundUser);
+ }
+
+ #[Test]
+ public function it_does_not_find_or_create_a_user_via_find_user_method()
+ {
+ $this->assertCount(0, UserFacade::all());
+
+ $provider = $this->provider();
+ $foundUser = $provider->findUser($this->socialite());
+
+ $this->assertNull($foundUser);
+
+ $this->assertCount(0, UserFacade::all());
+ $user = UserFacade::all()->get(0);
+ $this->assertNull($user);
+ }
+
+ #[Test]
+ public function it_finds_an_existing_user_via_find_or_create_user_method()
+ {
+ $provider = $this->provider();
+
+ $savedUser = $this->user()->save();
+
+ $this->assertCount(1, UserFacade::all());
+ $this->assertEquals([$savedUser], UserFacade::all()->all());
+ $this->assertEquals('foo', $savedUser->name);
+
+ $foundUser = $provider->findOrCreateUser($this->socialite());
+
+ $this->assertCount(1, UserFacade::all());
+ $this->assertEquals([$savedUser], UserFacade::all()->all());
+ $this->assertEquals($savedUser, $foundUser);
+ $this->assertEquals('Foo Bar', $savedUser->name);
+ }
+
+ #[Test]
+ public function it_finds_an_existing_user_via_find_or_create_user_method_but_doesnt_merge_data()
{
+ config(['statamic.oauth.merge_user_data' => false]);
+
$provider = $this->provider();
$savedUser = $this->user()->save();
$this->assertCount(1, UserFacade::all());
$this->assertEquals([$savedUser], UserFacade::all()->all());
+ $this->assertEquals('foo', $savedUser->name);
$foundUser = $provider->findOrCreateUser($this->socialite());
$this->assertCount(1, UserFacade::all());
$this->assertEquals([$savedUser], UserFacade::all()->all());
$this->assertEquals($savedUser, $foundUser);
+ $this->assertEquals('foo', $savedUser->name);
+ }
+
+ #[Test]
+ public function it_creates_a_user_via_find_or_create_user_method()
+ {
+ $this->assertCount(0, UserFacade::all());
+
+ $provider = $this->provider();
+ $provider->findOrCreateUser($this->socialite());
+
+ $this->assertCount(1, UserFacade::all());
+ $user = UserFacade::all()->get(0);
+ $this->assertNotNull($user);
+ $this->assertEquals('foo@bar.com', $user->email());
+ $this->assertEquals('Foo Bar', $user->name());
+ $this->assertEquals($user->id(), $provider->getUserId('foo-bar'));
}
#[Test]