DuendeSoftware / Support

Support for Duende Software products
20 stars 0 forks source link

Dotnet Framework client - UserInfo endpoint claims #1256

Closed MH61Aus closed 3 months ago

MH61Aus commented 3 months ago

Which version of Duende IdentityServer are you using?

7

Which version of .NET are you using?

8

Describe the bug

Question - I'm trying to hook one of our old apps into our identity server. I've been looking at the sample, but there's very little explanation about what's going on here, and i'm struggling to find any useful documentation for the Microsoft.Owin.Security.OpenIdConnect library anywhere.

I have set up an OWIN startup class like your sample, and am able to log in.

My home/index contains: var result = await HttpContext.GetOwinContext().Authentication.AuthenticateAsync("cookies");

after which i want to do

var userName = User.Identity.Name;
//go do stuff with the user name

In a core client, the OpenIdConnect Extensions lets me specify options.GetClaimsFromUserInfoEndpoint = true; in which case after gaining an identity token, a call is made to the user info endpoint to get the claims needed for sign in.

However this Microsoft.Owin.Security.OpenIdConnect library doesn't seem to have such an option, but fortunately, this behaviour seems implicit, as my ID token has no identifiers other than the sub, and I can see that my UserInfoEndpoint does get hit.

My UserInfoEndpoint is a child of ProfileService as I'm using integration with AspNetIdentity. The issue is that when I call context.AddRequestedClaims(principal.Claims); it does nothing. principal.Claims contains all the claims I need, but ProfileDataRequestContext.RequestedClaimTypes is empty.

So basically what I need to know is how I can make sure the RequestedClaimTypes are included in the call to the userinfoendpoint.

Note - although I've explained my scenario - the sample itself needs updating. the sample is just using the TestUserProfileService, but if you throw a breakpoint on line 48 of that service and inspect the context, you'd see the RequestedClaimTypes are empty. In the OWIN sample User.Identity.Name is null, and no user claims are written to the home page when the view iterates through all the claims.

AndersAbel commented 3 months ago

The call to AuthenticateAsync and User.Identity.Name should be redundant. If the cookie authentication scheme is properly setup to be active, it should already have populated User.Identity. If you prefer to call AuthenticateAsync you can do that of course, but that will not update User.Identity. The result is in the returned value.

The Owin libraries do not support the user info endpoint. We have a compatibility setting in IdentityServer on the client that you can set to include all the claims in the id_token: AlwaysIncludeUserClaimsInIdToken.

In your final note, are you referring to the MVC or WebForms samples? The WebForms sample do list claims when I try it. (I lack som component to run the MVC sample, have to troubleshoot that to test it)

MH61Aus commented 3 months ago

The good news is that I've got this all working.

I'm still a little confused by some of your responses.

When you say "The Owin libraries do not support the user info endpoint" - I could clearly see it hitting my user profile service. As mentioned above, the problem is that RequestedClaimTypes was empty. But the user info endpoint was being hit.

When you say the WebForms sample showed all the claims - they didn't with the default setup. Once I added AlwaysIncludeUserClaimsInIdToken as you suggested, then it showed the claims (as did the MVC sample). All the other samples I've used (as far as I recall) print the user profile claims on the home page, so I'd suggest updating these samples to do the same by adding the AlwaysIncludeUserClaimsInIdToken setting, and possibly explaining somewhere in the comments or the samples doc that the UserInfo endpoint can't be relied upon.

Adding AlwaysIncludeUserClaimsInIdToken still didn't get User.Identity.Name to bind to the username. I first had to add

                TokenValidationParameters = new TokenValidationParameters
                {
                    NameClaimType = "name"
                }

to UseOpenIdConnectAuthentication - however doing just that caused a startup exception, and I found I also had to add:

app.SetDefaultSignInAsAuthenticationType("cookies");

even though I only had the one auth type defined.

I would recommend adding this to the samples too, as I imagine accessing the username through User.Identity.Name is important to most people.

AndersAbel commented 3 months ago

When you say "The Owin libraries do not support the user info endpoint" - I could clearly see it hitting my user profile service. As mentioned above, the problem is that RequestedClaimTypes was empty. But the user info endpoint was being hit.

The User profile service is invoked from multiple places. The User Info endpoint is one of them, but it also called in other steps of the flow. Could it be that a breakpoint in your user profile service was hit, but it was not because of a call to the user info endpoint?

MH61Aus commented 3 months ago

you're right, I didn't inspect it properly - its called when obtaining and processing the auth code.

I think that clarifies all my concerns regarding basic setup. I guess the only thing I'm curious about now is where the best place to hook into cookie validation events would be - I can't find an equivalent of CookieAuthenticationEvents.ValidatePrincipal, as we want to check for revoked sessions, etc like we do in our core clients.

AndersAbel commented 3 months ago

It is possible to setup events for the cookie handler in the IdentityServer host too, but please open a separate issue for that. It is better to keep one issue to one topic.

Since the original topic for this issue is resolved I'm closing this.