OAuth Endpoint Reference

Complete documentation for all OAuth 2.0 endpoints with multi-language code examples.

Discovery Endpoint

/.well-known/oauth-authorization-server
GET
Returns OAuth server metadata per RFC 8414.
curl https://jmpy.me/.well-known/oauth-authorization-server
{
  "issuer": "https://jmpy.me",
  "authorization_endpoint": "https://jmpy.me/mcp/oauth/authorize",
  "token_endpoint": "https://jmpy.me/mcp/oauth/token",
  "revocation_endpoint": "https://jmpy.me/mcp/oauth/revoke",
  "registration_endpoint": "https://jmpy.me/mcp/oauth/register",
  "scopes_supported": [
    "shorturl:read",
    "shorturl:create",
    "qrcode:read",
    "qrcode:create",
    "analytics:read"
  ],
  "response_types_supported": ["code"],
  "grant_types_supported": ["authorization_code", "refresh_token"],
  "code_challenge_methods_supported": ["S256", "plain"]
}

Dynamic Client Registration

/mcp/oauth/register
POST
Register a new OAuth client application (RFC 7591).

Request Body

ParameterTypeRequiredDescription
client_namestringYesHuman-readable app name
redirect_urisstring[]YesAllowed callback URLs
grant_typesstring[]NoDefault: ["authorization_code"]
response_typesstring[]NoDefault: ["code"]
scopestringNoSpace-separated requested scopes
logo_uristringNoURL to app logo
client_uristringNoApp homepage URL
policy_uristringNoPrivacy policy URL
tos_uristringNoTerms of service URL
contactsstring[]NoContact email addresses
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"],
    "scope": "shorturl:read shorturl:create qrcode:read qrcode:create",
    "client_uri": "https://myapp.com",
    "policy_uri": "https://myapp.com/privacy"
  }'
{
  "client_id": "jmpy_client_abc123def456",
  "client_secret": "jmpy_secret_xyz789abc123",
  "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",
  "client_id_issued_at": 1704931200,
  "client_secret_expires_at": 0
}
Store credentials securely! The client_secret is only shown once. Store it in a secure location like environment variables or a secrets manager.

Authorization Endpoint

/mcp/oauth/authorize
GET
Initiate user authorization. Redirects to JMPY login/consent page.

Query Parameters

ParameterTypeRequiredDescription
client_idstringYesYour OAuth client ID
redirect_uristringYesCallback URL (must match registered URI)
response_typestringYesMust be code
scopestringNoSpace or plus-separated scopes
statestringRecommendedCSRF protection token
code_challengestringRecommendedPKCE code challenge
code_challenge_methodstringRecommendedS256 or plain

Authorization URL Builder

function buildAuthUrl(clientId, redirectUri, scopes, state) {
  const url = new URL('https://jmpy.me/mcp/oauth/authorize');
  url.searchParams.set('client_id', clientId);
  url.searchParams.set('redirect_uri', redirectUri);
  url.searchParams.set('response_type', 'code');
  url.searchParams.set('scope', scopes.join(' '));
  url.searchParams.set('state', state);
  return url.toString();
}

// Example usage
const authUrl = buildAuthUrl(
  'jmpy_client_abc123',
  'https://myapp.com/callback',
  ['shorturl:read', 'shorturl:create', 'qrcode:create'],
  'random_state_token'
);
// Redirect: window.location.href = authUrl;

Callback Response

After user approval, they’re redirected to your redirect_uri:
https://myapp.com/callback?code=AUTH_CODE_HERE&state=random_state_token
On denial:
https://myapp.com/callback?error=access_denied&error_description=User%20denied%20the%20request&state=random_state_token

Token Endpoint

/mcp/oauth/token
POST
Exchange authorization code for tokens, or refresh an access token.

Grant Type: Authorization Code

ParameterTypeRequiredDescription
grant_typestringYesauthorization_code
codestringYesAuthorization code from callback
client_idstringYesYour OAuth client ID
client_secretstringYesYour OAuth client secret
redirect_uristringYesSame URI used in authorization
code_verifierstringIf PKCEOriginal PKCE code verifier
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_HERE" \
  -d "client_id=jmpy_client_abc123" \
  -d "client_secret=jmpy_secret_xyz789" \
  -d "redirect_uri=https://myapp.com/callback" \
  -d "code_verifier=ORIGINAL_VERIFIER"
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "jmpy_rt_abc123def456xyz789",
  "scope": "shorturl:read shorturl:create qrcode:create"
}

Grant Type: Refresh Token

ParameterTypeRequiredDescription
grant_typestringYesrefresh_token
refresh_tokenstringYesYour refresh token
client_idstringYesYour OAuth client ID
client_secretstringYesYour OAuth client secret
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_abc123def456xyz789" \
  -d "client_id=jmpy_client_abc123" \
  -d "client_secret=jmpy_secret_xyz789"
Token Rotation: Each refresh returns a new refresh token. The old refresh token is invalidated. Always store and use the latest refresh token.

Revocation Endpoint

/mcp/oauth/revoke
POST
Revoke an access or refresh token.
ParameterTypeRequiredDescription
tokenstringYesToken to revoke
token_type_hintstringNoaccess_token or refresh_token
client_idstringYesYour OAuth client ID
client_secretstringYesYour OAuth client secret
curl -X POST https://jmpy.me/mcp/oauth/revoke \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "token=jmpy_rt_abc123def456xyz789" \
  -d "token_type_hint=refresh_token" \
  -d "client_id=jmpy_client_abc123" \
  -d "client_secret=jmpy_secret_xyz789"
{
  "success": true,
  "message": "Token revoked successfully"
}

Error Responses

All endpoints return standard OAuth 2.0 error responses:
Error CodeDescription
invalid_requestMissing or malformed parameter
invalid_clientClient authentication failed
invalid_grantAuthorization code/refresh token invalid or expired
unauthorized_clientClient not authorized for this grant type
unsupported_grant_typeGrant type not supported
invalid_scopeScope invalid or exceeds client permissions
access_deniedUser denied authorization
{
  "error": "invalid_grant",
  "error_description": "Authorization code has expired or was already used"
}

Next Steps