This commit is contained in:
2026-08-20 14:43:10 +08:00
parent 4df6a5f571
commit 4ae5e7c672
2 changed files with 38 additions and 4 deletions
+24
View File
@@ -100,6 +100,30 @@ describe('admin auth session', () => {
expect(auth.refreshToken).toBe('refresh-domestic') expect(auth.refreshToken).toBe('refresh-domestic')
}) })
it('accepts serialized long lifetimes from the real authentication response', async () => {
mocks.post.mockResolvedValue({
code: '200',
message: 'success',
data: {
tokenType: 'Bearer',
accessToken: token(),
expiresIn: '7200',
refreshToken: 'refresh-serialized',
refreshExpiresIn: '604800',
overseasAccessToken: null,
overseasExpiresIn: '0',
},
})
mocks.listCurrentPlatformPermissionCodes.mockResolvedValue(['platform:warehouse:list'])
const auth = useAuthStore()
await auth.login('platform_admin', 'password')
expect(auth.isAuthenticated).toBe(true)
expect(auth.refreshToken).toBe('refresh-serialized')
expect([...auth.permissionCodes]).toEqual(['platform:warehouse:list'])
})
it('loads permissions from the current authorization endpoint when the JWT only contains identity', async () => { it('loads permissions from the current authorization endpoint when the JWT only contains identity', async () => {
mocks.post.mockResolvedValue(loginResponse()) mocks.post.mockResolvedValue(loginResponse())
mocks.listCurrentPlatformPermissionCodes.mockResolvedValue(['platform:warehouse:list']) mocks.listCurrentPlatformPermissionCodes.mockResolvedValue(['platform:warehouse:list'])
+14 -4
View File
@@ -21,16 +21,26 @@ const REFRESH_LOCK_NAME = 'cc-admin-auth-refresh'
interface LoginResponse { interface LoginResponse {
tokenType: string tokenType: string
accessToken: string accessToken: string
expiresIn: number expiresIn: number | string
refreshToken: string refreshToken: string
refreshExpiresIn: number refreshExpiresIn: number | string
overseasAccessToken?: string | null overseasAccessToken?: string | null
overseasExpiresIn?: number overseasExpiresIn?: number | string
} }
class ExpiredAccessTokenError extends Error {} class ExpiredAccessTokenError extends Error {}
class SupersededSessionError extends Error {} class SupersededSessionError extends Error {}
function parseLifetimeSeconds(value: number | string, field: string) {
const normalized = typeof value === 'number'
? value
: /^\d+$/.test(value.trim()) ? Number(value) : Number.NaN
if (!Number.isSafeInteger(normalized) || normalized < 0) {
throw new Error(`登录响应中的 ${field} 格式无效`)
}
return normalized
}
function sessionFromToken(token: string) { function sessionFromToken(token: string) {
const claims = decodeJwtClaims(token) const claims = decodeJwtClaims(token)
if (claims.app_code !== 'PLATFORM') throw new Error('账号不属于当前应用端') if (claims.app_code !== 'PLATFORM') throw new Error('账号不属于当前应用端')
@@ -92,7 +102,7 @@ export const useAuthStore = defineStore('admin-auth', () => {
ensureCurrentGeneration(expectedGeneration) ensureCurrentGeneration(expectedGeneration)
replaceOverseasAccessToken( replaceOverseasAccessToken(
result.overseasAccessToken ?? null, result.overseasAccessToken ?? null,
result.overseasExpiresIn ?? 0, parseLifetimeSeconds(result.overseasExpiresIn ?? 0, 'overseasExpiresIn'),
) )
token.value = result.accessToken token.value = result.accessToken
refreshToken.value = result.refreshToken refreshToken.value = result.refreshToken