Skip to content

Commit 0eb1222

Browse files
Merge pull request #2 from designbycode/socialite
Socialite
2 parents 4336c96 + aa02ed7 commit 0eb1222

61 files changed

Lines changed: 4093 additions & 44 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
---
2+
name: socialite-development
3+
description: "Manages OAuth social authentication with Laravel Socialite. Activate when adding social login providers; configuring OAuth redirect/callback flows; retrieving authenticated user details; customizing scopes or parameters; setting up community providers; testing with Socialite fakes; or when the user mentions social login, OAuth, Socialite, or third-party authentication."
4+
license: MIT
5+
metadata:
6+
author: laravel
7+
---
8+
9+
# Socialite Authentication
10+
11+
## Documentation
12+
13+
Use `search-docs` for detailed Socialite patterns and documentation (installation, configuration, routing, callbacks, testing, scopes, stateless auth).
14+
15+
## Available Providers
16+
17+
Built-in: `facebook`, `twitter`, `twitter-oauth-2`, `linkedin`, `linkedin-openid`, `google`, `github`, `gitlab`, `bitbucket`, `slack`, `slack-openid`, `twitch`
18+
19+
Community: 150+ additional providers at [socialiteproviders.com](https://socialiteproviders.com). For provider-specific setup, use `WebFetch` on `https://socialiteproviders.com/{provider-name}`.
20+
21+
Configuration key in `config/services.php` must match the driver name exactly — note the hyphenated keys: `twitter-oauth-2`, `linkedin-openid`, `slack-openid`.
22+
23+
Twitter/X: Use `twitter-oauth-2` (OAuth 2.0) for new projects. The legacy `twitter` driver is OAuth 1.0. Driver names remain unchanged despite the platform rebrand.
24+
25+
Community providers differ from built-in providers in the following ways:
26+
- Installed via `composer require socialiteproviders/{name}`
27+
- Must register via event listener — NOT auto-discovered like built-in providers
28+
- Use `search-docs` for the registration pattern
29+
30+
## Adding a Provider
31+
32+
### 1. Configure the provider
33+
34+
Add the provider's `client_id`, `client_secret`, and `redirect` to `config/services.php`. The config key must match the driver name exactly.
35+
36+
### 2. Create redirect and callback routes
37+
38+
Two routes are needed: one that calls `Socialite::driver('provider')->redirect()` to send the user to the OAuth provider, and one that calls `Socialite::driver('provider')->user()` to receive the callback and retrieve user details.
39+
40+
### 3. Authenticate and store the user
41+
42+
In the callback, use `updateOrCreate` to find or create a user record from the provider's response (`id`, `name`, `email`, `token`, `refreshToken`), then call `Auth::login()`.
43+
44+
### 4. Customize the redirect (optional)
45+
46+
- `scopes()` — merge additional scopes with the provider's defaults
47+
- `setScopes()` — replace all scopes entirely
48+
- `with()` — pass optional parameters (e.g., `['hd' => 'example.com']` for Google)
49+
- `asBotUser()` — Slack only; generates a bot token (`xoxb-`) instead of a user token (`xoxp-`). Must be called before both `redirect()` and `user()`. Only the `token` property will be hydrated on the user object.
50+
- `stateless()` — for API/SPA contexts where session state is not maintained
51+
52+
### 5. Verify
53+
54+
1. Config key matches driver name exactly (check the list above for hyphenated names)
55+
2. `client_id`, `client_secret`, and `redirect` are all present
56+
3. Redirect URL matches what is registered in the provider's OAuth dashboard
57+
4. Callback route handles denied grants (when user declines authorization)
58+
59+
Use `search-docs` for complete code examples of each step.
60+
61+
## Additional Features
62+
63+
Use `search-docs` for usage details on: `enablePKCE()`, `userFromToken($token)`, `userFromTokenAndSecret($token, $secret)` (OAuth 1.0), retrieving user details.
64+
65+
User object: `getId()`, `getName()`, `getEmail()`, `getAvatar()`, `getNickname()`, `token`, `refreshToken`, `expiresIn`, `approvedScopes`
66+
67+
## Testing
68+
69+
Socialite provides `Socialite::fake()` for testing redirects and callbacks. Use `search-docs` for faking redirects, callback user data, custom token properties, and assertion methods.
70+
71+
## Common Pitfalls
72+
73+
- Config key must match driver name exactly — hyphenated drivers need hyphenated keys (`linkedin-openid`, `slack-openid`, `twitter-oauth-2`). Mismatch silently fails.
74+
- Every provider needs `client_id`, `client_secret`, and `redirect` in `config/services.php`. Missing any one causes cryptic errors.
75+
- `scopes()` merges with defaults; `setScopes()` replaces all scopes entirely.
76+
- Missing `stateless()` in API/SPA contexts causes `InvalidStateException`.
77+
- Redirect URL in `config/services.php` must exactly match the provider's OAuth dashboard (including trailing slashes and protocol).
78+
- Do not pass `state`, `response_type`, `client_id`, `redirect_uri`, or `scope` via `with()` — these are reserved.
79+
- Community providers require event listener registration via `SocialiteWasCalled`.
80+
- `user()` throws when the user declines authorization. Always handle denied grants.

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
/storage/*.key
99
/storage/pail
1010
/resources/js/actions
11-
/resources/js/routes
1211
/resources/js/wayfinder
1312
/vendor
1413
.DS_Store

AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ This application is a Laravel application and its main Laravel ecosystems packag
1515
- laravel/framework (LARAVEL) - v13
1616
- laravel/prompts (PROMPTS) - v0
1717
- laravel/sanctum (SANCTUM) - v4
18+
- laravel/socialite (SOCIALITE) - v5
1819
- laravel/wayfinder (WAYFINDER) - v0
1920
- laravel/boost (BOOST) - v2
2021
- laravel/mcp (MCP) - v0
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
3+
namespace App\Http\Controllers\Auth;
4+
5+
use App\Http\Controllers\Controller;
6+
use App\Models\Social;
7+
use App\Models\User;
8+
use Illuminate\Http\RedirectResponse;
9+
use Illuminate\Support\Facades\Auth;
10+
use Laravel\Socialite\Facades\Socialite;
11+
12+
class SocialiteController extends Controller
13+
{
14+
/**
15+
* Redirect the user to the provider's authentication page.
16+
*/
17+
public function redirect(string $provider): RedirectResponse
18+
{
19+
return Socialite::driver($provider)->redirect();
20+
}
21+
22+
/**
23+
* Obtain the user information from the provider.
24+
*/
25+
public function callback(string $provider): RedirectResponse
26+
{
27+
$socialUser = Socialite::driver($provider)->user();
28+
29+
$socialAccount = Social::where('provider', $provider)
30+
->where('provider_id', $socialUser->getId())
31+
->first();
32+
33+
if ($socialAccount) {
34+
Auth::login($socialAccount->user);
35+
36+
return redirect()->route('dashboard');
37+
}
38+
39+
$user = User::where('email', $socialUser->getEmail())->first();
40+
41+
if (! $user) {
42+
$user = User::create([
43+
'name' => $socialUser->getName() ?? $socialUser->getNickname(),
44+
'email' => $socialUser->getEmail(),
45+
'email_verified_at' => now(),
46+
]);
47+
}
48+
49+
$user->socials()->create([
50+
'provider' => $provider,
51+
'provider_id' => $socialUser->getId(),
52+
'avatar' => $socialUser->getAvatar(),
53+
]);
54+
55+
Auth::login($user);
56+
57+
return redirect()->route('dashboard');
58+
}
59+
}

app/Models/Social.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
namespace App\Models;
4+
5+
use Illuminate\Database\Eloquent\Attributes\Fillable;
6+
use Illuminate\Database\Eloquent\Factories\HasFactory;
7+
use Illuminate\Database\Eloquent\Model;
8+
use Illuminate\Database\Eloquent\Relations\BelongsTo;
9+
10+
#[Fillable(['user_id', 'provider', 'provider_id', 'avatar'])]
11+
class Social extends Model
12+
{
13+
use HasFactory;
14+
15+
public function user(): BelongsTo
16+
{
17+
return $this->belongsTo(User::class);
18+
}
19+
20+
public function isProvider(string $provider): bool
21+
{
22+
return $this->provider === $provider;
23+
}
24+
}

app/Models/User.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,22 @@ public function registries(): HasMany
2727
return $this->hasMany(Registry::class);
2828
}
2929

30+
public function hasLinkedProvider(string $provider): bool
31+
{
32+
return $this->socials()->where('provider', $provider)->exists();
33+
}
34+
35+
36+
public function socials(): HasMany
37+
{
38+
return $this->hasMany(Social::class);
39+
}
40+
41+
public function connectedAccounts(): HasMany
42+
{
43+
return $this->socials();
44+
}
45+
3046
/**
3147
* Get the attributes that should be cast.
3248
*

boost.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"skills": [
1111
"fortify-development",
1212
"laravel-best-practices",
13+
"socialite-development",
1314
"wayfinder-development",
1415
"pest-testing",
1516
"inertia-react-development",

composer.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@
1111
"require": {
1212
"php": "^8.3",
1313
"inertiajs/inertia-laravel": "^3.0",
14+
"laravel/cashier-paddle": "^2.8",
1415
"laravel/fortify": "^1.34",
1516
"laravel/framework": "^13.7",
1617
"laravel/sanctum": "^4.0",
18+
"laravel/socialite": "^5.27",
1719
"laravel/tinker": "^3.0",
1820
"laravel/wayfinder": "^0.1.14"
1921
},

0 commit comments

Comments
 (0)