Skip to content

Commit 038d1dd

Browse files
committed
move to a separate func
1 parent aaf21e4 commit 038d1dd

1 file changed

Lines changed: 49 additions & 32 deletions

File tree

packages/sync-engine/src/supabase/edge-functions/stripe-setup.ts

Lines changed: 49 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,45 @@ async function deleteSecret(
6060
}
6161
}
6262

63+
// Thrown when setup-secret validation fails; carries the HTTP status for the response.
64+
class SetupAuthError extends Error {
65+
constructor(
66+
readonly status: number,
67+
message: string
68+
) {
69+
super(message)
70+
}
71+
}
72+
73+
// Validates the caller's bearer secret against the per-install setup secret in vault.
74+
// Returns only on a strict match with a non-empty stored secret; throws on every other case.
75+
async function validateSetupSecret(callerSecret: string): Promise<void> {
76+
const dbUrl = Deno.env.get('SUPABASE_DB_URL')
77+
if (!dbUrl) {
78+
throw new SetupAuthError(500, 'SUPABASE_DB_URL not set')
79+
}
80+
81+
let authSql: ReturnType<typeof postgres> | undefined
82+
try {
83+
authSql = postgres(dbUrl, { max: 1, prepare: false })
84+
const secretResult = await authSql`
85+
SELECT decrypted_secret
86+
FROM vault.decrypted_secrets
87+
WHERE name = ${SETUP_SECRET_NAME}
88+
`
89+
const storedSecret: unknown = secretResult[0]?.decrypted_secret
90+
if (typeof storedSecret !== 'string' || storedSecret.length === 0) {
91+
throw new SetupAuthError(500, 'Setup secret not configured in vault')
92+
}
93+
if (callerSecret === storedSecret) {
94+
return
95+
}
96+
throw new SetupAuthError(403, 'Forbidden: Invalid setup secret')
97+
} finally {
98+
if (authSql) await authSql.end()
99+
}
100+
}
101+
63102
Deno.serve(async (req) => {
64103
// Extract project ref from SUPABASE_URL (format: https://{projectRef}.{base})
65104
const supabaseUrl = Deno.env.get('SUPABASE_URL')
@@ -89,39 +128,17 @@ Deno.serve(async (req) => {
89128
// never has to trust the bearer token for privileged Management operations.
90129
const accessToken = req.headers.get('x-management-api-token') ?? ''
91130

92-
{
93-
const dbUrl = Deno.env.get('SUPABASE_DB_URL')
94-
if (!dbUrl) {
95-
return new Response(JSON.stringify({ error: 'SUPABASE_DB_URL not set' }), {
96-
status: 500,
97-
headers: { 'Content-Type': 'application/json' },
98-
})
99-
}
100-
101-
let authSql: ReturnType<typeof postgres> | undefined
102-
try {
103-
authSql = postgres(dbUrl, { max: 1, prepare: false })
104-
const secretResult = await authSql`
105-
SELECT decrypted_secret
106-
FROM vault.decrypted_secrets
107-
WHERE name = ${SETUP_SECRET_NAME}
108-
`
109-
if (secretResult.length === 0) {
110-
return new Response('Setup secret not configured in vault', { status: 500 })
111-
}
112-
if (callerSecret !== secretResult[0].decrypted_secret) {
113-
return new Response('Forbidden: Invalid setup secret', { status: 403 })
114-
}
115-
} catch (error: unknown) {
116-
const err = error as Error
117-
console.error('Setup secret validation error:', error)
118-
return new Response(JSON.stringify({ error: err.message }), {
119-
status: 500,
120-
headers: { 'Content-Type': 'application/json' },
121-
})
122-
} finally {
123-
if (authSql) await authSql.end()
131+
try {
132+
await validateSetupSecret(callerSecret)
133+
} catch (error: unknown) {
134+
if (error instanceof SetupAuthError) {
135+
return new Response(error.message, { status: error.status })
124136
}
137+
console.error('Setup secret validation error:', error)
138+
return new Response(JSON.stringify({ error: (error as Error).message }), {
139+
status: 500,
140+
headers: { 'Content-Type': 'application/json' },
141+
})
125142
}
126143

127144
const supabaseAuthError = await authenticateWithSupabase(MGMT_API_BASE, projectRef, accessToken)

0 commit comments

Comments
 (0)