import { getRepository, Repository } from "typeorm"; import { ApiLogin } from "./entities/ApiLogin"; import { BaseRepository } from "./BaseRepository"; import crypto from "crypto"; import moment from "moment-timezone"; // tslint:disable-next-line:no-submodule-imports import uuidv4 from "uuid/v4"; import { DBDateFormat } from "../utils"; export class ApiLogins extends BaseRepository { private apiLogins: Repository; constructor() { super(); this.apiLogins = getRepository(ApiLogin); } async getUserIdByApiKey(apiKey: string): Promise { const [loginId, token] = apiKey.split("."); if (!loginId || !token) { return null; } const login = await this.apiLogins .createQueryBuilder() .where("id = :id", { id: loginId }) .andWhere("expires_at > NOW()") .getOne(); if (!login) { return null; } const hash = crypto.createHash("sha256"); hash.update(loginId + token); // Remember to use loginId as the salt const hashedToken = hash.digest("hex"); if (hashedToken !== login.token) { return null; } return login.user_id; } async addLogin(userId: string): Promise { // Generate random login id let loginId; while (true) { loginId = uuidv4(); const existing = await this.apiLogins.findOne({ where: { id: loginId, }, }); if (!existing) break; } // Generate token const token = uuidv4(); const hash = crypto.createHash("sha256"); hash.update(loginId + token); // Use loginId as a salt const hashedToken = hash.digest("hex"); // Save this to the DB await this.apiLogins.insert({ id: loginId, token: hashedToken, user_id: userId, logged_in_at: moment().format(DBDateFormat), expires_at: moment() .add(1, "day") .format(DBDateFormat), }); return `${loginId}.${token}`; } expireApiKey(apiKey) { const [loginId, token] = apiKey.split("."); if (!loginId || !token) return; return this.apiLogins.update( { id: loginId }, { expires_at: moment().format(DBDateFormat), }, ); } }