Image Principale

Découverte de Laravel Passport


Dans cet article, nous allons installer le nouveau package Laravel Passport sur une une installation de Laravel 5.3. Nous découvrirons comme authentifier des utilisateurs depuis une API très simplement et de façon sécurisée

Contact Person Mathieu Marteau
il y a 1 an

Pour cet article, nous partirons avec une toute nouvelle installation de Laravel 5.3. Assurez-vous d'avoir installé la dernière version de Laravel Installer sur votre machine de développement.

Pour commencer le développement, il vous faudra avoir à votre disposition une installation de Laravel 5.3 qui a accès à une base de données. Pour cela, c'est comme d'habitude, vous pouvez par exemple utiliser la box Vagrant Homestead créée pour Laravel.

Une fois cela fait, il vous faudra installer Laravel Passport. L'équipe de développement de Laravel a fait le choix de proposer ces fonctionnalités sous la forme d'un package indépendant, afin de laisser le choix aux développeurs d'utiliser ou non ces nouvelles fonctionnalités.

composer require laravel/passport

Comme tous les packages Laravel, il vous faudra ensuite indiquer dans votre code que vous voulez utiliser ce package en allant modifier le fichier config/app.php:

'providers' => [
    ...
    Laravel\Passport\PassportServiceProvider::class,
    ...
],

Une fois cela fait, il n'y a plus qu'à lancer les migrations afin de créer les différentes tables nécessaires à l'utilisation de Laravel Passport:

php artisan migrate

En plus des traditionnelles tables users et password_resets, la commande va alors créer 5 nouvelles tables dont nous allons détailler l'utilisation dans la suite de ces articles:

  • oauth_access_tokens
  • oauth_auth_codes
  • oauth_clients
  • oauth_personal_access_clients
  • oauth_refresh_tokens

    Pour continuer l'installation, il vous faudra lancer une dernière commande:

    php artisan passport:install

    Cette commande va inscrire plusieurs entrées dans votre base de données pour que vous puissiez commencer à travailler. Premièrement, dans la table oauth_clients, cela va vous créer deux entrées:

  • Un "Personal Access Client"
  • Un "Password Grant Client"

    De plus, cela va ajouter une entrée dans la table oauth_personal_access_clients. Nous détaillerons à quoi servent ces 3 lignes dans la suite de cet article.

Il faudra également indiquer dans la classe app/User.php que celle-ci peut utiliser les fonctions liées à l'authentification par tokens:

<?php

namespace App;

use Laravel\Passport\HasApiTokens;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    use HasApiTokens, Notifiable;
}

Nous allons ensuite créer dans notre application les routes permettant d'utiliser notre nouvelle méthode d'authentification. Pour cela, rendez-vous dans le fichier app/Providers/AuthServiceProvider.php:

...
use Laravel\Passport\Passport;
...

class AuthServiceProvider extends ServiceProvider
{
    ...
    public function boot()
    {
        $this->registerPolicies();

        Passport::routes();
    }
}

Enfin, pour terminer l'installation, il faudra indiquer dans le fichier config/auth.php que vous voulez utiliser un nouveau Guard correspondant à Laravel Passport.

'guards' => [
    'web' => [
        'driver' => 'session',
        'provider' => 'users',
    ],

    'api' => [
        'driver' => 'passport',
        'provider' => 'users',
    ],
],

Nous sommes enfin prêts à utiliser Laravel Passport!

Nous allons maintenant voir un exemple simple qui va nous permettre d'authentifier un utilisateur grâce à son mot de passe. Cela pourra être utile par exemple lors de la création d'une application mobile afin d'authentifier notre utilisateur lors d'une communication avec notre application Laravel.

Pour la démonstration, j'ai créé un utilisateur:

name: "Martin Loleur"
email: "martin.loleur@gmail.com"
password: "password"

Nous allons ensuite envoyer une requête HTTP à notre application afin de demander une token qui nous autorisera ensuite à demander des ressources à notre application.

Pour cela, j'utilise postman qui permet d'effectuer des requêtes HTTP très facilement.

Pour commencer, rendez-vous dans votre base de données, et jetez un coup d'oeil à la table oauth_clients. Vous verrez les deux lignes créées par la commande passport:install.

Pour mieux comprendre les champs de cette table, imaginez que vous voulez savoir depuis quelle application mobile a été demandée une certaine ressource, avec une certaine token. Pour cela, il suffirait de créer une nouvelle entrée dans oauth_clients qui aurait pour nom "Application 2.0 Grant Client".

Dans cet article, nous voulons que les utilisateurs puissent s'identifier à l'aide de leur mot de passe. Le type du client que nous allons utiliser est donc un "Password Grant Client". Comme vous le voyez, cette table comprend un champs personnal_access_client et password_client. Il existe une différence entre ces deux types de clients:

  • Les personnal_access_client permettent à vos utilisateurs de se créer eux-même des clients. Ainsi, si vous souhaitez autoriser la création d'applications tierces qui se connectent à votre système, vos utilisateurs pourront créer un client, puis demander des tokens à l'aide de ce client. Si vous vous rendez-compte que ce client n'est plus fiable, vous pouvez l'empêcher de créer de nouvelles tokens, et rendre inactives toutes celles qu'il aurait autorisé en passant son champs revoked à 1. On remarquera aussi que ce type de client doit être associé à un utilisateur de la table users grâce au champs user_id.
  • Les password_client vous permet de créer des tokens à partir de mots de passe des utilisateurs. C'est ce type de clienbt que nous allons utiliser. On remarquera que son champs user_idest NULL car aucun utilisateur ne doit être associé à ce type de client.

    Copiez-coller le champs secret de la ligne My Application Password Grant Client. qui est de type password_client.

    Nous allons maintenant faire une requête à l'URL suivante de notre application afin de demander une token:

    POST /oauth/token
    Body {
    grant_type: "password",
    client_id: "2",
    "client_secret": "VotreClientSecret",
    "username": "martin.loleur@gmail.com",
    "password": "password"
    }

Voici la réponse de cette requête:

{
  "token_type": "Bearer",
  "expires_in": 3155673600,
  "access_token": "SomeRandomString",
  "refresh_token": "SomeRandomString"
}

Tel que configuré par défaut, la refresh_token ne sert pas à grand chose puisque la durée d'expiration d'une access_token par défaut est de 100 ans. Nous entrerons plus en détail de cette notion plus bas.

Nous allons maintenant tester notre système de token en essayant d'accéder à une ressource. Rendez-vous dans le fichier routes/api.php et vous verrez qu'un code est présent par défaut:

Route::get('/user', function (Request $request) {
    return $request->user();
})->middleware('auth:api');

Il est donc temps de faire la requête GET suivante:

GET /api/user
{
    headers: {
        Authorization: "Bearer LaTokenObtenuePrécédemment"
    }
}

Comme vous le verrez, vous obtiendrez:

{
  "id": 1,
  "name": "Martin Loleur",
  "email": "martin.loleur@gmail.com",
  "created_at": "2016-09-09 18:10:24",
  "updated_at": "2016-09-09 18:10:24"
}

Félicitations, vous utilisez Laravel Passport!

EDIT 17/11/2016

Quelques petites précisions s'imposent soulevées par Stéphane dans les commentaires:

1) Les requêtes api doivent absolument comporter les en-têtes "Accept": "application/json" et "Content-type": "application/json", autrement elles seront considérées comme des requêtes web classiques et redirigées vers l'url de login.

2) Sur Postman, le champ "Authorization" dans le header passera sans problèmes car il n'y a pas de preflight request. Par contre, depuis une application javascript (dans mon cas Vue.js avec Vue-resource), le mécanisme de preflight bloque la requête si le serveur ne répond pas avec un header acceptant explicitement "Authorization" et "Content-type". J'ai résolu le problème en ajoutant cette ligne au début du fichier /routes/api.php :header( 'Access-Control-Allow-Headers: Authorization, Content-Type' );