|
1 | 1 | import { nanoid } from "nanoid"; |
2 | 2 | import { z } from "zod"; |
3 | 3 | import { VERSION } from "../../version.js"; |
| 4 | +import { isAdditionalApiKey } from "../apiKeys.js"; |
4 | 5 | import type { ApiClientConfiguration } from "../apiClientManager-api.js"; |
5 | 6 | import { generateJWT } from "../jwt.js"; |
6 | 7 | import { |
@@ -201,6 +202,15 @@ export type { |
201 | 202 |
|
202 | 203 | export * from "./getBranch.js"; |
203 | 204 |
|
| 205 | +export type CreatePublicTokenRequestBody = { |
| 206 | + scopes: string[]; |
| 207 | + expirationTime?: string | number; |
| 208 | + oneTimeUse?: boolean; |
| 209 | + realtime?: { skipColumns?: string[] }; |
| 210 | +}; |
| 211 | + |
| 212 | +const CreatePublicTokenResponseBody = z.object({ token: z.string() }); |
| 213 | + |
204 | 214 | /** |
205 | 215 | * Trigger.dev v3 API client |
206 | 216 | */ |
@@ -230,6 +240,21 @@ export class ApiClient { |
230 | 240 | this.futureFlags = futureFlags; |
231 | 241 | } |
232 | 242 |
|
| 243 | + /** |
| 244 | + * Key for signing a public access token locally. Only root keys can do this — |
| 245 | + * an additional key isn't the environment's signing material, so a token |
| 246 | + * signed with one would never verify. Throw rather than return a dead token. |
| 247 | + */ |
| 248 | + get #selfSigningKey(): string { |
| 249 | + if (isAdditionalApiKey(this.accessToken)) { |
| 250 | + throw new Error( |
| 251 | + "This additional API key cannot self-sign public tokens, and the server did not return one. Upgrade the server or use the root API key." |
| 252 | + ); |
| 253 | + } |
| 254 | + |
| 255 | + return this.accessToken; |
| 256 | + } |
| 257 | + |
233 | 258 | get fetchClient(): typeof fetch { |
234 | 259 | const headers = this.#getHeaders(false); |
235 | 260 |
|
@@ -325,7 +350,7 @@ export class ApiClient { |
325 | 350 | const claims = claimsHeader ? JSON.parse(claimsHeader) : undefined; |
326 | 351 |
|
327 | 352 | const jwt = await generateJWT({ |
328 | | - secretKey: this.accessToken, |
| 353 | + secretKey: this.#selfSigningKey, |
329 | 354 | payload: { |
330 | 355 | ...claims, |
331 | 356 | scopes: [`read:runs:${data.id}`], |
@@ -359,11 +384,20 @@ export class ApiClient { |
359 | 384 | ) |
360 | 385 | .withResponse() |
361 | 386 | .then(async ({ data, response }) => { |
| 387 | + const jwtHeader = response.headers.get("x-trigger-jwt"); |
| 388 | + |
| 389 | + if (typeof jwtHeader === "string") { |
| 390 | + return { |
| 391 | + ...data, |
| 392 | + publicAccessToken: jwtHeader, |
| 393 | + }; |
| 394 | + } |
| 395 | + |
362 | 396 | const claimsHeader = response.headers.get("x-trigger-jwt-claims"); |
363 | 397 | const claims = claimsHeader ? JSON.parse(claimsHeader) : undefined; |
364 | 398 |
|
365 | 399 | const jwt = await generateJWT({ |
366 | | - secretKey: this.accessToken, |
| 400 | + secretKey: this.#selfSigningKey, |
367 | 401 | payload: { |
368 | 402 | ...claims, |
369 | 403 | scopes: [`read:batch:${data.id}`], |
@@ -407,11 +441,20 @@ export class ApiClient { |
407 | 441 | ) |
408 | 442 | .withResponse() |
409 | 443 | .then(async ({ data, response }) => { |
| 444 | + const jwtHeader = response.headers.get("x-trigger-jwt"); |
| 445 | + |
| 446 | + if (typeof jwtHeader === "string") { |
| 447 | + return { |
| 448 | + ...data, |
| 449 | + publicAccessToken: jwtHeader, |
| 450 | + }; |
| 451 | + } |
| 452 | + |
410 | 453 | const claimsHeader = response.headers.get("x-trigger-jwt-claims"); |
411 | 454 | const claims = claimsHeader ? JSON.parse(claimsHeader) : undefined; |
412 | 455 |
|
413 | 456 | const jwt = await generateJWT({ |
414 | | - secretKey: this.accessToken, |
| 457 | + secretKey: this.#selfSigningKey, |
415 | 458 | payload: { |
416 | 459 | ...claims, |
417 | 460 | scopes: [`read:batch:${data.id}`], |
@@ -1107,7 +1150,7 @@ export class ApiClient { |
1107 | 1150 | const claims = claimsHeader ? JSON.parse(claimsHeader) : undefined; |
1108 | 1151 |
|
1109 | 1152 | const jwt = await generateJWT({ |
1110 | | - secretKey: this.accessToken, |
| 1153 | + secretKey: this.#selfSigningKey, |
1111 | 1154 | payload: { |
1112 | 1155 | ...claims, |
1113 | 1156 | scopes: [`write:waitpoints:${data.id}`], |
@@ -1870,6 +1913,22 @@ export class ApiClient { |
1870 | 1913 | ); |
1871 | 1914 | } |
1872 | 1915 |
|
| 1916 | + async createPublicToken( |
| 1917 | + body: CreatePublicTokenRequestBody, |
| 1918 | + requestOptions?: ZodFetchOptions |
| 1919 | + ): Promise<{ token: string }> { |
| 1920 | + return zodfetch( |
| 1921 | + CreatePublicTokenResponseBody, |
| 1922 | + `${this.baseUrl}/api/v1/auth/public-tokens`, |
| 1923 | + { |
| 1924 | + method: "POST", |
| 1925 | + headers: this.#getHeaders(false), |
| 1926 | + body: JSON.stringify(body), |
| 1927 | + }, |
| 1928 | + mergeRequestOptions(this.defaultRequestOptions, requestOptions) |
| 1929 | + ); |
| 1930 | + } |
| 1931 | + |
1873 | 1932 | retrieveBatch(batchId: string, requestOptions?: ZodFetchOptions) { |
1874 | 1933 | return zodfetch( |
1875 | 1934 | RetrieveBatchV2Response, |
|
0 commit comments