OAuth Endpoint Reference
Complete documentation for all OAuth 2.0 endpoints with multi-language code examples.
Discovery Endpoint
/.well-known/oauth-authorization-server
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
Register a new OAuth client application (RFC 7591).
Request Body
| Parameter | Type | Required | Description |
|---|
client_name | string | Yes | Human-readable app name |
redirect_uris | string[] | Yes | Allowed callback URLs |
grant_types | string[] | No | Default: ["authorization_code"] |
response_types | string[] | No | Default: ["code"] |
scope | string | No | Space-separated requested scopes |
logo_uri | string | No | URL to app logo |
client_uri | string | No | App homepage URL |
policy_uri | string | No | Privacy policy URL |
tos_uri | string | No | Terms of service URL |
contacts | string[] | No | Contact 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
Initiate user authorization. Redirects to JMPY login/consent page.
Query Parameters
| Parameter | Type | Required | Description |
|---|
client_id | string | Yes | Your OAuth client ID |
redirect_uri | string | Yes | Callback URL (must match registered URI) |
response_type | string | Yes | Must be code |
scope | string | No | Space or plus-separated scopes |
state | string | Recommended | CSRF protection token |
code_challenge | string | Recommended | PKCE code challenge |
code_challenge_method | string | Recommended | S256 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
Exchange authorization code for tokens, or refresh an access token.
Grant Type: Authorization Code
| Parameter | Type | Required | Description |
|---|
grant_type | string | Yes | authorization_code |
code | string | Yes | Authorization code from callback |
client_id | string | Yes | Your OAuth client ID |
client_secret | string | Yes | Your OAuth client secret |
redirect_uri | string | Yes | Same URI used in authorization |
code_verifier | string | If PKCE | Original 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
| Parameter | Type | Required | Description |
|---|
grant_type | string | Yes | refresh_token |
refresh_token | string | Yes | Your refresh token |
client_id | string | Yes | Your OAuth client ID |
client_secret | string | Yes | Your 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
Revoke an access or refresh token.
| Parameter | Type | Required | Description |
|---|
token | string | Yes | Token to revoke |
token_type_hint | string | No | access_token or refresh_token |
client_id | string | Yes | Your OAuth client ID |
client_secret | string | Yes | Your 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 Code | Description |
|---|
invalid_request | Missing or malformed parameter |
invalid_client | Client authentication failed |
invalid_grant | Authorization code/refresh token invalid or expired |
unauthorized_client | Client not authorized for this grant type |
unsupported_grant_type | Grant type not supported |
invalid_scope | Scope invalid or exceeds client permissions |
access_denied | User denied authorization |
{
"error": "invalid_grant",
"error_description": "Authorization code has expired or was already used"
}
Next Steps