Help us understand the problem. What is going on with this article?

Laravel5.5でAPI認証のパッケージ(Laravel Passport)を利用する

More than 1 year has passed since last update.
  • Laravel Passportの動作をmacOSで確認したメモ

環境

macOS Sierra

APIサーバー

  • Vagrant
  • VirtualBox
  • Laravel5.5

https://github.com/niiyz/Laravel-API-Auth-Sample

クライアントサーバー

  • MacのPHPビルトインサーバー

https://github.com/niiyz/Laravel-API-Auth-Client-Server-Sample

MacのPHPバージョンを7.0にする

  • Laravel5.5はPHP7.0以上が必須

brewを使用してPHP7インストール

brew update
brew install homebrew/php/php70  

パス設定

  • シェルの設定に追記後、反映
vi ~/.zshrc
export PATH="$(brew --prefix homebrew/php/php70)/bin:$PATH" <-1行追記する
source ~/.zshrc

確認

  • PHP7.0を確認
php -v                                                                                   
PHP 7.0.22 (cli) (built: Aug  7 2017 14:07:27) ( NTS )
Copyright (c) 1997-2017 The PHP Group
Zend Engine v3.0.0, Copyright (c) 1998-2017 Zend Technologies

APIサーバー(Laravel5.5)

Laravel5.5インストール

composer create-project --prefer-dist laravel/laravel Laravel-API-Auth-Sample dev-develop

プロジェクト直下にHomesteadインストール(vagrant)

composer require laravel/homestead --dev
php vendor/bin/homestead make
  • 起動
vagrant up
  • SSH接続
vagrant ssh

ユーザー認証(通常のユーザー作成準備)

  • ユーザーテーブルSeeder作成
php artisan make:seeder UsersTableSeeder

データ追加処理

  • ユーザーテーブルSeeder追記
database/seeds/UsersTableSeeder.php
<?php

use Illuminate\Database\Seeder;
use App\User;

class UsersTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        factory(User::class)->create([
            'name' => 'テスト',
            'email' => 'test@example.com'
        ]);
    }
}
  • DatabaseSeeder.phpのコメント解除
database/seeds/DatabaseSeeder.php
// コメントアウト
$this->call(UsersTableSeeder::class);

ログイン機能追加

  • ログイン画面用のリソースなどが追加される
php artisan make:auth

API認証

passportインストール

composer require laravel/passport

Provider追記

Laravel PassportパッケージはPackage Auto-Discoveryに対応しているためインストールした際に自動でサービスプロバイダに追加されるのでconfig/app.php記述する儀式が不要になったようです。

config/app.php
/*
 * Package Service Providers...
 */
// Laravel\Passport\PassportServiceProvider::class, // 1行追記

/*
 * Application Service Providers...
 */

DB初期化

php artisan migrate
  • UserSeederに記述したユーザーがusersテーブルに作成される
id         1
name       テスト
email      test@example.com
password   secret
  • OAuth用のテーブルが作成される
oauth_auth_codes
oauth_clients
oauth_personal_acces_clients
oauth_refresh_tokens

OAuth認証のユーザーを作成する

  • 「user ID」 はusers.id を指定する
  • callback用のURLを指定
  • 「Client ID」,「Client secret」がoauth_clientsテーブルに追加される
$ php artisan passport:client

 Which user ID should the client be assigned to?:
 > 1

 What should we name the client?:
 > test

 Where should we redirect the request after authorization? [http://localhost/auth/callback]:
 > http://localhost:3333/callback.php

New client created successfully.
Client ID: 1
Client secret: cZ9W9ysdUFKlowO5d2CzzFFJ1uOBfggjv5Jx5cwr

クライアントサーバー(Macビルドインサーバー)

認証画面を通すやり方

  • よくあるtwitterのアカウントで別サービスにログインする感じのやつ

oAuthサバーへアクセスする

get_token.php
<?php

require 'vendor/autoload.php';

use GuzzleHttp\Client as Client;

$guzzle = new Client;

$query = http_build_query([
    'client_id'     => '1',
    'redirect_uri'  => 'http://localhost:3333/callback.php',
    'response_type' => 'code',
    'scope'         => '',
]);

header('Location: http://192.168.10.10/oauth/authorize?' . $query);

承認画面

スクリーンショット 2017-09-05 20.23.51.png

  • 「Authorize」ボタンを押下するとcodeパラメータ付きでコールバックURLに戻る
  • 「Cancel」ボタン押下するとerrorパラメータ付きでコールバックURLに戻る
  • 承認するとoauth_access_tokens, oauth_auth_codesテーブルにデータが格納される

コールバック

get_token_callback.php
<?php 
require 'vendor/autoload.php';

use GuzzleHttp\Client as Client;

$http = new Client;

if ($_GET['code']) {
    $response = $http->post('http://192.168.10.10/oauth/token', [
        'form_params' => [
            'grant_type'    => 'authorization_code',
            'client_id'     => '1',
            'client_secret' => 'cZ9W9ysdUFKlowO5d2CzzFFJ1uOBfggjv5Jx5cwr',
            'redirect_uri'  => 'http://localhost:3333/callback.php',
            'code'          => $_GET['code'],
        ],
    ]);
    var_dump(json_decode((string)$response->getBody(), true));
}

出力結果

  • 取得したaccess_tokenを使って外部API認証に使う
array(4) {
  ["token_type"]=>
  string(6) "Bearer"
  ["expires_in"]=>
  int(1296000)
  ["access_token"]=>
  string(1071) "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6ImYxMmE0NDZkNmQxZjBlNzA4MWMwZWEwMzg5MDkxOWI1NWEzYzE5YTMxMGRlMGYwOWQ0ZjA4MmMzYTcxNGU1MzhmOGMzZTk3MzllZTc3OWQ1In0.eyJhdWQiOiIxIiwianRpIjoiZjEyYTQ0NmQ2ZDFmMGU3MDgxYzBlYTAzODkwOTE5YjU1YTNjMTlhMzEwZGUwZjA5ZDRmMDgyYzNhNzE0ZTUzOGY4YzNlOTczOWVlNzc5ZDUiLCJpYXQiOjE1MDQ2MDg2NTcsIm5iZiI6MTUwNDYwODY1NywiZXhwIjoxNTA1OTA0NjU3LCJzdWIiOiIxIiwic2NvcGVzIjpbXX0.kbvE4yrfGXcI4Bg0VW10-sDlFpsiG-_UB4uRIi_ylpB46kUkto3JdmlfWLINM83RWNrSkBdnzq6IYfPiHZY1wOw6qwObKG97pWhQVZO9yizVlZCe-ejRO-xv4do0QbXZQHzQqACwhSAYdQwymEJr3JqjrcaNUU8UV5wftlfLsDYNp-NVdo9hvq_7o49B79tIGG7F2izj0mmDHKXfhWLg5s8rFgkHw1q-Wn6o-YCMHgQhfWR9IVvJJ9bpywe45gcvse-MqaaFIQL4q-PHcSLAP0JHMrWLTHy0S2O-D10nWNE2XJyh3VPnxnKLH2jrGZpQVRpUN6RNQlftCxVJEoPruhyeE0Emb5KNJkwvFeIn2hEmLIseXqD4kuk0VESNlCz3VD0r6HRtvfpCTbHBK2uj_l69oYV0yUC6r_D-BfBK6ATZdTk-RIk3HV41ksMqOfpuSPymLoAieq_WqmU0i93VoACwoycirP6z1JTMuT4E1z8i_etgiodcOGpRWZQrePACURCxxGk1xGCaSRMVF_c42WNC5cw10wcHld9FToeFD4-KT_nBryz_eOMJ9Iw6foiyusRbR0vriGExOAesSiLpgLwnDEFBh3YpsDiOEVPvMJolMlVQ6tOnaUwyC3qunjrjVlbF9YR40r1b9DVJ7ZhhWGJNgYg0qadbNGt2uLIYscU"
  ["refresh_token"]=>
  string(706) "def50200ce6524158569f59de11fd955552ae132336954dbd53dffc22544a3af145d1cc0b2b78c36b3c65c238c1486f24b3b551061591a84ec92d68d4438c62de3e6bcffa0df6ce1fa7ea127078a107390b8d3023897eff6cef63064e39b9376ab4ae4e685cc5b9b6b3ed67efe312de73c06458b78c6e7b8e6719d4e11cc9e57315ebb36b0b5e869ac9c0a8ae5f973e12ae2d26f61192d0532c7aeff90e6b7802fc6bd78a7303583bee7f14fabf31486e6431320e6f3d206c61af9dfb482b8195fb0577e9f5cefeb765742400aec2f3f9e16d3b70c5d21732347190bae5287cd13ad01f0b42e1cd3cfc51c193bfce2ffcb5b06f75224c1a7c6498657d2320fd44a78bc047b9bf2bdd17654b69ce9c74280d42bab8be6619be419ee84700d7086ff638a9b715290286906274bccbe9b876afb1b83382b2b2c9840423210e3b5ca4c9cab938391844e261ca6b247e88b3bc96b18d505784c87ef6932c1ed9dec7cc8"
}

認証画面なしでパスワード付きで外部からAPIを実行する

  • 認証画面なしで外部からAPIを通してデータを保存したかったのでパスワード付きの認証にする

OAuth認証のユーザーを作成(APIサーバー)

  • パスワードオプション付きでOAuthユーザー作成
  • usersテーブルのidとは紐付けなくても良いみたい
# php artisan passport:client --password --name=password-user
Password grant client created successfully.
Client ID: 6
Client Secret: IzRQarKQ5y7b8Ni4Sf4nBvlDxeDAujm10lH6r3OW

API作成(APIサーバー)

  • 外部からアクセスしてデータを保存するAPI
routes/api.php
Route::middleware('auth:api')->get('/memo', function (Request $request) {

    $memo = $request->get('memo');

    // SAVE MEMO...

    return json_encode(array_merge(['status' => 'OK'], ['memo' => $memo . '-saved']));
});

クライアントサーバー(Macビルドインサーバー)

  • OAuthユーザーのID, Secretを使用する
  • 併せて通常のLaravelログインユーザーのID, Passwordも使用する
password_auth.php
<?php

require 'vendor/autoload.php';

use GuzzleHttp\Client as Client;

/**
 * Class APIClient
 */
class APIClient {

    /**
     * @var Client
     */
    protected $http;

    /**
     * @var
     */
    protected $accessToken;

    /**
     *
     */
    const API_SERVER = 'http://192.168.10.10';

    /**
     * APIClient constructor.
     */
    public function __construct()
    {
        $this->http = new Client;
    }

    /**
     * @param array $config
     */
    public function auth(array $config = [])
    {

        $response = $this->http->post(self::API_SERVER . '/oauth/token', [
            'form_params' => [
                'grant_type'    => 'password',
                'client_id'     => $config['client_id'],
                'client_secret' => $config['client_secret'],
                'username'      => $config['username'],
                'password'      => $config['password'],
                'scope'         => '',
            ],
        ]);

        $body = json_decode((string)$response->getBody(), true);

        $this->accessToken = $body['access_token'];

    }

    /**
     * @param string $memo
     */
    public function save(string $memo = '')
    {

        $response = $this->http->request('GET', self::API_SERVER . '/api/memo', [
            'headers' => [
                'Accept' => 'application/json',
                'Authorization' => 'Bearer '. $this->accessToken,
            ],
            'query' => ['memo' => $memo],
        ]);

        $body = json_decode((string)$response->getBody(), true);

        if (isset($body['status']) && $body['status'] === 'OK') {
            var_dump('saved memo');
        }
    }
}

// write .env etc
$config = [
    'client_id'     => '6',
    'client_secret' => 'IzRQarKQ5y7b8Ni4Sf4nBvlDxeDAujm10lH6r3OW',
    'username'      => 'test@example.com',
    'password'      => 'secret',
];

$client = new APIClient;

$client->auth($config);
$client->save('ほげ');
// string(10) "saved memo" array(2) { ["status"]=> string(2) "OK" ["memo"]=> string(12) "ほげ-saved" }

参照

https://github.com/laravel/passport
https://laravel.com/docs/5.5/authentication#authenticating-users
https://laravel.com/docs/master/passport#introduction

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした