Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

I am getting "code challenge required" when using IdentityServer4

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"     } 
like image 602
Shushan Avatar asked Feb 02 '20 17:02

Shushan


People also ask

Is IdentityServer4 obsolete?

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.

What is the use of IdentityServer4?

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.


2 Answers

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"     } 
like image 84
Gurdev Avatar answered Oct 13 '22 04:10

Gurdev


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.

like image 25
Sérgio Azevedo Avatar answered Oct 13 '22 04:10

Sérgio Azevedo