API Management を使えば Azure AD を使った認証をサクッと有効化出来ますが、もっとライトに組み込みたいと思ったので Azure Functions の機能を使って同じように実現できるかを調べました。
当然ながら Easy Auth は App Service の機能なので、Azure Functions に限定した内容ではないです。
Easy Auth はいろんなプロバイダが使えますが、基本的には Azure AD を使った認証を使って試しました。Managed Identity や Microsoft Graph と組み合わせたり、地味に使いどころが多いのが Azure AD です。
Windows App Service の Easy Auth は IIS Module として実装されているので、アプリケーションにトラフィックが届く前に色々処理が挟まります。
特に未ログイン状態の時の挙動が変わるので、これを機に気になる部分を試しました。
Easy Auth 有効後の未ログイン時レスポンス
Web API の場合は未ログイン時にログインページへリダイレクトされたら困るので、適当な API を作りいろんなパターンでリクエストを投げて確認して結果をまとめました。
- UA 関係なく X-Requested-With = XMLHttpRequest の場合
- 403 Forbidden を返す
- UA がブラウザかつ X-Requested-With = XMLHttpRequest 以外の場合
- リダイレクト用の HTML が返される (200 OK)
- それ以外の場合(例 : UA / X-Requested-With がない)
- 401 Unauthorized を返す
ちょっとわかりにくいですが、Web API として呼ばれたであろう時は 401 か 403 を返します。普通に XHR でブラウザからリクエストを投げた場合は 403 が返ってくるので分かりやすいです。
ブラウザかどうかの判定は Mozilla/
が UA の先頭にあるかを見てるだけっぽいです。結構ザル判定なので、User-Agent を HttpClient
で指定する時には気を付けたい感じです。
あとフローによっては 401 と 403 と別々のステータスコードが返ってくるのに注意したいです。エラーハンドリングをステータスコードで見ている場合、素通りしてしまうこともありそうです。
ClaimPrincipal を使って認証状態を確認する
Easy Auth は認証周りを全部 IIS Module が行ってくれるので、実際にアプリから利用する場合は Claims を復元する必要がありますが、Azure Functions は自動で復元して HttpContext.User
かバインディングで ClaimsPrincipal
を渡してくれます。
以下のような Function を用意して、どのような Claims が渡されるか確認しました。Easy Auth は Azure AD を有効化しているので、他のプロバイダよりも Claims が多いです。
public static class Function1 { [FunctionName("Function1")] public static async Task<IActionResult> Run( [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req, ILogger log) { var claimsPrincipal = req.HttpContext.User; var builder = new StringBuilder(); builder.AppendLine($"Name = {claimsPrincipal.Identity.Name ?? "(null)"}"); builder.AppendLine($"IsAuthenticated = {claimsPrincipal.Identity.IsAuthenticated}"); builder.AppendLine($"AuthenticationType = {claimsPrincipal.Identity.AuthenticationType ?? "(null)"}"); foreach (var claim in claimsPrincipal.Claims) { builder.AppendLine($"{claim.Type} = {claim.Value}"); } return new OkObjectResult(builder.ToString()); } }
今回試して初めて知りましたが、Easy Auth 以外に Function Key や Host Key を使った認証の場合でも ClaimsPrincipal
はセットされるようでした。
なので HttpContext.User
を参照すれば認証の種類に関係なく、簡単にログイン済みか判別できます。
キー・トークン未指定
Name = (null) IsAuthenticated = False AuthenticationType = (null)
Function Key
Name = (null) IsAuthenticated = True AuthenticationType = WebJobsAuthLevel http://schemas.microsoft.com/2017/07/functions/claims/authlevel = Function http://schemas.microsoft.com/2017/07/functions/claims/keyid = default
Host Key (default)
Name = (null) IsAuthenticated = True AuthenticationType = WebJobsAuthLevel http://schemas.microsoft.com/2017/07/functions/claims/authlevel = Function http://schemas.microsoft.com/2017/07/functions/claims/keyid = default
Host Key (_master)
Name = (null) IsAuthenticated = True AuthenticationType = WebJobsAuthLevel http://schemas.microsoft.com/2017/07/functions/claims/authlevel = Admin http://schemas.microsoft.com/2017/07/functions/claims/keyid = master
Easy Auth (Azure AD / MSA)
Name = me@shibayan.jp IsAuthenticated = True AuthenticationType = aad aud = 00000000-0000-0000-0000-000000000000 iss = https://sts.windows.net/00000000-0000-0000-0000-000000000000/ iat = 1572192363 nbf = 1572192363 exp = 1572196263 aio = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX http://schemas.microsoft.com/claims/authnmethodsreferences = pwd c_hash = _CV4pcEcUNZnJPI3eAsdmg http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress = me@shibayan.jp http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname = Shibamura http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname = Tatsuro http://schemas.microsoft.com/identity/claims/identityprovider = live.com ipaddr = xxx.xxx.xxx.xxx name = shibayan nonce = 58b2598f76a84491ab5931c862f7fe4c_20191027161602 http://schemas.microsoft.com/identity/claims/objectidentifier = 00000000-0000-0000-0000-000000000000 http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier = XXXXXXXXX-XXXXXXXXXXXXXXXXXXXXX_XXXXXXXXXXX http://schemas.microsoft.com/identity/claims/tenantid = 00000000-0000-0000-0000-000000000000 http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name = live.com#me@shibayan.jp uti = hII4-LTwnkqDubPwk0-sAA ver = 1.0 wids = 00000000-0000-0000-0000-000000000000
Easy Auth (Azure AD / Bearer Token)
Name = 00000000-0000-0000-0000-000000000000 IsAuthenticated = True AuthenticationType = aad aud = https://easyauth-api.azurewebsites.net iss = https://sts.windows.net/00000000-0000-0000-0000-000000000000/ iat = 1572157734 nbf = 1572157734 exp = 1572186834 aio = 42VgYFB78dtXa23ZvvmV0dNO3y3wAQA= appid = 00000000-0000-0000-0000-000000000000 appidacr = 2 http://schemas.microsoft.com/identity/claims/identityprovider = https://sts.windows.net/00000000-0000-0000-0000-000000000000/ http://schemas.microsoft.com/identity/claims/objectidentifier = 00000000-0000-0000-0000-000000000000 http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier = 00000000-0000-0000-0000-000000000000 http://schemas.microsoft.com/identity/claims/tenantid = 00000000-0000-0000-0000-000000000000 uti = o151aNRqEUuy5TiJ9xBiAA ver = 1.0
最後に書いてある Easy Auth (Azure AD / Bearer Token) というのはちょっと特殊な使い方です。微妙に Undocumented な使い方なので、いろいろと調べた後にブログにまとめておこうと思います。
Bearer Token を Managed Identity で取得できるようになると、Azure AD を使った認証をユーザーと関係ない部分で簡単に使えるようになるので、認証周りのパターンを作れると便利になると思っています。