| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140 |
- /**
- NOTE: This is not a database table, this is a model of a Token object that can be created/loaded
- and then has abilities after that.
- */
- import crypto from "node:crypto";
- import jwt from "jsonwebtoken";
- import _ from "lodash";
- import { getPrivateKey, getPublicKey } from "../lib/config.js";
- import errs from "../lib/error.js";
- import { global as logger } from "../logger.js";
- const ALGO = "RS256";
- export default () => {
- let tokenData = {};
- const self = {
- /**
- * @param {Object} payload
- * @returns {Promise}
- */
- create: (payload) => {
- if (!getPrivateKey()) {
- logger.error("Private key is empty!");
- }
- // sign with RSA SHA256
- const options = {
- algorithm: ALGO,
- expiresIn: payload.expiresIn || "1d",
- };
- payload.jti = crypto.randomBytes(12).toString("base64").substring(-8);
- return new Promise((resolve, reject) => {
- jwt.sign(payload, getPrivateKey(), options, (err, token) => {
- if (err) {
- reject(err);
- } else {
- tokenData = payload;
- resolve({
- token: token,
- payload: payload,
- });
- }
- });
- });
- },
- /**
- * @param {String} token
- * @returns {Promise}
- */
- load: (token) => {
- if (!getPublicKey()) {
- logger.error("Public key is empty!");
- }
- return new Promise((resolve, reject) => {
- try {
- if (!token || token === null || token === "null") {
- reject(new errs.AuthError("Empty token"));
- } else {
- jwt.verify(
- token,
- getPublicKey(),
- { ignoreExpiration: false, algorithms: [ALGO] },
- (err, result) => {
- if (err) {
- if (err.name === "TokenExpiredError") {
- reject(new errs.AuthError("Token has expired", err));
- } else {
- reject(err);
- }
- } else {
- tokenData = result;
- // Hack: some tokens out in the wild have a scope of 'all' instead of 'user'.
- // For 30 days at least, we need to replace 'all' with user.
- if (
- typeof tokenData.scope !== "undefined" &&
- _.indexOf(tokenData.scope, "all") !== -1
- ) {
- tokenData.scope = ["user"];
- }
- resolve(tokenData);
- }
- },
- );
- }
- } catch (err) {
- reject(err);
- }
- });
- },
- /**
- * Does the token have the specified scope?
- *
- * @param {String} scope
- * @returns {Boolean}
- */
- hasScope: (scope) => typeof tokenData.scope !== "undefined" && _.indexOf(tokenData.scope, scope) !== -1,
- /**
- * @param {String} key
- * @return {*}
- */
- get: (key) => {
- if (typeof tokenData[key] !== "undefined") {
- return tokenData[key];
- }
- return null;
- },
- /**
- * @param {String} key
- * @param {*} value
- */
- set: (key, value) => {
- tokenData[key] = value;
- },
- /**
- * @param [defaultValue]
- * @returns {Integer}
- */
- getUserId: (defaultValue) => {
- const attrs = self.get("attrs");
- if (attrs?.id) {
- return attrs.id;
- }
- return defaultValue || 0;
- },
- };
- return self;
- };
|