fix: the MCP dispatch credential carries the personal token id

MCPUserGuard.MCPDispatch now sets the in-memory credential's ID to the
subject's TokenID when the subject came from a personal usr_ token (no
ClientID), so a handler running under MCP dispatch can record which
token made a change. An OAuth subject's TokenID names the OAuth
server's token row, so its credential keeps ID 0. The credential is
still never saved.
This commit is contained in:
2026-10-28 11:30:44 +01:00
parent 07196a4bcb
commit a1b78b80f5
3 changed files with 68 additions and 6 deletions

View File

@@ -114,9 +114,13 @@ func (g *MCPUserGuard) MCPAllows(ctx context.Context, subject pact.MCPSubject, r
// MCPDispatch builds the principal (the user with the group grants, as the
// api_token guard loads it) and an in-memory token credential carrying the
// session's scopes, so user.scope sees what the session was granted. The
// credential has ID 0 and is never saved. The marker is honoured by the
// api_token guard only.
// session's scopes, so user.scope sees what the session was granted. For a
// personal-token subject (ClientID empty) the credential's ID is the
// subject's TokenID, the user_api_tokens row the session was opened with, so
// a handler can record which token made a change; an OAuth subject's TokenID
// is the OAuth server's own token row, not one of this plugin's, and its
// credential keeps ID 0. The credential is never saved. The marker is
// honoured by the api_token guard only.
func (g *MCPUserGuard) MCPDispatch(ctx context.Context, subject pact.MCPSubject, _ string) (pact.MCPDispatch, error) {
db, err := g.db()
if err != nil {
@@ -133,6 +137,9 @@ func (g *MCPUserGuard) MCPDispatch(ctx context.Context, subject pact.MCPSubject,
UserID: subject.UserID,
Scopes: lagoon.Jsonable[[]string]{Data: slices.Clone(subject.Scopes), Valid: true},
}
if subject.ClientID == "" {
credential.ID = subject.TokenID
}
return pact.MCPDispatch{Principal: principal, Credential: credential, Guards: []string{"api_token"}}, nil
}