nickbeen / socialiteproviders-google-one-tap
Google One Tap provider for Laravel Socialite
Requires
- php: ^8.1
- ext-json: *
- google/apiclient: ^2.15
- socialiteproviders/manager: ^4.5
Requires (Dev)
- orchestra/testbench: ^8.0|^9.0
- pestphp/pest: ^2.0
This package is auto-updated.
Last update: 2024-10-06 15:01:17 UTC
README
A provider for Laravel Socialite that allows authentication for Google through Google One Tap. The Google One Tap framework is build on top of OAuth2, but does not use the traditional OAuth authorize user flow. Instead of returning an access token, it returns an authenticating JWT token that expires after an hour.
Google One Tap does not sync with the session of your application, so you should solve this within your application. As long as the credentials of the user aren't revoked and the user is logged in with their Google account or Google Chrome browser, the application will be able to grab a new JWT token with minimal user interaction when necessary.
Installation
composer require nickbeen/socialiteproviders-google-one-tap
This package depends on google/apiclient
(including 200+ google\api-clients-services
packages) and will be also be included when installing this package.
You can run the google-task-composer-cleanup
script in composer.json
to only keep the Google API client packages needed for running this Socialite provider. DO NOT run this script if your application depends on google/apiclient
.
Usage
Please see the Base Installation Guide if Laravel Socialite isn't installed yet into your application.
Setup Google project
First you might need to create a new project at Google Cloud console, set up the OAuth consent screen and create a new OAuth Client ID. Within the Credentials menu you will find the client ID and client secret which you will need for authenticating.
Add configuration
You will need to store the client ID and client secret in your .env
file and add the configuration to config/services.php
. You will also need to add a redirection url which will be used for logging in and registering with Google One Tap. This package refers to a specific .env value for Google One Tap to avoid any clashes with the standard Google Socialite provider.
# .env GOOGLE_CLIENT_ID=314159265-pi.apps.googleusercontent.com GOOGLE_CLIENT_SECRET=mkhYkO_ECIIp11mcZ3CEClhmgh_9FWTV2M_ GOOGLE_LOGIN_URI=/auth/google-one-tap
# config/services.php return [ // other providers 'google-one-tap' => [ 'client_id' => env('GOOGLE_CLIENT_ID'), 'client_secret' => env('GOOGLE_CLIENT_SECRET'), 'redirect' => env('GOOGLE_LOGIN_URI'), ], ];
Add provider event listener
Configure the package's listener to listen for SocialiteWasCalled
events. Add the event to your boot()
method in app/Providers/AppServiceProvider
in Laravel 11 or add the event to your listen[]
array in app/Providers/EventServiceProvider
in Laravel 10. See the Base Installation Guide for detailed instructions.
With Laravel 11:
namespace App\Providers; use Illuminate\Support\Facades\Event; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider { /** * Bootstrap any application services. */ public function boot(): void { Event::listen(function (\SocialiteProviders\Manager\SocialiteWasCalled $event) { $event->extendSocialite('google-one-tap', \SocialiteProviders\GoogleOneTap\Provider::class); }); } }
With Laravel 10:
namespace App\Providers; use Illuminate\Support\ServiceProvider; class EventServiceProvider extends ServiceProvider { protected $listen = [ \SocialiteProviders\Manager\SocialiteWasCalled::class => [ // other providers \SocialiteProviders\GoogleOneTap\GoogleOneTapExtendSocialite::class, ], ]; }
Usage
Google One Tap requires a specific implementation both in the front-end as the back-end.
Front-end
On every page where you want to use Google One Tap, you will need to include the following script in the header of your html templates.
@guest <script src="https://accounts.google.com/gsi/client" async defer></script> @endguest
The actual Google One Tap prompt can be initiated with either javascript or html. The following code handles the response server side in html. It does not matter where you place this code. You can also append data-client_id
and data-login_uri
to any existing html element. Check references for more settings and variations such as a full javascript implementation.
@guest <div id="g_id_onload" data-auto_select="true" data-client_id="{{ config('services.google-one-tap.client_id') }}" data-login_uri="{{ config('services.google-one-tap.redirect') }}" data-use_fedcm_for_prompt="true"> </div> @endguest
Styling this element won't have any effect since Google One tap is migrating to FedCM which means the prompt will be handled by the browser itself if the browser supports it.
For signing out you should add a g_id_signout
class to your sign-out button to avoid a redirection loop because of data-auto_select
in the previous snippet.
<form action="{{ route('logout') }}" method="post"> @csrf <button class="g_id_signout">Sign out</button> </form>
Google One Tap has a cooldown period when a user closes the Google One Tap prompt. The more often a user closes the prompt, the longer it will take for the prompt to be able to reappear to the user. Therefore, you need to include a sign-in button for a fallback to a Google Sign-In prompt. You will likely only want to include this button on login and register pages. Only the data-type field is required.
<div class="g_id_signin" data-type="standard"> </div>
Back-end
Google One Tap is build on top of OAuth, but works different with an authenticating JTW token instead of with access tokens and refresh tokens. The redirect()
and refreshToken()
method won't be used in this context and will throw a DisallowedMethodException
as a reminder.
Your controller won't need to redirect the user and instead of resolving the user, you can immediately resolve the token.
use Laravel\Socialite\Facades\Socialite; return Socialite::driver('google-one-tap')->userFromToken($token);
This method will return the payload of the JWT token or throw an InvalidIdTokenException
if the provided token was invalid.
Payload array
Only use id
field as identifier for the user as it is unique among all Google Accounts and never reused. Don't use email
as an identifier because a Google Account can have multiple email addresses at different points in time.
Using the email
, email_verified
and host_domain
fields you can determine if Google hosts and is authoritative for an email address. In cases where Google is authoritative the user is confirmed to be the legitimate account owner.
Handling the payload
With the payload containing the id
you can now handle the user flow after the user finished interacting with the Google One Tap prompt. This usually involves either registering the user if the Google ID isn't present in your database or logging in the user if you have a user registered with this Google ID.
Optionally you can use email
to check if the user already has a user account or Socialite credentials from another provider, and possibly connect the accounts or notify the user account. In basic Laravel code it would look something like this:
// routes/web.php use App\Controllers\Auth\GoogleOneTapController; use Illuminate\Support\Facades\Route; Route::post('auth/google-one-tap', [GoogleOneTapController::class, 'handler']) ->middleware('guest') ->name('google-one-tap.handler');
// e.g. GoogleOneTapController.php use App\Models\User; use Illuminate\Http\Request; use Laravel\Socialite\Facades\Socialite; use SocialiteProviders\GoogleOneTap\Exceptions\InvalidIdTokenException; public function handler(Request $request) { // Verify and validate JWT received from Google One Tap prompt try { $googleUser = Socialite::driver('google-one-tap')->userFromToken($request->input('credential')); } catch (InvalidIdTokenException $exception) { return response()->json(['error' => $exception]) } // Log the user in if the Google ID is associated with a user if ($googleUser = User::where('google_id', $googleUser['id'])->first()) { auth()->login($googleUser); } // Send user to registration form to provide missing details like username return redirect()->view('register.google-one-tap', compact('googleUser')) }
FAQ
How can I use authoritative scopes with Google One Tap to e.g. upload to Google Drive?
Google One Tap can only be used for authentication (who you are). For authorization, you need to use the built-in Google provider of Laravel Socialite. Both providers can be used simultaneously to give you the best of both worlds.
Can I check if a user logged in with One Tap, used an existing session, etc.?
The select_by
field in the response from Google contains several possible values like auto
, user
and user_1tap
that indicate how the user interacted with your application when signing up or signing in. In Laravel the value can be easily accessed in your controller.
$select_by = request()->input('select_by')
References
- https://piraces.dev/posts/how-to-use-google-one-tap/
- https://developers.google.com/identity/gsi/web/guides/overview
- https://developers.google.com/identity/gsi/web/reference/html-reference
- https://developers.google.com/identity/gsi/web/reference/js-reference
- https://github.com/googleapis/google-api-php-client
- https://googleapis.github.io/google-api-php-client/main/
License
This package is licensed under the MIT License (MIT). See the LICENSE for more details.