datasquirel/users/user-auth.js

210 lines
6.6 KiB
JavaScript
Raw Normal View History

2023-09-21 14:00:04 +00:00
// @ts-check
const http = require("http");
2024-12-06 10:31:24 +00:00
const decrypt = require("../package-shared/functions/dsql/decrypt");
2024-12-06 11:55:03 +00:00
const getAuthCookieNames = require("../package-shared/functions/backend/cookies/get-auth-cookie-names");
2024-12-08 08:58:57 +00:00
const {
checkAuthFile,
} = require("../package-shared/functions/backend/auth/write-auth-files");
2024-12-10 14:20:48 +00:00
const parseCookies = require("../package-shared/utils/backend/parseCookies");
2024-12-08 08:58:57 +00:00
const minuteInMilliseconds = 60000;
const hourInMilliseconds = minuteInMilliseconds * 60;
const dayInMilliseconds = hourInMilliseconds * 24;
const weekInMilliseconds = dayInMilliseconds * 7;
const monthInMilliseconds = dayInMilliseconds * 30;
const yearInMilliseconds = dayInMilliseconds * 365;
2023-09-21 14:00:04 +00:00
/**
* Authenticate User from request
* ==============================================================================
* @description This Function takes in a request object and returns a user object
* with the user's data
*
* @param {Object} params - Arg
2024-12-08 08:58:57 +00:00
* @param {http.IncomingMessage & Object<string, any>} [params.request] - Http request object
* @param {http.IncomingMessage & Object<string, any>} [params.req] - Http request object
2024-12-13 13:08:41 +00:00
* @param {string} [params.cookieString]
2024-12-08 08:58:57 +00:00
* @param {string} [params.encryptedUserString] - Encrypted user string to use instead of getting from cookie header
* @param {string} [params.encryptionKey] - Encryption Key: alt env: DSQL_ENCRYPTION_PASSWORD
* @param {string} [params.encryptionSalt] - Encryption Salt: alt env: DSQL_ENCRYPTION_SALT
2023-09-21 14:00:04 +00:00
* @param {("deep" | "normal")} [params.level] - Optional. "Deep" value indicates an extra layer of security
2024-12-08 08:58:57 +00:00
* @param {string} [params.database] - Database Name (slug)
* @param {string | number} [params.dsqlUserId] - alt env: DSQL_API_USER_ID
* @param {number} [params.expiry] - Expiry time in milliseconds
2024-12-14 15:23:16 +00:00
* @param {string} [params.csrfHeaderName] - Optional. CSRF Header Name
* @param {boolean} [params.csrfHeaderIsValue] - If the csrf value is the name of the request http header
2023-09-21 14:00:04 +00:00
*
2024-10-19 16:45:42 +00:00
* @returns { import("../package-shared/types").AuthenticatedUser }
2023-09-21 14:00:04 +00:00
*/
2024-08-16 06:48:12 +00:00
function userAuth({
request,
2024-12-08 08:58:57 +00:00
req,
2024-08-16 06:48:12 +00:00
encryptionKey,
encryptionSalt,
level,
database,
2024-12-08 08:58:57 +00:00
dsqlUserId,
encryptedUserString,
expiry = weekInMilliseconds,
2024-12-13 13:08:41 +00:00
cookieString,
2024-12-14 15:23:16 +00:00
csrfHeaderIsValue,
csrfHeaderName,
2024-08-16 06:48:12 +00:00
}) {
2023-09-21 14:00:04 +00:00
try {
2024-12-14 15:59:41 +00:00
const finalRequest = req || request;
2024-12-08 08:58:57 +00:00
const finalEncryptionKey =
encryptionKey || process.env.DSQL_ENCRYPTION_PASSWORD;
const finalEncryptionSalt =
encryptionSalt || process.env.DSQL_ENCRYPTION_SALT;
2024-12-06 11:55:03 +00:00
2024-12-13 13:08:41 +00:00
const cookies = parseCookies({
2024-12-14 15:59:41 +00:00
request: finalRequest,
2024-12-13 13:08:41 +00:00
cookieString,
});
2024-12-08 08:58:57 +00:00
const keyNames = getAuthCookieNames({
userId: dsqlUserId || process.env.DSQL_API_USER_ID,
database: database || process.env.DSQL_DB_NAME,
});
2024-12-06 11:55:03 +00:00
const authKeyName = keyNames.keyCookieName;
const csrfName = keyNames.csrfCookieName;
2023-09-21 14:00:04 +00:00
2024-12-08 08:58:57 +00:00
const key = encryptedUserString
? encryptedUserString
: cookies[authKeyName];
2023-09-21 14:00:04 +00:00
const csrf = cookies[csrfName];
/**
* Grab the payload
*
* @description Grab the payload
*/
2024-12-08 08:58:57 +00:00
let userPayloadJSON = decrypt({
2023-09-21 14:00:04 +00:00
encryptedString: key,
2024-12-08 08:58:57 +00:00
encryptionKey: finalEncryptionKey,
encryptionSalt: finalEncryptionSalt,
2023-09-21 14:00:04 +00:00
});
/**
* Grab the payload
*
* @description Grab the payload
*/
2024-12-08 08:58:57 +00:00
if (!userPayloadJSON) {
2023-09-21 14:00:04 +00:00
return {
success: false,
payload: null,
msg: "Couldn't Decrypt cookie",
};
}
/**
* Grab the payload
*
* @description Grab the payload
*/
2024-12-08 08:58:57 +00:00
/** @type {import("../package-shared/types").DATASQUIREL_LoggedInUser} */
let userObject = JSON.parse(userPayloadJSON);
2023-09-21 14:00:04 +00:00
if (!userObject.csrf_k) {
return {
success: false,
payload: null,
msg: "No CSRF_K in decrypted payload",
};
}
2024-12-08 08:58:57 +00:00
if (!checkAuthFile(userObject.csrf_k)) {
return {
success: false,
payload: null,
msg: "Auth file doesn't exist",
};
}
2023-09-21 14:00:04 +00:00
/**
* Grab the payload
*
* @description Grab the payload
*/
2024-12-14 15:59:41 +00:00
if (level?.match(/deep/i) && finalRequest) {
if (
csrfHeaderName &&
finalRequest.headers[csrfHeaderName] !== userObject.csrf_k
) {
return {
success: false,
payload: null,
msg: "CSRF_K mismatch",
};
}
const targetCsrfHeaderKey = Object.keys(finalRequest.headers)
2024-12-14 16:20:25 +00:00
.map((k) => k.replace(/[^a-zA-Z0-9\-]/g, ""))
2024-12-14 15:59:41 +00:00
.find((k) => k == userObject.csrf_k);
if (csrfHeaderIsValue && !targetCsrfHeaderKey) {
return {
success: false,
payload: null,
msg: "CSRF_K Header Key mismatch",
};
}
2023-09-21 14:00:04 +00:00
}
2024-12-08 08:58:57 +00:00
const payloadCreationDate = Number(userObject.date);
if (
Number.isNaN(payloadCreationDate) ||
typeof payloadCreationDate !== "number"
) {
return {
success: false,
payload: null,
msg: "Payload Creation Date is not a number",
};
}
const timeElapsed = Date.now() - payloadCreationDate;
const finalExpiry = process.env.DSQL_SESSION_EXPIRY_TIME
? Number(process.env.DSQL_SESSION_EXPIRY_TIME)
: expiry;
if (timeElapsed > finalExpiry) {
return {
success: false,
payload: null,
msg: "Session has expired",
};
}
2023-09-21 14:00:04 +00:00
/**
* Return User Object
*
* @description Return User Object
*/
return {
success: true,
payload: userObject,
};
2024-10-11 09:15:29 +00:00
} catch (/** @type {any} */ error) {
2023-09-21 14:00:04 +00:00
/**
* Return User Object
*
* @description Return User Object
*/
return {
success: false,
payload: null,
2024-10-11 09:15:29 +00:00
msg: error.message,
2023-09-21 14:00:04 +00:00
};
}
}
module.exports = userAuth;