feat(api): separate API keys from MCP tokens, add travellers

An MCP token drives every assistant tool; an API key reads trips over HTTP.
Handing the same credential to a third-party integration gives it the whole
MCP surface, so the two are now separate kinds on `mcp_tokens` and the kind
sits in the WHERE clause — a token of the wrong kind is indistinguishable
from one that does not exist.

Also adds `travellers` to the trip payload: display name and an owner flag,
no ids and no email addresses. An integration key reads its owner's
itinerary; it is not a way to enumerate the people around them.

Documents the surface in the wiki, since integrators need something to read
that is not the source.
This commit is contained in:
Maurice
2026-08-28 01:29:04 +02:00
committed by Maurice
parent 9cddf683a5
commit 4b120d39c8
9 changed files with 381 additions and 20 deletions
+37
View File
@@ -321,6 +321,43 @@ export class AuthController {
return { success: true };
}
/**
* Integration keys for the public API (`/api/v1`).
*
* Separate from the MCP tokens above because they open different doors: an MCP
* token drives every assistant tool, an API key reads trips over HTTP. Same
* table, different `kind`, and each surface verifies only its own — so a key
* handed to a third-party integration cannot be replayed against /mcp.
*
* Unlike MCP tokens these are not forbidden on a managed instance: the surface
* they unlock is read-only and scoped to the caller's own trips, which is the
* whole reason it exists.
*/
@Get('api-tokens')
listApiTokens(@CurrentUser() user: User) {
return { tokens: this.tokens.listApiTokens(user.id) };
}
@Post('api-tokens')
@HttpCode(201)
createApiToken(@CurrentUser() user: User, @Body() body: McpTokenCreateDto, @Req() req: Request) {
this.limit('login', req, 5);
const result = this.tokens.createApiToken(user.id, body.name);
if (result.error) {
throw new HttpException({ error: result.error }, result.status!);
}
return { token: result.token };
}
@Delete('api-tokens/:id')
deleteApiToken(@CurrentUser() user: User, @Param('id') id: string) {
const result = this.tokens.deleteApiToken(user.id, id);
if (result.error) {
throw new HttpException({ error: result.error }, result.status!);
}
return { success: true };
}
@Post('ws-token')
@HttpCode(200)
wsToken(@CurrentUser() user: User) {