Skip to main content
OAuth 2.0 allows third-party applications to securely access mpath APIs using bearer tokens instead of requiring users to share their credentials.

Overview

mpath supports OAuth 2.0 authentication through Clerk’s built-in OAuth provider. This enables your application to:
  • Access mpath APIs on behalf of users
  • Request specific permissions (scopes)
  • Maintain secure, token-based authentication
  • Refresh tokens when they expire

Getting Started

Step 1: Create an OAuth Application

  1. Log in to your mpath account
  2. Contact your organization administrator to create an OAuth application
  3. The administrator will provide you with:
    • Client ID: Your application’s unique identifier
    • Client Secret: Keep this secure and never expose it in client-side code
    • Redirect URI: The URL where users will be redirected after authorization

Step 2: Configure Your Application

You’ll need the following endpoints from Clerk:
  • Authorization URL: {CLERK_FRONTEND_API_URL}/oauth/authorize
  • Token URL: {CLERK_FRONTEND_API_URL}/oauth/token
  • UserInfo URL: {CLERK_FRONTEND_API_URL}/oauth/userinfo
Your organization administrator can provide the CLERK_FRONTEND_API_URL.

OAuth Flow

The OAuth 2.0 authorization code flow consists of three steps:

1. Request Authorization

Redirect the user to the authorization endpoint:
GET {CLERK_FRONTEND_API_URL}/oauth/authorize?
  client_id=YOUR_CLIENT_ID&
  response_type=code&
  redirect_uri=YOUR_REDIRECT_URI&
  scope=read:people%20read:tasks&
  state=RANDOM_STATE_STRING
Parameters:
  • client_id: Your OAuth application’s Client ID
  • response_type: Always code for authorization code flow
  • redirect_uri: Must match one of your registered redirect URIs
  • scope: Space-separated list of requested permissions (see Scopes below)
  • state: A random string to prevent CSRF attacks

2. Exchange Code for Token

After the user authorizes your application, they’ll be redirected back with an authorization code. Exchange this code for an access token:
POST {CLERK_FRONTEND_API_URL}/oauth/token
Content-Type: application/x-www-form-urlencoded
Authorization: Basic BASE64(CLIENT_ID:CLIENT_SECRET)

grant_type=authorization_code&
code=AUTHORIZATION_CODE&
redirect_uri=YOUR_REDIRECT_URI
Response:
{
  "access_token": "eyJhbGc...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "refresh_token_here",
  "scope": "read:people read:tasks"
}

3. Use the Access Token

Include the access token in API requests:
const response = await fetch('https://your-mpath-instance.com/api/tasks', {
  headers: {
    Authorization: `Bearer ${accessToken}`,
  },
})

Scopes

Scopes define what permissions your application has. Request only the scopes you need:
ScopeDescription
read:peopleRead people and team information
write:peopleCreate, update, and delete people
read:tasksRead tasks
write:tasksCreate, update, and delete tasks
read:initiativesRead initiatives and objectives
write:initiativesCreate, update, and delete initiatives
read:meetingsRead meetings
write:meetingsCreate, update, and delete meetings
adminFull admin access (admins only)
Note: The admin scope is only available to organization administrators.

Token Refresh

Access tokens expire after a set period. Use the refresh token to obtain a new access token:
POST {CLERK_FRONTEND_API_URL}/oauth/token
Content-Type: application/x-www-form-urlencoded
Authorization: Basic BASE64(CLIENT_ID:CLIENT_SECRET)

grant_type=refresh_token&
refresh_token=YOUR_REFRESH_TOKEN

Example Integration

JavaScript/TypeScript

// 1. Redirect user to authorization
const authUrl = new URL(`${CLERK_FRONTEND_API_URL}/oauth/authorize`)
authUrl.searchParams.set('client_id', CLIENT_ID)
authUrl.searchParams.set('response_type', 'code')
authUrl.searchParams.set('redirect_uri', REDIRECT_URI)
authUrl.searchParams.set('scope', 'read:people read:tasks')
authUrl.searchParams.set('state', generateRandomState())
window.location.href = authUrl.toString()

// 2. Handle callback and exchange code for token
async function handleCallback(code: string) {
  const auth = btoa(`${CLIENT_ID}:${CLIENT_SECRET}`)
  const response = await fetch(`${CLERK_FRONTEND_API_URL}/oauth/token`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
      Authorization: `Basic ${auth}`,
    },
    body: new URLSearchParams({
      grant_type: 'authorization_code',
      code,
      redirect_uri: REDIRECT_URI,
    }),
  })
  const { access_token, refresh_token } = await response.json()
  
  // Store tokens securely (never in localStorage for production)
  // Then use access_token for API calls
}

// 3. Make API calls
async function fetchTasks(accessToken: string) {
  const response = await fetch('https://your-instance.com/api/tasks', {
    headers: {
      Authorization: `Bearer ${accessToken}`,
    },
  })
  return response.json()
}

Python

import requests
import base64
from urllib.parse import urlencode

# 1. Build authorization URL
auth_params = {
    'client_id': CLIENT_ID,
    'response_type': 'code',
    'redirect_uri': REDIRECT_URI,
    'scope': 'read:people read:tasks',
    'state': generate_random_state()
}
auth_url = f"{CLERK_FRONTEND_API_URL}/oauth/authorize?{urlencode(auth_params)}"
# Redirect user to auth_url

# 2. Exchange code for token
auth_string = base64.b64encode(f"{CLIENT_ID}:{CLIENT_SECRET}".encode()).decode()
response = requests.post(
    f"{CLERK_FRONTEND_API_URL}/oauth/token",
    headers={
        'Content-Type': 'application/x-www-form-urlencoded',
        'Authorization': f'Basic {auth_string}'
    },
    data={
        'grant_type': 'authorization_code',
        'code': authorization_code,
        'redirect_uri': REDIRECT_URI
    }
)
token_data = response.json()
access_token = token_data['access_token']

# 3. Make API calls
api_response = requests.get(
    'https://your-instance.com/api/tasks',
    headers={'Authorization': f'Bearer {access_token}'}
)
tasks = api_response.json()

Security Best Practices

Never expose your Client Secret in client-side code or public repositories.
  1. Store Secrets Securely: Keep your Client Secret on the server side only
  2. Use HTTPS: Always use HTTPS in production for token transmission
  3. Validate State: Always validate the state parameter to prevent CSRF attacks
  4. Token Storage: Store tokens securely and never in localStorage for production apps
  5. Scope Limitation: Request only the minimum scopes needed
  6. Token Refresh: Implement automatic token refresh before expiration

Error Handling

Common Errors

Error CodeDescriptionSolution
401Invalid or expired tokenRefresh the token or re-authenticate
403Insufficient permissionsRequest the required scopes
400Invalid requestCheck your request parameters

Handling Expired Tokens

async function makeApiCall(accessToken: string) {
  let response = await fetch('/api/tasks', {
    headers: { Authorization: `Bearer ${accessToken}` },
  })
  
  if (response.status === 401) {
    // Token expired, refresh it
    const newToken = await refreshAccessToken(refreshToken)
    // Retry the request
    response = await fetch('/api/tasks', {
      headers: { Authorization: `Bearer ${newToken}` },
    })
  }
  
  return response.json()
}

Testing

mpath provides a test script to verify your OAuth integration. Contact your administrator for access to the test script.

Troubleshooting

Authorization Fails

  • Verify the redirect URI matches exactly what’s registered
  • Check that your Client ID is correct
  • Ensure the OAuth application is enabled

Token Exchange Fails

  • Verify your Client ID and Client Secret are correct
  • Check that the authorization code hasn’t expired (codes expire quickly)
  • Ensure the redirect URI matches the one used in authorization

API Calls Fail

  • Verify the access token is included in the Authorization header
  • Check that the token format is correct: Bearer <token>
  • Ensure the requested scopes were granted
  • Verify the user has the necessary permissions in mpath

Support

For help with OAuth integration:
Related Guides: