OAuth2 authentication across Laravel projects
Tags: oauth, laravel, authentication, tutorial
Published: 2020-01-26 13:24:21

# Introduction
A question popping up every now and then is how to let users log in to separate (child) applications using a single account they own on a central application. 

In this post I try to explain how to achieve this infrastructure, by creating a central application using  [Laravel Passport](https://laravel.com/docs/6.x/passport)  (example.com) where users register once and then use  [OAuth2](https://oauth.net/2/)  to grant access to their account to the other applications (app1.example.com, app2.example.com, etc.) using  [Laravel Socialite](https://laravel.com/docs/6.x/socialite) . In this way, users will be able to login to the child applications without creating a new, separate account.

*Note:* the applications do not necessarily have to use the same domain.

![Authorization request (Laravel Passport)](https://johnbraun.blog/storage/wink/images/fmIHt5lcvv8wU7cEkePwfjV3QussBJ7fHbAYSE9Y.png)

If you're not yet familiar with the OAuth2 protocol, I've included a section on the how and what below. 

# What is OAuth2?
Before jumping into *Laravel Passport*, it is important to understand the OAuth protocol it implements. 

OAuth is an open standard, designed to provide **API access delegation**. Think of using a **third party** Twitter app which can tweet **on your behalf** to the Twitter platform. 

I explicitly mention [Twitter](https://www.twitter.com/)  since development of this standard was (amongst others) driven by lead developer [Blane Cook](https://en.wikipedia.org/wiki/Blaine_Cook_(programmer)), in need of authorization of external parties.

After initial release of version 1.0 in 2010, the protocol matured over the course of 2 years after which version 2.0 was released. This improved protocol offers support for **Bearer tokens** and provides "specific **authorization flows** for web applications, desktop applications, mobile phones, and smart devices." (via [Wikipedia](https://en.wikipedia.org/wiki/OAuth))

Let's have a look at what is meant with the terms *"Bearer token"* and *"authorization flow"* .

## Bearer tokens
The word "Bearer" means you're in possession of a certain (access) token. The bearer being the third party application, which possesses an **access token** issued by the **identity provider**. 

This token provides **immediate access** to a resource, without requiring a username and password. 

All necessary information is linked to this token, including user details and the **scope** of possible actions this third party may perform on behalf of this user. 

The bearer token is usually included in the Headers of a GET or POST request to an API endpoint. 

A concrete example of using the Bearer token is shown below, making a GET request to `/api/user` using the  [Guzzle HTTP library](http://docs.guzzlephp.org/en/stable/)  to a corresponding API endpoint sending along a Bearer Authorization header. 

```php
$response = $client->request('GET', '/api/user', [
    'headers' => [
        'Accept' => 'application/json',
        'Authorization' => 'Bearer ' . $accessToken,
    ],
]);
```

## Authorization flow
Now that we know what a Bearer access token is ... how do we obtain one? 
First, let's look at the formal roles in OAuth2:

* **Resource Owner**: the user who wants to login and delegate access to their account details to a third party application
* **Resource Server**: the server (API) where the user has an account
* **Client**: the third party application that wants to access the account information on the resource server

The OAuth 2.0 protocol performs a standard communication flow between **Client** and **Resource Server**, where each step and given/required parameters are defined in advance. 

Ultimately, the **Client** receives the Bearer **access token** from the **Resource server**. 

This process is illustrated in the figure below (created using  [draw.io](https://www.draw.io/?lightbox=1&highlight=0000ff&edit=_blank&layers=1&nav=1&title=OAuth%20dance.drawio#R7Vzdl5o4FP9rPKd9GA8QQH2cL7u70912a3e3fZqDEDUdJJTEUfev3yQk8hFUtMA4ezoPCpckJvfzd2%2FC9MDtcvMu8eLF7ziAYc8ygk0P3PUsa2S77JMTtinBNSRhnqAgJZkZYYL%2BhZJoSOoKBZAUGlKMQ4riItHHUQR9WqB5SYLXxWYzHBZ%2FNfbmUCNMfC%2FUqf%2BggC5S6tAaZPRfIJov1C%2Bb7ih9svRUY7kSsvACvM6RwH0P3CYY0%2FRqubmFIeed4kvab7zn6W5iCYxonQ7bz%2FHDHw9%2Ffnl4Al%2Bub9Bnigi4kqM8e%2BFKLvgTJHiV%2BJBRJzB5homcPd0qliR4FQWQj2r0wM16gSicxJ7Pn66ZDjDagi5DdmeySzYARYyd1yGaR4xGMW8wwxGdyBHVFFhDuNm7NnPHMaZpEC8hTbasiezg2pLJUsuA23dSwjoTmunKRou8wBTRk4oy3w2e8ZJdSHaewFprqHEOBky35C1O6ALPceSF9xn1psjbrM17zPkmOPoNUrqVhuKtKC7yG24Q%2FZK7%2FsqHYsxI7%2B42cmRxs1U3EVsv73Rl9A1zpChpV5utQxKy3uKu0P0jTBDjG1OXlJiunS%2F4sEgZf1J1O8BIafZeMof0mC7rKpLA0KPouTiP5qWtGdJtiPhSX4H1lIxHWcQx0xm1ZTnK9%2BeY%2BR7PUXSAl5xZnmSSz5bN9fBmhsLwFoc4Ea3B2BxbY5vRCU3wE1RPIhzBItc5f3P9nJE7GBqK76nhmVYDfHct5aW2KugZVX7LdHTmA9AW8x2N%2BWaf3efiwod1xMOCsfYiSkRAZh8hnvOhI3Wv1N%2Fw4jhkOkwR1gXIeEeLvK%2BUTV6QkqQJu2wqSxQEwqVWmVjRCPP2BHT5j8dDw8h82g%2BJ3CqamlkVp0CVsVmtGZurybtnuSEXS4Ce2eWcX767%2FyxcaaoDfSKRwRizCLRg3%2FwLJ8I2xqo7I%2BdGqBhUkaZJmVLdVW931mAXqYNN6JYLCso1cnXVMipUC7SmWqM6quULR%2FEoR6qpOgkMUMLw%2FuMqQazfglKeD1zz%2BVpjX7qeMRNFOPX8pxOGJTGOCHyk25jP2GfpTO3OxMcxPGURhHpUoLipb1rgbHVtQHVACQKYhlnlmECX2mPpuUkFD%2B94rNnilQxHKvrECX5GXHaG5%2FuQ7KIUa5mkRGZ9u9Y736FGVcFrJ5Hdkzb9WxPe7CR4edSBpXizCaij6gBSwQamDnOcqvTMbE29dMR%2BvQtih4FmCVcaxtAT%2BEBjZzGQiKbOQDTdjzZn4q8NtOmYRRu3dRm4FUhTlTqal8BAk8CtF%2Fm8fnQK%2B6Fj2jXZf2MZRhX7MzbvF0cDEhhYFyYBoCdarzZr3SHkF0tbVUpWB0kfAyz7o8z%2FFr0CuyhPG9SDr3Zr8rRrwVcOEdlD50blQCLVvRJ051WCQTAs1SWsQUUq0SkYtC1t7R0WU43%2BQFVQv%2BYeHammmnox1bWc3hnF1FJMMozRyGi4yApkrna0ygqMauXppswKaqWU1p6CVQOAnIuix7eblDTc7yucNlByyZHSvrviCMn9XDqQGtygCz5Rv5x3HEkJGg8GSWrLr6Sa5lilmGHqGMBy7ApE1ZqfqhUzREH1b2bks21V9pmGgqrks662SgP4voKEVv1CLil%2BgtGhn3qBcloIZw2oYMFfGs2pXAl1OpaOOodd1m9tPYvSZOKRON0bn6ENZ9JNjBEHfffPkFfwpZjU%2FjTnVOCRxY6dx2QZelMYfsQEiRp%2FBaJ8X2owxZTi5d5M4dhGjmGYw%2BFdOmVRcVtu5vzwQX9JfA%2F2Ra84QQT2n%2BD2MYbJEhHCfpkcUI0EU0%2FOznYbUhWzDKOGOqQ1zS51ZX%2F0lHEtUxoVxPiDKyIQEstWjGG80QOc5l%2FqREv%2BNx7rg8l6MIF%2BAvOhsBgwDzrHYtOLzJhadFGDkosa6uDd7tJDObpD6hS714fuh4XSCM5W8eMoznZe9DiDo1eGKmAG4DBjt9FLEwR3hXa48RdeNIc6jlCGbXhRwD%2FzWbPwFSdsucywqObzfnshTUuF9kvBOR1B7fIBK%2FulkbaqZr%2F%2Bo2vFojCoW8c022OtfgTk44fJ4QMAqdVlZcuf2%2FytFUpLKQgYVOzUdrrP79Q6QnLWPn8RDGYdz6jgtHRkgGUebILyvEAhmD2eFMrOrCO%2FTEEYDHUn1W1B2KlKe08ttGQqaY21fKNGKaQq9zizWLN%2FkocAUqUZnD4DgeN%2BJWQFfxaLmszEXNs%2BajZWldm0FtoVkPiZi%2BUg5IXnYupY3%2BXL7NDWlzM46z2CDtTArasGrvWialDrPJxd2vpSiZBBYBSQijxZpuFvcMzjC0M%2F27fsdkVEpwBSD4W8dYqJ0ty%2Bzi7VCx%2BDu5TA0lF2Xn7%2Fya4INOawy1Kza2na%2BmqPFtmDuil5a6cV3Vr7ip8gXSXcpn%2BbfPiDfeHpN77vI96OZIYcoWi%2Bt1JWwzq9JWd2NCX8603qNzK3wW7%2BSr1GdrpW%2Bo%2B3LdjnpWTjpWq7PaqZjbd2bMnVqzca%2B7UNwZwgxN4gSVNRnnhWsBwtxVuz2tlJSb9DS6Zn4xBN%2BQL4nhz7foDbPnme9wpbbcBpRgqmU3z1wXEqXqMCXbq%2FWiURkZ5OvGeYRdzMdnTDfCOq3bkXrRj4IjQ1at797YFM9fTYK9LSa0Kwj8RxBD0xVIRDu3yj0Z4zMfuSytK5mB9LN7VFrRGvVhp0gUjG81ePMro56%2BBYFUbV0FkHdpu9CS%2Be5f6dALj%2FDw%3D%3D) ). 

![The OAuth authorization flow](https://johnbraun.blog/storage/wink/images/epj9UpbUuBI0bIkbsuM9f6C6d2Vffn8cgyW7OzGo.png)

*Note:* it is assumed that the **Client** (third party application) is registered with the **Resource Server**.

After this little "dance", the **Client** now possesses an **access token** which can either be long-lived or short-lived (more secure). 

If the access token is **short-lived**, the **Resource Server** will also provide a **refresh token** which can be used - in combination with the **client secret** - to obtain a new access token in a way resembling *Step 3* in the diagram above. 

*Note: the "state" parameter is used to prevent CSRF related attacks, by verifying the request is not forged.*

Now, let's see how Laravel Passport implements this protocol.

# Laravel Passport
In case of our example, we want the **identity provider** (example.com) to make use of Laravel Passport.

Laravel Passport is an OAuth2 server, built upon the [League OAuth2 server](https://github.com/thephpleague/oauth2-server) . It provides an easy implementation for existing Laravel applications by requiring the composer package. 

## Resource server
To set up a resource server, follow the clear  [installation instructions](https://laravel.com/docs/6.x/passport#installation)  and/or watch [this explanation on Laracasts](https://laracasts.com/series/whats-new-in-laravel-5-3/episodes/13)  by Taylor Otwell. Don't forget to add the `hasApiTokens` trait to your `User` model. 

After installation, you now have the possibility to add new **Clients**having a **callback url** and an automatically generated **secret**. For each of the "child" applications (`app1.example.com`, `app2.example.com`, etc.) you need to create a new **Client**with their own callback, for now you might choose `https://app1.example.com/login/callback` for example (we will come back to this).

![Creating a new Client in Laravel Passport](https://johnbraun.blog/storage/wink/images/kUoCbgpigADcf0FxPSx3NdYHNM9E6VIIY0l0jFzn.png)

Laravel Passport will take care of the authorization dialog, providing an authorization code, verifying the client secret in combination with the authorization code and lastly provide a User object and (by default) a **long-lived** access token. The lifespan of the access and refresh tokens are  [configurable](https://laravel.com/docs/6.x/passport#token-lifetimes) . 

![Example of a Client having an ID and a secret (in Laravel Passport)](https://johnbraun.blog/storage/wink/images/GOtP1K8X367Gfy0jWaD5uo3ZNcPSQeUgJpqxkswO.png)

# Laravel Socialite
Now that we have a **Resource Server** (identity provider), we need to take care of the **Client**side of things using Laravel's package called  [Laravel Socialite](https://laravel.com/docs/6.x/socialite) .

Out of the box, it allows authentication with the services of *Facebook*, *Twitter*, *LinkedIn*, *Google*, *GitHub*, *GitLab* and *Bitbucket*. However, there is a  [collection of additional providers](https://socialiteproviders.netlify.com/) , amongst which an adapter supporting **Laravel Passport**.

To achieve a shared login system across multiple Laravel applications, my proposed solution involves making use of the  [Socialite provider for Laravel Passport.](https://socialiteproviders.netlify.com/providers/laravel-passport.html) 

Looking at the installation instructions of the Socialite provider, there are quite a number of steps, to make sure a client application is able to identify users using the Laravel Passport identity provider. 

1. Install Laravel Socialite
2. Install Laravel Passport **provider** for Socialite
3. Add methods to `LoginController`
4. Copy the 'laravelpassport' **config** to config/services.php
5. Add `SocialiteWasCalled` **event** and listener to `EventServiceProvider`

Now, this seems like a lot of steps to repeat for every **Client** application we might already have and want to couple to our **Resource Server**. That's why I created a support package, as described in the next section.

# Socialite-Passport support package
To simplify the process, I made [a support package](https://github.com/jhnbrn90/socialite-passport) ) that combines Laravel Socialite with the Passport driver and can be configured in a more efficient way.

![](https://johnbraun.blog/storage/wink/images/0L8EJfCNPKi6zeTkWB4bnqTpYucLijYjUS0rteKL.png)

## Installation
In a **Client** Laravel application that you want to couple to the Passport**Resource Server**, first install the package:

```
composer require jhnbrn90/socialite-passport
```

Next, publish the configuration file:

```
php artisan vendor:publish --provider="JhnBrn90\SocialitePassport\SocialitePassportServiceProvider" --tag="config" 
```

This will publish a file `socialite-passport.php` in the `config` directory, in which you can define which Controller (defaults to `LoginController` that ships with Laravel) and which method should be called when the login route (also configurable) is called.

```php
return [
    'controller' => [
        'class' => \App\Http\Controllers\Auth\LoginController::class,
        'method' => 'loginWithPassport',
    ],

    'route' => [
        'name' => 'login',
        'uri' => '/login',
    ],
];
```

Assuming the defaults in the above configuration, after Authorization is granted by the **Resource Owner** at the **Resource Server** (Laravel Passport), the method `loginWithPassport()` will be called and a `User` object will be injected. 

This method needs to be implemented in the defined controller (in our example, the `LoginController`):

```php
class LoginController extends Controller 
{
    public function loginWithPassport($user) 
    {
        // example:
        User::firstOrCreate(['name' => $user['name'], 'email' => ...]);
    }
}
```

To be able to provide the **Resource Server** with a `client_id`, `callback_uri`, etc. you must add these variables to your `.env` file:

```
LARAVELPASSPORT_CLIENT_ID=
LARAVELPASSPORT_CLIENT_SECRET=
LARAVELPASSPORT_REDIRECT_URI=/login/callback
LARAVELPASSPORT_HOST=https://example.com 
```

Remember, `LARAVELPASSPORT_CLIENT_ID` and `LARAVELPASSPORT_CLIENT_SECRET` come from the Laravel Passport **Resource Server**, where the **Client** needs to be created first. 

The package will make sure to match the route you provide as the `LARAVELPASSPORT_REDIRECT_URI` environment variable and proxies the request through the corresponding method and controller you've configured in the config file.

# Final remarks
That is all it takes to implement a basic functional shared login system. 

I hope this post could shed some light on the OAuth2 protocol and how it can be used with the mentioned tools to achieve a shared authentication system amongst different Laravel projects.

It might be a good idea to also store the access token (and the resource token) on the user model to be able to update information whenever a user changes its profile on the **Resource Server**. Or to collect other data of that user from the Resource Server of course.

## Summary
* [Laravel Passport](https://laravel.com/docs/6.x/passport)  implements a fully functional OAuth2 server (Resource Server)
* [Laravel Socialite](https://laravel.com/docs/6.x/socialite)  implements a way to authenticate with an OAuth2 server (Client)
* The  [Socialite provider](https://socialiteproviders.netlify.com/providers/laravel-passport.html)  for Laravel Passport allows authentication with Laravel Passport
* Clients can be prepared using  [this package](https://github.com/jhnbrn90/socialite-passport)  combining both Socialite with the Passport adapter