Skip to content
This repository was archived by the owner on Feb 18, 2023. It is now read-only.

Commit 97d153b

Browse files
authored
Merge pull request #85 from joselfonseca/feature/80-socialite
Add socialite integration
2 parents d3a47a4 + 708ada1 commit 97d153b

File tree

15 files changed

+813
-9
lines changed

15 files changed

+813
-9
lines changed

app/Models/SocialProvider.php

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

app/Models/User.php

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@
33
namespace App\Models;
44

55
use App\Support\HasRolesUuid;
6+
use App\Support\HasSocialLogin;
67
use App\Support\UuidScopeTrait;
78
use Illuminate\Database\Eloquent\Factories\HasFactory;
89
use Illuminate\Database\Eloquent\SoftDeletes;
910
use Illuminate\Foundation\Auth\User as Authenticatable;
1011
use Illuminate\Notifications\Notifiable;
12+
use Illuminate\Support\Facades\Hash;
1113
use Laravel\Passport\HasApiTokens;
1214
use Spatie\Permission\Traits\HasRoles;
1315

@@ -16,7 +18,7 @@
1618
*/
1719
class User extends Authenticatable
1820
{
19-
use Notifiable, UuidScopeTrait, HasFactory, HasApiTokens, HasRoles, SoftDeletes, HasRolesUuid {
21+
use Notifiable, UuidScopeTrait, HasFactory, HasApiTokens, HasRoles, SoftDeletes, HasSocialLogin, HasRolesUuid {
2022
HasRolesUuid::getStoredRole insteadof HasRoles;
2123
}
2224

@@ -51,14 +53,15 @@ class User extends Authenticatable
5153
'remember_token',
5254
];
5355

54-
/**
55-
* @param array $attributes
56-
* @return \Illuminate\Database\Eloquent\Model
57-
*/
56+
public function socialProviders()
57+
{
58+
return $this->hasMany(SocialProvider::class);
59+
}
60+
5861
public static function create(array $attributes = [])
5962
{
6063
if (array_key_exists('password', $attributes)) {
61-
$attributes['password'] = bcrypt($attributes['password']);
64+
$attributes['password'] = Hash::make($attributes['password']);
6265
}
6366

6467
$model = static::query()->create($attributes);

app/OAuthGrants/SocialGrant.php

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
<?php
2+
3+
namespace App\OAuthGrants;
4+
5+
use DateInterval;
6+
use Illuminate\Http\Request;
7+
use Laravel\Passport\Bridge\User;
8+
use League\OAuth2\Server\Entities\UserEntityInterface;
9+
use League\OAuth2\Server\Exception\OAuthServerException;
10+
use League\OAuth2\Server\Grant\AbstractGrant;
11+
use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
12+
use League\OAuth2\Server\Repositories\UserRepositoryInterface;
13+
use League\OAuth2\Server\RequestEvent;
14+
use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;
15+
use Psr\Http\Message\ServerRequestInterface;
16+
17+
class SocialGrant extends AbstractGrant
18+
{
19+
public function __construct(UserRepositoryInterface $userRepository, RefreshTokenRepositoryInterface $refreshTokenRepository)
20+
{
21+
$this->setUserRepository($userRepository);
22+
$this->setRefreshTokenRepository($refreshTokenRepository);
23+
$this->refreshTokenTTL = new DateInterval('P1M');
24+
}
25+
26+
/**
27+
* {@inheritdoc}
28+
*/
29+
public function respondToAccessTokenRequest(ServerRequestInterface $request, ResponseTypeInterface $responseType, DateInterval $accessTokenTTL)
30+
{
31+
// Validate request
32+
$client = $this->validateClient($request);
33+
$scopes = $this->validateScopes($this->getRequestParameter('scope', $request));
34+
$user = $this->validateUser($request);
35+
// Finalize the requested scopes
36+
$scopes = $this->scopeRepository->finalizeScopes($scopes, $this->getIdentifier(), $client, $user->getIdentifier());
37+
// Issue and persist new tokens
38+
$accessToken = $this->issueAccessToken($accessTokenTTL, $client, $user->getIdentifier(), $scopes);
39+
$refreshToken = $this->issueRefreshToken($accessToken);
40+
// Inject tokens into response
41+
$responseType->setAccessToken($accessToken);
42+
$responseType->setRefreshToken($refreshToken);
43+
44+
return $responseType;
45+
}
46+
47+
/**
48+
* {@inheritdoc}
49+
*/
50+
public function getIdentifier()
51+
{
52+
return 'social_grant';
53+
}
54+
55+
/**
56+
* @param ServerRequestInterface $request
57+
*
58+
* @throws OAuthServerException
59+
*
60+
* @return UserEntityInterface
61+
*/
62+
protected function validateUser(ServerRequestInterface $request)
63+
{
64+
$laravelRequest = new Request($request->getParsedBody());
65+
$user = $this->getUserEntityByRequest($laravelRequest);
66+
if (false === $user instanceof UserEntityInterface) {
67+
$this->getEmitter()->emit(new RequestEvent(RequestEvent::USER_AUTHENTICATION_FAILED, $request));
68+
69+
throw OAuthServerException::invalidCredentials();
70+
}
71+
72+
return $user;
73+
}
74+
75+
/**
76+
* Retrieve user by request.
77+
*
78+
* @param \Illuminate\Http\Request $request
79+
*
80+
* @throws \League\OAuth2\Server\Exception\OAuthServerException
81+
*
82+
* @return null|\Laravel\Passport\Bridge\User
83+
*/
84+
protected function getUserEntityByRequest(Request $request)
85+
{
86+
if (is_null($model = config('auth.providers.users.model'))) {
87+
throw OAuthServerException::serverError('Unable to determine user model from configuration.');
88+
}
89+
if (method_exists($model, 'byOAuthToken')) {
90+
$user = (new $model())->byOAuthToken($request);
91+
} else {
92+
throw OAuthServerException::serverError('Unable to find byLoggedInUser method on user model.');
93+
}
94+
95+
return ($user) ? new User($user->id) : null;
96+
}
97+
}

app/Providers/AuthServiceProvider.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@
22

33
namespace App\Providers;
44

5+
use App\OAuthGrants\SocialGrant;
56
use Carbon\Carbon;
67
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
8+
use Laravel\Passport\Bridge\RefreshTokenRepository;
9+
use Laravel\Passport\Bridge\UserRepository;
710
use Laravel\Passport\Passport;
11+
use League\OAuth2\Server\AuthorizationServer;
812

913
class AuthServiceProvider extends ServiceProvider
1014
{
@@ -25,8 +29,32 @@ class AuthServiceProvider extends ServiceProvider
2529
public function boot()
2630
{
2731
$this->registerPolicies();
32+
$this->extendAuthorizationServer();
2833
Passport::routes();
2934
Passport::tokensExpireIn(Carbon::now()->addHours(24));
3035
Passport::refreshTokensExpireIn(Carbon::now()->addDays(60));
3136
}
37+
38+
protected function makeSocialRequestGrant()
39+
{
40+
$grant = new SocialGrant(
41+
$this->app->make(UserRepository::class),
42+
$this->app->make(RefreshTokenRepository::class)
43+
);
44+
$grant->setRefreshTokenTTL(Passport::refreshTokensExpireIn());
45+
46+
return $grant;
47+
}
48+
49+
protected function extendAuthorizationServer()
50+
{
51+
$this->app->extend(AuthorizationServer::class, function ($server) {
52+
return tap($server, function ($server) {
53+
$server->enableGrantType(
54+
$this->makeSocialRequestGrant(),
55+
Passport::tokensExpireIn()
56+
);
57+
});
58+
});
59+
}
3260
}

app/Support/HasSocialLogin.php

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
namespace App\Support;
4+
5+
use App\Models\SocialProvider;
6+
use Illuminate\Database\Eloquent\ModelNotFoundException;
7+
use Illuminate\Http\Request;
8+
use Illuminate\Support\Facades\Hash;
9+
use Illuminate\Support\Str;
10+
use Laravel\Socialite\Facades\Socialite;
11+
12+
trait HasSocialLogin
13+
{
14+
public static function byOAuthToken(Request $request)
15+
{
16+
$userData = Socialite::driver($request->get('provider'))->userFromToken($request->get('token'));
17+
18+
try {
19+
$user = static::whereHas('socialProviders', function ($query) use ($request, $userData) {
20+
$query->where('provider', Str::lower($request->get('provider')))->where('provider_id', $userData->getId());
21+
})->firstOrFail();
22+
} catch (ModelNotFoundException $e) {
23+
$user = static::where('email', $userData->getEmail())->first();
24+
if (! $user) {
25+
$user = static::create([
26+
'name' => $userData->getName(),
27+
'email' => $userData->getEmail(),
28+
'uuid' => Str::uuid(),
29+
'password' => Hash::make(Str::random(16)),
30+
'email_verified_at' => now(),
31+
]);
32+
}
33+
SocialProvider::create([
34+
'user_id' => $user->id,
35+
'provider' => $request->get('provider'),
36+
'provider_id' => $userData->getId(),
37+
]);
38+
}
39+
40+
return $user;
41+
}
42+
}

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"intervention/imagecache": "^2.4",
2828
"laravel/framework": "^8.0",
2929
"laravel/passport": "^10.0",
30+
"laravel/socialite": "^5.1",
3031
"laravel/tinker": "^2.0",
3132
"spatie/laravel-fractal": "^5.8",
3233
"spatie/laravel-permission": "^3.17"

0 commit comments

Comments
 (0)