[App Service 認証(Easy Auth)] 特定の Azure AD アプリケーションからのアクセスのみ許可する

App Service では、App Service 認証という機能を有効にすることでアプリケーション側で実装を行わずに、簡単に Azure AD などの ID プロバイダー(以下、IdP) と SSO を実現することが出来ます。

[Azure App Service および Azure Functions での認証と承認](https://learn.microsoft.com/ja-jp/azure/app-service/overview-authentication-authorization)
>Azure App Service は組み込みの認証と認可の機能 (Easy Auth (簡単認証) と呼ばれることもあります) を提供するので、Web アプリ、RESTful API、モバイル バックエンド、さらには Azure Functions でも、最小限のコードを記述するだけで、またはまったく記述せずに、ユーザーのサインインとデータへのアクセスを可能にできます。

本記事では、App Service 認証を有効にする方法と、App Service 認証特定のアプリケーションからのアクセスのみ許可するように構成する方法について記載します。


# App Service 認証 (組み込みの認証機能) とは
App Service 上にデプロイした Web アプリケーションで、アプリケーション コードの実装無しで簡単に ユーザーの認証機能 (シングルサインオン(以下、SSO)) を実現する方法です。

SSO の実現には、通常アプリケーションコードで認証連携のための実装が必要になりますが、App Service では、App Service 認証を有効にすることで、認証連携に必要な処理 (IdP からアクセストークンを取得したり、認証セッションを管理したり、など) を認証用モジュールが肩代わりしてくれます。

[App Service 認証 機能のアーキテクチャ](https://learn.microsoft.com/ja-jp/azure/app-service/overview-authentication-authorization#feature-architecture)
> ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256703/2f04ad3c-a14a-ca70-edb3-13860c9c2dde.png)

App Service 認証を有効にすると、IdP と認証連携後取得したアクセストークンや、ID トークン、またユーザー情報 (ユーザー名など) をアプリケーションコードから取得することができるため、単純にアクセス制限として App Service 認証を活用するだけではなく、Web アプリケーション内でユーザー情報を元にしたログイン機能や機能制限なども行うことが出来ます。



但し、App Service 認証で実現できる SSO は、連携対象とする IdP やプロトコルは事前に用意されたものの中から選択する形となるため、例えば SAML 認証など対応していないプロトコルや IdP と連携を希望する場合はアプリケーション コード内で実装する形となります。

※ ちなみに、App Service では、SSO の他にも、Basic 認証、クライアント証明書による認証なども実現できますが、この話はまた別の記事でご紹介できればと思います。


# App Service 認証を有効にする
本記事では、Azure AD を IdP として認証連携するように App Service 認証を構成します。連携可能な IdP は、Azure AD の他にも、Google、Fracebook、Twitter、さらには OpenID Connect に対応している IdP 等です。

App Service 認証は、Azure ポータルから App Service を選択後、[認証] ブレードから有効にできます。Azure AD を IdP として構成するには、[認証] ブレード> [ID プロバイダーの追加] をクリックし、"ID プロバイダー" ドロップダウンで [Microsoft] を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256703/22a14943-9035-9434-a314-c1349613b9c7.png)


ID プロバイダーに [Microsoft] を選択後、基本的には規定値のままで動作できますが、構成の詳細などは、以下サイトに非常に分かりやすく記載されていますのでぜひ参照ください。
[Azure AD ログインを使用するように App Service または Azure Functions アプリを構成する](https://learn.microsoft.com/ja-jp/azure/app-service/configure-authentication-provider-aad#--option-1-create-a-new-app-registration-automatically)

>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256703/e52f26f5-a140-945f-720f-e3e0055064b7.png)


## (参考) Azure AD の構成を確認する
Azure AD などの IdP と SSO を構成する際には、通常アプリケーション (サービス プロバイダー) 側だけではなく、IdP にも対象アプリケーションと SSO するための構成を追加する必要があります。
App Service 認証では、[アプリの登録の種類] を　"アプリの登録を新規作成する" とすると、App Service と認証連携するための設定が Azure AD へ自動的に追加されます。実際にどのような値が追加されるか見てみます。

Azure AD へ追加された構成は、Azure ポータルより App Service 認証を有効にした App Service を選択後、[認証] > ID プロバイダー (Microsoft) に表示されるリンクより表示させることができます。または、Azure ポータルより [Azure Active Directory] > [アプリの登録] から "App Service 認証を登録したときの名前" で検索すると同じ構成が確認できます。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256703/2461fa44-3f23-d43d-4197-6adea805b2c3.png)


[概要] ブレードでは、App Service のアプリケーション (クライアント) ID、オブジェクト ID などが記載されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256703/daf6d064-66d0-365a-ec9e-649002e41b34.png)

[認証] ブレードでは、認証連携時に使用するリダイレクト URI やアカウントの種類などが記載されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256703/0a2ce79e-81b9-224d-1138-d928ca536811.png)


また、App Service 認証で Microsoft を IdP として選択した場合には、通常 OpenID Connect が SSO のプロトコルとして使用されます。Azure AD の OpenID Connect の構成は、以下 URL から取得できます。

[アプリの OpenID 構成ドキュメント URI を見つける](https://learn.microsoft.com/ja-jp/azure/active-directory/develop/v2-protocols-oidc#find-your-apps-openid-configuration-document-uri)
> - 既知の構成ドキュメント パス: /.well-known/openid-configuration
> - 機関 URL: https://login.microsoftonline.com/{tenant}/v2.0
> GET /{tenant}/v2.0/.well-known/openid-configuration
> Host: login.microsoftonline.com

実際にリクエストしてみると issuer や authorization_endpoint などが確認できます。

```json:/.well-known/openid-configuration
{
    "token_endpoint": "https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token",
 (中略)
    "issuer": "https://login.microsoftonline.com/{tenant}/v2.0",
    "request_uri_parameter_supported": false,
    "userinfo_endpoint": "https://graph.microsoft.com/oidc/userinfo",
    "authorization_endpoint": "https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize",
    "device_authorization_endpoint": "https://login.microsoftonline.com/{tenant}/oauth2/v2.0/devicecode"
 (中略)
}
```

# 特定の Azure AD アプリケーションからのみリクエストを許可するように構成する
App Service 認証を有効にしたのみでは、認証さえ通ってしまえば、基本的にはどのアプリケーション/ユーザーでも App Service 上のアプリケーションへアクセスすることが出来ます。これを、特定の Azure AD アプリケーション からのみリクエストを受け付けるように構成します。

構成方法は以下の 3 つあります。

**1 ) App Service 認証の追加の検証機能を使用する方法**（※一番簡単）
App Service 認証の追加の検証機能により、指定した Azure AD のアプリケーション/サービスプリンシパルのみ App Service へアクセスできるように構成することが出来ます。

この方法は、App Service 認証の以下ドキュメントでも紹介されています。
[追加の検証 (省略可能)](https://learn.microsoft.com/ja-jp/azure/app-service/configure-authentication-provider-aad#additional-validations-optional)

**2 ) アプリ ロールを使用する方法**
リクエスト元アプリケーションにロールを割り当てる方法です。アプリケーションに割り当てされたロールは、Azure AD から取得したアクセストークンより確認でき、これによりアプリケーションはロールをもとにアプリケーションコードの制御を行うことが出来ます。

この方法は、App Service 認証の以下ドキュメントでも紹介されています。
[デーモン クライアント アプリケーション (サービス間の呼び出し)](https://learn.microsoft.com/ja-jp/azure/app-service/configure-authentication-provider-aad#daemon-client-application-service-to-service-calls)

**3 ) Azure AD 側でユーザーの割り当てを有効化する方法**
Azure AD 側でユーザーの割り当てを有効化する方法は Azure AD の管理機能で、トークン発行が実施できるプリンシパルを絞るような機能です。アプリロールを使用する方法では、アプリケーション内でロールに応じて制御を行うのに対し、ユーザー割り当てを有効化する方法では、そもそも Azure AD から App Service へアクセスするためのトークン発行を特定のアプリケーション/ユーザーに制御します。


それぞれ順に試していきます。
## Option 1 ) App Service 認証の追加の検証機能を使用する方法
追加の検証機能を有効にするには、App Service 認証の構成の valdation セクションを変更します。既定値では、すべてのアプリケーション/サービスプリンシパルがアクセスできるようになっています。

```js:authsettingsV2
{
    "validation": {
        "defaultAuthorizationPolicy": {
            "allowedApplications": [],
            "allowedPrincipals": {
                "identities": []
            }
        }
    }
}
```
各プロパティ値の詳細はこちら
[追加の検証 (省略可能)](https://learn.microsoft.com/ja-jp/azure/app-service/configure-authentication-provider-aad#additional-validations-optional)
>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256703/50ae484f-d99b-8884-129d-b80f65bea842.png)

上記は Azure Resource Manager テンプレートまたは REST API で設定を変更できます。今回は、Azure Resource Explorer を使用した構成方法についてご紹介します。


### ① Azure Resource Explorer へアクセスします
Azure Resource Explorer: https://resources.azure.com 

### ② 次の手順を実行します。
a. ページの上部にある [Read/Write] を選択します。
b. 左側のブラウザーで、[subscriptions]><subscription_name>>[resourceGroups]><resource_group_name>>[providers]>[Microsoft.Web]>[sites]><app_name>>[config]>[authsettingsV2] に移動します。
c. [Edit] をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256703/21b3d29a-8615-35e0-ae81-dcc608eff039.png)

d. プロパティを変更します。
例えば、特定のサービスプリンシパルのみアクセス許可とする場合には、以下のようになります。

```js:authsettingsV2
"defaultAuthorizationPolicy": {
            "allowedPrincipals": {},
            "allowedApplications": [
              "＜許可対象のサービスプリンシパルに割り当てされたクライアント ID＞"
            ]
          }
```
e. [Put] をクリックします。

上記手順により、App Service 認証にて 追加の検証 が行われるよう構成が完了しました！
なお、構成の読み込みには少々時間を要する場合があるため、数分程待ってから、指定したクライアント ID にて取得したアクセストークンのみアクセスが許可されることをお試しいただくのが良いかと思います。


## Option 2 ) アプリ ロールを使用する方法


### ① リクエスト元アプリケーションを Azure AD に登録する
まずはリクエスト元アプリケーションを Azure AD に登録します。登録手順については、以下サイトの手順を参考にします。

[デーモン クライアント アプリケーション (サービス間の呼び出し)](https://learn.microsoft.com/ja-jp/azure/app-service/configure-authentication-provider-aad#daemon-client-application-service-to-service-calls) 
>1. Azure portal で、 [Active Directory] 、 [アプリの登録] 、 [新規登録] の順に選択します。
>1. [アプリケーションの登録] ページで、デーモン アプリの登録の [名前] を入力します。
>1. デーモン アプリケーションの場合、リダイレクト URI は不要であるため、空のままでかまいません。
>1. ［作成］ を選択します
>1. アプリの登録が作成されたら、 [アプリケーション (クライアント) ID] の値をコピーします。
>1. [証明書&シークレット]>[新しいクライアント シークレット]>[追加] を選択します。 ページに表示されるクライアント シークレットの値をコピーします。 二度と表示されることはありません。

### ② アプリロールを作成する
[アプリ ロール UI](https://learn.microsoft.com/ja-jp/azure/active-directory/develop/howto-add-app-roles-in-azure-ad-apps#app-roles-ui) を参考に、Azure ポータルより Azure Active Directory > [アプリの登録] から App Service 認証を有効にした App Service を選択 > [App roles] > [Create app role] で任意のアプリロールを作成します。"許可されたメンバーの種類" は、アプリケーションまたは両方にします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256703/812121f6-62bb-594b-b7e9-0a8231bf73b5.png)

### ③ アプリロールを付与する
[アプリケーションへのアプリ ロールの割り当て](https://learn.microsoft.com/ja-jp/azure/active-directory/develop/howto-add-app-roles-in-azure-ad-apps#assign-app-roles-to-applications)を参考に ① で Azure AD に登録したアプリケーションに ② で作成したアプリロールを付与します。具体的には、Azure ポータルより Azure Active Directory > [アプリの登録] から ① で作成したアプリケーションを選択 > [API のアクセス許可] > [アクセス許可の追加] > [マイ API] タブで ② で定義したアプリロールを選択、 [アクセス許可の追加] ボタンを押下します。

アプリロールの追加後、クライアント アプリケーションがアクセス許可を要求することを承認するために、必ず [管理者の同意を与える] をクリックしてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256703/c5f4eb87-2da4-c9a1-b4ac-4abf1a99be97.png)


これでアクセストークンにアプリロール(roles) が含まれるようになります。

アプリ ロールの仕組みや詳細については、Japan Azure Identity Support チームにより公開されている以下のブログが非常に分かりやすいです。さらに詳しく知りたいという方は、ご確認いただくことをおススメします！
[**Azure AD に登録できる 「アプリ」と「リソース」、「API 権限」を理解する (Japan Azure Identity Support Blog)**](https://jpazureid.github.io/blog/azure-active-directory/oauth2-application-resource-and-api-permissions/)

### ④ アプリケーションコードでアクセストークンを取得する
ユーザーのアクセストークンは、リクエストヘッダーから取得することができます。

[アプリ コードでのトークンの取得](https://learn.microsoft.com/ja-jp/azure/app-service/configure-authentication-oauth-tokens#retrieve-tokens-in-app-code)
>サーバー コードからプロバイダー固有のトークンが要求ヘッダーに挿入されるので、これらのトークンに簡単にアクセスできます。 次の表は、可能なトークン ヘッダー名を示しています。
>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256703/db78c936-9ecb-223d-0917-de8acffc6893.png)

実際に、取得したアクセストークンのペイロードを確認すると、以下のように追加した role が確認できます。アプリケーション コードでは、この role をもとにアプリケーション内で機能の制限などを行うことができます。

```json
{
  "aud": "{クライアント ID}",
  "iss": "https://sts.windows.net/{tenantId}/",
(中略)
  "roles": [
    "Task.Write"
  ]
(中略)
}
```




## Option 3 ) Azure AD 側でユーザーの割り当てを有効化する方法

Option 2 ) アプリ ロールを使用する方法の ①、②、③ まで同じ手順を行います。

### ① リクエスト元アプリケーションを Azure AD に登録する
Option 1 の手順を参照します。
### ② アプリロールを作成する
Option 1 の手順を参照します。
### ③ アプリロールを付与する
Option 1 の手順を参照します。
### ④ アプリの割り当てを有効にする
Azure ポータルより [Azure Active Directory] > [エンタープライズ アプリケーション] より App Service 認証を有効にした App Service を選択します。[プロパティ] のページから [アプリケーション ID] の値をコピーし、[割り当てが必要ですか?] を はい にして [保存] をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256703/6f8dc68d-0a0f-5fa4-3b03-5ca9abd9cf48.png)

これでアプリの割り当てが完了し、アクセス先 App Service へアクセスするためのアクセストークンの発行がアプリロールの割り当てが行われたアプリ (プリンシパル) のみに制限されます。


# 【番外編】App Service 認証が有効な App Service へアクセスする
App Service 認証がうまくいかないときなどに、App Service の代わりに Azure AD からアクセストークンを取得して App Service 認証を通過できるか検証を行いたい場合があります。

番外編では、本記事では、アプリケーションから App Service へアクセスするシナリオを想定しているので、実際にアクセス元アプリケーションとして Azure AD からアクセストークンを取得し、App Service 認証が有効な App Service へアクセスできることを確認してみます！

## Azure AD からアクセストークンを取得
App Service より公開されている以下ドキュメントの手順を参考に、App Service 認証が有効な App Service へアクセスします。
[デーモン クライアント アプリケーション (サービス間の呼び出し)](https://learn.microsoft.com/ja-jp/azure/app-service/configure-authentication-provider-aad#daemon-client-application-service-to-service-calls)
>アプリケーションは、(ユーザーの代わりではなく) それ自体の代わりに App Service または関数アプリでホストされる Web API を呼び出すトークンを取得できます。 このシナリオは、ログイン ユーザーなしでタスクを実行する非対話型デーモン アプリケーションに役立ちます。 これには標準の OAuth 2.0 クライアント資格情報の付与が使用されます。

具体的には、リクエスト元アプリケーションのクライアント ID とクライアント シークレットを使用し、Azure AD よりアクセストークンを取得後、取得したアクセストークンを  Authorization ヘッダーへ付与しリクエストを送信することで、App Service 認証が有効な App Service へアクセスできます。

Azure AD のアクセストークンは、/{tenant}/oauth2/v2.0/token から取得できます。
[Microsoft ID プラットフォームの OAuth 2.0 クライアント資格情報フロー](https://learn.microsoft.com/ja-jp/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow#first-case-access-token-request-with-a-shared-secret)
>最初のケース:共有シークレットを使ったアクセス トークン要求
>```
> POST /{tenant}/oauth2/v2.0/token HTTP/1.1           //Line breaks for clarity
> Host: login.microsoftonline.com
>Content-Type: application/x-www-form-urlencoded
>
>client_id=535fb089-9ff3-47b6-9bfb-4f1264799865
>&scope=https%3A%2F%2Fgraph.microsoft.com%2F.default
>&client_secret=sampleCredentia1s
>&grant_type=client_credentials
>```

上記 URL からアクセストークンを取得する際には、client_id は アクセス元アプリケーションとして Azure AD に登録したアプリケーションのアプリケーション ID (Clinet ID)、scope は "{App Service 認証で設定したアプリケーション ID}/.default" という形式で指定します。
例: ID プロバイダー(Microsoft) のアプリ (クライアント) ID が 190XXX-XXX-XX だった場合、scope の値は 190XXX-XXX-XX/.default （または、api://190XXX-XXX-XX/.default）となります。

{App Service 認証で設定したアプリケーション ID} は、Azure ポータルより、App Service 認証を有効にした App Service を選択後、[認証] 画面より、ID プロバイダー(Microsoft) のアプリ (クライアント) ID より確認することが出来ます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256703/b7129c88-c46a-0803-294d-6f3bb8bde2e3.png)


実際に Powershell でアクセストークンを取得してみます。

```powershell
$tenantId = '{Azure AD のテナント ID}'
$authBody=@{
    client_id = '{アクセス元アプリケーションのクライアント ID}'
    client_secret = 'XX'
    scope = '{アクセス先 App Service のクライアント ID}/.default'
    grant_type = 'client_credentials'
}
$tokenUri = "https://login.windows.net/$tenantId/oauth2/v2.0/token"

# アクセストークンを取得
$accessResponse = Invoke-WebRequest -Uri $tokenUri -ContentType 'application/x-www-form-urlencoded' -Body $authBody -Method Post -ErrorAction Stop -Verbose

# 取得したアクセストークンを確認
$accessToken = $accessResponse.content | ConvertFrom-Json
$accessToken.access_token
```

$accessToken.access_token でアクセストークンが確認できます。 ちなみに、アクセストークンのデコードには以下サイトが便利です。

https://jwt.io/


## アクセストークンを使って App Service へアクセスする
上記手順で取得したアクセストークンを Authorization ヘッダーに付与し、App Service 認証を有効にした App Service へリクエストを送信します。


```powershell
$authHeader = @{
    'Content-Type' = 'application/json'
    'Authorization' = 'Bearer ' + $accessToken.access_token
    }
 
$siteURI = 'https://{App Service リソース名}.azurewebsites.net/'
$getResponse = Invoke-WebRequest -Method Get -UseBasicParsing -Uri $siteURI  -Headers $authHeader -SessionVariable session -Verbose
```


これで無事に App Service へアクセスできることが確認できれば OK です。

また追加の情報などあれば追記していきます。おつかれさまでした！