ディップ Advent Calendar 2018の3日目です。
はじめに
先月、Oauth2.0の社内勉強会に参加しました。
その復習も兼ねて、LaravelのAPI認証パッケージ(Passport)を使ってOauth2.0の理解を深めたいと思います。
学習範囲
下記の記事が分かりやすかったです、いいねの数が物語る良記事です。
「アクセストークンの要求方法とそれに対する応答方法を標準化したものが OAuth 2.0 である」
OAuth 2.0ということで上記が学習対象です。
OAuth 2.0 全フローの図解と動画
RFC 6749(The OAuth 2.0 Authorization Framework)で定義されている4つの認可フローのうち、今回はResource Owner Password Credentials Grantのトークンエンドポイントへのリクエストとレスポンスをやってみます。
準備1 環境構築
Passportが使える環境を作ります。
環境情報
{
"name": "laravel/laravel",
"description": "The Laravel Framework.",
"keywords": ["framework", "laravel"],
"license": "MIT",
"type": "project",
"require": {
"php": ">=7.0.0",
"fideloper/proxy": "~3.3",
"laravel/framework": "5.5.*",
"laravel/passport": "~4.0",
"laravel/tinker": "~1.0",
"paragonie/random_compat": "2.*"
},
PHPと composerのインストール
Laravelプロジェクトの作成
composer create-project --prefer-dist laravel/laravel SamplePassport
###パッケージインストール
※バージョン指定間違えると上手くいかないので注意
composer require paragonie/random_compat:2.*
composer require laravel/passport=~4.0
DB構築
今回はファイルベースで手軽に使えるsqliteを利用します。
touch $PROJECT_HOME/database/database.sqlite3
接続情報設定を設定します。
※DB読み込めなかったのでDB_DATABASEは使いません。
DB_CONNECTION=sqlite
#DB_DATABASE=database/database.sqlite3
'sqlite' => [
'driver' => 'sqlite',
'database' => env('DB_DATABASE', database_path('database.sqlite3')),
'prefix' => '',
],
passport用のマイグレーションが追加されているので実行
# $PROJECT_HOME
php artisan migrate
tinkerを使えばコマンドラインからDB操作できました。
これでusersテーブルにユーザ情報を作成。
このデータのアイパスをResource Owner Password Credentials Grantで使います。
php artisan tinker
Psy Shell v0.9.9 (PHP 7.0.32-4+ubuntu16.04.1+deb.sury.org+1 — cli) by Justin Hileman
>>>
>>> $user1 = new App\User;
=> App\User {#2882}
>>> $user1->name = 'user1';
=> "user1"
>>> $user1->email = 'user1@test.com'
=> "user1@test.com"
>>> $user1->password = Hash::make('testtest');
=> "$2y$10$8IbtHGsKzlNKyA1i59GQHeTwtwfTYWVz7.uZYGCWR8pbR0HWK2hPm"
>>> $user1->save();
=> true
>>>
>>> $user2 = new App\User;
=> App\User {#2889}
>>> $user2->name = 'user2';
=> "user2"
>>> $user2->email = 'user2@test.com';
=> "user2@test.com"
>>> $user2->password = Hash::make('testtest');
=> "$2y$10$.h5Ha9Iqjo9yEqll9XqjWOdYVXhztFXKizdyAYWNTm0PFAp5eZlSS"
>>> $user2->save();
=> true
テーブルが作成されました。
プレフィックスがoauth_
のものがpassportのテーブルのようです。
usersテーブルにデータを挿入しました。
準備2 アプリの設定・修正
Personal access clientとPassword grant clientの生成
Personal access clientはImplicit Grantで使うようです。
Implicit Grantは認可サーバから返された認可画面でアイパスを入力し、認可リクエストを承認→認可サーバの認可エンドポイントにリクエストを送り、アクセストークンが返されるというようなフローのようです。
Password grant clientはResource Owner Password Credentials Grantで使うようです。
こちらはアイパスを入力する画面がアプリで、その後認可サーバのトークンエンドポイントにアイパス付きのトークンリクエストを投げてアクセストークンを受け取るフローのようです。
passportインストール時に初回生成されます。
> php artisan passport:install
Encryption keys generated successfully.
Personal access client created successfully.
Client ID: 1
Client Secret: mG7B3hzZ6mZzapT1SLvcGA43CbrbhfEeCU9tyVTV
Password grant client created successfully.
Client ID: 2
Client Secret: 77JofUiwYzR4Paw2sA5NtLzddA16YvD1qCxdhADF
passport:installは下記コマンドを実行しているようなので、以降は必要に応じて作成しましょう。
php artisan passport:keys
php artisan passport:client --personal
php artisan passport:client --password
ソース修正
認証済みユーザーのトークンとスコープを確認する為にトレイトを追加しヘルパメソッドを使えるようにする。
<?php
namespace App;
use Laravel\Passport\HasApiTokens;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
use HasApiTokens;
アクセストークンの発行・失効やクライアントとパーソナルアクセストークンの管理のルート設定
use Laravel\Passport\Passport;
...
public function boot()
{
$this->registerPolicies();
Passport::routes();
}
PassportのTokenGuardを利用してAPI認証が行われるように変更。
'guards' => [
...
'api' => [
'driver' => 'passport',
'provider' => 'users',
],
ここまでで3つのテーブルにデータが挿入され、一部ソースの修正をしました。
実施
今回は画面がないのでコマンドで認可サーバ(SamplePassport)へリクエストを送ります。
http://localhost:8000/oauth/token
にPOSTでリクエストします。
基本的にtoken要求はこのURIが利用されるみたいです。
curl -X POST -H 'Content-Type: application/json' -d '{"grant_type":"password", "client_id":"2", "client_secret":"77JofUiwYzR4Paw2sA5NtLzddA16YvD1qCxdhADF", "username":"user1@test.com", "password":"testtest","scope":"*"}' http://localhost:8000/oauth/token
Resource Owner Password Credentials Grantなのでgrant_typeがpassword、usernameとpasswordもリクエストに含めます。
あとは、クライアント認証が行われるので、passport:installで生成したPassword grant clientの情報もリクエストに含めましょう。
{
"grant_type":"password",
"client_id":"2",
"client_secret":"77JofUiwYzR4Paw2sA5NtLzddA16YvD1qCxdhADF",
"username":"user1@test.com",
"password":"testtest",
"scope":"*"
}
アクセストークンが取得できました。
{
"token_type":"Bearer",
"expires_in":31535999,
"access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6ImY0MzE5YTZjY2ZhMTc1Zjg5Yzk3YTU3YTRhYjI0MTM3MzdlZDE3OTMzZGE1YzJjMWUwYjNiNDliZjM4MWVhMmM2NmY3MWY1NDRkMjcwYjQ0In0.eyJhdWQiOiIyIiwianRpIjoiZjQzMTlhNmNjZmExNzVmODljOTdhNTdhNGFiMjQxMzczN2VkMTc5MzNkYTVjMmMxZTBiM2I0OWJmMzgxZWEyYzY2ZjcxZjU0NGQyNzBiNDQiLCJpYXQiOjE1NDM2Njc2NDMsIm5iZiI6MTU0MzY2NzY0MywiZXhwIjoxNTc1MjAzNjQyLCJzdWIiOiIxIiwic2NvcGVzIjpbIioiXX0.iqFoJMdUBnCOcZBFl9A-EYykmyJPvrQly2-3mrj-h0Sul16k1jumITOI3Tv5jmfowRemhrE5io604HYBgYcIWsjA1DFX0wFhDXtM2bcdA6r4gUjARnOi3XW3TUznsB27PtX5sP24XaZ66fUWcOgohrl_FBlGnO4vWUmExnsB27PtX5sP24XaZ66fUWcOgohrl_FBlGnO4vWUmExF3X7nI1I3LYY0NXoo5ZwR8Ccw-iYDuekX7O83J7WRGV9Lb5WgycF8wpQfO5gMtrSenqZsJFXuSH145tPk3TK0hDc9CsK7C7Y10F-0pqwR8Ccw-iYDuekX7O83Jzw87Ycm8d4rMH4P_kI9IvAZUm-tkJwSr9cdA8qQ7Ps7WRGV9Lb5WgycF8wpQfO5gM_01J0u4xZtdlyHn_JX4t6Wev3zAted0SyP0Bi14s-Oyv8zo2HAD562c_GKRfY3GSrY9doyoepEvoD_DEFQrha_Tz1pZszAgXyyE-fMczw87Ycm8d4rMH4P_kI9IvAZUm-tkJwSr9cdA8q7JW6909OKeQDb_8OO0GfWZhn9YVb4cSlqfytBXx5NzQ7PsDOs7BVLltgVoFZNO-T6bw31qfVch09-7d_xlrRfyIpm3k8tenYQ6rcbnqV2rTVO4wcNGwXGxoD7Ozs_CFbNpI3sevO5whCAg9zuQQie4s-Oyv8zo2HAD562c_GKRfY3GSrY9doyoepEvoD_DELtJB8eHlJyEvY2A91b09f4a073a59d59c1112d6a7155ef4d7551894f0SNITlHyhbG09UARE7lEsD90jiSstdsGtvoGQjNhrqz4Q1U7JW6909OKeQDb_8d9eea0f38590ce95f473646503b7920c05f6f6215cOO0GfWZhn9YVb4cSlqfytBXx5NzIuEo5VB3FeZadhxR04eII8s28VVJTXUEoo4f3e20c9a14f5b0063ab250b23486e8e5ced2a147dKWkYMfbBl8Krb1hl7XlVdP28JeyV2rTVO4wcNGwXGxoD7Ozs_CFbNpI3sevO5c9738d94c4cec86ac89947af6d6f80068873777100whCAg9zu6_o",
"refresh_token":"def50200e0372b0fc446c98f30d38ad64c89d9491b09f4a073a59d59c1112d6a7155ef4d7551894f00f1a6712b2ce17a7cdf19903e6627c05695b2c4da9bb9e102b33b90607b8ad9eea0f38590ce95f473646503b7920c05f6f6215c2c7aa35340221fef9836cd178c825ea61fcd24fcf281d13e8324dd88b19a64f3e20c9a14f5b0063ab250b23486e8e5ced2a147d7f0ccf94470604f636347884284e9344918c209fe823831e3dedfc43702e4c9738d94c4cec86ac89947af6d6f80068873777100b2f38bd5fb7877b8bd44f6ea7c9324d7b0048452cc7dadf8e4241873e671be7bc98fc60f76a8abfc962b65aab49f2274ccc5a3a737c4ded71bf8bf3fed779072eac56d2cd2746a46c1354a7ea104d5e6daab4adf64ebcb387560ae5bf2aef1cc6ccbca128a5471e66da297ec90362bcd43795edc2accb90ff5c777c9d0d2c1a1cd765973451d71bf9bf9eda004026fdecae39b41903a6208c20c569cd5cb025347"
}
migrateで作成したPassport関連テーブルにアクセストークンとリフレッシュトークンの情報が追加されました。
期限日時のデフォルトは1年ですね。
まとめ
- LaravelのPassportパッケージを使えばoauth2.0の実装が手軽に出来る。
ただ、oauth2.0の理解を厳かにしたまま進めるとうまく使いこなせないと思うのでしっかり学習する必要がありそうだ。 - TakahikoKawasakiさんはoauthマスター
-
Laravel5.5 passportではSNS認証でよく使われるAuthorization Code Grantの実装フローが記載されている。
パスポートVueコンポーネントを使った画面実装やテストまで出来るらしい、すごいぞLaravel!
参考
https://readouble.com/laravel/5.5/ja/passport.html
https://qiita.com/TakahikoKawasaki/items/e37caf50776e00e733be
https://qiita.com/TakahikoKawasaki/items/200951e5b5929f840a1f
https://qiita.com/zaburo/items/65de44194a2e67b59061