Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Socialite Discord Laravel Package

revolution/socialite-discord

Discord OAuth2 provider for Laravel Socialite. Adds Discord login to your Laravel app with a simple driver: install the package, add your Discord client ID and secret, then use Socialite::driver('discord') to authenticate users and retrieve their Discord profile.

View on GitHub
Deep Wiki
Context7

Getting Started

This package is a Laravel Socialite driver for Discord's OAuth2 endpoints. The first thing to do is check the README for the required registration method—some versions of revolution/socialite-discord ship a SocialiteDiscordServiceProvider you register in config/app.php, while others expect you to call Socialite::extend('discord', ...) yourself in a service provider's boot() method. Confirm which by opening src/ and looking for a ServiceProvider and a DiscordProvider class.

  1. Create a Discord application at https://discord.com/developers/applications, add an OAuth2 redirect (e.g. https://yourapp.test/auth/discord/callback), and copy the Client ID and Client Secret. The redirect must match exactly—Discord does no normalization.

  2. Configure credentials in config/services.php:

    'discord' => [
        'client_id'     => env('DISCORD_CLIENT_ID'),
        'client_secret' => env('DISCORD_CLIENT_SECRET'),
        'redirect'      => env('DISCORD_REDIRECT_URI'),
    ],
    
  3. Wire up two routes:

    Route::get('/auth/discord/redirect', fn () =>
        Socialite::driver('discord')
            ->scopes(['identify', 'email'])
            ->redirect()
    );
    
    Route::get('/auth/discord/callback', function () {
        $discord = Socialite::driver('discord')->user();
        // $discord->getId(), ->getNickname(), ->getName(), ->getEmail(), ->getAvatar()
        return redirect('/');
    });
    

Your first end-to-end use case is almost always "log in with Discord" on a gaming/community app: request identify + email, persist the Discord snowflake ID as a unique column, and use discriminator + avatar to render a profile.

Implementation Patterns

Standard login flow. Follow Socialite's normal pattern: redirect out, callback in, updateOrCreate on the Discord ID.

$discord = Socialite::driver('discord')->user();

$user = User::updateOrCreate(
    ['discord_id' => $discord->getId()],
    [
        'name'          => $discord->getNickname() ?? $discord->getName(),
        'email'         => $discord->getEmail(),
        'avatar_hash'   => $discord->getAvatar(),
        'discord_token' => $discord->token,
    ]
);

Auth::login($user);

Reaching into the raw payload. The Discord user object includes fields Socialite doesn't surface (discriminator, verified, banner, accent_color, locale, mfa_enabled). Access them via $discord->user (the raw array) or $discord->getRaw(), depending on which AbstractUser version the package extends. Wrap this in a small DTO so you're not scattering array access through the codebase.

Guild membership. If your app is a guild dashboard, request the guilds scope and then hit https://discord.com/api/users/@me/guilds with $discord->token in an Authorization: Bearer ... header to list servers the user can manage. For auto-joining new signups, use guilds.join (requires a bot in the target guild with CREATE_INVITE / membership permissions) and PUT /guilds/{guild.id}/members/{user.id}. This is not part of the driver—you'll do it with Http::withToken().

Storing the token for later API calls. The package follows Socialite's convention of exposing $user->token and $user->refreshToken. Persist both (encrypted cast) plus expiresIn if you plan to call the Discord API after login. Discord access tokens expire after 7 days; refresh via POST /api/oauth2/token with grant_type=refresh_token—Socialite will not do this for you.

Multiple account linking. For apps where users can link Discord after registering with email/password, split the redirect/callback from login: store the state or a session flag ("linking user X") and, in the callback, attach discord_id to the authenticated user rather than Auth::login().

Extending scopes per route. Socialite::driver('discord')->scopes([...]) is chainable and additive. Keep a default scope set in a small DiscordScopes constant class so scopes stay consistent between the redirect and any documentation.

Gotchas and Tips

  • Discriminators are dead. Discord's 2023 username migration sets discriminator to "0" for all migrated accounts and forces unique lowercase usernames. Do not build login keys or display strings like username#1234—treat username alone as unique and treat discriminator === '0' as "new-style account."
  • getEmail() can return null. The email scope is optional on Discord, and some users have no verified email. Make the email column nullable or fall back to a synthetic {discord_id}@users.noreply.discord placeholder, and never trust it as a global identifier.
  • Avatar handling. getAvatar() returns a hash, not a URL. Build the CDN URL yourself: https://cdn.discordapp.com/avatars/{id}/{hash}.png?size=128. If the hash is null, fall back to Discord's default avatars (https://cdn.discordapp.com/embed/avatars/{index}.png, index = (snowflake >> 22) % 6). Also note the newer .webp and animated .gif (hash starts with a_) variants.
  • API base URL and version. Old Discord Socialite packages target discord.com/api/v6 or discordapp.com/api; both are deprecated/redirected. Grep the provider for the base URL and bump it to https://discord.com/api/v10 (or v8) if you hit 404s or deprecation warnings. Forking the provider is often easier than PRing a dormant 8-star repo.
  • state is not automatic in some builds. Socialite validates state in AbstractProvider::user() only if you passed it—make sure you're using the standard redirect()/user() pair and, ideally, CSRF-protected callback routes with auth middleware where appropriate.
  • Token fields differ from other drivers. Discord's token response is access_token, refresh_token, expires_in, scope—the driver may map expiresIn differently than, say, Google or GitHub. Log the raw $user->user + token once during integration to see exactly what you're getting.
  • Scopes are space-separated, not comma-separated. Socialite's scopes() joins with %20; passing an array of arrays or a comma string will silently fail with an invalid_scope error from Discord. Valid scopes: identify, email, connections, guilds, guilds.join, gdm.join, bot, webhook.incoming, applications.commands.
  • Redirect URI mismatch is the #1 debugging issue. Discord returns invalid_request: Invalid "redirect_uri" in request—check that services.discord.redirect matches character-for-character, including trailing slashes, scheme, and port. Config caching (php artisan config:clear) is a common culprit during local dev.
  • Rate limits. user() is a single call, but if you fan out to /users/@me/guilds and guild-join calls during login you'll hit the global 50 req/sec bucket quickly on a busy callback. Queue the guild-membership work rather than doing it inline.
  • Maintenance risk. With 8 stars, 0 dependents, and a niche scope, this package is essentially a single-file OAuth2 driver. Read src/DiscordProvider.php before adopting it; expect to vendor or fork it if you need v10 endpoints, PKCE, or refresh-token helpers. The well-maintained alternative is socialiteproviders/discord—evaluating both side-by-side costs ten minutes and tells you which to standardize on.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
cgv/localized-resource