All files / services/user token.service.ts

89.13% Statements 41/46
58.33% Branches 7/12
100% Functions 7/7
89.13% Lines 41/46

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 1231x   1x 1x 1x 1x                     1x 1x 1x 1x   1x   1x 1x 1x       2x 2x             2x 1x       1x             1x 1x 1x 1x                   1x       2x 2x             2x         2x 1x     1x 1x 1x 1x               1x 1x 1x       1x 1x                   1x 1x       2x     1x      
import "../../../configEnv";
import { ITokenService } from "@schemas/services/token/token.v1.service";
import Knex from "knex";
import knexfile from "@services/user/knexfile";
import { loadModels } from "@lib/objection-utils/objection-utils.lib";
import path from "path";
import {
  AuthReq,
  AuthRsp,
  IssueReq,
  IssueRsp,
  RefreshReq,
  RefreshRsp,
  RevokeReq,
  RevokeRsp,
} from "@schemas/services/token/token.v1.schemas";
import UserToken from "@services/user/models/UserToken.model";
import { uniqueString } from "@lib/encryption/encryption.lib";
import { UserTokenExpired, UserTokenNotFound } from "@services/user/errors/token.errors";
import _ from "lodash";
 
export default class TokenService implements ITokenService {
  async init(): Promise<void> {
    const masterKnex = Knex(knexfile.production);
    const slaveKnex = Knex(knexfile.slave);
    loadModels(masterKnex, slaveKnex, path.join(__dirname, "models"));
  }
 
  async Auth(ctx, req: AuthReq): Promise<AuthRsp> {
    const { accessToken } = req;
    const token = await UserToken.query()
      .where({
        accessToken,
      })
      .select(["id", "userId"])
      .limit(1)
      .first();
    if (!token) {
      throw new UserTokenNotFound({
        accessToken,
      });
    }
    return {
      id: token.id,
      userId: token.userId,
    };
  }
 
  async Issue(ctx, req: IssueReq): Promise<IssueRsp> {
    const { userId, type, scope, appId, meta } = req;
    const accessToken = await this.newAccessToken();
    const refreshToken = await this.newRefreshToken();
    await UserToken.queryOnMaster().insert({
      userId,
      type,
      accessToken,
      refreshToken,
      scope: JSON.stringify(scope),
      appId,
      meta: JSON.stringify(meta),
      expiresAt: new Date(Date.now() + 3600000),
    });
    return { accessToken, refreshToken, expiresIn: 3600 };
  }
 
  async Refresh(ctx, req: RefreshReq): Promise<RefreshRsp> {
    const { refreshToken } = req;
    const token = await UserToken.query()
      .where({
        refreshToken,
      })
      .select("id", "expiresAt")
      .limit(1)
      .first();
    Iif (!token) {
      throw new UserTokenNotFound({
        refreshToken,
      });
    }
    if (Date.now() > token.expiresAt.getTime()) {
      throw new UserTokenExpired(token.expiresAt);
    }
 
    token.accessToken = await this.newAccessToken();
    token.expiresAt = new Date(Date.now() + 3600000);
    await token.$queryOnMaster().patch(_.pick(token, ["accessToken", "expiresAt"]));
    return {
      accessToken: token.accessToken,
      expiresIn: 3600,
    };
  }
 
  async Revoke(ctx, req: RevokeReq): Promise<RevokeRsp> {
    // @ts-ignore
    const { id, refreshToken, accessToken } = req;
    const query = UserToken.queryOnMaster();
    Iif (id) {
      query.where({
        id,
      });
    } else Eif (refreshToken) {
      query.where({
        refreshToken,
      });
    } else if (accessToken) {
      query.where({
        accessToken,
      });
    } else {
      throw new Error("bad request");
    }
    await query.delete();
    return {};
  }
 
  async newAccessToken(): Promise<string> {
    return uniqueString(32);
  }
  async newRefreshToken(): Promise<string> {
    return uniqueString(64);
  }
}