# How to Authenticate an OpenAI Plugin: Declarative Policies and OAuth PKCE Implementation

> Learn how to authenticate an OpenAI plugin. This guide covers declarative policies and OAuth PKCE implementation for secure access token management.

- Repository: [OpenAI/plugins](https://github.com/openai/plugins)
- Tags: how-to-guide
- Published: 2026-06-15

---

**OpenAI plugins use a declarative authentication model where the marketplace policy defines when authentication occurs (ON_INSTALL or ON_USE), the plugin manifest describes the required OAuth scopes and metadata, and the plugin backend implements the OAuth 2.0 PKCE flow to securely obtain and store access tokens.**

The `openai/plugins` repository demonstrates that authenticating an OpenAI plugin requires coordinating two distinct layers: a **declarative policy** that tells the Codex runtime when to trigger authentication, and an **imperative implementation** that handles the actual OAuth exchange. Understanding how to configure the marketplace entry and implement the PKCE flow in your plugin backend is essential for securing third-party API access.

## Configuring the Marketplace Authentication Policy

Authentication timing is controlled declaratively in [`.agents/plugins/marketplace.json`](https://github.com/openai/plugins/blob/main/.agents/plugins/marketplace.json). Each plugin entry contains a `policy.authentication` field that accepts two values:

- **ON_INSTALL**: Authentication is performed **once** immediately after the user installs the plugin. The plugin stores tokens for subsequent use.
- **ON_USE**: Authentication is performed **each time** the user invokes a skill that requires credentials. This is useful for short-lived or per-action authorization.

According to the specification in [`.agents/skills/plugin-creator/references/plugin-json-spec.md`](https://github.com/openai/plugins/blob/main/.agents/skills/plugin-creator/references/plugin-json-spec.md) (lines 54-56), the default value is `ON_INSTALL` when the field is omitted.

The Zoom plugin provides a concrete example in [`.agents/plugins/marketplace.json`](https://github.com/openai/plugins/blob/main/.agents/plugins/marketplace.json) (lines 15-16):

```json
{
  "name": "zoom",
  "source": {
    "source": "local",
    "path": "./plugins/zoom"
  },
  "policy": {
    "installation": "AVAILABLE",
    "authentication": "ON_INSTALL"
  },
  "category": "Communication"
}

```

## Understanding the Plugin Manifest Role

While the [`plugin.json`](https://github.com/openai/plugins/blob/main/plugin.json) manifest does not contain authentication settings directly, it defines the scopes, URLs, and UI metadata required for the OAuth flow. Located at `plugins/<name>/.codex-plugin/plugin.json`, this file describes the external service interface.

For example, [`plugins/zoom/.codex-plugin/plugin.json`](https://github.com/openai/plugins/blob/main/plugins/zoom/.codex-plugin/plugin.json) defines:

```json
{
  "name": "zoom",
  "version": "1.0.2",
  "keywords": [ "zoom", "oauth", "meeting-sdk" ],
  "interface": {
    "capabilities": [ "Interactive", "Read", "Write" ],
    "websiteURL": "https://developers.zoom.us/",
    "privacyPolicyURL": "https://www.zoom.com/en/trust/privacy/",
    "termsOfServiceURL": "https://www.zoom.com/en/trust/terms/"
  }
}

```

## Implementing the OAuth 2.0 PKCE Flow

Most OpenAI plugins use the **OAuth 2.0 PKCE** (Proof Key for Code Exchange) flow to obtain access tokens securely. As implemented in the Zoom reference at [`plugins/zoom/skills/zoom-apps-sdk/references/oauth.md`](https://github.com/openai/plugins/blob/main/plugins/zoom/skills/zoom-apps-sdk/references/oauth.md), the flow involves generating a PKCE pair, redirecting to the provider, exchanging the authorization code, and securely storing the resulting tokens.

### Generate PKCE Credentials

First, generate the PKCE verifier and challenge. From [`plugins/zoom/skills/zoom-apps-sdk/references/oauth.md`](https://github.com/openai/plugins/blob/main/plugins/zoom/skills/zoom-apps-sdk/references/oauth.md) (lines 17-28):

```javascript
const crypto = require('crypto');

// Generate PKCE pair
const verifier = crypto.randomBytes(32).toString('hex');
const challenge = crypto.createHash('sha256')
  .update(verifier)
  .digest('base64url');

```

Store the **verifier** server-side (typically in session or temporary storage) and send the **challenge** to the authorization endpoint.

### Handle Web-Based Authorization

For server-side implementations, handle the redirect from the OAuth provider. From [`plugins/zoom/skills/zoom-apps-sdk/references/oauth.md`](https://github.com/openai/plugins/blob/main/plugins/zoom/skills/zoom-apps-sdk/references/oauth.md) (lines 55-92):

```javascript
app.get('/auth', async (req, res) => {
  const { code, state } = req.query;
  // Validate CSRF state …
  const tokenResponse = await axios.post('https://zoom.us/oauth/token', null, {
    params: {
      grant_type: 'authorization_code',
      code,
      redirect_uri: process.env.ZOOM_APP_REDIRECT_URI,
      code_verifier: req.session.codeVerifier
    },
    headers: {
      Authorization: 'Basic ' + Buffer.from(
        `${process.env.ZOOM_APP_CLIENT_ID}:${process.env.ZOOM_APP_CLIENT_SECRET}`
      ).toString('base64')
    }
  });
  // Store tokens, obtain deeplink, redirect user …
});

```

### Implement In-Client OAuth (Best UX)

For the optimal user experience, handle OAuth within the client application itself. From [`plugins/zoom/skills/zoom-apps-sdk/references/oauth.md`](https://github.com/openai/plugins/blob/main/plugins/zoom/skills/zoom-apps-sdk/references/oauth.md) (lines 99-112):

```javascript
// Front‑end
const { codeChallenge, state } = await fetch('/api/auth/challenge').then(r => r.json());

zoomSdk.addEventListener('onAuthorized', async (event) => {
  await fetch('/api/auth/token', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ code: event.code, state: event.state })
  });
});

await zoomSdk.authorize({ codeChallenge, state });

```

## Token Storage and Refresh Patterns

After obtaining tokens, you must store them securely and handle expiration. The Zoom reference outlines two primary patterns in [`plugins/zoom/skills/zoom-apps-sdk/references/oauth.md`](https://github.com/openai/plugins/blob/main/plugins/zoom/skills/zoom-apps-sdk/references/oauth.md) (lines 91-95):

- **Redis**: Use for multi-instance production deployments where multiple servers need access to the same token state.
- **Session Cookie**: Suitable for single-server applications with simpler deployment requirements.

**Refresh tokens are single-use**. As noted in lines 64-66 of the OAuth reference, each time you exchange a refresh token for a new access token, the provider issues a new refresh token that must replace the old one in storage.

## Complete Integration Example

When a user installs a plugin with `"authentication": "ON_INSTALL"`:

1. The Codex UI prompts the user to authorize the external service.
2. Your backend executes the PKCE flow, stores the tokens (e.g., in Redis), and returns success.
3. Subsequent skill calls retrieve the stored token to call the third-party API.

For `"authentication": "ON_USE"`, step 1 is deferred until the first skill invocation requiring credentials, allowing per-action authorization.

## Summary

- **Declarative control**: Set `policy.authentication` to `ON_INSTALL` or `ON_USE` in [`.agents/plugins/marketplace.json`](https://github.com/openai/plugins/blob/main/.agents/plugins/marketplace.json) to define when authentication occurs.
- **Manifest metadata**: Define OAuth scopes and service URLs in `plugins/<name>/.codex-plugin/plugin.json` to describe the external service interface.
- **PKCE implementation**: Generate a code verifier and challenge, exchange the authorization code for tokens, and validate CSRF state to secure the flow.
- **Token lifecycle**: Store tokens securely (Redis for scale, sessions for simplicity) and handle single-use refresh tokens properly.
- **UX optimization**: Implement in-client OAuth when possible to avoid redirecting users away from the chat interface.

## Frequently Asked Questions

### What is the difference between ON_INSTALL and ON_USE authentication policies?

**ON_INSTALL** triggers authentication immediately after the user installs the plugin, storing tokens for all future skill invocations. **ON_USE** defers authentication until a skill actually requires credentials, which is useful for sensitive operations or when tokens should not persist between sessions. According to the plugin specification, `ON_INSTALL` is the default behavior when no policy is specified.

### Does the plugin.json manifest contain OAuth client secrets?

No. The [`plugin.json`](https://github.com/openai/plugins/blob/main/plugin.json) manifest located at `plugins/<name>/.codex-plugin/plugin.json` contains only public metadata such as the **privacy policy URL**, **terms of service URL**, and **capabilities**. Client secrets and OAuth implementation details remain in your backend environment variables and server code, never in the manifest that ships with the plugin.

### How do I handle token refresh in an OpenAI plugin?

Store the **refresh token** securely alongside the access token. When the access token expires, send the refresh token to the provider's token endpoint. According to the Zoom OAuth reference in [`plugins/zoom/skills/zoom-apps-sdk/references/oauth.md`](https://github.com/openai/plugins/blob/main/plugins/zoom/skills/zoom-apps-sdk/references/oauth.md), refresh tokens are **single-use**, meaning each refresh request returns both a new access token and a new refresh token that must replace the previous one in your storage system.

### Can I use authentication methods other than OAuth 2.0 PKCE?

While the OpenAI plugins repository demonstrates **OAuth 2.0 PKCE** as the standard pattern (used by the Zoom plugin), the architecture supports any authentication flow you implement in your backend. The marketplace policy (`ON_INSTALL` or `ON_USE`) is agnostic to the specific protocol, allowing you to implement API keys, JWT tokens, or custom authentication schemes as long as your backend handles the credential exchange and storage securely.