こんにちは。 SCOUTERフロントエンドエンジニア、現在Laravel勉強中の匠平@show60です。
弊社のプロダクトはフロントエンドをVue.js 、サーバーサイドをLaravelで実装しています。 私が携わっている新規事業のback checkでは、LaravelでAPIを実装し、フロントエンドからリクエストを送ることでアプリケーションを動作させています。
APIを実装する際に必要になるのがセキュアな認証。そこで広く使われるJWT認証について学んだことを今回お話したいと思います。
なぜ必要なのか、どのように使っているのかを焦点にやさしくお話しします。
ユーザー認証とは
ユーザー認証とは、アクセスしてきたユーザーが正当なユーザーかどうかをチェックすることです。 チェックに使用される要素としては以下のようなものがあります。
- 知る要素
- パスワード
- 持つ要素
- 鍵
- トークン
- 備える要素
- 指紋
- 声
- 虹彩 (目)
JWTはその中にユーザー認証情報を含ませることができるため、正当なユーザーかどうかをチェックできるということですね。
余談、よくパスワードを忘れた際に、母親の旧姓やペットの名前、出身校名を聞かれますが、これらは「知る要素」に分類されますね。 とはいえ、これらは他人にも知られる情報であるため、安全ではなく推奨されていません。
実家のペットの名前がモカちゃんであるとここに書くことで、私のアカウントはもはや安全ではなくなってしまいます。
参考: [Google Online Security Blog | New Research: Some Tough Questions for ‘Security Questions’ 2015年5月] (https://security.googleblog.com/2015/05/new-research-some-tough-questions-for.html)
今回の題材であるJWTは「持つ要素」に分類されるものです。
JWTとは
JWTとはJSON Web Tokenの略です。
JSONとはJavaScript Object Notationの略で、JavaScriptのオブジェクトの構造を持ったデータフォーマット。
Tokenとはユーザーを識別するための認証情報。
つまりJWTとは、JavaScriptのオブジェクトの形をした認証情報のことです。
JWTの構成
JWTは大きく3つの要素で構成されます。
- ヘッダー
- クレーム情報
- 署名
ヘッダー
この後の署名に使われている暗号化アルゴリズムや、トークンタイプなどのメタ情報が入ります。
クレーム情報
任意の情報を含ませることができ、ここにユーザー認証情報を記述します。
私の携わるbackcheckというプロダクトには、企業、候補者、推薦者、管理者と4つのユーザータイプがあります。それぞれのページを行き来できないようアクセスに制限をかけるため、このクレーム情報内にユーザータイプを表すキーを実装しています。
署名
ヘッダーに記述した暗号化アルゴリズムにより、ヘッダーとクレーム情報をBase64urlに変換したものから生成されます。
なぜJWT認証を使うのか
JWTを使うメリットは以下のようなものが挙げられます。
- 安全
- JWTに署名が含まれているため、改ざんがあってもチェックできるようになっている。
- 実装のしやすさ
- セキュアなToken発行が楽に実装できる。
- 管理のしやすさ
- URLに含むことができる文字で構成されているから、HTTPリクエストでの取り扱いが楽。
- 認証に必要となる情報はすべてJWTの中に入っているため、ユーザー認証情報をサーバーで管理する必要がない。DB問い合わせを行う必要がない。
JWT認証を使えば、安全かつ管理がしやすいセキュアな認証を簡単に実装できるということですね。
サーバーサイドで行うこととフロントエンドで行うこと
サーバーサイドとフロントエンドで行うことを分類しながら、JWT認証を使用する流れを説明します。
ユーザー初回登録時
- 【フロント】
- ユーザー情報 (メールアドレス、パスワード等) を送信
- 【サーバー】
- リクエストされたユーザー情報を元にJWTでトークンを生成
- トークンをフロントへ返す
- 【フロント】
- 返ってきたトークンがbase64urlでエンコードされているため、base64 に変換
- 変換したものをLocalStorageに保存
このときトークン内のユーザー情報には、上に書いた4種類のユーザータイプ情報も含んでいます。
2回目以降のログイン・画面更新時
- 【フロント】
- Tokenの有無の確認
- LocalStorageにTokenがあるかどうかを確認
- ある場合は、HTTPヘッダに入れてサーバーにアクセスする
- ない場合は、そのユーザータイプによって適切なページへリダイレクトさせる
- LocalStorageにTokenがあるかどうかを確認
- Tokenの有無の確認
- 【フロント】
- Tokenの有効期限の確認
- LocalStorageにTokenがある場合、その有効期限を確認
- 有効期限が切れている場合、サーバーに問い合わせし、Tokenを更新
- Tokenの有効期限の確認
- 【サーバー】
- Tokenの更新リクエストがきたらJWTをリフレッシュして返す(このときフロントでは、初回登録時と同じように返ってきたトークンをLocalStorageに保存します)
- 【フロント】
- サーバーにアクセス
- 有効なアクセスであることがフロントで確認されたらサーバーにアクセスをします
- サーバーにアクセス
- 【サーバー】
- トークンの認証を確認しデータを返す
初回登録時に発行したトークンをLocalStorageに保管しており、有効期限やユーザータイプの情報を持っているため、2回目以降のログイン・画面更新時のサーバーへのアクセスは多くても2回に抑えられています。
フロントエンドでの処理は、ユーザー情報がすべてトークン内に記述されており、JSONフォーマットなので処理もとても簡単に行えますね。
まとめ
実際にはサーバー内での処理を加えるとまだまだ言及しなくてはいけないことがありますが、流れをさっくりと説明させてもらいました。
さいごに
現在、株式会社SCOUTERでは、エンジニア、デザイナーの募集をしております。
興味のある方は、是非下記よりご応募ください!