The exchange
1
We send the user to you
During authorization we resolve the agent to its connection and redirect the browser to your login URL with four query parameters:
We send no
return_to. The callback is a fixed URL, so there is no redirect target for you to validate.2
You authenticate the user
Their existing session if they have one, your normal login if they do not. We never see credentials.
3
You redirect back with an assertion
4
We verify, ask for consent, and issue the token
We match
state to the request we stashed, verify the assertion against your key, show our consent screen, record the grant, and hand the client a code.state is claimed exactly once.
The assertion
A JWT signed with your private key — the same key you already sign session tokens and mint responses with.string
required
The subject. Opaque to us: we carry it into the grant, the run, and the token we mint against your
mint_url, and never parse it. It must be the same subject your own API authorizes — see Subjects.string
required
Exactly
https://api.usepatchwork.co/oauth/authorize/callback. Binds the assertion to this endpoint so it cannot be replayed elsewhere.string
required
The
state value we sent you. Binds this assertion to this one authorization request.integer
required
Expiry. Keep it short — a couple of minutes is plenty for a redirect. We allow 30 seconds of clock skew.
string
required
JWT header, not a claim. Names the signing key so we can match it in your JWKS.
RS256, RS384, RS512, ES256, ES384, ES512, PS256, PS384 or PS512. HS* is refused — we hold only your public key, and accepting a symmetric algorithm against a public key is a forgery.
Which key we check it against
The connection’s ownjwks_uri if it has one, otherwise your workspace’s. Most integrations have one key and should leave the connection’s blank; set it only when a connection has a genuinely separate signing key for a separate user pool. Either way it is the key you already published, matched by kid.
Minimal implementation
It is a page, not a redirect
The bridge is yours, so anything that decides who the subject is belongs here — and this is the only place it can live, because we do not model your tenants.- Tenant or workspace choice. A user who belongs to several picks one, and you bake the choice into
sub—user_9f2:acme. The MCP client then acts in that tenant for the life of the grant. - Step-up MFA, seat and plan checks, blocking a user, an interstitial explaining what is about to be connected: your policy, before you mint the subject.
Why it is safe
Checklist
- The URL is set on the connection the agent uses.
- Signed in, the bridge redirects back without the user doing anything.
- Signed out, it logs them in and then redirects back.
audis the callback URL exactly, including the scheme and host.nonceis thestateyou were given, unmodified.- The signature is asymmetric and the
kidis in your published JWKS. subis the same subject string your own API authorizes, and your mint endpoint will accept it from the relay branch — that is how the agent gets a token for your backend on every MCP call.