문제 설명
IdentityServer4를 사용할 때 "코드 챌린지 필요" 메시지가 나타납니다. (I am getting "code challenge required" when using IdentityServer4)
승인을 위해 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?}");
});
}
}
}
여기에 내 IdentityService 구성이 있습니다.
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
6"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"
}
참조 솔루션
방법 1:
I am pretty much sure that you are using version 4.0
or above. Let me know if I am correct?
In version 4.0
and above, the code flow + PKCE
is used by default, as this is more secure than Hybrid flow
according to the documentation.
Here is the link https://identityserver4.readthedocs.io/en/latest/topics/grant_types.html and link to relevant issue on github https://github.com/IdentityServer/IdentityServer4/issues/3728 describing it as a breaking change.
I also struggled with it for about 2 hours when I upgraded IdentityServer4 package to the latest version in one of my projects.
If you want to use Hybrid flow
set RequirePkce
to false
in your client configuration.
"Clients": {
/* Code removed for brevity */
RequirePkce : "false"
}
방법 2:
Got that error today and solved it by switching from:
options.ResponseType = "code id_token";
to
options.ResponseType = "code";
options.UsePkce = true;
Here's my complete client‑side options:
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;
Also, as I'm using IdentityServer on a docker and testing the client on the host, I had to configure an extra redirect Uri to be able to test:
RedirectUris =
{
"http://localhost:5001/signin‑oidc",
"http://host.docker.internal:5001/signin‑oidc",
"http://notused"
},
I'm basing my implementation on Dominic Baier's samples on GitHub.
Edit: I've come to understand now that for my case the response type could only be "code" because my client configuration is for Authorization Code + PKCE (an OAuth2 flow). You have "Hybrid" configured (an OIDC flow) that supports "code id_token" so although we has received the same error message, the problem was different.
방법 3:
try this : https://github.com/IdentityServer/IdentityServer4/issues/4238
set your RequirePkce as false in the configuration.
방법 4:
Run your application and once it is redirected to the browser page remove all the cookies Site Settings ‑> Under Usage‑> Cookies ‑> Clear Data respective to that URL(https://localhost:5002) before redirection to login page. Then restart the application. This solved code challenge issue
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 ‑ Identity Server
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"}
}
(by Shushan、Gurdev、Sérgio Azevedo、Aditya Upadhyay、Thorunitha Sathiyanarayanan)