Added theme add social driver redirect configuration callback

Allows someone using the theme system to configure the social driver
before a redirect action occurs, by passing a callback as an additional
param to the theme 'addSocialDriver' method.
This commit is contained in:
Dan Brown 2021-05-24 12:55:45 +01:00
parent 2c3523f6a1
commit c7322a71f7
No known key found for this signature in database
GPG Key ID: 46D9F943C24A2EF9
4 changed files with 84 additions and 11 deletions

View File

@ -19,10 +19,37 @@ use Symfony\Component\HttpFoundation\RedirectResponse;
class SocialAuthService class SocialAuthService
{ {
/**
* The core socialite library used.
* @var Socialite
*/
protected $socialite; protected $socialite;
protected $socialAccount;
protected $validSocialDrivers = ['google', 'github', 'facebook', 'slack', 'twitter', 'azure', 'okta', 'gitlab', 'twitch', 'discord']; /**
* The default built-in social drivers we support.
* @var string[]
*/
protected $validSocialDrivers = [
'google',
'github',
'facebook',
'slack',
'twitter',
'azure',
'okta',
'gitlab',
'twitch',
'discord'
];
/**
* Callbacks to run when configuring a social driver
* for an initial redirect action.
* Array is keyed by social driver name.
* Callbacks are passed an instance of the driver.
* @var array<string, callable>
*/
protected $configureForRedirectCallbacks = [];
/** /**
* SocialAuthService constructor. * SocialAuthService constructor.
@ -39,7 +66,7 @@ class SocialAuthService
public function startLogIn(string $socialDriver): RedirectResponse public function startLogIn(string $socialDriver): RedirectResponse
{ {
$driver = $this->validateDriver($socialDriver); $driver = $this->validateDriver($socialDriver);
return $this->getSocialDriver($driver)->redirect(); return $this->getDriverForRedirect($driver)->redirect();
} }
/** /**
@ -49,7 +76,7 @@ class SocialAuthService
public function startRegister(string $socialDriver): RedirectResponse public function startRegister(string $socialDriver): RedirectResponse
{ {
$driver = $this->validateDriver($socialDriver); $driver = $this->validateDriver($socialDriver);
return $this->getSocialDriver($driver)->redirect(); return $this->getDriverForRedirect($driver)->redirect();
} }
/** /**
@ -227,7 +254,7 @@ class SocialAuthService
/** /**
* Provide redirect options per service for the Laravel Socialite driver * Provide redirect options per service for the Laravel Socialite driver
*/ */
public function getSocialDriver(string $driverName): Provider protected function getDriverForRedirect(string $driverName): Provider
{ {
$driver = $this->socialite->driver($driverName); $driver = $this->socialite->driver($driverName);
@ -238,6 +265,10 @@ class SocialAuthService
$driver->with(['resource' => 'https://graph.windows.net']); $driver->with(['resource' => 'https://graph.windows.net']);
} }
if (isset($this->configureForRedirectCallbacks[$driverName])) {
$this->configureForRedirectCallbacks[$driverName]($driver);
}
return $driver; return $driver;
} }
@ -248,12 +279,19 @@ class SocialAuthService
* within the `Config/services.php` file. * within the `Config/services.php` file.
* Handler should be a Class@method handler to the SocialiteWasCalled event. * Handler should be a Class@method handler to the SocialiteWasCalled event.
*/ */
public function addSocialDriver(string $driverName, array $config, string $socialiteHandler) public function addSocialDriver(
{ string $driverName,
array $config,
string $socialiteHandler,
callable $configureForRedirect = null
) {
$this->validSocialDrivers[] = $driverName; $this->validSocialDrivers[] = $driverName;
config()->set('services.' . $driverName, $config); config()->set('services.' . $driverName, $config);
config()->set('services.' . $driverName . '.redirect', url('/login/service/' . $driverName . '/callback')); config()->set('services.' . $driverName . '.redirect', url('/login/service/' . $driverName . '/callback'));
config()->set('services.' . $driverName . '.name', $config['name'] ?? $driverName); config()->set('services.' . $driverName . '.name', $config['name'] ?? $driverName);
Event::listen(SocialiteWasCalled::class, $socialiteHandler); Event::listen(SocialiteWasCalled::class, $socialiteHandler);
if (!is_null($configureForRedirect)) {
$this->configureForRedirectCallbacks[$driverName] = $configureForRedirect;
}
} }
} }

View File

@ -53,9 +53,9 @@ class ThemeService
/** /**
* @see SocialAuthService::addSocialDriver * @see SocialAuthService::addSocialDriver
*/ */
public function addSocialDriver(string $driverName, array $config, string $socialiteHandler) public function addSocialDriver(string $driverName, array $config, string $socialiteHandler, callable $configureForRedirect = null)
{ {
$socialAuthService = app()->make(SocialAuthService::class); $socialAuthService = app()->make(SocialAuthService::class);
$socialAuthService->addSocialDriver($driverName, $config, $socialiteHandler); $socialAuthService->addSocialDriver($driverName, $config, $socialiteHandler, $configureForRedirect);
} }
} }

View File

@ -96,3 +96,17 @@ Theme::listen(ThemeEvents::APP_BOOT, function($app) {
], '\SocialiteProviders\Reddit\RedditExtendSocialite@handle'); ], '\SocialiteProviders\Reddit\RedditExtendSocialite@handle');
}); });
``` ```
In some cases you may need to customize the driver before it performs a redirect.
This can be done by providing a callback as a fourth parameter like so:
```php
Theme::addSocialDriver('reddit', [
'client_id' => 'abc123',
'client_secret' => 'def456789',
'name' => 'Reddit',
], '\SocialiteProviders\Reddit\RedditExtendSocialite@handle', function($driver) {
$driver->with(['prompt' => 'select_account']);
$driver->scopes(['open_id']);
});
```

View File

@ -1,6 +1,5 @@
<?php namespace Tests; <?php namespace Tests;
use BookStack\Auth\Access\SocialAuthService;
use BookStack\Auth\User; use BookStack\Auth\User;
use BookStack\Entities\Models\Page; use BookStack\Entities\Models\Page;
use BookStack\Entities\Tools\PageContent; use BookStack\Entities\Tools\PageContent;
@ -149,7 +148,7 @@ class ThemeTest extends TestCase
$this->setSettings(['registration-enabled' => 'true']); $this->setSettings(['registration-enabled' => 'true']);
$user = factory(User::class)->make(); $user = factory(User::class)->make();
$this->post('/register', ['email' => $user->email, 'name' => $user->name, 'password' => 'password']); $this->post('/register', ['email' => $user->email, 'name' => $user->name, 'password' => 'password']);
$this->assertCount(2, $args); $this->assertCount(2, $args);
$this->assertEquals('standard', $args[0]); $this->assertEquals('standard', $args[0]);
@ -184,6 +183,28 @@ class ThemeTest extends TestCase
$loginResp->assertSee('Super Cat Name'); $loginResp->assertSee('Super Cat Name');
} }
public function test_add_social_driver_allows_a_configure_for_redirect_callback_to_be_passed()
{
Theme::addSocialDriver(
'discord',
[
'client_id' => 'abc123',
'client_secret' => 'def456',
'name' => 'Super Cat Name',
],
'SocialiteProviders\Discord\DiscordExtendSocialite@handle',
function ($driver) {
$driver->with(['donkey' => 'donut']);
}
);
$loginResp = $this->get('/login/service/discord');
$redirect = $loginResp->headers->get('location');
$this->assertStringContainsString('donkey=donut', $redirect);
}
protected function usingThemeFolder(callable $callback) protected function usingThemeFolder(callable $callback)
{ {
// Create a folder and configure a theme // Create a folder and configure a theme