> For the complete documentation index, see [llms.txt](https://the-wishlist-documentation.gitbook.io/the-wishlist-documentation-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://the-wishlist-documentation.gitbook.io/the-wishlist-documentation-docs/rest-api-reference/authentication-best-practice.md).

# Authentication Best Practice

## API Client Credentials and Bearer Token Security Best Practices

The client credentials flow is appropriate for **confidential, server-to-server integrations**.

The client secret is the long-lived root credential. The bearer token is a short-lived credential generated from it.

> **Neither the client secret nor a client-credentials bearer token should ever be delivered to browser JavaScript, a PWA, mobile application, or any other publicly inspectable client.**

Anyone possessing a bearer token can use it without proving that they are the original recipient.

### Recommended Customer Requirements

<table data-search="false"><thead><tr><th>Area</th><th>Best practice</th></tr></thead><tbody><tr><td>Where requests originate</td><td>Secure backend, server, Lambda, integration platform, or protected job runner only</td></tr><tr><td>Secret storage</td><td>Dedicated secrets manager or protected CI/CD secret store</td></tr><tr><td>Token storage</td><td>Server memory or protected encrypted cache</td></tr><tr><td>Browser exposure</td><td>Never expose either credential to a browser</td></tr><tr><td>Logging</td><td>Never log request bodies, <code>Authorization</code> headers, secrets, or tokens</td></tr><tr><td>Renewal</td><td>Obtain a new token automatically before expiry</td></tr><tr><td>Isolation</td><td>Use separate credentials for production, staging, and each integration</td></tr><tr><td>Compromise response</td><td>If you believe a credential may have been compromised, iImmediately contact us to revoke or rotate the client secret and invalidate outstanding access</td></tr></tbody></table>

### 1. Keep the Client Secret Server-Side

The client secret must not appear in:

* Frontend JavaScript
* Shopify theme code
* Browser network requests
* Mobile applications
* Public or private Git repositories
* Docker images
* Shared documentation
* Slack messages
* Email
* Support tickets
* Shared Postman collections
* Unprotected environment or configuration files

A dedicated secrets manager is preferable, such as:

* AWS Secrets Manager
* Azure Key Vault
* Google Secret Manager
* HashiCorp Vault

Only the application that requests the token, and a limited number of authorised administrators, should be permitted to access the secret.

Environment variables are common and may be acceptable in simpler deployments, but they can be exposed through diagnostic output, process environments, or logs. Runtime retrieval from a secrets manager is more secure.

### 2. Keep the Bearer Token Server-Side

The access token should normally be cached by the backend and reused until shortly before expiry.

```
Read client secret from secrets manager
        ↓
Request bearer token
        ↓
Cache token in server memory
        ↓
Use token for API calls
        ↓
Renew shortly before expiry
```

The bearer token should not be returned to a website or stored in:

* `localStorage`
* `sessionStorage`
* Browser cookies
* HTML
* Frontend configuration
* Analytics platforms
* Error-reporting systems

If multiple application servers need to share a token, use an encrypted and access-controlled cache rather than a plaintext configuration file or unrestricted database table.

### 3. Never Log Either Credential

Customers should configure their application logging, API gateways, reverse proxies, and monitoring systems to redact:

```
Authorization
client_secret
access_token
refresh_token
```

It is safe to log metadata such as:

```
Token requested successfully
Client ID: abc123
Token expires at: 2026-07-13T12:30:00+10:00
Token request status: 200
```

Customers should not log:

```
Token: eyJhbGciOi...
Secret: Gxxxxxxxx...
Full token endpoint request body
Full Authorization header
```

### 4. Use HTTPS and Validate the Certificate

The token request must use HTTPS, and the client must validate the TLS certificate.

Customers should never disable certificate validation using options such as:

```bash
curl --insecure
curl -k
```

TLS protects the client secret and token while they are being transmitted.

However, HTTPS does not protect credentials that are exposed through application logs, source code, browser JavaScript, or insecure storage.

### 5. Request a New Token Automatically

For the `client_credentials` flow, the customer normally does not need a refresh token.

Retain the client ID and client secret securely and use those credentials to obtain another access token when required.

```javascript
async function getAccessToken() {
    const renewalMarginSeconds = 60;

    if (
        cachedToken &&
        currentTimeInSeconds() <
            tokenExpiresAt - renewalMarginSeconds
    ) {
        return cachedToken;
    }

    const clientSecret = await secretManager.get(
        "twc-client-secret"
    );

    const response = await requestToken({
        clientId: CLIENT_ID,
        clientSecret,
        grantType: "client_credentials"
    });

    cachedToken = response.access_token;
    tokenExpiresAt =
        currentTimeInSeconds() + response.expires_in;

    return cachedToken;
}
```

The integration should:

1. Check whether a cached access token exists.
2. Reuse the token while it remains valid.
3. Request a replacement shortly before expiry.
4. Discard the cached token if an API request returns `401 invalid_token`.
5. Request a new token.
6. Retry the original API request once.

The integration should not request a new token before every API call.

### 6. Support Secret Rotation and Incident Response

#### Security Requirements

The client credentials flow must only be used by a secure server-side application.

The client secret and generated bearer token must not be included in browser JavaScript, mobile applications, frontend configuration, source control, logs, analytics, shared Postman collections, or publicly accessible files.

Store the client secret in a secure secrets-management system and restrict access to the application components that require it. Retrieve and use the secret at runtime to obtain short-lived access tokens.

Cache the access token securely on the server and reuse it until shortly before it expires. Do not request a new token for every API call. When the token expires, use the client ID and client secret to request another token automatically.

Never include the client secret or bearer token in a URL or query string. Never log the token request body, token response, client secret, or API `Authorization` header.

***If you suspect a credential was compromised or exposed, contact The Wishlist Company immediately. We will revoke and replace the client secret.***

### 7. Example Token Request

#### Endpoint

```
POST /auth/realms/{tenantid}/protocol/openid-connect/token
```

#### Base URL

```
https://auth.au-aws.thewishlist.io
```

#### Example Using HTTP Basic Authentication

```bash
curl --fail --silent --show-error \
  --request POST \
  --url 'https://auth.au-aws.thewishlist.io/auth/realms/mystore/protocol/openid-connect/token' \
  --user "${CLIENT_ID}:${CLIENT_SECRET}" \
  --header 'Content-Type: application/x-www-form-urlencoded' \
  --data-urlencode 'grant_type=client_credentials'
```

#### Example Using Form Parameters

```bash
curl --fail --silent --show-error \
  --request POST \
  --url 'https://auth.au-aws.thewishlist.io/auth/realms/mystore/protocol/openid-connect/token' \
  --header 'Content-Type: application/x-www-form-urlencoded' \
  --data-urlencode 'grant_type=client_credentials' \
  --data-urlencode "client_id=${CLIENT_ID}" \
  --data-urlencode "client_secret=${CLIENT_SECRET}"
```

#### Example Response

```json
{
  "access_token": "eyJhbGciOi...",
  "expires_in": 3600,
  "refresh_expires_in": 0,
  "token_type": "Bearer",
  "not-before-policy": 0,
  "scope": "profile email"
}
```

The customer should use the value of `expires_in` to determine when to request another token.

The `access_token` value must not be logged, displayed to users, or exposed to a browser.
