web-dev-qa-db-ja.com

Azure Active Directory B2Cのグループごとに承認する

Azure Active Directory B2Cでグループを使用して認証する方法を見つけようとしています。たとえば、ユーザー経由で認証できます。

[Authorize(Users="Bill")]

ただし、これはあまり効果的ではなく、この使用例はほとんどありません。別の解決策は、ロールによる認証です。しかし、何らかの理由で、それは動揺しないようです。たとえば、ユーザーにロール「Global Admin」を与えて、次のことを試してみてください:

[Authorize(Roles="Global Admin")]

それは動作しません。グループまたはロールを介して認証する方法はありますか?

25
Green_qaue

これは機能しますが、探しているものを実現するために、認証ロジックに数行のコードを記述します。

まず、Azure AD(B2C)でRolesGroupsを区別する必要があります。

User Roleは非常に具体的であり、Azure AD(B2C)自体内でのみ有効です。ロールは、ユーザーが持つ権限 Azure AD 内の権限を定義します。

Group(またはSecurity Group)は、外部アプリケーションに公開できるユーザーグループメンバーシップを定義します。外部アプリケーションは、セキュリティグループの上にロールベースのアクセス制御をモデル化できます。はい、少し混乱するかもしれませんが、それはそうです。

したがって、最初のステップは、Azure AD B2CでGroupsをモデル化することです。グループを作成し、それらのグループにユーザーを手動で割り当てる必要があります。 Azureポータルでそれを行うことができます( https://portal.Azure.com/ ):

enter image description here

次に、アプリケーションに戻り、少しコーディングして、ユーザーが正常に認証されたら、ユーザーメンバーシップの Azure AD B2C Graph API を要求する必要があります。 このサンプル を使用して、ユーザーのグループメンバーシップを取得する方法に触発されます。 OpenID通知のいずれか(つまり SecurityTokenValidated )でこのコードを実行し、ユーザーロールをClaimsPrincipalに追加するのが最適です。

ClaimsPrincipalを変更してAzure ADセキュリティグループと「Role Claim」値を設定すると、ロール機能で認証属性を使用できるようになります。これは実際には5〜6行のコードです。

最後に、ここで機能に投票できます。 https://feedback.Azure.com/forums/169401-Azure-active-directory/suggestions/10123836-get-user-membership-groups-in- the-claims-with-ad-b は、Graph APIにクエリすることなくグループメンバーシップの要求を取得します。

18
astaykov

Azure ADからユーザーのグループメンバーシップを取得するには、「2、3行のコード」よりもかなり多くのことが必要なので、最終的には他の人を数日分の髪の毛を引っ張って頭をかかげるのに役立つものを共有したいと思いましたバンギング。

Project.jsonに次の依存関係を追加することから始めましょう。

_"dependencies": {
    ...
    "Microsoft.IdentityModel.Clients.ActiveDirectory": "3.13.8",
    "Microsoft.Azure.ActiveDirectory.GraphClient": "2.0.2"
}
_

AAD Graph APIにアクセスできるようにするには、アプリケーションを認証する必要があるため、最初のものが必要です。 2つ目は、ユーザーメンバーシップのクエリに使用するGraph APIクライアントライブラリです。言うまでもなく、バージョンはこのドキュメントの執筆時点でのみ有効であり、将来変更される可能性があります。

次に、スタートアップクラスのConfigure()メソッドで、おそらくOpenID Connect認証を構成する直前に、次のようにGraph APIクライアントを作成します。

_var authContext = new AuthenticationContext("https://login.microsoftonline.com/<your_directory_name>.onmicrosoft.com");
var clientCredential = new ClientCredential("<your_b2c_app_id>", "<your_b2c_secret_app_key>");
const string AAD_GRAPH_URI = "https://graph.windows.net";
var graphUri = new Uri(AAD_GRAPH_URI);
var serviceRoot = new Uri(graphUri, "<your_directory_name>.onmicrosoft.com");
this.aadClient = new ActiveDirectoryClient(serviceRoot, async () => await AcquireGraphAPIAccessToken(AAD_GRAPH_URI, authContext, clientCredential));
_

警告:秘密のアプリキーをハードコーディングせずに、安全な場所に保管してください。まあ、あなたはすでにそれを知っていましたよね? :)

ADクライアントコンストラクターに渡した非同期AcquireGraphAPIAccessToken()メソッドは、クライアントが認証トークンを取得する必要があるときに必要に応じて呼び出されます。メソッドは次のようになります。

_private async Task<string> AcquireGraphAPIAccessToken(string graphAPIUrl, AuthenticationContext authContext, ClientCredential clientCredential)
{
    AuthenticationResult result = null;
    var retryCount = 0;
    var retry = false;

    do
    {
        retry = false;
        try
        {
            // ADAL includes an in-memory cache, so this will only send a request if the cached token has expired
            result = await authContext.AcquireTokenAsync(graphAPIUrl, clientCredential);
        }
        catch (AdalException ex)
        {
            if (ex.ErrorCode == "temporarily_unavailable")
            {
                retry = true;
                retryCount++;
                await Task.Delay(3000);
            }
        }
    } while (retry && (retryCount < 3));

    if (result != null)
    {
        return result.AccessToken;
    }

    return null;
}
_

一時的な状態を処理するための再試行メカニズムが組み込まれていることに注意してください。アプリケーションのニーズに合わせて調整することができます。

アプリケーション認証とADクライアントのセットアップを処理したので、先に進み、OpenIdConnectイベントを利用して最終的にそれを利用できます。通常app.UseOpenIdConnectAuthentication()を呼び出してOpenIdConnectOptionsのインスタンスを作成するConfigure()メソッドに戻り、OnTokenValidatedイベントのイベントハンドラーを追加します。

_new OpenIdConnectOptions()
{
    ...         
    Events = new OpenIdConnectEvents()
    {
        ...
        OnTokenValidated = SecurityTokenValidated
    },
};
_

サインインしているユーザーのアクセストークンが取得、検証され、ユーザーIDが確立されると、イベントが発生します。 (AAD Graph APIを呼び出すために必要なアプリケーション独自のアクセストークンと混同しないでください!)ユーザーのグループメンバーシップについてGraph APIを照会し、追加のクレームの形式でIDにそれらのグループを追加するのに適した場所のようです:

_private Task SecurityTokenValidated(TokenValidatedContext context)
{
    return Task.Run(async () =>
    {
        var oidClaim = context.SecurityToken.Claims.FirstOrDefault(c => c.Type == "oid");
        if (!string.IsNullOrWhiteSpace(oidClaim?.Value))
        {
            var pagedCollection = await this.aadClient.Users.GetByObjectId(oidClaim.Value).MemberOf.ExecuteAsync();

            do
            {
                var directoryObjects = pagedCollection.CurrentPage.ToList();
                foreach (var directoryObject in directoryObjects)
                {
                    var group = directoryObject as Group;
                    if (group != null)
                    {
                        ((ClaimsIdentity)context.Ticket.Principal.Identity).AddClaim(new Claim(ClaimTypes.Role, group.DisplayName, ClaimValueTypes.String));
                    }
                }
                pagedCollection = pagedCollection.MorePagesAvailable ? await pagedCollection.GetNextPageAsync() : null;
            }
            while (pagedCollection != null);
        }
    });
}
_

ここでは、Roleクレームタイプを使用していますが、カスタムタイプを使用することもできます。

上記を実行した後、ClaimType.Roleを使用している場合、コントローラークラスまたはメソッドを次のように装飾するだけで済みます。

_[Authorize(Role = "Administrators")]
_

つまり、B2Cで「Administrators」という表示名で設定された指定グループがある場合はもちろんです。

ただし、カスタムクレームタイプの使用を選択した場合は、ConfigureServices()メソッドに次のようなものを追加して、クレームタイプに基づいて承認ポリシーを定義する必要があります。

_services.AddAuthorization(options => options.AddPolicy("ADMIN_ONLY", policy => policy.RequireClaim("<your_custom_claim_type>", "Administrators")));
_

次に、次のように特権コントローラークラスまたはメソッドを修飾します。

_[Authorize(Policy = "ADMIN_ONLY")]
_

わかった、もう終わった? -まあ、正確ではありません。

アプリケーションを実行してサインインしようとした場合、「操作を完了するための権限が不十分です」と主張するGraph APIから例外が発生します。明らかではないかもしれませんが、アプリケーションはapp_idとapp_keyを使用してADで正常に認証されますが、ADからユーザーの詳細を読み取るために必要な特権がありません。アプリケーションにこのようなアクセスを許可するために、 PowerShell用のAzure Active Directoryモジュール

次のスクリプトは私のためにトリックをしました:

_$tenantGuid = "<your_tenant_GUID>"
$appID = "<your_app_id>"

$userVal = "<admin_user>@<your_AD>.onmicrosoft.com"
$pass = "<admin password in clear text>"
$Creds = New-Object System.Management.Automation.PsCredential($userVal, (ConvertTo-SecureString $pass -AsPlainText -Force))

Connect-MSOLSERVICE -Credential $Creds
$msSP = Get-MsolServicePrincipal -AppPrincipalId $appID -TenantID $tenantGuid

$objectId = $msSP.ObjectId

Add-MsolRoleMember -RoleName "Company Administrator" -RoleMemberType ServicePrincipal -RoleMemberObjectId $objectId
_

そして、ようやく完了です! 「数行のコード」はどうですか? :)

42
Alex Lobakov

私はこれを書かれているように実装しましたが、2017年5月の時点で

((ClaimsIdentity)context.Ticket.Principal.Identity).AddClaim(new Claim(ClaimTypes.Role, group.DisplayName, ClaimValueTypes.String));

に変更する必要があります

((ClaimsIdentity)context.Ticket.Principal.Identity).AddClaim(new Claim(ClaimTypes.Role, group.DisplayName));

最新のライブラリで動作させるには

著者への素晴らしい仕事

また、Connect-MsolServiceに問題があり、ユーザー名とパスワードが最新のlibに正しく更新されない場合

5
user1197563