Migration from Core 1.1 to 2.0 - Authentication

307 Views Asked by At

In my .net core 1.1 code I'm doing the authentication as follows(which is sending the bearer token to external URL and look for claims in the return token). This code is in Configure method

app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationScheme = "Cookies"
        });

        app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
        {
            AuthenticationScheme = "oidc",
            SignInScheme = "Cookies",

            Authority = signinAuthority,
            RequireHttpsMetadata = signinHTTPS,

            ClientId = "skybus",
            ClientSecret = "secret",

            ResponseType = "code id_token",
            Scope = { "api1", "offline_access" },

            GetClaimsFromUserInfoEndpoint = true,
            SaveTokens = true
        });

Now I upgraded my code to .net Core 2.0 the both UseCookieAuthentication & UseOpenIdConnectAuthentication are changed. I'm finding it difficult to find what needs to be done in this case

What I changed it to is as follows in ConfigureServices method

services.AddAuthentication(options => {
            options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
        })
            .AddCookie()
            .AddOpenIdConnect(o =>
            {
                o.Authority = signinAuthority;
                o.SignInScheme = "Cookies";
                o.RequireHttpsMetadata = signinHTTPS;
                o.ClientId = "skybus";
                o.ClientSecret = "secret";
                o.ResponseType = "code id_token";
                o.GetClaimsFromUserInfoEndpoint = true;
                o.SaveTokens = true;
                o.Scope.Add("api1");
                o.Scope.Add("offline_access");
            });

In Browser I see this URL after the above changes. It should either show me the external login page if user is not logged in or return to home page of my website

http://localhost:5000/Account/Login?ReturnUrl=%2F

3

There are 3 best solutions below

0
Nouman Bhatti On BEST ANSWER

I followed this link from microsoft to start my migration. Most of the migration is covered by the this link, but I faced issue where most of my claims are missing.

With the ASP.NET Core 1.x, client would have received the claims: nbf, exp, iss, aud, nonce, iat, c_hash, sid, sub, auth_time, idp, amr.

In Core 2.0 we only get sid, sub and idp. What happened?

Microsoft added a new concept to their OpenID Connect handler called ClaimActions. Claim actions allow modifying how claims from an external provider are mapped (or not) to a claim in your ClaimsPrincipal. Looking at the ctor of the OpenIdConnectOptions, you can see that the handler will now skip the following claims by default:

ClaimActions.DeleteClaim("nonce");
ClaimActions.DeleteClaim("aud");
ClaimActions.DeleteClaim("azp");
ClaimActions.DeleteClaim("acr");
ClaimActions.DeleteClaim("amr");
ClaimActions.DeleteClaim("iss");
ClaimActions.DeleteClaim("iat");
ClaimActions.DeleteClaim("nbf");
ClaimActions.DeleteClaim("exp");
ClaimActions.DeleteClaim("at_hash");
ClaimActions.DeleteClaim("c_hash");
ClaimActions.DeleteClaim("auth_time");
ClaimActions.DeleteClaim("ipaddr");
ClaimActions.DeleteClaim("platf");
ClaimActions.DeleteClaim("ver");

If you want to “un-skip” a claim, you need to delete a specific claim action when setting up the handler. The following is the very intuitive syntax to get the amr claim back:

options.ClaimActions.Remove("amr");

Requesting more claims from the OIDC provider

When you are requesting more scopes, e.g. profile or custom scopes that result in more claims, there is another confusing detail to be aware of.

Depending on the response_type in the OIDC protocol, some claims are transferred via the id_token and some via the userinfo endpoint.

So first of all, you need to enable support for the userinfo endpoint in the handler:

options.GetClaimsFromUserInfoEndpoint = true;

In the end you need to add the following class to import all other custom claims

public class MapAllClaimsAction : ClaimAction
    {
        public MapAllClaimsAction() : base(string.Empty, string.Empty)
        {
        }

        public override void Run(JObject userData, ClaimsIdentity identity, string issuer)
        {
            foreach (var claim in identity.Claims)
            {
                // If this claimType is mapped by the JwtSeurityTokenHandler, then this property will be set
                var shortClaimTypeName = claim.Properties.ContainsKey(JwtSecurityTokenHandler.ShortClaimTypeProperty) ?
                    claim.Properties[JwtSecurityTokenHandler.ShortClaimTypeProperty] : string.Empty;

                // checking if claim in the identity (generated from id_token) has the same type as a claim retrieved from userinfo endpoint
                JToken value;
                var isClaimIncluded = userData.TryGetValue(claim.Type, out value) || userData.TryGetValue(shortClaimTypeName, out value);

                // if a same claim exists (matching both type and value) both in id_token identity and userinfo response, remove the json entry from the userinfo response
                if (isClaimIncluded && claim.Value.Equals(value.ToString(), StringComparison.Ordinal))
                {
                    if (!userData.Remove(claim.Type))
                    {
                        userData.Remove(shortClaimTypeName);
                    }
                }
            }

            // adding remaining unique claims from userinfo endpoint to the identity
            foreach (var pair in userData)
            {
                JToken value;
                var claimValue = userData.TryGetValue(pair.Key, out value) ? value.ToString() : null;
                identity.AddClaim(new Claim(pair.Key, claimValue, ClaimValueTypes.String, issuer));
            }
        }
    }

and then use the above class code to add it to ClaimActions

options.ClaimActions.Add(new MapAllClaimsAction());
0
Ryan Gunn On

I don't know if you are using IdentityServer4 but they have provided some samples on github for ASP.NET Core 2.0.

Hope this helps you.

Sample Project

0
jhpuro On

Did you take the authentication middleware into use?

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    app.UseAuthentication();
...