import {
getTracker,
OperationFullProvideIdentifierWithCUI,
PasswordEnrollmentOperationFull,
PasswordLoginOperationFull,
} from "@corbado/observe";
const tracker = () => getTracker()!;
let provideIdentifier: OperationFullProvideIdentifierWithCUI;
let passwordLogin: PasswordLoginOperationFull;
let passwordReset: PasswordEnrollmentOperationFull;
// Step 1: User navigates to the login page.
async function showLoginPage(emailInput: HTMLInputElement) {
tracker().flowStarted({
flowName: "login",
touchpoint: "account",
});
tracker().authMethodsDecisionStarted({
decisionName: "pre-identifier",
options: ["identifier-email", "passkey-login-cui", "social-other"],
});
provideIdentifier = tracker().provideIdentifierOperationFull(emailInput);
}
// Step 2: User submits their identifier.
async function submitIdentifier(email: string) {
provideIdentifier.provideIdentifier.postResponse.start({
explicitSpecType: "email",
});
const result = await checkIdentifier(email);
provideIdentifier.provideIdentifier.postResponse.finished(
{},
{ userReference: { userId: result.userId } },
);
}
// Step 3: Password login page loads after the identifier check.
function showPasswordLoginMethod(passwordInput: HTMLInputElement) {
tracker().authMethodsDecisionStarted({
decisionName: "post-identifier",
options: ["password-login-known-identifier", "reset-flow"],
});
passwordLogin = tracker().passwordLoginFullOperation({
inputHtmlField: passwordInput,
explicitSpecType: "password-known-identifier",
});
}
// Step 4: User clicks "forgot password" and starts recovery from the login flow.
async function startPasswordRecovery(email: string) {
tracker().flowStarted({
flowName: "recovery",
touchpoint: "login",
});
const crossEnvironmentTransactionID = crypto.randomUUID();
const emailLink = tracker().emailLinkOperationFull();
emailLink.send.start(
{ explicitSpecType: "email-link-login" },
{ userReference: { crossEnvironmentTransactionID } },
);
await sendPasswordResetEmail(email, { crossEnvironmentTransactionID });
emailLink.send.finished({});
return crossEnvironmentTransactionID;
}
// Step 5: User opens the password reset link from the email.
async function verifyResetLink(token: string, crossEnvironmentTransactionID: string) {
const emailLink = tracker().emailLinkOperationFull();
emailLink.postResponse.start(
{ explicitSpecType: "email-link-login" },
{ userReference: { crossEnvironmentTransactionID } },
);
const result = await verifyPasswordResetToken(token);
emailLink.postResponse.finished(
{},
{
userReference: {
userId: result.userId,
crossEnvironmentTransactionID,
},
},
);
return result.userId;
}
// Step 6: Password reset page loads.
function showSetNewPasswordPage(newPasswordInput: HTMLInputElement) {
passwordReset = tracker().passwordEnrollmentFullOperation({
inputHtmlField: newPasswordInput,
explicitSpecType: "password-reset",
});
}
// Step 7: User submits the new password, but is not logged in automatically.
async function setNewPassword(token: string, newPassword: string) {
passwordReset.postResponse.start({});
const result = await completePasswordReset(token, newPassword);
passwordReset.postResponse.finished({});
tracker().flowFinished({
flowName: "recovery",
userId: result.userId,
});
return result.userId;
}
// Step 8: User returns to login and submits their identifier again.
// Reuse showLoginPage() and submitIdentifier() from above; no new functions are needed.
// Step 9: Password login page loads again after the second identifier check.
function showPasswordLoginAfterRecovery(passwordInput: HTMLInputElement) {
tracker().authMethodsDecisionStarted({
decisionName: "post-identifier",
options: ["password-login-known-identifier", "reset-flow"],
});
passwordLogin = tracker().passwordLoginFullOperation({
inputHtmlField: passwordInput,
explicitSpecType: "password-known-identifier",
});
}
// Step 10: User logs in with the password they just set.
async function loginWithNewPassword(newPassword: string, userId: string) {
passwordLogin.postResponse.start({});
await loginWithPassword(newPassword);
passwordLogin.postResponse.finished({});
tracker().flowFinished({
flowName: "login",
userId,
});
}