しばやん雑記

Azure とメイドさんが大好きなフリーランスのプログラマーのブログ

Azure に実装されている 3 つの Easy Auth (Web Apps / Static Web Apps / Container Apps) の実装を再確認した

アプリケーション開発で地味に難しい認証周りですが、Azure の Web Apps / Static Web Apps / Container Apps には認証機能が組み込まれているため、コードを修正することなく追加できるので非常に便利です。

既に何回もブログで扱っているので、基本的な使い方については以下のエントリを参照してください。

少し前に GitHub や Sign in with Apple、汎用的な OpenID Connect に対応したので、適用可能なシナリオが大きく広がりました。最近では Azure AD B2C を導入して、OpenID Connect として Easy Auth に登録するパターンも増えてきました。以前はハックっぽい設定を入れる必要がありましたが、今では標準対応です。

継続的に Easy Auth の実装はアップデートが行われていて、少し前には OpenID Connect の PKCE 対応が追加されましたし、バグ修正も問題なく行われています。*1

Azure Portal やドキュメントでは Authentication という同じような扱いで、設定項目もほぼ同じなので実装されている機能も大体同じかと想像付きますが、実際には差が思ったよりもあるので困ることになります。

対象となるサービスが 3 つあるので、個別に実際にリソースをデプロイして動作を確認しました。

Easy Auth は個人的には革命的に便利な機能だと思っていますが、細かいところを見ると改善して欲しい点もあるため、何とかして開発チームにフィードバックをしていきたいです。

各 Easy Auth のアーキテクチャ

3 つのサービスに Easy Auth が実装されていますが、コア実装はほぼ同じライブラリを使って実現されています。OS によりアーキテクチャの差がありますが、実装はほぼ同一なので挙動も良くも悪くも同一です。

Web Apps (Windows / Linux)

オリジンとなる Web Apps の実装は初期リリースのタイミングで開発チームのクリスがブログで紹介しています。今でも大きく変わっていませんが、Windows では IIS のマネージドモジュールとして実装されています。

最新のアーキテクチャは公式ドキュメントで紹介されています。Windows は変わっていませんが、Linux はアプリケーションのコンテナーの前に Easy Auth のミドルウェアが実装されたコンテナーが存在しています。

App Service は元々アプリケーションのコンテナーの前にルーティングを行うコンテナーが存在していたので、その部分に実装されていると考えられます。恐らく .NET Core での実装となっているでしょう。

Static Web Apps

Static Web Apps はアーキテクチャが全く公開されていないので推測となりますが、ホスティングしているのは各リージョンにデプロイされた ASE なので、その上で動いている App Service の Easy Auth が使われていると考えられます。SWA のアーキテクチャは以前のエントリを参照してください。

SWA はファイルベースでの設定を行う必要があるので、Web Apps の Easy Auth とは若干異なる仕組みですが大した違いではありません。この辺りはもう少し情報が欲しいですね。

Container Apps

現状の Container Apps は Linux しか存在していないので、アーキテクチャは Web Apps の Linux とほぼ同じです。アプリケーションのコンテナーの前に Easy Auth 用のコンテナーが存在します。

ドキュメントには名前が出ていませんが Web Apps と同じくアンバサダーパターンが使われています。このコンテナーの実装は Web App と別にする必要性が存在しないので、同じものが使われていると期待します。

実際に外部から確認できる挙動を見ている限りは、同一の実装が使われているようです。

各 Easy Auth の設定方法とリファレンス

Easy Auth の実装はほとんど同じですが、それを設定する方法は各サービスで異なっています。具体的には ARM レベルで行うか、ファイルベースで行うかの違いです。

Web Apps (Windows / Linux)

Web Apps というか Azure Functions を含んだ App Service という括りで、歴史的経緯により Easy Auth の設定に v1 と v2 が存在しています。現在 Azure Portal の Authentication から設定すると自動的に v2 となりますが、以前に設定した場合は v1 になっているので Azure Portal からマイグレーションが行えます。

ARM Template や Bicep から設定する際には authsettingsV2 というリソースを定義する必要があります。Terraform は残念ながら現在も v1 が使われているはずです。

Azure Portal では設定が出来ない項目であっても ARM Template や Bicep では設定可能です。特にリバースプロキシ下で利用する際に必要となる以下の設定は重要となります。

App Service の前に Front Door を配置する構成は一般的なので、Front Door のカスタムドメインと Easy Auth を組み合わせて使う場合には設定値に要注意です。

Static Web Apps

Web Apps とは異なり Static Web Apps はファイルベースで設定する必要があるので若干特殊です。認証設定自体もアプリケーションと同一のリポジトリで管理できるメリットがあります。

ただし staticwebapps.config.json の定義はスキーマが無いと間違いやすいので、VS Code と JSON Schema を組み合わせて書くと入力補完がバシバシ利くようになります。

リファレンスを見て分かるように、設定項目自体は Web Apps の authsettingsV2 に酷似していますので、あまり悩むことなく定義を追加できるはずです。

ただし用意されていない設定もあるので、その場合は代替方法が別の設定に用意されていないか確認が必要です。具体的には forwardProxy 相当の設定は forwardingGateway で提供されています。

Static Web Apps には Enterprise-Grade Edge が用意されていますが、個別に Front Door を置いてルーティングを管理したいケースの方が多いので、こちらも Easy Auth を使う際には忘れないようにしたい設定です。

Container Apps

最後に Container Apps ですが、最後発ということもあり最初から v2 相当の設定が提供されているので、App Service で設定したことがある方なら何の違和感もなく対応できるはずです。

Azure Portal の設定画面も Web Apps と全く同じものが使われているので、設定しやすくなっています。

ARM レベルの話をすると authconfigs は配列になっているので、複数の設定を保持できるようになっていますが現状は current の 1 つだけのようです。

こちらも Front Door などのリバースプロキシを前に置くと、ホスト名の問題が発生するので Web Apps と同じように forwardProxy 設定を忘れないようにしましょう。

各 Easy Auth の実装で差のある機能

ここからは同じ Easy Auth 実装とはいえ機能にいくつか差があるので、その観点でいくつか紹介していきます。3 サービスで全く同じ挙動だと思い込んでいるとはまるので注意が必要です。

見出しに含まれているのは、その機能が利用可能なサービス名となります。

Token Store (Web Apps)

Web Apps では Azure Portal から設定するとデフォルトで有効化されているので、気にせずに使っていることが多そうなのが Token Store です。名前の通り IdP でのログイン時に取得した id_token / access_token / refresh_token をサーバー側で安全に保存してくれる機能です。

Easy Auth は IdP が返す id_tokenUserInfo エンドポイントに相当する API を実行してクレームを取得し、独自のセッショントークンを発行することで認証状態を維持しますが、そのセッションに access_tokenrefresh_token を紐づけることで、適切に scope の設定を行っておけば access_token を使って外部 API の実行が簡単に行えます。

保存されたトークンの使い方についてはドキュメントにまとまっています。基本はアプリケーションの HTTP リクエストヘッダーで渡されるので、サーバー側で API の呼び出しが可能となります。

Azure AD の場合はログインしたユーザーとして Graph API の呼び出しが行えるようになります。

非常に便利な機能なのですが、現状では Web Apps にしか実装されていないため、それ以外の Easy Auth では同時に返された access_token を利用することが出来ません。個人的に Token Store は 3 つのサービス全てで使えるようになって欲しいところです。

一般的な Web アプリケーションでは一度ログインすると、ブラウザを閉じたとしてもログイン状態が維持される機能が提供されていますが、Static Web Apps 以外の Easy Auth では発行されるクッキーがセッションになるため、ブラウザを閉じてしまうとログアウトされるようになっています。

様々な方法で確認したところ、Web Apps と Container Apps では永続クッキーを発行することは出来ないため、ブラウザを閉じると常に再ログインが必要となってしまいます。

ちなみに Web Apps と Container Apps では AppServiceAuthSession という名前のクッキーがセッションとして発行されますが、Static Web Apps では追加で StaticWebAppsAuthCookie という名前のクッキーが発行され、こちらが永続クッキーとなるためブラウザを閉じてもログイン状態は維持されます。

ただし Static Web Apps でもクッキーの有効期限が 8 時間後となっていて、スライディングで延長もされないため 8 時間後には再ログインが必要となってしまいます。Easy Auth の唯一イケていない部分がクッキー周りの実装だと思います。

ログイン時に offline_access を追加して refresh_token を取得しておいても、ブラウザを閉じればログインしなおしになるのであまり意味がないのが現状です。

/.auth/me エンドポイント (Web Apps / Static Web Apps)

フロントエンドのアプリケーションからユーザー情報やトークンを取得するためのエンドポイントとして /.auth/me が用意されていて、この API をブラウザから実行すると id_token をパースした結果が JSON で取得できます。Web Apps では access_token も取得できるので CORS 経由で別 API の実行も出来ます。

非常に便利な API ですが Token Store の有効化が前提となるため、以下のように Token Store が存在しない Container Apps では 404 が返ってきますし、Token Store をオフにした Web Apps でもエラーとなります。

例外として Static Web Apps に関しては SPA から簡単にユーザー情報を取得する方法が無くなってしまうため、以下のような JSON を返す簡易版の API が提供されています。

返ってくる JSON の形式が大きく異なっているので、Web Apps と同じコードは使えませんが大した問題ではありません。ちなみに SWA CLI でも /.auth/me が使えるので開発時にも便利です。

Client-directed sign-in (Web Apps / Container Apps)

少しこれまでと毛色が変わる機能で、ぶっちゃけほぼ知られていないと思われるのがこの Client-directed sign-in です。ほぼ覚えていないと思いますが Mobile Apps というサービスで使われていた名残という感じです。

機能としては IdP が提供する SDK を使って取得したトークンを Web Apps や Container Apps を渡して、Easy Auth のセッションに変換する機能です。Apple や Facebook などがモバイルアプリ向けに SDK を提供していますが、UX の観点からアプリ内でシームレスにトークンの取得までが行われますが、その後に Easy Auth で保護された API を使うための機能となります。

登録用のエンドポイントにトークンを POST すると Easy Auth のトークンが返ってくるので、実際の API アクセス時には X-ZUMO-AUTH という HTTP ヘッダーでトークンを渡して利用します。ちなみに ZUMO は Azure Mobile Service の略となっています。

同時に refresh_token を POST しておけば /.auth/refresh を実行してトークンの更新も行えるので便利ですが、現実的には OIDC ベースであれば取得した access_tokenAuthorization ヘッダーに渡せば Easy Auth を突破できるので、そちらの一般的な方法を使うことが多いです。

この挙動はドキュメント化されていない疑惑がありますが、X-ZUMO-AUTH と同じように動作します。