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:
Boy132 2025-01-15 18:29:06 +01:00 committed by GitHub
parent 885e03ee06
commit 4ec9171017
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 502 additions and 210 deletions

View 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();
}
}

View 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);
}
}

View 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();
}
}

View 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();
}
}

View 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);
}
}

View 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();
}
}

View File

@ -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;

View File

@ -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')

View File

@ -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);

View File

@ -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');
}

View File

@ -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,

View File

@ -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,
],
],
];