_posts/2018-09-14-functions-appservice-auth.html (706 lines of code) (raw):
---
title: "Azure Functions での App Service 認証/承認"
author_name: "Toru Itagaki"
tags:
- MigratedArticle
- Function App
- App Service
- Web Apps
---
<!-- article summary -->
<small><span style="font-style: italic">
この記事は <a href="https://docs.microsoft.com/ja-jp/archive/blogs/jpcie/azure-functions-での-app-service-認証-承認">旧 Japan Azure PaaS Support Blog</a> からのコピーとなります。<br />
記載されている内容は 2018 年 9 月 14 日 時点のものとなり、現時点におきましては変更されている可能性がありますので、ご注意下さい。
</span></small><hr />
<main class="content" data-bi-name="content" dir="ltr" id="main" lang="en-us" role="main">
<!-- <content> -->
<p>
こんにちは。Azure サポートの板垣です。
</p>
<p>
Azure App Service では、組み込みの認証/承認機能がサポートされています。
</p>
<p>
<a data-linktype="absolute-path" href="https://docs.microsoft.com/ja-jp/azure/app-service/app-service-authentication-overview">
Azure App Service での認証および承認
</a>
</p>
<p>
この機能は、Azure Functions でも利用することができ、HTTP トリガーで起動する Function に、任意の認証プロバイダーによる認証機能を付加することができます。
<br/>
これにより、認証されたクライアントのみを Function にアクセスできるようにさせることができます。
<br/>
今回は、認証プロバイダーとして Azure Active Directory を利用する場合の手順をご紹介します。
</p>
<p>
</p>
<hr/>
<h2 id="概要">
概要
</h2>
<p>
認証/承認機能が有効化された Function App にアクセスするためには、認証プロバイダーから取得した認証トークンを Function App に提示します。
<br/>
このため、Function App へのアクセス前に、認証プロバイダーから認証トークンを取得する必要があります。
<br/>
以下より、認証トークンを取得するための構成と、認証トークンの取得、使用方法を記載します。
</p>
<p>
</p>
<h2 id="function-app-で認証承認を構成する">
Function App で認証/承認を構成する
</h2>
<p>
Function App を作成し、プラットフォーム機能の [認証/承認] をクリックします。
</p>
<p>
<a href="azure-functions-での-app-service-認証-承認.html">
<img alt="" data-linktype="external" src="/blog/media/2018/09/14.png"/>
</a>
</p>
<p>
</p>
<p>
App Service 認証をオンにして "Azure Active Directory でのログイン" を選択し、認証プロバイダーとして Azure Active Directory を選択します。
<br/>
管理モードを "簡易" とし、新しい AD アプリを作成します。
</p>
<h2 id="section">
<a href="azure-functions-での-app-service-認証-承認.html">
<img alt="" data-linktype="external" src="/blog/media/2018/09/24.png"/>
</a>
</h2>
<p>
</p>
<p>
作成完了後、管理モードを "詳細" に切り替えると、作成された AD アプリの詳細を参照することができます。
<br/>
ここで、"クライアント ID" はこの AD アプリのリソースを表す ID となります。この ID を控えておきます。
</p>
<p>
<a href="azure-functions-での-app-service-認証-承認.html">
<img alt="" data-linktype="external" src="/blog/media/2018/09/31.png"/>
</a>
</p>
<p>
</p>
<p>
[関数の URL の取得] をクリックして、実行したい Function の URL を控えておきます。
</p>
<p>
<a href="azure-functions-での-app-service-認証-承認.html">
<img alt="" data-linktype="external" src="/blog/media/2018/09/8.png"/>
</a>
</p>
<p>
なお、HTTP トリガーで起動する Function は、既定では要求を承認するために API キーを必要とします(上図の code 値)。
<br/>
この設定は function.json で変更可能であり、authLevel プロパティを anonymous に設定すると、API キー無しでアクセス可能になります。
<br/>
API キーによる承認は Function ごとであるのに対して、本稿でご案内している Azure AD トークンによる承認は Function App 全体に対して適用されるという違いがあります。セキュリティ要件にあわせてそれぞれご利用ください。
</p>
<p>
<a data-linktype="absolute-path" href="https://docs.microsoft.com/ja-jp/azure/azure-functions/functions-bindings-http-webhook#trigger---configuration">
Azure Functions における HTTP と Webhook のバインド - トリガー - 構成
</a>
</p>
<p>
</p>
<p>
</p>
<h2 id="azure-active-directory-にアプリケーションを登録する">
Azure Active Directory にアプリケーションを登録する
</h2>
<p>
Function にアクセスするクライアント アプリケーションの定義を Azure AD に登録します。
<br/>
Azure AD アプリケーションは、Azure AD を利用するアプリケーションの定義です。Azure AD アプリケーションを登録することで、アプリケーションに対してトークンを発行する方法を定義します。
</p>
<p>
</p>
<p>
Azure AD テナントで、[アプリの登録] - [新しいアプリケーションの登録] をクリックします。
</p>
<p>
<a href="azure-functions-での-app-service-認証-承認.html">
<img alt="" data-linktype="external" src="/blog/media/2018/09/51.png"/>
</a>
</p>
<p>
</p>
<p>
</p>
<p>
以下、ネイティブ アプリケーション、Web アプリ/API のそれぞれを登録した場合について記載します。
<br/>
# 2018/09/19 アクセス許可の設定を追記しました。
<br/>
<br/>
</p>
<h3 id="ネイティブ-アプリケーション">
ネイティブ アプリケーション
</h3>
<p>
ネイティブ アプリケーションでは、クライアントに配布されるようなアプリケーションが想定されます。トークンを取得するために、基本的にユーザーが対話形式でサインインして、資格情報を提供します。
</p>
<p>
</p>
<p>
アプリケーションの種類を "ネイティブ" とします。リダイレクト URI は物理的に存在するものである必要はありませんが、アプリケーションに固有の値を設定しておきます。
</p>
<p>
<a href="azure-functions-での-app-service-認証-承認.html">
<img alt="" data-linktype="external" src="/blog/media/2018/09/42.png"/>
</a>
</p>
<p>
次に、アクセス許可の設定を追加します。この設定により、このアプリケーションで発行されるトークンが、Function App に対するアクセス許可を持つことを定義します。
</p>
<p>
[必要なアクセス許可] で [追加] をクリックします。
</p>
<p>
<a href="azure-functions-での-app-service-認証-承認.html">
<img alt="" data-linktype="external" src="/blog/media/2018/09/121.png"/>
</a>
</p>
<p>
</p>
<p>
API として、Function App で簡易作成した Azure AD アプリケーションを指定します。
</p>
<p>
<a href="azure-functions-での-app-service-認証-承認.html">
<img alt="" data-linktype="external" src="/blog/media/2018/09/131.png"/>
</a>
</p>
<p>
その Azure AD アプリケーションへのアクセスを許可するように設定し、完了します。
</p>
<p>
<a href="azure-functions-での-app-service-認証-承認.html">
<img alt="" data-linktype="external" src="/blog/media/2018/09/141.png"/>
</a>
</p>
<p>
</p>
<p>
最後に、登録したアプリケーションのアプリケーション ID を控えておきます。
</p>
<p>
<a href="azure-functions-での-app-service-認証-承認.html">
<img alt="" data-linktype="external" src="/blog/media/2018/09/6.png"/>
</a>
<br/>
<br/>
</p>
<h3 id="web-アプリapi-アプリケーション">
Web アプリ/API アプリケーション
</h3>
<p>
Web アプリ/API アプリケーションでは、サーバーでホストされるようなアプリケーションが想定されます。アプリケーション コードがユーザーからは機密となるため、トークンを取得するために、シークレット キーを使用して資格情報を提供することができます。
</p>
<p>
</p>
<p>
アプリケーションの種類を "Web アプリ/API" とします。サインオン URL は物理的に存在するものである必要はありませんが、アプリケーションに固有の値を設定しておきます。
</p>
<p>
<a href="azure-functions-での-app-service-認証-承認.html">
<img alt="" data-linktype="external" src="/blog/media/2018/09/9.png"/>
</a>
</p>
<p>
次に、ネイティブ アプリケーションと同様に、アクセス許可の設定を追加します。
</p>
<p>
<a href="azure-functions-での-app-service-認証-承認.html">
<img alt="" data-linktype="external" src="/blog/media/2018/09/151.png"/>
</a>
</p>
<p>
</p>
<p>
キーを生成します。"説明" と "有効期限" を設定後、[保存] をクリックすると、キー値が表示されます。
<br/>
キー値が表示されるのは保存後のみですので、このタイミングで必ずキー値を控えておきます。
</p>
<p>
<a href="azure-functions-での-app-service-認証-承認.html">
<img alt="" data-linktype="external" src="/blog/media/2018/09/112.png"/>
</a>
</p>
<p>
<br/>
最後に、登録したアプリケーションのアプリケーション ID を控えておきます。
</p>
<p>
<a href="azure-functions-での-app-service-認証-承認.html">
<img alt="" data-linktype="external" src="/blog/media/2018/09/10.png"/>
</a>
</p>
<p>
</p>
<p>
</p>
<p>
</p>
<p>
以上で Azure AD アプリケーションの登録は完了です。最後に、Azure AD テナントのプロパティからディレクトリ ID を控えておきます。
</p>
<p>
<a href="azure-functions-での-app-service-認証-承認.html">
<img alt="" data-linktype="external" src="/blog/media/2018/09/71.png"/>
</a>
</p>
<p>
</p>
<p>
</p>
<h2 id="クライアント-アプリケーションを実装する">
クライアント アプリケーションを実装する
</h2>
<p>
以下は、PowerShell スクリプトと C# アプリケーションで、 Function を実行する場合のサンプルです。
</p>
<p>
Azure AD からトークンを取得するために
<a data-linktype="external" href="https://www.nuget.org/packages/Microsoft.IdentityModel.Clients.ActiveDirectory">
ADAL.NET
</a>
を使用していますので、適宜ダウンロードしてください。
</p>
<p>
# 2018/09/19 コードを一部修正しました。
</p>
<h3 id="ネイティブ-アプリケーション-1">
ネイティブ アプリケーション
</h3>
<p>
[PowerShell]
<br/>
[powershell]
<br/>
### ADAL.NET をロード ###
<br/>
$adal = "C:\ADAL\Microsoft.IdentityModel.Clients.ActiveDirectory.dll"
<br/>
[System.Reflection.Assembly]::LoadFrom($adal)
</p>
<p>
### Azure AD テナントの設定 ###
<br/>
# Azure AD テナントのディレクトリ ID
<br/>
$adId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
</p>
<p>
# トークン発行元
<br/>
$authority = "https://login.microsoftonline.com/$adId"
</p>
<p>
### Function App で作成した AD アプリ ###
<br/>
# クライアント ID
<br/>
$resourceApplicationId = "11f15836-8e45-4fb9-b9f8-b66611b21f8b"
</p>
<p>
### クライアント用に作成した AD アプリ ###
<br/>
# アプリケーション ID
<br/>
$clientApplicationId = "dbcb7ba3-c667-4323-a310-a5e834b32675"
</p>
<p>
# リダイレクト URI
<br/>
$redirectUri = "https://jpciesample/MyNativeApp"
</p>
<p>
### トークン生成 ###
<br/>
# ADAL の AuthenticationContext オブジェクト
<br/>
$authContext = New-Object Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext -ArgumentList $authority
</p>
<p>
# トークン要求 (資格情報がキャッシュされていない場合、資格情報プロンプトが起動します)
<br/>
$platformParameters = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.PlatformParameters" -ArgumentList "Auto"
<br/>
$authResult = $authContext.AcquireTokenAsync($resourceApplicationId, $clientApplicationId, $redirectUri, $platformParameters)
</p>
<p>
# Bearer トークン
<br/>
$bearerToken = $authResult.Result.CreateAuthorizationHeader()
</p>
<p>
### Function の呼び出し###
<br/>
# Function の URL
<br/>
$functionUri = "https://jpcieauthtest.azurewebsites.net/api/HttpTriggerCSharp1?code=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
</p>
<p>
# 要求 Body
<br/>
$body="{'name': 'Azure'}"
</p>
<p>
# HTTP 要求ヘッダの Authorization ヘッダに Bearerトークンを設定
<br/>
$requestHeader = @{
<br/>
"Authorization" = $bearerToken
<br/>
}
</p>
<p>
# HTTPS のセキュリティ プロトコルを TLS 1.2 に設定
<br/>
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
</p>
<p>
# Function に HTTP 要求を送信
<br/>
try{
<br/>
$response = Invoke-RestMethod -Uri $functionUri -Method Post -Headers $requestHeader -Body $body -ContentType "application/json"
<br/>
echo $response
<br/>
}catch{
<br/>
Write-Error($_.Exception);
<br/>
}
<br/>
[/powershell]
</p>
<p>
[C#]
</p>
<p>
[csharp]
<br/>
/* Azure AD テナントの設定 */
<br/>
// Azure AD テナントのディレクトリ ID
<br/>
string adId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
</p>
<p>
// トークン発行元
<br/>
string authority = "https://login.microsoftonline.com/" + adId;
</p>
<p>
/* Function App で作成した AD アプリ */
<br/>
// クライアント ID
<br/>
string resourceApplicationId = "11f15836-8e45-4fb9-b9f8-b66611b21f8b";
</p>
<p>
/* クライアント用に作成した AD アプリ */
<br/>
// アプリケーション ID
<br/>
string clientApplicationId = "dbcb7ba3-c667-4323-a310-a5e834b32675";
</p>
<p>
// リダイレクト URI
<br/>
string redirectUri = "https://jpciesample/MyNativeApp";
</p>
<p>
/* トークン生成 */
<br/>
// ADAL の AuthenticationContext オブジェクト
<br/>
AuthenticationContext authContext = new AuthenticationContext(authority);
</p>
<p>
// トークン要求 (資格情報がキャッシュされていない場合、資格情報プロンプトが起動します)
<br/>
var authResult = authContext.AcquireTokenAsync(resourceApplicationId, clientApplicationId, new Uri(redirectUri), new PlatformParameters(PromptBehavior.Auto, null));
</p>
<p>
// Bearer トークン
<br/>
string bearerToken = authResult.Result.CreateAuthorizationHeader();
</p>
<p>
/* Function の呼び出し */
<br/>
// Function の URL
<br/>
string functionUri = "https://jpcieauthtest.azurewebsites.net/api/HttpTriggerCSharp1?code=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
</p>
<p>
// 要求 Body
<br/>
string bodydata = "{\"name\": \"Azure\"}";
</p>
<p>
// Function に HTTP 要求を送信
<br/>
HttpWebRequest request = WebRequest.Create(functionUri) as HttpWebRequest;
<br/>
request.Method = "POST";
<br/>
request.Headers["Authorization"] = bearerToken;
<br/>
request.ContentType = "application/json";
</p>
<p>
Stream reqstr = request.GetRequestStream();
<br/>
reqstr.Write(Encoding.ASCII.GetBytes(bodydata), 0, bodydata.Length);
<br/>
reqstr.Close();
</p>
<p>
HttpWebResponse response = request.GetResponse() as HttpWebResponse;
<br/>
Stream resstr = response.GetResponseStream();
<br/>
StreamReader sr = new StreamReader(resstr);
<br/>
string resresult = sr.ReadToEnd();
</p>
<p>
Console.WriteLine(resresult);
<br/>
[/csharp]
</p>
<p>
</p>
<p>
</p>
<h3 id="web-アプリapi-アプリケーション-1">
Web アプリ/API アプリケーション
</h3>
<p>
[PowerShell]
<br/>
[powershell]
<br/>
### ADAL.NET をロード ###
<br/>
$adal = "C:\ADAL\Microsoft.IdentityModel.Clients.ActiveDirectory.dll"
<br/>
[System.Reflection.Assembly]::LoadFrom($adal)
</p>
<p>
### Azure AD テナントの設定 ###
<br/>
# Azure AD テナントのディレクトリ ID
<br/>
$adId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
</p>
<p>
# トークン発行元
<br/>
$authority = "https://login.microsoftonline.com/$adId"
</p>
<p>
### Function App で作成した AD アプリ ###
<br/>
# クライアント ID
<br/>
$resourceApplicationId = "11f15836-8e45-4fb9-b9f8-b66611b21f8b"
</p>
<p>
### クライアント用に作成した AD アプリ ###
<br/>
# アプリケーション ID
<br/>
$clientApplicationId = "3cd3551e-fc68-4510-b236-64c207b353fb"
</p>
<p>
# リダイレクト URI
<br/>
$redirectUri = "https://jpciesample/MyNativeApp"
</p>
<p>
# シークレット キー
<br/>
$secretKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
</p>
<p>
### トークン生成 ###
<br/>
# ADAL の AuthenticationContext オブジェクト
<br/>
$authContext = New-Object Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext -ArgumentList $authority
</p>
<p>
# クライアント資格情報
<br/>
$clientCredential = New-Object -TypeName Microsoft.IdentityModel.Clients.ActiveDirectory.ClientCredential ($clientApplicationId, $secretKey)
</p>
<p>
# トークン要求
<br/>
$authResult = $authContext.AcquireTokenAsync($resourceApplicationId, $clientCredential)
</p>
<p>
# Bearer トークン
<br/>
$bearerToken = $authResult.Result.CreateAuthorizationHeader()
</p>
<p>
### Function の呼び出し###
<br/>
# Function の URL
<br/>
$functionUri = "https://jpcieauthtest.azurewebsites.net/api/HttpTriggerCSharp1?code=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
</p>
<p>
# 要求 Body
<br/>
$body="{'name': 'Azure'}"
</p>
<p>
# HTTP 要求ヘッダの Authorization ヘッダに Bearerトークンを設定
<br/>
$requestHeader = @{
<br/>
"Authorization" = $bearerToken
<br/>
}
</p>
<p>
# HTTPS のセキュリティ プロトコルを TLS 1.2 に設定
<br/>
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
</p>
<p>
# Function に HTTP 要求を送信
<br/>
try{
<br/>
$response = Invoke-RestMethod -Uri $functionUri -Method Post -Headers $requestHeader -Body $body -ContentType "application/json"
<br/>
echo $response
<br/>
}catch{
<br/>
Write-Error($_.Exception);
<br/>
}
<br/>
[/powershell]
</p>
<p>
[C#]
</p>
<p>
[csharp]
<br/>
/* Azure AD テナントの設定 */
<br/>
// Azure AD テナントのディレクトリ ID
<br/>
string adId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
</p>
<p>
// トークン発行元
<br/>
string authority = "https://login.microsoftonline.com/" + adId;
</p>
<p>
/* Function App で作成した AD アプリ */
<br/>
// クライアント ID
<br/>
string resourceApplicationId = "11f15836-8e45-4fb9-b9f8-b66611b21f8b";
</p>
<p>
/* クライアント用に作成した AD アプリ */
<br/>
// アプリケーション ID
<br/>
string clientApplicationId = "3cd3551e-fc68-4510-b236-64c207b353fb";
</p>
<p>
// シークレット キー
<br/>
string secretKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
</p>
<p>
/* トークン生成 */
<br/>
// ADAL の AuthenticationContext オブジェクト
<br/>
AuthenticationContext authContext = new AuthenticationContext(authority);
</p>
<p>
// クライアント資格情報
<br/>
var clientCredential = new ClientCredential(clientApplicationId, secretKey);
</p>
<p>
// トークン要求
<br/>
var authResult = authContext.AcquireTokenAsync(resourceApplicationId, clientCredential);
</p>
<p>
// Bearer トークン
<br/>
string bearerToken = authResult.Result.CreateAuthorizationHeader();
</p>
<p>
/* Function の呼び出し */
<br/>
// Function の URL
<br/>
string functionUri = "https://jpcieauthtest.azurewebsites.net/api/HttpTriggerCSharp1?code=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
</p>
<p>
// 要求 Body
<br/>
string bodydata = "{\"name\": \"Azure\"}";
</p>
<p>
// Function に HTTP 要求を送信
<br/>
HttpWebRequest request = WebRequest.Create(functionUri) as HttpWebRequest;
<br/>
request.Method = "POST";
<br/>
request.Headers["Authorization"] = bearerToken;
<br/>
request.ContentType = "application/json";
</p>
<p>
Stream reqstr = request.GetRequestStream();
<br/>
reqstr.Write(Encoding.ASCII.GetBytes(bodydata), 0, bodydata.Length);
<br/>
reqstr.Close();
</p>
<p>
HttpWebResponse response = request.GetResponse() as HttpWebResponse;
<br/>
Stream resstr = response.GetResponseStream();
<br/>
StreamReader sr = new StreamReader(resstr);
<br/>
string resresult = sr.ReadToEnd();
</p>
<p>
Console.WriteLine(resresult);
<br/>
[/csharp]
</p>
<hr/>
<hr/>
<p>
今回は、Azure Functions を例としてご案内しましたが、同じ方法で Azure Web Apps、Azure API Apps で公開する API に対して認証/承認アクセスを行うこともできます。
<br/>
実行する API をパブリック アクセスから制限したいような場合に、有効なソリューションとなりますので、ご参考ください。
</p>
<!-- </content> -->
</main>