Search code examples
c#identityserver4asp.net-core-webapi

PasswordTokenRequest returning invalid_client


I am attempting to get a token from a .net core api controller using a password with identity server 4. I am receiving Error invalid_client.

here is the controller.

   [HttpGet]
        public async Task<IActionResult> Get()
        {
            var client = new HttpClient();
            var disco =  await client.GetDiscoveryDocumentAsync("https://localhost:44321");
            var tokenResponse = await client.RequestPasswordTokenAsync(new PasswordTokenRequest
            {
                Address = disco.TokenEndpoint,
                ClientId = "htmlClient",
                ClientSecret = "secretpassword",
                UserName = "[email protected]",
                Password = "password",
                Scope = "WebApi.ReadAccess"
            });
            return Ok();
        }

here is the config

public class Config
    {
        public static IEnumerable<ApiResource> GetApiResources()
        {
            return new List<ApiResource>
            {
                new ApiResource(
                    "WebApi.ReadAccess",
                    "WebApi API",
                    new List<string> {
                        JwtClaimTypes.Id,
                        JwtClaimTypes.Email,
                        JwtClaimTypes.Name,
                        JwtClaimTypes.GivenName,
                        JwtClaimTypes.FamilyName
                    }
                ),

                new ApiResource("WebApi.FullAccess", "WebApi API")
            };
        }

        public static IEnumerable<Client> GetClients()
        {
            return new[]
            {
                new Client
                {
                    Enabled = true,
                    ClientName = "HTML Page Client",
                    ClientId = "htmlClient",
                    AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,

                    ClientSecrets =
                    {
                        new Secret("secretpassword")
                    },

                    AllowedScopes = { "WebApi.ReadAccess" }
                }
            };
        }
    }

in startup.cs in configureServices

 services.AddIdentityServer()
                   .AddInMemoryApiResources(Config.GetApiResources())
                   .AddInMemoryClients(Config.GetClients())
                   .AddProfileService<ProfileService>()
                   .AddDeveloperSigningCredential();

            services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme =
                                           JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme =
                                           JwtBearerDefaults.AuthenticationScheme;
            }).AddJwtBearer(o =>
            {
                o.Authority = "https://localhost:44321";
                o.Audience = "WebApi.ReadAccess";
                o.RequireHttpsMetadata = false;
            });

and in configure I have app.UseIdentityServer();

public  void Configure(IApplicationBuilder app, IHostingEnvironment env, BooksContext booksContext)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseIdentityServer();
    app.UseMvc();
    app.UseSwagger();
    app.UseSwaggerUI(options =>
        options.SwaggerEndpoint("/swagger/v2/swagger.json", "Book Chapter Service"));
    app.UseDefaultFiles();
    app.UseStaticFiles();
}

Solution

  • Try changing your code to following. I have given the generic code, you can change it according to your need.

                 return new List<ApiResource>
                {
                    new ApiResource
                    {
                        Name = "api",
                        DisplayName = "WebApi API",
                        Scopes =
                        {
                            new Scope("WebApi.ReadAccess", "Read write access to web api")
    
                        }
                    },
                    new ApiResource
                    {
                        Name = "api",
                        DisplayName = "WebApi API",
                        Scopes =
                        {
                            new Scope("WebApi.FullAccess", "Full access to web api")
    
                        }
                    }
                }
    
    

    and

    o.Audience = "api";
    

    The reason being, Your o.Audience name should match ApiResource.Name because it indicates mapping between your authority and audience. For example in your case Authority https://localhost:44321 has audience lets say called "api". "api" also is a name of your ApiResource which is giving authority to create access token. Hope this helps!