# How to Handle OAuth Authentication in Claude Plugins: A Complete MCP Server Implementation Guide

> Learn to handle OAuth authentication in Claude plugins with this MCP server implementation guide. Securely manage tokens and enable seamless API calls using the OS keychain.

- Repository: [Anthropic/claude-plugins-community](https://github.com/anthropics/claude-plugins-community)
- Tags: how-to-guide
- Published: 2026-09-13

---

**Claude plugins implement OAuth 2.0 by declaring auth requirements in the plugin manifest, launching browser-based consent through the Claude UI, handling callbacks on a local MCP server, and storing tokens securely in the OS keychain for automatic refresh and seamless injection into API calls.**

Claude plugins in the `anthropics/claude-plugins-community` repository communicate with external APIs through an MCP (Claude Code Micro-Connector Platform) server. When a plugin requires access to user data from third-party services like Gmail, Salesforce, or Stripe, it follows a standardized OAuth 2.0 pattern that remains zero-configuration for end users after the initial setup.

## Declaring OAuth Requirements in plugin.json

Every OAuth-enabled Claude plugin begins by declaring authentication requirements in its manifest. In [`.claude-plugin/plugin.json`](https://github.com/anthropics/claude-plugins-community/blob/main/.claude-plugin/plugin.json), developers specify the provider, required scopes, and redirect URI that the local MCP server will use to receive callbacks.

According to the community repository patterns found in [`quickdesign/.claude-plugin/plugin.json`](https://github.com/anthropics/claude-plugins-community/blob/main/quickdesign/.claude-plugin/plugin.json) and [`eli5/.claude-plugin/plugin.json`](https://github.com/anthropics/claude-plugins-community/blob/main/eli5/.claude-plugin/plugin.json), the manifest structure follows this format:

```json
{
  "name": "gmail-connector",
  "version": "1.0.0",
  "auth": {
    "provider": "google",
    "scopes": [
      "https://www.googleapis.com/auth/gmail.readonly",
      "https://www.googleapis.com/auth/gmail.send"
    ],
    "redirect_uri": "http://localhost:3000/oauth/callback"
  },
  "tools": [
    { "name": "list-threads", "handler": "listThreads.js" },
    { "name": "send-mail", "handler": "sendMail.js" }
  ]
}

```

The `auth` object signals to the Claude platform that this plugin requires OAuth authorization, while the `scopes` array defines the specific permissions the plugin requests from the identity provider.

## Launching the OAuth Consent Flow

Plugins initiate the OAuth flow through a gateway command registered in the skill implementation. As documented in [`quickdesign/skills/quickdesign/SKILL.md`](https://github.com/anthropics/claude-plugins-community/blob/main/quickdesign/skills/quickdesign/SKILL.md), developers typically implement a `/setup` command that generates and prints a clickable authorization URL.

When users execute this command, the Claude UI displays a URL like `https://accounts.google.com/o/oauth2/auth?...` and automatically opens the system browser to the provider's consent page. This pattern ensures the only required user interaction is the initial consent click—after authorization, Claude agents can invoke tools without further credential prompts.

## Handling OAuth Callbacks on the MCP Server

The MCP server runs a local HTTP endpoint—typically implemented with Express or Fastify—to receive the OAuth callback at the specified redirect URI. The callback handler extracts the authorization code and exchanges it for access and refresh tokens via the provider's token endpoint.

Here is a complete [`oauth-callback.js`](https://github.com/anthropics/claude-plugins-community/blob/main/oauth-callback.js) implementation as used in the community plugins:

```javascript
const express = require('express')
const axios = require('axios')
const keytar = require('keytar')

const router = express.Router()
router.get('/oauth/callback', async (req, res) => {
  const { code } = req.query
  try {
    const tokenResp = await axios.post('https://oauth2.googleapis.com/token', null, {
      params: {
        client_id: process.env.GOOGLE_CLIENT_ID,
        client_secret: process.env.GOOGLE_CLIENT_SECRET,
        code,
        grant_type: 'authorization_code',
        redirect_uri: 'http://localhost:3000/oauth/callback'
      }
    })
    const { access_token, refresh_token, expires_in } = tokenResp.data
    // Store securely
    await keytar.setPassword('claude-plugin', 'gmail-token', JSON.stringify({
      access_token,
      refresh_token,
      expiry: Date.now() + expires_in * 1000
    }))
    res.send('✅ OAuth successful – you can now use Gmail tools in Claude.')
  } catch (e) {
    console.error(e)
    res.status(500).send('❌ OAuth failed')
  }
})
module.exports = router

```

This route receives the `code` parameter from the OAuth provider, exchanges it for credentials, and immediately delegates to the secure storage layer.

## Secure Token Storage with OS Keychain

Claude plugins never store tokens as plaintext on the filesystem. Instead, they use the `keytar` library (or equivalent `keychain` bindings) to encrypt credentials in the operating system's native credential store—macOS Keychain, Windows Credential Manager, or Linux secret-service.

In the callback handler above, `keytar.setPassword()` serializes the token bundle as JSON and stores it under the service name `claude-plugin` with the account key `gmail-token`. This ensures that `access_token` and `refresh_token` values remain encrypted at the OS level and are accessible only to the user account that created them.

## Automatic Token Refresh

The MCP server includes transparent refresh logic in the token-vault module. Before making API calls, the server checks the `expiry` timestamp stored with the token. If the token expires within 60 seconds or has already expired, the server automatically calls the provider's refresh endpoint using the stored `refresh_token`, updates the encrypted storage with the new credentials, and proceeds with the request.

This interceptor pattern prevents authentication errors during tool execution and maintains seamless operation without requiring users to re-authenticate.

## Using OAuth Tokens in API Calls

Every tool defined in the MCP server retrieves stored tokens from the vault and includes them in the `Authorization: Bearer <token>` header. The following [`listThreads.js`](https://github.com/anthropics/claude-plugins-community/blob/main/listThreads.js) example demonstrates retrieving and using the token:

```javascript
const keytar = require('keytar')
const axios = require('axios')

module.exports = async () => {
  const tokenStr = await keytar.getPassword('claude-plugin', 'gmail-token')
  const { access_token, refresh_token, expiry } = JSON.parse(tokenStr)

  // Refresh if needed (simplified)
  if (Date.now() > expiry - 60000) {
    // ... call refresh endpoint and update keytar (omitted for brevity)
  }

  const resp = await axios.get(
    'https://gmail.googleapis.com/gmail/v1/users/me/threads',
    { headers: { Authorization: `Bearer ${access_token}` } }
  )
  return resp.data.threads.map(t => t.id)
}

```

The tool retrieves the encrypted token from the OS keychain, validates the expiry timestamp, and attaches the bearer token to the outgoing HTTP request.

## Summary

- **Declare OAuth in [`plugin.json`](https://github.com/anthropics/claude-plugins-community/blob/main/plugin.json)**: Specify provider, scopes, and redirect URI in the [`.claude-plugin/plugin.json`](https://github.com/anthropics/claude-plugins-community/blob/main/.claude-plugin/plugin.json) manifest, following examples in `quickdesign` and `eli5` plugins.
- **Launch via gateway command**: Use a `/setup` skill command to generate the authorization URL and open the browser, as documented in [`quickdesign/skills/quickdesign/SKILL.md`](https://github.com/anthropics/claude-plugins-community/blob/main/quickdesign/skills/quickdesign/SKILL.md).
- **Handle callbacks locally**: Implement an Express or Fastify route at `/oauth/callback` to exchange authorization codes for tokens.
- **Store in OS keychain**: Use `keytar` to encrypt tokens in macOS Keychain, Windows Credential Manager, or Linux secret-service.
- **Refresh automatically**: Check token expiry before API calls and transparently refresh using the provider's token endpoint when necessary.
- **Reference implementations**: Consult [`.claude-plugin/marketplace.json`](https://github.com/anthropics/claude-plugins-community/blob/main/.claude-plugin/marketplace.json) for the complete list of OAuth-enabled plugins and their configurations.

## Frequently Asked Questions

### How do I declare OAuth scopes in a Claude plugin?

Declare OAuth requirements in your [`.claude-plugin/plugin.json`](https://github.com/anthropics/claude-plugins-community/blob/main/.claude-plugin/plugin.json) file by adding an `auth` object containing the `provider` name, a `scopes` array of permission strings, and the `redirect_uri` where your MCP server listens. The manifest format supports major providers like Google, Salesforce, and Stripe, with each scope defining specific API access rights.

### Where are OAuth tokens stored in Claude plugins?

OAuth tokens are stored in the operating system's native credential store using libraries like `keytar` or `keychain`. This approach encrypts tokens in macOS Keychain, Windows Credential Manager, or Linux secret-service, ensuring they never exist as plaintext on the filesystem and remain accessible only to the local user account.

### How does automatic token refresh work in Claude plugins?

The MCP server checks the `expiry` timestamp on stored tokens before executing API calls. If a token expires within 60 seconds or has already expired, the server automatically calls the OAuth provider's refresh endpoint using the stored `refresh_token`, updates the encrypted keychain entry with new credentials, and proceeds with the request without user intervention.

### What is the role of the MCP server in OAuth authentication?

The MCP (Claude Code Micro-Connector Platform) server serves as the local OAuth client, hosting the callback handler that exchanges authorization codes for tokens, managing secure storage and retrieval through the OS keychain, intercepting tool invocations to inject bearer tokens, and handling automatic refresh cycles to maintain valid sessions indefinitely.