Skip to content

Commit 987e696

Browse files
committed
fix(auth): preserve OAuth flow when switching accounts on consent page
1 parent 59fc084 commit 987e696

File tree

2 files changed

+64
-2
lines changed
  • apps/sim/app

2 files changed

+64
-2
lines changed

apps/sim/app/(auth)/oauth/consent/page.tsx

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,14 +100,28 @@ export default function OAuthConsentPage() {
100100
)
101101

102102
const handleSwitchAccount = useCallback(async () => {
103+
if (!consentCode) return
104+
105+
const res = await fetch(`/api/auth/oauth2/authorize-params?consent_code=${consentCode}`)
106+
if (!res.ok) {
107+
setError('Unable to switch accounts. Please re-initiate the connection.')
108+
return
109+
}
110+
111+
const params = (await res.json()) as Record<string, string | null>
112+
const authorizeUrl = new URL('/api/auth/oauth2/authorize', window.location.origin)
113+
for (const [key, value] of Object.entries(params)) {
114+
if (value) authorizeUrl.searchParams.set(key, value)
115+
}
116+
103117
await signOut({
104118
fetchOptions: {
105119
onSuccess: () => {
106-
window.location.href = '/login'
120+
window.location.href = authorizeUrl.toString()
107121
},
108122
},
109123
})
110-
}, [])
124+
}, [consentCode])
111125

112126
if (loading) {
113127
return (
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { db } from '@sim/db'
2+
import { verification } from '@sim/db/schema'
3+
import { eq } from 'drizzle-orm'
4+
import type { NextRequest } from 'next/server'
5+
import { NextResponse } from 'next/server'
6+
7+
/**
8+
* Returns the original OAuth authorize parameters stored in the verification record
9+
* for a given consent code. Used by the consent page to reconstruct the authorize URL
10+
* when switching accounts.
11+
*/
12+
export async function GET(request: NextRequest) {
13+
const consentCode = request.nextUrl.searchParams.get('consent_code')
14+
if (!consentCode) {
15+
return NextResponse.json({ error: 'consent_code is required' }, { status: 400 })
16+
}
17+
18+
const [record] = await db
19+
.select({ value: verification.value })
20+
.from(verification)
21+
.where(eq(verification.identifier, consentCode))
22+
.limit(1)
23+
24+
if (!record) {
25+
return NextResponse.json({ error: 'Invalid or expired consent code' }, { status: 404 })
26+
}
27+
28+
const data = JSON.parse(record.value) as {
29+
clientId: string
30+
redirectURI: string
31+
scope: string[]
32+
codeChallenge: string
33+
codeChallengeMethod: string
34+
state: string | null
35+
nonce: string | null
36+
}
37+
38+
return NextResponse.json({
39+
client_id: data.clientId,
40+
redirect_uri: data.redirectURI,
41+
scope: data.scope.join(' '),
42+
code_challenge: data.codeChallenge,
43+
code_challenge_method: data.codeChallengeMethod,
44+
state: data.state,
45+
nonce: data.nonce,
46+
response_type: 'code',
47+
})
48+
}

0 commit comments

Comments
 (0)