Skip to main content
Some Blaxel integrations support OAuth-based authentication. You can automate such a flow to allow multiple users (tenants) to go through an OAuth flow and each have their integration. This approach is particularly useful when you need to automate the creation of dedicated MCP servers for each tenant with customized access permissions. This guide takes the example of creating such a flow for a Gmail integration.

Prerequisites

1. Create the APP

  • Select Web application
  • Choose a name. It will be displayed when your user logins through Google
  • The redirect URIs will be the URL where your user will be redirected to after Google authorizes the request. It must be server-side as it will need to access secret credentials.
Finish creating the application. You’ll be given you a client_id and client_secret: keep them securely.

2. Configure scope

Scopes are used to request sufficient access on the user’s account. In this example, we want to connect his account to the MCP server to send emails.
  • Go to Data access
  • Click on “Add or remove scopes”
  • Add gmail.send
Some scopes (like this one) require an HTTPS callback URL. You won’t be able to test them easily locally without using ngrok or a similar tool which allows you to have an HTTPS URL bound to your localhost.
By default, it will work with your own account without any review by Google. You can look for the Audience tab on left to add more users. To make it global, you will need to launch a review process from Google with the “Publish app” button.
We recommend creating a separate OAuth flow specifically for the Gmail integration. Don’t simply add the Gmail scope to an existing OAuth flow, as this would require all users to grant email permissions even when they don’t need Gmail functionality.

Overview of the OAuth flow

The integration follows this redirect pattern:
UI → Your Backend → Google → Your Backend → UI

Step-by-step guide

Step 1: User initiates connection

  • In your app’s UI, user clicks a button to “connect with Google” (or equivalent) during integration setup
  • UI redirects to your backend OAuth endpoint

Step 2: Backend initiates Google OAuth

Your backend should redirect to Google’s OAuth URL with these parameters: Required Scopes:
  • https://www.googleapis.com/auth/gmail.send
  • openid
  • profile
  • email
OAuth URL:
https://accounts.google.com/o/oauth2/auth?client_id={CLIENT_ID}&redirect_uri={REDIRECT_URI}&scope=email+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fgmail.send+openid+profile&response_type=code&access_type=offline

Step 3: Google authorization

  • Google prompts user to login and approve requested scopes
  • Google redirects back to your redirect_uri with a temporary authorization code

Step 4: Exchange Code for Tokens

Your backend exchanges the authorization code for access and refresh tokens: Example: Token Exchange (Python)
import requests
import json

def exchange_code_for_tokens(auth_code, client_id, client_secret, redirect_uri):
    """
    Exchange authorization code for access and refresh tokens
    """
    token_url = "https://oauth2.googleapis.com/token"
    
    data = {
        'code': auth_code,
        'client_id': client_id,
        'client_secret': client_secret,
        'redirect_uri': redirect_uri,
        'grant_type': 'authorization_code'
    }
    
    try:
        response = requests.post(token_url, data=data)
        response.raise_for_status()
        
        token_data = response.json()
        
        return {
            'access_token': token_data['access_token'],
            'refresh_token': token_data['refresh_token'],
            'expires_in': token_data['expires_in']
        }
    
    except requests.exceptions.RequestException as e:
        print(f"Token exchange failed: {e}")
        if hasattr(e.response, 'json'):
            print(f"Error details: {e.response.json()}")
        raise
Example: cURL request
curl -X POST https://oauth2.googleapis.com/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "code=AUTHORIZATION_CODE" \
  -d "client_id=YOUR_CLIENT_ID" \
  -d "client_secret=YOUR_CLIENT_SECRET" \
  -d "redirect_uri=YOUR_REDIRECT_URI" \
  -d "grant_type=authorization_code"

Step 5: Create integration

Once you have the refresh token, create the integration on Blaxel (for an MCP server in this case) with these parameters:
// Create the integration connection on blaxel (store the secrets values)
curl -X POST https://api.blaxel.ai/v0/integrations/connections \
  -H "Content-Type: application/json" \
  -H "X-Blaxel-Workspace: YOUR_WORKSPACE_ID"
  -H "Authorization: Bearer BL_API_KEY" \
  -d '{
    "metadata": {
      "name": "gmail-google-oauth",
      "displayName": "Gmail Google OAuth Connection"
    },
    "spec": {
      "integration": "gmail",
      "secret": {
        "CLIENT_ID": "your-google-client-id.apps.googleusercontent.com",
        "CLIENT_SECRET": "your-google-client-secret",
        "REFRESH_TOKEN": "user-refresh-token-from-oauth"
      }
    }
  }'
Parameters Required:
  • CLIENT_ID: Your Google OAuth application client ID
  • CLIENT_SECRET: Your Google OAuth application client secret
  • REFRESH_TOKEN: The refresh token obtained from the OAuth flow
You can then create the actual MCP server that uses this integration connection:
// Create the MCP binded to the integration connection
curl -X POST https://api.blaxel.ai/v0/functions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer BL_API_KEY" \
  -H "X-Blaxel-Workspace: YOUR_WORKSPACE_ID"
  -d '{
    "metadata": {
      "name": "gmail-mcp-server",
      "displayName": "Gmail MCP Server"
    },
    "spec": {
      "type": "mcp",
      "integrationConnections": ["gmail-google-oauth"],
      "runtime": {
        "type": "mcp"
      }
    }
  }'

Step 6: Complete Integration

  • Save MCP configuration in your system
  • Redirect user back to frontend with success confirmation
  • Integration is now ready for use

Troubleshooting

  • Invalid scope error: Ensure Gmail API is enabled in Google Cloud Console
  • Redirect URI mismatch: Verify redirect URI matches exactly in Google OAuth app settings
  • Token refresh issues: Check that access_type=offline is included in initial OAuth request