OAuth 2.0 Integration
JMPY.me provides OAuth 2.0 authentication following industry standards, allowing third-party applications to securely access user accounts.
Standards ComplianceOur OAuth implementation follows:
- RFC 6749 - OAuth 2.0 Authorization Framework
- RFC 7636 - PKCE (Proof Key for Code Exchange)
- RFC 7591 - Dynamic Client Registration
- RFC 8414 - Authorization Server Metadata
Quick Start
Register Your Application
Register your app to get OAuth credentials (client_id and client_secret).curl -X POST https://jmpy.me/mcp/oauth/register \
-H "Content-Type: application/json" \
-d '{
"client_name": "My Awesome App",
"redirect_uris": ["https://myapp.com/callback"],
"grant_types": ["authorization_code"],
"response_types": ["code"],
"scope": "shorturl:read shorturl:create qrcode:read qrcode:create"
}'
Response:{
"client_id": "jmpy_client_abc123...",
"client_secret": "jmpy_secret_xyz789...",
"client_name": "My Awesome App",
"redirect_uris": ["https://myapp.com/callback"]
}
Redirect User to Authorization
When a user wants to connect, redirect them to our authorization endpoint:const authUrl = new URL('https://jmpy.me/mcp/oauth/authorize');
authUrl.searchParams.set('client_id', 'YOUR_CLIENT_ID');
authUrl.searchParams.set('redirect_uri', 'https://myapp.com/callback');
authUrl.searchParams.set('response_type', 'code');
authUrl.searchParams.set('scope', 'shorturl:read shorturl:create');
authUrl.searchParams.set('state', 'random_csrf_token');
// For PKCE (recommended)
authUrl.searchParams.set('code_challenge', codeChallenge);
authUrl.searchParams.set('code_challenge_method', 'S256');
window.location.href = authUrl.toString();
Handle the Callback
After user approval, they’re redirected back with an authorization code:https://myapp.com/callback?code=AUTH_CODE&state=random_csrf_token
Exchange Code for Tokens
Exchange the authorization code for access and refresh tokens:curl -X POST https://jmpy.me/mcp/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code" \
-d "code=AUTH_CODE" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_CLIENT_SECRET" \
-d "redirect_uri=https://myapp.com/callback" \
-d "code_verifier=ORIGINAL_CODE_VERIFIER"
Response:{
"access_token": "eyJhbGciOiJIUzI1...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "jmpy_rt_abc123...",
"scope": "shorturl:read shorturl:create"
}
Make API Calls
Use the access token to make API requests:curl https://jmpy.me/api/v1/urls \
-H "Authorization: Bearer eyJhbGciOiJIUzI1..."
OAuth Endpoints
| Endpoint | URL | Description |
|---|
| Discovery | /.well-known/oauth-authorization-server | OAuth server metadata |
| Register | POST /mcp/oauth/register | Dynamic client registration |
| Authorize | GET /mcp/oauth/authorize | User authorization page |
| Token | POST /mcp/oauth/token | Token exchange |
| Revoke | POST /mcp/oauth/revoke | Token revocation |
Pre-Registered Clients
For common AI assistants, JMPY provides pre-registered OAuth clients:
| App | Client ID | Documentation |
|---|
| OpenAI/ChatGPT | openai | GPT Actions Guide |
| Claude (MCP) | claude | MCP Setup |
| Zapier | zapier | Coming soon |
Pre-registered clients have verified credentials and streamlined setup. Users can connect these apps directly from their JMPY dashboard.
Token Lifecycle
Access Tokens
- Type: JWT (JSON Web Token)
- Expiration: 1 hour
- Usage: Include in
Authorization: Bearer <token> header
Refresh Tokens
- Expiration: 30 days
- Usage: Exchange for new access token when expired
- Rotation: Each refresh returns a new refresh token (old token is invalidated)
# Refresh an expired access token
curl -X POST https://jmpy.me/mcp/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=refresh_token" \
-d "refresh_token=jmpy_rt_abc123..." \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_CLIENT_SECRET"
PKCE (Recommended)
PKCE (Proof Key for Code Exchange) prevents authorization code interception attacks. We strongly recommend using PKCE for all OAuth flows.
Generate Code Verifier and Challenge
// Generate a random code verifier
function generateCodeVerifier() {
const array = new Uint8Array(32);
crypto.getRandomValues(array);
return base64UrlEncode(array);
}
// Create the code challenge (SHA-256 hash)
async function generateCodeChallenge(verifier) {
const encoder = new TextEncoder();
const data = encoder.encode(verifier);
const hash = await crypto.subtle.digest('SHA-256', data);
return base64UrlEncode(new Uint8Array(hash));
}
function base64UrlEncode(buffer) {
return btoa(String.fromCharCode(...buffer))
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
}
Use in Authorization Request
const codeVerifier = generateCodeVerifier();
const codeChallenge = await generateCodeChallenge(codeVerifier);
// Store codeVerifier securely for later use in token exchange
sessionStorage.setItem('pkce_verifier', codeVerifier);
// Add to authorization URL
authUrl.searchParams.set('code_challenge', codeChallenge);
authUrl.searchParams.set('code_challenge_method', 'S256');
Error Handling
OAuth endpoints return standard error codes per RFC 6749:
| Error | Description |
|---|
invalid_request | Missing or invalid parameters |
invalid_client | Client authentication failed |
invalid_grant | Authorization code invalid or expired |
unauthorized_client | Client not authorized for this grant type |
access_denied | User denied the authorization request |
invalid_scope | Requested scope is invalid or exceeds plan limits |
{
"error": "invalid_grant",
"error_description": "Authorization code has expired"
}
Next Steps