Endpoints
POST /api/auth/ban-account
DELETE /api/auth/ban-account
POST /api/auth/ban-account
Bans the authenticated user’s account. Requires password verification and 2FA if enabled. Cancels all active Polar subscriptions.
Request
User password for verification
Required if 2FA is enabled. TOTP code from authenticator app.
Response
“Account banned successfully”
DELETE /api/auth/ban-account
Unbans the authenticated user’s account. Requires password verification and 2FA if enabled. Returns 400 error if account is not currently banned.
Request
User password for verification
Required if 2FA is enabled. TOTP code from authenticator app.
Response
“Account unbanned successfully”
Implementation Details
Code Reference
export async function POST(request: NextRequest) {
const corsResponse = handleCors(request)
if (corsResponse) return corsResponse
const blocked = await protectRoute(request, { requested: 2 })
if (blocked) return blocked
const auth = await requireAuth(request)
if ('error' in auth) return auth.error
try {
const body = await request.json()
const validated = banAccountSchema.parse(body)
const user = await prisma.user.findUnique({
where: { id: auth.userId },
include: {
serviceAccess: true,
},
})
if (!user || !user.passwordHash) {
return errorResponse('User not found', 404, request.headers.get('origin'))
}
const isValid = await bcrypt.compare(validated.password, user.passwordHash)
if (!isValid) {
logger.warn('Account ban failed: Invalid password', auth.userId)
return errorResponse('Invalid password', 401, request.headers.get('origin'))
}
if (user.twoFactorEnabled) {
if (!validated.verificationCode) {
return errorResponse('2FA verification code is required', 401, request.headers.get('origin'))
}
if (!user.twoFactorSecret) {
return errorResponse('2FA is enabled but secret is missing', 500, request.headers.get('origin'))
}
const isValid2FA = speakeasy.totp.verify({
secret: user.twoFactorSecret,
encoding: 'base32',
token: validated.verificationCode,
window: 2,
})
if (!isValid2FA) {
logger.warn('Account ban failed: Invalid 2FA code', auth.userId)
return errorResponse('Invalid 2FA verification code', 401, request.headers.get('origin'))
}
}
for (const service of user.serviceAccess) {
if (service.polarSubscriptionId && process.env.POLAR_ACCESS_TOKEN) {
try {
const response = await fetch(`https://api.polar.sh/v1/subscriptions/${service.polarSubscriptionId}`, {
method: 'DELETE',
headers: {
'Authorization': `Bearer ${process.env.POLAR_ACCESS_TOKEN}`,
'Content-Type': 'application/json',
},
})
if (response.ok) {
logger.info(`Canceled Polar subscription: ${service.polarSubscriptionId}`, auth.userId)
} else {
logger.warn(`Failed to cancel Polar subscription: ${service.polarSubscriptionId}`, response.status)
}
} catch (error) {
logger.error('Failed to cancel Polar subscription:', error)
}
}
}
await createAuditLog(auth.userId, 'USER_BAN', {
email: user.email,
})
await prisma.user.update({
where: { id: auth.userId },
data: {
banned: true,
},
} as any)
logger.info(`User account banned: ${user.email}`, auth.userId)
return jsonResponse(
{ success: true, message: 'Account banned successfully' },
200,
request.headers.get('origin')
)
} catch (error: any) {
if (error.name === 'ZodError') {
logger.warn('Ban account validation error:', error.errors)
return errorResponse(error.errors[0].message, 400, request.headers.get('origin'))
}
logger.error('Ban account error:', error)
return errorResponse('Internal server error', 500, request.headers.get('origin'))
}
}
Status Codes
Validation error or account not banned (DELETE only)
Invalid password or 2FA code
Example Requests
Ban Account
curl -X POST https://auth.nullpass.xyz/api/auth/ban-account \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"password": "yourpassword123",
"verificationCode": "123456"
}'
Unban Account
curl -X DELETE https://auth.nullpass.xyz/api/auth/ban-account \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"password": "yourpassword123",
"verificationCode": "123456"
}'
Security Notes
- Requires password verification
- Requires 2FA code if 2FA is enabled
- All Polar subscriptions are canceled when banning
- Banned accounts cannot authenticate
- Audit logs are created for ban/unban actions
Audit Events
- USER_BAN: Account banned (POST)
- USER_UPDATE: Account unbanned (DELETE, with
action: 'account_unbanned')