Identity Server 4 - Getting invalid_client error

13,294

The invalid_client error typically means the Client ID or Client Secret is incorrect. In this case, you are not including the Client Secret in your request to IdentityServer. Add "client_secret: 'secret'" to your request

Updated data:

 var user = { client_id: "js", client_secret: "secret", grant_type: 'password', username: "testuser", password: "testpasswrd", scope: 'obApi' };

Alternatively, you can not require a ClientSecret in your client config

var websiteClient = new Client()
{
    // we will be using Angular JS to access the API - so naming it js
    ClientId = "js",

    // just a human friendly name
    ClientName = "JavaScript Client",

    // set to GrantType.ResourceOwnerPassword - because using Username/Password to login
    AllowedGrantTypes = websiteGrants,

    // secret for authentication
    //TODO: Change the secret 
    ClientSecrets = { secret },

    // Disable client secret validation
    RequireClientSecret = false,

    // we need to access the fhApi from Angular JS front-end 
    // fhApi is defined in Resources file as an API Resource
    AllowedScopes = { "obApi" }
};

Heres a snippet from the IdentityServer4 ClientSecretValidator.cs with the exact error you're getting back as proof https://github.com/IdentityServer/IdentityServer4/blob/release/src/IdentityServer4/Validation/ClientSecretValidator.cs

var parsedSecret = await _parser.ParseAsync(context);
if (parsedSecret == null)
{
    await RaiseFailureEvent("unknown", "No client id found");

    _logger.LogError("No client identifier found");
    return fail;
}

Regarding your second question about getting tokens for JS, Android, and iOS, you might need to consider which OpenID Grant Type you will use for each scenario. The general recommendation I've seen posted from the IdentityServer devs is to use Implicit flow for web applications and Authorization Code (or Hybrid) Flow. You can read more about it here: http://docs.identityserver.io/en/release/topics/grant_types.html

Share:
13,294
Dawood Awan
Author by

Dawood Awan

Updated on June 13, 2022

Comments

  • Dawood Awan
    Dawood Awan almost 2 years

    I am new to Identity Server. I haven't configured it before. But I need it for a Project I am working on.

    The API will be serving an Angular JS Client, iOS App and an Android App. We need to implement authentication and authorisation.

    Note: I am trying to configure Identity Server and my API in the same Web API project.

    I have followed the documentation and configured Identity Server as the following:

    In startup.cs, in ConfigureServices()

            services.AddTransient<IProfileService, CustomProfileService>();
            services.AddTransient<IResourceOwnerPasswordValidator, CustomResourceOwnerPasswordValidator>();
    
    
    
            services.AddIdentityServer()
                .AddTemporarySigningCredential()
                // add the resources that need to be secured
                .AddInMemoryApiResources(IdentityServerConfig.Resources.GetApiResources())
                // add the clients that will be access the ApiResources
                .AddInMemoryClients(IdentityServerConfig.Clients.GetClients());
    

    The CustomProfileService and CustomResourceOwnerPasswordValidator and the same as this answer: https://stackoverflow.com/a/35306021/1910735

    In Configure()

            // as this API will also be acting as an
            app.UseIdentityServer();
    
    
            // now setup the Identity Server client, this API will also be the client 
    
            app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
            {
                Authority = "http://localhost:44337",
                RequireHttpsMetadata = false,
    
    
                ApiName = "obApi"
            });
    

    Here is the GetClients()

        public static IEnumerable<Client> GetClients()
        {
            var clients = new List<Client>();
    
    
    
            var websiteGrants = new List<string> { GrantType.ResourceOwnerPassword };
            var secret = new Secret("secret".Sha256());
    
            var websiteClient = new Client()
            {
                // we will be using Angular JS to access the API - so naming it js
                ClientId = "js",
    
                // just a human friendly name
                ClientName = "JavaScript Client",
    
                // set to GrantType.ResourceOwnerPassword - because using Username/Password to login
                AllowedGrantTypes = websiteGrants, 
    
                // secret for authentication
                //TODO: Change the secret 
                ClientSecrets = { secret },
    
                // we need to access the fhApi from Angular JS front-end 
                // fhApi is defined in Resources file as an API Resource
                AllowedScopes = { "obApi" }
            };
    
    
            clients.Add(websiteClient);
    
            return clients;
        }
    

    and here is the GetApiResources()

        public static IEnumerable<ApiResource> GetApiResources()
        {
            // e.g. if we want to protect an API called api1 - then we will add it here
            // these values are hard coded for now - but we can get from DB, config file etc.
    
            return new List<ApiResource>
                            {
                                new ApiResource("obApi", "Order2Bite API")
                            };
        }
    

    Now because I want to use it Angular JS, iOS and Android I want to just get the Access Token from the Identity Server, and then use the Access Token for Authentication and Authorisation.

    for this I am trying to access the /connect/token from a JS client

    But I am getting an invalid_client error.

            var user = { client_id: "js", grant_type: 'password', username: "testuser", password: "testpasswrd", scope: 'obApi' };
    
            var urlEncodedUrl = {
                'Content-Type': 'application/x-www-form-urlencoded',
            };
    
            this.$http({
                method: 'POST', url: "http://localhost:44337/connect/token",
                headers: urlEncodedUrl,
                data: user,
    
            })
                .then(data => {
                    console.log(data)
                },
                data => {
                    console.log(data)
    
                });
    

    enter image description here

    The error I get on the server side is ' No client identifier found': enter image description here

    1 - Why am I getting this error?

    2 - As I need to get the Token programmatically in JS, Android and iOS, I need to use /connect/token, am I correct on this? Am I on the correct path?

  • Dawood Awan
    Dawood Awan almost 7 years
    yes it works when i include the client_secret but I am confused. in the documentation it says client_secret is OPTIONAL. docs.identityserver.io/en/release/endpoints/token.html
  • kg743
    kg743 almost 7 years
    Please see my updated answer. The default constructor of Client defaults RequireClientSecret to true. Simply set it to false if you don't want to require a client secret. You are correct, it is not required to include a client secret on ResourceOwner flow authentication. IdentityServer3 required it, but v4 no longer does IIRC.