OAuth2サーバの実装を提供するLaravel Passportを使ってアクセストークンによるAPI認証をやってみよう!
「Laravel5.8でユーザ認証」まで出来ているところから始めます。
目次
開発環境
Laradock
Laravel 5.8
MySQL 5.7
Laravel Passportインストール
【LaradockのWorkspaceにログイン】
PS C:\laravel\laradock> docker-compose exec workspace bash
【Laravel Passportパッケージ導入】
root@1a1904fc0e8c:/var/www# composer require laravel/passport
【データベース作成】
root@1a1904fc0e8c:/var/www# php artisan migrate
以下のテーブルが作成された。
oauth_auth_codes
oauth_access_tokens
oauth_refresh_tokens
oauth_clients
oauth_personal_access_clients
【暗号キーとクライアントの作成】
root@1a1904fc0e8c:/var/www# php artisan passport:install
Encryption keys generated successfully.
Personal access client created successfully.
Client ID: 1
Client secret: KFYSkSXbYyJkAzFd5bd6LP6kniUqQUuSWsEiCuRS
Password grant client created successfully.
Client ID: 2
Client secret: nkSYsl8KYl0mZf1Eplz1YSLq82GC07eG9RyO40ZW
パーソナルアクセスクライアント、パスワードグラントクライアントとそれぞれのアクセストークンの生成に必要な暗号キーが生成されoauth_clientsテーブルに登録される。
Userモデルを修正
Laravel\Passport\HasApiTokensトレイト追加
app/Models/User.php
<?php
namespace App\Models;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Laravel\Passport\HasApiTokens; // 追加
class User extends Authenticatable
{
use HasApiTokens, Notifiable; // HasApiTokens追加
}
AuthServiceProviderを修正
AuthServiceProviderのbootメソッドでPassport::routes()を呼ぶことでoauth関連のルーティングが設定される。
app/Providers/AuthServiceProvider.php
use Laravel\Passport\Passport; // 追記
class AuthServiceProvider extends ServiceProvider
{
・・・
public function boot()
{
$this->registerPolicies();
Passport::routes(); // 追記
}
}
【Routeの確認】
root@1a1904fc0e8c:/var/www# php artisan route:list
+--------+----------+-----------------------------------------+-----------------------------------+---------------------------------------------------------------------------+--------------+
| Domain | Method | URI | Name | Action | Middleware |
+--------+----------+-----------------------------------------+-----------------------------------+---------------------------------------------------------------------------+--------------+
| | GET|HEAD | / | | Closure | web |
| | GET|HEAD | api/user | | Closure | api,auth:api |
| | GET|HEAD | home | home | App\Http\Controllers\HomeController@index | web,auth |
| | POST | login | | App\Http\Controllers\Auth\LoginController@login | web,guest |
| | GET|HEAD | login | login | App\Http\Controllers\Auth\LoginController@showLoginForm | web,guest |
| | POST | logout | logout | App\Http\Controllers\Auth\LoginController@logout | web |
| | POST | oauth/authorize | passport.authorizations.approve | Laravel\Passport\Http\Controllers\ApproveAuthorizationController@approve | web,auth |
| | GET|HEAD | oauth/authorize | passport.authorizations.authorize | Laravel\Passport\Http\Controllers\AuthorizationController@authorize | web,auth |
| | DELETE | oauth/authorize | passport.authorizations.deny | Laravel\Passport\Http\Controllers\DenyAuthorizationController@deny | web,auth |
| | POST | oauth/clients | passport.clients.store | Laravel\Passport\Http\Controllers\ClientController@store | web,auth |
| | GET|HEAD | oauth/clients | passport.clients.index | Laravel\Passport\Http\Controllers\ClientController@forUser | web,auth |
| | DELETE | oauth/clients/{client_id} | passport.clients.destroy | Laravel\Passport\Http\Controllers\ClientController@destroy | web,auth |
| | PUT | oauth/clients/{client_id} | passport.clients.update | Laravel\Passport\Http\Controllers\ClientController@update | web,auth |
| | POST | oauth/personal-access-tokens | passport.personal.tokens.store | Laravel\Passport\Http\Controllers\PersonalAccessTokenController@store | web,auth |
| | GET|HEAD | oauth/personal-access-tokens | passport.personal.tokens.index | Laravel\Passport\Http\Controllers\PersonalAccessTokenController@forUser | web,auth |
| | DELETE | oauth/personal-access-tokens/{token_id} | passport.personal.tokens.destroy | Laravel\Passport\Http\Controllers\PersonalAccessTokenController@destroy | web,auth |
| | GET|HEAD | oauth/scopes | passport.scopes.index | Laravel\Passport\Http\Controllers\ScopeController@all | web,auth |
| | POST | oauth/token | passport.token | Laravel\Passport\Http\Controllers\AccessTokenController@issueToken | throttle |
| | POST | oauth/token/refresh | passport.token.refresh | Laravel\Passport\Http\Controllers\TransientTokenController@refresh | web,auth |
| | GET|HEAD | oauth/tokens | passport.tokens.index | Laravel\Passport\Http\Controllers\AuthorizedAccessTokenController@forUser | web,auth |
| | DELETE | oauth/tokens/{token_id} | passport.tokens.destroy | Laravel\Passport\Http\Controllers\AuthorizedAccessTokenController@destroy | web,auth |
| | POST | password/email | password.email | App\Http\Controllers\Auth\ForgotPasswordController@sendResetLinkEmail | web,guest |
| | GET|HEAD | password/reset | password.request | App\Http\Controllers\Auth\ForgotPasswordController@showLinkRequestForm | web,guest |
| | POST | password/reset | password.update | App\Http\Controllers\Auth\ResetPasswordController@reset | web,guest |
| | GET|HEAD | password/reset/{token} | password.reset | App\Http\Controllers\Auth\ResetPasswordController@showResetForm | web,guest |
| | GET|HEAD | register | register | App\Http\Controllers\Auth\RegisterController@showRegistrationForm | web,guest |
| | POST | register | | App\Http\Controllers\Auth\RegisterController@register | web,guest |
+--------+----------+-----------------------------------------+-----------------------------------+---------------------------------------------------------------------------+--------------+
auth.php修正
認証Guardでapiのドライバーを「token」から「passport」に変更。
config/auth.php
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'passport', // 変更
'provider' => 'users',
'hash' => false,
],
],
フロントエンド
Passport Vueコンポーネントが用意されているので動作確認だけね。
【Vueコンポーネント生成】
root@1a1904fc0e8c:/var/www# php artisan vendor:publish --tag=passport-components
resources/js/components/passport ┣ Clients.vue ┣ AuthorizedClients.vue ┗ PersonalAccessTokens.vue
【Vueコンポーネント登録】
app.jsに生成されたVueコンポーネントを登録します。
resources/js/app.js
Vue.component(
'passport-clients',
require('./components/passport/Clients.vue').default
);
Vue.component(
'passport-authorized-clients',
require('./components/passport/AuthorizedClients.vue').default
);
Vue.component(
'passport-personal-access-tokens',
require('./components/passport/PersonalAccessTokens.vue').default
);
【Viewにコンポーネントを記述】
とりあえず、HomeController.phpで表示するように。
resources/views/home.blade.php
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">Dashboard</div>
<div class="card-body">
@if (session('status'))
<div class="alert alert-success" role="alert">
{{ session('status') }}
</div>
@endif
You are logged in!
{{-- 追記 --}}
<passport-clients></passport-clients>
<passport-authorized-clients></passport-authorized-clients>
<passport-personal-access-tokens></passport-personal-access-tokens>
{{-- /追記 --}}
</div>
</div>
</div>
</div>
</div>
@endsection
【アセットをコンパイル】
root@1a1904fc0e8c:/var/www# npm run dev
> @ dev /var/www
> npm run development
> @ development /var/www
> cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js
sh: 1: cross-env: not found
npm ERR! file sh
npm ERR! code ELIFECYCLE
npm ERR! errno ENOENT
npm ERR! syscall spawn
npm ERR! @ development: `cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js`
npm ERR! spawn ENOENT
npm ERR!
npm ERR! Failed at the @ development script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
npm WARN Local package.json exists, but node_modules missing, did you mean to install?
<以下省略>
「sh: 1: cross-env: not found」ってエラーでたね。。。
そもそもnode_moduleが無いじゃない!というわけでインストール
root@1a1904fc0e8c:/var/www# npm install
もう一回!
root@1a1904fc0e8c:/var/www# npm run dev
> @ dev /var/www
> npm run development
> @ development /var/www
> cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js
Additional dependencies must be installed. This will only take a moment.
Running: yarn add vue-template-compiler --dev --production=false
↓割と時間かかる・・・
DONE Compiled successfully in 9920ms 2:44:46 PM
Asset Size Chunks Chunk Names
/css/app.css 173 KiB /js/app [emitted] /js/app
/js/app.js 1.48 MiB /js/app [emitted] /js/app
root@25295143b25f:/var/www#
ちゃんと動いたっぽい。
【動作確認】
Userログインしてみる
http://localhost/home
出ないw
ブラウザのキャッシュクリアで出た!
「Create New Client」をクリックしてみる
oauth_clientsに登録された。
id 3
user_id 3
name Test
secret PrNWxYUoxPOGTNVBeic4zQT5QCNiunRLVfzs5I3V
redirect http://localhost/home
personal_access_client 0
password_client 0
「Create New Token」をクリックしてみる
oauth_access_tokensに登録された。
id 1f2cdb06b0179dd847d19a8d901608e484817d40dfe96c60fb6abb0a31e7ed58a3ea025b420dc316
user_id 3
client_id 1
name Test
scopes []
revoked 0
created_at 2019-08-21 00:14:54
updated_at 2019-08-21 00:14:54
expires_at 2020-08-21 00:14:54
Personal Access Tokenを利用してユーザ情報取得を取得してみる
PostmanとかAdvanced REST clientとかを使って
GET
http://localhost/api/user
application/json
Authorization Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6IjFmMmNkYjA2YjAxNzlkZDg0N2QxOWE4ZDkwMTYwOGU0ODQ4MTdkNDBkZmU5NmM2MGZiNmFiYjBhMzFlN2VkNThhM2VhMDI1YjQyMGRjMzE2In0.eyJhdWQiOiIxIiwianRpIjoiMWYyY2RiMDZiMDE3OWRkODQ3ZDE5YThkOTAxNjA4ZTQ4NDgxN2Q0MGRmZTk2YzYwZmI2YWJiMGEzMWU3ZWQ1OGEzZWEwMjViNDIwZGMzMTYiLCJpYXQiOjE1NjYzMTU5NzcsIm5iZiI6MTU2NjMxNTk3NywiZXhwIjoxNTk3OTM4Mzc3LCJzdWIiOiIzIiwic2NvcGVzIjpbXX0.kA8yR3I5D4wfAcgyz6mIpTt0eL9HZucd6KEKcSdhGS-8OEkBPLqmHIlGWH-w7PhMAtDEN1R7YADSRWiASrrKvbt7WpEOSzifR1C5vFxwu4mNL5wF9jflBXnBJEAra7lSzSpYdOTVOXGBgM7DVcpRlHismGbO2snMtf8hUI8lPaBRtx-7eL_DIIhKcZ1z8GRPo_nVOC1c_sl-5l7m0RS8B9xJX1V4oqkgWYDgWiWp8xBjFcrkRp_L3hOGKkJgB73k1JFJM6DwvTi4leDZj3NnTofOT9rHLueH3ZneawBVATKANyhN0kMsSnG4qthS4biyB2F-IHwGSsq629-T8nXFBC3RQmh1ICcRtUHg76rbXeMsvwSaufbu5wlVcfSYhnT10fM5zxOuvKGNLFHyqj2QXJHstxsFyZI0XvyQXSRWEwj4dkLjJuNz54tkgQD2B1WWByzbWQUfoAE4UUOIoPvNc7rERAUoIJmRccsP02iwyamltASlyDC73qRiFr3Wcv-r9NomDqd67PI98BCER39kPMZnjCgPj6ViROdmbHLYY7ZlfQKv9783Od5ReiIQUeyCBopWFxPA4-fAyEDG--r8QhjK_v9b_h9KBy7lWxFlmtUvJN5MFVO3j_04nWAsSQgQosOvPqAoEEB9WX74zdGDzD0I4GVp9ccuxlLrbWAHSkg
{
"id": 3,
"name": "テストユーザ",
"email": "*****@gmail.com",
"email_verified_at": "2019-08-15 14:21:17",
"created_at": "2019-08-15 14:20:57",
"updated_at": "2019-08-15 14:21:17"
}
取得できたみたいというわけでとりあえず動作確認完了。
感想
アクセストークン自体はどこにも保存されないので紛失したら再発行ということね。
フロントエンドではPersonal Access Tokenの動作確認だけだったが、Password GrantやAuthorization Code Grantはやってみておきたいところ・・・だがまたいずれ。。。