OAuth 2.0 Integration
JMPY.me provides OAuth 2.0 authentication following industry standards, allowing third-party applications to securely access user accounts.
Standards Compliance Our 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-serverOAuth server metadata Register POST /mcp/oauth/registerDynamic client registration Authorize GET /mcp/oauth/authorizeUser authorization page Token POST /mcp/oauth/tokenToken exchange Revoke POST /mcp/oauth/revokeToken revocation
Pre-Registered Clients
For common AI assistants, JMPY provides pre-registered OAuth clients:
App Client ID Documentation OpenAI/ChatGPT openaiGPT Actions Guide Claude (MCP) claudeMCP Setup Zapier zapierComing 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_requestMissing or invalid parameters invalid_clientClient authentication failed invalid_grantAuthorization code invalid or expired unauthorized_clientClient not authorized for this grant type access_deniedUser denied the authorization request invalid_scopeRequested scope is invalid or exceeds plan limits
{
"error" : "invalid_grant" ,
"error_description" : "Authorization code has expired"
}
Next Steps
Available Scopes Learn about permission scopes
Endpoint Reference Detailed endpoint documentation
OpenAI Integration Set up JMPY with ChatGPT
Testing Guide Test your integration