From e1b1cc01de67ffeff0fa2dffc9d41268f5b1b160 Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Thu, 28 May 2026 17:30:47 -0700 Subject: [PATCH] fix(auth): return 403 instead of 500 for blocked sign-in/sign-up attempts The hooks.before middleware threw plain Errors for the four auth-policy gates (registration disabled, email/password disabled, login allowlist, blocked signup domains). better-auth surfaces an uncaught hook Error as a generic 500 SERVER_ERROR, so users hitting these gates saw 'Failed to create account' with no actionable message. Throw APIError('FORBIDDEN', { message }) instead so the endpoints return a clean 403 with the policy message, which the client surfaces directly. Internal/server failures (email send, provider userinfo fetch, ID-token parse) intentionally remain plain Errors so they continue to surface as 500s. --- apps/sim/lib/auth/auth.ts | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/apps/sim/lib/auth/auth.ts b/apps/sim/lib/auth/auth.ts index 3ea2384a2d2..faffc248668 100644 --- a/apps/sim/lib/auth/auth.ts +++ b/apps/sim/lib/auth/auth.ts @@ -9,7 +9,7 @@ import { toError } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { betterAuth } from 'better-auth' import { drizzleAdapter } from 'better-auth/adapters/drizzle' -import { createAuthMiddleware } from 'better-auth/api' +import { APIError, createAuthMiddleware } from 'better-auth/api' import { nextCookies } from 'better-auth/next-js' import { admin, @@ -793,12 +793,16 @@ export const auth = betterAuth({ hooks: { before: createAuthMiddleware(async (ctx) => { if (ctx.path.startsWith('/sign-up') && isRegistrationDisabled) - throw new Error('Registration is disabled, please contact your admin.') + throw new APIError('FORBIDDEN', { + message: 'Registration is disabled, please contact your admin.', + }) if (!isEmailPasswordEnabled) { const emailPasswordPaths = ['/sign-in/email', '/sign-up/email', '/email-otp'] if (emailPasswordPaths.some((path) => ctx.path.startsWith(path))) - throw new Error('Email/password authentication is disabled. Please use SSO to sign in.') + throw new APIError('FORBIDDEN', { + message: 'Email/password authentication is disabled. Please use SSO to sign in.', + }) } if ( @@ -826,13 +830,17 @@ export const auth = betterAuth({ } if (!isAllowed) { - throw new Error('Access restricted. Please contact your administrator.') + throw new APIError('FORBIDDEN', { + message: 'Access restricted. Please contact your administrator.', + }) } } } if (ctx.path.startsWith('/sign-up') && isSignupEmailBlocked(ctx.body?.email)) { - throw new Error('Sign-ups from this email domain are not allowed.') + throw new APIError('FORBIDDEN', { + message: 'Sign-ups from this email domain are not allowed.', + }) } if (ctx.path === '/oauth2/authorize' || ctx.path === '/oauth2/token') {