fix(account-delete): judge step-up auth by amr sign-in time, not token iat
This commit is contained in:
parent
043ef579a8
commit
83c2deb561
3 changed files with 167 additions and 19 deletions
|
|
@ -1,13 +1,13 @@
|
|||
import { corsHeaders, handleCorsPreflightRequest } from '../_shared/cors.ts'
|
||||
import { requireUser, authErrorResponse, type AuthError } from '../_shared/auth.ts'
|
||||
import { createServiceRoleClient } from '../_shared/quota.ts'
|
||||
import { hasRecentAuthentication } from './recent-auth.ts'
|
||||
|
||||
interface DeleteAccountRequest {
|
||||
confirmation: string
|
||||
}
|
||||
|
||||
const CONFIRMATION_PHRASE = 'DELETE_MY_ACCOUNT'
|
||||
const RECENT_AUTH_SECONDS = 10 * 60
|
||||
const STORAGE_BUCKETS = ['audio', 'exports', 'avatars'] as const
|
||||
|
||||
function jsonResponse(body: Record<string, unknown>, status = 200): Response {
|
||||
|
|
@ -17,22 +17,6 @@ function jsonResponse(body: Record<string, unknown>, status = 200): Response {
|
|||
})
|
||||
}
|
||||
|
||||
function decodeJwtIssuedAt(authorization: string | null): number | null {
|
||||
const token = authorization?.replace(/^Bearer\s+/i, '')
|
||||
if (!token) return null
|
||||
const payloadPart = token.split('.')[1]
|
||||
if (!payloadPart) return null
|
||||
|
||||
try {
|
||||
const normalized = payloadPart.replace(/-/g, '+').replace(/_/g, '/')
|
||||
const padded = normalized.padEnd(Math.ceil(normalized.length / 4) * 4, '=')
|
||||
const payload = JSON.parse(atob(padded)) as { iat?: unknown }
|
||||
return typeof payload.iat === 'number' ? payload.iat : null
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
async function listStorageFiles(
|
||||
serviceClient: ReturnType<typeof createServiceRoleClient>,
|
||||
bucket: string,
|
||||
|
|
@ -81,9 +65,10 @@ Deno.serve(async (req: Request) => {
|
|||
return jsonResponse({ error: 'Explicit confirmation is required', code: 'CONFIRMATION_REQUIRED' }, 400)
|
||||
}
|
||||
|
||||
const issuedAt = decodeJwtIssuedAt(req.headers.get('Authorization'))
|
||||
// Step-up auth: judged by the session's amr authentication time, never the
|
||||
// access token iat (which every refresh_token grant resets).
|
||||
const nowSeconds = Math.floor(Date.now() / 1000)
|
||||
if (issuedAt === null || nowSeconds - issuedAt > RECENT_AUTH_SECONDS || issuedAt > nowSeconds + 60) {
|
||||
if (!hasRecentAuthentication(req.headers.get('Authorization'), nowSeconds)) {
|
||||
return jsonResponse({ error: 'Recent authentication is required', code: 'REAUTHENTICATION_REQUIRED' }, 403)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue