Discord OAuth should be straightforward. Socialite exists, there's a community Discord provider, the pieces are all there. And yet the first time I wired it up for Galaxy Community, I spent two hours filling gaps the docs quietly skipped over.
The setup
Install Socialite and the Discord provider:
composer require laravel/socialite socialiteproviders/discord
Register the provider in your AppServiceProvider:
// app/Providers/AppServiceProvider.php
use SocialiteProviders\Discord\Provider;
use Laravel\Socialite\Contracts\Factory;
public function boot(): void
{
$socialite = $this->app->make(Factory::class);
$socialite->extend('discord', fn ($app) =>
$socialite->buildProvider(Provider::class, config('services.discord'))
);
}
Add your credentials to config/services.php:
'discord' => [
'client_id' => env('DISCORD_CLIENT_ID'),
'client_secret' => env('DISCORD_CLIENT_SECRET'),
'redirect' => env('DISCORD_REDIRECT_URI'),
],
The routes and controller
// routes/web.php
Route::get('/auth/discord', [AuthController::class, 'redirect']);
Route::get('/auth/discord/callback', [AuthController::class, 'callback']);
// AuthController.php
public function redirect()
{
return Socialite::driver('discord')->redirect();
}
public function callback()
{
$discordUser = Socialite::driver('discord')->user();
$user = User::updateOrCreate(
['discord_id' => $discordUser->getId()],
[
'name' => $discordUser->getName(),
'email' => $discordUser->getEmail(),
'avatar' => $discordUser->getAvatar(),
]
);
Auth::login($user, remember: true);
return redirect()->intended('/dashboard');
}
The part the docs skip
By default the email scope isn't requested. If you need the user's email - and you probably do - ask for it explicitly:
return Socialite::driver('discord')
->scopes(['identify', 'email'])
->redirect();
Also make sure your Discord application's redirect URI matches exactly - trailing slash and all. Discord will silently reject mismatches with a generic error that tells you nothing useful.
