web-dev-qa-db-fra.com

Impossible de récupérer les revendications dans .NET Core 2.0

J'utilise un serveur d'authentification OpenId Connect, en particulier Identity Server 4 (version 1.5.2) sur .NET Core 1.1. Je l’utilise avec ASP.NET Framework MVC 5 et les applications Web ASP.NET Core 1.1 MVC. La configuration suivante provient d'une application Web .NET Core 1.1:

public void Configure(
    IApplicationBuilder app,
    IHostingEnvironment env,
    ILoggerFactory loggerFactory)
{
    app.UseDeveloperExceptionPage();
    app.UseStatusCodePages();
    app.UseRewriter(new RewriteOptions().AddRedirectToHttps());
    app.UseStaticFiles();

    #region Configure Authentication
    JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

    app.UseCookieAuthentication(
        new CookieAuthenticationOptions
        {
            AuthenticationScheme = "Cookies",
            AutomaticAuthenticate = true,
            AccessDeniedPath = "/AccessDenied"
        });
    app.UseOpenIdConnectAuthentication(
        new OpenIdConnectOptions
        {
            AuthenticationScheme = "oidc",
            SignInScheme = "Cookies",

            Authority = "https://localhost:44316",
            ClientId = "test-mule",
            ClientSecret = "secret",
            ResponseType = "code id_token",

            SaveTokens = true,
            GetClaimsFromUserInfoEndpoint = true,

            PostLogoutRedirectUri = "https://localhost:44324",
            RequireHttpsMetadata = true,

            Scope = { "openid", "profile", "name", "email", "org", "role" },

            TokenValidationParameters = new TokenValidationParameters
            {
                NameClaimType = "name",
                RoleClaimType = "role"
            }
        });
    #endregion Configure Authentication

    app.UseMvc();
}

Une fois connecté, je peux répertorier les revendications associées à l'utilisateur authentifié:

var claims = User.Claims.OrderBy(c => c.Type).ToList();

Sur l'application ASP.NET 1.1, cela fournit la liste des revendications suivante:

amr         pwd
aud         test-mule
auth_time   1504529067
c_hash      nouhsuXtd5iKT7B33zxkxg
email       tom@
exp         1504532668
family_name Cobley
given_name  Tom
iat         1504529068
idp         local
iss         https://localhost:44316
name        tom
nbf         1504529068
nonce       6364012...
org         IBX
role        SysAdmin
role        TeleMarketing
role        AccountManager
role        DataManager
role        Member
sid         2091...
sub         1b19...440fa

Ce qui est ce que je veux/attend.

J'essaie maintenant de répliquer ce problème dans ma première application ASP.NET Core 2.0 avec la configuration Startup suivante:

public Startup()
{
    JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
}

public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthentication("Cookies")
        .AddCookie("Cookies", options =>
            {
                options.LoginPath = "/SignIn";
                options.AccessDeniedPath = "/AccessDenied";
            })
        .AddOpenIdConnect("oidc", options =>
            {
                options.SignInScheme = "Cookies";
                options.Authority = "https://localhost:44316";
                options.ClientId = "test-mule";
                options.ClientSecret = "secret";
                options.ResponseType = "code id_token";

                options.SaveTokens = true;
                options.GetClaimsFromUserInfoEndpoint = true;

                options.SignedOutRedirectUri = "https://localhost:44367";
                options.RequireHttpsMetadata = true;

                options.Scope.Clear();
                options.Scope.Add("openid");
                options.Scope.Add("profile");
                options.Scope.Add("name");
                options.Scope.Add("email");
                options.Scope.Add("org");
                options.Scope.Add("role");

                options.TokenValidationParameters = new TokenValidationParameters
                {
                    NameClaimType = "name",
                    RoleClaimType = "role"
                };
            });

    // Add framework services.
    services.AddMvc();
}

Pour référence l'action du contrôleur /Signin ressemble à ceci:

[Route("/SignIn")]
public IActionResult SignIn(string returnUrl = null)
{
    if (!Url.IsLocalUrl(returnUrl)) returnUrl = "/";

    var props = new AuthenticationProperties
    {
        RedirectUri = returnUrl
    };

    return Challenge(props, "oidc");
}

Dans cet environnement, après une connexion réussie, si je liste les revendications de l'utilisateur, je ne vois qu'un sous-ensemble de ce qui est disponible dans Core 1.1:

email       tom@...
family_name Cobley
given_name  Tom
idp         local
name        tom.cobley
sid         2091...
sub         1b19...440fa

J'ai exécuté des journaux de trace à la fois sur le client et sur le serveur, mais je ne peux rien voir/identifier. Je suppose également que ce n'est pas un problème d'Identity Server puisqu'il s'agit de 'juste' un service Open Id Connect qui devrait être cohérent avec tout client?

Quelqu'un peut-il m'indiquer dans la bonne direction où je me trompe?

Merci.

Mettre à jour

Suite à la suggestion de MVCutter, j'ai ajouté un gestionnaire d'événements `OnUserInformationReceived car j'avais remarqué que toutes mes revendications personnalisées n'étaient pas correctement mappées à l'identité de l'utilisateur. Je ne sais pas pourquoi c'est nécessaire ou s'il y a un meilleur endroit pour le faire, mais cela semble me donner ce que je veux pour le moment.

private Task OnUserInformationReceivedHandler(
    UserInformationReceivedContext context)
{
    if (!(context.Principal.Identity is ClaimsIdentity claimsId))
    {
        throw new Exception();
    }

    // Get a list of all claims attached to the UserInformationRecieved context
    var ctxClaims = context.User.Children().ToList();

    foreach (var ctxClaim in ctxClaims)
    {
        var claimType = ctxClaim.Path;
        var token = ctxClaim.FirstOrDefault();
        if (token == null)
        {
            continue;
        }

        var claims = new List<Claim>();
        if (token.Children().Any())
        {
            claims.AddRange(
                token.Children()
                    .Select(c => new Claim(claimType, c.Value<string>())));
        }
        else
        {
            claims.Add(new Claim(claimType, token.Value<string>()));
        }

        foreach (var claim in claims)
        {
            if (!claimsId.Claims.Any(
                c => c.Type == claim.Type &&
                     c.Value == claim.Value))
            {
                claimsId.AddClaim(claim);
            }
        }
    }

    return Task.CompletedTask;
}
6
Neilski

S'il vous plaît voir ci-dessous, j'ai rencontré le même problème que vous. Je suis sûr qu'il nous manque un problème de configuration mais, pour le moment, j'ai analysé les valeurs renvoyées par UserInfoEndpoint dans le gestionnaire d'événements OnUserInformationReceived.

    public override void ConfigureServices(IServiceCollection services)
    {
        services.AddAuthentication(sharedOptions =>
        {
            sharedOptions.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            sharedOptions.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            sharedOptions.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
        })
            .AddCookie()
            .AddOpenIdConnect(o =>
            {
                o.ClientId = "sss";
                o.ClientSecret = "sss";
                o.RequireHttpsMetadata = false;
                o.Authority = "http://localhost:60000/";
                o.MetadataAddress = "http://localhost:60000/IdSrv/.well-known/openid-configuration";
                o.ResponseType = OpenIdConnectResponseType.IdTokenToken;
                o.CallbackPath = new PathString("/CGI/Home/Index");
                o.SignedOutCallbackPath = new PathString("/CGI/Account/LoggedOut");
                o.Scope.Add("openid");
                o.Scope.Add("roles");
                o.SaveTokens = true;
                o.GetClaimsFromUserInfoEndpoint = true;
                o.Events = new OpenIdConnectEvents()
                {
                    OnUserInformationReceived = (context) =>
                    {
                        ClaimsIdentity claimsId = context.Principal.Identity as ClaimsIdentity;

                        var roles = context.User.Children().FirstOrDefault(j => j.Path == JwtClaimTypes.Role).Values().ToList();
                        claimsId.AddClaims(roles.Select(r => new Claim(JwtClaimTypes.Role, r.Value<String>())));

                        return Task.FromResult(0);
                    }
                };
                o.TokenValidationParameters = new TokenValidationParameters
                {
                    NameClaimType = JwtClaimTypes.Name,
                    RoleClaimType = JwtClaimTypes.Role,
                };
            });
    }

Modifier:

J'ai trouvé qu'il existe une méthode d'extension pour la propriété ClaimsAction appelée MapUniqueJsonKey, elle semble fonctionner pour les clés à valeur unique personnalisées, mais les bombes sur les types de tableaux comme les rôles ... se rapprocher

o.ClaimActions.MapUniqueJsonKey("UserType", "UserType");
6
MVCutter

ASP.NET Core 2 a introduit une propriété ClaimActions dans OpenIdConnectionOptions. La collection par défaut de ClaimActions supprimera les revendications que vous recherchez. Vous pouvez récupérer les revendications en désactivant ClaimActions sur votre objet options:

options.ClaimActions.Clear();

Voir aussi: https://leastprivilege.com/2017/11/15/missing-claims-in-the-asp-net-core-2-openid-connect-handler/

1
OdeToCode