ASP.NET 4.6.2 Web API 的 Identity Server 4 导致重定向
Identity Server 4 with ASP.NET 4.6.2 Web API causes redirect
我正在尝试将 Identity Server 4 与使用 .NET Framework 4.6.2 编写的 Web API 结合使用。我正在使用 IdentityServer3.Contrib.AccessTokenValidation
答案中提到的 IdentityServer3.Contrib.AccessTokenValidation
库。但是,对受保护端点的调用返回 402 重定向。
这是来自 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);
}
}
});
}
来自身份服务器的代码:
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
});
以下是在Identity Server中为API注册的客户端:
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
}
如果我需要添加更多代码,请告诉我。当用于调用受 Identity Server 4 保护的 .NET Core API 时,相同的访问令牌不会导致重定向。但是,当在 .NET 4.6.2 中与 API 一起使用时,它会导致重定向。
在 API 中,将 ValidationMode = ValidationMode.ValidationEndpoint
更改为 ValidationMode = ValidationMode.Both
。这将使 Identity Server 能够使用 JWT 的本地验证和参考令牌的验证端点。
我正在尝试将 Identity Server 4 与使用 .NET Framework 4.6.2 编写的 Web API 结合使用。我正在使用 IdentityServer3.Contrib.AccessTokenValidation
答案中提到的 IdentityServer3.Contrib.AccessTokenValidation
库。但是,对受保护端点的调用返回 402 重定向。
这是来自 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);
}
}
});
}
来自身份服务器的代码:
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
});
以下是在Identity Server中为API注册的客户端:
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
}
如果我需要添加更多代码,请告诉我。当用于调用受 Identity Server 4 保护的 .NET Core API 时,相同的访问令牌不会导致重定向。但是,当在 .NET 4.6.2 中与 API 一起使用时,它会导致重定向。
在 API 中,将 ValidationMode = ValidationMode.ValidationEndpoint
更改为 ValidationMode = ValidationMode.Both
。这将使 Identity Server 能够使用 JWT 的本地验证和参考令牌的验证端点。