I am trying to redirect to IdentityServer for authorization, and getting "code challenge required" in redirect URL.
An error message shows invalid_request with code challenge required, and also my redirect url http://localhost:44367/signin-oidc#error=invalid_request&error_description=code%20challenge%20required&state=CfDJ8Cq6lLUEMhZLqMhFVN
Here is my client configuration:
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?}"); }); } } }
And here is my IdentityService4 configuration
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(); }); } }
and 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" }
IdentityServer4 support will last until the end of life of . NET Core 3.1 that means till November 2022. In that way, Duende provides new documentation for the fifth service version.
IdentityServer is an authentication server that implements OpenID Connect (OIDC) and OAuth 2.0 standards for ASP.NET Core. It's designed to provide a common way to authenticate requests to all of your applications, whether they're web, native, mobile, or API endpoints.
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" }
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With