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.