13 votes

Comment ajouter des rôles aux demandes dans IdentityServer4 ?

Je suis nouveau sur IdentityServer et j'ai été confronté à ce problème toute la journée. À tel point que je suis presque sur le point d'abandonner. Je sais que cette question a été posée maintes et maintes fois et j'ai essayé de nombreuses solutions différentes, mais aucune ne semble fonctionner. J'espère que vous pourrez m'aider à me mettre sur la bonne voie.

J'ai d'abord installé les modèles d'IdentityServer4 en exécutant dotnet new -i identityserver4.templates et créé un nouveau projet avec le modèle is4aspid en exécutant dotnet new is4aspid -o IdentityServer .

Après cela, j'ai créé une nouvelle base de données IdentityServer et j'ai lancé les migrations. À ce moment-là, j'avais une structure de base de données d'identité par défaut.

Dans Config.cs, j'ai modifié MVC client à ce qui suit :

new Client
{
    ClientId = "mvc",
    ClientName = "MVC Client",

    AllowedGrantTypes = GrantTypes.Implicit,
    ClientSecrets = { new Secret("47C2A9E1-6A76-3A19-F3C0-S37763QB36D9".Sha256()) },

    RedirectUris = { "https://localhost:44307/signin-oidc" },
    FrontChannelLogoutUri = "https://localhost:44307/signout-oidc",
    PostLogoutRedirectUris = { "https://localhost:44307/signout-callback-oidc" },

    AllowOfflineAccess = true,
    AllowedScopes = { "openid", "profile", "api1", JwtClaimTypes.Role }                
},

Et a changé le GetApis à cette méthode :

public static IEnumerable<ApiResource> GetApis()
{
    return new ApiResource[]
    {
        new ApiResource("api1", "My API #1", new List<string>() { "role" })
    };
}

Bien entendu, il n'y avait pas encore d'utilisateurs dans la base de données. J'ai donc ajouté un formulaire d'inscription et enregistré deux utilisateurs fictifs, l'un avec le nom d'utilisateur suivant admin@example.com et un autre avec le nom d'utilisateur subscriber@example.com .

Pour attribuer les rôles à ces utilisateurs, j'ai créé la méthode suivante dans Startup.cs.

private async Task CreateUserRoles(IServiceProvider serviceProvider) {
    var RoleManager = serviceProvider.GetRequiredService<RoleManager<IdentityRole>>();
    var UserManager = serviceProvider.GetRequiredService<UserManager<ApplicationUser>>();

    IdentityResult adminRoleResult;
    IdentityResult subscriberRoleResult;

    bool adminRoleExists = await RoleManager.RoleExistsAsync("Admin");
    bool subscriberRoleExists = await RoleManager.RoleExistsAsync("Subscriber");

    if (!adminRoleExists) {
        adminRoleResult = await RoleManager.CreateAsync(new IdentityRole("Admin"));
    }

    if(!subscriberRoleExists) {
        subscriberRoleResult = await RoleManager.CreateAsync(new IdentityRole("Subscriber"));
    }

    ApplicationUser userToMakeAdmin = await UserManager.FindByNameAsync("admin@example.com");
    await UserManager.AddToRoleAsync(userToMakeAdmin, "Admin");

    ApplicationUser userToMakeSubscriber = await UserManager.FindByNameAsync("subscriber@example.com");
    await UserManager.AddToRoleAsync(userToMakeSubscriber, "Subscriber");
}

Dans le Configure de la même classe, j'ajoute le paramètre IServiceProvider services et appelé la méthode ci-dessus comme suit : CreateUserRoles(services).Wait(); . À ce moment-là, ma base de données contenait deux rôles.

Ensuite, j'ai créé une nouvelle solution (dans le même projet) et, dans le fichier Startup.cs de cette solution, j'ai ajouté ce qui suit dans la balise ConfigureServices méthode.

    JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

    services.AddAuthentication(options =>
    {
        options.DefaultScheme = "Cookies";
        options.DefaultChallengeScheme = "oidc";
    })
        .AddCookie("Cookies")
        .AddOpenIdConnect("oidc", options => {
            options.SaveTokens = true;
            options.ClientId = "mvc";
            options.ClientSecret = "32D7A7W0-0ALN-2Q44-A1H4-A37990NN83BP";
            options.RequireHttpsMetadata = false;
            options.Authority = "http://localhost:5000/";
            options.ClaimActions.MapJsonKey("role", "role");
        });

Après cela, j'ai ajouté app.UseAuthentication(); dans le Configure de la même classe.

J'ai ensuite créé une nouvelle page avec les instructions if suivantes.

if(User.Identity.IsAuthenticated) {
 <div>Yes, user is authenticated</div>
} 

if(User.IsInRole("ADMIN")) {
 <div>Yes, user is admin</div>
}

Je me suis connecté avec admin@example.com mais la deuxième instruction if renvoie False . J'ai inspecté toutes les revendications en les passant en boucle comme ceci.

@foreach (var claim in User.Claims) {
    <dt>@claim.Type</dt>
    <dd>@claim.Value</dd>
}

Mais il n'y avait aucune revendication de rôle à trouver, seulement sid, sub, idp, preferred_username et name.

J'ai essayé d'y introduire le rôle de manière à ce que la deuxième instruction if renvoie True, mais après de multiples essais, je n'ai pas encore réussi à le faire fonctionner. Quelqu'un peut-il voir ce que je dois faire pour que cela fonctionne ? Je suis un débutant absolu dans IdentityServer4 et je fais de mon mieux pour le comprendre. Toute aide sera appréciée. Merci d'avance !

EDIT 1 :

Merci à cette question y cette question J'ai l'impression d'être sur la bonne voie. J'ai fait quelques modifications mais je n'arrive toujours pas à le faire fonctionner. Je viens d'essayer ce qui suit.

  1. J'ai créé une nouvelle classe ProfileService dans mon projet IdentityServer avec le contenu suivant.

    public class MyProfileService : IProfileService { public MyProfileService() { } public Task GetProfileDataAsync(ProfileDataRequestContext context) { var roleClaims = context.Subject.FindAll(JwtClaimTypes.Role); List<string> list = context.RequestedClaimTypes.ToList(); context.IssuedClaims.AddRange(roleClaims); return Task.CompletedTask; }

    public Task IsActiveAsync(IsActiveContext context) { return Task.CompletedTask; } }

Ensuite, j'ai enregistré cette classe dans la méthode ConfigureServices en ajoutant la ligne services.AddTransient<IProfileService, MyProfileService>(); . Après cela, j'ai ajouté une nouvelle ligne à la méthode GetIdentityResources, qui ressemble maintenant à ceci.

public static IEnumerable<IdentityResource> GetIdentityResources()
{
    return new IdentityResource[]
    {
        new IdentityResources.OpenId(),
        new IdentityResources.Profile(),
        new IdentityResource("roles", new[] { "role" })
};
}

J'ai également ajouté les rôles à mon client Mvc comme suit : AllowedScopes = { "openid", "profile", "api1", "roles" } .

Ensuite, je suis passé à l'autre projet et j'ai ajouté les lignes suivantes dans l'oidc .AddOpenIdConnect.

options.ClaimActions.MapJsonKey("role", "role", "role");
options.TokenValidationParameters.RoleClaimType = "role";

Mais je n'arrive toujours pas à le faire fonctionner comme je le voudrais. Quelqu'un sait-il ce qui me manque ?

7voto

Feras Taleb Points 283

Il y a deux choses que vous devez faire pour vous assurer que vous obtiendrez les rôles des utilisateurs dans les réclamations :

1- Dans le projet IdentityServer4 : vous devez avoir une implémentation de IProfileService. http://docs.identityserver.io/en/latest/reference/profileservice.html

n'oubliez pas d'ajouter la classe dans le fichier startup.cs comme ceci

services.AddIdentityServer()
// I just removed some other configurations for clarity
                **.AddProfileService<IdentityProfileService>();**

2- Dans le fichier startup.cs du projet Web Client : lors de la configuration de l'openId, vous devez mentionner ceci :

services.AddAuthentication(options =>
        {
            options.DefaultScheme = "Cookies";
            options.DefaultChallengeScheme = "oidc";
        })
        .AddCookie("Cookies")
        .AddOpenIdConnect("oidc", options =>
        {
            options.SignInScheme = "Cookies";
            options.Authority = "Identity URL ";
            options.RequireHttpsMetadata = true;

            options.ClientId = "saas_crm_webclient";
            options.ClientSecret = "49C1A7E1-0C79-4A89-A3D6-A37998FB86B0";
            options.ResponseType = "code id_token";
            options.SaveTokens = true;
            options.GetClaimsFromUserInfoEndpoint = false;

            options.Scope.Add("test.api");
            options.Scope.Add("identity.api");
            options.Scope.Add("offline_access");

            **options.ClaimActions.Add(new JsonKeyClaimAction("role", null, "role"));**

            **options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
            {
                NameClaimType = "name",
                RoleClaimType = "role"
            };**
        });

5voto

d_f Points 490

Question légèrement différente, réponse absolument identique.

Avec le Edit 1 La configuration de l'IdP semble suffisante pour fournir les deux identité y accès jetons avec rôles sur demande . La seule chose qui reste à faire est de configurer le client pour qu'il demande le jeton d'accès (le client .Net ne le fait pas par défaut), ou de demander simplement le fichier roles dans le jeton d'identité.

Pour obtenir les rôles avec id_token la configuration côté client doit inclure options.Scope.Add("roles");

Pour obtenir les rôles avec le jeton du porteur, ce jeton doit être demandé en spécifiant options.ResponseType = "id_token token"; dans la configuration côté client.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X