Update login pipeline. Limit login to only password
This commit is contained in:
@@ -40,13 +40,6 @@ export const handler: BunextAPIRouteHandler<APIResponseObject> = async (
|
||||
}
|
||||
|
||||
try {
|
||||
if (
|
||||
user_types.find((ty) => ty.user_type == "read_only") &&
|
||||
!req.method.match(/get/)
|
||||
) {
|
||||
throw new Error(`This user can only read records.`);
|
||||
}
|
||||
|
||||
const [table, id] = params.query.paths.split("/") as [
|
||||
TableType,
|
||||
string | number,
|
||||
@@ -63,10 +56,6 @@ export const handler: BunextAPIRouteHandler<APIResponseObject> = async (
|
||||
};
|
||||
|
||||
switch (table) {
|
||||
case "users":
|
||||
return await users(crud_params);
|
||||
case "user_types":
|
||||
return await userTypes(crud_params);
|
||||
case "media":
|
||||
return await media(crud_params);
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ export const handler: BunextAPIRouteHandler<APIResponseObject> = async (
|
||||
const { id, ids, media_paradigm, user_id } = body;
|
||||
|
||||
const can_delete_all_media = checkUserAccess({
|
||||
includes: ["admin", "board_member"],
|
||||
includes: ["admin"],
|
||||
user_types,
|
||||
});
|
||||
|
||||
|
||||
@@ -1,133 +0,0 @@
|
||||
import type {
|
||||
BUN_SQLITE_WGUI_SSO_LOGIN_CODES,
|
||||
BUN_SQLITE_WGUI_USERS,
|
||||
BunSQLiteTables,
|
||||
} from "@/db/types/db";
|
||||
import { AppData } from "@/src/data/app-data";
|
||||
import { SiteData } from "@/src/data/site-data";
|
||||
import sendEmail from "@/src/functions/backend/email/send-email";
|
||||
import type { ApiReqParams, SSOAuth } from "@/src/types";
|
||||
import { setCookies } from "@/src/utils/cookies";
|
||||
import { encrypt } from "@/src/utils/crypt";
|
||||
import EJSON from "@/src/utils/ejson";
|
||||
import BunSQLite from "@moduletrace/bun-sqlite";
|
||||
import BunSQLiteB from "@moduletrace/bun-sqlite";
|
||||
import type {
|
||||
APIResponseObject,
|
||||
BunextAPIRouteHandler,
|
||||
} from "@moduletrace/bunext/types";
|
||||
|
||||
export const handler: BunextAPIRouteHandler<APIResponseObject> = async ({
|
||||
req,
|
||||
body,
|
||||
}) => {
|
||||
try {
|
||||
const { login } = body as ApiReqParams;
|
||||
|
||||
if (!login?.email_or_username) {
|
||||
throw new Error(`Please pass an email or phone number`);
|
||||
}
|
||||
|
||||
const target_user = (
|
||||
await BunSQLiteB.select<
|
||||
BUN_SQLITE_WGUI_USERS,
|
||||
(typeof BunSQLiteTables)[number]
|
||||
>({
|
||||
table: "users",
|
||||
query: {
|
||||
query: {
|
||||
email: {
|
||||
value: login.email_or_username,
|
||||
},
|
||||
username: {
|
||||
value: login.email_or_username,
|
||||
},
|
||||
},
|
||||
searchOperator: "OR",
|
||||
},
|
||||
})
|
||||
)?.singleRes;
|
||||
|
||||
if (!target_user?.id || !target_user.email) {
|
||||
throw new Error(`User not Found!`);
|
||||
}
|
||||
|
||||
const sso_code = Math.random().toString().slice(2, 8);
|
||||
|
||||
const email_component = (
|
||||
<div>
|
||||
<div>Use this Code to Complete your login</div>
|
||||
<h1>{sso_code}</h1>
|
||||
<div>
|
||||
Code expires in {AppData["SSOCodeExpiryMinutes"]} minutes.
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const send_mail = await sendEmail({
|
||||
content: email_component,
|
||||
to:
|
||||
process.env.NODE_ENV == "production"
|
||||
? target_user.email
|
||||
: "[email protected]",
|
||||
subject: `Use one-time-code to complete your login.`,
|
||||
text: `Complete your login to the SBF portal`,
|
||||
title: `${SiteData["SiteName"]} SSO`,
|
||||
options: {
|
||||
priority: "high",
|
||||
},
|
||||
});
|
||||
|
||||
if (!send_mail.success) {
|
||||
return send_mail;
|
||||
}
|
||||
|
||||
const record_sso = await BunSQLite.insert<
|
||||
BUN_SQLITE_WGUI_SSO_LOGIN_CODES,
|
||||
(typeof BunSQLiteTables)[number]
|
||||
>({
|
||||
data: [
|
||||
{
|
||||
code: sso_code,
|
||||
user_id: target_user.id,
|
||||
},
|
||||
],
|
||||
table: "sso_login_codes",
|
||||
update_on_duplicate: true,
|
||||
});
|
||||
|
||||
const sso_auth: SSOAuth = {
|
||||
user_id: target_user.id,
|
||||
sso_code,
|
||||
email: target_user.email,
|
||||
};
|
||||
|
||||
const sso_auth_string = EJSON.stringify(sso_auth);
|
||||
if (!sso_auth_string) {
|
||||
throw new Error(`Couldn't Stringify SSO Auth`);
|
||||
}
|
||||
|
||||
const encrypted_sso_auth_string = await encrypt(sso_auth_string);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
bunext_api_route_res_transform_fn(res) {
|
||||
const new_res = res.clone();
|
||||
setCookies(new_res, [
|
||||
{
|
||||
name: AppData["SSOAuthCookieName"],
|
||||
value: encrypted_sso_auth_string,
|
||||
},
|
||||
]);
|
||||
return new_res;
|
||||
},
|
||||
};
|
||||
} catch (error: any) {
|
||||
console.log("error", error.message);
|
||||
|
||||
return {
|
||||
success: false,
|
||||
msg: error.message,
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -1,74 +0,0 @@
|
||||
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 uploadAndRecordMedia from "@/src/functions/backend/db/users/media/upload-and-record-media";
|
||||
import type { ApiReqParams } from "@/src/types";
|
||||
import type {
|
||||
APIResponseObject,
|
||||
BunextAPIRouteHandler,
|
||||
} from "@moduletrace/bunext/types";
|
||||
|
||||
export const handler: BunextAPIRouteHandler<APIResponseObject> = async ({
|
||||
body,
|
||||
}) => {
|
||||
try {
|
||||
const { google_token } = body as ApiReqParams;
|
||||
|
||||
if (!google_token) {
|
||||
throw new Error(`No Google token was provided`);
|
||||
}
|
||||
|
||||
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,
|
||||
});
|
||||
|
||||
const target_user = target_user_res.singleRes;
|
||||
|
||||
if (!target_user?.id) {
|
||||
throw new Error(`No account exists for ${googleUser.email}`);
|
||||
}
|
||||
|
||||
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,
|
||||
});
|
||||
} catch (error: any) {
|
||||
return {
|
||||
success: false,
|
||||
msg: error.message,
|
||||
};
|
||||
}
|
||||
};
|
||||
+32
-52
@@ -1,14 +1,9 @@
|
||||
import type {
|
||||
BUN_SQLITE_WGUI_SSO_LOGIN_CODES,
|
||||
BUN_SQLITE_WGUI_USERS,
|
||||
BunSQLiteTables,
|
||||
} from "@/db/types/db";
|
||||
import { AppData } from "@/src/data/app-data";
|
||||
import loginUser from "@/src/functions/backend/auth/login-user";
|
||||
import type { ApiReqParams, SSOAuth } from "@/src/types";
|
||||
import { getCookie } from "@/src/utils/cookies";
|
||||
import { decrypt } from "@/src/utils/crypt";
|
||||
import EJSON from "@/src/utils/ejson";
|
||||
import type { ApiReqParams } from "@/src/types";
|
||||
import BunSQLite from "@moduletrace/bun-sqlite";
|
||||
import type {
|
||||
APIResponseObject,
|
||||
@@ -16,51 +11,17 @@ import type {
|
||||
} from "@moduletrace/bunext/types";
|
||||
|
||||
export const handler: BunextAPIRouteHandler<APIResponseObject> = async ({
|
||||
req,
|
||||
body,
|
||||
}) => {
|
||||
try {
|
||||
const { sso_code } = body as ApiReqParams;
|
||||
const { login } = body as ApiReqParams;
|
||||
|
||||
const sso_cookie = getCookie(req, AppData["SSOAuthCookieName"]);
|
||||
|
||||
if (!sso_cookie) {
|
||||
throw new Error(`No SSO session found!`);
|
||||
if (!login?.email_or_username) {
|
||||
throw new Error(`Please enter your email or username`);
|
||||
}
|
||||
|
||||
if (!sso_code) {
|
||||
throw new Error(`No SSO code sent!`);
|
||||
}
|
||||
|
||||
const decrypted_sso_json = await decrypt(sso_cookie);
|
||||
const decrypted_sso_object = EJSON.parse(decrypted_sso_json) as SSOAuth;
|
||||
|
||||
const target_sso = (
|
||||
await BunSQLite.select<
|
||||
BUN_SQLITE_WGUI_SSO_LOGIN_CODES,
|
||||
(typeof BunSQLiteTables)[number]
|
||||
>({
|
||||
table: "sso_login_codes",
|
||||
query: {
|
||||
query: {
|
||||
code: {
|
||||
value: sso_code,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
).singleRes;
|
||||
|
||||
if (!target_sso?.code) {
|
||||
throw new Error(`Invalid Code!`);
|
||||
}
|
||||
|
||||
const now = Date.now();
|
||||
const time_elapsed = now - Number(target_sso.updated_at);
|
||||
const expirty_time = AppData["SSOCodeExpiryMinutes"] * 60 * 1000;
|
||||
|
||||
if (time_elapsed > expirty_time) {
|
||||
throw new Error(`Code Expired. Please Login again.`);
|
||||
if (!login?.password) {
|
||||
throw new Error(`Please enter your password`);
|
||||
}
|
||||
|
||||
const target_user = (
|
||||
@@ -71,16 +32,35 @@ export const handler: BunextAPIRouteHandler<APIResponseObject> = async ({
|
||||
table: "users",
|
||||
query: {
|
||||
query: {
|
||||
email: decrypted_sso_object.email
|
||||
? { value: decrypted_sso_object.email }
|
||||
: undefined,
|
||||
email: {
|
||||
value: login.email_or_username,
|
||||
},
|
||||
username: {
|
||||
value: login.email_or_username,
|
||||
},
|
||||
},
|
||||
searchOperator: "OR",
|
||||
},
|
||||
})
|
||||
).singleRes;
|
||||
)?.singleRes;
|
||||
|
||||
if (!target_user?.id) {
|
||||
throw new Error(`This device wasn't used to get this SSO code`);
|
||||
if (!target_user?.id || !target_user.password) {
|
||||
throw new Error(`Invalid email or password`);
|
||||
}
|
||||
|
||||
let password_matches = false;
|
||||
|
||||
try {
|
||||
password_matches = await Bun.password.verify(
|
||||
login.password,
|
||||
target_user.password,
|
||||
);
|
||||
} catch (error) {
|
||||
password_matches = false;
|
||||
}
|
||||
|
||||
if (!password_matches) {
|
||||
throw new Error(`Invalid email or password`);
|
||||
}
|
||||
|
||||
return await loginUser({
|
||||
@@ -92,4 +72,4 @@ export const handler: BunextAPIRouteHandler<APIResponseObject> = async ({
|
||||
msg: error.message,
|
||||
};
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -1,4 +1,8 @@
|
||||
import type { BUN_SQLITE_WGUI_USERS } from "@/db/types/db";
|
||||
import type {
|
||||
BUN_SQLITE_WGUI_USER_TYPES,
|
||||
BUN_SQLITE_WGUI_USERS,
|
||||
} from "@/db/types/db";
|
||||
import { AppData } from "@/src/data/app-data";
|
||||
import grabUsers from "@/src/functions/backend/db/users/grab-users";
|
||||
import type { ApiReqParams, TableType } from "@/src/types";
|
||||
import BunSQLite from "@moduletrace/bun-sqlite";
|
||||
@@ -6,8 +10,6 @@ import type {
|
||||
APIResponseObject,
|
||||
BunextAPIRouteHandler,
|
||||
} from "@moduletrace/bunext/types";
|
||||
import { hash } from "bun";
|
||||
import _ from "lodash";
|
||||
|
||||
export const handler: BunextAPIRouteHandler<
|
||||
APIResponseObject<BUN_SQLITE_WGUI_USERS>
|
||||
@@ -23,26 +25,69 @@ export const handler: BunextAPIRouteHandler<
|
||||
},
|
||||
});
|
||||
|
||||
console.log("users_res", users_res);
|
||||
|
||||
if (users_res.singleRes?.id) {
|
||||
return {
|
||||
success: false,
|
||||
};
|
||||
}
|
||||
|
||||
let final_insert_data = [
|
||||
...(body?.insert_data?.map((data) => {
|
||||
return { ...data, password: hash(data.password || "") };
|
||||
}) || []),
|
||||
];
|
||||
const final_insert_data = await Promise.all(
|
||||
body?.insert_data?.map(async (data) => {
|
||||
if (!data.password) {
|
||||
throw new Error(
|
||||
`Password is required to create the first user.`,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
...data,
|
||||
password: await Bun.password.hash(data.password, {
|
||||
algorithm: "bcrypt",
|
||||
cost: AppData["HashCost"],
|
||||
}),
|
||||
id: 1,
|
||||
};
|
||||
}) || [],
|
||||
);
|
||||
|
||||
const POST = await BunSQLite.insert<BUN_SQLITE_WGUI_USERS, TableType>({
|
||||
table: "users",
|
||||
data: final_insert_data,
|
||||
});
|
||||
|
||||
return POST;
|
||||
const new_users_res = await grabUsers({
|
||||
query: {
|
||||
sql_query: {
|
||||
limit: 1,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!new_users_res.singleRes?.id) {
|
||||
throw new Error(
|
||||
`Couldn't find newly created user record! Try again.`,
|
||||
);
|
||||
}
|
||||
|
||||
const add_super_admin_user_types = await BunSQLite.insert<
|
||||
BUN_SQLITE_WGUI_USER_TYPES,
|
||||
TableType
|
||||
>({
|
||||
table: "user_types",
|
||||
data: [
|
||||
{
|
||||
user_type: "super_admin",
|
||||
user_id: new_users_res.singleRes.id,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
console.log("add_super_admin_user_types", add_super_admin_user_types);
|
||||
|
||||
return {
|
||||
...POST,
|
||||
singleRes: new_users_res.singleRes,
|
||||
};
|
||||
} catch (error: any) {
|
||||
return {
|
||||
success: false,
|
||||
|
||||
Reference in New Issue
Block a user