使用 IdentityServer4 时出现“需要代码挑战”
Posted
技术标签:
【中文标题】使用 IdentityServer4 时出现“需要代码挑战”【英文标题】:I am getting "code challenge required" when using IdentityServer4 【发布时间】:2020-05-18 13:51:51 【问题描述】:我正在尝试重定向到 IdentityServer 进行授权,并在重定向 URL 中获得“需要代码质询”。
错误消息显示 invalid_request 需要代码质询,以及我的重定向 URL http://localhost:44367/signin-oidc#error=invalid_request&error_description=code%20challenge%20required&state=CfDJ8Cq6lLUEMhZLqMhFVN
这是我的客户端配置:
namespace TestClient
public class Startup
public Startup(IConfiguration configuration)
Configuration = configuration;
public IConfiguration Configuration get;
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
services.Configure<CookiePolicyOptions>(options =>
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
);
services.AddControllersWithViews();
ConfigureIdentityServer(services);
services.AddCors();
private void ConfigureIdentityServer(IServiceCollection services)
var builder = services.AddAuthentication(options => SetAuthenticationOptions(options));
services.AddMvcCore()
.AddAuthorization();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
builder.AddCookie();
builder.AddOpenIdConnect(options => SetOpenIdConnectOptions(options));
private void SetAuthenticationOptions(AuthenticationOptions options)
options.DefaultScheme = Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectDefaults.AuthenticationScheme;
private void SetOpenIdConnectOptions(OpenIdConnectOptions options)
options.Authority = "https://localhost:44346";
options.ClientId = "TestIdentityServer";
options.RequireHttpsMetadata = false;
options.Scope.Add("profile");
options.Scope.Add("openid");
options.Scope.Add("TestIdentityServer");
options.ResponseType = "code id_token";
options.SaveTokens = true;
options.ClientSecret = "0b4168e4-2832-48ea-8fc8-7e4686b3620b";
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
if (env.IsDevelopment())
app.UseDeveloperExceptionPage();
else
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCors(builder => builder
.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod()
);
app.UseCookiePolicy();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
endpoints.MapControllerRoute(
name: "default",
pattern: "controller=Home/action=Index/id?");
);
这是我的 IdentityService4 配置
public class Startup
public Startup(IConfiguration configuration)
Configuration = configuration;
public IConfiguration Configuration get;
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
IdentityModelEventSource.ShowPII = true;
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddControllersWithViews();
services.AddRazorPages();
services.AddMvc().SetCompatibilityVersion(Microsoft.AspNetCore.Mvc.CompatibilityVersion.Version_3_0);
services.Configure<IISOptions>(iis =>
iis.AuthenticationDisplayName = "Windows";
iis.AutomaticAuthentication = false;
);
var builder = services.AddIdentityServer(options =>
options.Events.RaiseErrorEvents = true;
options.Events.RaiseInformationEvents = true;
options.Events.RaiseFailureEvents = true;
options.Events.RaiseSuccessEvents = true;
);
// this adds the config data from DB (clients, resources)
builder.AddInMemoryIdentityResources(Configuration.GetSection("IdentityResources"));
builder.AddInMemoryApiResources(Configuration.GetSection("ApiResources"));
builder.AddInMemoryClients(Configuration.GetSection("clients"));
services.AddAuthentication();
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
if (env.IsDevelopment())
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
else
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseIdentityServer();
app.UseEndpoints(endpoints =>
endpoints.MapControllerRoute(
name: "default",
pattern: "controller=Home/action=Index/id?");
endpoints.MapRazorPages();
);
和 appsettings.json
"IdentityResources": [
"Name": "openid",
"DisplayName": "Your user identifier",
"Required": true,
"UserClaims": [
"sub"
]
,
"Name": "profile",
"DisplayName": "User profile",
"Description": "Your user profile information (first name, last name, etc.)",
"Emphasize": true,
"UserClaims": [
"name",
"family_name",
"given_name",
"middle_name",
"preferred_username",
"profile",
"picture",
"website",
"gender",
"birthdate",
"zoneinfo",
"locale",
"updated_at"
]
],
"ApiResources": [
"Name": "TestIdentityServer",
"DisplayName": "TestIdentityServer API Services",
"Scopes": [
"Name": "TestIdentityServer",
"DisplayName": "TestIdentityServer API Services"
]
],
"Clients": [
"ClientId": "TestIdentityServer",
"ClientName": "TestIdentityServer Credentials Client",
// 511536EF-F270-4058-80CA-1C89C192F69A
"ClientSecrets": [ "Value": "entAuCGhsOQWRYBVx26BCgZxeMt/TqeVZzzpNJ9Ub1M=" ],
"AllowedGrantTypes": [ "hybrid" ],
"AllowedScopes": [ "openid", "profile", "TestIdentityServer" ],
"RedirectUris": [ "http://localhost:44367/signin-oidc" ],
//"FrontChannelLogoutUris": [ "http://localhost:44367/Home/Privacy" ],
//"PostLogoutRedirectUris": [ "http://localhost:44367/Home/Privacy" ],
"redirect_uri": "http://localhost:44367/signin-oidc"
【问题讨论】:
我遇到了同样的问题 - 你能解决这个问题吗? 这里有同样的问题。我什至不确定什么是代码挑战,因为常规的 GrantType.Code 不会要求这个。 【参考方案1】:我很确定您使用的是4.0
或更高版本。让我知道我是否正确?
在4.0
及更高版本中,默认使用code flow + PKCE
,因为根据文档,这比Hybrid flow
更安全。
这是链接 https://identityserver4.readthedocs.io/en/latest/topics/grant_types.html 和 github 上相关问题的链接 https://github.com/IdentityServer/IdentityServer4/issues/3728,将其描述为重大更改。
当我在我的一个项目中将 IdentityServer4 包升级到最新版本时,我也为此苦苦挣扎了大约 2 个小时。
如果您想在客户端配置中使用Hybrid flow
,请将RequirePkce
设置为false
。
"Clients":
/* Code removed for brevity */
RequirePkce : "false"
【讨论】:
是的,在较新版本的 IS4 中,我们需要确认在(客户端和服务器)配置中都没有使用 Pkce。【参考方案2】:今天遇到了这个错误并通过切换解决了它:
options.ResponseType = "code id_token";
到
options.ResponseType = "code";
options.UsePkce = true;
这是我完整的客户端选项:
options.Authority = "http://localhost:8000";
options.RequireHttpsMetadata = false; // dev only
options.ClientId = "testAPI";
options.ClientSecret = secret;
// code flow + PKCE (PKCE is turned on by default)
options.ResponseType = "code";
options.UsePkce = true;
options.Scope.Clear();
options.Scope.Add("openid");
options.Scope.Add("profile");
options.Scope.Add("offline_access");
options.Scope.Add("testAPI");
options.ClaimActions.MapJsonKey("website", "website");
//options.ResponseMode = "form_post";
//options.CallbackPath = "/signin-oidc";
// keeps id_token smaller
options.GetClaimsFromUserInfoEndpoint = true;
options.SaveTokens = true;
另外,由于我在 docker 上使用 IdentityServer 并在主机上测试客户端,我必须配置一个额外的重定向 Uri 才能进行测试:
RedirectUris =
"http://localhost:5001/signin-oidc",
"http://host.docker.internal:5001/signin-oidc",
"http://notused"
,
我的实现基于 Dominic Baier 在GitHub 上的样本。
编辑:我现在明白,对于我的情况,响应类型只能是“代码”,因为我的客户端配置是授权代码 + PKCE(一个 OAuth2 流)。 您已配置支持“code id_token”的“混合”(OIDC 流程),因此尽管我们收到了相同的错误消息,但问题有所不同。
【讨论】:
【参考方案3】:试试这个:https://github.com/IdentityServer/IdentityServer4/issues/4238
在配置中将您的 RequirePkce 设置为 false。
【讨论】:
欢迎堆栈溢出。您能解释一下链接中的内容吗?即使链接过期,您的答案也必须保持有效。【参考方案4】:运行您的应用程序,一旦它被重定向到浏览器页面删除所有 cookie 站点设置 -> 在使用情况下 -> Cookies -> 清除与该 URL 相关的数据(https://localhost: 5002) 在重定向到登录页面之前。然后重新启动应用程序。这解决了代码挑战问题
services.AddAuthentication(options =>
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
)
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme)
.AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options =>
options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.Authority = "https://localhost:5005";
options.ClientId = "movies_mvc_client";
options.ClientSecret = "secret";
options.ResponseType = "code";
options.SaveTokens = true;
// options.RequireHttpsMetadata = false;
options.GetClaimsFromUserInfoEndpoint = true;
options.Scope.Add("openid");
options.Scope.Add("profile");
);
Config.cs - 身份服务器
new Client
ClientId = "movies_mvc_client",
ClientSecrets = new Secret("secret".Sha256()) ,
AllowedGrantTypes = GrantTypes.Code,
RedirectUris = "https://localhost:5002/signin-oidc" ,
// FrontChannelLogoutUri = "https://localhost:44300/signout-oidc",
PostLogoutRedirectUris = "https://localhost:5002/signout-callback-oidc" ,
AllowOfflineAccess = true,
AllowedScopes = "openid", "profile","movies_mvc_client"
【讨论】:
这如何解决问题(为什么会这样)?以上是关于使用 IdentityServer4 时出现“需要代码挑战”的主要内容,如果未能解决你的问题,请参考以下文章
使用 IdentityServer4 对 Web API 进行基于角色的授权
ServiceStack 与 IdentityServer4 集成