mirror of
https://github.com/pelican-dev/panel.git
synced 2025-05-19 19:54:45 +02:00
OAuth improvements (#903)
* rework oauth provider creation & lodaing * add separate setup form * use wizard for setup * add provider class for discord * cleanup and fixes * don't throw exception when creating duplicate provider * update profile and login pages * did not mean to remove the whole else, oops * use import
This commit is contained in:
parent
885e03ee06
commit
4ec9171017
65
app/Extensions/OAuth/Providers/AuthentikProvider.php
Normal file
65
app/Extensions/OAuth/Providers/AuthentikProvider.php
Normal file
@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace App\Extensions\OAuth\Providers;
|
||||
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use SocialiteProviders\Authentik\Provider;
|
||||
|
||||
final class AuthentikProvider extends OAuthProvider
|
||||
{
|
||||
public function getId(): string
|
||||
{
|
||||
return 'authentik';
|
||||
}
|
||||
|
||||
public function getProviderClass(): string
|
||||
{
|
||||
return Provider::class;
|
||||
}
|
||||
|
||||
public function getServiceConfig(): array
|
||||
{
|
||||
return [
|
||||
'client_id' => null,
|
||||
'client_secret' => env('OAUTH_STEAM_CLIENT_SECRET'),
|
||||
'allowed_hosts' => [
|
||||
str_replace(['http://', 'https://'], '', env('APP_URL')),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function getSettingsForm(): array
|
||||
{
|
||||
return array_merge(parent::getSettingsForm(), [
|
||||
TextInput::make('OAUTH_AUTHENTIK_BASE_URL')
|
||||
->label('Base URL')
|
||||
->placeholder('Base URL')
|
||||
->columnSpan(2)
|
||||
->required()
|
||||
->url()
|
||||
->autocomplete(false)
|
||||
->default(env('OAUTH_AUTHENTIK_BASE_URL')),
|
||||
TextInput::make('OAUTH_AUTHENTIK_DISPLAY_NAME')
|
||||
->label('Display Name')
|
||||
->placeholder('Display Name')
|
||||
->columnSpan(2)
|
||||
->autocomplete(false)
|
||||
->default(env('OAUTH_AUTHENTIK_DISPLAY_NAME', 'Authentik')),
|
||||
]);
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return env('OAUTH_AUTHENTIK_DISPLAY_NAME') ?? 'Authentik';
|
||||
}
|
||||
|
||||
public function getHexColor(): string
|
||||
{
|
||||
return '#fd4b2d';
|
||||
}
|
||||
|
||||
public static function register(): self
|
||||
{
|
||||
return new self();
|
||||
}
|
||||
}
|
36
app/Extensions/OAuth/Providers/CommonProvider.php
Normal file
36
app/Extensions/OAuth/Providers/CommonProvider.php
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace App\Extensions\OAuth\Providers;
|
||||
|
||||
final class CommonProvider extends OAuthProvider
|
||||
{
|
||||
protected function __construct(private string $id, private ?string $providerClass, private ?string $icon, private ?string $hexColor)
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getProviderClass(): ?string
|
||||
{
|
||||
return $this->providerClass;
|
||||
}
|
||||
|
||||
public function getIcon(): ?string
|
||||
{
|
||||
return $this->icon;
|
||||
}
|
||||
|
||||
public function getHexColor(): ?string
|
||||
{
|
||||
return $this->hexColor;
|
||||
}
|
||||
|
||||
public static function register(string $id, ?string $providerClass = null, ?string $icon = null, ?string $hexColor = null): static
|
||||
{
|
||||
return new self($id, $providerClass, $icon, $hexColor);
|
||||
}
|
||||
}
|
57
app/Extensions/OAuth/Providers/DiscordProvider.php
Normal file
57
app/Extensions/OAuth/Providers/DiscordProvider.php
Normal file
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace App\Extensions\OAuth\Providers;
|
||||
|
||||
use Filament\Forms\Components\Placeholder;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Components\Wizard\Step;
|
||||
use Illuminate\Support\HtmlString;
|
||||
use Illuminate\Support\Str;
|
||||
use SocialiteProviders\Discord\Provider;
|
||||
use Webbingbrasil\FilamentCopyActions\Forms\Actions\CopyAction;
|
||||
|
||||
final class DiscordProvider extends OAuthProvider
|
||||
{
|
||||
public function getId(): string
|
||||
{
|
||||
return 'discord';
|
||||
}
|
||||
|
||||
public function getProvider(): string
|
||||
{
|
||||
return Provider::class;
|
||||
}
|
||||
|
||||
public function getSetupSteps(): array
|
||||
{
|
||||
return array_merge([
|
||||
Step::make('Register new Discord OAuth App')
|
||||
->schema([
|
||||
Placeholder::make('')
|
||||
->content(new HtmlString('<p>Visit the <u><a href="https://discord.com/developers/applications" target="_blank">Discord Developer Portal</a></u> and click on <b>New Application</b>. Enter a <b>Name</b> (e.g. your panel name) and click on <b>Create</b>.</p><p>Copy the <b>Client ID</b> and the <b>Client Secret</b>, you will need them in the final step.</p>')),
|
||||
Placeholder::make('')
|
||||
->content(new HtmlString('<p>Under <b>Redirects</b> add the below URL.</p>')),
|
||||
TextInput::make('_noenv_redirect')
|
||||
->label('Redirect URL')
|
||||
->disabled()
|
||||
->hintAction(CopyAction::make())
|
||||
->formatStateUsing(fn () => config('app.url') . (Str::endsWith(config('app.url'), '/') ? '' : '/') . 'auth/oauth/callback/discord'),
|
||||
]),
|
||||
], parent::getSetupSteps());
|
||||
}
|
||||
|
||||
public function getIcon(): string
|
||||
{
|
||||
return 'tabler-brand-discord-f';
|
||||
}
|
||||
|
||||
public function getHexColor(): string
|
||||
{
|
||||
return '#5865F2';
|
||||
}
|
||||
|
||||
public static function register(): self
|
||||
{
|
||||
return new self();
|
||||
}
|
||||
}
|
56
app/Extensions/OAuth/Providers/GithubProvider.php
Normal file
56
app/Extensions/OAuth/Providers/GithubProvider.php
Normal file
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace App\Extensions\OAuth\Providers;
|
||||
|
||||
use Filament\Forms\Components\Placeholder;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Components\Wizard\Step;
|
||||
use Illuminate\Support\HtmlString;
|
||||
use Illuminate\Support\Str;
|
||||
use Webbingbrasil\FilamentCopyActions\Forms\Actions\CopyAction;
|
||||
|
||||
final class GithubProvider extends OAuthProvider
|
||||
{
|
||||
public function getId(): string
|
||||
{
|
||||
return 'github';
|
||||
}
|
||||
|
||||
public function getSetupSteps(): array
|
||||
{
|
||||
return array_merge([
|
||||
Step::make('Register new Github OAuth App')
|
||||
->schema([
|
||||
Placeholder::make('')
|
||||
->content(new HtmlString('<p>Visit the <u><a href="https://github.com/settings/developers" target="_blank">Github Developer Dashboard</a></u>, go to <b>OAuth Apps</b> and click on <b>New OAuth App</b>.</p><p>Enter an <b>Application name</b> (e.g. your panel name), set <b>Homepage URL</b> to your panel url and enter the below url as <b>Authorization callback URL</b>.</p>')),
|
||||
TextInput::make('_noenv_callback')
|
||||
->label('Authorization callback URL')
|
||||
->disabled()
|
||||
->hintAction(CopyAction::make())
|
||||
->default(fn () => config('app.url') . (Str::endsWith(config('app.url'), '/') ? '' : '/') . 'auth/oauth/callback/github'),
|
||||
Placeholder::make('')
|
||||
->content(new HtmlString('<p>When you filled all fields click on <b>Register application</b>.</p>')),
|
||||
]),
|
||||
Step::make('Create Client Secret')
|
||||
->schema([
|
||||
Placeholder::make('')
|
||||
->content(new HtmlString('<p>Once you registered your app, generate a new <b>Client Secret</b>.</p><p>You will also need the <b>Client ID</b>.</p>')),
|
||||
]),
|
||||
], parent::getSetupSteps());
|
||||
}
|
||||
|
||||
public function getIcon(): string
|
||||
{
|
||||
return 'tabler-brand-github-f';
|
||||
}
|
||||
|
||||
public function getHexColor(): string
|
||||
{
|
||||
return '#4078c0';
|
||||
}
|
||||
|
||||
public static function register(): self
|
||||
{
|
||||
return new self();
|
||||
}
|
||||
}
|
112
app/Extensions/OAuth/Providers/OAuthProvider.php
Normal file
112
app/Extensions/OAuth/Providers/OAuthProvider.php
Normal file
@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
namespace App\Extensions\OAuth\Providers;
|
||||
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Components\Wizard\Step;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Illuminate\Support\Str;
|
||||
use SocialiteProviders\Manager\SocialiteWasCalled;
|
||||
|
||||
abstract class OAuthProvider
|
||||
{
|
||||
protected static array $providers = [];
|
||||
|
||||
public static function get(?string $id = null): array|self
|
||||
{
|
||||
return $id ? static::$providers[$id] : static::$providers;
|
||||
}
|
||||
|
||||
protected function __construct()
|
||||
{
|
||||
if (array_key_exists($this->getId(), static::$providers)) {
|
||||
logger()->warning("Tried to create duplicate OAuth provider with id '{$this->getId()}'");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
config()->set('services.' . $this->getId(), array_merge($this->getServiceConfig(), ['redirect' => '/auth/oauth/callback/' . $this->getId()]));
|
||||
|
||||
if ($this->getProviderClass()) {
|
||||
Event::listen(function (SocialiteWasCalled $event) {
|
||||
$event->extendSocialite($this->getId(), $this->getProviderClass());
|
||||
});
|
||||
}
|
||||
|
||||
static::$providers[$this->getId()] = $this;
|
||||
}
|
||||
|
||||
abstract public function getId(): string;
|
||||
|
||||
public function getProviderClass(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getServiceConfig(): array
|
||||
{
|
||||
$id = Str::upper($this->getId());
|
||||
|
||||
return [
|
||||
'client_id' => env("OAUTH_{$id}_CLIENT_ID"),
|
||||
'client_secret' => env("OAUTH_{$id}_CLIENT_SECRET"),
|
||||
];
|
||||
}
|
||||
|
||||
public function getSettingsForm(): array
|
||||
{
|
||||
$id = Str::upper($this->getId());
|
||||
|
||||
return [
|
||||
TextInput::make("OAUTH_{$id}_CLIENT_ID")
|
||||
->label('Client ID')
|
||||
->placeholder('Client ID')
|
||||
->columnSpan(2)
|
||||
->required()
|
||||
->password()
|
||||
->revealable()
|
||||
->autocomplete(false)
|
||||
->default(env("OAUTH_{$id}_CLIENT_ID")),
|
||||
TextInput::make("OAUTH_{$id}_CLIENT_SECRET")
|
||||
->label('Client Secret')
|
||||
->placeholder('Client Secret')
|
||||
->columnSpan(2)
|
||||
->required()
|
||||
->password()
|
||||
->revealable()
|
||||
->autocomplete(false)
|
||||
->default(env("OAUTH_{$id}_CLIENT_SECRET")),
|
||||
];
|
||||
}
|
||||
|
||||
public function getSetupSteps(): array
|
||||
{
|
||||
return [
|
||||
Step::make('OAuth Config')
|
||||
->columns(4)
|
||||
->schema($this->getSettingsForm()),
|
||||
];
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return Str::title($this->getId());
|
||||
}
|
||||
|
||||
public function getIcon(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getHexColor(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function isEnabled(): bool
|
||||
{
|
||||
$id = Str::upper($this->getId());
|
||||
|
||||
return env("OAUTH_{$id}_ENABLED", false);
|
||||
}
|
||||
}
|
74
app/Extensions/OAuth/Providers/SteamProvider.php
Normal file
74
app/Extensions/OAuth/Providers/SteamProvider.php
Normal file
@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace App\Extensions\OAuth\Providers;
|
||||
|
||||
use Filament\Forms\Components\Placeholder;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Components\Wizard\Step;
|
||||
use Illuminate\Support\HtmlString;
|
||||
use SocialiteProviders\Steam\Provider;
|
||||
|
||||
final class SteamProvider extends OAuthProvider
|
||||
{
|
||||
public function getId(): string
|
||||
{
|
||||
return 'steam';
|
||||
}
|
||||
|
||||
public function getProviderClass(): string
|
||||
{
|
||||
return Provider::class;
|
||||
}
|
||||
|
||||
public function getServiceConfig(): array
|
||||
{
|
||||
return [
|
||||
'client_id' => null,
|
||||
'client_secret' => env('OAUTH_STEAM_CLIENT_SECRET'),
|
||||
'allowed_hosts' => [
|
||||
str_replace(['http://', 'https://'], '', env('APP_URL')),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function getSettingsForm(): array
|
||||
{
|
||||
return [
|
||||
TextInput::make('OAUTH_STEAM_CLIENT_SECRET')
|
||||
->label('Web API Key')
|
||||
->placeholder('Web API Key')
|
||||
->columnSpan(4)
|
||||
->required()
|
||||
->password()
|
||||
->revealable()
|
||||
->autocomplete(false)
|
||||
->default(env('OAUTH_STEAM_CLIENT_SECRET')),
|
||||
];
|
||||
}
|
||||
|
||||
public function getSetupSteps(): array
|
||||
{
|
||||
return array_merge([
|
||||
Step::make('Create API Key')
|
||||
->schema([
|
||||
Placeholder::make('')
|
||||
->content(new HtmlString('Visit <u><a href="https://steamcommunity.com/dev/apikey" target="_blank">https://steamcommunity.com/dev/apikey</a></u> to generate an API key.')),
|
||||
]),
|
||||
], parent::getSetupSteps());
|
||||
}
|
||||
|
||||
public function getIcon(): string
|
||||
{
|
||||
return 'tabler-brand-steam-f';
|
||||
}
|
||||
|
||||
public function getHexColor(): string
|
||||
{
|
||||
return '#00adee';
|
||||
}
|
||||
|
||||
public static function register(): self
|
||||
{
|
||||
return new self();
|
||||
}
|
||||
}
|
@ -2,12 +2,16 @@
|
||||
|
||||
namespace App\Filament\Admin\Pages;
|
||||
|
||||
use App\Extensions\OAuth\Providers\OAuthProvider;
|
||||
use App\Models\Backup;
|
||||
use App\Notifications\MailTested;
|
||||
use App\Traits\EnvironmentWriterTrait;
|
||||
use Exception;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Forms\Components\Actions;
|
||||
use Filament\Forms\Components\Actions\Action as FormAction;
|
||||
use Filament\Forms\Components\Group;
|
||||
use Filament\Forms\Components\Hidden;
|
||||
use Filament\Forms\Components\Placeholder;
|
||||
use Filament\Forms\Components\Section;
|
||||
use Filament\Forms\Components\Select;
|
||||
@ -28,9 +32,9 @@ use Filament\Pages\Page;
|
||||
use Filament\Support\Enums\MaxWidth;
|
||||
use Illuminate\Http\Client\Factory;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Illuminate\Support\Facades\Notification as MailNotification;
|
||||
use Illuminate\Support\HtmlString;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* @property Form $form
|
||||
@ -461,67 +465,55 @@ class Settings extends Page implements HasForms
|
||||
|
||||
private function oauthSettings(): array
|
||||
{
|
||||
$oauthProviders = Config::get('auth.oauth');
|
||||
|
||||
$formFields = [];
|
||||
|
||||
foreach ($oauthProviders as $providerName => $providerConfig) {
|
||||
$providerEnvPrefix = strtoupper($providerName);
|
||||
$oauthProviders = OAuthProvider::get();
|
||||
foreach ($oauthProviders as $oauthProvider) {
|
||||
$id = Str::upper($oauthProvider->getId());
|
||||
$name = Str::title($oauthProvider->getId());
|
||||
|
||||
$fields = [
|
||||
Toggle::make("OAUTH_{$providerEnvPrefix}_ENABLED")
|
||||
->onColor('success')
|
||||
->offColor('danger')
|
||||
->onIcon('tabler-check')
|
||||
->offIcon('tabler-x')
|
||||
->live()
|
||||
->columnSpan(1)
|
||||
->label('Enabled')
|
||||
->default(env("OAUTH_{$providerEnvPrefix}_ENABLED", false)),
|
||||
];
|
||||
|
||||
if (array_key_exists('client_id', $providerConfig['service'] ?? [])) {
|
||||
$fields[] = TextInput::make("OAUTH_{$providerEnvPrefix}_CLIENT_ID")
|
||||
->label('Client ID')
|
||||
->columnSpan(2)
|
||||
->required()
|
||||
->password()
|
||||
->revealable()
|
||||
->autocomplete(false)
|
||||
->hidden(fn (Get $get) => !$get("OAUTH_{$providerEnvPrefix}_ENABLED"))
|
||||
->default(env("OAUTH_{$providerEnvPrefix}_CLIENT_ID", $providerConfig['service']['client_id'] ?? ''))
|
||||
->placeholder('Client ID');
|
||||
}
|
||||
|
||||
if (array_key_exists('client_secret', $providerConfig['service'] ?? [])) {
|
||||
$fields[] = TextInput::make("OAUTH_{$providerEnvPrefix}_CLIENT_SECRET")
|
||||
->label('Client Secret')
|
||||
->columnSpan(2)
|
||||
->required()
|
||||
->password()
|
||||
->revealable()
|
||||
->autocomplete(false)
|
||||
->hidden(fn (Get $get) => !$get("OAUTH_{$providerEnvPrefix}_ENABLED"))
|
||||
->default(env("OAUTH_{$providerEnvPrefix}_CLIENT_SECRET", $providerConfig['service']['client_secret'] ?? ''))
|
||||
->placeholder('Client Secret');
|
||||
}
|
||||
|
||||
if (array_key_exists('base_url', $providerConfig['service'] ?? [])) {
|
||||
$fields[] = TextInput::make("OAUTH_{$providerEnvPrefix}_BASE_URL")
|
||||
->label('Base URL')
|
||||
->columnSpanFull()
|
||||
->autocomplete(false)
|
||||
->hidden(fn (Get $get) => !$get("OAUTH_{$providerEnvPrefix}_ENABLED"))
|
||||
->default(env("OAUTH_{$providerEnvPrefix}_BASE_URL", ''))
|
||||
->placeholder('Base URL');
|
||||
}
|
||||
|
||||
$formFields[] = Section::make(ucfirst($providerName))
|
||||
$formFields[] = Section::make($name)
|
||||
->columns(5)
|
||||
->icon($providerConfig['icon'] ?? 'tabler-brand-oauth')
|
||||
->collapsed(fn () => !env("OAUTH_{$providerEnvPrefix}_ENABLED", false))
|
||||
->icon($oauthProvider->getIcon() ?? 'tabler-brand-oauth')
|
||||
->collapsed(fn () => !env("OAUTH_{$id}_ENABLED", false))
|
||||
->collapsible()
|
||||
->schema($fields);
|
||||
->schema([
|
||||
Hidden::make("OAUTH_{$id}_ENABLED")
|
||||
->live()
|
||||
->default(env("OAUTH_{$id}_ENABLED")),
|
||||
Actions::make([
|
||||
FormAction::make("disable_oauth_$id")
|
||||
->visible(fn (Get $get) => $get("OAUTH_{$id}_ENABLED"))
|
||||
->label('Disable')
|
||||
->color('danger')
|
||||
->action(function (Set $set) use ($id) {
|
||||
$set("OAUTH_{$id}_ENABLED", false);
|
||||
}),
|
||||
FormAction::make("enable_oauth_$id")
|
||||
->visible(fn (Get $get) => !$get("OAUTH_{$id}_ENABLED"))
|
||||
->label('Enable')
|
||||
->color('success')
|
||||
->steps($oauthProvider->getSetupSteps())
|
||||
->modalHeading("Enable $name")
|
||||
->modalSubmitActionLabel('Enable')
|
||||
->modalCancelAction(false)
|
||||
->action(function ($data, Set $set) use ($id) {
|
||||
$data = array_merge([
|
||||
"OAUTH_{$id}_ENABLED" => 'true',
|
||||
], $data);
|
||||
|
||||
$data = array_filter($data, fn ($value) => !Str::startsWith($value, '_noenv'));
|
||||
|
||||
foreach ($data as $key => $value) {
|
||||
$set($key, $value);
|
||||
}
|
||||
}),
|
||||
])->columnSpan(1),
|
||||
Group::make($oauthProvider->getSettingsForm())
|
||||
->visible(fn (Get $get) => $get("OAUTH_{$id}_ENABLED"))
|
||||
->columns(4)
|
||||
->columnSpan(4),
|
||||
]);
|
||||
}
|
||||
|
||||
return $formFields;
|
||||
|
@ -3,6 +3,7 @@
|
||||
namespace App\Filament\Pages\Auth;
|
||||
|
||||
use App\Exceptions\Service\User\TwoFactorAuthenticationTokenInvalid;
|
||||
use App\Extensions\OAuth\Providers\OAuthProvider;
|
||||
use App\Facades\Activity;
|
||||
use App\Models\ActivityLog;
|
||||
use App\Models\ApiKey;
|
||||
@ -30,13 +31,13 @@ use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Get;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Pages\Auth\EditProfile as BaseEditProfile;
|
||||
use Filament\Support\Colors\Color;
|
||||
use Filament\Support\Enums\MaxWidth;
|
||||
use Filament\Support\Exceptions\Halt;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\HtmlString;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Validation\Rules\Password;
|
||||
use Laravel\Socialite\Facades\Socialite;
|
||||
|
||||
@ -126,8 +127,9 @@ class EditProfile extends BaseEditProfile
|
||||
Tab::make('OAuth')
|
||||
->icon('tabler-brand-oauth')
|
||||
->visible(function () {
|
||||
foreach (config('auth.oauth') as $name => $data) {
|
||||
if ($data['enabled']) {
|
||||
$oauthProviders = OAuthProvider::get();
|
||||
foreach ($oauthProviders as $oauthProvider) {
|
||||
if ($oauthProvider->isEnabled()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -135,23 +137,27 @@ class EditProfile extends BaseEditProfile
|
||||
return false;
|
||||
})
|
||||
->schema(function () {
|
||||
$providers = [];
|
||||
$actions = [];
|
||||
|
||||
foreach (config('auth.oauth') as $name => $data) {
|
||||
if (!$data['enabled']) {
|
||||
$oauthProviders = OAuthProvider::get();
|
||||
foreach ($oauthProviders as $oauthProvider) {
|
||||
if (!$oauthProvider->isEnabled()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$unlink = array_key_exists($name, $this->getUser()->oauth ?? []);
|
||||
$id = $oauthProvider->getId();
|
||||
$name = $oauthProvider->getName();
|
||||
|
||||
$providers[] = Action::make("oauth_$name")
|
||||
->label(($unlink ? 'Unlink ' : 'Link ') . Str::title($name))
|
||||
$unlink = array_key_exists($id, $this->getUser()->oauth ?? []);
|
||||
|
||||
$actions[] = Action::make("oauth_$id")
|
||||
->label(($unlink ? 'Unlink ' : 'Link ') . $name)
|
||||
->icon($unlink ? 'tabler-unlink' : 'tabler-link')
|
||||
->color($data['color'])
|
||||
->action(function (UserUpdateService $updateService) use ($name, $unlink) {
|
||||
->color(Color::hex($oauthProvider->getHexColor()))
|
||||
->action(function (UserUpdateService $updateService) use ($id, $name, $unlink) {
|
||||
if ($unlink) {
|
||||
$oauth = auth()->user()->oauth;
|
||||
unset($oauth[$name]);
|
||||
unset($oauth[$id]);
|
||||
|
||||
$updateService->handle(auth()->user(), ['oauth' => $oauth]);
|
||||
|
||||
@ -161,13 +167,13 @@ class EditProfile extends BaseEditProfile
|
||||
->title("OAuth provider '$name' unlinked")
|
||||
->success()
|
||||
->send();
|
||||
} elseif (config("auth.oauth.$name.enabled")) {
|
||||
} else {
|
||||
redirect(Socialite::with($name)->redirect()->getTargetUrl());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return [Actions::make($providers)];
|
||||
return [Actions::make($actions)];
|
||||
}),
|
||||
|
||||
Tab::make('2FA')
|
||||
|
@ -2,13 +2,14 @@
|
||||
|
||||
namespace App\Filament\Pages\Auth;
|
||||
|
||||
use App\Extensions\OAuth\Providers\OAuthProvider;
|
||||
use Coderflex\FilamentTurnstile\Forms\Components\Turnstile;
|
||||
use Filament\Forms\Components\Actions;
|
||||
use Filament\Forms\Components\Actions\Action;
|
||||
use Filament\Forms\Components\Component;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Pages\Auth\Login as BaseLogin;
|
||||
use Illuminate\Support\Str;
|
||||
use Filament\Support\Colors\Color;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
class Login extends BaseLogin
|
||||
@ -57,16 +58,19 @@ class Login extends BaseLogin
|
||||
{
|
||||
$actions = [];
|
||||
|
||||
foreach (config('auth.oauth') as $name => $data) {
|
||||
if (!$data['enabled']) {
|
||||
$oauthProviders = OAuthProvider::get();
|
||||
foreach ($oauthProviders as $oauthProvider) {
|
||||
if (!$oauthProvider->isEnabled()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$actions[] = Action::make("oauth_$name")
|
||||
->label(Str::title($name))
|
||||
->icon($data['icon'])
|
||||
->color($data['color'])
|
||||
->url(route('auth.oauth.redirect', ['driver' => $name], false));
|
||||
$id = $oauthProvider->getId();
|
||||
|
||||
$actions[] = Action::make("oauth_$id")
|
||||
->label($oauthProvider->getName())
|
||||
->icon($oauthProvider->getIcon())
|
||||
->color(Color::hex($oauthProvider->getHexColor()))
|
||||
->url(route('auth.oauth.redirect', ['driver' => $id], false));
|
||||
}
|
||||
|
||||
return Actions::make($actions);
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Extensions\OAuth\Providers\OAuthProvider;
|
||||
use App\Filament\Pages\Auth\EditProfile;
|
||||
use Filament\Notifications\Notification;
|
||||
use Illuminate\Auth\AuthManager;
|
||||
@ -29,7 +30,7 @@ class OAuthController extends Controller
|
||||
protected function redirect(string $driver): RedirectResponse
|
||||
{
|
||||
// Driver is disabled - redirect to normal login
|
||||
if (!config("auth.oauth.$driver.enabled")) {
|
||||
if (!OAuthProvider::get($driver)->isEnabled()) {
|
||||
return redirect()->route('auth.login');
|
||||
}
|
||||
|
||||
@ -42,7 +43,7 @@ class OAuthController extends Controller
|
||||
protected function callback(Request $request, string $driver): RedirectResponse
|
||||
{
|
||||
// Driver is disabled - redirect to normal login
|
||||
if (!config("auth.oauth.$driver.enabled")) {
|
||||
if (!OAuthProvider::get($driver)->isEnabled()) {
|
||||
return redirect()->route('auth.login');
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,11 @@ namespace App\Providers;
|
||||
use App\Checks\NodeVersionsCheck;
|
||||
use App\Checks\PanelVersionCheck;
|
||||
use App\Checks\UsedDiskSpaceCheck;
|
||||
use App\Extensions\OAuth\Providers\AuthentikProvider;
|
||||
use App\Extensions\OAuth\Providers\CommonProvider;
|
||||
use App\Extensions\OAuth\Providers\DiscordProvider;
|
||||
use App\Extensions\OAuth\Providers\GithubProvider;
|
||||
use App\Extensions\OAuth\Providers\SteamProvider;
|
||||
use App\Models;
|
||||
use App\Models\ApiKey;
|
||||
use App\Models\Node;
|
||||
@ -21,14 +26,12 @@ use Illuminate\Database\Eloquent\Relations\Relation;
|
||||
use Illuminate\Foundation\Application;
|
||||
use Illuminate\Foundation\Console\AboutCommand;
|
||||
use Illuminate\Support\Facades\Blade;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\URL;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Illuminate\Support\Str;
|
||||
use Laravel\Sanctum\Sanctum;
|
||||
use SocialiteProviders\Manager\SocialiteWasCalled;
|
||||
use Spatie\Health\Checks\Checks\CacheCheck;
|
||||
use Spatie\Health\Checks\Checks\DatabaseCheck;
|
||||
use Spatie\Health\Checks\Checks\DebugModeCheck;
|
||||
@ -82,20 +85,20 @@ class AppServiceProvider extends ServiceProvider
|
||||
Scramble::registerApi('client', ['api_path' => 'api/client', 'info' => ['version' => '1.0']])->afterOpenApiGenerated($bearerTokens);
|
||||
Scramble::registerApi('remote', ['api_path' => 'api/remote', 'info' => ['version' => '1.0']])->afterOpenApiGenerated($bearerTokens);
|
||||
|
||||
$oauthProviders = [];
|
||||
foreach (config('auth.oauth') as $name => $data) {
|
||||
config()->set("services.$name", array_merge($data['service'], ['redirect' => "/auth/oauth/callback/$name"]));
|
||||
// Default OAuth providers included with Socialite
|
||||
CommonProvider::register('facebook', null, 'tabler-brand-facebook-f', '#1877f2');
|
||||
CommonProvider::register('x', null, 'tabler-brand-x-f', '#1da1f2');
|
||||
CommonProvider::register('linkedin', null, 'tabler-brand-linkedin-f', '#0a66c2');
|
||||
CommonProvider::register('google', null, 'tabler-brand-google-f', '#4285f4');
|
||||
GithubProvider::register();
|
||||
CommonProvider::register('gitlab', null, 'tabler-brand-gitlab', '#fca326');
|
||||
CommonProvider::register('bitbucket', null, 'tabler-brand-bitbucket-f', '#205081');
|
||||
CommonProvider::register('slack', null, 'tabler-brand-slack', '#6ecadc');
|
||||
|
||||
if (isset($data['provider'])) {
|
||||
$oauthProviders[$name] = $data['provider'];
|
||||
}
|
||||
}
|
||||
|
||||
Event::listen(function (SocialiteWasCalled $event) use ($oauthProviders) {
|
||||
foreach ($oauthProviders as $name => $provider) {
|
||||
$event->extendSocialite($name, $provider);
|
||||
}
|
||||
});
|
||||
// Additional OAuth providers from socialiteproviders.com
|
||||
AuthentikProvider::register();
|
||||
DiscordProvider::register();
|
||||
SteamProvider::register();
|
||||
|
||||
FilamentColor::register([
|
||||
'danger' => Color::Red,
|
||||
|
114
config/auth.php
114
config/auth.php
@ -1,7 +1,5 @@
|
||||
<?php
|
||||
|
||||
use Filament\Support\Colors\Color;
|
||||
|
||||
return [
|
||||
|
||||
'lockout' => [
|
||||
@ -25,116 +23,4 @@ return [
|
||||
],
|
||||
],
|
||||
|
||||
'oauth' => [
|
||||
// Default providers
|
||||
'facebook' => [
|
||||
'enabled' => env('OAUTH_FACEBOOK_ENABLED'),
|
||||
'icon' => 'tabler-brand-facebook-f',
|
||||
'color' => Color::hex('#1877f2'),
|
||||
'service' => [
|
||||
'client_id' => env('OAUTH_FACEBOOK_CLIENT_ID'),
|
||||
'client_secret' => env('OAUTH_FACEBOOK_CLIENT_SECRET'),
|
||||
],
|
||||
],
|
||||
'x' => [
|
||||
'enabled' => env('OAUTH_X_ENABLED'),
|
||||
'icon' => 'tabler-brand-x-f',
|
||||
'color' => Color::hex('#1da1f2'),
|
||||
'service' => [
|
||||
'client_id' => env('OAUTH_X_CLIENT_ID'),
|
||||
'client_secret' => env('OAUTH_X_CLIENT_SECRET'),
|
||||
],
|
||||
],
|
||||
'linkedin' => [
|
||||
'enabled' => env('OAUTH_LINKEDIN_ENABLED'),
|
||||
'icon' => 'tabler-brand-linkedin-f',
|
||||
'color' => Color::hex('#0a66c2'),
|
||||
'service' => [
|
||||
'client_id' => env('OAUTH_LINKEDIN_CLIENT_ID'),
|
||||
'client_secret' => env('OAUTH_LINKEDIN_CLIENT_SECRET'),
|
||||
],
|
||||
],
|
||||
'google' => [
|
||||
'enabled' => env('OAUTH_GOOGLE_ENABLED'),
|
||||
'icon' => 'tabler-brand-google-f',
|
||||
'color' => Color::hex('#4285f4'),
|
||||
'service' => [
|
||||
'client_id' => env('OAUTH_GOOGLE_CLIENT_ID'),
|
||||
'client_secret' => env('OAUTH_GOOGLE_CLIENT_SECRET'),
|
||||
],
|
||||
],
|
||||
'github' => [
|
||||
'enabled' => env('OAUTH_GITHUB_ENABLED'),
|
||||
'icon' => 'tabler-brand-github-f',
|
||||
'color' => Color::hex('#4078c0'),
|
||||
'service' => [
|
||||
'client_id' => env('OAUTH_GITHUB_CLIENT_ID'),
|
||||
'client_secret' => env('OAUTH_GITHUB_CLIENT_SECRET'),
|
||||
],
|
||||
],
|
||||
'gitlab' => [
|
||||
'enabled' => env('OAUTH_GITLAB_ENABLED'),
|
||||
'icon' => 'tabler-brand-gitlab',
|
||||
'color' => Color::hex('#fca326'),
|
||||
'service' => [
|
||||
'client_id' => env('OAUTH_GITLAB_CLIENT_ID'),
|
||||
'client_secret' => env('OAUTH_GITLAB_CLIENT_SECRET'),
|
||||
],
|
||||
],
|
||||
'bitbucket' => [
|
||||
'enabled' => env('OAUTH_BITBUCKET_ENABLED'),
|
||||
'icon' => 'tabler-brand-bitbucket-f',
|
||||
'color' => Color::hex('#205081'),
|
||||
'service' => [
|
||||
'client_id' => env('OAUTH_BITBUCKET_CLIENT_ID'),
|
||||
'client_secret' => env('OAUTH_BITBUCKET_CLIENT_SECRET'),
|
||||
],
|
||||
],
|
||||
'slack' => [
|
||||
'enabled' => env('OAUTH_SLACK_ENABLED'),
|
||||
'icon' => 'tabler-brand-slack',
|
||||
'color' => Color::hex('#6ecadc'),
|
||||
'service' => [
|
||||
'client_id' => env('OAUTH_SLACK_CLIENT_ID'),
|
||||
'client_secret' => env('OAUTH_SLACK_CLIENT_SECRET'),
|
||||
],
|
||||
],
|
||||
|
||||
// Additional providers from socialiteproviders.com
|
||||
'authentik' => [
|
||||
'enabled' => env('OAUTH_AUTHENTIK_ENABLED'),
|
||||
'icon' => null,
|
||||
'color' => Color::hex('#fd4b2d'),
|
||||
'service' => [
|
||||
'base_url' => env('OAUTH_AUTHENTIK_BASE_URL'),
|
||||
'client_id' => env('OAUTH_AUTHENTIK_CLIENT_ID'),
|
||||
'client_secret' => env('OAUTH_AUTHENTIK_CLIENT_SECRET'),
|
||||
],
|
||||
'provider' => \SocialiteProviders\Authentik\Provider::class,
|
||||
],
|
||||
'discord' => [
|
||||
'enabled' => env('OAUTH_DISCORD_ENABLED'),
|
||||
'icon' => 'tabler-brand-discord-f',
|
||||
'color' => Color::hex('#5865F2'),
|
||||
'service' => [
|
||||
'client_id' => env('OAUTH_DISCORD_CLIENT_ID'),
|
||||
'client_secret' => env('OAUTH_DISCORD_CLIENT_SECRET'),
|
||||
],
|
||||
'provider' => \SocialiteProviders\Discord\Provider::class,
|
||||
],
|
||||
'steam' => [
|
||||
'enabled' => env('OAUTH_STEAM_ENABLED'),
|
||||
'icon' => 'tabler-brand-steam-f',
|
||||
'color' => Color::hex('#00adee'),
|
||||
'service' => [
|
||||
'client_id' => null,
|
||||
'client_secret' => env('OAUTH_STEAM_CLIENT_SECRET'),
|
||||
'allowed_hosts' => [
|
||||
str_replace(['http://', 'https://'], '', env('APP_URL')),
|
||||
],
|
||||
],
|
||||
'provider' => \SocialiteProviders\Steam\Provider::class,
|
||||
],
|
||||
],
|
||||
|
||||
];
|
||||
|
Loading…
x
Reference in New Issue
Block a user