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
- Log in to your mpath account
- Contact your organization administrator to create an OAuth application
- 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
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:
| Scope | Description |
|---|
read:people | Read people and team information |
write:people | Create, update, and delete people |
read:tasks | Read tasks |
write:tasks | Create, update, and delete tasks |
read:initiatives | Read initiatives and objectives |
write:initiatives | Create, update, and delete initiatives |
read:meetings | Read meetings |
write:meetings | Create, update, and delete meetings |
admin | Full 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.
- Store Secrets Securely: Keep your Client Secret on the server side only
- Use HTTPS: Always use HTTPS in production for token transmission
- Validate State: Always validate the
state parameter to prevent CSRF attacks
- Token Storage: Store tokens securely and never in localStorage for production apps
- Scope Limitation: Request only the minimum scopes needed
- Token Refresh: Implement automatic token refresh before expiration
Error Handling
Common Errors
| Error Code | Description | Solution |
|---|
| 401 | Invalid or expired token | Refresh the token or re-authenticate |
| 403 | Insufficient permissions | Request the required scopes |
| 400 | Invalid request | Check 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: