From 70b4feab10c1c35ee0aa4ef0141b10130e1831f9 Mon Sep 17 00:00:00 2001 From: Jordi Baguette Date: Wed, 18 Jun 2025 08:25:03 +0200 Subject: [PATCH 1/6] :heavy_plus_sign: Add laracord discord socialite driver --- composer.json | 1 + config/discord.php | 14 ++++++++++++++ 2 files changed, 15 insertions(+) diff --git a/composer.json b/composer.json index 3e25506..3640bbd 100644 --- a/composer.json +++ b/composer.json @@ -30,6 +30,7 @@ "illuminate/validation": "^12.0", "illuminate/view": "^12.0", "intonate/tinker-zero": "^1.2", + "laracord/socialite-discord": "dev-next", "laravel-zero/framework": "^12.0", "laravel/sanctum": "^4.0", "react/async": "^4.2", diff --git a/config/discord.php b/config/discord.php index 4de2fd3..bd30677 100644 --- a/config/discord.php +++ b/config/discord.php @@ -17,6 +17,20 @@ 'token' => env('DISCORD_TOKEN', ''), + /* + |-------------------------------------------------------------------------- + | Discord Client Secret + |-------------------------------------------------------------------------- + | + | This is the client secret associated with your Discord application. + | It is used during the OAuth2 authorization process in conjunction + | with the client ID. Keep this secret safe and never expose it + | publicly, as it could be used to impersonate your application. + | + */ + + 'secret' => env('DISCORD_CLIENT_SECRET', ''), + /* |-------------------------------------------------------------------------- | Gateway Intents From e16e87e53b9833b23e5db72fe78a50ccdf66ecf8 Mon Sep 17 00:00:00 2001 From: Jordi Baguette Date: Wed, 18 Jun 2025 08:25:33 +0200 Subject: [PATCH 2/6] :wrench: Add options to Laracord to manage discord oauth behaviour --- src/Bot/Concerns/HasDiscordAuth.php | 101 ++++++++++++++++++++++++++++ src/Laracord.php | 1 + 2 files changed, 102 insertions(+) create mode 100644 src/Bot/Concerns/HasDiscordAuth.php diff --git a/src/Bot/Concerns/HasDiscordAuth.php b/src/Bot/Concerns/HasDiscordAuth.php new file mode 100644 index 0000000..e9ba968 --- /dev/null +++ b/src/Bot/Concerns/HasDiscordAuth.php @@ -0,0 +1,101 @@ +discordAuthScopes = $discordAuthScopes; + + return $this; + } + + /** + * Get the discord Oauth2 scopes. + */ + public function getDiscordAuthScopes(): array + { + return $this->discordAuthScopes; + } + + /** + * Set the redirect URL after successful Discord authentication. + */ + public function withDiscordLoggedInRedirect(string $url): self + { + $this->discordLoggedInRedirect = $url; + + return $this; + } + + /** + * Get the redirect URL after successful Discord authentication. + */ + public function getDiscordLoggedInRedirect(): string + { + return $this->discordLoggedInRedirect; + } + + /** + * Set the redirect URL after failed Discord authentication. + */ + public function withDiscordLoginFailedRedirect(string $url): self + { + $this->discordLogInFailedRedirect = $url; + + return $this; + } + + /** + * Get the redirect URL after failed Discord authentication. + */ + public function getDiscordLoginFailedRedirect(): string + { + return $this->discordLogInFailedRedirect; + } + + /** + * Set the closure that should be executed after a successful Discord login. + */ + public function afterDiscordAuthCallback(?Closure $afterDiscordAuthCallback): self + { + $this->afterDiscordAuthCallback = $afterDiscordAuthCallback; + + return $this; + } + + /** + * Get the closure that should be executed after a successful Discord login. + */ + public function getAfterDiscordAuthCallback(): ?Closure + { + return $this->afterDiscordAuthCallback; + } +} diff --git a/src/Laracord.php b/src/Laracord.php index 4fd6c78..0fa26fa 100644 --- a/src/Laracord.php +++ b/src/Laracord.php @@ -23,6 +23,7 @@ class Laracord Concerns\HasConsole, Concerns\HasContextMenus, Concerns\HasDiscord, + Concerns\HasDiscordAuth, Concerns\HasEvents, Concerns\HasHooks, Concerns\HasHttpServer, From 4ed13b3c732b930242776ca508f25e6384020820 Mon Sep 17 00:00:00 2001 From: Jordi Baguette Date: Wed, 18 Jun 2025 08:25:51 +0200 Subject: [PATCH 3/6] :sparkles: Custom url generator in case no request context is available --- src/Http/Routing/UrlGenerator.php | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 src/Http/Routing/UrlGenerator.php diff --git a/src/Http/Routing/UrlGenerator.php b/src/Http/Routing/UrlGenerator.php new file mode 100644 index 0000000..ec18e21 --- /dev/null +++ b/src/Http/Routing/UrlGenerator.php @@ -0,0 +1,21 @@ + Date: Wed, 18 Jun 2025 08:26:29 +0200 Subject: [PATCH 4/6] :sparkles: Add custom Authenticatable implementation for discord only --- src/Auth/DiscordAuthenticatable.php | 83 +++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 src/Auth/DiscordAuthenticatable.php diff --git a/src/Auth/DiscordAuthenticatable.php b/src/Auth/DiscordAuthenticatable.php new file mode 100644 index 0000000..6fca6cd --- /dev/null +++ b/src/Auth/DiscordAuthenticatable.php @@ -0,0 +1,83 @@ +discord_id; + } + + /** + * Get the name of the password attribute for the user. + * Since there are no passwords, we can return null or an empty string. + * + * @return string + */ + public function getAuthPasswordName() + { + return null; + } + + /** + * Get the password for the user. + * Since there are no passwords, return null to ensure no password-based auth. + * + * @return string|null + */ + public function getAuthPassword() + { + return null; + } + + /** + * Get the token value for the "remember me" session. + * Not using remember tokens for Discord-only auth. + * + * @return string|null + */ + public function getRememberToken() + { + return null; + } + + /** + * Set the token value for the "remember me" session. + * Not using remember tokens for Discord-only auth. + * + * @param string $value + * @return void + */ + public function setRememberToken($value) + { + // + } + + /** + * Get the column name for the "remember me" token. + * Not using remember tokens for Discord-only auth. + * + * @return string|null + */ + public function getRememberTokenName() + { + return null; + } +} \ No newline at end of file From 4bf667632d2f2df9dd2657eb25d13e04ed8ba789 Mon Sep 17 00:00:00 2001 From: Jordi Baguette Date: Wed, 18 Jun 2025 08:26:52 +0200 Subject: [PATCH 5/6] :sparkles: Add controllers to redirect to and handle callback from discord --- .../Controllers/DiscordCallbackController.php | 44 +++++++++++++++++++ .../Controllers/DiscordLoginController.php | 17 +++++++ 2 files changed, 61 insertions(+) create mode 100644 src/Http/Controllers/DiscordCallbackController.php create mode 100644 src/Http/Controllers/DiscordLoginController.php diff --git a/src/Http/Controllers/DiscordCallbackController.php b/src/Http/Controllers/DiscordCallbackController.php new file mode 100644 index 0000000..96808a8 --- /dev/null +++ b/src/Http/Controllers/DiscordCallbackController.php @@ -0,0 +1,44 @@ +setRequest($request) + ->user(); + + $model = app('bot')->getUserModel(); + + if (! class_exists($model)) { + return redirect()->to(app('bot')->getDiscordLoginFailedRedirect()); + } + + $user = $model::updateOrCreate( + ['discord_id' => $discordUser->getId()], + [ + 'username' => $discordUser->getName() ?? $discordUser->getNickname(), + ] + ); + + $afterDiscordAuthCallback = app('bot')->getAfterDiscordAuthCallback(); + if (is_callable($afterDiscordAuthCallback)) { + $afterDiscordAuthCallback($user, $discordUser); + } + + auth()->login($user); + + return redirect()->to(app('bot')->getDiscordLoggedInRedirect()); + } catch (Exception) { + return redirect()->to(app('bot')->getDiscordLoginFailedRedirect()); + } + } +} diff --git a/src/Http/Controllers/DiscordLoginController.php b/src/Http/Controllers/DiscordLoginController.php new file mode 100644 index 0000000..6bd64c6 --- /dev/null +++ b/src/Http/Controllers/DiscordLoginController.php @@ -0,0 +1,17 @@ +setRequest($request) + ->scopes(app('bot')->getDiscordAuthScopes()) + ->redirect(); + } +} From 012a57b71441339ccd4c6c04b301b66084b1de0e Mon Sep 17 00:00:00 2001 From: Jordi Baguette Date: Wed, 18 Jun 2025 08:27:02 +0200 Subject: [PATCH 6/6] :sparkles: Configure discord oauth in service provider --- src/LaracordServiceProvider.php | 38 ++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/src/LaracordServiceProvider.php b/src/LaracordServiceProvider.php index 03fc594..ad32d02 100644 --- a/src/LaracordServiceProvider.php +++ b/src/LaracordServiceProvider.php @@ -10,17 +10,24 @@ use Illuminate\Foundation\Configuration\Middleware; use Illuminate\Foundation\Console\PackageDiscoverCommand; use Illuminate\Foundation\PackageManifest as BasePackageManifest; +use Illuminate\Routing\Router; +use Illuminate\Routing\UrlGenerator; use Illuminate\Support\AggregateServiceProvider; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Date; use Illuminate\Support\Facades\File; use Illuminate\Support\Facades\Http; +use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Storage; +use Laracord\Bot\Hook; use Laracord\Console\Commands; use Laracord\Console\Console; use Laracord\Console\Prompts; use Laracord\Discord\Message; use Laracord\Http\Kernel; +use Laracord\Http\Controllers\DiscordCallbackController; +use Laracord\Http\Controllers\DiscordLoginController; +use Laracord\Http\Routing\UrlGenerator as LaracordUrlGenerator; use LaravelZero\Framework\Components\Database\Provider as DatabaseProvider; use LaravelZero\Framework\Components\Log\Provider as LogProvider; use React\EventLoop\Loop; @@ -99,6 +106,7 @@ public function register() $this->registerLoop(); $this->registerConsole(); $this->registerLogger(); + $this->registerUrlGenerator(); $this->app->singleton(KernelContract::class, Kernel::class); $this->app->singleton(Middleware::class, fn () => new Middleware); @@ -116,7 +124,22 @@ public function register() $this->app->singleton(Message::class, fn () => Message::make($bot)); - return $this->bot($bot); + return $this->bot($bot) + ->registerHook(Hook::AFTER_HTTP_SERVER_START, function (Laracord $bot) { + config([ + 'services.discord' => [ + 'client_id' => $bot->discord()->id, + 'client_secret' => config('discord.secret'), + 'redirect' => route('oauth.discord.callback'), + ], + ]); + }) + ->withRoutes(function (Router $router) { + Route::middleware('web')->group(function() { + Route::get('/oauth/discord/login', DiscordLoginController::class)->name('oauth.discord.login'); + Route::get('/oauth/discord/callback', DiscordCallbackController::class)->name('oauth.discord.callback'); + }); + }); })); $this->app->alias(Laracord::class, 'bot'); @@ -236,6 +259,19 @@ protected function registerLogger(): void $this->app->booting(fn () => $this->app->register(LogProvider::class)); } + /** + * Register the URL generator. + */ + protected function registerUrlGenerator(): void + { + $this->app->bind(UrlGenerator::class, function ($app) { + $request = $app->has('request') ? $app->make('request') : null; + return new LaracordUrlGenerator($request); + }); + + $this->app->alias(UrlGenerator::class, 'url'); + } + /** * Register the default components. */