トークンのベストプラクティス
以下はトークンを使うときに考慮するべき内容の一部です。
機密を守り、安全を守る:署名鍵は他の資格情報と同様に扱って、必要なサービスにのみ公開します。
ペイロードには機密データを追加しない:トークンは不正な操作から保護するために署名され、簡単にデコードされます。性能と安全性を最大限発揮できるように、ペイロードには必要最小限のクレームを追加します。
トークンに有効期限を指定する:技術的に、署名されたトークンは、署名鍵が変わったり、有効期限が明示的に設定されない限り、永久的に有効になります。これは潜在的な問題となるため、トークンの有効期限や失効させる方法を設けておくようにします。
HTTPSを利用する:要求の傍受やトークンの侵害などの懸念があるため、トークンはHTTPS以外の接続で送信しないようにします。
認可のユースケースをすべて考慮する:独自のサーバーで生成されたトークンであることを確実にするために、二次的なトークン検証システムを追加して、要件を満たす必要があるかもしれません。
保存して再利用する: アプリケーションの攻撃可能領域を広げないように、不必要な通信を減らし、認可サーバーからアクセストークンを取得して保管し、トークンの制限を(必要であれば)最適化します。新しいトークンを要求するにのではなく、有効期限が切れるまでは、呼び出しの際に保管されているトークンを使用します。トークンを保管する方法はアプリケーションの特性に依存します。一般的に、データベース(セッションの有無にかかわらず、APIを呼び出す必要のあるアプリ)やHTTPセッション(対話型のセッションに対してアクティビティの枠が制限されているアプリ)などが対策として使用されます。サーバー側のストレージとトークンの再利用についての例は、「トークンのストレージ」を参照してください。
トークンとクッキー
通常、シングルページアプリ(React・Vue・AngularJS + Nodeなど)、ネイティブモバイルアプリ(iOS・Androidなど)とWeb API(Node・Ruby・ASP.NETやその混合)には、トークンベースの認証が最も適しています。サーバー側のWebアプリケーションでは、伝統的にクッキーベースの認証が使用されてきました。
トークンを用いた認証は一般的に、ユーザーの認証時にトークンを生成して、そのトークンを後続する各API要求の認証
ヘッダーで設定することで実装されます。このトークンには、JSON Web Tokenなど、標準的なものを使用して、ほとんどのプラットフォームにライブラリーがあり、独自のクリプトを作成しなくてもいいようにします。
どちらの方法でも、ユーザーから同じ量の情報を取得することができます。これは、(Lock、JavaScriptライブラリ、またはプレーンリンクを使って)ログイン要求で送信されるscope
パラメーターによって制御されます。scope
は、.signin({scope:'openid name email'})
メソッドのパラメーターで、ログイン要求ではクエリ文字列の一部になります。
デフォルトでは、トークンが大きくなるのを避けるため、トークンベースの認証ではscope=openid
を使用します。標準のOpenID Connect(OIDC)クレームは、トークンにスコープ値として追加することにより、制御することができます。scope=openid name email family_name address phone_number
が例として挙げられます。詳細については、openid.netの「標準クレーム」を参照してください。
トークンを用いた認証とCookieを用いた認証は組み合わせて使用することができます。WebアプリとAPIが同じドメインから提供される場合には、Cookieだけでも十分機能するためめ、トークンを用いた認証は必要ない可能性があります。必要であれば、当社からWebアプリフローでJWTを返すこともできます。提供しているSDKはそれぞれ動作が異なります。APIを(既存のCookieを使用するのではなく)JavaScriptから呼び出したい場合には、トークンの送信と保管を扱えるように、Web WorkersやJavaScriptのクロージャを使ってアクセストークンを設定する必要があります。詳細については、「トークンのストレージ」ページのブラウザのインメモリシナリオのセクションをお読みください。
リフレッシュトークンの使用
リフレッシュトークンは以下のフローを実装する場合にのみ取得することができます。
APIへのオフラインアクセスを制限すると([Auth0 Dashboard]>[Applications(アプリケーション)]>[APIs(API)]>[Settings(設定)]の[Allow Offline Access(オフラインアクセスを許可する)]スイッチを介して構成されるセーフガード)、要求にoffline_access
スコープが含まれていても、APIのリフレッシュトークンは返されません。
ルールはリフレッシュトークンの交換で実行されます。特別なロジックを実行するには、ルール内のcontext.protocol
プロパティを確認してください。値がoauth2-refresh-token
の場合、ルールは交換中に実行されます。
リフレッシュトークンを取得しようしても、ルールのコンテキストオブジェクトでオーディエンスパラメーターを使用できません。オーディエンスパラメーターを追加しようとした際にエラーを受信した場合には、それがトークンに設定されていないことを確認してください。
context.redirect
でリダイレクトの実行を試みると、認証フローはエラーを返します。
ルールを使ってカスタムクレームをトークンに追加した場合には、ルールが存在している限り、リフレッシュトークンを使用すると、新しいトークンにカスタムクレームが含まれます。新しいトークンはカスタムクレームを自動的には継承しませんが、リフレッシュトークンフローでルールが実行されるため、同じコードが実行されます。これによって、認可済みのアプリケーションに新しいリフレッシュトークンの取得を強制することなく、新規発行されたトークンにカスタムクレームを追加や変更できるようになります。
リフレッシュトークンの制限事項
Auth0では、有効なリフレッシュトークンの数をユーザーあたり・アプリケーションあたり200に制限しています。この制限は有効なトークンのみに適用されます。制限数に達した状態で新しいトークンが作成されると、システムは、該当するユーザーとアプリケーションを対象として、最も古いトークンを取り消し、削除します。取り消されたトークンや期限の切れたトークンは、制限数にカウントされません。
自動テスト
リフレッシュトークンは自動テストによって蓄積され、通常はテストが実行されている間に使用されます。制限の対象となるトークンが蓄積されるのを防ぐために、Auth0 Management APIを使って不要なリフレッシュトークンを削除することができます。
Management APIを使ってユーザーを作成します。テストではこのユーザーを使用します。
応答は
user_id
を返します。後で行うテスト中はこのIDを継続して使用します。テストが完了したら、Management APIを使ってユーザーを削除します。テストユーザーが削除されたら、リフレッシュトークンを含む、関連付けられているアーティファクトも削除されます。
今後のテストのためにテストユーザーを残しておきたい場合には、以下を行います。
Management APIのデバイス資格情報エンドポイントを使って、ユーザーのリフレッシュトークンを一覧表示します。エンドポイントが最大1000のトークンを返します。この際、蓄積されたトークンやページネーションの使用にかかわらず、リスト内は順不同になります。
DELETEメソッドを使って、これらの資格情報を削除します。
ユーザーに1000以上のトークンがある場合には、ユーザーのトークンがなくなるまで、リストの作成とトークンの削除を繰り返します。
期限が切れそうなリフレッシュトークンを構成する
ユーザーがAuth0でアプリケーションにログインし、offline_access
が認可要求で要求された場合、新しいリフレッシュトークンがユーザーに対して発行されます。ユーザーがログアウトしてから同じデバイスでもう一度ログインすると、新しいリフレッシュトークンが発行されます。アプリケーションがユーザーのリフレッシュトークンを保管する方法によっては、最初のログインのリフレッシュトークンが古くなるため、両方とも同じオーディエンスで発行されている場合、アプリケーションはほぼ確実に新しいリフレッシュトークンを使用します。詳細については、「トークンのストレージ」をお読みください。
古いリフレッシュトークンの蓄積を避けるために、リフレッシュトークンの制限によって最も古いトークンから削除されるとしても、リフレッシュトークンに有効期限を設定することをお勧めします。ローテーションの有無(または再利用可能か)にかかわらず、リフレッシュトークンにアイドル値または絶対値で有効期限を設定することができます。どちらの値も使用されていないトークンを削除して、ユーザーのトークンが蓄積されるのを防ぐのに役立ちます。詳細については、「リフレッシュトークンの有効期限を設定する」をお読みください。
JWTの検証
ミドルウェアか、既存のサードパーティのオープンソースライブラリーを使用して、JWTを解析し検証することをお勧めします。JWT.ioでは、.NETやPython、Java、Ruby、Objective-C、Swift、PHPといった、さまざまなプラットフォームと言語のライブラリーが揃っています。
署名アルゴリズム
アプリケーションまたはAPIに対して発行されたトークンに署名するアルゴリズムです。署名はJWTの一部で、トークンの送信者が自称のとおりであることを検証し、メッセージが途中で変更されていないことを保証するために使用されます。JWTの詳細については、「JSON Webトークン」をお読みください。署名の詳細については、「JSON Webトークン構造」をお読みください。
署名アルゴリズムは以下から選べます。
RS256(SHA-256を持つRSA署名):公開鍵暗号方式で、公開鍵と秘密鍵の2つがあり、後者は秘密にしておかなくてはなりません。署名の生成に使う秘密鍵は、Auth0が持っていて、JWTのコンシューマーは、Auth0が提供するメタデータエンドポイントから公開鍵を取得しJWT署名の検証に使用します。
HS256(SHA-256を持つHMAC):共通鍵暗号方式で、非公開の秘密鍵が1つあるのみです。両当事者がこの鍵を共有します。同じ鍵が署名の生成と検証に使用されるので、鍵が解読されないように細心の注意を払う必要があります。この秘密鍵(シークレット)は、アプリケーション(クライアントシークレット)またはAPI(署名シークレット)を登録してHS256署名アルゴリズムを選択すると作成されます。
最も安全で、当社が推奨するのはRS256の使用です。理由は:
RS256では、秘密鍵の所有者(Auth0)のみがトークンに署名できる一方、誰でも公開鍵を使ってトークンの有効性を確認できます。
RS256では、複数のオーディエンスに有効なトークンを要求できます。
RS256では、秘密鍵が解読されたとしても、新しいシークレットでアプリケーションやAPIをデプロイし直すことなく、鍵のローテーションを実施できます(HS256では再デプロイが必要)。
HS256では、秘密鍵が侵害されると、新しいシークレットでAPIをデプロイし直す必要があります。
署名鍵
JWKSに複数の署名鍵があると仮定して進めておけば、いい結果に繋がります。Auth0のJWKSエンドポイントにある署名鍵は通常1つであるため、不要に思われるかもしれませんが、署名証明書のローテーションでJWKSに複数の署名鍵が見つかる可能性があります。
アプリケーションの性能を向上させ、レート制限の超過を回避できるように、署名鍵はキャッシュすることをお勧めします。ただし、トークンのデコードに失敗した場合には、必ずキャッシュを無効にして、新しい署名キーを取得し、もう一度だけ試すようにしてください。