fix(review): harden payment, oauth, and migration paths

This commit is contained in:
IanShaw027
2026-04-22 10:26:22 +08:00
parent 7fbd5177c2
commit c229f33e9e
33 changed files with 704 additions and 79 deletions

View File

@@ -173,20 +173,12 @@ describe('oauth adoption auth api', () => {
expect(hasPendingOAuthSuggestedProfile({})).toBe(false)
})
it('prepares an oauth bind access token cookie before redirect binding', async () => {
it('requests an HttpOnly oauth bind cookie before redirect binding', async () => {
localStorage.setItem('auth_token', 'access-token-value')
const setCookie = vi.fn()
Object.defineProperty(document, 'cookie', {
configurable: true,
get: () => '',
set: setCookie
})
const { prepareOAuthBindAccessTokenCookie } = await import('@/api/auth')
prepareOAuthBindAccessTokenCookie()
await prepareOAuthBindAccessTokenCookie()
expect(setCookie).toHaveBeenCalledTimes(1)
expect(setCookie.mock.calls[0]?.[0]).toContain('oauth_bind_access_token=access-token-value')
expect(post).toHaveBeenCalledWith('/auth/oauth/bind-token')
})
})

View File

@@ -278,33 +278,11 @@ export function persistOAuthTokenContext(tokens: Partial<OAuthTokenResponse>): v
}
}
export function prepareOAuthBindAccessTokenCookie(): void {
if (typeof document === 'undefined' || typeof window === 'undefined') {
export async function prepareOAuthBindAccessTokenCookie(): Promise<void> {
if (!getAuthToken()) {
return
}
const token = getAuthToken()
if (!token) {
return
}
const secure = window.location.protocol === 'https:' ? '; Secure' : ''
const path = resolveOAuthBindCookiePath()
document.cookie =
`oauth_bind_access_token=${encodeURIComponent(token)}; Path=${path}/auth/oauth; Max-Age=600; SameSite=Lax${secure}`
}
function resolveOAuthBindCookiePath(): string {
const apiBase = ((import.meta.env.VITE_API_BASE_URL as string | undefined) || '/api/v1').replace(/\/$/, '')
try {
return new URL(apiBase, window.location.origin).pathname.replace(/\/$/, '') || '/api/v1'
} catch {
if (apiBase.startsWith('/')) {
return apiBase
}
return '/api/v1'
}
await apiClient.post('/auth/oauth/bind-token')
}
/**

View File

@@ -153,10 +153,10 @@ export function buildOAuthBindingStartURL(
return `${normalized}/auth/oauth/${provider}/start?${params.toString()}`
}
export function startOAuthBinding(
export async function startOAuthBinding(
provider: BindableOAuthProvider,
options: BuildOAuthBindingStartURLOptions = {}
): void {
): Promise<void> {
if (typeof window === 'undefined') {
return
}
@@ -164,7 +164,7 @@ export function startOAuthBinding(
if (!startURL) {
return
}
prepareOAuthBindAccessTokenCookie()
await prepareOAuthBindAccessTokenCookie()
window.location.href = startURL
}