Skip to content

Commit 4b7a4ef

Browse files
committed
evoke old token on re-login to prevent token accumulation
1 parent ae8c505 commit 4b7a4ef

4 files changed

Lines changed: 36 additions & 6 deletions

File tree

src/commands/auth/login.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import { App } from '../../components/App.js';
66
import { KeyValue } from '../../components/KeyValue.js';
77
import {
88
setToken,
9+
setTokenId,
10+
getTokenId,
911
setApiUrl,
1012
setWorkspaceId,
1113
setProjectId,
@@ -123,7 +125,8 @@ function LoginCommand({
123125
}
124126

125127
authServerRef.current = server;
126-
const url = getLoginWithCliAuthUrl(server.port, server.state);
128+
const oldTokenId = getTokenId();
129+
const url = getLoginWithCliAuthUrl(server.port, server.state, oldTokenId);
127130
setAuthUrl(url);
128131

129132
await openWithFallback(url);
@@ -140,6 +143,9 @@ function LoginCommand({
140143
}
141144

142145
setTokenValue(result.token);
146+
if (result.tokenId) {
147+
setTokenId(result.tokenId);
148+
}
143149
setStep('verifying');
144150
server.close();
145151
} catch (err) {

src/lib/auth-server.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { randomBytes } from 'node:crypto';
33

44
export interface AuthResult {
55
token: string;
6+
tokenId: number | null;
67
email: string;
78
}
89

@@ -135,6 +136,7 @@ export async function startAuthServer(options?: {
135136
const callbackToken = url.searchParams.get('token');
136137
const callbackState = url.searchParams.get('state');
137138
const callbackEmail = url.searchParams.get('email') || '';
139+
const callbackTokenId = url.searchParams.get('tokenId');
138140

139141
// Validate state parameter
140142
if (callbackState !== state) {
@@ -155,7 +157,12 @@ export async function startAuthServer(options?: {
155157
res.end(SUCCESS_HTML);
156158

157159
clearTimeout(timeoutId);
158-
resolveAuth!({ token: callbackToken, email: callbackEmail });
160+
const parsedTokenId = callbackTokenId ? parseInt(callbackTokenId, 10) : null;
161+
resolveAuth!({
162+
token: callbackToken,
163+
tokenId: Number.isNaN(parsedTokenId) ? null : parsedTokenId,
164+
email: callbackEmail,
165+
});
159166
});
160167

161168
// Bind to localhost only

src/lib/config.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export interface EnvironmentUrls {
1919
// ─── Config Interface ──────────────────────────────────────
2020
export interface UnlayerConfig {
2121
token?: string;
22+
tokenId?: number | null;
2223
tokenEnvVar?: string;
2324
apiUrl?: string;
2425
workspaceId?: number | null;
@@ -33,6 +34,9 @@ const schema = {
3334
token: {
3435
type: 'string' as const,
3536
},
37+
tokenId: {
38+
type: ['number', 'null'] as const,
39+
},
3640
tokenEnvVar: {
3741
type: 'string' as const,
3842
default: DEFAULT_TOKEN_ENV_VAR,
@@ -204,6 +208,15 @@ export function setToken(token: string): void {
204208

205209
export function clearToken(): void {
206210
config.delete('token');
211+
config.delete('tokenId');
212+
}
213+
214+
export function getTokenId(): number | null | undefined {
215+
return config.get('tokenId');
216+
}
217+
218+
export function setTokenId(id: number | null): void {
219+
config.set('tokenId', id);
207220
}
208221

209222
export function getApiUrl(): string {

src/lib/urls.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,17 @@ export function getProjectCreateUrl(workspaceId: number | string): string {
2424
}
2525

2626
/** CLI auth API route on the console (creates PAT and redirects to localhost callback) */
27-
export function getCliAuthUrl(port: number, state: string): string {
28-
return `${getConsoleUrl()}/api/cli-auth?port=${port}&state=${encodeURIComponent(state)}`;
27+
export function getCliAuthUrl(port: number, state: string, tokenId?: number | null): string {
28+
let url = `${getConsoleUrl()}/api/cli-auth?port=${port}&state=${encodeURIComponent(state)}`;
29+
if (tokenId) {
30+
url += `&tokenId=${tokenId}`;
31+
}
32+
return url;
2933
}
3034

3135
/** Accounts login page with returnUrl pointing to cli-auth (chains: login → PAT → localhost) */
32-
export function getLoginWithCliAuthUrl(port: number, state: string): string {
33-
const cliAuthUrl = getCliAuthUrl(port, state);
36+
export function getLoginWithCliAuthUrl(port: number, state: string, tokenId?: number | null): string {
37+
const cliAuthUrl = getCliAuthUrl(port, state, tokenId);
3438
return `${getAccountsUrl()}/?returnUrl=${encodeURIComponent(cliAuthUrl)}`;
3539
}
3640

0 commit comments

Comments
 (0)