& so it begins
This commit is contained in:
148
packages/logic/domains/auth/controller.ts
Normal file
148
packages/logic/domains/auth/controller.ts
Normal file
@@ -0,0 +1,148 @@
|
||||
import { AuthContext, MiddlewareContext, MiddlewareOptions } from "better-auth";
|
||||
import { AccountRepository } from "../user/account.repository";
|
||||
import { FlowExecCtx } from "@/core/flow.execution.context";
|
||||
import { ResultAsync } from "neverthrow";
|
||||
import type { Err } from "@pkg/result";
|
||||
import { authErrors } from "./errors";
|
||||
import { logger } from "@pkg/logger";
|
||||
import { nanoid } from "nanoid";
|
||||
import { db } from "@pkg/db";
|
||||
|
||||
export class AuthController {
|
||||
private readonly mins = 10;
|
||||
|
||||
constructor(private accountRepo: AccountRepository) {}
|
||||
|
||||
sendEmailChangeVerificationEmail(
|
||||
fctx: FlowExecCtx,
|
||||
newEmail: string,
|
||||
token: string,
|
||||
url: string,
|
||||
): ResultAsync<void, Err> {
|
||||
logger.info("Sending email change verification link", {
|
||||
...fctx,
|
||||
newEmail,
|
||||
});
|
||||
logger.debug("Original URL", { ...fctx, url });
|
||||
|
||||
const transformedUrl = url
|
||||
.replace("/api/auth/verify-email", "/account/verify-email")
|
||||
.replace("/api/", "/");
|
||||
|
||||
logger.debug("Transformed URL", { ...fctx, transformedUrl });
|
||||
|
||||
// Simulate email sending with 90/10 success/failure
|
||||
const success = Math.random() > 0.1;
|
||||
|
||||
if (!success) {
|
||||
logger.error("Failed to send email change verification link", {
|
||||
...fctx,
|
||||
error: "Simulated email service failure",
|
||||
});
|
||||
return ResultAsync.fromPromise(
|
||||
Promise.reject(
|
||||
authErrors.emailChangeVerificationFailed(
|
||||
fctx,
|
||||
"Simulated email service failure",
|
||||
),
|
||||
),
|
||||
(error) => error as Err,
|
||||
);
|
||||
}
|
||||
|
||||
logger.info("Email change verification sent successfully", {
|
||||
...fctx,
|
||||
newEmail,
|
||||
});
|
||||
return ResultAsync.fromSafePromise(Promise.resolve(undefined));
|
||||
}
|
||||
|
||||
swapAccountPasswordForTwoFactor(
|
||||
fctx: FlowExecCtx,
|
||||
ctx: MiddlewareContext<
|
||||
MiddlewareOptions,
|
||||
AuthContext & { returned?: unknown; responseHeaders?: Headers }
|
||||
>,
|
||||
) {
|
||||
logger.info("Swapping account password for 2FA", {
|
||||
...fctx,
|
||||
});
|
||||
|
||||
if (!ctx.path.includes("two-factor")) {
|
||||
return ResultAsync.fromSafePromise(Promise.resolve(ctx));
|
||||
}
|
||||
|
||||
if (!ctx.body.password || ctx.body.password.length === 0) {
|
||||
return ResultAsync.fromSafePromise(Promise.resolve(ctx));
|
||||
}
|
||||
|
||||
logger.info("Rotating password for 2FA setup for user", {
|
||||
...fctx,
|
||||
userId: ctx.body.userId,
|
||||
});
|
||||
|
||||
return this.accountRepo
|
||||
.rotatePassword(fctx, ctx.body.userId, nanoid())
|
||||
.mapErr((err) => {
|
||||
logger.error("Failed to rotate password for 2FA", {
|
||||
...fctx,
|
||||
error: err,
|
||||
});
|
||||
return authErrors.passwordRotationFailed(fctx, err.detail);
|
||||
})
|
||||
.map((newPassword) => {
|
||||
logger.info("Password rotated successfully for 2FA setup", {
|
||||
...fctx,
|
||||
});
|
||||
return {
|
||||
...ctx,
|
||||
body: { ...ctx.body, password: newPassword },
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
sendMagicLink(
|
||||
fctx: FlowExecCtx,
|
||||
email: string,
|
||||
token: string,
|
||||
url: string,
|
||||
): ResultAsync<void, Err> {
|
||||
logger.info("Sending magic link", { ...fctx, email });
|
||||
logger.debug("Original URL", { ...fctx, url });
|
||||
|
||||
const transformedUrl = url
|
||||
.replace("/api/auth/magic-link/verify", "/auth/magic-link")
|
||||
.replace("/api/", "/");
|
||||
|
||||
logger.debug("Transformed URL", { ...fctx, transformedUrl });
|
||||
|
||||
// Simulate email sending with 90/10 success/failure
|
||||
const success = Math.random() > 0.1;
|
||||
|
||||
if (!success) {
|
||||
logger.error("Failed to send magic link email", {
|
||||
...fctx,
|
||||
error: "Simulated email service failure",
|
||||
});
|
||||
return ResultAsync.fromPromise(
|
||||
Promise.reject(
|
||||
authErrors.magicLinkEmailFailed(
|
||||
fctx,
|
||||
"Simulated email service failure",
|
||||
),
|
||||
),
|
||||
(error) => error as Err,
|
||||
);
|
||||
}
|
||||
|
||||
logger.info("Magic link email sent successfully", {
|
||||
...fctx,
|
||||
email,
|
||||
});
|
||||
return ResultAsync.fromSafePromise(Promise.resolve(undefined));
|
||||
}
|
||||
}
|
||||
|
||||
export function getAuthController(): AuthController {
|
||||
return new AuthController(new AccountRepository(db));
|
||||
}
|
||||
Reference in New Issue
Block a user