APIとSPAの構成(SPA + API)
このセクションでは、当社シナリオでAPIを実装する方法を説明します。
APIエンドポイントの定義
まず、APIのエンドポイントを定義する必要があります。
APIエンドポイントとは
APIエンドポイントは
たとえば、レストランAPIには/orders
や/customers
などのエンドポイントがあるかもしれません。このAPIに接続するアプリケーションは、関連するHTTPメソッド(POST
、GET
、PUT
、PATCH
、DELETE
)を使ってAPIエンドポイントを呼び出すことにより、CRUD操作(作成、読み取り、更新、削除)を実行することができます。
この実装では、2つのエンドポイントのみ定義します。1つは従業員の全タイムシートのリストを取得するため、もう1つは従業員がタイムシートエントリーを新規作成できるようにするためのものです。
/timesheets
エンドポイントへのHTTP GET
要求は、ユーザーがタイムシートを取得できるようにし、/timesheets
へのHTTP POST
要求は、ユーザーが新たなタイムシートを追加できるようにします。
Node.jsでの実装を参照してください。
エンドポイントのセキュリティ確保
APIがヘッダーの一部にBearerアクセストークンのある要求を受け取った場合、最初にすべきことはトークンの検証です。これは複数の手順で構成され、そのうち1つでも失敗した場合、要求は、Missing or invalid token
という呼び出し元アプリへのエラーメッセージとともに拒否されなければなりません。
APIは以下の検証を実行すべきです。
JWTが整形式であることを確認する
署名を確認する
標準クレームを検証する
検証プロセスにはアプリケーション権限(スコープ)の確認も含まれますが、これに関しては次の段落で別に説明します。
アクセストークン検証の詳細については、「アクセストークンを検証する」を参照してください。
Node.jsでの実装を参照してください。
アプリケーション権限の確認
この段階ではJWTの有効性が検証されています。最後の手順は、アプリケーションが保護されたリソースにアクセスするために必要な権限を持っているかどうかを検証することです。
そのためには、APIがデコードされたJWTのスコープを確認する必要があります。このクレームはペイロードの一部で、スペースで区切られた文字列のリストです。
Node.jsでの実装を参照してください。
ユーザーIDの判断
どちらのエンドポイント(タイムシートのリスト取得用と新規タイムシート追加用)でも、ユーザーのIDを判断する必要があります。
これは、タイムシートのリスト取得に関しては、要求元のユーザーのタイムシートのみを返すようにするためです。一方の新規タイムシートの追加に関しては、タイムシートがその要求元のユーザーと関連付けられていることを確認するためです。
標準JWTクレームの1つは、クレームの対象である本人を識別するsub
クレームです。暗黙的付与フローの場合、このクレームにはAuth0ユーザーの一意の識別子であるIDが含まれます。これを使って、外部システムのいかなる情報でも、特定ユーザーに関連付けることができます。
また、カスタムクレームを使って、メールアドレスなど別のユーザー属性をアクセストークンに追加し、ユーザーを一意に識別することもできます。
Node.jsでの実装を参照してください。
SPAの実装
このセクションでは、当社シナリオでSPAを実装する方法を説明します。
ユーザーの認証
ユーザーの認証にはauth0.jsライブラリが使われます。Auth0アプリケーションの新しいインスタンスを次のように初期化できます。
var auth0 = new auth0.WebAuth({
clientID: '{yourClientId}',
domain: '{yourDomain}',
responseType: 'token id_token',
audience: 'YOUR_API_IDENTIFIER',
redirectUri: '{https://yourApp/callback}',
scope: 'openid profile read:timesheets create:timesheets'
});
Was this helpful?
以下の構成値を渡す必要があります。
clientID:Auth0のクライアントIdの値。これはDashboardにある[Application(アプリケーション)]の[Settings(設定)]で取得できます。
domain:Auth0ドメインの値。これはDashboardにある[Application(アプリケーション)]の[Settings(設定)]で取得できます。
responseType:使用する認証フローです。暗黙フローを使うSPAの場合は、
token id_token
に設定します。token
の部分はURLフラグメントでアクセストークンを返すフローをトリガーし、id_token
の部分はIDトークンも返すフローをトリガーします。audience:API識別子の値。これはDashboardにある[Settings of your API(APIの設定)]で取得できます。
redirectUri:ユーザー認証後のAuth0のリダイレクト先URL。
scope:IDトークンとアクセストークンで返される情報を決定するスコープ。
openid profile
のスコープは、IDトークン中のユーザープロファイル情報をすべて返します。APIを呼び出すために必要なスコープも要求する必要があります。この場合は、read:timesheets create:timesheets
スコープです。そうすることで、アクセストークンにこれらのスコープがあるようにします。
認証フローを開始するには、authorize()
メソッドを呼び出すことができます。
auth0.authorize();
Was this helpful?
Auth0は認証後、Auth0アプリケーションの新たなインスタンス構成時に指定したredirectUriに再びリダイレクトして戻ります。この時点で、URLハッシュフラグメントを解析するparseHash()
メソッドを呼び出してAuth0認証応答の結果を抽出する必要があります。
parseHashが返すauthResultオブジェクトの内容は、どの認証パラメーターが使われたかによって異なります。以下を含む可能性があります。
idToken:ユーザープロファイル情報が入ったIDトークンJWT
accessToken:audienceで指定されたAPIのアクセストークン
expiresIn:アクセストークンの有効期限(秒数)を含む文字列
トークンを保管するための最適な場所を決定します。シングルページアプリ(SPA)に1つでもバックエンドサーバーがあれば、トークンは認可コードフローまたはProof Key for Code Exchange(PKCE)を使った認可コードフローを使ってサーバー側で処理します。
対応するバックエンドサーバーがないSPAの場合、ログイン時に新しいトークンを要求し、それらをメモリ内に保存しますが、永続的に保存しないようにします。APIの呼び出しをするには、SPAがトークンのインメモリコピーを使用します。
SPAにおけるセッションの処理方法については、「JavaScriptシングルページアプリのQuickstart」で、「認可トークンの処理」セクションに記載されている例を参照してください。
Angular 2での実装を参照してください。
ユーザープロファイルの取得
トークンから情報を抽出する
このセクションでは、アクセストークンと/userinfoエンドポイントを使って、ユーザー情報を取得する方法について説明します。APIの呼び出しを避けたい場合には、ライブラリーを使って、単にIDトークンをデコードすることもできます(必ず先に検証をしてください)。他のユーザー情報が追加で必要な場合は、バックエンドからのManagement APIの使用を検討してください。
ユーザーのプロファイル情報を取得するために、返されたauthResult.accessToken
を渡してclient.userInfo
メソッドを呼び出すことができます。その場合、/userinfoエンドポイントに要求が送られ、以下の例によく似た、ユーザー情報が入ったuser
オブジェクトが返されます。
{
"email_verified": "false",
"email": "test@example.com",
"clientID": "AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHH",
"updated_at": "2017-02-07T20:50:33.563Z",
"name": "tester9@example.com",
"picture": "https://gravatar.com/avatar/example.png",
"user_id": "auth0|123456789012345678901234",
"nickname": "tester9",
"created_at": "2017-01-20T20:06:05.008Z",
"sub": "auth0|123456789012345678901234"
}
Was this helpful?
これらのプロパティにはuserInfo
関数を呼び出す際に渡されたコールバック関数でアクセスできます。
const accessToken = authResult.accessToken;
auth0.client.userInfo(accessToken, (err, profile) => {
if (profile) {
// Get the user’s nickname and profile image
var nickname = profile.nickname;
var picture = profile.picture;
}
});
Was this helpful?
Angular 2での実装を参照してください。
スコープに基づいた条件付きUI要素の表示
ユーザーのscope
に基づいて、特定のUI要素を表示または非表示にしたい場合があります。ユーザーに発行されたスコープを決めるには、最初に認可プロセスで要求されたスコープを保存する必要があります。ユーザーが認可されると、authResult
でscope
も返されます。
authResult
のscope
が空だと、要求したすべてのスコープが認められたことになります。authResult
のscope
が空でない場合は、異なる一連のスコープが認められ、authResult.scope
にあるスコープを使用すべきであることを意味します。
Angular 2での実装を参照してください。
APIの呼び出し
APIから安全なリソースにアクセスするには、認証されたユーザーのアクセストークンを、送信される要求に入れる必要があります。これには、Bearer
スキームを使用して、Authorization
ヘッダー内でアクセストークンを送る必要があります。
Angular 2での実装を参照してください。
アクセストークンの更新
安全のため、ユーザーアクセストークンのライフタイムは短くすることが推奨されます。Auth0 DashboardでAPIを作成する場合、デフォルトのライフタイムは7200
秒(2時間)ですが、APIごとに制御できます。
いったん期限が切れたアクセストークンは、APIのアクセスに利用できなくなります。再びアクセスを得るには、新たなアクセストークンを得る必要があります。
最初のアクセストークンを取得する際に使った認証フローを繰り返すことで、新しいアクセストークンを取得できます。しかしこれは、SPAでは最適な方法とはいえません。ユーザーを現在のタスクからリダイレクトして再び認証フローを完了させるような手間をかけさせたくない場合があるからです。
そのような場合は、サイレント認証を使うとよいでしょう。サイレント認証で使われる認証フローでは、Auth0はリダイレクトでのみ返答して、ログインページで返答することはありません。しかし、このためにはユーザーはすでにシングルサインオン経由でログインしている必要があります。
Angular 2での実装を参照してください。