mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-13 14:32:03 +00:00
fix(mcp): close OAuth 2.1 resource-server gaps before the remote surface ships
Harden the remote MCP endpoint and the in-repo OAuth bridge against the adversarial review of the milestone branch. The two ship-blockers made the "read-only, revocable" promise false; the rest tightened the surface. Audience binding at the resource server: a token whose only grant is the new `health:read` scope is the MCP read credential. The REST edge now accepts it on safe (read) methods and refuses it on writes/deletes with 403, so a connector token can read over `/mcp` and GET routes but can never POST/DELETE the record despite a "read-only" consent. The `/mcp` resolver is unaffected. Revocable, theft-detectable refresh tokens: refresh artifacts now anchor to a persistent connection row. Revoking a connection stops every future refresh and kills its access tokens; a replayed (already-rotated) refresh is treated as theft and revokes the whole connection. Connections surface in settings as the unit a user can disconnect. The old access token is revoked on rotation. Also: fail closed on the OAuth/`/mcp` surface when no canonical origin is configured and mark the discovery documents non-cacheable; honour the global API kill-switch on `/mcp`, `/token`, `/authorize`, and `/register`; cap the CIMD and registration body reads while streaming instead of after; show the verified client origin on consent; validate CIMD redirect-URI schemes; assert same-origin on the consent POST; and add a daily prune for expired connector tokens. New migration 0195 adds the connection anchor and links access tokens to it.
This commit is contained in:
1 parent
06363771ee
commit
22eb9cba0d
39 files changed
+1826
-72
No files matched your search
@@ -29,6 +29,12 @@ export interface IssueTokenOptions {
|
||||
* carries continuity). Ignored when `expiresInDays` alone is supplied.
|
||||
*/
|
||||
expiresInMinutes?: number;
|
||||
/**
|
||||
* Links this access token to an MCP OAuth connection (H2). Set only by the
|
||||
* `/api/mcp/oauth` bridge so a connection revoke can kill every access row it
|
||||
* issued and the connector token list can exclude the transient access rows.
|
||||
*/
|
||||
mcpConnectionId?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -53,6 +59,9 @@ export async function issueApiToken(
|
||||
tokenHash: tokenHashValue,
|
||||
permissions: opts.permissions ?? ["*"],
|
||||
expiresAt,
|
||||
...(opts.mcpConnectionId
|
||||
? { mcpConnectionId: opts.mcpConnectionId }
|
||||
: {}),
|
||||
},
|
||||
select: { id: true },
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user