BEACHSIDE BLOG

Azure と GitHub と C# が好きなエンジニアの個人メモ ( ・ㅂ・)و ̑̑

Easy Auth の App Service Token Store

前回 App Service の Authentication / Authorization feature (a.k.a. Easy Auth ) のアーキテクチャーに関する Chris さんのブログの意訳をしましたが、その続編の Chris さんのブログを今回も意訳します。2016年の記事ですが、参考になります。

オリジナルのブログはこちらになります。
App Service Token Store - CGillum Dev Blog

このブログを元に公式ドキュメントではここら辺に書かれています。

docs.microsoft.com

ドキュメント読めば十分な気もしてますが、今回も興味本位で意訳のメモをします。

はじめに

App Service のトークンストアは、App Service の Authentication / Authorization feature (a.k.a. Easy Auth ) に追加された機能です。名称が示すようにトークンストアは、 OAuth トークンのリポジトリで、アプリのエンドユーザーに関連しています。

ユーザーがAzure Active Directory や Facebook (または他にサポートされている ID プロバイダー)といった認証プロバイダーを介してアプリにログインした時、ID プロバイダーは、ユーザーの ID を証明する1つ以上のトークンを提供し、また、所有するリソースへのアクセスも提供する場合もあります。

Easy Auth が構成された App Service は、デフォルトで、アプリのコードからアクセス可能なトークンストアが組み込まれています。

App Service の Web Apps, Mobile Apps, API Apps, Function App の全てのプランで利用可能です。

Common Scenarios for the Token Store

アプリを開発して、Facebook アカウントの認証でログインする機能がほしいとしましょう。さらに、アプリで Facebook のタイム来に投稿したいとします。このようなアクションを実行するために Facebook API を呼び出すには、Facebook によって発行され、これを実行するのに適切な権限を持った OAuth トークンが必要です 。Token Store は、これらのトークンを自動的に収集して保存し、アプリがトークンに簡単にアクセスできるためのものです。

同様に、自身の組織や会社の Directory の替わりに Azure Active Directory の Graph APIMicrosoft Graph の呼び出しを必要とするアプリを作成している場合、アプリは、必要なアクセストークンをトークンストアに自動で保存するよう構成すべきです。Graph API にアクセスする Azure Active Directory のアプリを構成する方法の詳細は、今後のブログで説明します。

便利ですが、全てのアプリがこれらの機能を必要とはしません。例えば、もしステージングスロットへのアクセスを保護するためだけに Easy Auth を使い、他にこれらのトークンを使う必要がないなら、トークンストアの機能を安全に機能を無効にできます。

Accessing the Tokens

バックエンドのコードでこれらのトークにアクセスるのは、 HTTP リクエストのヘッダーを読み取るのと同じくらい簡単です。ヘッダーに X-MS-TOKEN-{provider}-{type} といったキーがあります。見れるヘッダーの一覧は以下です。

Azure Active Directory Token Request Headers

  • X-MS-TOKEN-AAD-ID-TOKEN
  • X-MS-TOKEN-AAD-ACCESS-TOKEN
  • X-MS-TOKEN-AAD-EXPIRES-ON
  • X-MS-TOKEN-AAD-REFRESH-TOKEN

Facebook Token Request Headers

  • X-MS-TOKEN-FACEBOOK-ACCESS-TOKEN
  • X-MS-TOKEN-FACEBOOK-EXPIRES-ON

Google Token Request Headers

  • X-MS-TOKEN-GOOGLE-ID-TOKEN
  • X-MS-TOKEN-GOOGLE-ACCESS-TOKEN
  • X-MS-TOKEN-GOOGLE-EXPIRES-ON
  • X-MS-TOKEN-GOOGLE-REFRESH-TOKEN

Microsoft Account Token Request Headers

  • X-MS-TOKEN-MICROSOFTACCOUNT-ACCESS-TOKEN
  • X-MS-TOKEN-MICROSOFTACCOUNT-EXPIRES-ON
  • X-MS-TOKEN-MICROSOFTACCOUNT-AUTHENTICATION-TOKEN
  • X-MS-TOKEN-MICROSOFTACCOUNT-REFRESH-TOKEN

Twitter Token Request Headers

  • X-MS-TOKEN-TWITTER-ACCESS-TOKEN
  • X-MS-TOKEN-TWITTER-ACCESS-TOKEN-SECRET

リクエストヘッダーを見ることは、ユーザー固有の OAUth トークンを入手するためのシンプルな方法です。SDK や外部 API の呼び出し、データベースアクセスやファイルアクセスは必要ありません(これは Easy Auth を作るためのテーマのひとつです)。これらのヘッダーの値は、素のトークンの値なので、認証プロバイダーの API を呼び出す問いにそのまま使えます。パースする必要はありません。

特定のユーザーがアプリでどのトークンが利用できるかを確認するには、ログインしてリクエストヘッダーを確認してください。ローカルの IIS モジュールによってヘッダーに追加されています。これは、前回ポストしたブログ、App Service Auth Architecture で説明してます。

クライアント(例えばブラウザーの Javascript)からトークンにアクセスしたい場合や、ログインしているユーザーに関するリッチな情報を取得したい場合は、認証済みの状態で /.auth/me のエンドポイントに GET のリクエストを送信することで取得できます。アプリへのアクセスに使用する認証方法(Cookieまたはトークン)でこの API にアクセスできます。リクエストのフォーマットはシンプルです:

GET /.auth/me

これで以下のような JSON のレスポンスが返ってきます。

[{
  "provider_name":"<provider>",
  "user_id": "<user_id>",
  "user_claims":[{"typ": "<claim-type>","val": "<claim-value>"}, ...],
  "access_token":"<access_token>",
  "access_token_secret":"<access_token_secret>",
  "authentication_token":"<authentication_token>",
  "expires_on":"<iso8601_datetime>",
  "id_token":"<id_token>",
  "refresh_token":"<refresh_token>"
}]

この API は、現在ログインしているユーザーの情報を取得するために Spp Service Server SDK が内部で使っています。

どの方法を選択しても、実際に利用可能なトークンは、アプリにログインするために使った認証プロバイダーと、そのにsん章プロバイダーが構成したスコープによって異なります。例えば Google のログインには常にアクセストークンが含まれていますが、オフラインアクセスのスコープが構成されてないとリフレッシュトークンは含まれません。

Where the Tokens Live

内部的には、これらすべてのトークンは、ローカルファイルストレージの D:/home/data/.auth/tokens に保存されています。トークンはアプリ固有の暗号化キーを使ってユーザーごとに .json ファイルに暗号化され、ベストプラクティスに従って暗号で署名されています。これはアプリが心配する必要のない内部の話です。安全であることだけ認識しておいてください。プログラムでトークンのファイルを読み込んで複合化することもできますが、今後のサービスのアップデートによってフォーマットが変わる可能性があるため、おすすめしません。

別の方法として、Azure Blob Storage のコンテナーをプロビジョニングして、そのコンテナーの SAS URL (read/write/list access) をアプリに構成することができます。この SAS の URL は、アプリケーション設定で WEBSITE_AUTH_TOKEN_CONTAINER_SASURL に保存しましょう。このアプリケーション設定が存在する場合、全てのトークンは指定した Blob コンテナーに保存され、読み込まれます。

Refreshing Tokens

アクセストークンを使う上で重要なことは、それらのほとんどがいつか有効切れになるということです。Facebook だと、60日でアクセストークが有効切れになります。Azure AD、Microsoft アカウント、や Google は有効期限が1時間のアクセストークンを発行します。どのケースでも、ユーザーに再認証させることで、新たに有効なトークンを取得できます。60日に一度だけ再認証を行う必要がある Facebook はよいです。しかし、有効期限が1時間の Azure AD、Microsoft アカウント、Google は実用的ではありません。

新しいアクセストークンを取得するためにユーザーに再認証を要求させるのを回避するには、認証済みの状態で GET のリクエストをアプリケーションの /.auth/refresh エンドポイントに発行します。これは /.auth/me と同様にビルトインのエンドポイントです。呼び出されると、Easy Auth のモジュールは、自動で認証されたユーザーのトークンストアのアクセストークンをリフレッシュします。そして、アプリのトークンに関するリクエストは、更新された最新のトークンを使います。この機能を使うには、トークンストアに認証プロバイダーのリフレッシュトークンが含まれている必要があります。この方法に慣れていない場合は、次のヒントをご覧ください。

Google

"access_type=offline" のクエリ文字列パラメーターを /.auth/login API にの呼び出しに追加してください(Mobile App SDK を使っている場合、LogicAsync オーバーロードの1つでこれを追加できます)。

Microsoft アカウント

Azure ポータルで wl.offline_access のスコープを選択してください。

Azure AD

これは少し複雑です。Graph API アクセスの有効化に関するブログをご覧ください。セットアップ手順に従って進めれば、Azure AD のリフレッシュトークンの取得ができます。

公式ドキュメントでのこの設定方法は以下です。
認証と認可の高度な使用方法 - Azure App Service | Microsoft Docs

Javascript (jQueryを使用) のクライアントからトークンを更新するサンプルは以下です。同様のロジックは、他の言語で書けるでしょう:

function refreshTokens() {
  var refreshUrl = "/.auth/refresh";
  $.ajax(refreshUrl) .done(function() {
    console.log("Token refresh completed successfully.");
  }) .fail(function() {
    console.log("Token refresh failed. See application logs for details.");
  });

重要なポイントは、/.auth/refresh API は、成功が保証されないことです。例えば、ユーザーはいつでもアプリの権限を取り消すことができます。このような場合、既存のアクセストークンを更新しようとすると、403 Forbidden のレスポンスで失敗するでしょう。また、認証プロバイダーがリフレッシュトークンを返す構成をしていない場合(例えば、適切なオフラインのスコープを設定しなかった場合)、400 Bad Request のレスポンスで失敗すること期待されます。どちらの場合でも、アプリケーションログで詳細を確認できます(アプリケーションログを有効にしている場合)。

Mobile App で Azure Service Mobile SDK を使い、内部的に App Service のバックエンドで認証に x-zumo-auth ヘッダーを使っていると、x-zumo-auth JWT は有効期限が短いことに気づくでしょう。モバイルの認証トークンも /.auth/refresh エンドポイントを使ってリフレッシュします。x-zumo-auth ヘッダー( Mobile Client SDK を使うとデフォルトで付きます)をつけて GET リクエストを /.auth/refresh エンドポイントに送信すれば、アプリで使う新しい認証トークンと一緒にレスポンスが返ってきます。リフレッシュトークンに関する同じ制限が引き続き適用されることには注意してください。

オリジナルのブログでは、このあと Mobile App に関する情報が書かれていますが、Mobile App は私が使わないので省略します。

終わりに

いい勉強になりました♪