diff --git a/spx-gui/package-lock.json b/spx-gui/package-lock.json index 8af7064aa0..98f3e09be4 100644 --- a/spx-gui/package-lock.json +++ b/spx-gui/package-lock.json @@ -44,7 +44,6 @@ "hast": "^1.0.0", "hast-util-raw": "^9.1.0", "hast-util-sanitize": "^5.0.2", - "jwt-decode": "^4.0.0", "konva": "^9.3.1", "localforage": "^1.10.0", "lodash": "^4.17.21", diff --git a/spx-gui/package.json b/spx-gui/package.json index 898991f330..3c1f080953 100644 --- a/spx-gui/package.json +++ b/spx-gui/package.json @@ -51,7 +51,6 @@ "hast": "^1.0.0", "hast-util-raw": "^9.1.0", "hast-util-sanitize": "^5.0.2", - "jwt-decode": "^4.0.0", "konva": "^9.3.1", "localforage": "^1.10.0", "lodash": "^4.17.21", diff --git a/spx-gui/src/apis/common/client.test.ts b/spx-gui/src/apis/common/client.test.ts index 447cba80ac..474e7f7874 100644 --- a/spx-gui/src/apis/common/client.test.ts +++ b/spx-gui/src/apis/common/client.test.ts @@ -112,4 +112,18 @@ describe('Client', () => { expect(globalFetchMock).toHaveBeenCalledTimes(1) }) }) + + describe('explicit authorization headers', () => { + it('should preserve an explicit Authorization header instead of overwriting it with the token provider', async () => { + fetchMock.mockResolvedValueOnce(new Response(JSON.stringify({ username: 'alice' }), { status: 200 })) + client.setTokenProvider(async () => 'shared-token') + + await client.get('/user', undefined, { + headers: new Headers({ Authorization: 'Bearer direct-token' }) + }) + + const req = fetchMock.mock.calls[0]?.[0] as Request + expect(req.headers.get('Authorization')).toBe('Bearer direct-token') + }) + }) }) diff --git a/spx-gui/src/apis/common/client.ts b/spx-gui/src/apis/common/client.ts index 27f2ad19e0..14c0b0e9f6 100644 --- a/spx-gui/src/apis/common/client.ts +++ b/spx-gui/src/apis/common/client.ts @@ -97,7 +97,7 @@ export class Client { options?.signal?.throwIfAborted() const headers = options?.headers ?? new Headers() if (body != null) headers.set('Content-Type', 'application/json') - if (token != null) headers.set('Authorization', `Bearer ${token}`) + if (token != null && !headers.has('Authorization')) headers.set('Authorization', `Bearer ${token}`) if (sentryTraceHeader != null) headers.set('Sentry-Trace', sentryTraceHeader) if (sentryBaggageHeader != null) headers.set('Baggage', sentryBaggageHeader) return new Request(url, { method, headers, body }) @@ -143,7 +143,7 @@ export class Client { const token = await this.tokenProvider() options?.signal?.throwIfAborted() const headers = options?.headers ?? new Headers() - if (token != null) headers.set('Authorization', `Bearer ${token}`) + if (token != null && !headers.has('Authorization')) headers.set('Authorization', `Bearer ${token}`) if (sentryTraceHeader != null) headers.set('Sentry-Trace', sentryTraceHeader) if (sentryBaggageHeader != null) headers.set('Baggage', sentryBaggageHeader) const req = new Request(url, { method, headers, body: payload }) diff --git a/spx-gui/src/apis/user.ts b/spx-gui/src/apis/user.ts index a7cda5fa20..f0d21074f8 100644 --- a/spx-gui/src/apis/user.ts +++ b/spx-gui/src/apis/user.ts @@ -48,8 +48,8 @@ export async function isUsernameTaken(username: string) { } } -export function getSignedInUser(): Promise { - return client.get(`/user`) as Promise +export function getSignedInUser(options?: { headers?: Headers }): Promise { + return client.get(`/user`, undefined, options) as Promise } export type UpdateSignedInUserParams = Partial> diff --git a/spx-gui/src/pages/sign-in/token.vue b/spx-gui/src/pages/sign-in/token.vue index 357879f0f4..58c06f13b5 100644 --- a/spx-gui/src/pages/sign-in/token.vue +++ b/spx-gui/src/pages/sign-in/token.vue @@ -25,16 +25,14 @@ html-type="submit" :loading="handleSubmit.isLoading.value" > - {{ buttonText }} + {{ $t({ en: 'Sign in', zh: '登录' }) }}