Autoriser par groupe dans Azure Active Directory B2C

j'essaie de comprendre comment autoriser l'utilisation de groupes dans Azure Active Directory B2C. Je peux autoriser via User par exemple:

[Authorize(Users="Bill")]

cependant ce n'est pas très efficace et je vois très peu de cas d'utilisation pour cela. Une autre solution consisterait à autoriser via Role. Cependant, pour une raison qui ne semble pas wowrk. Si je donne à un utilisateur le rôle "Global Admin" par exemple, et que j'essaie:

[Authorize(Roles="Global Admin")]

ça ne marche pas. Est-il possible d'autoriser via des Groupes ou Les rôles?

19
demandé sur Saca 2016-10-28 12:35:29

3 réponses

Cela va fonctionner, cependant, vous devez écrire quelques lignes de code dans votre logique d'authentification afin d'obtenir ce que vous recherchez.

tout d'Abord, il faut différencier entre Roles et Groups in Azure AD (B2C).

User Role est très spécifique et n'est valable que dans L'Azure AD (B2C) elle-même. Le rôle définit quelles permissions à l'intérieur d'Azure AD l'utilisateur.

Group (ou Security Group) définit l'appartenance à un groupe d'utilisateurs, peut être exposé aux applications externes. Les applications externes peuvent modéliser contrôle d'accès sur le dessus de Groupes De Sécurité. Oui, je sais que ça peut sembler un peu confus, mais c'est ce que c'est.

Donc, votre première étape consiste à modéliser votre Groups dans Azure AD B2C - vous devez créer les groupes et assigner manuellement les utilisateurs à ces groupes. Vous pouvez le faire dans le portail D'Azur (https://portal.azure.com/):

enter image description here

puis, retour à votre application, vous devrez coder un peu et demander le Azure AD B2C Graph API pour les adhésions utilisateurs une fois que l'utilisateur est authentifié avec succès. Vous pouvez utiliser cet exemple pour s'inspirer sur la façon d'obtenir des adhésions de groupes d'utilisateurs. Il est préférable d'exécuter ce code dans l'une des Notifications OpenID (i.e. SecurityTokenValidated) et ajoute le rôle d'utilisateur Auprincipal claims.

une fois que vous aurez modifié le principe des claims pour avoir des groupes de sécurité et des valeurs de "Role Claim" azurés, vous pourrez utiliser L'attribut Authrize avec la fonctionnalité Roles. C'est vraiment 5-6 lignes de code.

Enfin, vous pouvez donner votre vote pour la fonctionnalité ici: https://feedback.azure.com/forums/169401-azure-active-directory/suggestions/10123836-get-user-membership-groups-in-the-claims-with-ad-b afin d'obtenir la revendication d'appartenance à un groupe sans avoir à interroger L'API Graph pour cela.

15
répondu astaykov 2016-10-28 10:29:40

obtenir des adhésions de groupe pour un utilisateur de Azure AD nécessite un peu plus que juste "quelques lignes de code", donc j'ai pensé que je partagerais ce qui a finalement fonctionné pour moi pour sauver d'autres quelques jours de la valeur de cheveux-tirant et la tête-banging.

commençons par ajouter les dépendances suivantes au projet.json:

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

la première est nécessaire car nous avons besoin d'authentifier notre application pour qu'elle puisse accéder à L'API AAD Graph. Le second est le Bibliothèque de clients de l'API de graphes que nous utiliserons pour interroger les adhésions des utilisateurs. Il va sans dire que les versions ne sont valables qu'à la rédaction de ce document et peuvent changer dans le futur.

ensuite, dans la méthode Configure () de la classe de démarrage, peut-être juste avant de configurer L'authentification OpenID Connect, nous créons le client D'API Graph comme suit:

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));

attention: ne codez pas votre clé d'application secrète, mais conservez-la dans un endroit sûr. Eh bien, vous avez déjà savait que, droite? :)

la méthode asynchrone AcquireGraphAPIAccessToken () que nous avons remise au constructeur du client AD sera appelée si nécessaire lorsque le client aura besoin d'obtenir un token d'authentification. Voici ce que la méthode ressemble à ceci:

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;
}

notez qu'il dispose d'un mécanisme de Ré-essai intégré pour gérer les conditions transitoires, que vous pouvez adapter aux besoins de votre application.

maintenant que nous avons pris soin de l'authentification des applications et la configuration du client publicitaire, nous pouvons aller de l'avant et puiser dans les événements OpenIdConnect pour finalement en faire usage. De retour dans la méthode Configure () où nous appellerions typiquement app.UseOpenIdConnectAuthentication() et créer une instance de OpenIdConnectOptions, nous ajoutons un gestionnaire d'événements pour l'événement Ontokenvalided:

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

l'événement est déclenché lorsque le token d'accès pour l'utilisateur de connexion a été obtenu, validé et que l'identité de l'Utilisateur a été établie. (À ne pas confondre avec le jeton d'accès propre à l'application nécessaire pour appeler AAD L'API graphique!) Il semble être un bon endroit pour interroger L'API Graph pour les adhésions de groupes d'utilisateurs et ajouter ces groupes sur l'identité, sous la forme de revendications supplémentaires:

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);
        }
    });
}

utilisé ici est le type de revendication de rôle, cependant vous pouvez utiliser un personnalisé.

après avoir fait ce qui précède, si vous utilisez ClaimType.Rôle, tout ce que vous devez faire est de décorer votre contrôleur de classe ou de la méthode comme suit:

[Authorize(Role = "Administrators")]

C'est, bien sûr, à condition que vous avez désigné groupe configuré en B2C avec un nom d'affichage de "Administrateurs".

si, cependant, vous avez choisi d'utiliser un type de revendication personnalisé, vous devez définir une politique d'autorisation basée sur le type de revendication en ajoutant quelque chose comme ceci dans la méthode ConfigureServices (), par exemple:

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

et ensuite décorer une classe ou une méthode de controller privilégiée comme suit:

[Authorize(Policy = "ADMIN_ONLY")]

Ok, on a fini? - Eh bien, pas exactement.

si vous avez lancé votre application et essayez de vous connecter, vous obtiendriez une exception de L'API de graphique affirmant "privilèges insuffisants pour terminer l'opération". Ce n'est peut-être pas évident, mais bien que votre application s'authentifie avec succès avec AD en utilisant son app_id et app_key, elle n'a pas les privilèges nécessaires pour lire les détails des utilisateurs de votre annonce. Afin d'accorder un tel accès, j'ai choisi d'utiliser le Azure Active Directory Module for PowerShell

le script suivant a fait l'affaire pour moi:

$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

et maintenant c'est fini! Qu'est-ce que ça veut dire "quelques lignes de code"? :)

34
répondu Alex Lobakov 2017-01-02 00:20:12

j'ai imploré ceci comme écrit, mais à partir de mai 2017 la ligne

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

doit être remplacé par

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

Pour le faire fonctionner avec la dernière libs

Excellent travail de l'auteur

aussi si vous avez un problème avec Connect-MsolService donnant un mauvais nom d'utilisateur et une mise à jour de mot de passe à la dernière lib

4
répondu user1197563 2017-05-11 19:49:54