First Commit. Duplicate bunext-sqlite preset

This commit is contained in:
2026-10-18 16:47:11 +01:00
commit 81e4a8aecc
718 changed files with 41695 additions and 0 deletions
+100
View File
@@ -0,0 +1,100 @@
import issueSSOLoginCode from "@/src/functions/backend/auth/issue-sso-login-code";
import logEvent, { grabRequestIP } from "@/src/functions/backend/logger";
import { AppData } from "@/src/data/app-data";
import rateLimit from "@/src/utils/rate-limit";
import findUserByIdentifier from "@/src/functions/backend/auth/find-user-by-identifier";
import grabLoginMethod from "@/src/functions/backend/auth/grab-login-method";
import { LoginIdentifierRequestSchema } from "@/src/schemas/auth-schema";
import parseWithSchema from "@/src/utils/parse-with-schema";
import safeErrorMessage from "@/src/utils/safe-error-message";
import AppError from "@/src/utils/app-error";
import type {
APIResponseObject,
BunextAPIRouteHandler,
} from "@moduletrace/bunext/types";
export const handler: BunextAPIRouteHandler<APIResponseObject> = async ({
req,
body,
}) => {
try {
if (grabLoginMethod() != "code") {
throw new AppError(`Please log in with your password.`);
}
const { login } = parseWithSchema({
schema: LoginIdentifierRequestSchema,
data: body,
});
const window_ms = AppData["RateLimitWindowMinutes"] * 60 * 1000;
const ip_limit = rateLimit({
key: `sso-code-request:ip:${grabRequestIP(req) || "unknown"}`,
limit: AppData["SSOCodeRequestLimit"] * 4,
window_ms,
});
const identifier_limit = rateLimit({
key: `sso-code-request:id:${String(login.email_or_username).toLowerCase()}`,
limit: AppData["SSOCodeRequestLimit"],
window_ms,
});
if (!ip_limit.allowed || !identifier_limit.allowed) {
throw new AppError(`Too many requests. Please try again later.`);
}
const target_user = await findUserByIdentifier({
identifier: login.email_or_username,
});
/**
* Same response whether or not the account exists, so this
* endpoint can't be used to enumerate users.
*/
if (!target_user?.id || !target_user.email) {
await logEvent({
title: "SSO login code requested for unknown user",
category: "auth",
plain_text: `SSO login code requested for unknown identifier.`,
ip_address: grabRequestIP(req),
});
return {
success: true,
};
}
await logEvent({
title: "SSO login code requested",
category: "auth",
user_id: target_user.id,
plain_text: `User #${target_user.id} [${target_user.first_name} ${target_user.last_name}] requested an SSO login code.`,
metadata: {
user_id: target_user.id,
},
ip_address: grabRequestIP(req),
});
return issueSSOLoginCode({
user_id: target_user.id,
email: target_user.email,
method: "code",
});
} catch (error: unknown) {
await logEvent({
title: "SSO login code request failed",
category: "auth",
plain_text: `SSO login code request failed: ${
error instanceof Error ? error.message : String(error)
}`,
ip_address: grabRequestIP(req),
});
return {
success: false,
msg: safeErrorMessage({ error }),
};
}
};
+124
View File
@@ -0,0 +1,124 @@
import loginUser from "@/src/functions/backend/auth/login-user";
import verifyGoogleIdToken from "@/src/functions/backend/auth/verify-google-id-token";
import grabUsers from "@/src/functions/backend/db/users/grab-users";
import createSignupUser from "@/src/functions/backend/auth/create-signup-user";
import generateUniqueUsername from "@/src/functions/backend/auth/generate-unique-username";
import uploadAndRecordMedia from "@/src/functions/backend/db/users/media/upload-and-record-media";
import logEvent, { grabRequestIP } from "@/src/functions/backend/logger";
import { GoogleLoginRequestSchema } from "@/src/schemas/auth-schema";
import parseWithSchema from "@/src/utils/parse-with-schema";
import safeErrorMessage from "@/src/utils/safe-error-message";
import AppError from "@/src/utils/app-error";
import type {
APIResponseObject,
BunextAPIRouteHandler,
} from "@moduletrace/bunext/types";
export const handler: BunextAPIRouteHandler<APIResponseObject> = async ({
req,
body,
}) => {
try {
const { google_token, google_signup } = parseWithSchema({
schema: GoogleLoginRequestSchema,
data: body,
});
const googleClientId = process.env.GOOGLE_CLIENT_ID;
if (!googleClientId) {
throw new Error(`Google login is not configured on this server`);
}
const googleUser = await verifyGoogleIdToken({
clientId: googleClientId,
idToken: google_token,
});
const target_user_res = await grabUsers({
query: {
search_term_email: googleUser.email,
},
exact_match: true,
});
let target_user = target_user_res.singleRes;
if (!target_user?.id && google_signup) {
const new_user_id = await createSignupUser({
first_name:
googleUser.given_name ||
googleUser.name?.split(" ")[0] ||
googleUser.email.split("@")[0] ||
"",
last_name: googleUser.family_name,
email: googleUser.email.toLowerCase(),
username: await generateUniqueUsername({
email: googleUser.email,
}),
});
await logEvent({
title: "Google signup",
category: "auth",
user_id: new_user_id,
plain_text: `User #${new_user_id} signed up with Google.`,
metadata: {
user_id: new_user_id,
},
ip_address: grabRequestIP(req),
});
target_user = (
await grabUsers({
query: { user_id: new_user_id },
})
).singleRes;
}
if (!target_user?.id) {
throw new AppError(
`No account exists for ${googleUser.email}. Please sign up first.`,
);
}
if (googleUser.picture && !target_user.profile_media_id) {
const fullResUrl = googleUser.picture.replace(/=s\d+-c$/, "=s0");
const res = await fetch(fullResUrl);
if (res.ok) {
const bytes = new Uint8Array(await res.arrayBuffer());
await uploadAndRecordMedia({
data: bytes,
user: target_user,
media_paradigms: ["user-profile-image"],
media_type: "image",
is_primary: true,
media_name: `user-${target_user.id}-profile`,
media_mime_type: "jpeg",
});
}
}
return await loginUser({
user_id: target_user.id,
req,
});
} catch (error: unknown) {
await logEvent({
title: "Google login failed",
category: "auth",
plain_text: `Google login failed: ${
error instanceof Error ? error.message : String(error)
}`,
ip_address: grabRequestIP(req),
});
return {
success: false,
msg: safeErrorMessage({ error }),
};
}
};
+52
View File
@@ -0,0 +1,52 @@
import loginUser from "@/src/functions/backend/auth/login-user";
import verifySSOCode from "@/src/functions/backend/auth/verify-sso-code";
import grabLoginMethod from "@/src/functions/backend/auth/grab-login-method";
import logEvent, { grabRequestIP } from "@/src/functions/backend/logger";
import { LoginCodeRequestSchema } from "@/src/schemas/auth-schema";
import parseWithSchema from "@/src/utils/parse-with-schema";
import safeErrorMessage from "@/src/utils/safe-error-message";
import type {
APIResponseObject,
BunextAPIRouteHandler,
} from "@moduletrace/bunext/types";
export const handler: BunextAPIRouteHandler<APIResponseObject> = async ({
req,
body,
}) => {
try {
const { sso_code } = parseWithSchema({
schema: LoginCodeRequestSchema,
data: body,
});
/**
* Only codes issued by the site-wide login method complete a login.
* In password mode that means the password step has passed.
*/
const user_id = await verifySSOCode({
req,
sso_code,
method: grabLoginMethod(),
});
return await loginUser({
user_id,
req,
});
} catch (error: unknown) {
await logEvent({
title: "SSO login failed",
category: "auth",
plain_text: `SSO login failed: ${
error instanceof Error ? error.message : String(error)
}`,
ip_address: grabRequestIP(req),
});
return {
success: false,
msg: safeErrorMessage({ error }),
};
}
};
+102
View File
@@ -0,0 +1,102 @@
import issueSSOLoginCode from "@/src/functions/backend/auth/issue-sso-login-code";
import findUserByIdentifier from "@/src/functions/backend/auth/find-user-by-identifier";
import verifyUserPassword from "@/src/functions/backend/auth/verify-user-password";
import grabLoginMethod from "@/src/functions/backend/auth/grab-login-method";
import logEvent, { grabRequestIP } from "@/src/functions/backend/logger";
import { AppData } from "@/src/data/app-data";
import rateLimit from "@/src/utils/rate-limit";
import { PasswordLoginRequestSchema } from "@/src/schemas/auth-schema";
import parseWithSchema from "@/src/utils/parse-with-schema";
import safeErrorMessage from "@/src/utils/safe-error-message";
import AppError from "@/src/utils/app-error";
import type {
APIResponseObject,
BunextAPIRouteHandler,
} from "@moduletrace/bunext/types";
/**
* First factor of password login. A correct password issues an emailed
* login code (second factor), completed through `api/auth/login`.
*/
export const handler: BunextAPIRouteHandler<APIResponseObject> = async ({
req,
body,
}) => {
try {
if (grabLoginMethod() != "password") {
throw new AppError(`Password login is turned off.`);
}
const { login } = parseWithSchema({
schema: PasswordLoginRequestSchema,
data: body,
});
const identifier = login.email_or_username;
const password = login.password;
const window_ms = AppData["RateLimitWindowMinutes"] * 60 * 1000;
const ip_limit = rateLimit({
key: `password-login:ip:${grabRequestIP(req) || "unknown"}`,
limit: AppData["PasswordAttemptLimit"] * 4,
window_ms,
});
const identifier_limit = rateLimit({
key: `password-login:id:${identifier.toLowerCase()}`,
limit: AppData["PasswordAttemptLimit"],
window_ms,
});
if (!ip_limit.allowed || !identifier_limit.allowed) {
throw new AppError(`Too many attempts. Please try again later.`);
}
const target_user = await findUserByIdentifier({ identifier });
const is_valid = await verifyUserPassword({
user_id: target_user?.id,
password,
});
if (!is_valid || !target_user?.id || !target_user.email) {
await logEvent({
title: "Password login failed",
category: "auth",
user_id: target_user?.id || undefined,
plain_text: target_user?.id
? `Wrong password for user #${target_user.id}.`
: `Password login attempted for unknown identifier.`,
ip_address: grabRequestIP(req),
});
return {
success: false,
msg: `Incorrect email, username or password.`,
};
}
await logEvent({
title: "Password verified",
category: "auth",
user_id: target_user.id,
plain_text: `User #${target_user.id} [${target_user.first_name} ${target_user.last_name}] entered a correct password. Login code sent.`,
metadata: {
user_id: target_user.id,
},
ip_address: grabRequestIP(req),
});
return issueSSOLoginCode({
user_id: target_user.id,
email: target_user.email,
method: "password",
});
} catch (error: unknown) {
return {
success: false,
msg: safeErrorMessage({ error }),
};
}
};
@@ -0,0 +1,95 @@
import issueSSOLoginCode from "@/src/functions/backend/auth/issue-sso-login-code";
import findUserByIdentifier from "@/src/functions/backend/auth/find-user-by-identifier";
import grabLoginMethod from "@/src/functions/backend/auth/grab-login-method";
import logEvent, { grabRequestIP } from "@/src/functions/backend/logger";
import { AppData } from "@/src/data/app-data";
import rateLimit from "@/src/utils/rate-limit";
import { LoginIdentifierRequestSchema } from "@/src/schemas/auth-schema";
import parseWithSchema from "@/src/utils/parse-with-schema";
import safeErrorMessage from "@/src/utils/safe-error-message";
import AppError from "@/src/utils/app-error";
import type {
APIResponseObject,
BunextAPIRouteHandler,
} from "@moduletrace/bunext/types";
/**
* Emails a password reset code (password login mode only). Also how
* accounts without a password (e.g. created before password mode, or via
* Google) set their first one.
*/
export const handler: BunextAPIRouteHandler<APIResponseObject> = async ({
req,
body,
}) => {
try {
if (grabLoginMethod() != "password") {
throw new AppError(`Password login is turned off.`);
}
const { login } = parseWithSchema({
schema: LoginIdentifierRequestSchema,
data: body,
});
const identifier = login.email_or_username;
const window_ms = AppData["RateLimitWindowMinutes"] * 60 * 1000;
const ip_limit = rateLimit({
key: `password-reset:ip:${grabRequestIP(req) || "unknown"}`,
limit: AppData["SSOCodeRequestLimit"] * 4,
window_ms,
});
const identifier_limit = rateLimit({
key: `password-reset:id:${identifier.toLowerCase()}`,
limit: AppData["SSOCodeRequestLimit"],
window_ms,
});
if (!ip_limit.allowed || !identifier_limit.allowed) {
throw new AppError(`Too many requests. Please try again later.`);
}
const target_user = await findUserByIdentifier({ identifier });
/**
* Same response whether or not the account exists.
*/
if (!target_user?.id || !target_user.email) {
await logEvent({
title: "Password reset requested for unknown user",
category: "auth",
plain_text: `Password reset requested for unknown identifier.`,
ip_address: grabRequestIP(req),
});
return {
success: true,
};
}
await logEvent({
title: "Password reset requested",
category: "auth",
user_id: target_user.id,
plain_text: `User #${target_user.id} requested a password reset code.`,
metadata: {
user_id: target_user.id,
},
ip_address: grabRequestIP(req),
});
return issueSSOLoginCode({
user_id: target_user.id,
email: target_user.email,
method: "reset",
});
} catch (error: unknown) {
return {
success: false,
msg: safeErrorMessage({ error }),
};
}
};
+107
View File
@@ -0,0 +1,107 @@
import type { BUN_SQLITE_BSQLITE_USERS, BunSQLiteTables } from "@/db/types/db";
import issueSSOLoginCode from "@/src/functions/backend/auth/issue-sso-login-code";
import { grabRequestIP } from "@/src/functions/backend/logger";
import grabLoginMethod from "@/src/functions/backend/auth/grab-login-method";
import { AppData } from "@/src/data/app-data";
import type { SSOAuth } from "@/src/types";
import { getCookie } from "@/src/utils/cookies";
import { decrypt } from "@/src/utils/crypt";
import EJSON from "@/src/utils/ejson";
import rateLimit from "@/src/utils/rate-limit";
import BunSQLite from "@moduletrace/bun-sqlite";
import safeErrorMessage from "@/src/utils/safe-error-message";
import AppError from "@/src/utils/app-error";
import type {
APIResponseObject,
BunextAPIRouteHandler,
} from "@moduletrace/bunext/types";
/**
* Re-sends the login code for the pending login held in the SSO cookie.
* The cookie is only issued once the first step has passed, and only
* codes for the current login method (or a password reset) are resent.
*/
export const handler: BunextAPIRouteHandler<APIResponseObject> = async ({
req,
}) => {
try {
const window_ms = AppData["RateLimitWindowMinutes"] * 60 * 1000;
const ip_limit = rateLimit({
key: `sso-resend:ip:${grabRequestIP(req) || "unknown"}`,
limit: AppData["SSOCodeRequestLimit"] * 4,
window_ms,
});
if (!ip_limit.allowed) {
throw new AppError(`Too many requests. Please try again later.`);
}
const sso_cookie = getCookie(req, AppData["SSOAuthCookieName"]);
/**
* No pending login (e.g. the identifier didn't match an account).
* Answer like a real resend so this can't be used to probe.
*/
if (!sso_cookie) {
return {
success: true,
};
}
const sso_auth = EJSON.parse(await decrypt(sso_cookie)) as SSOAuth;
const max_age_ms = AppData["SSOPendingLoginMaxMinutes"] * 60 * 1000;
const login_method = grabLoginMethod();
const is_current_method =
sso_auth?.method == login_method ||
(login_method == "password" && sso_auth?.method == "reset");
if (
!sso_auth?.user_id ||
!sso_auth.method ||
!is_current_method ||
!sso_auth.first_step_at ||
Date.now() - sso_auth.first_step_at > max_age_ms
) {
throw new AppError(`Your login session expired. Please start again.`);
}
const user_limit = rateLimit({
key: `sso-resend:user:${sso_auth.user_id}`,
limit: AppData["SSOCodeRequestLimit"],
window_ms,
});
if (!user_limit.allowed) {
throw new AppError(`Too many requests. Please try again later.`);
}
const target_user = (
await BunSQLite.select<
BUN_SQLITE_BSQLITE_USERS,
(typeof BunSQLiteTables)[number]
>({
table: "users",
targetId: sso_auth.user_id,
})
).singleRes;
if (!target_user?.id || !target_user.email) {
throw new AppError(`Your login session expired. Please start again.`);
}
return issueSSOLoginCode({
user_id: target_user.id,
email: target_user.email,
method: sso_auth.method,
first_step_at: sso_auth.first_step_at,
});
} catch (error: unknown) {
return {
success: false,
msg: safeErrorMessage({ error }),
};
}
};
+79
View File
@@ -0,0 +1,79 @@
import loginUser from "@/src/functions/backend/auth/login-user";
import verifySSOCode from "@/src/functions/backend/auth/verify-sso-code";
import setUserPassword from "@/src/functions/backend/auth/set-user-password";
import grabLoginMethod from "@/src/functions/backend/auth/grab-login-method";
import logEvent, { grabRequestIP } from "@/src/functions/backend/logger";
import validatePassword from "@/src/utils/validate-password";
import { ResetPasswordRequestSchema } from "@/src/schemas/auth-schema";
import parseWithSchema from "@/src/utils/parse-with-schema";
import safeErrorMessage from "@/src/utils/safe-error-message";
import AppError from "@/src/utils/app-error";
import type {
APIResponseObject,
BunextAPIRouteHandler,
} from "@moduletrace/bunext/types";
/**
* Completes a password reset: checks the emailed reset code, saves the new
* password, then logs the user in. The emailed code is the proof of
* identity here, as with any email-based reset.
*/
export const handler: BunextAPIRouteHandler<APIResponseObject> = async ({
req,
body,
}) => {
try {
if (grabLoginMethod() != "password") {
throw new AppError(`Password login is turned off.`);
}
const { sso_code, new_password } = parseWithSchema({
schema: ResetPasswordRequestSchema,
data: body,
});
const password_error = validatePassword({ password: new_password });
if (password_error) {
throw new AppError(password_error);
}
const user_id = await verifySSOCode({
req,
sso_code,
method: "reset",
});
await setUserPassword({ user_id, password: new_password });
await logEvent({
title: "Password reset",
category: "auth",
user_id,
plain_text: `User #${user_id} reset their password.`,
metadata: {
user_id,
},
ip_address: grabRequestIP(req),
});
return await loginUser({
user_id,
req,
});
} catch (error: unknown) {
await logEvent({
title: "Password reset failed",
category: "auth",
plain_text: `Password reset failed: ${
error instanceof Error ? error.message : String(error)
}`,
ip_address: grabRequestIP(req),
});
return {
success: false,
msg: safeErrorMessage({ error }),
};
}
};
+157
View File
@@ -0,0 +1,157 @@
import type { BUN_SQLITE_BSQLITE_USERS, BunSQLiteTables } from "@/db/types/db";
import issueSSOLoginCode from "@/src/functions/backend/auth/issue-sso-login-code";
import createSignupUser from "@/src/functions/backend/auth/create-signup-user";
import setUserPassword from "@/src/functions/backend/auth/set-user-password";
import grabLoginMethod from "@/src/functions/backend/auth/grab-login-method";
import validatePassword from "@/src/utils/validate-password";
import { grabRequestIP, logError } from "@/src/functions/backend/logger";
import { AppData } from "@/src/data/app-data";
import rateLimit from "@/src/utils/rate-limit";
import BunSQLite from "@moduletrace/bun-sqlite";
import { SignupRequestSchema } from "@/src/schemas/auth-schema";
import parseWithSchema from "@/src/utils/parse-with-schema";
import safeErrorMessage from "@/src/utils/safe-error-message";
import AppError from "@/src/utils/app-error";
import type {
APIResponseObject,
BunextAPIRouteHandler,
} from "@moduletrace/bunext/types";
export const handler: BunextAPIRouteHandler<APIResponseObject> = async ({
req,
body,
}) => {
try {
const { signup } = parseWithSchema({
schema: SignupRequestSchema,
data: body,
});
const email = signup.email.trim().toLowerCase();
const username = signup.username.trim().toLowerCase();
const window_ms = AppData["RateLimitWindowMinutes"] * 60 * 1000;
const ip_limit = rateLimit({
key: `signup:ip:${grabRequestIP(req) || "unknown"}`,
limit: AppData["SSOCodeRequestLimit"] * 4,
window_ms,
});
const email_limit = rateLimit({
key: `signup:email:${email}`,
limit: AppData["SSOCodeRequestLimit"],
window_ms,
});
if (!ip_limit.allowed || !email_limit.allowed) {
throw new AppError(`Too many requests. Please try again later.`);
}
if (!AppData["UsernameRegex"].test(username)) {
throw new AppError(
`Usernames must be 3-32 characters: lowercase letters, numbers, dots, dashes or underscores`,
);
}
const login_method = grabLoginMethod();
const password_error =
login_method == "password"
? validatePassword({ password: signup.password })
: undefined;
if (password_error) {
throw new AppError(password_error);
}
const existing_email_user = (
await BunSQLite.select<
BUN_SQLITE_BSQLITE_USERS,
(typeof BunSQLiteTables)[number]
>({
table: "users",
query: {
query: {
email: {
value: email,
},
},
},
})
)?.singleRes;
/**
* Respond exactly as a fresh signup would, so this endpoint can't
* be used to discover which emails have accounts. In code mode the
* owner gets a login code. In password mode nothing is sent, since
* a code alone mustn't skip their password.
*/
if (existing_email_user?.id) {
if (login_method == "password") {
return {
success: true,
};
}
return issueSSOLoginCode({
user_id: existing_email_user.id,
email,
method: "code",
});
}
const existing_username_user = (
await BunSQLite.select<
BUN_SQLITE_BSQLITE_USERS,
(typeof BunSQLiteTables)[number]
>({
table: "users",
query: {
query: {
username: {
value: username,
},
},
},
})
)?.singleRes;
if (existing_username_user?.id) {
throw new AppError(
`That username is taken. Please choose another.`,
);
}
const new_user_id = await createSignupUser({
first_name: signup.first_name.trim(),
last_name: signup.last_name?.trim(),
email,
username,
});
if (login_method == "password" && signup.password) {
await setUserPassword({
user_id: new_user_id,
password: signup.password,
});
}
return issueSSOLoginCode({
user_id: new_user_id,
email,
method: login_method,
});
} catch (error: unknown) {
await logError({
title: "Signup failed",
error,
ip_address: grabRequestIP(req),
}).catch(() => {});
return {
success: false,
msg: safeErrorMessage({ error }),
};
}
};