mirror of
https://github.com/Mintplex-Labs/anything-llm.git
synced 2024-11-19 20:50:09 +01:00
105 lines
3.5 KiB
JavaScript
105 lines
3.5 KiB
JavaScript
|
const { makeJWT } = require("../utils/http");
|
||
|
const prisma = require("../utils/prisma");
|
||
|
|
||
|
/**
|
||
|
* Temporary auth tokens are used for simple SSO.
|
||
|
* They simply enable the ability for a time-based token to be used in the query of the /sso/login URL
|
||
|
* to login as a user without the need of a username and password. These tokens are single-use and expire.
|
||
|
*/
|
||
|
const TemporaryAuthToken = {
|
||
|
expiry: 1000 * 60 * 6, // 1 hour
|
||
|
tablename: "temporary_auth_tokens",
|
||
|
writable: [],
|
||
|
|
||
|
makeTempToken: () => {
|
||
|
const uuidAPIKey = require("uuid-apikey");
|
||
|
return `allm-tat-${uuidAPIKey.create().apiKey}`;
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Issues a temporary auth token for a user via its ID.
|
||
|
* @param {number} userId
|
||
|
* @returns {Promise<{token: string|null, error: string | null}>}
|
||
|
*/
|
||
|
issue: async function (userId = null) {
|
||
|
if (!userId)
|
||
|
throw new Error("User ID is required to issue a temporary auth token.");
|
||
|
await this.invalidateUserTokens(userId);
|
||
|
|
||
|
try {
|
||
|
const token = this.makeTempToken();
|
||
|
const expiresAt = new Date(Date.now() + this.expiry);
|
||
|
await prisma.temporary_auth_tokens.create({
|
||
|
data: {
|
||
|
token,
|
||
|
expiresAt,
|
||
|
userId: Number(userId),
|
||
|
},
|
||
|
});
|
||
|
|
||
|
return { token, error: null };
|
||
|
} catch (error) {
|
||
|
console.error("FAILED TO CREATE TEMPORARY AUTH TOKEN.", error.message);
|
||
|
return { token: null, error: error.message };
|
||
|
}
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Invalidates (deletes) all temporary auth tokens for a user via their ID.
|
||
|
* @param {number} userId
|
||
|
* @returns {Promise<boolean>}
|
||
|
*/
|
||
|
invalidateUserTokens: async function (userId) {
|
||
|
if (!userId)
|
||
|
throw new Error(
|
||
|
"User ID is required to invalidate temporary auth tokens."
|
||
|
);
|
||
|
await prisma.temporary_auth_tokens.deleteMany({
|
||
|
where: { userId: Number(userId) },
|
||
|
});
|
||
|
return true;
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Validates a temporary auth token and returns the session token
|
||
|
* to be set in the browser localStorage for authentication.
|
||
|
* @param {string} publicToken - the token to validate against
|
||
|
* @returns {Promise<{sessionToken: string|null, token: import("@prisma/client").temporary_auth_tokens & {user: import("@prisma/client").users} | null, error: string | null}>}
|
||
|
*/
|
||
|
validate: async function (publicToken = "") {
|
||
|
/** @type {import("@prisma/client").temporary_auth_tokens & {user: import("@prisma/client").users} | undefined | null} **/
|
||
|
let token;
|
||
|
|
||
|
try {
|
||
|
if (!publicToken)
|
||
|
throw new Error(
|
||
|
"Public token is required to validate a temporary auth token."
|
||
|
);
|
||
|
token = await prisma.temporary_auth_tokens.findUnique({
|
||
|
where: { token: String(publicToken) },
|
||
|
include: { user: true },
|
||
|
});
|
||
|
if (!token) throw new Error("Invalid token.");
|
||
|
if (token.expiresAt < new Date()) throw new Error("Token expired.");
|
||
|
if (token.user.suspended) throw new Error("User account suspended.");
|
||
|
|
||
|
// Create a new session token for the user valid for 30 days
|
||
|
const sessionToken = makeJWT(
|
||
|
{ id: token.user.id, username: token.user.username },
|
||
|
"30d"
|
||
|
);
|
||
|
|
||
|
return { sessionToken, token, error: null };
|
||
|
} catch (error) {
|
||
|
console.error("FAILED TO VALIDATE TEMPORARY AUTH TOKEN.", error.message);
|
||
|
return { sessionToken: null, token: null, error: error.message };
|
||
|
} finally {
|
||
|
// Delete the token after it has been used under all circumstances if it was retrieved
|
||
|
if (token)
|
||
|
await prisma.temporary_auth_tokens.delete({ where: { id: token.id } });
|
||
|
}
|
||
|
},
|
||
|
};
|
||
|
|
||
|
module.exports = { TemporaryAuthToken };
|