Identity Server 4 - Getting invalid_client error
Asked Answered
S

1

9

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://mcmap.net/q/224930/-identityserver4-register-userservice-and-get-users-from-database-in-asp-net-core

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?

Subclass answered 11/5, 2017 at 10:17 Comment(2)
Where do you configure the user? You are logging in with grant_type=password and username/password. It seems user does not exist,Discredit
the request is not reaching CustomResourceOwnerPasswordValidator - so it means identity server is throwing the invalid_client error before reaching the Password validator - @RuardvanElburgSubclass
N
10

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

Northnorthwest answered 12/5, 2017 at 7:56 Comment(2)
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.htmlSubclass
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.Northnorthwest

© 2022 - 2024 — McMap. All rights reserved.