Social login API with Laravel + Passport
This article is about how we can integrate social login API in our laravel project using passport.
Assumption
I am going to assume that you have already integrated passport in your project. If you haven’t, you can visit the official documentation.
Requirements
- Laravel passport installed and configured.
Packages
- Socialite
- laravel-passport-socialite
We need Socialite to enable social authentication functionality in our project. Laravel provides a simple, convenient way to authenticate with OAuth providers using Laravel Socialite. Socialite currently supports authentication with Facebook, Twitter, LinkedIn, Google, GitHub, GitLab, and Bitbucket.
Unfortunately, Passport doesn’t provide a grant for social login. So, we also need to install laravel-passport-socialite plugin to provide the missing social grant. It is a very small package. It acts as a bridge between laravel’s native packages i.e. (passport and socialite).
Steps
- Install socialite
composer require laravel/socialite
2. Update users table
Now, we need to add the following two columns in the users table to store the provider name and the provider id provided by the respective provider.
$table->string('provider')->nullable();
$table->string('provider_id')->nullable();
provider column will store provider name like google or facebook. provider_id will store a id provided by the respective provider.
Then migrate the changes.
php artisan migrate
3. Update User model
We need to make provider and provider_id columns fillable in our users table by adding those the fillable attribute.
protected $fillable = [
'name',
'email',
'password',
'provider', 'provider_id'
];
4. Create AccessTokenController.php
Now we need to create AccessTokenController to overwrite the issueToken function provided by Passport.
<?php
namespace App\Http\Controllers;
use GuzzleHttp\Exception\ClientException;
use Illuminate\Http\JsonResponse;
use Laravel\Passport\Http\Controllers\AccessTokenController as PassportAccessTokenController;
use League\OAuth2\Server\Exception\OAuthServerException;
use Psr\Http\Message\ServerRequestInterface;
use Nyholm\Psr7\Response as Psr7Response;
class AccessTokenController extends PassportAccessTokenController
{ public function issueToken(ServerRequestInterface $request)
{
try {
$response = $this->getResponse($request);
$token = json_decode($response->getContent(), true)['access_token'];return response()->json([
'success' => true,
'message'=>'User token generated',
'token' => $token
]);
} catch (ClientException $exception) {
$error = json_decode($exception->getResponse()->getBody());
throw OAuthServerException::invalidRequest('access_token', object_get($error, 'error.message'));
}
}
public function getResponse($request){
return $this->convertResponse(
$this->server->respondToAccessTokenRequest($request, new Psr7Response)
);
}
}
5. Binding AccessTokenController
We need to bind the passport’s AccessTokenController with our controller’s AccessTokenController.
Go to App\Providers\AppServiceProvider.php
and place the following in the binding attribute.
public $bindings = [
\Laravel\Passport\Http\Controllers\AccessTokenController::class => \App\Http\Controllers\AccessTokenController::class
];
At this point, we need to install laravel-passport-socialite.
6. Install laravel-passport-socialite plugin
composer require schedula/laravel-passport-socialite
7. Configure laravel-passport-socialite plugin
When the composer installs this package successfully, register the Schedula\Laravel\PassportSocialite\PassportSocialiteServiceProvider::class
in your config/app.php
configuration file.
'providers' => [
// Other service providers...
Schedula\Laravel\PassportSocialite\PassportSocialiteServiceProvider::class,
],
In step 8 and 9 we configure a repository to keep our logic for social login. But It can be done differently.
8. Create User repository
We can create a user repository to store the logic to create and update our social user.
create App\Repositories\User\UserReporitory.php
<?php
namespace App\Repositories\User;
use App\Models\User;
use Laravel\Socialite\Two\User as ProviderUser;
class UserRepository implements UserRepositoryInterface
{
public function socialLogin(ProviderUser $providerUser, string $provider)
{
try{
$user = User::where('provider_id', $providerUser->getId())->first();
if (!$user) {
if($providerUser->getEmail()){
$user = User::where('email', $providerUser->getEmail())->first();
if($user){
$this->updateSocialUser($user, $providerUser, $provider);
}
else{
$user = $this->createSocialUser($providerUser, $provider);
$user->markEmailAsVerified();
}
}else{
$user = $this->createSocialUser($providerUser, $provider);
}
}
return $user;
}catch(\Exception $e){
return $e;
}
}
private function createSocialUser($social_user, $provider)
{
return User::create([
'name' => $social_user->getName(),
'email' => $social_user->getEmail(),
'provider_id' => $social_user->getId(),
'provider' => $provider,
]);
}
private function updateSocialUser($user, $social_user, $provider)
{
$user->update([
'name' => $social_user->getName(),
'email' => $social_user->getEmail(),
'provider_id' => $social_user->getId(),
'provider' => $provider,
]);
}
}
Also, create App\Repositories\User\UserReporitoryInterface.php
<?php
namespace App\Repositories\User;
use Laravel\Socialite\Two\User as ProviderUser;
interface UserRepositoryInterface
{
public function socialLogin(ProviderUser $providerUser, string $provider);
}
9. Binding repository and interface
Create a new service provider App\Providers\RepositoryServiceProvider.php
.
Now bind App\Repositories\User\UserReporitoryInterface.php
with App\Repositories\User\UserReporitory.php
in App\Providers\RepositoryServiceProvider.php
.
<?php
namespace App\Providers;
use App\Repositories\User\UserRepository;
use App\Repositories\User\UserRepositoryInterface;
use Illuminate\Support\ServiceProvider;
class RepositoryServiceProvider extends ServiceProvider
{
/**
* Register services.
*
* @return void
*/
public function register()
{
$this->app->bind(UserRepositoryInterface::class, UserRepository::class);
}
/**
* Bootstrap services.
*
* @return void
*/
public function boot()
{
//
}
}
Now, include RepositoryServieProvider in config/app.php
.
'providers' => [
// Other service providers...
App\Providers\RepositoryServiceProvider::class,
],
10. Create Resolver
Now we have to create a resolver file in App\Services\Resolver\SocialUserResolver.php
.
<?php
namespace App\Services\Resolver;
use App\Repositories\User\UserRepository;
use Exception;
use Coderello\SocialGrant\Resolvers\SocialUserResolverInterface;
use Illuminate\Contracts\Auth\Authenticatable;
use Laravel\Socialite\Facades\Socialite;
class SocialUserResolver implements SocialUserResolverInterface
{
/**
* Resolve user by provider credentials.
*
* @param string $provider
* @param string $accessToken
*
* @return Authenticatable|null
*/
public function resolveUserByProviderCredentials(string $provider, string $accessToken): ?Authenticatable
{
$providerUser = null;
try {
$providerUser = Socialite::driver($provider)->stateless()->userFromToken($accessToken);
} catch (Exception $exception) {
}
if ($providerUser) {
$userRepository = new UserRepository();
return $userRepository->socialLogin($providerUser, $provider);
}
return null;
}
}
Here we use the user repository. If you have done it differently, you can change.
11. Binding the resolver
Go to App\Providers\AppServiceProvider.php
and place the following inside the binding attribute.
\Coderello\SocialGrant\Resolvers\SocialUserResolverInterfaceSocialUserResolverInterface::class => \App\Services\Resolver\SocialUserResolverSocialUserResolver::class,
That’s it in our project. Now we need to create a new request in Postman.
12. Create social login request
Create a post request <project_url>/oauth/token with following parameters. This oauth/token is provided by passport. You don't need to create a new route for this.
All the fields are required.
grant_type: always “social” for social login.
client_id: id from oauth_clients database table.
client_secret: secret column value from oauth_clients table
provider: provider name like facebook or google.
access_token: access token provided by the respective provider.
You can see this article to know how to get a google access token for your email.
After filling all the fields, send the request and you will get a token in the response. And you can use the token to make other authorized requests.
Hope it helps!