Skip to content

Commit 704026c

Browse files
amikofalvyclaude
andauthored
Bind tenant/project into anonymous session JWTs for global apps (#2924)
For global apps, embed the tenant/project from request headers into the anonymous session JWT at creation time. On refresh, preserve the original JWT's tenant binding instead of re-reading from headers. In the run auth middleware, extract tid/pid from the anonymous JWT payload and use them for scope resolution instead of falling back to request headers. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent c278ea4 commit 704026c

2 files changed

Lines changed: 18 additions & 2 deletions

File tree

.changeset/then-gold-deer.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@inkeep/agents-api": patch
3+
---
4+
5+
Bind tenant/project into anonymous session JWTs for global apps

agents-api/src/domains/run/routes/auth.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,8 @@ app.openapi(
180180

181181
const secret = getAnonJwtSecret();
182182
let anonUserId: string | undefined;
183+
let refreshTenantId: string | null | undefined;
184+
let refreshProjectId: string | null | undefined;
183185

184186
const authHeader = c.req.header('Authorization');
185187
if (authHeader?.startsWith('Bearer ')) {
@@ -196,6 +198,8 @@ app.openapi(
196198
payload.sub.startsWith('anon_')
197199
) {
198200
anonUserId = payload.sub;
201+
refreshTenantId = typeof payload.tid === 'string' ? payload.tid : null;
202+
refreshProjectId = typeof payload.pid === 'string' ? payload.pid : null;
199203
} else {
200204
logger.debug(
201205
{ appId, tokenApp: payload.app, tokenType: payload.type },
@@ -222,14 +226,21 @@ app.openapi(
222226
anonUserId = `anon_${crypto.randomUUID()}`;
223227
}
224228

229+
const tenantId = isRefresh
230+
? (refreshTenantId ?? appRecord.tenantId)
231+
: appRecord.tenantId || c.req.header('x-inkeep-tenant-id') || null;
232+
const projectId = isRefresh
233+
? (refreshProjectId ?? appRecord.projectId)
234+
: appRecord.projectId || c.req.header('x-inkeep-project-id') || null;
235+
225236
const lifetimeSeconds = env.INKEEP_ANON_SESSION_LIFETIME_SECONDS;
226237
const now = Math.floor(Date.now() / 1000);
227238
const exp = now + lifetimeSeconds;
228239
const expiresAt = new Date(exp * 1000).toISOString();
229240

230241
const token = await new SignJWT({
231-
tid: appRecord.tenantId,
232-
pid: appRecord.projectId,
242+
tid: tenantId,
243+
pid: projectId,
233244
app: appId,
234245
type: 'anonymous',
235246
})

0 commit comments

Comments
 (0)