From 435ea200d877077a5c942b0f9ce1f92b8b9be05b Mon Sep 17 00:00:00 2001 From: te2wow Date: Thu, 12 Feb 2026 14:58:18 +0900 Subject: [PATCH 1/3] fix(auth-js): handle absolute URL in basePath --- .changeset/fix-auth-js-absolute-basepath.md | 5 +++++ packages/auth-js/src/client.ts | 12 ++++++++++++ packages/auth-js/src/react.tsx | 5 +++-- 3 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 .changeset/fix-auth-js-absolute-basepath.md diff --git a/.changeset/fix-auth-js-absolute-basepath.md b/.changeset/fix-auth-js-absolute-basepath.md new file mode 100644 index 000000000..6adc3b6b9 --- /dev/null +++ b/.changeset/fix-auth-js-absolute-basepath.md @@ -0,0 +1,5 @@ +--- +'@hono/auth-js': patch +--- + +Handle absolute URL in basePath of AuthConfigManager.setConfig() diff --git a/packages/auth-js/src/client.ts b/packages/auth-js/src/client.ts index 313f7df9e..48e25428c 100644 --- a/packages/auth-js/src/client.ts +++ b/packages/auth-js/src/client.ts @@ -173,6 +173,18 @@ interface ParsedUrl { toString: () => string } +export function normalizeBasePath(config: T): T { + if (config.basePath && /^https?:\/\//.test(config.basePath)) { + const url = new URL(config.basePath) + return { + ...config, + baseUrl: url.origin, + basePath: url.pathname.replace(/\/$/, ''), + } + } + return config +} + export function parseUrl(url?: string): ParsedUrl { const defaultUrl = 'http://localhost:3000/api/auth' const parsedUrl = new URL(url ? (url.startsWith('http') ? url : `https://${url}`) : defaultUrl) diff --git a/packages/auth-js/src/react.tsx b/packages/auth-js/src/react.tsx index 0ee40f5eb..af7bc8808 100644 --- a/packages/auth-js/src/react.tsx +++ b/packages/auth-js/src/react.tsx @@ -2,7 +2,7 @@ import type { BuiltInProviderType, RedirectableProviderType } from '@auth/core/p import type { LoggerInstance, Session } from '@auth/core/types' import * as React from 'react' import { useCallback, useContext, useEffect, useMemo, useState } from 'react' -import { ClientSessionError, fetchData, now, parseUrl, useOnline } from './client' +import { ClientSessionError, fetchData, normalizeBasePath, now, parseUrl, useOnline } from './client' import type { WindowProps, AuthState, @@ -53,7 +53,8 @@ class AuthConfigManager { } setConfig(userConfig: Partial): void { - this.config = { ...this.config, ...userConfig } + const normalized = normalizeBasePath(userConfig) + this.config = { ...this.config, ...normalized } } getConfig(): AuthClientConfig { From 9aaf63c4f2e4aa0ffca4145ea75768ceb353b044 Mon Sep 17 00:00:00 2001 From: te2wow Date: Thu, 12 Feb 2026 14:58:31 +0900 Subject: [PATCH 2/3] test(auth-js): add tests for normalizeBasePath --- packages/auth-js/src/client.test.ts | 46 +++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 packages/auth-js/src/client.test.ts diff --git a/packages/auth-js/src/client.test.ts b/packages/auth-js/src/client.test.ts new file mode 100644 index 000000000..ede538fe7 --- /dev/null +++ b/packages/auth-js/src/client.test.ts @@ -0,0 +1,46 @@ +import { describe, it, expect } from 'vitest' +import { normalizeBasePath } from './client' + +describe('normalizeBasePath', () => { + it('should split absolute http URL into baseUrl and basePath', () => { + const result = normalizeBasePath({ baseUrl: '', basePath: 'http://localhost:8000/api/auth' }) + expect(result.baseUrl).toBe('http://localhost:8000') + expect(result.basePath).toBe('/api/auth') + expect(`${result.baseUrl}${result.basePath}/session`).toBe('http://localhost:8000/api/auth/session') + }) + + it('should split absolute https URL into baseUrl and basePath', () => { + const result = normalizeBasePath({ baseUrl: '', basePath: 'https://example.com/auth' }) + expect(result.baseUrl).toBe('https://example.com') + expect(result.basePath).toBe('/auth') + expect(`${result.baseUrl}${result.basePath}/session`).toBe('https://example.com/auth/session') + }) + + it('should not modify relative basePath', () => { + const result = normalizeBasePath({ baseUrl: 'http://localhost:3000', basePath: '/custom/auth' }) + expect(result.baseUrl).toBe('http://localhost:3000') + expect(result.basePath).toBe('/custom/auth') + expect(`${result.baseUrl}${result.basePath}/session`).toBe('http://localhost:3000/custom/auth/session') + }) + + it('should handle absolute URL without path', () => { + const result = normalizeBasePath({ baseUrl: '', basePath: 'http://localhost:8000' }) + expect(result.baseUrl).toBe('http://localhost:8000') + expect(result.basePath).toBe('') + expect(`${result.baseUrl}${result.basePath}/session`).toBe('http://localhost:8000/session') + }) + + it('should override baseUrl when both are absolute URLs', () => { + const result = normalizeBasePath({ baseUrl: 'http://localhost:3000', basePath: 'http://localhost:8000/api/auth' }) + expect(result.baseUrl).toBe('http://localhost:8000') + expect(result.basePath).toBe('/api/auth') + expect(`${result.baseUrl}${result.basePath}/session`).toBe('http://localhost:8000/api/auth/session') + }) + + it('should handle absolute URL with trailing slash', () => { + const result = normalizeBasePath({ baseUrl: '', basePath: 'http://localhost:8000/api/auth/' }) + expect(result.baseUrl).toBe('http://localhost:8000') + expect(result.basePath).toBe('/api/auth') + expect(`${result.baseUrl}${result.basePath}/session`).toBe('http://localhost:8000/api/auth/session') + }) +}) From 7c311a65bd6e161f69c73f9f0bb0bcf651fc0905 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Thu, 12 Feb 2026 13:01:44 +0000 Subject: [PATCH 3/3] ci: apply automated fixes --- packages/auth-js/src/client.test.ts | 21 ++++++++++++++++----- packages/auth-js/src/react.tsx | 9 ++++++++- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/packages/auth-js/src/client.test.ts b/packages/auth-js/src/client.test.ts index ede538fe7..a59e53803 100644 --- a/packages/auth-js/src/client.test.ts +++ b/packages/auth-js/src/client.test.ts @@ -6,7 +6,9 @@ describe('normalizeBasePath', () => { const result = normalizeBasePath({ baseUrl: '', basePath: 'http://localhost:8000/api/auth' }) expect(result.baseUrl).toBe('http://localhost:8000') expect(result.basePath).toBe('/api/auth') - expect(`${result.baseUrl}${result.basePath}/session`).toBe('http://localhost:8000/api/auth/session') + expect(`${result.baseUrl}${result.basePath}/session`).toBe( + 'http://localhost:8000/api/auth/session' + ) }) it('should split absolute https URL into baseUrl and basePath', () => { @@ -20,7 +22,9 @@ describe('normalizeBasePath', () => { const result = normalizeBasePath({ baseUrl: 'http://localhost:3000', basePath: '/custom/auth' }) expect(result.baseUrl).toBe('http://localhost:3000') expect(result.basePath).toBe('/custom/auth') - expect(`${result.baseUrl}${result.basePath}/session`).toBe('http://localhost:3000/custom/auth/session') + expect(`${result.baseUrl}${result.basePath}/session`).toBe( + 'http://localhost:3000/custom/auth/session' + ) }) it('should handle absolute URL without path', () => { @@ -31,16 +35,23 @@ describe('normalizeBasePath', () => { }) it('should override baseUrl when both are absolute URLs', () => { - const result = normalizeBasePath({ baseUrl: 'http://localhost:3000', basePath: 'http://localhost:8000/api/auth' }) + const result = normalizeBasePath({ + baseUrl: 'http://localhost:3000', + basePath: 'http://localhost:8000/api/auth', + }) expect(result.baseUrl).toBe('http://localhost:8000') expect(result.basePath).toBe('/api/auth') - expect(`${result.baseUrl}${result.basePath}/session`).toBe('http://localhost:8000/api/auth/session') + expect(`${result.baseUrl}${result.basePath}/session`).toBe( + 'http://localhost:8000/api/auth/session' + ) }) it('should handle absolute URL with trailing slash', () => { const result = normalizeBasePath({ baseUrl: '', basePath: 'http://localhost:8000/api/auth/' }) expect(result.baseUrl).toBe('http://localhost:8000') expect(result.basePath).toBe('/api/auth') - expect(`${result.baseUrl}${result.basePath}/session`).toBe('http://localhost:8000/api/auth/session') + expect(`${result.baseUrl}${result.basePath}/session`).toBe( + 'http://localhost:8000/api/auth/session' + ) }) }) diff --git a/packages/auth-js/src/react.tsx b/packages/auth-js/src/react.tsx index af7bc8808..dacfa69da 100644 --- a/packages/auth-js/src/react.tsx +++ b/packages/auth-js/src/react.tsx @@ -2,7 +2,14 @@ import type { BuiltInProviderType, RedirectableProviderType } from '@auth/core/p import type { LoggerInstance, Session } from '@auth/core/types' import * as React from 'react' import { useCallback, useContext, useEffect, useMemo, useState } from 'react' -import { ClientSessionError, fetchData, normalizeBasePath, now, parseUrl, useOnline } from './client' +import { + ClientSessionError, + fetchData, + normalizeBasePath, + now, + parseUrl, + useOnline, +} from './client' import type { WindowProps, AuthState,