OpenAI Plugin Authentication Types: Three Methods to Secure API Endpoints
OpenAI plugins support three distinct authentication models—none, service_http, and user_http—defined in the plugin manifest to control how credentials are obtained and injected into API requests.
The openai/plugins repository provides a standardized framework for securing plugin endpoints. These plugin authentication options determine whether requests are sent publicly, signed with static service keys, or authorized via user-specific OAuth 2.0 flows.
The Three Plugin Authentication Models
OpenAI plugins declare their security requirements through the auth field in manifest.json. Each model dictates how the ChatGPT runtime handles credentials before forwarding requests to your backend.
None (Public Endpoints)
The none type indicates that the plugin exposes public endpoints requiring no authentication. The ChatGPT runtime forwards requests without adding any authorization headers.
Use this model for simple data sources, public information feeds, or demo plugins where data does not require protection. In manifest.json, configure it as:
{
"auth": {
"type": "none"
}
}
Service HTTP (API Keys)
The service_http type enables plugins to authenticate using static API keys stored in the plugin's configuration. According to the implementation in plugins/common/auth/service_http.py, the runtime injects the token into outgoing requests—typically as an Authorization: Bearer <token> header—without exposing the secret to end users.
This approach suits single-tenant APIs or internal backends where the plugin acts as a trusted client. The manifest configuration includes:
{
"auth": {
"type": "service_http",
"authorization_type": "Bearer",
"verification_tokens": {
"openai": "YOUR_OPENAI_VERIFICATION_TOKEN"
}
}
}
In your plugin code, retrieve the key from environment variables injected by the runtime:
import os
import requests
API_KEY = os.getenv("SERVICE_API_KEY")
def fetch_secure_data():
headers = {"Authorization": f"Bearer {API_KEY}"}
resp = requests.get("https://api.example.com/secure", headers=headers)
resp.raise_for_status()
return resp.json()
User HTTP (OAuth 2.0)
The user_http type implements OAuth 2.0 authentication for user-specific data access. As implemented in plugins/common/auth/oauth.py, the runtime orchestrates the consent flow: redirecting users to the provider's authorization page, exchanging codes for tokens, and automatically injecting access tokens into subsequent requests.
This model supports multiple grant types handled in plugins/common/auth/oauth_flow.py:
- Authorization Code (standard web flow)
- PKCE (for mobile and single-page applications)
- Client Credentials (service-to-service on behalf of the app)
- Device Code (for devices without browsers)
Use user_http when your plugin acts on behalf of a user, such as accessing calendars, email, or CRM systems. The manifest requires OAuth endpoint configuration:
{
"auth": {
"type": "user_http",
"authorization_type": "Bearer",
"oauth": {
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET",
"scopes": ["read", "write"],
"authorization_endpoint": "https://provider.com/oauth/authorize",
"token_endpoint": "https://provider.com/oauth/token"
}
}
}
The implementation follows standard OAuth patterns:
import os
import requests
from urllib.parse import urlencode
def build_consent_url():
params = {
"client_id": os.getenv("OAUTH_CLIENT_ID"),
"redirect_uri": "https://my-plugin.example.com/callback",
"response_type": "code",
"scope": "read write",
"state": "random_csrf_token"
}
return f"https://provider.com/oauth/authorize?{urlencode(params)}"
def exchange_code_for_token(code):
data = {
"client_id": os.getenv("OAUTH_CLIENT_ID"),
"client_secret": os.getenv("OAUTH_CLIENT_SECRET"),
"grant_type": "authorization_code",
"code": code,
"redirect_uri": "https://my-plugin.example.com/callback"
}
resp = requests.post("https://provider.com/oauth/token", data=data)
resp.raise_for_status()
return resp.json()["access_token"]
def fetch_user_data(access_token):
headers = {"Authorization": f"Bearer {access_token}"}
resp = requests.get("https://api.provider.com/me", headers=headers)
resp.raise_for_status()
return resp.json()
Core Authentication Implementation Files
The openai/plugins repository organizes authentication logic into specific modules that handle credential injection and token management according to the OpenAPI specification.
plugins/common/auth/service_http.py
This file contains the runtime implementation for the service_http model. It manages the injection of static service tokens into request headers and ensures that secrets configured in the plugin environment are never transmitted to the client.
plugins/common/auth/oauth.py
The core OAuth 2.0 logic resides here, handling authorization URL generation, token exchange with providers, and automatic refresh of expired tokens. This module manages the secure storage of user access tokens obtained through the consent flow.
plugins/common/auth/oauth_flow.py
Higher-level helpers in this file support the various OAuth grant types. It abstracts the differences between Authorization Code, PKCE, Client Credentials, and Device Code flows, providing a unified interface for the runtime to obtain user-specific credentials.
openapi.yaml
The OpenAPI specification includes security objects that map to the authentication type declared in manifest.json. These definitions ensure that the ChatGPT runtime knows which endpoints require authorization and what type of credentials to provide.
Summary
- OpenAI plugin authentication offers three models:
nonefor public data,service_httpfor static API keys, anduser_httpfor OAuth 2.0 user consent flows. - The
auth.typefield inmanifest.jsondeclares which model the plugin uses, determining how the ChatGPT runtime prepares requests. service_httpcredentials are stored in the plugin configuration and injected by the code inplugins/common/auth/service_http.py, keeping secrets hidden from users.user_httpsupports Authorization Code, PKCE, Client Credentials, and Device Code grants through the implementation inplugins/common/auth/oauth.pyandoauth_flow.py.- All authentication configurations must align with the
securitydefinitions inopenapi.yamlto ensure proper credential mapping.
Frequently Asked Questions
What is the difference between service_http and user_http authentication?
service_http uses static API keys associated with the plugin itself, not individual users. The runtime stores these secrets and injects them into every request. user_http implements OAuth 2.0 to obtain per-user access tokens, requiring explicit user consent through a provider's authorization page before the plugin can access their data.
Which OAuth 2.0 grant types does the OpenAI plugin system support?
According to the implementation in plugins/common/auth/oauth_flow.py, the platform supports Authorization Code for standard web flows, PKCE for mobile and browser-based clients, Client Credentials for service-to-service authentication, and Device Code for input-constrained devices. The manifest configuration determines which flow the runtime initiates.
Can I implement plugin authentication without requiring any credentials?
Yes. By setting "type": "none" in the manifest.json auth object, you create a plugin that accesses public endpoints without authentication. This is suitable for open data sources and demo applications where no sensitive information is transmitted.
How does the ChatGPT runtime handle token security?
For service_http, the runtime stores API keys in the plugin's backend environment, never exposing them to the frontend or end users. For user_http, access tokens obtained through OAuth flows are managed securely by the runtime and injected into requests server-side, with refresh logic handled automatically by plugins/common/auth/oauth.py to maintain sessions without storing long-lived credentials.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →