Web アプリで AzureAD 認証を構成するには
Web アプリケーション
適切なトークンを持つリクエストに応じたアクセス制御を行う
適切なトークン情報を持たない場合 Azure AD の認可エンドポイントへ誘導する
その際に必要とするアプリケーション情報を指定する
トークンを返却してもらうための URI を指定する
Azure Active Directory
アクセスしているユーザーの正当性を確認する
登録されたアプリに対してユーザーの同意を取得する
トークンを発行してアプリケーションに返却する
9
10.
Azure Web Appユーザー認証の動作イメージ
Web App は Azure AD を信頼し、認証を委託する ”クライ
アント”アプリケーション
ユーザーは AAD で認証を受け、 Web App が自身の情報にアクセスすることに同意する
Web App は指定のディレクトリで発行された認可コードを持つリクエストを処理する
10
ApplicationEasy
Auth
Azure AD 認証の有効化
SQLDatabase の Active Directory 管理者を指定する
名前がややこしいが、 AD を管理する人ではなく、 SQL DB にアクセスする AAD ユー
ザーやアプリケーションを管理する人
18
19.
SQL DB のAD 管理者によるアクセス
AD 管理者は自身の AAD 認証情報を使用して SQL
Database にアクセスできる
SQL Server Management Studio や Azure Data Studio など、 Azure AD 認証に
対応したクライアントツールを使用
19
20.
AAD ユーザーの登録
AD 管理者は他のユーザーをSQL DB にユーザー登録、
データベースロールを割り当てることができる
この操作は SQL 認証で接続した管理者では実行できない
アクセスさせたいユーザーが多数の場合は AAD グループで登録すると良い
20
-- AAD ユーザーの登録
CREATE USER [sqluser1@tenantname.onmicrosoft.com] FROM EXTERNAL PROVIDER;
ALTER ROLE db_reader ADD MEMBER [sqluser1@tenantname.onmicrosoft.com];
-- AAD グループの登録
CREATE USER [sqlusers-group] FROM EXTERNAL PROVIDER;
ALTER ROLE db_owner ADD MEMBER [sqlusers-group];
T-SQL
AAD アプリによるアクセス
ユーザーが介在しない場合は AADにアプリケーションを登
録して SQL DB にアクセスする
常駐型デーモンアプリ、バッチアプリ、 Web アプリなど
接続文字列に埋め込まれた情報ではなく AAD 発行のアクセストークンを使用する
24
25.
AAD アプリによるアクセス
アクセストークンを取得する方法は以下の4パターン
① 新規に作成したサービスプリンシパルのキー(または証明書)を使用する
②AAD 認証設定時に生成されたサービスプリンシパルのキーを使用する
③ システム割り当てマネージド ID を使用する
④ ユーザー割り当てマネージド ID を使用する
トークンを取得すれば SqlConnection の作成は同じ
25
using Microsoft.Data.SqlClient;
private static SqlConnection GetAadAppTokenConnection()
{
var token = GetAccessToken(); // トークンの取得方法によって実装を変える
var constr = @"Server=tcp:sqlsvrName.database.windows.net,1433;Initial Catalog=dbName;";
var connection = new SqlConnection(constr);
connection.AccessToken = token;
return connection;
}
C#
実際には環境変数等の
外部に切り出すこと
26.
AAD アプリによるアクセス
前述の ①〜④で作成されるサービスプリンシパルに対して
SQL Database に対するアクセス権を付与する
A) データベースユーザーおよびロールに登録する
B) すでにデータベースユーザーおよびロールに登録された AAD グループに追加する
26
-- A) DB 管理者による AAD アプリの登録
CREATE USER [sqlapp_sp] FROM EXTERNAL PROVIDER;
ALTER ROLE db_reader ADD MEMBER [sqlapp_sp];
T-SQL
27.
① 新規サービスプリンシパルによるアクセス
Azure ADにアプリケーション登録権限を持った管理者が下
記の情報を準備
アプリケーション名
アプリケーション ID
ディレクトリ ID
クライアント シークレット
上記の情報をもとに、前述のように
A) SQL DB のユーザーを作成するか
B) AAD グループに追加しておく
27
https://docs.microsoft.com/ja-jp/azure/active-directory/develop/howto-create-service-principal-portal
28.
① 新規サービスプリンシパルによるアクセス
アプリケーションはキーや証明書を使用して SQLDatabase
に対するアクセストークンを取得することができる
Azure AD にアクセス可能な環境であれば、必ずしも Azure 上で動作させる必要はなく、
オンプレミスや他社クラウドでも良い
28
using Microsoft.Identity.Client;
private static string GetAccessTokenWithServicePrincipal()
{
var directoryid = "guid-of-your-azuread-directory-id";
var applicatonid = "guid-of-your-application-id";
var clientkey = "key-secret-generated";
var scopes = new[] { "https://database.windows.net/.default" };
var app = ConfidentialClientApplicationBuilder
.Create(applicationid)
.WithAuthority(new Uri($"https://login.microsoftonline.com/{directoryid}"))
.WithClientSecret(clientkey)
.Build();
var authresult = app.AcquireTokenForClient(scopes).ExecuteAsync().Result;
return authresult.AccessToken;
}
C#
https://www.nuget.org/packages/Microsoft.Identity.Client/
実際には環境変数等の外部に切り出すこと
Azure Web App なら構成情報に記載
29.
② 認証設定のサービスプリンシパルを流用
AAD 認証を設定したWeb App は既にAzure AD にアプリ
登録されている
29
Web App と同名のアプリケーションが
登録されているのでこの名前で
A) SQL DB のユーザーを作成するか
B) AAD グループに追加しておく
30.
② 認証設定のサービスプリンシパルを流用
Web Appの認証設定画面の情報は環境変数から取得でき
るように設定されている
ASP.NET Core アプリケーションであれば Environment.GetEnvironmantVariable()
や Configuration で取得できる
30
using Microsoft.Identity.Client;
private static string GetAccessTokenFromAppServiceEasyAuth ()
{
var clientid = _config["WEBSITE_AUTH_CLIENT_ID"];
var secret = _config["WEBSITE_AUTH_CLIENT_SECRET"];
var guidpattern = @"([A-Fa-f0-9]{8})¥-([A-Fa-f0-9]{4})¥-([A-Fa-f0-9]{4})¥-([A-Fa-f0-9]{4})¥-([A-Fa-f0-9]{12})";
var tenantid = Regex.Match(_config["WEBSITE_AUTH_OPENID_ISSUER"], guidpattern).Groups[0].Value;
var authority = new Uri($"https://login.microsoftonline.com/{tenantid}");
var scopes = new[] { "https://database.windows.net/.default" };
var app = ConfidentialClientApplicationBuilder
.Create(clientid).WithAuthority(authority).WithClientSecret(secret).Build();
var builder = app.AcquireTokenForClient(scopes);
var authResult = await builder.ExecuteAsync();
return authResult.AccessToken;
}
C#
③ システム割当マネージ IDを使用したアクセス
Web App にマネージド ID を割り当てておくと、そこで動作
するアプリがトークンを取得するエンドポイントが用意される
この場合も Web App と同じ名前のサービスプリンシパルが AAD に作成される
32
https://docs.microsoft.com/ja-jp/azure/app-service/overview-managed-identity?tabs=dotnet
Web App と同名のアプリケーションが
登録されているのでこの名前で
A) SQL DB のユーザーを作成するか
B) AAD グループに追加しておく
33.
③ システム割当マネージ IDを使用したアクセス
Web App にデプロイされたアプリは実行環境からマネージ
ド ID を使用してアクセストークンを取得することができる
アクセストークンの取得は Azure AD のトークンエンドポイントではなく、Web App 環境内に
用意されるエンドポイントを使用する
ASP.NET Core の場合は System.Net.Http や Json.NET などを使用する
33
using System.Net.Http;
using Newtonsoft.Json.Linq;
private static async Task<string> GetAccessTokenFromSystemAssignedManagedId()
{
var resourceuri = "https%3A%2F%2Fdatabase.windows.net%2F";
var msiurl = string.Format("{0}?resource={1}&api-version=2017-09-01", _config["MSI_ENDPOINT"], resourceuri);
string token = null;
using (var hc = new HttpClient())
{
hc.DefaultRequestHeaders.Add("Secret", _config["MSI_SECRET"]);
var ret = await hc.GetStringAsync(msiurl);
token = JObject.Parse(ret)["access_token"].ToString();
}
return token;
}
C#
マネージ ID 用のトークンエンドポイント URL が設定され
ている環境変数を使用して下記 URL を組み立てている
http://127.0.0.1:41631/MSI/token/
?resource=http://database.windows.net
&api-version=2017-09-01
34.
③ システム割当マネージ IDを使用したアクセス
マネージド ID を使用してアクセストークンを取得する専用の
ライブラリが提供されている場合はそちらを使うと良い
ASP.NET Core の場合は Microsoft.Azure.Services.AppAuthentication が利用で
きる
34
using Microsoft.Azure.Services.AppAuthentication;
private static async Task<string> GetAccessTokenFromSystemAssignedManagedId()
{
var provider = new AzureServiceTokenProvider();
var token = await provider.GetAccessTokenAsync("https://database.windows.net/");
return token;
}
C#
アクセストークンを取得した
い対象リソースの URI だけ
指定すれば良い
35.
④ ユーザ割当マネージ IDを利用したアクセス
ユーザー割り当てマネージ ID は柔軟な構成が可能
1つのマネージ ID を複数の WebApp や VM に割り当てて共用する
1つの Web App に対して複数のマネージ ID を割り当てアクセス先に応じて使い分けるこ
とができる
35
対象となる Web App に割り当てる
マネージ ID と同名のサービスプリンシ
パルが登録されているのでこの名前で
A) SQL DB のユーザーを作成するか
B) AAD グループに追加しておく
36.
④ ユーザ割当マネージ IDを利用したアクセス
アクセストークンの取得クライアント ID を指定する以外システ
ム割当の場合とほぼ同じ
36
using System.Net.Http;
using Newtonsoft.Json.Linq;
private static async Task<string> GetAccessTokenFromSystemAssignedManagedId()
{
string token = null;
using (var hc = new HttpClient())
{
var resourceuri = "https%3A%2F%2Fdatabase.windows.net%2F";
var msiurl = string.Format("{0}?resource={1}&api-version=2017-09-01&clientid={2}"
, _config["MSI_ENDPOINT"], resourceuri, "guid-of-clientid-for-uamid");
hc.DefaultRequestHeaders.Add("Secret", _config["MSI_SECRET"]);
var ret = await hc.GetStringAsync(msiurl);
token = JObject.Parse(ret)["access_token"].ToString();
}
return token;
}
C#
37.
Tips : AADユーザーの確認
SQL Database に登録した AAD ユーザーやグループおよ
びデータベースロールは下記のように確認できる
37
SELECT
DP1.name AS DatabaseRoleName,
isnull (DP2.name, 'No members') AS DatabaseUserName ,
DP2.type_desc as PrincipalType,
DP2.sid
FROM sys.database_role_members AS DRM
RIGHT OUTER JOIN sys.database_principals AS DP1
ON DRM.role_principal_id = DP1.principal_id
LEFT OUTER JOIN sys.database_principals AS DP2
ON DRM.member_principal_id = DP2.principal_id
WHERE DP1.type = 'R'
ORDER BY DP1.name;
T-SQL
38.
Tips:現在の SQL セッションのユーザー
SQLDatabase に接続できたら現在どのユーザーで接続
しているのかを確認してみると良い
38
using Microsoft.Data.SqlClient;
private static async Task OnGet()
{
var connection = GetAadAppTokenConnection();
try
{
await connection.OpenAsync();
using ( var cmd = connection.CreateCommand() )
{
cmd.CommandText = "select suser_name()";
var dbuser = cmd.ExecuteScalar().ToString();
}
}
finally
{
connection.Close();
}
}
C#
39.
Tips : アプリ名の重複
WebApp の以下の操作で ”同名のサービスプリンシパル”
が作成され、SQL DB への登録時に区別がつかない
AAD ユーザー認証を構成する
システム割当マネージ ID を有効にする
39
ユーザー認証(簡易モード)で作成された
サービスプリンシパル
マネージ ID で作成されたサービスプリンシパル
-- A) DB 管理者による AAD アプリの登録
CREATE USER [ainaba-aadauth-web]
FROM EXTERNAL PROVIDER;
T-SQL
Tips : アプリ名の重複
SQLDB への登録時に SID を使用してアプリケーションを
明示的に指定することができる
41
## アプリケーション ID から SID を生成
PS> $b = [Guid]::Parse("eb49934f-…").ToByteArray()
PS> "0x" + (($b | % {$_.ToString("X2")}) –join "")
0x4F9349EB85BD…
PowerShell
-- sid を指定することで AAD アプリとは別名をつける
CREATE USER [ainaba-aadauth-web-mi2]
with sid = 0x4F9349EB85BD…, type = E;
-- 別名のユーザーをデータベースロールに追加
ALTER ROLE db_owner ADD MEMBER [ainaba-aadauth-web-mi2];
T-SQL
42.
補足: VM でアプリが動作する場合
アプリがAzure VM 上で動作する場合も同様の実装が可能
だがいくつか差異が発生する
AAD ユーザー認証を自動構成してくれる簡易機能は提供されない
> 対応する Web アプリの認証するライブラリを利用して自力で実装する
サービスプリンシパルのキー情報などは環境変数に自動設定されない
> 自力で環境変数に設定するか、構成設定ファイルやレジストリで管理する
マネージ ID によるトークンエンドポイントが異なる
> URL が異なる程度だがコードの流用時に注意
システム割当マネージ ID は VM インスタンスごとに異なるオブジェクトになる
> ユーザー割当マネージ ID や AAD グループによる管理を検討する
詳細は下記の Blog などを参照
https://ayuina.github.io/ainaba-csa-blog/sqldb-aad-authentication/
42
アプリ側でアクセストークンを取得する
こちらの手法であれば任意のタイミングに任意のリソースに対
してアクセストークンを取得できる
48
private static asyncTask<string> GetAccessTokenAsUser()
{
var clientid = _config["WEBSITE_AUTH_CLIENT_ID"];
var secret = _config["WEBSITE_AUTH_CLIENT_SECRET"];
var guidpattern = @"([A-Fa-f0-9]{8})¥-([A-Fa-f0-9]{4})¥-([A-Fa-f0-9]{4})¥-([A-Fa-f0-9]{4})¥-([A-Fa-f0-9]{12})";
var tenantid = Regex.Match(_config["WEBSITE_AUTH_OPENID_ISSUER"], guidpattern).Groups[0].Value;
var authority = new Uri($"https://login.microsoftonline.com/{tenantid}");
var scopes = new[] { "https://database.windows.net/.default" };
var app = ConfidentialClientApplicationBuilder
.Create(clientid).WithAuthority(authority).WithClientSecret(secret).Build() as IByRefreshToken;
var authcode = Request.Headers["X-MS-TOKEN-AAD-REFRESH-TOKEN"][0];
var builder = app.AcquireTokenByRefreshToken(scopes, authcode);
var result = await builder.ExecuteAsync();
return result.AccessToken;
}
ARM