7

Securing ASP.NET Core APIs with the Client Credentials Grant Type

 3 years ago
source link: https://espressocoder.com/2019/09/04/securing-asp-net-core-apis-with-the-client-credentials-grant-type/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

OAuth 2.0 is an industry standard protocol for authorization. It is designed to accommodate a wide range of applications such as web, desktop, and mobile apps by applying specific authorization processes. A different grant type is specified for each use case. Today we will be discussing the Client Credentials grant type, what it is, and how it can be implemented with various identity providers in ASP.NET Core.

key-3348307_1920-1024x576.jpg

What is the Client Credentials Grant Type?

In OAuth2, a client is an application that can request a token from an identity provider. As the name implies, the client credentials grant type is used to request a token under the context of a client, not a user. For authentication, two pieces of information are typically provided, a client ID and a client secret. If the provided credentials are valid, the identity provider will issue a token to the requesting application.

The token will be signed by the identity provider however, it will may not contain some of the common claim types such as the sub claim. This makes sense as the sub claim is used to represent a principle’s unique identifier.

Many other claims and scopes however will be provided in a client credentials token. As long as the required claims are included meet, it may be used for authentication. Applications that trust in identity providers tokens are commonly referred to as a relying party.

When Should I Use the Client CREDENTIALS Grant Type?

A key characteristic of the client credentials grant type is the ability to request a token outside of the context of a user. Ideally a token should belong to a specific principle / user however, this is not always possible. For example, if you need to authenticate a scheduled request between two back-end systems, you likely will not have a specific user to authenticate with. This is where client credentials can come in handy.

Requesting an Access Token

Lets take a look at how we can request a client credentials token from an identity provider. Since the client credentials grant type is based on the OAuth 2.0 specification, the process for requesting a token will be similar no matter which identity provider is used. That said, in the example below we will be using my favorite identity platform Auth0.

public static async Task Main(string[] args)
var client = new HttpClient
BaseAddress = new Uri("https://espressocoder.auth0.com")
var content = new StringContent(
JsonConvert.SerializeObject(
client_id = "...",
client_secret = "...",
audience = "https://jrtech.oauth.samples",
grant_type = "client_credentials"
}), Encoding.UTF8, "application/json");
var response = await client.PostAsync("oauth/token", content);
var tokenResponse = await response.Content.ReadAsStringAsync();
public static async Task Main(string[] args)
{
    var client = new HttpClient
    {
        BaseAddress = new Uri("https://espressocoder.auth0.com")
    };

    var content = new StringContent(
        JsonConvert.SerializeObject(
            new 
            { 
                client_id = "...", 
                client_secret = "...", 
                audience = "https://jrtech.oauth.samples", 
                grant_type = "client_credentials" 
            }), Encoding.UTF8, "application/json"); 
            
    var response = await client.PostAsync("oauth/token", content); 
    var tokenResponse = await response.Content.ReadAsStringAsync();
}

Tokens are requested by a client via the token endpoint. Above we are using the built in HttpClient in .NET Core and sending the request in the request body. For client credentials requests, there are four key pieces of information required in the request.

  • Client ID – Uniquely identifies the client requesting the token
  • Client Secret – Password used to authenticate the token request
  • Audience – Uniquely identifies the relying party
  • Grant Type – Must be client_credentials

If the token request is successful, you will receive a successful access token response.

"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"example",
"expires_in":3600,
"example_parameter":"example_value"
{
  "access_token":"2YotnFZFEjr1zCsicMWpAA",
  "token_type":"example",
  "expires_in":3600,
  "example_parameter":"example_value"
}

Access Token You Say?

The access_token field from the response will contain a JSON Web Token (JWT). This token can be used to authenticate subsequent requests to the relying party. JSON Web Tokens are an industry standard method for representing security claims across multiple systems. Looking at our previous example, we might retrieve a token that looks like this.

eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik9FTkZOMEpGTmpnM05rTkROemsxUlVSQ1FrVkJRVUpGTTBORVJUZzRPVFpFTURFeVJqa3pSZyJ9.eyJpc3MiOiJodHRwczovL2VzcHJlc3NvY29kZXIuYXV0aDAuY29tLyIsInN1YiI6Im5FMnpLN0xuc2hIeHZZUFl0NFhQVGd5VUg4aHJubURaQGNsaWVudHMiLCJhdWQiOiJodHRwczovL2pydGVjaC5vYXV0aC5zYW1wbGVzIiwiaWF0IjoxNTY2ODY4NzU2LCJleHAiOjE1NjY5NTUx……

The token is encoded however, using a tool like jwt.io we can decode it. This can be helpful in order to observe the payload included in the token. Below are the contents of the token generated by our example app.

"iss": "https://espressocoder.auth0.com/",
"sub": "nE2zK7LnshHxvYPYt4XPTgyUH8hrnmDZ@clients",
"aud": "https://jrtech.oauth.samples",
"iat": 1566868756,
"exp": 1566955156,
"azp": "nE2zK7LnshHxvYPYt4XPTgyUH8hrnmDZ",
"gty": "client-credentials"
{
  "iss": "https://espressocoder.auth0.com/",
  "sub": "nE2zK7LnshHxvYPYt4XPTgyUH8hrnmDZ@clients",
  "aud": "https://jrtech.oauth.samples",
  "iat": 1566868756,
  "exp": 1566955156,
  "azp": "nE2zK7LnshHxvYPYt4XPTgyUH8hrnmDZ",
  "gty": "client-credentials"
}

Using an Access Token in an API Request

Once we have a token, we can use it to authenticate against a relying party. This is done by adding the access token as an authentication header and sending it with our requests. It sounds simple but, really, it is. Take a look at the code below.

var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var response = await client.GetStringAsync("https://jrtech.oauth.samples")
var client = new HttpClient(); 
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token); 
var response = await client.GetStringAsync("https://jrtech.oauth.samples")

Now there is still one last piece. If the relying party (https://jrtech.oauth.samples in this case) is not configured to trust tokens from our auth0 account, then the request will fail. But rest assured, we will take a look at this next.

Verifying Access Tokens

Verifying the access token can be done using standard middleware components in ASP.NET Core. First we need to add the following lines to the ConfigureServices method in the Startup.cs class.

// removed from brevity
services.AddAuthentication(); <br>
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
options.Authority = "https://espressocoder.auth0.com";
options.Audience = "https://jrtech.oauth.samples";
// removed from brevity

services.AddAuthentication(); 
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.Authority = "https://espressocoder.auth0.com"; options.Audience = "https://jrtech.oauth.samples"; });

Next, we have to add the authentication middleware the request pipeline. In most cases, we will want to add this right before the MVC middleware component. The order in which the middleware components are added defines the order they are invoked. In our case, we only want authenticated requests to make it to MVC.

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
// Removed for brevity
app.UseAuthentication();
app.UseMvc(routes =>
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    // Removed for brevity

    app.UseAuthentication();
    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });
}

Now, our api will only permit requests that have an access token included from our Auth0 account. Unauthorized requests will receive a 403 http response.

Conclusion

Its is important to secure your web applications. Likewise, tt is equally important to use the right tool for the job. When securing non interactive applications, where a user is not initiating the request, the client_credentials grant type is your friend!

In the next article we will be discussing how to secure SPA applications with the OpenID Connect Authorization Code Flow with PKCE. STAY TUNED!

Like this:

Loading...

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK