fix(auth): stabilize header session state and retry team token errors

This commit is contained in:
Ruslan Bakiev
2026-04-21 13:50:11 +07:00
parent 7b4eaeeb92
commit e1e6993f35
4 changed files with 78 additions and 28 deletions

View File

@@ -7,7 +7,8 @@
export const useAuth = () => { export const useAuth = () => {
const { getToken, initTokens, idToken } = useLogtoTokens() const { getToken, initTokens, idToken } = useLogtoTokens()
const me = useState<{ id?: string | null } | null>('me', () => null) const me = useState<{ id?: string | null } | null>('me', () => null)
const isAuthenticated = computed(() => !!me.value?.id) const logtoUser = useState<Record<string, unknown> | null>('logto-user', () => null)
const isAuthenticated = computed(() => !!(me.value?.id || logtoUser.value))
const loggedIn = isAuthenticated const loggedIn = isAuthenticated
/** /**

View File

@@ -27,6 +27,7 @@ const CLIENT_MAP: Record<string, string> = {
export const useGraphQL = () => { export const useGraphQL = () => {
const auth = useAuth() const auth = useAuth()
const { activeLogtoOrgId } = useActiveTeam() const { activeLogtoOrgId } = useActiveTeam()
const { refreshTokens } = useLogtoTokens()
const getClientId = (endpoint: Endpoint, api: Api): string => { const getClientId = (endpoint: Endpoint, api: Api): string => {
return CLIENT_MAP[`${endpoint}:${api}`] || 'default' return CLIENT_MAP[`${endpoint}:${api}`] || 'default'
@@ -77,20 +78,37 @@ export const useGraphQL = () => {
): Promise<TResult> => { ): Promise<TResult> => {
const clientId = getClientId(endpoint, api) const clientId = getClientId(endpoint, api)
const { client } = useApolloClient(clientId) const { client } = useApolloClient(clientId)
const context = await getAuthContext(endpoint, api) const executeOnce = async () => {
const context = await getAuthContext(endpoint, api)
const result = await client.query({
query: document,
variables,
context,
fetchPolicy: 'network-only'
})
const result = await client.query({ if (result.errors?.length) {
query: document, throw new Error(result.errors[0]?.message || 'GraphQL error')
variables, }
context,
fetchPolicy: 'network-only'
})
if (result.errors?.length) { return result.data as TResult
throw new Error(result.errors[0]?.message || 'GraphQL error')
} }
return result.data as TResult try {
return await executeOnce()
} catch (error) {
const message = error instanceof Error ? error.message : String(error)
const isAuthContextFailure = message.includes('Invalid Compact JWS')
|| message.includes('Context creation failed')
|| message.includes('Received status code 500')
if (endpoint === 'team' && isAuthContextFailure) {
await refreshTokens()
return await executeOnce()
}
throw error
}
} }
const mutate = async <TResult, TVariables extends Record<string, unknown>>( const mutate = async <TResult, TVariables extends Record<string, unknown>>(
@@ -101,19 +119,36 @@ export const useGraphQL = () => {
): Promise<TResult> => { ): Promise<TResult> => {
const clientId = getClientId(endpoint, api) const clientId = getClientId(endpoint, api)
const { client } = useApolloClient(clientId) const { client } = useApolloClient(clientId)
const context = await getAuthContext(endpoint, api) const mutateOnce = async () => {
const context = await getAuthContext(endpoint, api)
const result = await client.mutate({
mutation: document,
variables,
context
})
const result = await client.mutate({ if (result.errors?.length) {
mutation: document, throw new Error(result.errors[0]?.message || 'GraphQL error')
variables, }
context
})
if (result.errors?.length) { return result.data as TResult
throw new Error(result.errors[0]?.message || 'GraphQL error')
} }
return result.data as TResult try {
return await mutateOnce()
} catch (error) {
const message = error instanceof Error ? error.message : String(error)
const isAuthContextFailure = message.includes('Invalid Compact JWS')
|| message.includes('Context creation failed')
|| message.includes('Received status code 500')
if (endpoint === 'team' && isAuthContextFailure) {
await refreshTokens()
return await mutateOnce()
}
throw error
}
} }
return { execute, mutate } return { execute, mutate }

View File

@@ -2,14 +2,28 @@ export default defineNuxtPlugin(() => {
const originalConsoleError = console.error const originalConsoleError = console.error
console.error = (...args: unknown[]) => { console.error = (...args: unknown[]) => {
const hasApolloDevtoolsWarning = args.some((arg) => { const serializedArgs = args
if (typeof arg !== 'string') return false .map((arg) => {
if (typeof arg === 'string') return arg
if (arg instanceof Error) return `${arg.message}\n${arg.stack || ''}`
try {
return JSON.stringify(arg)
} catch {
return String(arg)
}
})
.join(' ')
return ( const hasApolloDevtoolsWarning = (
arg.includes('connectToDevTools') && (
arg.includes('devtools.enabled') serializedArgs.includes('connectToDevTools')
&& serializedArgs.includes('devtools.enabled')
) )
}) || (
serializedArgs.includes('go.apollo.dev/c/err')
&& serializedArgs.includes('"message":104')
)
)
if (hasApolloDevtoolsWarning) { if (hasApolloDevtoolsWarning) {
return return

View File

@@ -37,10 +37,10 @@ export default defineEventHandler(async (event) => {
let token: string | null = null let token: string | null = null
try { try {
token = await client.getAccessToken('https://teams.optovia.ru', organizationId) token = await client.getIdToken()
} catch { } catch {
try { try {
token = await client.getIdToken() token = await client.getAccessToken('https://teams.optovia.ru', organizationId)
} catch { } catch {
return return
} }