What is the use of Client secrets? - identityserver4

We are using EntityFramework Core to store configuration data. What is use of store Client secrets along with clients? Or can we add/modify clients secrets later on?
public static IEnumerable<Client> GetClients()
{
return new List<Client>
{
new Client
{
ClientId = "client",
// no interactive user, use the clientid/secret for authentication
AllowedGrantTypes = GrantTypes.ClientCredentials,
// secret for authentication
ClientSecrets =
{
new Secret("secret".Sha256())
},
// scopes that client has access to
AllowedScopes = { "api1" },
AccessTokenLifetime=3600
}

You will need the secret to access the Token Endpoint when programmatically requesting tokens.
For example, you can use the IdentityModel library:
var client = new TokenClient(
doc.TokenEndpoint,
"client_id",
"secret");
var response = await client.RequestClientCredentialsAsync("scope");
var token = response.AccessToken;

Although Gavin Sutherland's answer explains how to use client secrets, I believe the question is more about why they exist. Basically, think of the client ID and client secret as a username / password enabling a specific client application to initiate OpenID Connect requests (with the other IdentityServer Client configuration elements defining which requests are permitted).
You can change secrets, but synchronizing the secret between IdentityServer and the client application is a manual process. (It isn't like certain key rotation scenarios where you can have more than one secret active for awhile, removing the old one after all clients have updated, if that's what you're thinking.)
If you look at any OIDC / OAuth2 third-party identity provider (Google, Facebook, etc.) they all operate by issuing client IDs and secrets associated with specific clients (normally this means specific domains).

Related

ASP.NET 6 WebAPI Authentication with SSO

I have an ASP.NET 6.0 Web API project. I would like to add authentication and authorization to it, but it must use SSO via Azure.
We already have a SPA application that does this, it uses the Angular MSAL library to redirect the user to an SSO Login page, then returns to the SPA with an access token. The access token is then added to the header of each request to the Web API, which uses it to enforce authentication.
Now we want to share our web API with other teams within our organization, and we would like to have that login process just be another API call, rather than a web page.
Conceptually, a client would hit the /login endpoint of our API, passing in a userID and password. The web API would then get an access token from Azure, then return it as the payload of the login request. It's then up to the client to add that token to subsequent request headers.
I have done this with regular ASP.NET Identity, where all of the user and role data is stored in a SQL database, but since our organization uses SSO via Azure Active Directory, we would rather use that.
I have researched this topic online, and so far all of the examples I have seen use a separate SPA, just like we already have. But as this is a web api, not a front-end, we need to have an API method that does this instead.
Is this even possible? I know Microsoft would rather not have user credentials flow through our own web server, where a dishonest programmer might store them for later misuse. I understand that. But I'm not sure there's a way around this.
Thanks.
I believe you are looking for the Resource Owner Password (ROP) flow. You can use IdentityModel.OidcClient to implement it.
Sample code:
public class Program
{
static async Task Main()
{
// call this in your /login endpoint and return the access token to the client
var response = await RequestTokenAsync("bob", "bob");
if (!response.IsError)
{
var accessToken = response.AccessToken;
Console.WriteLine(accessToken);
}
}
static async Task<TokenResponse> RequestTokenAsync(string userName, string password)
{
var client = new HttpClient();
var disco = await client.GetDiscoveryDocumentAsync(Constants.Authority);
if (disco.IsError) throw new Exception(disco.Error);
var response = await client.RequestPasswordTokenAsync(new PasswordTokenRequest
{
Address = disco.TokenEndpoint,
ClientId = "roclient",
ClientSecret = "secret",
UserName = userName,
Password = password,
Scope = "resource1.scope1 resource2.scope1",
Parameters =
{
{ "acr_values", "tenant:custom_account_store1 foo bar quux" }
}
});
if (response.IsError) throw new Exception(response.Error);
return response;
}
}
Sample taken from IdentityServer4 repository where you can find more ROP flow client examples.
I would recommend that you don't go with this implementation and instead have all clients obtain their access tokens directly from Azure AD like you did with your Angular SPA.

Protect a single api resource with multiple IDServers

So I have a .Net Core web api, lets call it "CMS" and its currently protected by an IdentityServer4 server as an api resource. I have configured the ID4 server to have the IDP Claim of MyIDP.
For business reasons, I need to give a client their own IdentityServer but they would also like to have their users access the same api "CMS" .
Is this possible?
In the StartUp.cs of my CMS api it currently looks like this
services.AddAuthentication("Bearer")
.AddIdentityServerAuthentication(options =>
{
options.Authority = "http://www.idserver1.com";
options.RequireHttpsMetadata = true;
options.ApiName = "cmsapi";
});
so to add protection for another id server I assume i could just duplicate the AddAuthentication but change the scheme name from Bearer to something else but that seems wrong?
The reason I think this should be possible because I have been able to add multiple external providers to my Web Application in this manner . But this is for s sign in flow and not for an api.
If this is possible how do I go about this?
This can be achieved quite simply. Suppose you want to issue a separate subdomain for each of your clients: auth0.yourdomain.com, auth1.yourdomain.com and you want an api resource to respect the token from either of those identity providers.
Assuming that the signing key is the same, you can configure a shared issuer uri on the identity server side in Startup.cs->ConfigureServices(...):
var builder = services.AddIdentityServer(options => {
options.IssuerUri = "auth.yourdomain.com";
})
...
And then on the api side you can respect the single issuer uri without having to duplicate authentication schemes:
services.AddAuthentication("Bearer")
.AddIdentityServerAuthentication(options =>
{
options.Authority = "auth.yourdomain.com";
options.RequireHttpsMetadata = true;
options.ApiName = "cmsapi";
});
One thing I can't remember is if the request scheme (http/https) is inferred for the issuer uri or not so you might need to specify that as well (https:\\auth.yourdomain.com). Other than that, this sort of implementation should be quite seamless as far as your clients are concerned.
i think i may have figured out the solution, based off another problem that was happening to me over here
Using Client Credentials flow on identityserver4 and custom AuthorizationHandler User.Identity.isAuthenticated = false
turns out you can use multiple authenticationschemes to protect an api and choose which things to protect with what using the authenticationSchemes property of the Authorize Attribute.
so you would just need a way to map the incoming bearer token to the correct authentication scheme

Get an identity token for a client

Using IdentitServer4 I've create a client for a windows application. To call into another authentication service (ie, AWS STS) I need to setup federation to my ID server and using an identity token.
Is it possible to get an identity token for a client?
The following code give me the access token but the identity token is null.
var disco = await DiscoveryClient.GetAsync(Properties.Settings.Default.IdentityUrl);
if (disco.IsError)
{
return false;
}
var tokenClient = new TokenClient(disco.TokenEndpoint, _executionContext.ClientID, _executionContext.Secret);
var tokenResponse = await tokenClient.RequestClientCredentialsAsync("api.v1");
_executionContext.AgentToken = tokenResponse.AccessToken; // OK
_executionContext.IdentityToken = tokenResponse.IdentityToken; // NULL
No, by definition a client cannot request an identity token for itself. Only on behalf of a user. From the docs:
User
A user is a human that is using a registered client to access
resources.
Client
A client is a piece of software that requests tokens from
IdentityServer - either for authenticating a user (requesting an
identity token) or for accessing a resource (requesting an access
token).
The reason that a client can't request an identity token for itself is because it doesn't have (and can't have) a sub claim:
The presence (or absence) of the sub claim lets the API distinguish
between calls on behalf of clients and calls on behalf of users.
Here's an example on how to request an identity token on behalf of a user using the password grant.

Identity Server4 Introspection Endpoint authentication with client credentials

I am a bit confused about the use case with reference tokens and the introspection endpoint in a MVC/Webforms scenario.
My basic question is why is the introspection endpoint only setup to allow authentication requests from ApiResource credentials, (api1/apisecret) and not allow for client credentials? I am able to use the code bellow with ApiResource credentials, I just get unauthorized with client credentials. I think this is as designed.
I want to give our systems admins the ability to revoke the tokens and force logout users. My plan is to use the CookieAuthenticationProvider’s OnValidateIdentity to periodically validate the reference tokens and force a logout if needed. I think that I can get similar functionality with short lived self-contained JWT tokens and refresh tokens but liked the simplicity of using reference tokens. I can obviously use two sets of credentials in the same application, one for the client and one ApiResource, but is seams more intuitive to use/manage one set of credentials, the client’s. Should I not use reference tokens for a server-side trusted application?
My clients are setup like the following.
AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
AccessTokenType = AccessTokenType.Reference,
Here are a few notes from my research.
Here are the docs about Introspection Endpoint - http://docs.identityserver.io/en/release/endpoints/introspection.html
Here is Dominick Baier's post about reference tokens - https://leastprivilege.com/2015/11/25/reference-tokens-and-introspection/
Here is an example of the IntrospectionClient call I am using. https://github.com/IdentityServer/IdentityServer4.Samples/blob/release/Clients/src/ConsoleIntrospectionClient/Program.cs
Here is the spec for introspection and as you can see it does not indicate the type of authentication required. - https://www.rfc-editor.org/rfc/rfc7662
To prevent token scanning attacks, the endpoint MUST also require
some form of authorization to access this endpoint, such as client
authentication as described in OAuth 2.0 [RFC6749] or a separate
OAuth 2.0 access token such as the bearer token described in OAuth
2.0 Bearer Token Usage [RFC6750]. The methods of managing and validating these authentication credentials are out of scope of this
specification.
This is a sample of a ApiResource with a secret.
// simple version with ctor
new ApiResource("api1", "Some API 1")
{
// this is needed for introspection when using reference tokens
ApiSecrets = { new Secret("apisecret".Sha256()) }
},
Here is the sample client form the ConsoleIntrospectionClient client.
new Client
{
ClientId = "roclient.reference",
ClientSecrets =
{
new Secret("secret".Sha256())
},
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
AllowedScopes = { "api1", "api2.read_only" },
AccessTokenType = AccessTokenType.Reference
},

IdentityServer4 - Call an API as a User from MVC

I'm getting my knickers in a twist trying to understand how to call an API protected via IdentityServer4.
Basically, I have the following sites:
- an IdentityServer application,
- a web API and
- a client web application.
My setup is just like the IdentityServer samples here.
I define a Client which represents my client web application, and an APIResource which represents my Web Api.
From within my client web application I want to make an HTTP call to the WebAPI, but I want to appear as if I am the logged in user, so I want to make the 'email' scope available to the Web Api.
The way I'm doing from within the Web Application is to grab the 'access_token', and to pass it to the Web API:
var accessToken = await httpContextAccessor.HttpContext.Authentication.GetTokenAsync($"access_token");
var client = new HttpClient();
client.SetBearerToken(accessToken);
This allows me to call the Client, so the authorization step is working, but the User Claims on the Web Api do not have the appropriate scopes.
Am I doing something wrong?
The access_token can contain claim information in IdentityServer4. The required claims must be specified in the ApiResource definition.
Otherwise, you have to send a JWT id_token along with the request.
new ApiResource(ApiResourceNames.SomeApiAccess, "Access to some api.", new List<string>(){
new IdentityResources.OpenId().Name,
new IdentityResources.Profile().Name,
new IdentityResources.Email().Name
}),
You can add scopes in your web api like
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
Authority = "https://demo.identityserver.io",
ApiName = "api1",
AllowedScopes = { "api1.read", "api1.write" }
});
How did you configure your web api? Post the code if it still doesn't work!

Resources