Authenticate With Any OAuth Provider With Laravel Socialite
Tags: laravel, php
Published: 2023-08-25 19:48:20

Ever tried to create an account on a website and find out there's no option to "login with Google"? Annoying, right? You're not alone. Luckily for you I can help you prevent that and get more signups for your app

In this post I will show you how to create a universal Socialite Controller that works for any socialite driver

To get start, let's install socialite with composer on a Laravel pronect

```bash
composer require laravel/socialite
```

So the way socialite works is that a user clicks on a link that redirects them to a 3rd party site to login and then redirected back to your site for the actual login.

So first, let's create the socialite controller

```bash
php artisan make:controller SocialiteController
```

## Redirect

So this is when we redirect the user to 3rd party OAuth provider to login. Add this method to the SocialiteController

```php
/**
 * @param string $driver
 * @return RedirectResponse
 */
public function redirect(string $driver): RedirectResponse
{
    return Socialite::driver($driver)->redirect();
}
```

So the redirect takes a `$driver` as an argument so we can add any driver any time. Say, you had used Google before, and now you want add GitHub, you just change a route parameter

## Callback

After the user is logged in, they are  redirected to a callback URI that does the actual login. Add the following method to the controller

```php
/**
 * @param string $driver
 * @return RedirectResponse
 */
public function callback(string $driver): RedirectResponse
{
    try {
        $socilite = Socialite::driver($driver)->user();
    } catch (Exception) {
        return to_route('login')->with('error', 'Something went during. Please try again.');
    }

    $user = User::whereEmail($socilite->getEmail())->first();

    if (is_null($user)) {
        $user = User::create([
            'name' => $socilite->getName(),
            'email' => $socilite->getEmail(),
            'password' => Hash::make($socilite->getId()),
        ]);

        event(new Registered($user));
    }

    auth()->login($user, true);
    session()->regenerate();

    return redirect()->intended(RouteServiceProvider::HOME);
}
```

This method also takes the `$driver` as an argument to tell socialite which provider was used login.

If for some reason something went wrong, we redirect the user to the login page with a flash error message

We then check if the user has an existing account, if not, we create an new one with a random password which they can change later from the profile page if they so wish

Else, we log them in and regenerate session to prevent session high jacking by bad actors

Finally we redirect the user to the intended URL

## Routes

To  make this work, we need to register the routes for this controller

```php
Route::get('/auth/{driver}/redirect', [SocialiteController::class, 'redirect'])
    ->name('socialite.redirect')
    ->whereIn('driver', ['google']);

Route::get('/auth/{driver}/callback', [SocialiteController::class, 'callback'])
    ->name('socialite.callback')
    ->whereIn('driver', ['google']);
```

Here we add `{driver}` as a route parameter which will be injected into the `callback` and `redirect` method thanks to [Laravel's Dependncy Injection](https://laravel.com/docs/10.x/controllers#dependency-injection-and-controllers).

Say a bad actor realizes that the `{driver}` parameter is dynamic, and tries other characters like a social driver that does not exist, that could have unwanted side effects.

To prevent this, we call the `whereIn()` method and tell Laravel to expect only a defined set of values for the `driver` param, in this case just "google". Anything else besides that will throw a 404 Not Found.

## Socialite authenticate with Google

Our code is ready for use, in production! In this example, let's allow users to login to our app using their google accounts.

First, get credentials from Google Cloud Console

**Step 1**, visit <https://console.cloud.google.com> and signin with your google account

**Step 2**, Create a new project or use an existing one

**Step 3,** Click the 3bar menu and select APIs & Services > Credentials

![image](https://res.cloudinary.com/dwinzyahj/image/upload/v1692917279/Screenshot_from_2023-08-24_23-26-23_r58ryd.png)

**Step 4**, Click on Create Credentials > OAuth Client ID

**Step 5**, Select Web Application from the dropdown and enter name and redirect URIs

![image](https://res.cloudinary.com/dwinzyahj/image/upload/v1692917279/Screenshot_from_2023-08-24_23-30-02_iktsou.png)

On the redirect URI this is where you put the link to your callback route replace the `{driver}` param with "google". For example `http://localhost:8000/auth/google/callback`

Now all that is left is just creating a link that your users can click to login with Google. For example if you have a Breeze Starter kit installed

```html
<a href='{{ route('socialite.redirect', 'google') }}'>
    <x-secondary-button aria-label="Continue with google"
                        class="w-full justify-center mt-5">
        <img src="https://tuk-cdn.s3.amazonaws.com/can-uploader/sign_in-svg2.svg"
             alt="google">
        <p class="text-base font-medium ml-4 text-gray-700">Continue with Google</p>
    </x-secondary-button>
</a>
```

I hope this helps you to create more smooth login flows for your apps. To make sure you don't miss another tutorial like this when it's up, follow me here on DEV and get a ping each time I make a new post.