diff --git a/web/e2e/auth-entry.spec.ts b/web/e2e/auth-entry.spec.ts
index a7a6a3aa..2a639b53 100644
--- a/web/e2e/auth-entry.spec.ts
+++ b/web/e2e/auth-entry.spec.ts
@@ -8,9 +8,17 @@ test.describe('Auth Entry', () => {
})
test('validates required fields and preserves returnTo on register link', async ({ page }) => {
+ await page.route('**/runtime-config.js', async (route) => {
+ await route.fulfill({
+ status: 200,
+ contentType: 'application/javascript',
+ body: 'window.__SKILLHUB_RUNTIME_CONFIG__ = { authDirectEnabled: "true", authDirectProvider: "local" }',
+ })
+ })
await page.goto('/login?returnTo=%2Fdashboard%2Ftokens')
await expect(page.getByRole('heading', { name: 'Login to SkillHub' })).toBeVisible()
+ await expect(page.getByText(/password compatibility layer/i)).toHaveCount(0)
await page.getByRole('button', { name: 'Login' }).click()
await expect(page.getByText('Username is required')).toBeVisible()
diff --git a/web/src/i18n/locales/en.json b/web/src/i18n/locales/en.json
index 7b01d0bd..bcfcc801 100644
--- a/web/src/i18n/locales/en.json
+++ b/web/src/i18n/locales/en.json
@@ -360,7 +360,6 @@
"noAccount": "Don't have an account?",
"register": "Sign up now",
"oauthHint": "After OAuth authentication, you will be automatically redirected back to this site.",
- "passwordCompatHint": "This deployment has the password compatibility layer enabled. The form will route to {{name}} instead of the fixed local account endpoint.",
"enterpriseSsoTitle": "Enterprise SSO",
"enterpriseSsoHint": "This deployment has the compatibility layer enabled. If your browser already has a {{name}} session, you can try establishing a SkillHub session directly.",
"enterpriseSsoAutoHint": "This deployment has automatic {{name}} probing enabled. If it does not succeed, you can continue with the standard login methods.",
diff --git a/web/src/i18n/locales/ru.json b/web/src/i18n/locales/ru.json
index 2fa4f8d5..4fbb14b8 100644
--- a/web/src/i18n/locales/ru.json
+++ b/web/src/i18n/locales/ru.json
@@ -360,7 +360,6 @@
"noAccount": "Нет аккаунта?",
"register": "Зарегистрироваться",
"oauthHint": "После аутентификации OAuth вы будете автоматически перенаправлены обратно на этот сайт.",
- "passwordCompatHint": "В этом развёртывании включён слой совместимости паролей. Форма направит запрос в {{name}} вместо фиксированной локальной учётной записи.",
"enterpriseSsoTitle": "Корпоративный SSO",
"enterpriseSsoHint": "В этом развёртывании включён слой совместимости. Если в браузере уже есть сессия {{name}}, можно попробовать сразу установить сессию SkillHub.",
"enterpriseSsoAutoHint": "В этом развёртывании включено автоматическое зондирование {{name}}. Если оно не сработает, продолжайте стандартными способами входа.",
diff --git a/web/src/i18n/locales/zh.json b/web/src/i18n/locales/zh.json
index c4fc51e0..36d43a7c 100644
--- a/web/src/i18n/locales/zh.json
+++ b/web/src/i18n/locales/zh.json
@@ -360,7 +360,6 @@
"noAccount": "还没有账号?",
"register": "立即注册",
"oauthHint": "使用 OAuth 登录时,认证完成后会自动返回当前站点。",
- "passwordCompatHint": "当前部署已启用账号密码兼容接入层。表单将路由到 {{name}},而不是固定使用本地账号接口。",
"enterpriseSsoTitle": "企业单点登录",
"enterpriseSsoHint": "当前部署已启用兼容接入层。若浏览器中已存在 {{name}} 会话,可直接尝试建立 SkillHub 登录态。",
"enterpriseSsoAutoHint": "当前部署已启用自动 {{name}} 探测。若未成功,你仍可继续使用现有登录方式。",
diff --git a/web/src/pages/login.test.tsx b/web/src/pages/login.test.tsx
index 3b7fca7d..2c35c14a 100644
--- a/web/src/pages/login.test.tsx
+++ b/web/src/pages/login.test.tsx
@@ -6,6 +6,7 @@ import { describe, expect, it, vi } from 'vitest'
const authMethodsFixture = vi.hoisted(() => ({
methods: [] as Array<{ id: string, methodType: string }>,
bootstrapEnabled: false,
+ directEnabled: false,
isError: false,
returnTo: '',
navigate: vi.fn(),
@@ -38,7 +39,7 @@ vi.mock('lucide-react', () => ({
}))
vi.mock('@/api/client', () => ({
- getDirectAuthRuntimeConfig: () => ({ enabled: false }),
+ getDirectAuthRuntimeConfig: () => ({ enabled: authMethodsFixture.directEnabled, provider: 'local' }),
getSessionBootstrapRuntimeConfig: () => ({ enabled: authMethodsFixture.bootstrapEnabled, provider: 'proxy' }),
}))
@@ -83,6 +84,7 @@ describe('LoginPage', () => {
cleanup()
authMethodsFixture.methods = []
authMethodsFixture.bootstrapEnabled = false
+ authMethodsFixture.directEnabled = false
authMethodsFixture.isError = false
authMethodsFixture.returnTo = ''
authMethodsFixture.navigate.mockClear()
@@ -103,6 +105,14 @@ describe('LoginPage', () => {
expect(html).toContain('login.register')
})
+ it('does not expose password routing details when direct login is configured', () => {
+ authMethodsFixture.directEnabled = true
+ const html = renderToStaticMarkup(