Identity Server 4 with ASP.NET 4.6.2 Web API causes redirect

I am trying to use Identity Server 4 with my Web API written in .NET Framework 4.6.2. I am using the IdentityServer3.Contrib.AccessTokenValidation library as mentioned in this answer. But, the calls to the protected endpoints are returning a 402 redirect.

Here’s the code from the API:

private void ConfigureAuth(IAppBuilder app)
        {
            app.Map("/api",
                    apiApp =>
                    {
                        apiApp.UseCors(corsOptions);
                        apiApp.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions
                        {
                            Authority = apiAuthority,
                            ValidationMode = ValidationMode.ValidationEndpoint,
                            RequiredScopes = new[] { "api" }
                        });
                        SetupOpenIdAuthentication(apiApp);
                    });
        }

        private static void SetupOpenIdAuthentication(IAppBuilder app)
        {
            app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AuthenticationType = "Cookies",
                ExpireTimeSpan = new TimeSpan(0, Configuration.SessionTimeoutInMinutes, 0),
                SlidingExpiration = true
            });
            app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
            {
                Authority = apiAuthority,
                ClientId = "clientId",
                RedirectUri = apiRootUri + "Help",
                PostLogoutRedirectUri = apiRootUri,
                ResponseType = "id_token token",
                Scope = "openid profile roles api all_claims",
                SignInAsAuthenticationType = "Cookies",
                Notifications = new OpenIdConnectAuthenticationNotifications()
                {
                    RedirectToIdentityProvider = n =>
                    {
                        // if signing out, add the id_token_hint
                        if (n.ProtocolMessage.RequestType == Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectRequestType.Logout)
                        {
                            var idTokenHint = n.OwinContext.Authentication.User.FindFirst("id_token");
                            if (idTokenHint != null)
                            {
                                n.ProtocolMessage.IdTokenHint = idTokenHint.Value;
                            }
                        }

                        if (n.ProtocolMessage.RequestType == Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectRequestType.Authentication)
                        {
                            if (IsAjaxRequest(n.Request) && n.Response.StatusCode == (int)System.Net.HttpStatusCode.Unauthorized)
                            {
                                n.Response.StatusCode = (int)System.Net.HttpStatusCode.Unauthorized;
                                n.HandleResponse();
                                return Task.FromResult(0);
                            }
                        }

                        return Task.FromResult(0);
                    },
                    SecurityTokenValidated = n =>
                    {
                        // keep the id_token for logout
                        n.AuthenticationTicket.Identity.AddClaim(
                            new Claim("id_token", n.ProtocolMessage.IdToken));

                        //Add Role claims as MS claims so Authorize works on API methods when used without the bearer token
                        foreach (var claim in n.AuthenticationTicket.Identity.Claims.Where(x => x.Type == JwtClaimTypes.Role).ToList())
                        {
                            n.AuthenticationTicket.Identity.AddClaim(new Claim(ClaimTypes.Role, claim.Value));
                        }

                        return Task.FromResult(0);
                    },
                    AuthenticationFailed = n =>
                    {
                        // Pass in the context back to the app
                        n.OwinContext.Response.Redirect("/Help/Error");
                        //TODO:  Create authentication failure page
                        n.HandleResponse(); // Suppress the exception
                        return Task.FromResult(0);
                    }
                }
            });
        }

Code from Identity Server:
var builder = services.AddIdentityServer(options =>
            {
                options.Events.RaiseErrorEvents = true;
                options.Events.RaiseInformationEvents = true;
                options.Events.RaiseFailureEvents = true;
                options.Events.RaiseSuccessEvents = true;

                // see https://identityserver4.readthedocs.io/en/latest/topics/resources.html
                options.EmitStaticAudienceClaim = true;
            })
            .AddSigningCredential(rsaCertificate)
            .AddInMemoryIdentityResources(IdentityConfig.IdentityResources)
            .AddInMemoryApiScopes(IdentityConfig.ApiScopes)
            .AddInMemoryClients(IdentityConfig.Clients)
            .AddAspNetIdentity<User>()
            .AddProfileService<CustomProfileService>()
            .AddWsFederationPlugin(options =>
            {
                options.Licensee = "License";
                options.LicenseKey = "Key"
            })
            .AddInMemoryRelyingParties(new List<RelyingParty>());

            services.AddAuthentication(sharedOptions =>
            {
                sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                sharedOptions.DefaultChallengeScheme = WsFederationDefaults.AuthenticationScheme;
            })
            .AddWsFederation(options =>
            {
                options.Wtrealm = "azureAppId";
                options.MetadataAddress = "metadataAddress";
            });
            builder.Services.ConfigureApplicationCookie(options =>
                {
                    options.Cookie.IsEssential = true;
                    options.Cookie.SameSite = SameSiteMode.None; //SameSiteMode.Unspecified in .NET Core 3.1
                });

The following is the client registered for the API in Identity Server:
new Client
            {
                ClientId = "clientId",
                AllowedGrantTypes = GrantTypes.ImplicitAndClientCredentials,
                ClientSecrets = { new Secret("secret".Sha256()) },
                RedirectUris = { "https://localhost:44302/", $"{apiUrl}/Help" },
                PostLogoutRedirectUris = { $"{apiUrl}" },
                AllowedScopes = new List<string>
                {
                    IdentityServerConstants.StandardScopes.OpenId,
                    IdentityServerConstants.StandardScopes.Profile,
                    "api",
                    "roles",
                    "all_claims"
                },
                RequirePkce = false,
                AllowOfflineAccess = true,
                AllowAccessTokensViaBrowser = true
            }

Please let me know if I need to add more code. The same access token when used to call a .NET Core API protected with Identity Server 4 will not cause a redirect. But, when used with an API in .NET 4.6.2, it causes a redirect.

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

In the API, change ValidationMode = ValidationMode.ValidationEndpoint to ValidationMode = ValidationMode.Both. Which would enable Identity Server to use local validation for JWTs and validation endpoint for reference tokens.


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x