feat: enhance error handling and logging for bank connection process (#44)
This commit is contained in:
@@ -91,12 +91,32 @@ describe('GET /api/extensions/enable-banking/callback', () => {
|
||||
expect(location).toContain('connection_id=conn-1')
|
||||
})
|
||||
|
||||
it('redirects with error when bank returns error param', async () => {
|
||||
it('redirects with error when bank returns error param (no state)', async () => {
|
||||
const response = await GET(makeRequest({ error: 'access_denied', error_description: 'User cancelled' }))
|
||||
|
||||
expect(response.status).toBe(307)
|
||||
const location = response.headers.get('location') || ''
|
||||
expect(location).toContain('bank_error=User%20cancelled')
|
||||
// No state → no DB cleanup attempted
|
||||
expect(mockFrom).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('cleans up pending connection when bank returns error with state', async () => {
|
||||
mockFrom.mockImplementation(() =>
|
||||
mockChain({ data: null, error: null })
|
||||
)
|
||||
|
||||
const response = await GET(makeRequest({
|
||||
error: 'access_denied',
|
||||
error_description: 'Denied data sharing consent',
|
||||
state: 'pending-state',
|
||||
}))
|
||||
|
||||
expect(response.status).toBe(307)
|
||||
const location = response.headers.get('location') || ''
|
||||
expect(location).toContain('bank_error=Denied%20data%20sharing%20consent')
|
||||
// Should clean up the pending row
|
||||
expect(mockFrom).toHaveBeenCalledWith('bank_connections')
|
||||
})
|
||||
|
||||
it('redirects with error when code or state is missing', async () => {
|
||||
|
||||
@@ -22,7 +22,44 @@ export async function GET(request: Request) {
|
||||
|
||||
if (error) {
|
||||
const errorMessage = errorDescription || error
|
||||
console.error('Bank authorization error:', errorMessage)
|
||||
console.error('[enable-banking] Bank authorization denied', {
|
||||
error,
|
||||
error_description: errorDescription,
|
||||
has_state: !!state,
|
||||
})
|
||||
|
||||
// Clean up the pending bank_connections row so it doesn't accumulate
|
||||
if (state) {
|
||||
try {
|
||||
const supabase = await createServiceClient()
|
||||
|
||||
// Fetch connection details for logging before updating
|
||||
const { data: pendingConn } = await supabase
|
||||
.from('bank_connections')
|
||||
.select('id, user_id, bank_name')
|
||||
.eq('oauth_state', state)
|
||||
.eq('status', 'pending')
|
||||
.single()
|
||||
|
||||
if (pendingConn) {
|
||||
console.error('[enable-banking] Authorization denied details', {
|
||||
connection_id: pendingConn.id,
|
||||
user_id: pendingConn.user_id,
|
||||
bank_name: pendingConn.bank_name,
|
||||
error_code: error,
|
||||
error_description: errorDescription,
|
||||
})
|
||||
|
||||
await supabase
|
||||
.from('bank_connections')
|
||||
.update({ status: 'error', error_message: errorMessage, oauth_state: null })
|
||||
.eq('id', pendingConn.id)
|
||||
}
|
||||
} catch (cleanupError) {
|
||||
console.error('[enable-banking] Failed to clean up pending bank connection:', cleanupError)
|
||||
}
|
||||
}
|
||||
|
||||
return NextResponse.redirect(
|
||||
`${baseUrl}/settings?bank_error=${encodeURIComponent(errorMessage)}`
|
||||
)
|
||||
|
||||
@@ -92,6 +92,35 @@ export const enableBankingExtension: Extension = {
|
||||
|
||||
const psuType = companySettings?.entity_type === 'aktiebolag' ? 'business' : 'personal'
|
||||
|
||||
log.info('[enable-banking] Starting bank connection', {
|
||||
user_id: user.id,
|
||||
bank: aspsp_name,
|
||||
country: aspsp_country,
|
||||
entity_type: companySettings?.entity_type,
|
||||
psu_type: psuType,
|
||||
})
|
||||
|
||||
// Clean up any stale pending connections for this user+bank
|
||||
// to avoid conflicts with the new authorization
|
||||
const { data: staleConnections } = await supabase
|
||||
.from('bank_connections')
|
||||
.select('id')
|
||||
.eq('user_id', user.id)
|
||||
.eq('bank_name', aspsp_name)
|
||||
.eq('status', 'pending')
|
||||
|
||||
if (staleConnections && staleConnections.length > 0) {
|
||||
log.info('[enable-banking] Cleaning up stale pending connections', {
|
||||
count: staleConnections.length,
|
||||
})
|
||||
await supabase
|
||||
.from('bank_connections')
|
||||
.update({ status: 'error', error_message: 'Superseded by new connection attempt', oauth_state: null })
|
||||
.eq('user_id', user.id)
|
||||
.eq('bank_name', aspsp_name)
|
||||
.eq('status', 'pending')
|
||||
}
|
||||
|
||||
const redirectUrl = `${process.env.NEXT_PUBLIC_APP_URL}/api/extensions/enable-banking/callback`
|
||||
|
||||
// Generate cryptographic state token for CSRF protection
|
||||
|
||||
Reference in New Issue
Block a user