I am getting "code challenge required" when using IdentityServer4

18,199

Solution 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"
    }

Solution 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.

Solution 3

try this : https://github.com/IdentityServer/IdentityServer4/issues/4238

set your RequirePkce as false in the configuration.

Share:
18,199
Shushan
Author by

Shushan

Updated on June 27, 2022

Comments

  • Shushan
    Shushan almost 2 years

    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"
        }
    
  • LuizZ
    LuizZ over 3 years
    Welcome to stack overflow. Can you please explain what is in the link? Your answer must remain valid even if the link expires.
  • Terai
    Terai over 3 years
    yes, in the newer version of IS4 we need to confirm the not use of Pkce in both (the client and the server) config.
  • ryanwebjackson
    ryanwebjackson over 2 years
    How does this solve the problem (why does this work)?