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 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 handler
rpc.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);
}
});
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 to BASE_URL env var)Returns: Response - HTTP 302 redirect to /auth/error
Example:
return redirectToAuthError("Invalid credentials");
// Redirects to: http://localhost:5173/auth/error?error=Invalid_credentials
buildAuthErrorUrl(errorMessage, frontendUrl?)Builds the error page URL without creating a redirect response.
Parameters:
errorMessage (string): User-friendly error messagefrontendUrl (string, optional): Frontend base URLReturns: string - Full URL to error page
Example:
const errorUrl = buildAuthErrorUrl("Session expired");
// Returns: "http://localhost:5173/auth/error?error=Session_expired"
encodeAuthError(message)Encodes error messages for URL transmission (replaces spaces with underscores, following better-auth convention).
Parameters:
message (string): Error message to encodeReturns: string - Encoded message
Example:
const encoded = encodeAuthError("User not found");
// Returns: "User_not_found"
Always provide clear, actionable error messages:
// ✅ Good - clear and actionable
redirectToAuthError("Invalid credentials. Please try again.");
// ❌ Bad - technical jargon
redirectToAuthError("LDAP bind failed: ERR_INVALID_DN");
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"
);
}
For strategies that auto-create users, check if registration is allowed:
import { AuthApi } from "@checkstack/auth-common";
// Check platform registration status
const authClient = rpcClient.forPlugin(AuthApi);
const { allowRegistration } = await authClient.getRegistrationStatus();
if (!allowRegistration) {
return redirectToAuthError(
"Registration is disabled. Please contact an administrator."
);
}
The /auth/error page:
_ back to spaces)When testing your custom auth strategy:
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
curl -X POST http://localhost:3000/api/auth-backend/custom/login \
-H "Content-Type: application/json" \
-d '{}'
Should redirect to /auth/error with validation message
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.