wake-up-neo.com

Ansprüche können in .NET Core 2.0 nicht abgerufen werden

Ich verwende einen OpenId Connect-Authentifizierungsserver , insbesondere Identity Server 4 (Version 1.5.2) unter .NET Core 1.1. Ich habe dies mit ASP.NET Framework MVC 5 und ASP.NET Core 1.1 MVC-Webanwendungen ausgeführt. Die folgende Konfiguration stammt aus einer .NET Core 1.1-Webanwendung:

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

Sobald ich angemeldet bin, kann ich die Ansprüche auflisten, die dem authentifizierten Benutzer zugeordnet sind:

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

In der ASP.NET 1.1-Anwendung wird die folgende Anspruchsliste bereitgestellt:

amr         pwd
aud         test-mule
auth_time   1504529067
c_hash      nouhsuXtd5iKT7B33zxkxg
email       [email protected]
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

Welches ist, was ich will/erwarte.

Ich versuche jetzt, dieses Verhalten in meiner ersten ASP.NET Core 2.0 -Anwendung mit der folgenden Startup-Konfiguration zu replizieren:

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

Als Referenz sieht die /Signin controller Aktion so aus:

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

    var props = new AuthenticationProperties
    {
        RedirectUri = returnUrl
    };

    return Challenge(props, "oidc");
}

In dieser Umgebung wird nach einer erfolgreichen Anmeldung, wenn ich die Ansprüche des Benutzers aufführe, nur eine Teilmenge dessen angezeigt, was in Core 1.1 verfügbar ist:

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

Ich habe Ablaufverfolgungsprotokolle sowohl auf dem Client als auch auf dem Server ausgeführt, kann jedoch nichts sehen/identifizieren. Ich gehe auch davon aus, dass es sich nicht um ein Identity Server-Problem handelt, da es sich um 'nur' einen Open Id Connect-Dienst handelt, der mit jedem Client konsistent sein sollte.

Kann mich jemand in die richtige Richtung weisen, wo ich falsch liege?

Vielen Dank.

Aktualisieren

Dem Vorschlag von MVCutter folgend, fügte ich einen OnUserInformationReceived-Ereignishandler hinzu, da ich bemerkt hatte, dass nicht alle meine benutzerdefinierten Ansprüche korrekt der Benutzeridentität zugeordnet wurden. Ich bin mir nicht sicher, warum dies notwendig ist oder ob es einen besseren Ort dafür gibt, aber es scheint mir zu geben, was ich jetzt will.

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

Bitte sehen Sie unten, ich bin auf dasselbe Problem gestoßen wie Sie. Ich bin sicher, dass es ein Konfigurationsproblem gibt, das wir vermissen, aber vorerst habe ich die vom UserInfoEndpoint im OnUserInformationReceived-Ereignishandler zurückgegebenen Werte analysiert.

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

Bearbeiten:

Ich fand heraus, dass es eine Erweiterungsmethode für die ClaimsAction-Eigenschaft namens MapUniqueJsonKey gibt. Diese Methode scheint für benutzerdefinierte Schlüssel mit einem Wert zu funktionieren, aber Bomben auf Array-Typen wie Rollen ... rücken näher

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

In ASP.NET Core 2 wurde eine ClaimActions-Eigenschaft für OpenIdConnectionOptions eingeführt. Die standardmäßige Sammlung von ClaimActions entfernt die von Ihnen gesuchten Ansprüche. Sie können die Ansprüche zurückerhalten, indem Sie ClaimActions für Ihr Optionsobjekt löschen:

options.ClaimActions.Clear();

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

1
OdeToCode