---
title: Authentication and authorization
slug: cms-13/api/VPNg-authentication-and-authorization
createdAt: 2026-07-30T20:23:01.528Z
---

This article covers how to configure OAuth clients, obtain access tokens, and manage signing credentials for production and local development environments for Content Management System (CMS 13).

For an overview of the REST API and its endpoints, see [Introduction to the CMS REST API](docId\:MM1lPFKxUy4D2kTAk9-T3).

## Configure OAuth clients

OAuth clients authenticate external applications and services that interact with the REST API. Each client requires a `ClientId` and `ClientSecret` pair.

Opti ID handles interactive user authentication for the CMS editorial interface. OAuth clients configured through `CmsServiceOauthOptions` authenticate service-to-service API calls.

### Configure clients in `appsettings.json`

Define OAuth clients in `appsettings.json` under the `Optimizely:Cms:Service:Oauth:Clients` section.

```json
{
  "Optimizely": {
    "Cms": {
      "Service": {
        "Oauth": {
          "Clients": [
            {
              "ClientId": "YOUR_CLIENT_ID",
              "ClientSecret": "YOUR_CLIENT_SECRET"
            }
          ]
        }
      }
    }
  }
}
```

Replace `YOUR_CLIENT_ID` and `YOUR_CLIENT_SECRET` with values appropriate for your application.

To verify the client configuration, request a token as described in the following sections. A `401` response indicates the client credentials do not match the configured values.

### Configure clients with environment variables on DXP

On Optimizely Digital Experience Platform (DXP), configure clients securely using environment variables through the DXP App Settings. Use the standard double-underscore separator convention to map to the JSON hierarchy.

:::Iframe{code="<Anchor label=&#x22;App Settings&#x22; target=&#x22;_blank&#x22; href=&#x22;https://docs.developers.optimizely.com/digital-experience-platform/docs/manage-app-settings&#x22;>" iframeHeight="0"}

:::

The following example shows the environment variable pattern for the first client (index `0`):

```javascript
Optimizely__Cms__Service__Oauth__Clients__0__ClientId=YOUR_CLIENT_ID
Optimizely__Cms__Service__Oauth__Clients__0__ClientSecret=YOUR_CLIENT_SECRET
```

Add additional clients by incrementing the index (for example, `__1__ClientId`, `__1__ClientSecret`).

:::BlockQuote
🚧 ImportantStore client secrets securely. Do not commit secrets to source control or expose them in client-side code.
:::

### Configure clients in code for local development

For local testing, configure clients programmatically in `Startup.cs`:

```csharp
if (_webHostingEnvironment.IsDevelopment())
{
    services.Configure<CmsServiceOauthOptions>(o =>
    {
        o.AddDevelopmentSigningCredentials();
        o.Clients.Add(new OauthClient
        {
            ClientId = "localtest",
            ClientSecret = "localtest"
        });
    });
}
```

## Configure signing credentials

Signing credentials secure tokens that the OAuth endpoint issues. The configuration differs between DXP-hosted environments and local development.

### DXP environments

On DXP, `services.AddCmsCloudPlatformSupport()` configures signing credentials through a secure certificate. This method is required for DXP deployment and handles certificate management without manual configuration. See Deploy an existing CMS site.

:::Iframe{code="<Anchor label=&#x22;Deploy an existing CMS site&#x22; target=&#x22;_blank&#x22; href=&#x22;https://docs.developers.optimizely.com/digital-experience-platform/docs/deploying-an-existing-cms-site&#x22;>"}

:::

### Local development

For local development, call `AddDevelopmentSigningCredentials()` on `CmsServiceOauthOptions`. This method generates in-memory signing credentials on each application startup. Signing credentials

```csharp
if (_webHostingEnvironment.IsDevelopment())
{
    services.Configure<CmsServiceOauthOptions>(o =>
        o.AddDevelopmentSigningCredentials());
}
```

:::BlockQuote
🚧 ImportantDevelopment signing credentials are regenerated on every startup, stored only in memory, and must not be used in production environments.
:::

## Request an access token

The OAuth token endpoint supports the `client_credentials` grant type and issues short-lived tokens. To request a token, send a `POST` request to the token endpoint with your client credentials.

### Token request

### JSON request body

```javascript
POST /_cms/v1/oauth/token
Content-Type: application/json

{
  "grant_type": "client_credentials",
  "client_id": "CLIENT_ID",
  "client_secret": "CLIENT_SECRET"
}
```

### URL-encoded request body

```javascript
POST /_cms/v1/oauth/token
Content-Type: application/x-www-form-urlencoded
Authorization: Basic Base64(GetBytes(client_id:client_secret))

grant_type=client_credentials
```

### Token response

A successful request returns a JSON response containing the access token.

```json
{
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "token_type": "Bearer",
  "expires_in": 300
}
```

The `expires_in` value indicates the token lifetime in seconds. Request a new token before the current token expires.

## Authenticate API requests

Include the access token in the `Authorization` header of each API request.

```javascript
GET /_cms/v1/contenttypes
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...
```

## Troubleshoot

### `401` Unauthorized response

Verify that the ClientId and ClientSecret match the values configured in appsettings.json or environment variables. Verify the token has not expired.

### Token endpoint returns `404`

Confirm that the application calls `AddCms()` in the service configuration. This method registers the REST API v1 and OAuth endpoints.

### Signing credential errors in local development

Ensure `AddDevelopmentSigningCredentials()` is called in the development configuration block. This method must execute before the application attempts to issue tokens.

### Environment variable configuration not applying on DXP

Verify the double-underscore separator pattern. For example, use `Optimizely__Cms__Service__Oauth__Clients__0__ClientId` rather than colons or single underscores.
