A comprehensive authentication and OTP error handling system built on the @khni/error-handler foundation, designed for seamless integration with the @khni/auth package. This TypeScript/JavaScript library provides structured error classes, type-safe error codes, and consistent HTTP response mappings for robust authentication workflows.
npm install @khni/auth-errors
pnpm add @khni/auth-errors
yarn add @khni/auth-errors- π·οΈ Type-safe error codes with full TypeScript support
- π― Separated concerns - Authentication and OTP errors in separate modules
- π Domain vs Unexpected errors - Clear distinction between user-facing and system errors
- π§ Factory patterns - Consistent error creation with utilities
- π Comprehensive documentation - Full JSDoc support for API Extractor
- π¦ HTTP status mapping - Automatic status code and message mapping
- π‘οΈ Type guards - Runtime error type checking
import {
AuthDomainError,
AuthUnexpectedError,
OtpDomainError,
AuthErrorCodes,
OtpErrorCodes,
} from "@khni/auth-errors";
// Throw authentication domain errors
if (emailAlreadyExists) {
throw new AuthDomainError(AuthErrorCodes.AUTH_USED_EMAIL);
}
// Throw OTP domain errors
if (otpIsInvalid) {
throw new OtpDomainError(OtpErrorCodes.OTP_INVALID);
}
// Throw unexpected errors with original cause
try {
await createUser(userData);
} catch (error) {
throw new AuthUnexpectedError(
AuthErrorCodes.AUTH_USER_CREATION_FAILED,
error
);
}Domain Errors (User-facing, expected):
AUTH_USED_EMAIL- Email already registeredINCORRECT_CREDENTIALS- Invalid email/passwordINVALID_ACCESS_TOKEN- Malformed or invalid JWTREFRESH_TOKEN_INVALID- Expired or revoked refresh token- And 15+ more...
Unexpected Errors (System-level):
AUTH_USER_CREATION_FAILED- Database failure during user creationFINDING_USER_FAILED- User search operation failedISSUE_TOKEN_FAILED- JWT generation failure- And 7+ more...
Domain Errors (User-facing, expected):
OTP_INVALID- Incorrect OTP codeOTP_EXPIRED- OTP code has expiredOTP_TOKEN_INVALID- Invalid OTP session token
Unexpected Errors (System-level):
OTP_CREATION_FAILED- OTP generation service failureOTP_VERIFICATION_FAILED- OTP validation service failure
import {
AuthDomainError,
AuthUnexpectedError,
OtpDomainError,
AuthErrorCodes,
OtpErrorCodes,
} from "@khni/auth-errors";
// Authentication domain errors
function registerUser(email: string) {
if (await userExists(email)) {
throw new AuthDomainError(
AuthErrorCodes.AUTH_USED_EMAIL,
`Email ${email} is already registered`,
{ email, source: "registration" }
);
}
}
// OTP domain errors
function verifyOtp(userId: string, otpCode: string) {
const storedOtp = await getStoredOtp(userId);
if (storedOtp.code !== otpCode) {
throw new OtpDomainError(
OtpErrorCodes.OTP_INVALID,
"Invalid verification code",
{ userId, attempts: storedOtp.attempts }
);
}
}
// Unexpected errors with cause
async function resetPassword(userId: string, newPassword: string) {
try {
await updatePassword(userId, newPassword);
} catch (error) {
throw new AuthUnexpectedError(
AuthErrorCodes.PASSWORD_RESET_FAILED,
error,
"Password reset operation failed",
{ userId, timestamp: new Date() }
);
}
}import { AuthErrorFactories, OtpErrorFactories } from "@khni/auth-errors";
// Simplified error creation
function authenticateUser(email: string, password: string) {
const user = await findUserByEmail(email);
if (!user) {
throw AuthErrorFactories.domain("USER_IS_NOT_EXIST", { email });
}
if (!user.verified) {
throw AuthErrorFactories.domain("AUTH_UNVERIFIED_EMAIL", {
userId: user.id,
});
}
if (otpAttempts > 3) {
throw OtpErrorFactories.domain("OTP_INVALID", {
userId: user.id,
attempts: otpAttempts,
});
}
}import {
AuthError,
OtpError,
isAuthDomainError,
isOtpDomainError,
getAuthErrorResponse,
getOtpErrorResponse,
} from "@khni/auth-errors";
app.use((error: Error, req: Request, res: Response, next: NextFunction) => {
// Handle authentication errors
if (error instanceof AuthError) {
const config = getAuthErrorResponse(error.code);
if (isAuthDomainError(error)) {
// Log domain errors as warnings
logger.warn("Auth domain error", { code: error.code, meta: error.meta });
} else {
// Log unexpected errors with full details
logger.error("Auth unexpected error", {
code: error.code,
cause: error.cause,
meta: error.meta,
});
}
return res.status(config.statusCode).json({
error: config.responseMessage,
code: error.code,
});
}
// Handle OTP errors
if (error instanceof OtpError) {
const config = getOtpErrorResponse(error.code);
if (isOtpDomainError(error)) {
logger.warn("OTP domain error", { code: error.code, meta: error.meta });
} else {
logger.error("OTP unexpected error", {
code: error.code,
cause: error.cause,
meta: error.meta,
});
}
return res.status(config.statusCode).json({
error: config.responseMessage,
code: error.code,
});
}
// Handle other errors
next(error);
});Base class for all authentication errors.
Expected business logic errors during authentication.
System-level failures during authentication.
Base class for all OTP errors.
Expected business logic errors during OTP operations.
System-level failures during OTP operations.
Returns HTTP status code and message for authentication errors.
Returns HTTP status code and message for OTP errors.
isAuthDomainError(error: unknown)isAuthUnexpectedError(error: unknown)isOtpDomainError(error: unknown)isOtpUnexpectedError(error: unknown)
.domain(code, meta?)- Create domain errors.unexpected(code, cause, meta?)- Create unexpected errors
.domain(code, meta?)- Create domain errors.unexpected(code, cause, meta?)- Create unexpected errors
| Code | HTTP Status | Message | Description |
|---|---|---|---|
AUTH_USED_EMAIL |
409 | Email already registered | Email exists during registration |
AUTH_USED_IDENTIFIER |
409 | Identifier already registered | Username/identifier taken |
AUTH_UNVERIFIED_EMAIL |
403 | Email verification required | User needs to verify email |
INCORRECT_CREDENTIALS |
401 | Invalid credentials | Wrong email/password |
USER_NOT_LOCAL |
400 | External authentication required | OAuth user tried local auth |
REFRESH_TOKEN_INVALID |
401 | Refresh token is invalid or expired | Refresh token validation failed |
INVALID_ACCESS_TOKEN |
401 | Access token is invalid or expired | Malformed JWT token |
EXPIRED_ACCESS_TOKEN |
401 | Access token is expired | JWT token expired |
MISSING_ACCESS_TOKEN |
401 | Access token is missing | No Authorization header |
MISSING_REFRESH_TOKEN |
400 | You are already logged out | No refresh token during logout |
USER_IS_NOT_EXIST |
400 | Email does not exist | User not found |
| Code | HTTP Status | Message | Description |
|---|---|---|---|
FINDING_USER_FAILED |
500 | Something went wrong while finding the user | Database search failure |
AUTH_USER_CREATION_FAILED |
500 | Account creation failed | User creation database failure |
ISSUE_TOKEN_FAILED |
400 | Unexpected error while issuing tokens | JWT generation failure |
PASSWORD_RESET_FAILED |
500 | Password Reset Failed | Password update operation failed |
REFRESHTOKEN_REVOKE_FAILED |
500 | Failed to Logout | Refresh token revocation failed |
REFRESHTOKEN_CREATE_FAILED |
500 | Failed to create refresh token | Refresh token creation failed |
REFRESHTOKEN_VERIFY_FAILED |
500 | Failed to verify refresh token | Refresh token verification failed |
LOGIN_FAILED |
500 | Something went wrong while login | General login process failure |
| Code | HTTP Status | Message | Description |
|---|---|---|---|
OTP_INVALID |
401 | The OTP you entered is incorrect | Wrong OTP code provided |
OTP_EXPIRED |
401 | The OTP is expired | OTP code has expired |
TOKEN_EXPIRED |
401 | Token is expired | OTP session token expired |
OTP_TOKEN_INVALID |
401 | Please request new OTP | Invalid or expired OTP token |
| Code | HTTP Status | Message | Description |
|---|---|---|---|
OTP_CREATION_FAILED |
500 | Something went wrong while creating OTP | OTP generation service failure |
OTP_VERIFICATION_FAILED |
500 | Something went wrong while verifying OTP | OTP validation service failure |
// Use domain errors for business logic
if (!user.exists) {
throw new AuthDomainError("USER_IS_NOT_EXIST");
}
// Use unexpected errors for system failures
try {
await databaseOperation();
} catch (error) {
throw new AuthUnexpectedError("FINDING_USER_FAILED", error);
}// Include relevant context in meta
throw new AuthDomainError("INCORRECT_CREDENTIALS", "Multiple failed attempts", {
userId: user.id,
attempts: loginAttempts,
ip: request.ip,
});import { isOtpDomainError } from "@khni/auth-errors";
try {
await verifyOtp(userId, otpCode);
} catch (error) {
if (isOtpDomainError(error)) {
// User can retry with new OTP
return res.status(401).json({
error: error.message,
canRetry: true,
});
}
throw error; // Re-throw unexpected errors
}import {
AuthError,
isAuthDomainError,
isAuthUnexpectedError,
} from "@khni/auth-errors";
function logAuthError(error: AuthError) {
if (isAuthDomainError(error)) {
logger.warn("Authentication domain error", {
code: error.code,
message: error.message,
meta: error.meta,
});
} else if (isAuthUnexpectedError(error)) {
logger.error("Authentication system error", {
code: error.code,
message: error.message,
cause: error.cause,
meta: error.meta,
stack: error.stack,
});
// Send to error monitoring service
sentry.captureException(error);
}
}If you're migrating from a previous version:
-
Import paths changed:
// Before import { AuthError } from "@khni/auth-errors"; // After import { AuthError } from "@khni/auth-errors"; // Paths remain the same but internal structure changed
-
Error code constants are now enums:
// Before throw new Error("USER_NOT_FOUND"); // After throw new AuthDomainError(AuthErrorCodes.USER_IS_NOT_EXIST);
- Fork the repository
- Create a feature branch:
git checkout -b feature/new-error-codes - Commit your changes:
git commit -am 'Add new error codes' - Push to the branch:
git push origin feature/new-error-codes - Submit a pull request
MIT License - see LICENSE file for details.
- π Full API Documentation
- π Issue Tracker
- π¬ Discussions