fix: redirect bank OAuth callback to /settings/banking (#165)
* fix: redirect bank OAuth callback to /settings/banking The callback was redirecting to /settings which immediately redirects to /settings/company, dropping the bank_connected and connection_id query params. The auto-sync code that triggers the initial 90-day transaction fetch lives in /settings/banking and never received the params. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test: add coverage for invalid_code_format redirect path Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
8855ef1485
commit
8855aa99f7
@@ -57,6 +57,7 @@ describe('GET /api/extensions/enable-banking/callback', () => {
|
||||
|
||||
expect(response.status).toBe(307)
|
||||
const location = response.headers.get('location') || ''
|
||||
expect(location).toContain('/settings/banking?')
|
||||
expect(location).toContain('bank_error=invalid_state')
|
||||
})
|
||||
|
||||
@@ -87,6 +88,7 @@ describe('GET /api/extensions/enable-banking/callback', () => {
|
||||
|
||||
expect(response.status).toBe(307)
|
||||
const location = response.headers.get('location') || ''
|
||||
expect(location).toContain('/settings/banking?')
|
||||
expect(location).toContain('bank_connected=true')
|
||||
expect(location).toContain('connection_id=conn-1')
|
||||
})
|
||||
@@ -96,6 +98,7 @@ describe('GET /api/extensions/enable-banking/callback', () => {
|
||||
|
||||
expect(response.status).toBe(307)
|
||||
const location = response.headers.get('location') || ''
|
||||
expect(location).toContain('/settings/banking?')
|
||||
expect(location).toContain('bank_error=User%20cancelled')
|
||||
// No state → no DB cleanup attempted
|
||||
expect(mockFrom).not.toHaveBeenCalled()
|
||||
@@ -114,6 +117,7 @@ describe('GET /api/extensions/enable-banking/callback', () => {
|
||||
|
||||
expect(response.status).toBe(307)
|
||||
const location = response.headers.get('location') || ''
|
||||
expect(location).toContain('/settings/banking?')
|
||||
expect(location).toContain('bank_error=Denied%20data%20sharing%20consent')
|
||||
// Should clean up the pending row
|
||||
expect(mockFrom).toHaveBeenCalledWith('bank_connections')
|
||||
@@ -124,6 +128,16 @@ describe('GET /api/extensions/enable-banking/callback', () => {
|
||||
|
||||
expect(response.status).toBe(307)
|
||||
const location = response.headers.get('location') || ''
|
||||
expect(location).toContain('/settings/banking?')
|
||||
expect(location).toContain('bank_error=missing_parameters')
|
||||
})
|
||||
|
||||
it('redirects with error when code fails format validation', async () => {
|
||||
const response = await GET(makeRequest({ code: '!!bad!!', state: 'some-state' }))
|
||||
|
||||
expect(response.status).toBe(307)
|
||||
const location = response.headers.get('location') || ''
|
||||
expect(location).toContain('/settings/banking?')
|
||||
expect(location).toContain('bank_error=invalid_code_format')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -61,18 +61,18 @@ export async function GET(request: Request) {
|
||||
}
|
||||
|
||||
return NextResponse.redirect(
|
||||
`${baseUrl}/settings?bank_error=${encodeURIComponent(errorMessage)}`
|
||||
`${baseUrl}/settings/banking?bank_error=${encodeURIComponent(errorMessage)}`
|
||||
)
|
||||
}
|
||||
|
||||
if (!code || !state) {
|
||||
return NextResponse.redirect(`${baseUrl}/settings?bank_error=missing_parameters`)
|
||||
return NextResponse.redirect(`${baseUrl}/settings/banking?bank_error=missing_parameters`)
|
||||
}
|
||||
|
||||
// Validate authorization code format
|
||||
const codePattern = /^[a-zA-Z0-9._~+\/-]{8,2048}$/
|
||||
if (!codePattern.test(code)) {
|
||||
return NextResponse.redirect(`${baseUrl}/settings?bank_error=invalid_code_format`)
|
||||
return NextResponse.redirect(`${baseUrl}/settings/banking?bank_error=invalid_code_format`)
|
||||
}
|
||||
|
||||
const supabase = await createServiceClient()
|
||||
@@ -93,7 +93,7 @@ export async function GET(request: Request) {
|
||||
hasCode: !!code,
|
||||
})
|
||||
return NextResponse.redirect(
|
||||
`${baseUrl}/settings?bank_error=${encodeURIComponent('invalid_state')}`
|
||||
`${baseUrl}/settings/banking?bank_error=${encodeURIComponent('invalid_state')}`
|
||||
)
|
||||
}
|
||||
|
||||
@@ -171,7 +171,7 @@ export async function GET(request: Request) {
|
||||
.single()
|
||||
|
||||
const redirectTarget = userSettings?.onboarding_complete
|
||||
? `/settings?bank_connected=true&connection_id=${connectionId}`
|
||||
? `/settings/banking?bank_connected=true&connection_id=${connectionId}`
|
||||
: `/onboarding?bank_connected=true&connection_id=${connectionId}`
|
||||
|
||||
return NextResponse.redirect(`${baseUrl}${redirectTarget}`)
|
||||
@@ -197,7 +197,7 @@ export async function GET(request: Request) {
|
||||
}
|
||||
|
||||
return NextResponse.redirect(
|
||||
`${baseUrl}/settings?bank_error=${encodeURIComponent('Connection failed')}`
|
||||
`${baseUrl}/settings/banking?bank_error=${encodeURIComponent('Connection failed')}`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user