# Consumer OAuth for Custom MCP Servers Configure per-user OAuth authentication so your AI agent's end-users can connect their own accounts to third-party services Consumer OAuth allows each end-user of your Chipp app to authenticate with their own account on third-party services. Instead of using a single shared API key, each user connects their personal account (e.g., their Salesforce, HubSpot, or Google account), and the AI agent acts on their behalf. ## When to Use Consumer OAuth **Use Consumer OAuth when:** - Users need to access **their own data** (CRM contacts, calendar events, emails) - The third-party API requires **user-specific authentication** - You want **accountability** for which user performed each action - The service enforces **per-user rate limits or permissions** **Use Bearer Token when:** - Accessing **shared company resources** that all users can see - The MCP server connects to **internal APIs** you control - You want the **simplest possible setup** --- ## How Consumer OAuth Works 1. **User initiates chat** with your Chipp app 2. **AI agent calls a tool** that requires user authentication 3. **Chipp detects** the user hasn't connected their account 4. **User sees OAuth prompt** and clicks to authenticate 5. **Browser opens** the third-party's login page 6. **User authorizes** your app to access their data 7. **Callback returns** with authorization code 8. **Chipp exchanges** code for access & refresh tokens 9. **Tokens stored** securely per-user in the database 10. **Tool executes** with the user's access token Subsequent requests use the stored tokens. Chipp automatically refreshes expired tokens using the refresh token. --- ## Configuration Configure Consumer OAuth in the **Pro Actions** modal when adding a custom MCP server. ### Step 1: Create OAuth App on Third-Party Service First, register an OAuth application with the service you want to integrate (e.g., Salesforce, HubSpot, Google). You'll need to configure: | Setting | Value | |---------|-------| | **Redirect URI** | `https://app.chipp.ai/api/chat/mcp/oauth/callback` | | **Scopes** | Request the minimum scopes needed for your tools | After registration, you'll receive: - **Client ID** - Public identifier for your OAuth app - **Client Secret** - Private key (keep this secure!) ### Step 2: Find OAuth Endpoints Locate the OAuth 2.0 endpoints in the service's documentation: | Endpoint | Description | Example | |----------|-------------|---------| | **Authorize URL** | Where users grant permission | `https://login.salesforce.com/services/oauth2/authorize` | | **Token URL** | Where codes are exchanged for tokens | `https://login.salesforce.com/services/oauth2/token` | ### Step 3: Configure in Chipp 1. Go to your app's **Build** tab 2. Open the **Pro Actions** modal 3. Click **Add Custom MCP Server** 4. Enter your **MCP Server URL** 5. Select **Consumer OAuth (per-user)** as the auth type Fill in the OAuth configuration: ### Step 4: Test the Configuration Click **Test OAuth Configuration** to verify your setup works: 1. A new window opens with the third-party's login page 2. Log in and authorize the connection 3. The window closes and shows a success message 4. If successful, your MCP server's tools are fetched and cached Test with your own account first. If the OAuth flow completes and tools load successfully, the configuration is correct. --- ## Token Request Styles Different OAuth providers expect credentials formatted differently during the token exchange. Choose the style that matches your provider: ### form_post (Most Common) Credentials sent as form-encoded body parameters. **Try this first.** ```http POST /oauth/token HTTP/1.1 Content-Type: application/x-www-form-urlencoded grant_type=authorization_code& code=AUTH_CODE& client_id=YOUR_CLIENT_ID& client_secret=YOUR_CLIENT_SECRET& redirect_uri=https://app.chipp.ai/api/chat/mcp/oauth/callback ``` **Used by:** Google, GitHub, LinkedIn, most OAuth providers ### json_basic Credentials sent in HTTP Basic Authorization header, body as JSON. ```http POST /oauth/token HTTP/1.1 Authorization: Basic base64(client_id:client_secret) Content-Type: application/json { "grant_type": "authorization_code", "code": "AUTH_CODE", "redirect_uri": "https://app.chipp.ai/api/chat/mcp/oauth/callback" } ``` **Used by:** Some enterprise APIs, Stripe ### form_basic Credentials in Basic header, body as form-encoded. ```http POST /oauth/token HTTP/1.1 Authorization: Basic base64(client_id:client_secret) Content-Type: application/x-www-form-urlencoded grant_type=authorization_code& code=AUTH_CODE& redirect_uri=https://app.chipp.ai/api/chat/mcp/oauth/callback ``` **Used by:** Salesforce, some enterprise OAuth providers If token exchange fails, try a different token style. The error message often doesn't indicate the wrong style was used. --- ## Passing User Tokens to Your MCP Server When a user has authenticated, Chipp passes their access token to your MCP server in the `Authorization` header: ```http POST /mcp HTTP/1.1 Authorization: Bearer USER_ACCESS_TOKEN Content-Type: application/json ``` Your MCP server can extract this token and use it for API calls: ```typescript // In your MCP server server.tool( "get_my_contacts", "Fetch the current user's contacts from Salesforce", {}, async (args, context) => { // The user's access token is in the Authorization header const userToken = context?.meta?.authorization?.replace("Bearer ", ""); const response = await fetch("https://your-instance.salesforce.com/services/data/v58.0/sobjects/Contact", { headers: { "Authorization": `Bearer ${userToken}` } }); const contacts = await response.json(); return { content: [{ type: "text", text: JSON.stringify(contacts, null, 2) }] }; } ); ``` --- ## Token Refresh Access tokens expire. Chipp automatically handles token refresh: 1. Before each MCP call, Chipp checks if the token is expired (with 5-minute buffer) 2. If expired and a refresh token exists, Chipp requests a new access token 3. The new token is stored and used for the request 4. If refresh fails, the user is prompted to re-authenticate Your MCP server doesn't need to handle token refresh - Chipp manages it automatically. Some OAuth providers (like Google) only return a refresh token on the **first** authorization. If users need to re-authenticate, they may need to revoke access in their account settings first. --- ## User Experience When a user chats with your app and the AI needs to access a tool requiring Consumer OAuth: 1. **First time:** User sees a message prompting them to connect their account 2. **Click to connect:** Opens the OAuth provider's login/consent page 3. **Authorize:** User grants your app access to their data 4. **Automatic return:** Browser closes, chat continues seamlessly 5. **Subsequent chats:** No prompts - tokens are reused automatically ### What Users See ``` 🔐 This action requires access to your Salesforce account. [Connect Salesforce Account] Click above to securely connect. We'll only request access to the data needed for this app. ``` After connecting: ``` ✓ Connected to Salesforce as john@example.com Now fetching your recent contacts... ``` --- ## Security Considerations --- ## Troubleshooting ### "Invalid redirect_uri" Error The redirect URI in your OAuth app configuration doesn't match what Chipp sends. **Solution:** Ensure your OAuth app's redirect URI is exactly: ``` https://app.chipp.ai/api/chat/mcp/oauth/callback ``` ### "Invalid client" Error The client ID or secret is incorrect. **Solution:** Double-check credentials. Some providers have separate credentials for production vs. sandbox environments. ### Token Exchange Fails Silently The token request style doesn't match what the provider expects. **Solution:** Try each token request style (form_post, json_basic, form_basic) until one works. ### "Access Denied" During Authorization User declined the consent prompt, or the requested scopes aren't allowed. **Solution:** - Check if the user clicked "Deny" instead of "Allow" - Verify your OAuth app has access to the requested scopes - Some scopes require app review/approval ### Refresh Token Missing The OAuth provider only issues refresh tokens on initial authorization. **Solution:** - User should revoke access in the provider's settings - Re-authorize to get a new refresh token - Some providers require adding `access_type=offline` or `prompt=consent` to the authorize URL ### Tools Don't Load After OAuth The OAuth succeeded but tools aren't showing. **Solution:** - Check that your MCP server is running and accessible - Verify the server URL is correct - Check MCP server logs for errors - Ensure the server responds to `tools/list` correctly --- ## Example: Salesforce Integration Here's a complete example configuring Consumer OAuth for Salesforce: ### 1. Create Connected App in Salesforce 1. Go to **Setup** → **Apps** → **App Manager** 2. Click **New Connected App** 3. Configure: - **Connected App Name:** My Chipp Integration - **API Name:** My_Chipp_Integration - **Enable OAuth Settings:** ✓ - **Callback URL:** `https://app.chipp.ai/api/chat/mcp/oauth/callback` - **Selected OAuth Scopes:** `api`, `refresh_token`, `offline_access` 4. Save and wait for activation (can take 10 minutes) 5. Copy **Consumer Key** (Client ID) and **Consumer Secret** ### 2. Configure in Chipp | Field | Value | |-------|-------| | OAuth Client ID | `3MVG9...` (your Consumer Key) | | OAuth Client Secret | `ABC123...` (your Consumer Secret) | | Authorize URL | `https://login.salesforce.com/services/oauth2/authorize` | | Token URL | `https://login.salesforce.com/services/oauth2/token` | | Scopes | `api refresh_token offline_access` | | Token Request Style | `form_basic` | ### 3. Build Your MCP Server ```typescript server.tool( "search_salesforce_contacts", "Search for contacts in the user's Salesforce org", { query: z.string().describe("Search query for contact name or email") }, async ({ query }, context) => { const token = context?.meta?.authorization?.replace("Bearer ", ""); // Get instance URL from token info (simplified) const instanceUrl = "https://your-instance.salesforce.com"; const soql = `SELECT Id, Name, Email, Phone FROM Contact WHERE Name LIKE '%${query}%' LIMIT 10`; const response = await fetch( `${instanceUrl}/services/data/v58.0/query?q=${encodeURIComponent(soql)}`, { headers: { "Authorization": `Bearer ${token}` } } ); const results = await response.json(); return { content: [{ type: "text", text: JSON.stringify(results.records, null, 2) }] }; } ); ``` --- ## Related Guides - [Building Custom MCP Servers](/docs/guides/building-custom-mcp-servers) - Complete guide to building MCP servers - [Pro Actions Getting Started](/docs/pro-actions/getting-started) - Use pre-built MCP integrations - [Chipp MCP Server](/docs/guides/chipp-mcp-server) - Connect Claude Code to manage your Chipp apps