OAuth2 client credentials flow | Ory

OAuth2 Client Credentials Flow

OAuth2 is a protocol that allows third-party applications to access a user's data, without having to expose their credentials to the third-party application. OAuth2 provides a number of different flows to accomplish this goal, and one of the most commonly used is the Client Credentials flow.

The Client Credentials flow is used when an application needs to access its own resources, rather than a user's resources. In this flow, the application sends its client ID and client secret to the authorization server, and in return receives an access token that can be used to access protected resources.

OAuth2 Server and Client

POST /oauth2/token?grant=client_credentials&...

Info

The JavaScript example code contained in this article is exemplary and explains what happens under the hood. You should use tried and tested open source libraries to consume OAuth2 and OpenID Connect. You shouldn't write this code yourself, just as you wouldn't write your own SHA512 library.

Basic Authentication

The first and most common mechanism for authenticating a client in the Client Credentials flow is Basic Authentication. In this mechanism, the client sends its client ID and client secret as part of the Authorization header in an HTTP request. The Authorization header contains a Base64-encoded string of {URL-encoded-client-ID}:{URL-encoded-client-secret}.

To create a client capable of using the Basic Authentication mechanism with the Ory CLI, run the following command:

ory create oauth2-client --project "$PROJECT_ID" \
  --name "Client Credentials Demo" \
  --grant-type client_credentials \
  --token-endpoint-auth-method client_secret_basic

To use the created client to obtain an access token using Basic Authentication, refer to the following code example:

const clientID = "the-client-id"
const clientSecret = "the-secret-id"
const basicAuth = bas64_encode(url_encode(clientID) + ":" + url_encode(clientSecret))

const requestOptions = {
  method: "POST",
  headers: {
    Authorization: "Basic " + basicAuth,
    "Content-Type": "application/x-www-form-urlencoded",
  },
  body: "grant_type=client_credentials&scope=read",
}

fetch("https://your-project.projects.oryapis.com/oauth2/token", requestOptions)
  .then((response) => response.json())
  .then((data) => console.log(data))

Body Authentication

In the Body Authentication mechanism, the client sends its client ID and client secret as parameters in the body of the HTTP request. This is similar to Basic Authentication, but instead of sending the credentials in the Authorization header, they are sent in the request body.

To create a client capable of using the Body Authentication mechanism with the Ory CLI, run the following command:

ory create oauth2-client --project "$PROJECT_ID" \
  --name "Client Credentials Demo" \
  --grant-type client_credentials \
  --token-endpoint-auth-method client_secret_post

To use the created client to obtain an access token using Body Authentication, refer to the following code example:

const clientID = "the-client-id"
const clientSecret = "the-secret-id"
const qs = new URLSearchParams()
qs.set("grant_type", "client_credentials")
qs.set("client_id", clientID)
qs.set("client_secret", clientSecret)
qs.set("scope", read)

const requestOptions = {
  method: "POST",
  headers: { "Content-Type": "application/x-www-form-urlencoded" },
  body: qs.toString(),
}

fetch("https://your-project.projects.oryapis.com/oauth2/token", requestOptions)
  .then((response) => response.json())
  .then((data) => console.log(data))

JWT Bearer Profile for OAuth2 Client Authentication

The JWT Bearer Grant Type mechanism is similar to Basic and Body Authentication, but instead of sending the client ID and client secret, the client sends a JSON Web Token (JWT) which was signed by its cryptographic key.

To create a client capable of using the JWT Bearer Grant Type mechanism with Ory, you can use the following CLI command:

ory create oauth2-client --project "$PROJECT_ID" \
  --name "Client Credentials Demo" \
  --grant-type client_credentials \
  --token-endpoint-auth-method private_key_jwt \
  --jwks-uri https://example.org/path/to/clients/public_key_set.jwks

Note

Using the HTTP API, you can also send the JSON Web Key Set as part of the payload (key jwks) when creating the client. This is not possible using the Ory CLI.

When authenticating the client at the token endpoint, you generate and sign the JSON Web Token with the following claims:

To use the created client to obtain an access token using the JWT Bearer profile, refer to the following code example:

const clientID = "the-client-id"
const qs = new URLSearchParams()
qs.set("grant_type", "client_credentials")
qs.set("client_id", clientID)
qs.set("client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer")
qs.set(
  "client_assertion",
  createSignedJWT(
    {
      iss: "https://example.org",
      sub: clientID,
      aud: "https://your-project.projects.oryapis.com",
      jti: randomUUID(),
      exp: Math.floor((Date.now() + 1000 * 60 * 60) / 1000),
      iat: Date.now(),
    },
    privateKey,
  ),
)

const requestOptions = {
  method: "POST",
  headers: { "Content-Type": "application/x-www-form-urlencoded" },
  body: qs.toString(),
}

fetch("https://your-project.projects.oryapis.com/oauth2/token", requestOptions)
  .then((response) => response.json())
  .then((data) => console.log(data))

Rotating the Signing Key

Ory caches the key set it downloads from jwks_uri and starts at most one refresh attempt every 15 seconds per jwks_uri. Ory rejects an assertion signed with a key it has not picked up yet, and after a recent refresh a new key can stay unknown to Ory for up to 15 seconds. Publish the new key at jwks_uri at least 15 seconds before you sign assertions with it, and keep the retired key published for as long as assertions signed with it can still arrive.

This caching contract applies wherever Ory downloads a client's key set, not only to this flow.