Server OAuth2 with Laravel Passport

Server OAuth2 with Laravel Passport

If you have tried to create an OAuth server you will know that it can be quite complex. To simplify this problem, you can use the Laravel’s Passport library, which allows you to set up an Oauth2 server in your application in a simple and very fast way.
Assuming you have the basic notions of **Oauth2** and **Laravel**, we will analyze how to build an OAuth2 server using the **Laravel Passport library**.

### Server configuration
Install the dependencies required by the Passport library.
Following the installation, you will need some configurations to make Laravel recognize the Passport library.
We install Passport using composer:

```sh
$composer require laravel/passport
```

Now we’ll have to make sure Laravel recognizes it.
We activate the service in our Laravel application, adding the PassportServiceProvider provider to the list of providers in **config/app.php**:

```sh
...
'providers' => [
 
        /*
         * Laravel Service Providers...
         */
        Illuminate\Auth\AuthServiceProvider::class,
        Illuminate\Broadcasting\BroadcastServiceProvider::class,
        Illuminate\Bus\BusServiceProvider::class,
        Illuminate\Cache\CacheServiceProvider::class,
        Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class,
        Illuminate\Cookie\CookieServiceProvider::class,
        Illuminate\Database\DatabaseServiceProvider::class,
        Illuminate\Encryption\EncryptionServiceProvider::class,
        Illuminate\Filesystem\FilesystemServiceProvider::class,
        Illuminate\Foundation\Providers\FoundationServiceProvider::class,
        Illuminate\Hashing\HashServiceProvider::class,
        Illuminate\Mail\MailServiceProvider::class,
        Illuminate\Notifications\NotificationServiceProvider::class,
        Illuminate\Pagination\PaginationServiceProvider::class,
        Illuminate\Pipeline\PipelineServiceProvider::class,
        Illuminate\Queue\QueueServiceProvider::class,
        Illuminate\Redis\RedisServiceProvider::class,
        Illuminate\Auth\Passwords\PasswordResetServiceProvider::class,
        Illuminate\Session\SessionServiceProvider::class,
        Illuminate\Translation\TranslationServiceProvider::class,
        Illuminate\Validation\ValidationServiceProvider::class,
        Illuminate\View\ViewServiceProvider::class,
 
        /*
         * Package Service Providers...
         */
        Laravel\Tinker\TinkerServiceProvider::class,
 
        /*
         * Application Service Providers...
         */
        App\Providers\AppServiceProvider::class,
        App\Providers\AuthServiceProvider::class,
        App\Providers\BroadcastServiceProvider::class,
        App\Providers\EventServiceProvider::class,
        App\Providers\RouteServiceProvider::class,
        Laravel\Passport\PassportServiceProvider::class,
],
...
...
```

Next we launch the artisan migrate command to create the necessary tables for the Passport library in the database.

```sh
$php artisan migrate
```

More precisely, the tables created in the DB are the following:

```sh
oauth_access_tokens
oauth_clients
oauth_auth_codes
oauth_personal_access_clients
oauth_refresh_tokens
```

Next, we need to generate a public and private key pair that we will use in the Passport library for encryption.
To do this we’ll use the artisan command:

 
```sh
$php artisan passport:install
```

This command creates the keys in **storage/oauth-public.key** e **storage/oauth-private.key**, in addition to create the credentials of some demo clients (which we will discuss shortly).
Later we add OAuth to the existing user model.
To do this we’ll add to the user model class:

```sh


namespace App;
 
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Laravel\Passport\HasApiTokens;
 
class User extends Authenticatable
{
    use HasApiTokens;
 
    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name', 'email', 'password',
    ];
 
    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password', 'remember_token',
    ];
}
```

**HasApiToken** contains support methods that are used for validating tokens in the request and for checking resources.
Furthermore, we need to register the routes provided by the Passport library in our Laravel application.
These paths will be used for standard OAuth2 operations such as authorization, request for access tokens and others.
Add the Passport routes, in **app/Providers/AuthServiceProvider.php**:

```sh
...
/**
  * Register any authentication / authorization services.
  *
  * @return void
  */
public function boot()
{
    $this->registerPolicies();
     
    Passport::routes();
}
...
```

Finally, update the **config/auth.php** file, as we are going to use the Passport library for **API authentication**.

```sh
	'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
 
        'api' => [
            'driver' => 'passport',
            'provider' => 'users',
        ],
    ],
```

Now, we’ve done all that is required to configuring the OAuth2 server.

### Configure demo resources
In this section, we will set up a demo resource that can be requested by an API call.
Our demo resource will return user information as long as there is a valid **uid** parameter present in the **GET request**.
Let’s create a controller **app/Http/Controllers/UserController.php** with the following content.

```sh
get("uid", 0);
      $user = User::find($user_id);
      return $user;
    }
}
```

As usual, you need to add a route in the **routes/web.php** file, but since we are talking about the API path, it needs special treatment.
The API routes are defined in **theroutes/api.php** file.
So let’s go ahead adding our customized API path:

```sh
get('/user', function (Request $request) {
    return $request->user();
});
 
// custom API route
Route::middleware('auth:api')->get('/user/get', 'UserController@get');
```

Although we have defined it as **/user/get**, the API route is **/api/user/get**, which is what you should use when requesting a resource on that route. The prefix of the API is managed automatically by Laravel.

### How to use the OAuth2 APIs
In this last section, we will discuss how to create client credentials to consume the **OAuth2 API**.

After creating the OAuth2 server for your application, we can allow a third party to connect and use the available APIs.
First of all, third-party apps have to register with their application to be able to use the APIs.

In a nutshell they are considered as client applications and will receive a client id and a secret client upon registration.
The Passport library provides an artisan command to create client accounts.

We show how to create an account for a demo client:

```sh
$php artisan passport:client

User ID should the client be assigned to:
 > 1
 
 Name of the client:
 > Demo OAuth2 Client Account
 
 Redirect the request after authorization: [http://localhost/auth/callback]:
 > http://localhost/oauth2_client/callback.php
 
New client.
Client ID: 1
Client secret: zMm0tQ9Cp7LbjK3QTgPy1pssoT1X0u7sg0YWUW01
```

By executing the **passaport::clientcommand**, calls are made before allowing the creation of the client account.
Among these, one of the most important, asks for the callback URL.
The callback URL indicates where the user will be postponed after third-party authorization.
And this is where the authorization code will be used to exchange the access token, will be sent.
Now, let’s test the OAuth2 APIs in the Laravel application.
For demonstration purposes, we will create the **oauth2_client directory** under the root.
We create the file **oauth2_client/auth_redirection.php** with the following content.

```sh
 '1',
    'redirect_uri' => 'http://localhost/oauth2_client/callback.php',
    'response_type' => 'code',
    'scope' => '',
));
 
header('Location: http://your-laravel-site-url/oauth/authorize?'.$query);
```
Let's make sure to adjust the client_id and redirect_uri parameters.
So, let's create the file **oauth2_client/callback.php** with the following content.
```sh
 'authorization_code',
        'client_id' => '1',
        'client_secret' => 'kMw3rP2dP5hgjk3OYfPy1fcfon1D3l3sr2DMFWP3',
        'redirect_uri' => 'http://localhost/oauth2_client/callback.php',
        'code' => $_REQUEST['code']
    );
 
    curl_setopt($c,CURLOPT_URL, $url);
    curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
 
    $params_strn = '';
 
    if (is_array($prms) && count($prms))
    {
        foreach($prms as $key=>$value) {
            $params_strn .= $key.'='.$value.'&';
        }
 
        rtrim($params_strn, '&');
 
        curl_setopt($c,CURLOPT_POST, count($prms));
        curl_setopt($c,CURLOPT_POSTFIELDS, $params_strn);
    }
 
    $res = curl_exec($c);
    curl_close($c);
    $response = json_decode($res);
     
    // check if the response includes access_token
    if (isset($response->access_token) && $response->access_token)
    {
        //  store the access_token in the session
        $access_token = $response->access_token;
 
        // use above token to make further api calls in this session or until the access token expires
        $c = curl_init();
        $url = 'http://your-laravel-site-url/api/user/get';
        $header = array(
        'Authorization: Bearer '. $access_token
        );
        $query = http_build_query(array('uid' => '1'));
 
        curl_setopt($c,CURLOPT_URL, $url . '?' . $query);
        curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($c, CURLOPT_HTTPHEADER, $header);
        $res = curl_exec($c);
        curl_close($c);
        $response = json_decode($res);
        var_dump($res);
    }
    else
    {
        // debugging here
    }
}
```

Make sure to adjust the URLs and credentials of the client according to the configuration in the file above.
### How it works
Now let’s test everything.
As an end user, we have two applications available:
1. The first is the Laravel application on which we already have an account and contains information that you can share with other third-party applications.
2. The second is the third-party demo client application, auth_redirection.php and callback.php, which retrieves information from the Laravel application using the OAuth API.

The flow starts with the third-party client application.

Let’s go ahead and open the URL **http://localhost/oauth2_client/auth_redirection.php** in browser, which should redirect the user to the Laravel application.
If you have not already logged into the Laravel application, the application will ask you to do so.
Once the user is logged in, the application displays the authorization page.
If the user authorizes this request, he will be redirected to the third-party client application, at the address **http://localhost/oauth2_client/callback.php**.

Once the third-party application receives the authorization code, exchange that code with the Laravel application to get the access token. And that’s exactly what is done in the file **oauth2_client/callback.php**.

```sh
$c = curl_init();
$url = 'http://your-laravel-site-url/oauth/token';
 
$prms = array(
    'grant_type' => 'authorization_code',
    'client_id' => '1',
    'client_secret' => 'kMw3rP2dP5hgjk3OYfPy1fcfon1D3l3sr2DMFWP3',
    'redirect_uri' => 'http://localhost/oauth2_client/callback.php',
    'code' => $_REQUEST['code']
);
 
curl_setopt($c,CURLOPT_URL, $url);
curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
 
$params_strn = '';
 
if (is_array($prms) && count($prms))
{
    foreach($prms as $key=>$value) {
        $params_strn .= $key.'='.$value.'&';
    }
 
    rtrim($params_strn, '&');
 
    curl_setopt($c,CURLOPT_POST, count($prms));
    curl_setopt($c,CURLOPT_POSTFIELDS, $params_strn);
}
 
$res = curl_exec($c);
curl_close($c);
$response = json_decode($res);
```

Next, the third-party application checks the response of the CURL request to see if it contains a valid access token.

As soon as the third-party application obtains the access token, it will use it to make further API calls to request resources from the Laravel application.
Of course, the access token must be passed in every request that requires resources from the Laravel application.

We have tried to imitate the use case as the third party application wants to access user information from the Laravel application.

We have already built an API endpoint, **http://your-laravel-site-url/api/user/get**, in the Laravel application that simplifies the task:

```sh
// check if response includes access_token
if (isset($response->access_token) && $response->access_token)
{
    // store the access_token in the session...
    $access_token = $response->access_token;
 
    // use above token to make further api calls in this session or until the access token expires
    $c = curl_init();
    $url = 'http://your-laravel-site-url/api/user/get';
    $header = array(
    'Authorization: Bearer '. $access_token
    );
    $query = http_build_query(array('uid' => '1'));
 
    curl_setopt($c,CURLOPT_URL, $url . '?' . $query);
    curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($c, CURLOPT_HTTPHEADER, $header);
    $res = curl_exec($c);
    curl_close($c);
    $response = json_decode($res);
    var_dump($res);
}
```

And this is the complete flow to consume the **OAuth2 APIs in Laravel**.

In finish, although Laravel already makes it easy to perform authentication via traditional login forms, makes API authentication is a bit breeze using Laravel Passport, which provides a full **OAuth2** server implementation for your Laravel application in a matter of minutes.

One Reply to “Server OAuth2 with Laravel Passport”

Leave a Reply

Your email address will not be published.