socialiteproviders/reddit
Reddit OAuth provider for Laravel Socialite. Adds Reddit login to your Laravel app using Socialite, supporting OAuth2 authentication with minimal setup. Install via Composer and configure your Reddit client ID and secret.
This package is an OAuth 2.0 driver for Reddit that plugs into Laravel Socialite. It contains no Reddit API client — it only handles the authorization-code exchange and returns a Laravel\Socialite\Two\User object. Anything beyond "who is this Redditor" is your job via Guzzle/Http.
Minimal setup:
composer require socialiteproviders/reddit
https://www.reddit.com/prefs/apps. Copy the client ID (under the app name) and secret.config/services.php:'reddit' => [
'client_id' => env('REDDIT_CLIENT_ID'),
'client_secret' => env('REDDIT_CLIENT_SECRET'),
'redirect' => env('REDDIT_REDIRECT_URI'), // e.g. https://myapp.test/auth/reddit/callback
],
SocialiteProviders\Manager event, not auto-discovery:// Laravel 11+ — AppServiceProvider::boot()
use Illuminate\Support\Facades\Event;
use SocialiteProviders\Manager\SocialiteWasCalled;
use SocialiteProviders\Reddit\RedditExtendSocialite;
Event::listen(SocialiteWasCalled::class, RedditExtendSocialite::class.'@handle');
On Laravel 10 and below, add the same mapping to EventServiceProvider::$listen instead.
Route::get('/auth/reddit', fn () => Socialite::driver('reddit')
->scopes(['identity'])
->redirect());
Route::get('/auth/reddit/callback', function () {
$redditUser = Socialite::driver('reddit')->user();
// getId() => t2_xxxxx without the prefix, getName() => username
// getNickname() => username, getAvatar() => avatar URL, getEmail() => null
});
First use case: "Log in with Reddit" on a Laravel app, typically followed by reading the user's subscribed subreddits or posting on their behalf.
Scope-first authorization. Reddit grants exactly what you ask for at redirect time and there is no incremental-consent prompt. Decide up front:
Socialite::driver('reddit')
->scopes(['identity', 'read', 'submit']) // add 'flair', 'edit', 'privatemessages' as needed
->redirect();
Stateless flow for APIs / SPAs. If your callback is hit by a mobile client or a separate front-end, use ->stateless() on both the redirect and the callback, and pass your own signed state through with(['state' => ...]).
Persisting the token set. $user->accessTokenResponseBody holds the raw payload, including refresh_token, expires_in, and scope. Store these on the user record — the driver will not refresh anything for you.
$user = Socialite::driver('reddit')->user();
SocialiteUser::updateOrCreate(
['reddit_id' => $user->getId()],
[
'username' => $user->getName(),
'avatar' => $user->getAvatar(),
'access_token' => $user->token,
'refresh_token' => $user->refreshToken,
'expires_at' => now()->addSeconds($user->expiresIn ?? 3600),
'scopes' => $user->accessTokenResponseBody['scope'] ?? null,
]
);
Token refresh job. Reddit's access tokens are short-lived (~1 hour). Refresh with basic auth against the same endpoint the provider uses internally:
Http::asForm()
->withBasicAuth(config('services.reddit.client_id'), config('services.reddit.client_secret'))
->withHeaders(['User-Agent' => config('services.reddit.user_agent')])
->post('https://www.reddit.com/api/v1/access_token', [
'grant_type' => 'refresh_token',
'refresh_token' => $user->refresh_token,
]);
Always send a descriptive User-Agent on every Reddit API call (including oauth.reddit.com). Reddit throttles or blocks generic/empty agents aggressively. Put it in config/services.php and a shared Http::withHeaders() macro or a small RedditClient service.
Multi-provider apps. Keep the Event::listen registration alongside your other SocialiteProviders entries; the single SocialiteWasCalled listener pattern scales to Google, GitHub, Discord, etc. with no per-provider service provider sprawl.
Testing. Socialite's Guzzle client is not intercepted by Http::fake(). Mock the driver instead:
Socialite::shouldReceive('driver->user')->andReturn(
(new \Laravel\Socialite\Two\User)->map([
'id' => 'abc123', 'nickname' => 'spez', 'name' => 'spez',
'email' => null, 'avatar' => 'https://...',
])
);
Then Http::fake(['oauth.reddit.com/*' => Http::response([...])]) for whatever your own Reddit API layer does.
Driver [reddit] is not supported is almost always a missing SocialiteWasCalled listener or a stale php artisan config:cache/event:cache. Clear both before debugging anything else.$user->getEmail() is null; the Reddit API does not expose it. Design your users table so email is nullable or gate registration behind an email prompt.t2_abc123. getId() returns the id with the t2_ prefix stripped. Don't mix both forms as keys across tables — pick one and normalize.duration=permanent to the authorize URL yields a long-lived one. The provider does not add this for you — extend RedditExtendSocialite or override the redirect URL if you need it.error=invalid_scope), so assert the returned scope string matches what you requested before assuming a permission exists.SocialiteProviders\Reddit\Provider and override getUserByToken() / mapUserToObject() when you need the raw /api/v1/me payload (e.g. is_gold, link_karma, created_utc), then register your subclass under the same reddit key in the SocialiteWasCalled listener.^4.1 if you need a hard guarantee before upgrading — nothing in the changelog announces breaking changes, but a changelog-only release note means no feature to plan around either. Run your auth flow against a test Reddit app after any minor bump.How can I help you explore Laravel packages today?