Custom Authentication Strategy Error Handling
When implementing custom authentication strategies (like LDAP or custom OAuth providers), it’s important to provide a consistent error experience for end users. The auth-backend plugin provides utilities to redirect authentication errors to a centralized error page with user-friendly messages.
The Error Redirect Utility
Section titled “The Error Redirect Utility”The redirectToAuthError utility ensures all authentication errors are handled consistently by redirecting users to /auth/error with properly encoded error messages.
import { redirectToAuthError } from "@checkstack/auth-backend";
// In your custom HTTP authentication handlerrpc.registerHttpHandler("/api/auth-backend/custom/login", async (req: Request) => { try { const credentials = await req.json();
// Validate credentials if (!credentials.username || !credentials.password) { return redirectToAuthError("Username and password are required"); }
// Authenticate user const result = await authenticateUser(credentials);
if (!result.success) { return redirectToAuthError( result.error || "Authentication failed" ); }
// Success - return session cookie return Response.json({ success: true }, { headers: { "Set-Cookie": `session=${result.sessionToken}; ...` } });
} catch (error) { logger.error("Auth error:", error); const message = error instanceof Error ? error.message : "Authentication failed. Please try again."; return redirectToAuthError(message); }});Available Functions
Section titled “Available Functions”redirectToAuthError(errorMessage, frontendUrl?)
Section titled “redirectToAuthError(errorMessage, frontendUrl?)”Creates an HTTP redirect response to the auth error page.
Parameters:
errorMessage(string): User-friendly error messagefrontendUrl(string, optional): Frontend base URL (defaults toBASE_URLenv var)
Returns: Response - HTTP 302 redirect to /auth/error
Example:
return redirectToAuthError("Invalid credentials");// Redirects to: http://localhost:5173/auth/error?error=Invalid_credentialsbuildAuthErrorUrl(errorMessage, frontendUrl?)
Section titled “buildAuthErrorUrl(errorMessage, frontendUrl?)”Builds the error page URL without creating a redirect response.
Parameters:
errorMessage(string): User-friendly error messagefrontendUrl(string, optional): Frontend base URL
Returns: string - Full URL to error page
Example:
const errorUrl = buildAuthErrorUrl("Session expired");// Returns: "http://localhost:5173/auth/error?error=Session_expired"encodeAuthError(message)
Section titled “encodeAuthError(message)”Encodes error messages for URL transmission (replaces spaces with underscores, following better-auth convention).
Parameters:
message(string): Error message to encode
Returns: string - Encoded message
Example:
const encoded = encodeAuthError("User not found");// Returns: "User_not_found"Best Practices
Section titled “Best Practices”1. Use User-Friendly Messages
Section titled “1. Use User-Friendly Messages”Always provide clear, actionable error messages:
// ✅ Good - clear and actionableredirectToAuthError("Invalid credentials. Please try again.");
// ❌ Bad - technical jargonredirectToAuthError("LDAP bind failed: ERR_INVALID_DN");2. Handle All Error Cases
Section titled “2. Handle All Error Cases”Ensure every error path redirects to the error page:
try { // ... auth logic ...} catch (error) { // Always redirect, never return JSON errors return redirectToAuthError( error instanceof Error ? error.message : "Authentication failed" );}3. Check Registration Status
Section titled “3. Check Registration Status”For strategies that auto-create users, check if registration is allowed:
import { AuthApi } from "@checkstack/auth-common";
// Check platform registration statusconst authClient = rpcClient.forPlugin(AuthApi);const { allowRegistration } = await authClient.getRegistrationStatus();
if (!allowRegistration) { return redirectToAuthError( "Registration is disabled. Please contact an administrator." );}Error Page Behavior
Section titled “Error Page Behavior”The /auth/error page:
- Decodes underscore-encoded messages (converts
_back to spaces) - Maps technical errors to user-friendly equivalents
- Provides “Try Again” and “Go Home” buttons for navigation
- Uses consistent styling with the rest of the application
Testing
Section titled “Testing”When testing your custom auth strategy:
-
Test with invalid credentials:
Terminal window curl -X POST http://localhost:3000/api/auth-backend/custom/login \-H "Content-Type: application/json" \-d '{"username":"wrong","password":"wrong"}'Should redirect to
/auth/error -
Test with missing fields:
Terminal window curl -X POST http://localhost:3000/api/auth-backend/custom/login \-H "Content-Type: application/json" \-d '{}'Should redirect to
/auth/errorwith validation message -
Verify error messages are user-friendly:
- Navigate to the error page in a browser
- Confirm underscores are converted to spaces
- Check that messages are clear and actionable
Example: LDAP Plugin
Section titled “Example: LDAP Plugin”See plugins/auth-ldap-backend/src/index.ts for a complete reference implementation that uses redirectToAuthError for all error cases in a custom authentication handler.