| 56 | export type SignKiloTokenExtra = Pick< |
| 57 | KiloTokenPayload, |
| 58 | | 'isAdmin' |
| 59 | | 'gastownAccess' |
| 60 | | 'botId' |
| 61 | | 'organizationId' |
| 62 | | 'organizationRole' |
| 63 | | 'internalApiUse' |
| 64 | | 'createdOnPlatform' |
| 65 | | 'tokenSource' |
| 66 | | 'deviceAuthRequestCode' |
| 67 | | 'deviceSessionId' |
| 68 | | 'orgMemberships' |
| 69 | | 'runtimeAdmission' |
| 70 | | 'runtimeAuthorization' |
| 71 | >; |
| 72 | |
| 73 | export async function signKiloToken(params: { |
| 74 | userId: string; |
| 75 | /** |
| 76 | * Omit (or pass `undefined`) to mint an internal-service token with no |
| 77 | * `apiTokenPepper` claim at all — verifiers treat an absent claim as |
| 78 | * "skip pepper comparison", unlike an explicit `null`, which is compared |
| 79 | * against the account's current pepper. |
| 80 | */ |
| 81 | pepper?: string | null; |
| 82 | secret: string; |
| 83 | expiresInSeconds: number; |
| 84 | audience?: string; |
| 85 | env?: string; |
| 86 | extra?: SignKiloTokenExtra; |
| 87 | }): Promise<{ token: string; expiresAt: string }> { |
| 88 | const now = Math.floor(Date.now() / 1000); |
| 89 | const exp = now + params.expiresInSeconds; |
| 90 | |
| 91 | const payload: Record<string, unknown> = { |
| 92 | kiloUserId: params.userId, |
| 93 | apiTokenPepper: params.pepper, |
| 94 | version: KILO_TOKEN_VERSION, |