diff --git a/.changeset/rotten-clocks-report.md b/.changeset/rotten-clocks-report.md new file mode 100644 index 000000000000..3e0189e5bde8 --- /dev/null +++ b/.changeset/rotten-clocks-report.md @@ -0,0 +1,5 @@ +--- +"@astrojs/db": patch +--- + +Fix an issue where short-lived tokens were not being authorized diff --git a/packages/db/src/core/tokens.ts b/packages/db/src/core/tokens.ts index e7f9c630ff0e..ea284c067caa 100644 --- a/packages/db/src/core/tokens.ts +++ b/packages/db/src/core/tokens.ts @@ -3,7 +3,7 @@ import { homedir } from 'node:os'; import { join } from 'node:path'; import { pathToFileURL } from 'node:url'; import { MISSING_PROJECT_ID_ERROR, MISSING_SESSION_ID_ERROR } from './errors.js'; -import { getAstroStudioEnv, getAstroStudioUrl, safeFetch } from './utils.js'; +import { getAstroStudioEnv, getAstroStudioUrl, getRemoteDatabaseUrl, safeFetch } from './utils.js'; export const SESSION_LOGIN_FILE = pathToFileURL(join(homedir(), '.astro', 'session-token')); export const PROJECT_ID_FILE = pathToFileURL(join(process.cwd(), '.astro', 'link')); @@ -26,11 +26,20 @@ class ManagedLocalAppToken implements ManagedAppToken { class ManagedRemoteAppToken implements ManagedAppToken { token: string; session: string; + region: string | undefined; projectId: string; ttl: number; renewTimer: NodeJS.Timeout | undefined; + static async getRegionCode(): Promise { + const pingResponse = await safeFetch(new URL(`${getRemoteDatabaseUrl()}/ping`)); + const pingResult = await pingResponse.json() as {success: true, data: {region: string} }; + return pingResult.data.region; + } + + static async create(sessionToken: string, projectId: string) { + const region = await ManagedRemoteAppToken.getRegionCode(); const response = await safeFetch( new URL(`${getAstroStudioUrl()}/auth/cli/token-create`), { @@ -38,7 +47,7 @@ class ManagedRemoteAppToken implements ManagedAppToken { headers: new Headers({ Authorization: `Bearer ${sessionToken}`, }), - body: JSON.stringify({ projectId }), + body: JSON.stringify({ projectId, region }), }, (res) => { throw new Error(`Failed to create token: ${res.status} ${res.statusText}`); @@ -49,20 +58,22 @@ class ManagedRemoteAppToken implements ManagedAppToken { return new ManagedRemoteAppToken({ token: shortLivedAppToken, session: sessionToken, + region, projectId, ttl, }); } - constructor(options: { token: string; session: string; projectId: string; ttl: number }) { + constructor(options: { token: string; session: string; region: string | undefined; projectId: string; ttl: number }) { this.token = options.token; this.session = options.session; + this.region = options.region; this.projectId = options.projectId; this.ttl = options.ttl; this.renewTimer = setTimeout(() => this.renew(), (1000 * 60 * 5) / 2); } - private async fetch(url: string, body: unknown) { + private async fetch(url: string, body: Record) { return safeFetch( `${getAstroStudioUrl()}${url}`, { @@ -71,9 +82,9 @@ class ManagedRemoteAppToken implements ManagedAppToken { Authorization: `Bearer ${this.session}`, 'Content-Type': 'application/json', }, - body: JSON.stringify(body), + body: JSON.stringify({...body, region: this.region}), }, - () => { + () => { throw new Error(`Failed to fetch ${url}.`); } );