ON_INSTALL vs ON_USE Authentication for OpenAI Plugins: Lifecycle Guide

ON_INSTALL authentication executes once during plugin installation to capture user consent and store a persistent OAuth token, while ON_USE authentication activates on every subsequent API call to retrieve and apply that stored token without additional user interaction.

Understanding the difference between ON_INSTALL and ON_USE authentication is critical when building secure OpenAI plugins in the openai/plugins repository. These two lifecycles determine when user consent is obtained, how access tokens are managed, and whether your plugin requires interactive authentication or can operate as a background service. The authentication flow you choose directly impacts the user experience and security model defined in your plugin manifest.

Understanding the Two Authentication Lifecycles

ON_INSTALL: The One-Time Setup Phase

ON_INSTALL authentication runs exactly once—when a user first installs your plugin from the marketplace or re-authorizes it. According to the plugin manifest schema in .agents/skills/plugin-creator/references/plugin-json-spec.md, this phase triggers an OAuth flow that returns a user-bound access token along with a refresh token. The platform stores these credentials alongside the user's install record, making them available for subsequent calls without requiring the user to re-authenticate.

ON_USE: The Recurring Runtime Phase

ON_USE authentication activates every time the plugin receives a request after installation. As documented in plugins/zoom/skills/rest-api/references/authentication.md, this phase retrieves the previously stored token (or generates a service-to-service token) and attaches it to API requests. This lifecycle operates silently in the background, enabling seamless plugin functionality without interrupting the user experience.

How ON_INSTALL Configures the OAuth Flow

The ON_INSTALL lifecycle is defined in your plugin.json manifest through the auth object. When oninstall is set to true, the OpenAI platform initiates the OAuth consent screen during installation.

{
  "schema_version": "v1",
  "name_for_human": "My Awesome Plugin",
  "auth": {
    "type": "oauth",
    "oninstall": true,
    "client_id": "YOUR_CLIENT_ID",
    "client_secret": "YOUR_CLIENT_SECRET",
    "scopes": ["read", "write"]
  }
}

As shown in the Zoom plugin example at plugins/zoom/skills/oauth/SKILL.md, this configuration triggers the standard OAuth 2.0 authorization code flow. The resulting access_token and refresh_token are encrypted and tied to the specific user's installation record, creating a persistent authentication context that survives across chat sessions.

How ON_USE Retrieves Tokens at Runtime

During the ON_USE phase, your plugin backend must retrieve the stored credentials and apply them to outgoing API requests. The following Python implementation demonstrates the token retrieval pattern used in production plugins:

import requests

def call_plugin_api(user_id, endpoint, payload):
    # Retrieve the stored token from the install record

    token = get_stored_token(user_id)
    
    headers = {"Authorization": f"Bearer {token}"}
    resp = requests.post(endpoint, json=payload, headers=headers)
    
    # Handle token expiration via refresh flow

    if resp.status_code == 401:
        token = refresh_token(user_id)
        headers["Authorization"] = f"Bearer {token}"
        resp = requests.post(endpoint, json=payload, headers=headers)
    
    return resp.json()

This runtime logic executes on every plugin invocation, ensuring the active token is always applied to API calls without requiring user interaction.

Service-to-Service Authentication Without ON_INSTALL

Not all plugins require user-scoped permissions. For backend services or administrative functions, you can configure ON_USE authentication without an ON_INSTALL phase by setting oninstall to false and implementing client-credentials flow:

{
  "auth": {
    "type": "oauth",
    "oninstall": false,
    "client_id": "SERVER_CLIENT_ID",
    "client_secret": "SERVER_CLIENT_SECRET",
    "scopes": ["admin"]
  }
}

In this configuration, your plugin exchanges its client credentials for a short-lived access token during each request cycle:

def get_service_token():
    resp = requests.post(
        "https://auth.example.com/oauth/token",
        data={"grant_type": "client_credentials"},
        auth=("SERVER_CLIENT_ID", "SERVER_CLIENT_SECRET")
    )
    return resp.json()["access_token"]

This approach, documented in plugins/zoom/skills/rest-api/references/authentication.md, is ideal for data-ingestion pipelines or system-to-system integrations that operate independently of individual user contexts.

Summary

  • ON_INSTALL runs once during plugin installation to execute OAuth flows and persist user-bound tokens in the platform's secure storage.
  • ON_USE runs on every API call to retrieve stored tokens or generate service credentials, requiring no user interaction.
  • The oninstall boolean flag in .agents/skills/plugin-creator/references/plugin-json-spec.md determines whether your plugin requires upfront user consent or operates via service-to-service authentication.
  • Production implementations must handle token refresh during the ON_USE phase to maintain uninterrupted service when access tokens expire.

Frequently Asked Questions

What happens if the ON_USE token expires during a request?

When an ON_USE token expires, your plugin should detect the 401 Unauthorized response and execute the refresh token flow using the credentials stored during the ON_INSTALL phase. The refresh_token obtained during installation is used to generate a new access_token without requiring the user to re-authenticate, as demonstrated in the token refresh logic within the runtime handler.

Can a plugin use ON_USE without implementing ON_INSTALL?

Yes, plugins can operate entirely with ON_USE authentication by setting "oninstall": false in the manifest and implementing client-credentials OAuth or API key authentication. This configuration is common for service-to-service integrations where individual user context is unnecessary, allowing the plugin to authenticate using static credentials or short-lived tokens fetched at request time.

Where are ON_INSTALL tokens stored in the OpenAI platform?

According to the authentication documentation in plugins/zoom/skills/oauth/SKILL.md, tokens obtained during the ON_INSTALL phase are encrypted and stored alongside the user's plugin installation record in the platform's secure credential vault. These tokens are isolated per user and plugin instance, ensuring that subsequent ON_USE calls retrieve the correct authentication context without exposing credentials to the plugin code itself.

How do I configure the authentication type in the plugin manifest?

The authentication type is defined in the auth object within your plugin.json file located at plugins/[your-plugin]/.codex-plugin/plugin.json. Set "type": "oauth" and "oninstall": true for user-consent flows, or "oninstall": false for service-only authentication. The complete schema definition is available in .agents/skills/plugin-creator/references/plugin-json-spec.md, which validates supported authentication methods including OAuth 2.0, server-to-server tokens, and JWT.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →