Verify a one-time password (OTP)
This example demonstrates how to verify a one-time password for user authentication. The verification process includes checking if the user exists, ensuring they're active, validating their authentication method configuration, and verifying the provided OTP code.
ts
import { type AsyncResult, Result } from "typescript-result";
interface PasswordAuthMethod {
type: "password";
password: string;
}
interface OneTimePasswordAuthMethod {
type: "one-time-password";
challenge: {
code: string;
expiresAt: Date;
} | null;
}
interface User {
id: string;
status: "active" | "inactive";
email: string;
authMethods: (PasswordAuthMethod | OneTimePasswordAuthMethod)[];
}
declare function findUserById(id: string): AsyncResult<User, NotFoundError>;
function verifyOneTimePassword(userId: string, code: string) {
return findUserById(userId).map((user) => {
if (user.status !== "active") {
return Result.error(
new UserNotActiveError("Cannot verify OTP for inactive user"),
);
}
const authMethod = user.authMethods.find(
(method) => method.type === "one-time-password",
);
if (!authMethod) {
return Result.error(
new InvalidAuthMethodError(
"User has not configured one-time password as auth method",
),
);
}
if (!authMethod.challenge) {
return Result.error(
new InvalidAuthMethodError(
"User has not requested a one-time password challenge",
),
);
}
if (authMethod.challenge.code !== code) {
return Result.error(
new InvalidOneTimePasswordError("Invalid one-time password"),
);
}
return Result.ok();
});
}
const result = await verifyOneTimePassword("user-id", "123456")
ts
import { type AsyncResult, Result } from "typescript-result";
interface PasswordAuthMethod {
type: "password";
password: string;
}
interface OneTimePasswordAuthMethod {
type: "one-time-password";
challenge: {
code: string;
expiresAt: Date;
} | null;
}
interface User {
id: string;
status: "active" | "inactive";
email: string;
authMethods: (PasswordAuthMethod | OneTimePasswordAuthMethod)[];
}
declare function findUserById(id: string): AsyncResult<User, NotFoundError>;
function verifyOneTimePassword(userId: string, code: string) {
return Result.gen(function* () {
const user = yield* findUserById(userId);
if (user.status !== "active") {
return Result.error(
new UserNotActiveError("Cannot verify OTP for inactive user"),
);
}
const authMethod = user.authMethods.find(
(method) => method.type === "one-time-password",
);
if (!authMethod) {
return Result.error(
new InvalidAuthMethodError(
"User has not configured one-time password as auth method",
),
);
}
if (!authMethod.challenge) {
return Result.error(
new InvalidAuthMethodError(
"User has not requested a one-time password challenge",
),
);
}
if (authMethod.challenge.code !== code) {
return Result.error(
new InvalidOneTimePasswordError("Invalid one-time password"),
);
}
return;
});
}
const result = await verifyOneTimePassword("user-id", "123456")
ts
class NotFoundError extends Error {
readonly type = "not-found-error";
}
class UserNotActiveError extends Error {
readonly type = "user-not-active-error";
}
class InvalidAuthMethodError extends Error {
readonly type = "invalid-auth-method-error";
}
class InvalidOneTimePasswordError extends Error {
readonly type = "invalid-one-time-password-error";
}