From cc81a35db7b320ccd7e63ee10c93b8aac8fad866 Mon Sep 17 00:00:00 2001 From: Matthew Phillips Date: Thu, 14 Mar 2024 08:50:41 -0400 Subject: [PATCH 1/4] Create new token when current has expired --- packages/db/src/core/tokens.ts | 46 +++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/packages/db/src/core/tokens.ts b/packages/db/src/core/tokens.ts index 379a3f68111b..89995b3343de 100644 --- a/packages/db/src/core/tokens.ts +++ b/packages/db/src/core/tokens.ts @@ -31,9 +31,20 @@ class ManagedRemoteAppToken implements ManagedAppToken { session: string; projectId: string; ttl: number; + expires: Date; renewTimer: NodeJS.Timeout | undefined; static async create(sessionToken: string, projectId: string) { + const { token: shortLivedAppToken, ttl } = await this.createToken(sessionToken, projectId); + return new ManagedRemoteAppToken({ + token: shortLivedAppToken, + session: sessionToken, + projectId, + ttl, + }); + } + + static async createToken(sessionToken: string, projectId: string): Promise<{ token: string; ttl: number; }> { const spinner = ora('Connecting to remote database...').start(); const response = await safeFetch( new URL(`${getAstroStudioUrl()}/auth/cli/token-create`), @@ -54,13 +65,8 @@ class ManagedRemoteAppToken implements ManagedAppToken { await new Promise((resolve) => setTimeout(resolve, 2000)); spinner.succeed(green('Connected to remote database.')); - const { token: shortLivedAppToken, ttl } = await response.json(); - return new ManagedRemoteAppToken({ - token: shortLivedAppToken, - session: sessionToken, - projectId, - ttl, - }); + const { token, ttl } = await response.json(); + return { token, ttl }; } constructor(options: { token: string; session: string; projectId: string; ttl: number }) { @@ -69,6 +75,7 @@ class ManagedRemoteAppToken implements ManagedAppToken { this.projectId = options.projectId; this.ttl = options.ttl; this.renewTimer = setTimeout(() => this.renew(), (1000 * 60 * 5) / 2); + this.expires = getExpiresFromTtl(this.ttl); } private async fetch(url: string, body: Record) { @@ -88,24 +95,30 @@ class ManagedRemoteAppToken implements ManagedAppToken { ); } + tokenIsValid() { + return new Date() > this.expires; + } + async renew() { clearTimeout(this.renewTimer); delete this.renewTimer; - try { + + if(this.tokenIsValid()) { const response = await this.fetch('/auth/cli/token-renew', { token: this.token, projectId: this.projectId, }); if (response.status === 200) { + this.expires = getExpiresFromTtl(this.ttl); this.renewTimer = setTimeout(() => this.renew(), (1000 * 60 * this.ttl) / 2); } else { throw new Error(`Unexpected response: ${response.status} ${response.statusText}`); - } - } catch (error: any) { - const retryIn = (60 * this.ttl) / 10; - // eslint-disable-next-line no-console - console.error(`Failed to renew token. Retrying in ${retryIn} seconds.`, error?.message); - this.renewTimer = setTimeout(() => this.renew(), retryIn * 1000); + } + } else { + const { token, ttl } = await ManagedRemoteAppToken.createToken(this.session, this.projectId); + this.token = token; + this.ttl = ttl; + this.expires = getExpiresFromTtl(ttl); } } @@ -163,3 +176,8 @@ export async function getManagedAppTokenOrExit(token?: string): Promise Date: Thu, 14 Mar 2024 09:00:57 -0400 Subject: [PATCH 2/4] Add changeset --- .changeset/long-buckets-end.md | 5 +++++ packages/db/src/core/tokens.ts | 7 ++++++- 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 .changeset/long-buckets-end.md diff --git a/.changeset/long-buckets-end.md b/.changeset/long-buckets-end.md new file mode 100644 index 000000000000..efeee9af90bc --- /dev/null +++ b/.changeset/long-buckets-end.md @@ -0,0 +1,5 @@ +--- +"@astrojs/db": patch +--- + +Fetch new app token when previous has expired diff --git a/packages/db/src/core/tokens.ts b/packages/db/src/core/tokens.ts index 89995b3343de..dc390aab3fc8 100644 --- a/packages/db/src/core/tokens.ts +++ b/packages/db/src/core/tokens.ts @@ -99,6 +99,10 @@ class ManagedRemoteAppToken implements ManagedAppToken { return new Date() > this.expires; } + createRenewalTimer() { + return setTimeout(() => this.renew(), (1000 * 60 * this.ttl) / 2); + } + async renew() { clearTimeout(this.renewTimer); delete this.renewTimer; @@ -110,7 +114,7 @@ class ManagedRemoteAppToken implements ManagedAppToken { }); if (response.status === 200) { this.expires = getExpiresFromTtl(this.ttl); - this.renewTimer = setTimeout(() => this.renew(), (1000 * 60 * this.ttl) / 2); + this.renewTimer = this.createRenewalTimer(); } else { throw new Error(`Unexpected response: ${response.status} ${response.statusText}`); } @@ -119,6 +123,7 @@ class ManagedRemoteAppToken implements ManagedAppToken { this.token = token; this.ttl = ttl; this.expires = getExpiresFromTtl(ttl); + this.renewTimer = this.createRenewalTimer(); } } From f443f44b2bd3b0cff31c22c7efdb0c86943d7292 Mon Sep 17 00:00:00 2001 From: Matthew Phillips Date: Thu, 14 Mar 2024 10:28:26 -0400 Subject: [PATCH 3/4] Rename renew timer function: --- packages/db/src/core/tokens.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/db/src/core/tokens.ts b/packages/db/src/core/tokens.ts index dc390aab3fc8..7ce9036782db 100644 --- a/packages/db/src/core/tokens.ts +++ b/packages/db/src/core/tokens.ts @@ -99,7 +99,7 @@ class ManagedRemoteAppToken implements ManagedAppToken { return new Date() > this.expires; } - createRenewalTimer() { + createRenewTimer() { return setTimeout(() => this.renew(), (1000 * 60 * this.ttl) / 2); } @@ -114,7 +114,7 @@ class ManagedRemoteAppToken implements ManagedAppToken { }); if (response.status === 200) { this.expires = getExpiresFromTtl(this.ttl); - this.renewTimer = this.createRenewalTimer(); + this.renewTimer = this.createRenewTimer(); } else { throw new Error(`Unexpected response: ${response.status} ${response.statusText}`); } @@ -123,7 +123,7 @@ class ManagedRemoteAppToken implements ManagedAppToken { this.token = token; this.ttl = ttl; this.expires = getExpiresFromTtl(ttl); - this.renewTimer = this.createRenewalTimer(); + this.renewTimer = this.createRenewTimer(); } } From 9ea41c415aef4c2d3e5d8042d64108ca671cd3a2 Mon Sep 17 00:00:00 2001 From: Matthew Phillips Date: Fri, 15 Mar 2024 08:11:15 -0400 Subject: [PATCH 4/4] Handle creating a new token on renewal --- packages/db/src/core/tokens.ts | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/packages/db/src/core/tokens.ts b/packages/db/src/core/tokens.ts index 7ce9036782db..b5c441d4201d 100644 --- a/packages/db/src/core/tokens.ts +++ b/packages/db/src/core/tokens.ts @@ -119,11 +119,17 @@ class ManagedRemoteAppToken implements ManagedAppToken { throw new Error(`Unexpected response: ${response.status} ${response.statusText}`); } } else { - const { token, ttl } = await ManagedRemoteAppToken.createToken(this.session, this.projectId); - this.token = token; - this.ttl = ttl; - this.expires = getExpiresFromTtl(ttl); - this.renewTimer = this.createRenewTimer(); + try { + const { token, ttl } = await ManagedRemoteAppToken.createToken(this.session, this.projectId); + this.token = token; + this.ttl = ttl; + this.expires = getExpiresFromTtl(ttl); + this.renewTimer = this.createRenewTimer(); + } catch { + // If we get here we couldn't create a new token. Since the existing token + // is expired we really can't do anything and should exit. + throw new Error(`Token has expired and attempts to renew it have failed, please try again.`); + } } }