Laravel Passportで自身のJavaScriptからAPIを利用する方法
2017-11-02
Laravel Passportは、Laravel上にOAuth2によるAPI認証を簡単に実装できるライブラリです。
この、Laravel Passportを利用すると、自身のWebアプリケーション上に実装したAPIを、外部へと簡単にセキュアに公開できます。
しかしながら、自身のWebアプリケーションの画面上でも同一のAPI利用したい場合があるでしょう。そんな場合もいちいち認証処理を経由するのは面倒なので、Laravel Passportでは自身のJavaScriptからAPIを利用する方法が用意されています。今回はそれについて紹介します。
今回紹介している方法は、一部公式ドキュメントに方法の記載がありますが、そのとおりにやってもトークンの渡し方に不備があり認証が通らないことがあったので、その点を踏まえ公式ドキュメントよりも詳しく解説していきます。
前提
公式ドキュメント(日本語翻訳サイト)を参考に、Laravel Passportのインストールおよび初期設定をしておきましょう。
実装方法
Laravel側で必要なことは、いたって簡単です。web
ミドルウェアグループにCreateFreshApiToken
ミドルウェアを追加するだけです。app/Http/Kernel.php
を開き、以下のように書き加えましょう。
protected $middlewareGroups = [
'web' => [
// 他のミドルウェア…
\Laravel\Passport\Http\Middleware\CreateFreshApiToken::class, // 追加
],
'api' => [
// 他のミドルウェア…
],
];
そして、JavaScript側でX-Requested-With
ヘッダーを送るようにしてやれば、設定完了です。
基本的にはこれだけで、Laravelはlaravel_token
という名前のクッキーを送信し、それによってアクセストークンを明示的に渡さなくてもAPIを利用できるようになります。
この、laravel_token
というクッキーの名称を変更したい場合は、こちらの記事を参照してください。
公式ドキュメントを見ると、この状態でJavaScriptからAPIリクエストを行うと、自動的にX-CSRF-TOKEN
ヘッダーを送り、そのまま利用できるとの記載がありますが、これは恐らく誤記です。少なくとも、私の環境では。
公式ドキュメントでも、私の環境でも、JavaScriptのHTTPクライアントライブラリには、axiosを利用しています。axiosは、自動的にX-CSRF-TOKEN
ヘッダーではなく、X-XSRF-TOKEN
ヘッダーを送ります。
1字違いですがこれは誤字ではなく、X-CSRF-TOKEN
ヘッダーは暗号化されていないトークン、X-XSRF-TOKEN
ヘッダーは暗号化されたトークンを意味するようです。これが世界的な標準なのかはわかりませんが、少なくともLaravel上ではそのような住み分けがされています。
そして、Laravel Passportでは、X-CSRF-TOKEN
ヘッダーしかサポートしていません。
つまり、axiosを使った場合、そのままでは利用できません。
この問題を解消する方法をいくつか挙げます。お好きな方法で実装しましょう。
metaタグを用意し、リクエストヘッダーを設定する
JavaScriptを実行する画面のBladeテンプレートの<head>
内に、次のような<meta>
タグを用意してください。
<meta name="csrf-token" content="{{ csrf_token() }}">
これで、<meta>
タグのcontent
に暗号化されていないトークンがセットされます。
そして、以下のように、HTTPリクエストヘッダーにX-CSRF-TOKEN
を含むよう設定すれば完了です。
axios.defaults.headers.common = {
'X-CSRF-TOKEN': document.head.querySelector('meta[name="csrf-token"]').content,
'X-Requested-With': 'XMLHttpRequest'
};
// または
var request = axios.create({
headers: {
'X-CSRF-TOKEN': document.head.querySelector('meta[name="csrf-token"]').content,
'X-Requested-With': 'XMLHttpRequest'
}
});
恐らくこれが最も確実な方法です。
クッキーの自動暗号化を解除し、axiosの自動トークン送信機能を利用する
Laravelでは通常、EncryptCookies
ミドルウェアによって、全てのクッキーが自動的に暗号化されます。そのままではX-CSRF-TOKEN
ヘッダーに暗号化されていないトークンをセットするのが不可能なので、トークンのクッキーXSRF-TOKEN
だけ暗号化されないように設定します。
app/Http/Middleware/EncryptCookies.php
のexcept
プロパティにXSRF-TOKEN
を追加し、クッキーに送信されるトークンが暗号化されないようにします。
protected $except = [
//
'XSRF-TOKEN'
];
そして、以下のように、トークン送信用のHTTPリクエストヘッダーをX-CSRF-TOKEN
に設定すれば完了です。
axios.defaults.xsrfHeaderName = 'X-CSRF-TOKEN';
axios.defaults.headers.common = {
'X-Requested-With': 'XMLHttpRequest'
};
// または
var request = axios.create({
xsrfHeaderName: 'X-CSRF-TOKEN',
headers: {
'X-Requested-With': 'XMLHttpRequest'
}
});
これはaxiosが、xsrfCookieName
(デフォルトでXSRF-TOKEN
)に設定したクッキーの値を、自動的にxsrfHeaderName
(デフォルトでX-XSRF-TOKEN
)に設定したヘッダーの値にセットして、リクエストに含めてくれる機能を利用したものです。
本来の使い方とは少しズレますが、少ない記述でクッキーだけで完結するため、こういった実装もありでしょう。
その他の実装方法
クッキーだけできちんと動作する仕組みを細かく作りたい場合は、新たにトークンクッキーを送信するミドルウェアを実装するといいでしょう。
ここでは細かく解説はしませんが、独自にミドルウェアを実装することによって、より細かい制御をすることが可能になります。
ちなみに
そもそもLaravel PassportでX-CSRF-TOKEN
ヘッダーだけではなくX-XSRF-TOKEN
ヘッダーにも対応すればいいのでは!と思い、X-XSRF-TOKEN
ヘッダーに対応させてPull Requestしてみましたが、Taylor Otwell氏曰く、クロスドメインのクッキーポリシー上X-CSRF-TOKEN
ヘッダーは改ざんを防止しますがX-XSRF-TOKEN
ヘッダーは改ざんのリスクを伴うため採用しないとのことでした。
何とか代替案でもいいからX-XSRF-TOKEN
ヘッダーでも動作するように、手軽に利用できるようにして欲しいものですが、こればかりは期待できなさそうですね。諦めてX-CSRF-TOKEN
ヘッダーを使うようにしましょう。
まとめ
今回は、Laravel Passportで自身のJavaScriptからAPIを利用する方法を、JavaScript側の実装方法と併せて紹介しました。
様々なJavaScriptフレームワークの台頭により、SPA等を実装する場面が増え、APIもその分利用頻度が上がってきています。皆さんも是非、より簡単によりセキュアにAPIを実装できるLaravel Passport、利用してみてはいかがでしょうか。