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)
});
The error I get on the server side is ' No client identifier found':
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?
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