mirror of
https://github.com/iflytek/skillhub.git
synced 2026-08-27 11:14:59 +00:00
fix(auth): prevent login page redirect loop
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
This commit is contained in:
parent
3522bad295
commit
243e9b68f4
6 changed files with 30 additions and 19 deletions
|
|
@ -123,7 +123,6 @@ export function Layout() {
|
|||
) : (
|
||||
<Link
|
||||
to="/login"
|
||||
search={{ returnTo: '' }}
|
||||
className="hover:opacity-80 transition-opacity"
|
||||
>
|
||||
{t('nav.login')}
|
||||
|
|
|
|||
|
|
@ -183,8 +183,8 @@ const skillsRoute = createRoute({
|
|||
const loginRoute = createRoute({
|
||||
getParentRoute: () => rootRoute,
|
||||
path: 'login',
|
||||
validateSearch: (search: Record<string, unknown>): { returnTo: string; reason?: string } => ({
|
||||
returnTo: typeof search.returnTo === 'string' ? search.returnTo : '',
|
||||
validateSearch: (search: Record<string, unknown>): { returnTo?: string; reason?: string } => ({
|
||||
returnTo: typeof search.returnTo === 'string' && search.returnTo ? search.returnTo : undefined,
|
||||
reason: typeof search.reason === 'string' ? search.reason : undefined,
|
||||
}),
|
||||
component: LoginPage,
|
||||
|
|
|
|||
|
|
@ -1,15 +1,19 @@
|
|||
import { describe, expect, it } from 'vitest'
|
||||
import * as authMethods from './use-auth-methods'
|
||||
import { getAuthMethodsQueryOptions, useAuthMethods } from './use-auth-methods'
|
||||
|
||||
/**
|
||||
* use-auth-methods is a thin useQuery wrapper around authApi.getMethods.
|
||||
* The query key includes the returnTo parameter for proper cache isolation.
|
||||
* There are no exported pure functions or data transformations to unit-test.
|
||||
*
|
||||
* This file verifies the public API surface so that accidental export removals are caught.
|
||||
*/
|
||||
describe('use-auth-methods module exports', () => {
|
||||
it('exports useAuthMethods hook', () => {
|
||||
expect(authMethods.useAuthMethods).toBeTypeOf('function')
|
||||
describe('getAuthMethodsQueryOptions', () => {
|
||||
it('keeps login auth-method lookup local to the page and bypasses the global 401 redirect', () => {
|
||||
const options = getAuthMethodsQueryOptions('/dashboard')
|
||||
|
||||
expect(options.queryKey).toEqual(['auth', 'methods', '/dashboard'])
|
||||
expect(options.retry).toBe(false)
|
||||
expect(options.meta).toEqual({ skipGlobalErrorHandler: true })
|
||||
expect(options.queryFn).toBeTypeOf('function')
|
||||
})
|
||||
})
|
||||
|
||||
describe('use-auth-methods module exports', () => {
|
||||
it('exports useAuthMethods hook', () => {
|
||||
expect(useAuthMethods).toBeTypeOf('function')
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -5,9 +5,17 @@ import type { AuthMethod } from '@/api/types'
|
|||
/**
|
||||
* Loads the backend-advertised authentication methods for the current entry point.
|
||||
*/
|
||||
export function useAuthMethods(returnTo?: string) {
|
||||
return useQuery<AuthMethod[]>({
|
||||
export function getAuthMethodsQueryOptions(returnTo?: string) {
|
||||
return {
|
||||
queryKey: ['auth', 'methods', returnTo ?? ''],
|
||||
queryFn: () => authApi.getMethods(returnTo),
|
||||
})
|
||||
retry: false,
|
||||
meta: {
|
||||
skipGlobalErrorHandler: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export function useAuthMethods(returnTo?: string) {
|
||||
return useQuery<AuthMethod[]>(getAuthMethodsQueryOptions(returnTo))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ export function ResetPasswordPage() {
|
|||
<p className="rounded-md border border-emerald-200 bg-emerald-50 px-3 py-2 text-sm text-emerald-700">
|
||||
{t('resetPassword.successMessage')}
|
||||
</p>
|
||||
<Link to="/login" search={{ returnTo: '' }} className="block text-center font-medium text-primary hover:underline">
|
||||
<Link to="/login" className="block text-center font-medium text-primary hover:underline">
|
||||
{t('resetPassword.backToLogin')}
|
||||
</Link>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ export function SecuritySettingsPage() {
|
|||
clearSessionScopedQueries(queryClient)
|
||||
queryClient.setQueryData(['auth', 'me'], null)
|
||||
}
|
||||
await navigate({ to: '/login', search: { returnTo: '' } })
|
||||
await navigate({ to: '/login' })
|
||||
} catch (error) {
|
||||
if (error instanceof ApiError && error.status === 401) {
|
||||
setErrorMessage(t('security.invalidCurrentPassword'))
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue