UNPKG

@cocalc/server

Version:

CoCalc server functionality: functions used by either the hub and the next.js server

76 lines (68 loc) 2.73 kB
import basePath from "@cocalc/backend/base-path"; import { v4 } from "uuid"; import passwordHash from "@cocalc/backend/auth/password-hash"; import getPool from "@cocalc/database/pool"; import { expireTime } from "@cocalc/database/pool/util"; import Cookies from "cookies"; import type { Request } from "express"; import generateHash from "@cocalc/server/auth/hash"; export const COOKIE_NAME = `${ basePath.length <= 1 ? "" : encodeURIComponent(basePath) }remember_me`; // Create a remember me cookie for the given account_id and store // it in the database. The cookie is similar to using a server // assigned random uuid-v4 as a password. The user knows the // uuid-v4, and we only store what it hashes to, so even if // somebody gets our database, they can't make fake cookies and use // them to sign in. export async function createRememberMeCookie( account_id: string, arg_ttl_s?: number ): Promise<{ // the value of the cookie, which encodes // a random uuid-v4 and the hash algorithm value: string; // time to live of the cookie in seconds, after which the // database considers it invalid; cookie should have same age ttl_s: number; }> { // compute the value and ttl_s: const session_id: string = v4(); const hash_session_id: string = passwordHash(session_id); const x: string[] = hash_session_id.split("$"); const value = [x[0], x[1], x[2], session_id].join("$"); const ttl_s: number = arg_ttl_s ?? 24 * 3600 * 30; // 30 days -- seems to work well, but this could be per user configurable, etc. // store the cookie in the database const pool = getPool(); await pool.query( "INSERT INTO remember_me (hash, expire, account_id) VALUES($1::TEXT, $2::TIMESTAMP, $3::UUID)", [hash_session_id.slice(0, 127), expireTime(ttl_s), account_id] ); return { value, ttl_s }; } // delete the remember me database entry for the given hash export async function deleteRememberMe(hash: string): Promise<void> { const pool = getPool(); await pool.query("DELETE FROM remember_me WHERE hash=$1::TEXT", [ hash.slice(0, 127), ]); } // delete all remember me cookies for the account export async function deleteAllRememberMe(account_id: string): Promise<void> { const pool = getPool(); await pool.query("DELETE FROM remember_me WHERE account_id=$1::UUID", [ account_id, ]); } export function getRememberMeHash(req: Request): string | undefined { const cookies = new Cookies(req); const rememberMe = cookies.get(COOKIE_NAME); if (!rememberMe) { return; } const x: string[] = rememberMe.split("$"); if (x.length !== 4) { throw Error("badly formatted remember_me cookie"); } return generateHash(x[0], x[1], parseInt(x[2]), x[3]).slice(0, 127); }