# OAuth2 Client Credentials Flow for Workloads

> Octelium documentation. Canonical page: <https://octelium.com/docs/octelium/latest/user/clientless/oauth2>.

`WORKLOAD` *Users* (read more about *User* management [here](https://octelium.com/docs/octelium/latest/management/core/user.md)) can use the standard OAuth2 client credentials authentication flow (read more [here](https://datatracker.ietf.org/doc/html/rfc6749#section-4.4)) to authenticate themselves to the *Cluster* and start accessing its publicly exposed HTTP-based *Services* (read more about publicly exposed BeyondCorp *Services* [here](https://octelium.com/docs/octelium/latest/management/core/service/clientless.md)), such as HTTP/gRPC-based APIs or Kubernetes clusters, exactly like any protected public SaaS HTTP-based resource without having to install clients on their hosts, use special SDKs or even having to be aware of the *Cluster*'s existence at all. This allows you to write applications in any programming language and use standard OAuth2 libraries to securely access all the *Cluster*'s publicly exposed *Services* via a single identity and credential.

> **Note:**
>
> In addition to using OAuth2 client credentials flow *Credentials*, you can also generate an access token *Credential* and use it directly as a bearer token to access publicly exposed *Services*. Read more [here](https://octelium.com/docs/octelium/latest/management/core/credential.md#access-tokens).

It simply works as follows:

1. Obtain an OAuth2 client credential *Credential* (read more [here](https://octelium.com/docs/octelium/latest/management/core/credential.md#oauth2-client-credentials)) as follows:

```bash
octeliumctl create cred --user root --type oauth2 my-oauth-cred

# Here is the command output
Client ID: nz7y-hagw
Client Secret: AQpA691Z...
```

Now you can use the client credentials, for example, within your application to authenticate to the *Cluster*'s OAuth2 token endpoint located at the URL `https://<DOMAIN>/oauth2/token`. For example, let's assume we want to access the *Service* `my-api` and obtain the access token that can then be used to access your *Services* via the standard bearer authentication (i.e. via the HTTP request header `Authorization: Bearer <ACCESS_TOKEN>`).

Using shell and curl, this can be simply done as follows:

```bash
curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d 'client_id=<CLIENT_ID>&client_secret=<CLIENT_SECRET>&grant_type=client_credentials' 'https://<DOMAIN>/oauth2/token'

# Now we use the obtained access token from the above request to access the Service
curl -H "Authorization: Bearer <ACCESS_TOKEN>" https://my-api.<DOMAIN>
```

Here is an equivalent example in Golang:

```go
package main

import (
	"context"
	"fmt"
	"io"

	"golang.org/x/oauth2/clientcredentials"
)

func main() {
	if err := doMain(context.Background()); err != nil {
		panic(err)
	}
}

func doMain(ctx context.Context) error {

	// Your Cluster domain
	domain := "example.com"
	// Configuration for the client credentials flow
	config := clientcredentials.Config{
		ClientID:     "y56y-9ru3",
		ClientSecret: "AQpAt200_CHp2S9G...",
		TokenURL:     fmt.Sprintf("https://%s/oauth2/token", domain),
	}

	// Now we obtain an access token from our OAuth2 client credentials
	_, err := config.Token(ctx)
	if err != nil {
		return err
	}

	// Now we access our Service "my-api"
	client := config.Client(ctx)
	resp, err := client.Get(fmt.Sprintf("https://my-api.%s", domain))
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	bodyBytes, err := io.ReadAll(resp.Body)
	if err != nil {
		return err
	}

	fmt.Printf("Response: %s\n", string(bodyBytes))

	return nil
}
```

And here is an equivalent example in Typescript:

```typescript
import axios from "axios";
import { URLSearchParams } from "url";

const clientId = "y56y-9ru3";
const clientSecret = "AQpAyteKC0UPDI1U...";
const domain = "<DOMAIN>";

const getAccessToken = async (): Promise<string> => {
  try {
    const params = new URLSearchParams();
    params.append("grant_type", "client_credentials");
    params.append("client_id", clientId);
    params.append("client_secret", clientSecret);

    const response = await axios.post(
      `https://${domain}/oauth2/token`,
      params,
      {
        headers: {
          "Content-Type": "application/x-www-form-urlencoded",
        },
      }
    );

    return response.data.access_token;
  } catch (error) {
    console.error("Error fetching access token:", error);
    throw error;
  }
};

const accessProtectedResource = async (token: string) => {
  try {
    const response = await axios.get(`https://my-api.${domain}`, {
      headers: {
        Authorization: `Bearer ${token}`,
      },
    });
    console.log(response)
  } catch (error) {
    throw error;
  }
};

const main = async () => {
  const accessToken = await getAccessToken();
  await accessProtectedResource(accessToken);
};

main();

```

> **Note:**
>
> You can optionally use Octelium scopes as OAuth2 scopes. Read more about scopes [here](https://octelium.com/docs/octelium/latest/user/scope.md).

There are standard libraries in almost all the major programming languages to use the OAuth2 client credentials flow and obtain the access token. Some examples are:

- [Rust](https://docs.rs/oauth2/latest/oauth2/index.html#client-credentials-grant)
- [Golang](https://pkg.go.dev/golang.org/x/oauth2/clientcredentials)
- [Python](https://oauthlib.readthedocs.io/en/latest/oauth2/grants/credentials.html)
- [Node.js](https://www.npmjs.com/package/simple-oauth2#client-credentials-grant)

> **Note:**
>
> You can also use the issued access token in the `X-Octelium-Auth: <ACCESS_TOKEN>` header instead of using it in the typical `Authorization: Bearer <ACCESS_TOKEN>` header.

## Workload Identity Federation

You can also use OpenID Connect JWT-based assertions in OAuth2 client credentials authentication, as defined in [RFC 7523](https://datatracker.ietf.org/doc/html/rfc7523) to avoid having to issue OAuth2 client credential *Credentials*. This allows `WORKLOAD` Users to authenticate themselves using OIDC identity tokens issued by the identity provider hosting the workload ( e.g. Azure, GitHub Actions, Kubernetes clusters, SPIFFE, etc...) and automatically access the Cluster's HTTP-based resources in a "secretless" way that does not require issuing and distributing OAuth2 client credential *Credentials* as shown above. Here is an example in Golang but you can apply the same flow in any language:

```go
package main

import (
	"context"
	"fmt"
	"io"
	"net/http"

	"golang.org/x/oauth2/clientcredentials"
)

func main() {
	ctx := context.Background()

	domain := "example.com"

	// 1. Configure the OAuth2 Client Credentials exchange
	config := clientcredentials.Config{
		TokenURL: fmt.Sprintf("https://%s/oauth2/token", domain),
		EndpointParams: map[string][]string{
			"client_assertion_type": {"urn:ietf:params:oauth:client-assertion-type:jwt-bearer"},
			"client_assertion":      {"<YOUR_JWT>"},
		},
	}

	// 2. Create an HTTP Client with your OAuth2 client credentials configuration
	httpClient := config.Client(ctx)

	// 3. Access the "demo-nginx" Service
	resp, err := httpClient.Get(fmt.Sprintf("https://demo-nginx.%s/", domain))
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()

	body, _ := io.ReadAll(resp.Body)
	if resp.StatusCode != http.StatusOK {
		panic(err)
	}

	fmt.Printf("Response: %s\n", string(body))
}
```
