MCP OAuth (connector authorization)
Operators connecting Claude.ai or ChatGPT MCP connectors do not call these routes manually — the connector platform runs the flow. Administrators must enable at least one tool domain on Account Settings → AI / MCP before authorization succeeds. Pair operator context with Landlord MCP — Connector OAuth and Management session authentication (browser JWT contrast). Workflow pairing: Setup sequence after go-live (hub: API Reference — Setup sequence after go-live).
Start with Who uses these routes, then Discovery and the Authorization code flow sequence. Token shapes: Token response. Scope and policy: Scopes and tool permissions and Redirect URI policy. Revocation: Revocation and re-authorization. Operator UI: Account Settings → AI / MCP. Full pairing matrix: MCP OAuth section cross-reference · API Reference — API guide cross-reference.
Vivin's Core API hosts an OAuth 2.1 Authorization Server for hosted MCP connectors (for example Claude.ai and ChatGPT). Connectors discover endpoints, register dynamically, run PKCE authorization code + refresh-token grants, and receive a management JWT the Landlord MCP server already accepts at /mcp.
This page documents the HTTP contract on the API host. For operator setup, consent behaviour, and when to use API keys instead, see Landlord MCP — Connector OAuth.
These routes are not the partner Authentication Bearer keys used on /{platform}-integration prefixes. They are also not POST /auth/login — see Management session authentication.
Who uses these routes
| Caller | Typical use |
|---|---|
| Claude.ai / ChatGPT MCP connectors | Full OAuth flow — discovery → register → authorize → token |
| Native/desktop MCP clients | Same flow with loopback redirect_uri values (http://localhost / http://127.0.0.1, RFC 8252) |
| IDE configs (Cursor, Claude Desktop) | Usually paste an API key Bearer token instead — no OAuth |
| Custom scripts | Prefer Management session login or Landlord MCP with an API key |
All routes are public (no Authorization header on discovery/register/authorize/token) but rate-limited per client IP. The consenting landlord is identified from a Vivin refresh cookie or email/password on the consent form — never from unauthenticated client-supplied account ids.
Discovery
RFC 8414 metadata is exposed at either well-known path on the API host:
GET /.well-known/oauth-authorization-server HTTP/1.1
Host: api.vivin.app
GET /.well-known/openid-configuration HTTP/1.1
Host: api.vivin.app
Both return the same JSON (CORS *, cache max-age=3600). Example shape:
{
"issuer": "https://api.vivin.app",
"authorization_endpoint": "https://api.vivin.app/oauth/authorize",
"token_endpoint": "https://api.vivin.app/oauth/token",
"registration_endpoint": "https://api.vivin.app/oauth/register",
"response_types_supported": ["code"],
"grant_types_supported": ["authorization_code", "refresh_token"],
"code_challenge_methods_supported": ["S256"],
"token_endpoint_auth_methods_supported": ["none", "client_secret_post"],
"scopes_supported": [
"bookings",
"listings",
"properties",
"tenants",
"maintenance",
"payments",
"bills",
"owners",
"analytics"
]
}
issuer and endpoint URLs follow the request host (including staging hosts such as dapi.vivin.app).
Dynamic client registration
RFC 7591 registration for trusted connector callbacks:
POST /oauth/register HTTP/1.1
Host: api.vivin.app
Content-Type: application/json
{
"client_name": "Claude MCP",
"redirect_uris": ["https://claude.ai/api/mcp/auth_callback"],
"grant_types": ["authorization_code", "refresh_token"],
"token_endpoint_auth_method": "none"
}
Response (201) — registered client metadata (optional client_secret when auth method requires it):
{
"client_id": "…",
"client_id_issued_at": 1710000000,
"redirect_uris": ["https://claude.ai/api/mcp/auth_callback"],
"grant_types": ["authorization_code", "refresh_token"],
"token_endpoint_auth_method": "none",
"client_name": "Claude MCP"
}
Registration rejects redirect URIs outside the trusted policy.
Authorization code flow
sequenceDiagram
participant Connector as MCP connector
participant API as Core API (OAuth)
participant Landlord as Landlord browser
participant MCP as Landlord MCP /mcp
Connector->>API: GET /.well-known/oauth-authorization-server
Connector->>API: POST /oauth/register
Connector->>Landlord: Redirect GET /oauth/authorize (PKCE challenge)
Landlord->>API: Consent (session cookie or login form)
API-->>Connector: Redirect with ?code=
Connector->>API: POST /oauth/token (code + code_verifier)
API-->>Connector: access_token (JWT) + refresh_token
Connector->>MCP: Bearer JWT → Mcp-Session-Id
MCP->>API: Tool calls with management JWT
Authorize
GET /oauth/authorize?response_type=code
&client_id=<client_id>
&redirect_uri=<registered_uri>
&code_challenge=<S256_challenge>
&code_challenge_method=S256
&state=<opaque>
&scope=bookings%20listings HTTP/1.1
Host: api.vivin.app
| Parameter | Requirement |
|---|---|
response_type | Must be code |
client_id | Registered client |
redirect_uri | Exact match to a registered URI (loopback: port-agnostic per RFC 8252) |
code_challenge / code_challenge_method | PKCE mandatory — only S256 accepted (plain rejected) |
scope | Optional space-separated subset of supported scopes |
state | Recommended opaque value echoed on redirect |
Behaviour
- If the landlord already has a valid Vivin refresh cookie, the HTML consent page shows their email and the tool domains they will grant.
- Otherwise the consent form collects email/password, then shows the same allow/deny UI.
- Allow redirects to
redirect_uriwith?code=…&state=…(authorization code TTL 120 seconds, single-use). - Deny redirects with
error=access_denied. - Invalid
client_idorredirect_urireturns an inline HTML error page (no redirect — avoids open-redirect leaks). - Zero enabled MCP tool domains for the account returns
access_denied— enable domains on AI / MCP → Tool permissions first.

Token
POST /oauth/token accepts application/x-www-form-urlencoded or JSON bodies.
Authorization code grant
POST /oauth/token HTTP/1.1
Host: api.vivin.app
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code=<authorization_code>
&redirect_uri=<same_as_authorize>
&client_id=<client_id>
&code_verifier=<pkce_verifier>
Refresh token grant
POST /oauth/token HTTP/1.1
Host: api.vivin.app
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token
&refresh_token=<refresh_token>
&client_id=<client_id>
Refresh tokens live 60 days, rotate on each successful refresh, and bind to the same client + landlord.
Token response
Response (200) — OAuth token envelope (no-store):
{
"access_token": "<management JWT>",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "<opaque refresh token>",
"scope": "bookings listings properties"
}
| Field | Meaning |
|---|---|
access_token | Same management JWT shape as POST /auth/login — use as Authorization: Bearer on Landlord MCP /mcp and Core API routes the user's role permits. JWT includes tokenSource: "mcp" for audit. |
expires_in | Seconds until JWT expiry (from JWT exp claim) |
refresh_token | Opaque refresh handle — 60-day lifetime, rotates on each refresh grant |
scope | Space-separated tool domains actually granted (intersection of account tool permissions, user role, and consent) |
After the initial Bearer exchange, Landlord MCP clients should send Mcp-Session-Id on follow-up streamable HTTP requests — see Landlord MCP — How it connects.
Scopes and tool permissions
Advertised scopes map one-to-one to Landlord MCP tool domains:
| OAuth scope | Landlord MCP domain |
|---|---|
bookings | Bookings tools |
listings | Listings tools |
properties | Properties tools |
tenants | Tenants tools |
maintenance | Maintenance / tickets tools |
payments | Payments tools |
bills | Bills tools |
owners | Owners tools |
analytics | Analytics / KPI tools |
Issued scope is the intersection of:
- Account Settings → AI / MCP → Tool permissions (account-wide Off / Read-only / Edit),
- The consenting user's role permissions and per-user AI access overrides, and
- The
scopeparameter on the authorize request (when present).
A domain set to Off at account level is omitted from the token even if the connector requests it.
Redirect URI policy
At registration and on every authorize / token call, redirect URIs must match the trust policy:
| Class | Examples |
|---|---|
| Anthropic Claude.ai | https://claude.ai/api/mcp/auth_callback |
| OpenAI ChatGPT | https://chatgpt.com/connector_platform_oauth_redirect, or any https://chatgpt.com/connector/oauth/… path prefix |
| Loopback (RFC 8252) | http://localhost:*/*, http://127.0.0.1:*/* — any port and path |
Arbitrary https:// origins, non-HTTPS remotes (except loopback), and data: / javascript: schemes are rejected — this blocks open-redirect token exfiltration.
Revocation and re-authorization
There is no public revoke endpoint for operators today. Access ends when:
- The refresh token expires (60 days without a successful refresh), or
- AI access is disabled for the user on Per-user permissions, or
- Every MCP tool domain is set to Off on refresh — the next
grant_type=refresh_tokencall returnsaccess_deniedand the connector must send the landlord through/oauth/authorizeagain.
Rotate compromised connector sessions by disabling AI access, then re-authorizing after policy is restored.
Error responses
OAuth errors use RFC 6749-style JSON on the token endpoint:
{
"error": "invalid_grant",
"error_description": "Authorization code expired or already used"
}
Common error values: invalid_client, invalid_grant, invalid_request, unsupported_grant_type, access_denied. Authorize-step protocol errors redirect to the registered redirect_uri with error and error_description query parameters when the redirect URI is already trusted.
For management JWT expiry on MCP tool calls (not OAuth refresh), see Error handling.
MCP OAuth section cross-reference
| Topic | Pair with |
|---|---|
| Discovery | Landlord MCP — Connector OAuth (operator narrative) |
| Authorization code flow | AI / MCP — Connector OAuth (settings UI) |
| Token response | Management session (JWT login contrast), Landlord MCP — How it connects (Mcp-Session-Id) |
| Scopes and tool permissions | AI / MCP — Tool permissions, Users and roles |
| Revocation and re-authorization | AI / MCP — Per-user permissions |
Related
Setup sequence after go-live
- Enable at least one domain on AI / MCP → Tool permissions before connector authorization
- Pair connector setup with Automation & AI — External AI clients (MCP)
- Poll AI usage (
landlord_chat) after teams adopt Claude/ChatGPT connectors
Companion API guides
- Management session authentication — Browser
POST /auth/loginJWT (distinct from OAuth-issued tokens) - AI usage — Internal LLM cost ledger on the same management JWT
- Error handling —
401/403on Core API and MCP tool calls - Landlord MCP — Tool catalogue and
/mcpsession headers