@lyxa.ai/core 1.0.10 → 1.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,14 +1,77 @@
1
1
  import { SignOptions } from "jsonwebtoken";
2
2
  import { EntityContext } from "../trpc/context";
3
+ import { RefreshToken } from "./refreshtoken";
3
4
  export declare enum AuthEntityType {
4
5
  USER = "USER",
5
6
  SELLER = "SELLER",
6
7
  DELIVERY_BOY = "DELIVERY_BOY",
7
8
  CRM = "CRM"
8
9
  }
9
- export declare function signToken(entity: {
10
+ export declare enum TokenType {
11
+ ACCESS = "ACCESS",
12
+ REFRESH = "REFRESH"
13
+ }
14
+ export interface RefreshTokenRecord {
15
+ token: string;
16
+ entityId: string;
17
+ entityType: AuthEntityType;
18
+ expiresAt: Date;
19
+ isRevoked: boolean;
20
+ metadata?: Record<string, unknown>;
21
+ }
22
+ export declare function signAccessToken(entity: {
10
23
  id: string;
11
24
  type: AuthEntityType;
12
25
  parameters: Record<string, unknown>;
13
26
  }, options?: SignOptions): Promise<string>;
14
- export declare function verifyToken(entityTypes: AuthEntityType[], token: string): Promise<EntityContext | undefined>;
27
+ export declare function generateRefreshToken(entity: {
28
+ id: string;
29
+ type: AuthEntityType;
30
+ parameters?: Record<string, unknown>;
31
+ }, options?: {
32
+ expiresIn?: string | number;
33
+ }): Promise<string>;
34
+ export declare function verifyAccessToken(entityTypes: AuthEntityType[], token: string): Promise<EntityContext | undefined>;
35
+ export declare function validateRefreshToken(token: string): Promise<{
36
+ valid: boolean;
37
+ entity?: {
38
+ id: string;
39
+ type: AuthEntityType;
40
+ parameters?: Record<string, unknown>;
41
+ };
42
+ }>;
43
+ export declare function refreshTokens(refreshToken: string): Promise<{
44
+ accessToken: string;
45
+ refreshToken: string;
46
+ entity: {
47
+ id: string;
48
+ type: AuthEntityType;
49
+ };
50
+ } | null>;
51
+ export declare function revokeRefreshToken(token: string): Promise<boolean>;
52
+ export declare function revokeAllRefreshTokens(entityId: string, entityType: AuthEntityType): Promise<boolean>;
53
+ export declare function signTokenPair(entity: {
54
+ id: string;
55
+ type: AuthEntityType;
56
+ parameters?: Record<string, unknown>;
57
+ }, options?: {
58
+ accessTokenOptions?: SignOptions;
59
+ refreshTokenExpiresIn?: string | number;
60
+ }): Promise<{
61
+ accessToken: string;
62
+ refreshToken: string;
63
+ }>;
64
+ export declare function handleTokenRenewal(refreshToken: string): Promise<{
65
+ success: boolean;
66
+ entity?: {
67
+ id: string;
68
+ type: AuthEntityType;
69
+ parameters: Record<string, unknown>;
70
+ };
71
+ tokenStatus: 'valid' | 'expired' | 'revoked' | 'invalid';
72
+ newTokens?: {
73
+ accessToken: string;
74
+ refreshToken?: string | null;
75
+ };
76
+ }>;
77
+ export declare const RefreshTokenModel: import("@typegoose/typegoose").ReturnModelType<typeof RefreshToken>;
@@ -3,11 +3,21 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.AuthEntityType = void 0;
7
- exports.signToken = signToken;
8
- exports.verifyToken = verifyToken;
6
+ exports.RefreshTokenModel = exports.TokenType = exports.AuthEntityType = void 0;
7
+ exports.signAccessToken = signAccessToken;
8
+ exports.generateRefreshToken = generateRefreshToken;
9
+ exports.verifyAccessToken = verifyAccessToken;
10
+ exports.validateRefreshToken = validateRefreshToken;
11
+ exports.refreshTokens = refreshTokens;
12
+ exports.revokeRefreshToken = revokeRefreshToken;
13
+ exports.revokeAllRefreshTokens = revokeAllRefreshTokens;
14
+ exports.signTokenPair = signTokenPair;
15
+ exports.handleTokenRenewal = handleTokenRenewal;
9
16
  const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
17
+ const crypto_1 = require("crypto");
18
+ const refreshtoken_1 = require("./refreshtoken");
10
19
  const index_1 = require("../../index");
20
+ const mongo_1 = require("../mongo");
11
21
  var AuthEntityType;
12
22
  (function (AuthEntityType) {
13
23
  AuthEntityType["USER"] = "USER";
@@ -15,7 +25,12 @@ var AuthEntityType;
15
25
  AuthEntityType["DELIVERY_BOY"] = "DELIVERY_BOY";
16
26
  AuthEntityType["CRM"] = "CRM";
17
27
  })(AuthEntityType || (exports.AuthEntityType = AuthEntityType = {}));
18
- async function signToken(entity, options) {
28
+ var TokenType;
29
+ (function (TokenType) {
30
+ TokenType["ACCESS"] = "ACCESS";
31
+ TokenType["REFRESH"] = "REFRESH";
32
+ })(TokenType || (exports.TokenType = TokenType = {}));
33
+ async function signAccessToken(entity, options) {
19
34
  if (!entity?.type) {
20
35
  throw new Error('Entity type is required to sign token');
21
36
  }
@@ -29,17 +44,204 @@ async function signToken(entity, options) {
29
44
  id: entity.id,
30
45
  type: entity.type,
31
46
  parameters: entity.parameters,
47
+ tokenType: TokenType.ACCESS
32
48
  };
33
- return jsonwebtoken_1.default.sign(payload, secret, options);
49
+ const tokenOptions = {
50
+ ...options,
51
+ expiresIn: options?.expiresIn || '1d'
52
+ };
53
+ return jsonwebtoken_1.default.sign(payload, secret, tokenOptions);
34
54
  }
35
- async function verifyToken(entityTypes, token) {
55
+ async function generateRefreshToken(entity, options) {
56
+ const tokenValue = (0, crypto_1.randomBytes)(40).toString('hex');
57
+ const expiresIn = options?.expiresIn || '7d';
58
+ const expiresAt = new Date();
59
+ if (typeof expiresIn === 'string') {
60
+ const match = expiresIn.match(/^(\d+)([smhdwy])$/);
61
+ if (match) {
62
+ const value = parseInt(match[1]);
63
+ const unit = match[2];
64
+ switch (unit) {
65
+ case 's':
66
+ expiresAt.setSeconds(expiresAt.getSeconds() + value);
67
+ break;
68
+ case 'm':
69
+ expiresAt.setMinutes(expiresAt.getMinutes() + value);
70
+ break;
71
+ case 'h':
72
+ expiresAt.setHours(expiresAt.getHours() + value);
73
+ break;
74
+ case 'd':
75
+ expiresAt.setDate(expiresAt.getDate() + value);
76
+ break;
77
+ case 'w':
78
+ expiresAt.setDate(expiresAt.getDate() + (value * 7));
79
+ break;
80
+ case 'y':
81
+ expiresAt.setFullYear(expiresAt.getFullYear() + value);
82
+ break;
83
+ }
84
+ }
85
+ else {
86
+ expiresAt.setDate(expiresAt.getDate() + 7);
87
+ }
88
+ }
89
+ else if (typeof expiresIn === 'number') {
90
+ expiresAt.setSeconds(expiresAt.getSeconds() + expiresIn);
91
+ }
92
+ await storeRefreshToken({
93
+ token: tokenValue,
94
+ entityId: entity.id,
95
+ entityType: entity.type,
96
+ expiresAt,
97
+ isRevoked: false,
98
+ metadata: entity.parameters
99
+ });
100
+ return tokenValue;
101
+ }
102
+ async function storeRefreshToken(tokenRecord) {
103
+ const tokenRepository = (0, index_1.getLibraries)().getAuthTokenRepository();
104
+ await tokenRepository.saveRefreshToken(tokenRecord);
105
+ }
106
+ async function verifyAccessToken(entityTypes, token) {
36
107
  for (const entityType of entityTypes) {
37
108
  const secret = await (0, index_1.getLibraries)().getSecretsService().getKey(entityType.toUpperCase() + "_JWT_TOKEN");
38
109
  try {
39
- return jsonwebtoken_1.default.verify(token, secret);
110
+ const decoded = jsonwebtoken_1.default.verify(token, secret);
111
+ if (decoded.tokenType !== TokenType.ACCESS) {
112
+ continue;
113
+ }
114
+ return decoded;
40
115
  }
41
116
  catch (err) {
42
117
  }
43
118
  }
119
+ return undefined;
120
+ }
121
+ async function validateRefreshToken(token) {
122
+ const tokenRepository = (0, index_1.getLibraries)().getAuthTokenRepository();
123
+ const storedToken = await tokenRepository.findRefreshToken(token);
124
+ if (!storedToken) {
125
+ return { valid: false };
126
+ }
127
+ if (storedToken.expiresAt && new Date() > storedToken.expiresAt) {
128
+ return { valid: false };
129
+ }
130
+ if (storedToken.isRevoked) {
131
+ return { valid: false };
132
+ }
133
+ return {
134
+ valid: true,
135
+ entity: {
136
+ id: storedToken.entityId,
137
+ type: storedToken.entityType,
138
+ parameters: storedToken.metadata
139
+ }
140
+ };
141
+ }
142
+ async function refreshTokens(refreshToken) {
143
+ const validation = await validateRefreshToken(refreshToken);
144
+ if (!validation.valid || !validation.entity) {
145
+ return null;
146
+ }
147
+ await revokeRefreshToken(refreshToken);
148
+ const entity = validation.entity;
149
+ const newAccessToken = await signAccessToken({
150
+ id: entity.id,
151
+ type: entity.type,
152
+ parameters: entity.parameters || {}
153
+ });
154
+ const newRefreshToken = await generateRefreshToken({
155
+ id: entity.id,
156
+ type: entity.type,
157
+ parameters: entity.parameters
158
+ });
159
+ return {
160
+ accessToken: newAccessToken,
161
+ refreshToken: newRefreshToken,
162
+ entity: {
163
+ id: entity.id,
164
+ type: entity.type
165
+ }
166
+ };
167
+ }
168
+ async function revokeRefreshToken(token) {
169
+ const tokenRepository = (0, index_1.getLibraries)().getAuthTokenRepository();
170
+ return tokenRepository.revokeRefreshToken(token);
171
+ }
172
+ async function revokeAllRefreshTokens(entityId, entityType) {
173
+ const tokenRepository = (0, index_1.getLibraries)().getAuthTokenRepository();
174
+ return tokenRepository.revokeAllRefreshTokensForEntity(entityId, entityType);
175
+ }
176
+ async function signTokenPair(entity, options) {
177
+ const accessToken = await signAccessToken({
178
+ id: entity.id,
179
+ type: entity.type,
180
+ parameters: entity.parameters || {}
181
+ }, options?.accessTokenOptions);
182
+ const refreshToken = await generateRefreshToken({
183
+ id: entity.id,
184
+ type: entity.type,
185
+ parameters: entity.parameters
186
+ }, { expiresIn: options?.refreshTokenExpiresIn });
187
+ return {
188
+ accessToken,
189
+ refreshToken
190
+ };
191
+ }
192
+ async function handleTokenRenewal(refreshToken) {
193
+ const tokenRepository = (0, index_1.getLibraries)().getAuthTokenRepository();
194
+ const storedToken = await tokenRepository.findRefreshToken(refreshToken);
195
+ if (!storedToken) {
196
+ return {
197
+ success: false,
198
+ tokenStatus: 'invalid'
199
+ };
200
+ }
201
+ const isRevoked = storedToken.isRevoked;
202
+ if (isRevoked) {
203
+ return {
204
+ success: false,
205
+ tokenStatus: 'revoked'
206
+ };
207
+ }
208
+ const isExpired = storedToken.isExpired();
209
+ const entityData = {
210
+ id: storedToken.entityId,
211
+ type: storedToken.entityType,
212
+ parameters: storedToken.metadata || {}
213
+ };
214
+ if (isExpired) {
215
+ const newTokens = await signTokenPair({
216
+ id: storedToken.entityId,
217
+ type: storedToken.entityType,
218
+ parameters: storedToken.metadata || {}
219
+ });
220
+ await tokenRepository.revokeRefreshToken(refreshToken);
221
+ return {
222
+ success: true,
223
+ entity: entityData,
224
+ tokenStatus: 'expired',
225
+ newTokens: {
226
+ accessToken: newTokens.accessToken,
227
+ refreshToken: newTokens.refreshToken
228
+ }
229
+ };
230
+ }
231
+ const newAccessToken = await signAccessToken({
232
+ id: storedToken.entityId,
233
+ type: storedToken.entityType,
234
+ parameters: storedToken.metadata || {}
235
+ });
236
+ return {
237
+ success: true,
238
+ entity: entityData,
239
+ tokenStatus: 'valid',
240
+ newTokens: {
241
+ accessToken: newAccessToken,
242
+ refreshToken: null
243
+ }
244
+ };
44
245
  }
246
+ exports.RefreshTokenModel = (0, mongo_1.createGlobalModel)(refreshtoken_1.RefreshToken);
45
247
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"/","sources":["libraries/auth/index.ts"],"names":[],"mappings":";;;;;;AAeA,8BAuBC;AAED,kCASC;AAjDD,gEAA8C;AAC9C,uCAAyC;AAGzC,IAAY,cAKX;AALD,WAAY,cAAc;IACtB,+BAAa,CAAA;IACb,mCAAiB,CAAA;IACjB,+CAA6B,CAAA;IAC7B,6BAAW,CAAA;AACf,CAAC,EALW,cAAc,8BAAd,cAAc,QAKzB;AAMM,KAAK,UAAU,SAAS,CAC3B,MAAiF,EACjF,OAAqB;IAErB,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;IAC7D,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,IAAA,oBAAY,GAAE;SAC9B,iBAAiB,EAAE;SACnB,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;IAEtD,IAAI,CAAC,MAAM,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CAAC,mCAAmC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;IACtE,CAAC;IAED,MAAM,OAAO,GAAG;QACZ,EAAE,EAAE,MAAM,CAAC,EAAE;QACb,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,UAAU,EAAE,MAAM,CAAC,UAAU;KAChC,CAAC;IAEF,OAAO,sBAAG,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;AAC9C,CAAC;AAEM,KAAK,UAAU,WAAW,CAAC,WAA6B,EAAE,KAAa;IAC1E,KAAI,MAAM,UAAU,IAAI,WAAW,EAAE,CAAC;QAClC,MAAM,MAAM,GAAG,MAAM,IAAA,oBAAY,GAAE,CAAC,iBAAiB,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,WAAW,EAAE,GAAG,YAAY,CAAC,CAAC;QAExG,IAAI,CAAC;YACD,OAAO,sBAAG,CAAC,MAAM,CAAC,KAAK,EAAE,MAAO,CAAkB,CAAC;QACvD,CAAC;QAAC,OAAM,GAAG,EAAE,CAAC;QACd,CAAC;IACL,CAAC;AACL,CAAC","sourcesContent":["import jwt, {SignOptions} from \"jsonwebtoken\";\r\nimport {getLibraries} from \"../../index\";\r\nimport {EntityContext} from \"../trpc/context\";\r\n\r\nexport enum AuthEntityType {\r\n USER = \"USER\",\r\n SELLER = \"SELLER\",\r\n DELIVERY_BOY = \"DELIVERY_BOY\",\r\n CRM = \"CRM\"\r\n}\r\n\r\n/**\r\n * Sign a JWT for the given entity (id + type).\r\n * It will fetch the secret for that entity type and create a token.\r\n */\r\nexport async function signToken(\r\n entity: { id: string; type: AuthEntityType, parameters: Record<string, unknown> },\r\n options?: SignOptions\r\n): Promise<string> {\r\n if (!entity?.type) {\r\n throw new Error('Entity type is required to sign token');\r\n }\r\n\r\n const secret = await getLibraries()\r\n .getSecretsService()\r\n .getKey(`${entity.type.toUpperCase()}_JWT_TOKEN`);\r\n\r\n if (!secret) {\r\n throw new Error(`No secret found for entity type ${entity.type}`);\r\n }\r\n\r\n const payload = {\r\n id: entity.id,\r\n type: entity.type,\r\n parameters: entity.parameters,\r\n };\r\n\r\n return jwt.sign(payload, secret, options);\r\n}\r\n\r\nexport async function verifyToken(entityTypes: AuthEntityType[], token: string): Promise<EntityContext | undefined> {\r\n for(const entityType of entityTypes) {\r\n const secret = await getLibraries().getSecretsService().getKey(entityType.toUpperCase() + \"_JWT_TOKEN\");\r\n\r\n try {\r\n return jwt.verify(token, secret!) as EntityContext;\r\n } catch(err) {\r\n }\r\n }\r\n}\r\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"/","sources":["libraries/auth/index.ts"],"names":[],"mappings":"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","sourcesContent":["import jwt, { SignOptions, JwtPayload } from \"jsonwebtoken\";\r\nimport { EntityContext } from \"../trpc/context\";\r\nimport { randomBytes } from \"crypto\";\r\nimport {RefreshToken} from \"./refreshtoken\";\r\nimport {getLibraries} from \"../../index\";\r\nimport { createGlobalModel } from \"../mongo\";\r\n\r\nexport enum AuthEntityType {\r\n USER = \"USER\",\r\n SELLER = \"SELLER\",\r\n DELIVERY_BOY = \"DELIVERY_BOY\",\r\n CRM = \"CRM\"\r\n}\r\n\r\nexport enum TokenType {\r\n ACCESS = \"ACCESS\",\r\n REFRESH = \"REFRESH\"\r\n}\r\n\r\nexport interface RefreshTokenRecord {\r\n token: string;\r\n entityId: string;\r\n entityType: AuthEntityType;\r\n expiresAt: Date;\r\n isRevoked: boolean;\r\n metadata?: Record<string, unknown>;\r\n}\r\n\r\n/**\r\n * Sign a JWT access token for the given entity (id + type).\r\n * It will fetch the secret for that entity type and create a token.\r\n */\r\nexport async function signAccessToken(\r\n entity: { id: string; type: AuthEntityType, parameters: Record<string, unknown> },\r\n options?: SignOptions\r\n): Promise<string> {\r\n if (!entity?.type) {\r\n throw new Error('Entity type is required to sign token');\r\n }\r\n\r\n const secret = await getLibraries()\r\n .getSecretsService()\r\n .getKey(`${entity.type.toUpperCase()}_JWT_TOKEN`);\r\n\r\n if (!secret) {\r\n throw new Error(`No secret found for entity type ${entity.type}`);\r\n }\r\n\r\n const payload = {\r\n id: entity.id,\r\n type: entity.type,\r\n parameters: entity.parameters,\r\n tokenType: TokenType.ACCESS\r\n };\r\n\r\n const tokenOptions: SignOptions = {\r\n ...options,\r\n expiresIn: options?.expiresIn || '1d'\r\n };\r\n\r\n return jwt.sign(payload, secret, tokenOptions);\r\n}\r\n\r\n/**\r\n * Generate a refresh token for the given entity\r\n */\r\nexport async function generateRefreshToken(\r\n entity: { id: string; type: AuthEntityType, parameters?: Record<string, unknown> },\r\n options?: { expiresIn?: string | number }\r\n): Promise<string> {\r\n const tokenValue = randomBytes(40).toString('hex');\r\n\r\n const expiresIn = options?.expiresIn || '7d';\r\n const expiresAt = new Date();\r\n\r\n if (typeof expiresIn === 'string') {\r\n // Parse duration strings like '7d', '30d', etc.\r\n const match = expiresIn.match(/^(\\d+)([smhdwy])$/);\r\n if (match) {\r\n const value = parseInt(match[1]);\r\n const unit = match[2];\r\n\r\n switch (unit) {\r\n case 's': expiresAt.setSeconds(expiresAt.getSeconds() + value); break;\r\n case 'm': expiresAt.setMinutes(expiresAt.getMinutes() + value); break;\r\n case 'h': expiresAt.setHours(expiresAt.getHours() + value); break;\r\n case 'd': expiresAt.setDate(expiresAt.getDate() + value); break;\r\n case 'w': expiresAt.setDate(expiresAt.getDate() + (value * 7)); break;\r\n case 'y': expiresAt.setFullYear(expiresAt.getFullYear() + value); break;\r\n }\r\n } else {\r\n // Default to 7 days if format is invalid\r\n expiresAt.setDate(expiresAt.getDate() + 7);\r\n }\r\n } else if (typeof expiresIn === 'number') {\r\n // Interpret number as seconds\r\n expiresAt.setSeconds(expiresAt.getSeconds() + expiresIn);\r\n }\r\n\r\n await storeRefreshToken({\r\n token: tokenValue,\r\n entityId: entity.id,\r\n entityType: entity.type,\r\n expiresAt,\r\n isRevoked: false,\r\n metadata: entity.parameters\r\n });\r\n\r\n return tokenValue;\r\n}\r\n\r\n/**\r\n * Store a refresh token in the database\r\n */\r\nasync function storeRefreshToken(tokenRecord: RefreshTokenRecord): Promise<void> {\r\n const tokenRepository = getLibraries().getAuthTokenRepository();\r\n await tokenRepository.saveRefreshToken(tokenRecord);\r\n}\r\n\r\n/**\r\n * Verify an access token for the given entity types\r\n */\r\nexport async function verifyAccessToken(entityTypes: AuthEntityType[], token: string): Promise<EntityContext | undefined> {\r\n for(const entityType of entityTypes) {\r\n const secret = await getLibraries().getSecretsService().getKey(entityType.toUpperCase() + \"_JWT_TOKEN\");\r\n\r\n try {\r\n const decoded = jwt.verify(token, secret!) as EntityContext & { tokenType?: TokenType };\r\n\r\n // Verify this is an access token\r\n if (decoded.tokenType !== TokenType.ACCESS) {\r\n continue;\r\n }\r\n\r\n return decoded;\r\n } catch(err) {\r\n // Continue to the next entity type\r\n }\r\n }\r\n\r\n return undefined;\r\n}\r\n\r\n/**\r\n * Validate a refresh token and return the associated entity\r\n */\r\nexport async function validateRefreshToken(token: string): Promise<{\r\n valid: boolean;\r\n entity?: { id: string; type: AuthEntityType; parameters?: Record<string, unknown> };\r\n}> {\r\n const tokenRepository = getLibraries().getAuthTokenRepository();\r\n const storedToken = await tokenRepository.findRefreshToken(token);\r\n\r\n if (!storedToken) {\r\n return { valid: false };\r\n }\r\n\r\n if (storedToken.expiresAt && new Date() > storedToken.expiresAt) {\r\n return { valid: false };\r\n }\r\n\r\n if (storedToken.isRevoked) {\r\n return { valid: false };\r\n }\r\n\r\n return {\r\n valid: true,\r\n entity: {\r\n id: storedToken.entityId,\r\n type: storedToken.entityType,\r\n parameters: storedToken.metadata\r\n }\r\n };\r\n}\r\n\r\n/**\r\n * Refresh authentication tokens\r\n * Returns new access and refresh tokens\r\n */\r\nexport async function refreshTokens(refreshToken: string): Promise<{\r\n accessToken: string;\r\n refreshToken: string;\r\n entity: { id: string; type: AuthEntityType };\r\n} | null> {\r\n // Validate the refresh token\r\n const validation = await validateRefreshToken(refreshToken);\r\n\r\n if (!validation.valid || !validation.entity) {\r\n return null;\r\n }\r\n\r\n await revokeRefreshToken(refreshToken);\r\n\r\n // Create new tokens\r\n const entity = validation.entity;\r\n const newAccessToken = await signAccessToken({\r\n id: entity.id,\r\n type: entity.type,\r\n parameters: entity.parameters || {}\r\n });\r\n\r\n const newRefreshToken = await generateRefreshToken({\r\n id: entity.id,\r\n type: entity.type,\r\n parameters: entity.parameters\r\n });\r\n\r\n return {\r\n accessToken: newAccessToken,\r\n refreshToken: newRefreshToken,\r\n entity: {\r\n id: entity.id,\r\n type: entity.type\r\n }\r\n };\r\n}\r\n\r\n/**\r\n * Revoke a refresh token\r\n */\r\nexport async function revokeRefreshToken(token: string): Promise<boolean> {\r\n const tokenRepository = getLibraries().getAuthTokenRepository();\r\n return tokenRepository.revokeRefreshToken(token);\r\n}\r\n\r\n/**\r\n * Revoke all refresh tokens for a specific entity\r\n */\r\nexport async function revokeAllRefreshTokens(entityId: string, entityType: AuthEntityType): Promise<boolean> {\r\n const tokenRepository = getLibraries().getAuthTokenRepository();\r\n return tokenRepository.revokeAllRefreshTokensForEntity(entityId, entityType);\r\n}\r\n\r\n/**\r\n * Sign both access and refresh tokens - utility for initial login/authentication\r\n */\r\nexport async function signTokenPair(\r\n entity: { id: string; type: AuthEntityType; parameters?: Record<string, unknown> },\r\n options?: { accessTokenOptions?: SignOptions; refreshTokenExpiresIn?: string | number }\r\n): Promise<{\r\n accessToken: string;\r\n refreshToken: string;\r\n}> {\r\n const accessToken = await signAccessToken({\r\n id: entity.id,\r\n type: entity.type,\r\n parameters: entity.parameters || {}\r\n }, options?.accessTokenOptions);\r\n\r\n const refreshToken = await generateRefreshToken({\r\n id: entity.id,\r\n type: entity.type,\r\n parameters: entity.parameters\r\n }, { expiresIn: options?.refreshTokenExpiresIn });\r\n\r\n return {\r\n accessToken,\r\n refreshToken\r\n };\r\n}\r\n\r\n/**\r\n * Handle the refresh token validation and renewal logic\r\n * @param refreshToken The refresh token to validate or renew\r\n * @returns Result object with token status and renewal information\r\n */\r\nexport async function handleTokenRenewal(\r\n refreshToken: string,\r\n): Promise<{\r\n success: boolean;\r\n entity?: {\r\n id: string;\r\n type: AuthEntityType;\r\n parameters: Record<string, unknown>;\r\n };\r\n tokenStatus: 'valid' | 'expired' | 'revoked' | 'invalid';\r\n newTokens?: {\r\n accessToken: string;\r\n refreshToken?: string | null;\r\n };\r\n}> {\r\n const tokenRepository = getLibraries().getAuthTokenRepository();\r\n\r\n const storedToken = await tokenRepository.findRefreshToken(refreshToken);\r\n\r\n if (!storedToken) {\r\n return {\r\n success: false,\r\n tokenStatus: 'invalid'\r\n };\r\n }\r\n\r\n const isRevoked = storedToken.isRevoked;\r\n if (isRevoked) {\r\n return {\r\n success: false,\r\n tokenStatus: 'revoked'\r\n };\r\n }\r\n\r\n const isExpired = storedToken.isExpired();\r\n\r\n const entityData = {\r\n id: storedToken.entityId,\r\n type: storedToken.entityType,\r\n parameters: storedToken.metadata || {}\r\n };\r\n\r\n if (isExpired) {\r\n const newTokens = await signTokenPair({\r\n id: storedToken.entityId,\r\n type: storedToken.entityType,\r\n parameters: storedToken.metadata || {}\r\n });\r\n\r\n await tokenRepository.revokeRefreshToken(refreshToken);\r\n\r\n return {\r\n success: true,\r\n entity: entityData,\r\n tokenStatus: 'expired',\r\n newTokens: {\r\n accessToken: newTokens.accessToken,\r\n refreshToken: newTokens.refreshToken\r\n }\r\n };\r\n }\r\n\r\n const newAccessToken = await signAccessToken({\r\n id: storedToken.entityId,\r\n type: storedToken.entityType,\r\n parameters: storedToken.metadata || {}\r\n });\r\n\r\n return {\r\n success: true,\r\n entity: entityData,\r\n tokenStatus: 'valid',\r\n newTokens: {\r\n accessToken: newAccessToken,\r\n refreshToken: null\r\n }\r\n };\r\n}\r\n\r\nexport const RefreshTokenModel = createGlobalModel(RefreshToken);\r\n\r\n"]}
@@ -0,0 +1,15 @@
1
+ import { DocumentType, ReturnModelType } from '@typegoose/typegoose';
2
+ import { TimeStamps } from '@typegoose/typegoose/lib/defaultClasses';
3
+ import { AuthEntityType } from "./index";
4
+ export declare class RefreshToken extends TimeStamps {
5
+ token: string;
6
+ entityId: string;
7
+ entityType: AuthEntityType;
8
+ expiresAt?: Date | null;
9
+ isRevoked: boolean;
10
+ metadata?: Record<string, unknown>;
11
+ static findValidToken(this: ReturnModelType<typeof RefreshToken>, token: string): Promise<DocumentType<RefreshToken> | null>;
12
+ static revokeAllForEntity(this: ReturnModelType<typeof RefreshToken>, entityId: string, entityType: AuthEntityType): Promise<number>;
13
+ isExpired(): boolean;
14
+ static cleanupExpiredTokens(this: ReturnModelType<typeof RefreshToken>, olderThan?: Date): Promise<number>;
15
+ }
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var RefreshToken_1;
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.RefreshToken = void 0;
14
+ const typegoose_1 = require("@typegoose/typegoose");
15
+ const defaultClasses_1 = require("@typegoose/typegoose/lib/defaultClasses");
16
+ const index_1 = require("./index");
17
+ let RefreshToken = RefreshToken_1 = class RefreshToken extends defaultClasses_1.TimeStamps {
18
+ token;
19
+ entityId;
20
+ entityType;
21
+ expiresAt;
22
+ isRevoked;
23
+ metadata;
24
+ static async findValidToken(token) {
25
+ const query = { token, isRevoked: false };
26
+ query.$or = [
27
+ { expiresAt: { $exists: false } },
28
+ { expiresAt: null },
29
+ { expiresAt: { $gt: new Date() } }
30
+ ];
31
+ return this.findOne(query);
32
+ }
33
+ static async revokeAllForEntity(entityId, entityType) {
34
+ const result = await this.updateMany({ entityId, entityType, isRevoked: false }, { isRevoked: true });
35
+ return result.modifiedCount;
36
+ }
37
+ isExpired() {
38
+ if (!this.expiresAt)
39
+ return false;
40
+ return new Date() > this.expiresAt;
41
+ }
42
+ static async cleanupExpiredTokens(olderThan) {
43
+ const cutoffDate = olderThan || new Date();
44
+ const result = await this.deleteMany({
45
+ expiresAt: { $ne: null, $lt: cutoffDate },
46
+ isRevoked: true
47
+ });
48
+ return result.deletedCount;
49
+ }
50
+ };
51
+ exports.RefreshToken = RefreshToken;
52
+ __decorate([
53
+ (0, typegoose_1.prop)({ required: true, type: () => String, unique: true }),
54
+ __metadata("design:type", String)
55
+ ], RefreshToken.prototype, "token", void 0);
56
+ __decorate([
57
+ (0, typegoose_1.prop)({ required: true, type: () => String }),
58
+ __metadata("design:type", String)
59
+ ], RefreshToken.prototype, "entityId", void 0);
60
+ __decorate([
61
+ (0, typegoose_1.prop)({
62
+ required: true,
63
+ type: () => String,
64
+ enum: index_1.AuthEntityType
65
+ }),
66
+ __metadata("design:type", String)
67
+ ], RefreshToken.prototype, "entityType", void 0);
68
+ __decorate([
69
+ (0, typegoose_1.prop)({ required: false, type: () => Date }),
70
+ __metadata("design:type", Object)
71
+ ], RefreshToken.prototype, "expiresAt", void 0);
72
+ __decorate([
73
+ (0, typegoose_1.prop)({ required: true, type: () => Boolean, default: false }),
74
+ __metadata("design:type", Boolean)
75
+ ], RefreshToken.prototype, "isRevoked", void 0);
76
+ __decorate([
77
+ (0, typegoose_1.prop)({ required: false, type: () => Object }),
78
+ __metadata("design:type", Object)
79
+ ], RefreshToken.prototype, "metadata", void 0);
80
+ exports.RefreshToken = RefreshToken = RefreshToken_1 = __decorate([
81
+ (0, typegoose_1.modelOptions)({
82
+ schemaOptions: {
83
+ collection: 'refresh_tokens',
84
+ autoCreate: true,
85
+ toJSON: { virtuals: true },
86
+ toObject: { virtuals: true }
87
+ },
88
+ options: {
89
+ allowMixed: typegoose_1.Severity.ALLOW
90
+ }
91
+ }),
92
+ (0, typegoose_1.index)({ entityId: 1, entityType: 1 }),
93
+ (0, typegoose_1.index)({ isRevoked: 1 })
94
+ ], RefreshToken);
95
+ //# sourceMappingURL=refreshtoken.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refreshtoken.js","sourceRoot":"/","sources":["libraries/auth/refreshtoken.ts"],"names":[],"mappings":"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","sourcesContent":["import { prop, getModelForClass, modelOptions, index, Severity, DocumentType, ReturnModelType } from '@typegoose/typegoose';\r\nimport { TimeStamps } from '@typegoose/typegoose/lib/defaultClasses';\r\nimport { getLibraries } from '../../index';\r\nimport {AuthEntityType, RefreshTokenModel} from \"./index\";\r\n\r\n@modelOptions({\r\n schemaOptions: {\r\n collection: 'refresh_tokens',\r\n autoCreate: true,\r\n toJSON: { virtuals: true },\r\n toObject: { virtuals: true }\r\n },\r\n options: {\r\n allowMixed: Severity.ALLOW // Allow mixed types for metadata\r\n }\r\n})\r\n@index({ entityId: 1, entityType: 1 })\r\n@index({ isRevoked: 1 })\r\nexport class RefreshToken extends TimeStamps {\r\n @prop({ required: true, type: () => String, unique: true })\r\n public token!: string;\r\n\r\n @prop({ required: true, type: () => String })\r\n public entityId!: string;\r\n\r\n @prop({\r\n required: true,\r\n type: () => String,\r\n enum: AuthEntityType\r\n })\r\n public entityType!: AuthEntityType;\r\n\r\n @prop({ required: false, type: () => Date })\r\n public expiresAt?: Date | null;\r\n\r\n @prop({ required: true, type: () => Boolean, default: false })\r\n public isRevoked!: boolean;\r\n\r\n @prop({ required: false, type: () => Object })\r\n public metadata?: Record<string, unknown>;\r\n\r\n /**\r\n * Static method to find a valid token\r\n */\r\n public static async findValidToken(\r\n this: ReturnModelType<typeof RefreshToken>,\r\n token: string\r\n ): Promise<DocumentType<RefreshToken> | null> {\r\n const query: any = { token, isRevoked: false };\r\n\r\n // Only check expiration if token has an expiration date\r\n query.$or = [\r\n { expiresAt: { $exists: false } },\r\n { expiresAt: null },\r\n { expiresAt: { $gt: new Date() } }\r\n ];\r\n\r\n return this.findOne(query);\r\n }\r\n\r\n /**\r\n * Static method to revoke all tokens for an entity\r\n */\r\n public static async revokeAllForEntity(\r\n this: ReturnModelType<typeof RefreshToken>,\r\n entityId: string,\r\n entityType: AuthEntityType\r\n ): Promise<number> {\r\n const result = await this.updateMany(\r\n { entityId, entityType, isRevoked: false },\r\n { isRevoked: true }\r\n );\r\n\r\n return result.modifiedCount;\r\n }\r\n\r\n /**\r\n * Check if this token is expired\r\n */\r\n public isExpired(): boolean {\r\n if (!this.expiresAt) return false;\r\n return new Date() > this.expiresAt;\r\n }\r\n\r\n /**\r\n * Static method to clean up old expired tokens\r\n * Call this periodically to keep your database clean\r\n */\r\n public static async cleanupExpiredTokens(\r\n this: ReturnModelType<typeof RefreshToken>,\r\n olderThan?: Date\r\n ): Promise<number> {\r\n const cutoffDate = olderThan || new Date();\r\n\r\n const result = await this.deleteMany({\r\n expiresAt: { $ne: null, $lt: cutoffDate },\r\n isRevoked: true // Only delete tokens that are both expired and already revoked\r\n });\r\n\r\n return result.deletedCount;\r\n }\r\n}\r\n"]}
@@ -0,0 +1,18 @@
1
+ import { AuthEntityType } from "./index";
2
+ import { DocumentType } from "@typegoose/typegoose";
3
+ import { RefreshToken } from "./refreshtoken";
4
+ export declare class TokenRepository {
5
+ saveRefreshToken(token: {
6
+ token: string;
7
+ entityId: string;
8
+ entityType: AuthEntityType;
9
+ expiresAt?: Date | null;
10
+ isRevoked?: boolean;
11
+ metadata?: Record<string, unknown>;
12
+ }): Promise<DocumentType<RefreshToken>>;
13
+ findRefreshToken(token: string): Promise<DocumentType<RefreshToken> | null>;
14
+ revokeRefreshToken(token: string): Promise<boolean>;
15
+ revokeAllRefreshTokensForEntity(entityId: string, entityType: AuthEntityType): Promise<boolean>;
16
+ getActiveTokensForEntity(entityId: string, entityType: AuthEntityType): Promise<DocumentType<RefreshToken>[]>;
17
+ cleanupExpiredTokens(olderThan?: Date): Promise<number>;
18
+ }
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.TokenRepository = void 0;
10
+ const index_1 = require("./index");
11
+ const typedi_1 = require("typedi");
12
+ let TokenRepository = class TokenRepository {
13
+ async saveRefreshToken(token) {
14
+ return index_1.RefreshTokenModel.create(token);
15
+ }
16
+ async findRefreshToken(token) {
17
+ return index_1.RefreshTokenModel.findValidToken(token);
18
+ }
19
+ async revokeRefreshToken(token) {
20
+ const result = await index_1.RefreshTokenModel.updateOne({ token }, { isRevoked: true });
21
+ return result.modifiedCount > 0;
22
+ }
23
+ async revokeAllRefreshTokensForEntity(entityId, entityType) {
24
+ const count = await index_1.RefreshTokenModel.revokeAllForEntity(entityId, entityType);
25
+ return count > 0;
26
+ }
27
+ async getActiveTokensForEntity(entityId, entityType) {
28
+ return index_1.RefreshTokenModel.find({
29
+ entityId,
30
+ entityType,
31
+ isRevoked: false,
32
+ $or: [
33
+ { expiresAt: { $exists: false } },
34
+ { expiresAt: null },
35
+ { expiresAt: { $gt: new Date() } }
36
+ ]
37
+ });
38
+ }
39
+ async cleanupExpiredTokens(olderThan) {
40
+ return index_1.RefreshTokenModel.cleanupExpiredTokens(olderThan);
41
+ }
42
+ };
43
+ exports.TokenRepository = TokenRepository;
44
+ exports.TokenRepository = TokenRepository = __decorate([
45
+ (0, typedi_1.Service)()
46
+ ], TokenRepository);
47
+ //# sourceMappingURL=repository.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repository.js","sourceRoot":"/","sources":["libraries/auth/repository.ts"],"names":[],"mappings":";;;;;;;;;AAAA,mCAA0D;AAG1D,mCAA+B;AAGxB,IAAM,eAAe,GAArB,MAAM,eAAe;IAIxB,KAAK,CAAC,gBAAgB,CAAC,KAOtB;QACG,OAAO,yBAAiB,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC3C,CAAC;IAKD,KAAK,CAAC,gBAAgB,CAAC,KAAa;QAChC,OAAO,yBAAiB,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;IACnD,CAAC;IAKD,KAAK,CAAC,kBAAkB,CAAC,KAAa;QAClC,MAAM,MAAM,GAAG,MAAM,yBAAiB,CAAC,SAAS,CAC5C,EAAE,KAAK,EAAE,EACT,EAAE,SAAS,EAAE,IAAI,EAAE,CACtB,CAAC;QAEF,OAAO,MAAM,CAAC,aAAa,GAAG,CAAC,CAAC;IACpC,CAAC;IAKD,KAAK,CAAC,+BAA+B,CACjC,QAAgB,EAChB,UAA0B;QAE1B,MAAM,KAAK,GAAG,MAAM,yBAAiB,CAAC,kBAAkB,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;QAC/E,OAAO,KAAK,GAAG,CAAC,CAAC;IACrB,CAAC;IAKD,KAAK,CAAC,wBAAwB,CAC1B,QAAgB,EAChB,UAA0B;QAE1B,OAAO,yBAAiB,CAAC,IAAI,CAAC;YAC1B,QAAQ;YACR,UAAU;YACV,SAAS,EAAE,KAAK;YAChB,GAAG,EAAE;gBACD,EAAE,SAAS,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;gBACjC,EAAE,SAAS,EAAE,IAAI,EAAE;gBACnB,EAAE,SAAS,EAAE,EAAE,GAAG,EAAE,IAAI,IAAI,EAAE,EAAE,EAAE;aACrC;SACJ,CAAC,CAAC;IACP,CAAC;IAMD,KAAK,CAAC,oBAAoB,CAAC,SAAgB;QACvC,OAAO,yBAAiB,CAAC,oBAAoB,CAAC,SAAS,CAAC,CAAC;IAC7D,CAAC;CACJ,CAAA;AAvEY,0CAAe;0BAAf,eAAe;IAD3B,IAAA,gBAAO,GAAE;GACG,eAAe,CAuE3B","sourcesContent":["import {AuthEntityType, RefreshTokenModel} from \"./index\";\r\nimport {DocumentType} from \"@typegoose/typegoose\";\r\nimport {RefreshToken} from \"./refreshtoken\";\r\nimport {Service} from \"typedi\";\r\n\r\n@Service()\r\nexport class TokenRepository {\r\n /**\r\n * Save a refresh token\r\n */\r\n async saveRefreshToken(token: {\r\n token: string;\r\n entityId: string;\r\n entityType: AuthEntityType;\r\n expiresAt?: Date | null;\r\n isRevoked?: boolean;\r\n metadata?: Record<string, unknown>;\r\n }): Promise<DocumentType<RefreshToken>> {\r\n return RefreshTokenModel.create(token);\r\n }\r\n\r\n /**\r\n * Find a refresh token by its value\r\n */\r\n async findRefreshToken(token: string): Promise<DocumentType<RefreshToken> | null> {\r\n return RefreshTokenModel.findValidToken(token);\r\n }\r\n\r\n /**\r\n * Revoke a specific refresh token\r\n */\r\n async revokeRefreshToken(token: string): Promise<boolean> {\r\n const result = await RefreshTokenModel.updateOne(\r\n { token },\r\n { isRevoked: true }\r\n );\r\n\r\n return result.modifiedCount > 0;\r\n }\r\n\r\n /**\r\n * Revoke all refresh tokens for a specific entity\r\n */\r\n async revokeAllRefreshTokensForEntity(\r\n entityId: string,\r\n entityType: AuthEntityType\r\n ): Promise<boolean> {\r\n const count = await RefreshTokenModel.revokeAllForEntity(entityId, entityType);\r\n return count > 0;\r\n }\r\n\r\n /**\r\n * Get all active tokens for an entity\r\n */\r\n async getActiveTokensForEntity(\r\n entityId: string,\r\n entityType: AuthEntityType\r\n ): Promise<DocumentType<RefreshToken>[]> {\r\n return RefreshTokenModel.find({\r\n entityId,\r\n entityType,\r\n isRevoked: false,\r\n $or: [\r\n { expiresAt: { $exists: false } },\r\n { expiresAt: null },\r\n { expiresAt: { $gt: new Date() } }\r\n ]\r\n });\r\n }\r\n\r\n /**\r\n * Clean up expired tokens (can be called periodically if needed)\r\n * Note: MongoDB TTL index will also handle this automatically for tokens with expiresAt\r\n */\r\n async cleanupExpiredTokens(olderThan?: Date): Promise<number> {\r\n return RefreshTokenModel.cleanupExpiredTokens(olderThan);\r\n }\r\n}\r\n"]}
@@ -6,6 +6,7 @@ import { RedisService } from "./redis";
6
6
  import { ConfigurationService } from "./configuration";
7
7
  import { UploadService } from "./upload";
8
8
  import { FirebaseService } from "./firebase";
9
+ import { TokenRepository } from "./auth/repository";
9
10
  export declare class LibraryManager {
10
11
  getLogger(): LyxaLogger;
11
12
  getMicroServiceDispatcher(): MicroServiceDispatcher;
@@ -14,6 +15,7 @@ export declare class LibraryManager {
14
15
  getRedisService(): RedisService;
15
16
  getConfigurationService(): ConfigurationService;
16
17
  getFirebaseService(): FirebaseService;
18
+ getAuthTokenRepository(): TokenRepository;
17
19
  getGlobalUploadService(): UploadService;
18
20
  getTenetUploadService(): UploadService;
19
21
  }
@@ -16,6 +16,7 @@ const redis_1 = require("./redis");
16
16
  const configuration_1 = require("./configuration");
17
17
  const upload_1 = require("./upload");
18
18
  const firebase_1 = require("./firebase");
19
+ const repository_1 = require("./auth/repository");
19
20
  let LibraryManager = class LibraryManager {
20
21
  getLogger() {
21
22
  return typedi_1.Container.get(logger_1.LyxaLogger);
@@ -38,6 +39,9 @@ let LibraryManager = class LibraryManager {
38
39
  getFirebaseService() {
39
40
  return typedi_1.Container.get(firebase_1.FirebaseService);
40
41
  }
42
+ getAuthTokenRepository() {
43
+ return typedi_1.Container.get(repository_1.TokenRepository);
44
+ }
41
45
  getGlobalUploadService() {
42
46
  return new upload_1.UploadService();
43
47
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"/","sources":["libraries/index.ts"],"names":[],"mappings":";;;;;;;;;AAAA,mCAA0C;AAC1C,qCAAuC;AACvC,qCAAoC;AACpC,6CAAoD;AACpD,uCAA+C;AAC/C,mCAAqC;AACrC,mDAAqD;AACrD,qCAAuC;AACvC,yCAA2C;AAGpC,IAAM,cAAc,GAApB,MAAM,cAAc;IAChB,SAAS;QACZ,OAAO,kBAAS,CAAC,GAAG,CAAC,mBAAU,CAAC,CAAC;IACrC,CAAC;IACM,yBAAyB;QAC5B,OAAO,kBAAS,CAAC,GAAG,CAAC,mCAAsB,CAAC,CAAC;IACjD,CAAC;IACM,gBAAgB;QACnB,OAAO,kBAAS,CAAC,GAAG,CAAC,sBAAa,CAAC,CAAC;IACxC,CAAC;IACM,iBAAiB;QACpB,OAAO,kBAAS,CAAC,GAAG,CAAC,8BAAoB,CAAC,CAAC;IAC/C,CAAC;IACM,eAAe;QAClB,OAAO,kBAAS,CAAC,GAAG,CAAC,oBAAY,CAAC,CAAC;IACvC,CAAC;IACM,uBAAuB;QAC1B,OAAO,kBAAS,CAAC,GAAG,CAAC,oCAAoB,CAAC,CAAC;IAC/C,CAAC;IACM,kBAAkB;QACrB,OAAO,kBAAS,CAAC,GAAG,CAAC,0BAAe,CAAC,CAAC;IAC1C,CAAC;IACM,sBAAsB;QACzB,OAAO,IAAI,sBAAa,EAAE,CAAC;IAC/B,CAAC;IACM,qBAAqB;QACxB,OAAO,IAAI,sBAAa,CAAC,OAAO,GAAG,IAAI,CAAC,uBAAuB,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACrF,CAAC;CACJ,CAAA;AA5BY,wCAAc;yBAAd,cAAc;IAD1B,IAAA,gBAAO,GAAE;GACG,cAAc,CA4B1B","sourcesContent":["import {Container, Service} from \"typedi\";\r\nimport {TwilioService} from \"./twilio\";\r\nimport {LyxaLogger} from \"./logger\";\r\nimport {MicroServiceDispatcher} from \"./dispatcher\";\r\nimport {SecretManagerService} from \"./secrets\";\r\nimport {RedisService} from \"./redis\";\r\nimport {ConfigurationService} from \"./configuration\";\r\nimport {UploadService} from \"./upload\";\r\nimport {FirebaseService} from \"./firebase\";\r\n\r\n@Service()\r\nexport class LibraryManager {\r\n public getLogger(): LyxaLogger {\r\n return Container.get(LyxaLogger);\r\n }\r\n public getMicroServiceDispatcher(): MicroServiceDispatcher {\r\n return Container.get(MicroServiceDispatcher);\r\n }\r\n public getTwilioService(): TwilioService {\r\n return Container.get(TwilioService);\r\n }\r\n public getSecretsService(): SecretManagerService {\r\n return Container.get(SecretManagerService);\r\n }\r\n public getRedisService(): RedisService {\r\n return Container.get(RedisService);\r\n }\r\n public getConfigurationService(): ConfigurationService {\r\n return Container.get(ConfigurationService);\r\n }\r\n public getFirebaseService(): FirebaseService {\r\n return Container.get(FirebaseService);\r\n }\r\n public getGlobalUploadService(): UploadService {\r\n return new UploadService();\r\n }\r\n public getTenetUploadService(): UploadService {\r\n return new UploadService(\"lyxa-\" + this.getConfigurationService().options.tenet);\r\n }\r\n}\r\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"/","sources":["libraries/index.ts"],"names":[],"mappings":";;;;;;;;;AAAA,mCAA0C;AAC1C,qCAAuC;AACvC,qCAAoC;AACpC,6CAAoD;AACpD,uCAA+C;AAC/C,mCAAqC;AACrC,mDAAqD;AACrD,qCAAuC;AACvC,yCAA2C;AAC3C,kDAAkD;AAG3C,IAAM,cAAc,GAApB,MAAM,cAAc;IAChB,SAAS;QACZ,OAAO,kBAAS,CAAC,GAAG,CAAC,mBAAU,CAAC,CAAC;IACrC,CAAC;IACM,yBAAyB;QAC5B,OAAO,kBAAS,CAAC,GAAG,CAAC,mCAAsB,CAAC,CAAC;IACjD,CAAC;IACM,gBAAgB;QACnB,OAAO,kBAAS,CAAC,GAAG,CAAC,sBAAa,CAAC,CAAC;IACxC,CAAC;IACM,iBAAiB;QACpB,OAAO,kBAAS,CAAC,GAAG,CAAC,8BAAoB,CAAC,CAAC;IAC/C,CAAC;IACM,eAAe;QAClB,OAAO,kBAAS,CAAC,GAAG,CAAC,oBAAY,CAAC,CAAC;IACvC,CAAC;IACM,uBAAuB;QAC1B,OAAO,kBAAS,CAAC,GAAG,CAAC,oCAAoB,CAAC,CAAC;IAC/C,CAAC;IACM,kBAAkB;QACrB,OAAO,kBAAS,CAAC,GAAG,CAAC,0BAAe,CAAC,CAAC;IAC1C,CAAC;IACM,sBAAsB;QACzB,OAAO,kBAAS,CAAC,GAAG,CAAC,4BAAe,CAAC,CAAC;IAC1C,CAAC;IACM,sBAAsB;QACzB,OAAO,IAAI,sBAAa,EAAE,CAAC;IAC/B,CAAC;IACM,qBAAqB;QACxB,OAAO,IAAI,sBAAa,CAAC,OAAO,GAAG,IAAI,CAAC,uBAAuB,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACrF,CAAC;CACJ,CAAA;AA/BY,wCAAc;yBAAd,cAAc;IAD1B,IAAA,gBAAO,GAAE;GACG,cAAc,CA+B1B","sourcesContent":["import {Container, Service} from \"typedi\";\r\nimport {TwilioService} from \"./twilio\";\r\nimport {LyxaLogger} from \"./logger\";\r\nimport {MicroServiceDispatcher} from \"./dispatcher\";\r\nimport {SecretManagerService} from \"./secrets\";\r\nimport {RedisService} from \"./redis\";\r\nimport {ConfigurationService} from \"./configuration\";\r\nimport {UploadService} from \"./upload\";\r\nimport {FirebaseService} from \"./firebase\";\r\nimport {TokenRepository} from \"./auth/repository\";\r\n\r\n@Service()\r\nexport class LibraryManager {\r\n public getLogger(): LyxaLogger {\r\n return Container.get(LyxaLogger);\r\n }\r\n public getMicroServiceDispatcher(): MicroServiceDispatcher {\r\n return Container.get(MicroServiceDispatcher);\r\n }\r\n public getTwilioService(): TwilioService {\r\n return Container.get(TwilioService);\r\n }\r\n public getSecretsService(): SecretManagerService {\r\n return Container.get(SecretManagerService);\r\n }\r\n public getRedisService(): RedisService {\r\n return Container.get(RedisService);\r\n }\r\n public getConfigurationService(): ConfigurationService {\r\n return Container.get(ConfigurationService);\r\n }\r\n public getFirebaseService(): FirebaseService {\r\n return Container.get(FirebaseService);\r\n }\r\n public getAuthTokenRepository(): TokenRepository {\r\n return Container.get(TokenRepository);\r\n }\r\n public getGlobalUploadService(): UploadService {\r\n return new UploadService();\r\n }\r\n public getTenetUploadService(): UploadService {\r\n return new UploadService(\"lyxa-\" + this.getConfigurationService().options.tenet);\r\n }\r\n}\r\n"]}
@@ -2,5 +2,5 @@ import { Connection } from 'mongoose';
2
2
  import { ReturnModelType } from '@typegoose/typegoose';
3
3
  import { AnyParamConstructor, IModelOptions } from "@typegoose/typegoose/lib/types";
4
4
  export declare function initGlobalConnection(uri: string, environment: string): Promise<Connection>;
5
- export declare function createGlobalModel<T extends AnyParamConstructor<any>>(Class: T): ReturnModelType<T>;
5
+ export declare function createGlobalModel<T extends AnyParamConstructor<any>>(Class: T, options?: IModelOptions): ReturnModelType<T>;
6
6
  export declare function createGlobalDiscriminatorModel<Base extends AnyParamConstructor<any>, Sub extends AnyParamConstructor<any>>(baseModel: ReturnModelType<Base>, subClass: Sub, discriminatorName?: string, options?: IModelOptions): ReturnModelType<Sub>;
@@ -8,19 +8,48 @@ const typegoose_1 = require("@typegoose/typegoose");
8
8
  const soft_delete_plugin_1 = require("./plugins/soft-delete-plugin");
9
9
  (0, soft_delete_plugin_1.applySoftDeletePlugin)();
10
10
  let globalConnection;
11
+ let pendingModels = [];
12
+ let isConnectionInitialized = false;
11
13
  async function initGlobalConnection(uri, environment) {
12
14
  if (!globalConnection) {
13
15
  globalConnection = (0, mongoose_1.createConnection)(uri, {
14
16
  dbName: `lyxa-${environment}-global`,
15
17
  });
18
+ isConnectionInitialized = true;
19
+ for (const { Class, options, callback } of pendingModels) {
20
+ const model = (0, typegoose_1.getModelForClass)(Class, {
21
+ existingConnection: globalConnection,
22
+ ...options
23
+ });
24
+ callback(model);
25
+ }
26
+ pendingModels = [];
16
27
  }
17
28
  return globalConnection;
18
29
  }
19
- function createGlobalModel(Class) {
20
- if (!globalConnection) {
21
- throw new Error('Global connection not initialized. Call initGlobalConnection(...) first.');
30
+ function createGlobalModel(Class, options) {
31
+ if (globalConnection && isConnectionInitialized) {
32
+ return (0, typegoose_1.getModelForClass)(Class, { existingConnection: globalConnection, ...options });
33
+ }
34
+ else {
35
+ let modelInstance = null;
36
+ const modelProxy = new Proxy({}, {
37
+ get: function (target, prop) {
38
+ if (!modelInstance) {
39
+ throw new Error('Model not initialized yet. Database connection pending.');
40
+ }
41
+ return modelInstance[prop];
42
+ }
43
+ });
44
+ pendingModels.push({
45
+ Class,
46
+ options,
47
+ callback: (model) => {
48
+ modelInstance = model;
49
+ }
50
+ });
51
+ return modelProxy;
22
52
  }
23
- return (0, typegoose_1.getModelForClass)(Class, { existingConnection: globalConnection });
24
53
  }
25
54
  function createGlobalDiscriminatorModel(baseModel, subClass, discriminatorName, options) {
26
55
  if (!globalConnection) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"/","sources":["libraries/mongo/index.ts"],"names":[],"mappings":";;AAUA,oDAOC;AAKD,8CASC;AAWD,wEAmBC;AA7DD,uCAAwD;AACxD,oDAAsG;AAGtG,qEAAqE;AAGrE,IAAA,0CAAqB,GAAE,CAAC;AACxB,IAAI,gBAAwC,CAAC;AAEtC,KAAK,UAAU,oBAAoB,CAAC,GAAW,EAAE,WAAmB;IACvE,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACpB,gBAAgB,GAAG,IAAA,2BAAgB,EAAC,GAAG,EAAE;YACrC,MAAM,EAAE,QAAQ,WAAW,SAAS;SACvC,CAAC,CAAC;IACP,CAAC;IACD,OAAO,gBAAgB,CAAC;AAC5B,CAAC;AAKD,SAAgB,iBAAiB,CAC7B,KAAQ;IAER,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CACX,0EAA0E,CAC7E,CAAC;IACN,CAAC;IACD,OAAO,IAAA,4BAAgB,EAAC,KAAK,EAAE,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,CAAC,CAAC;AAC7E,CAAC;AAWD,SAAgB,8BAA8B,CAI1C,SAAgC,EAChC,QAAa,EACb,iBAA0B,EAC1B,OAAuB;IAEvB,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CACX,0EAA0E,CAC7E,CAAC;IACN,CAAC;IAED,OAAO,IAAA,yCAA6B,EAAC,SAAS,EAAE,QAAQ,EAAE,iBAAiB,EAAE;QACzE,kBAAkB,EAAE,gBAAgB;QACpC,GAAG,OAAO;KACb,CAAC,CAAC;AACP,CAAC","sourcesContent":["import { Connection, createConnection } from 'mongoose';\r\nimport {getDiscriminatorModelForClass, getModelForClass, ReturnModelType} from '@typegoose/typegoose';\r\nimport {AnyParamConstructor, IModelOptions} from \"@typegoose/typegoose/lib/types\";\r\n\r\nimport { applySoftDeletePlugin } from './plugins/soft-delete-plugin';\r\n\r\n// Apply the soft delete plugin globally before any connections or models\r\napplySoftDeletePlugin();\r\nlet globalConnection: Connection | undefined;\r\n\r\nexport async function initGlobalConnection(uri: string, environment: string) {\r\n if (!globalConnection) {\r\n globalConnection = createConnection(uri, {\r\n dbName: `lyxa-${environment}-global`,\r\n });\r\n }\r\n return globalConnection;\r\n}\r\n\r\n/**\r\n * Creates a Typegoose model from a class (with a single, global connection).\r\n */\r\nexport function createGlobalModel<T extends AnyParamConstructor<any>>(\r\n Class: T\r\n): ReturnModelType<T> {\r\n if (!globalConnection) {\r\n throw new Error(\r\n 'Global connection not initialized. Call initGlobalConnection(...) first.'\r\n );\r\n }\r\n return getModelForClass(Class, { existingConnection: globalConnection });\r\n}\r\n\r\n/**\r\n * Creates a Typegoose *discriminator* model from a base model,\r\n * using a derived sub-class & optional discriminator value.\r\n *\r\n * @param baseModel The ReturnModelType (model) for the base class\r\n * @param subClass The derived class constructor\r\n * @param discriminatorName A string to label this sub-class (e.g. \"DOG\")\r\n * @param options Additional model options\r\n */\r\nexport function createGlobalDiscriminatorModel<\r\n Base extends AnyParamConstructor<any>,\r\n Sub extends AnyParamConstructor<any>,\r\n>(\r\n baseModel: ReturnModelType<Base>,\r\n subClass: Sub,\r\n discriminatorName?: string,\r\n options?: IModelOptions,\r\n): ReturnModelType<Sub> {\r\n if (!globalConnection) {\r\n throw new Error(\r\n 'Global connection not initialized. Call initGlobalConnection(...) first.',\r\n );\r\n }\r\n\r\n return getDiscriminatorModelForClass(baseModel, subClass, discriminatorName, {\r\n existingConnection: globalConnection,\r\n ...options,\r\n });\r\n}\r\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"/","sources":["libraries/mongo/index.ts"],"names":[],"mappings":";;AAkBA,oDAoBC;AAOD,8CA+BC;AAWD,wEAmBC;AA1GD,uCAAwD;AACxD,oDAAsG;AAGtG,qEAAqE;AAGrE,IAAA,0CAAqB,GAAE,CAAC;AACxB,IAAI,gBAAwC,CAAC;AAG7C,IAAI,aAAa,GAIZ,EAAE,CAAC;AACR,IAAI,uBAAuB,GAAG,KAAK,CAAC;AAE7B,KAAK,UAAU,oBAAoB,CAAC,GAAW,EAAE,WAAmB;IACvE,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACpB,gBAAgB,GAAG,IAAA,2BAAgB,EAAC,GAAG,EAAE;YACrC,MAAM,EAAE,QAAQ,WAAW,SAAS;SACvC,CAAC,CAAC;QAGH,uBAAuB,GAAG,IAAI,CAAC;QAG/B,KAAK,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,aAAa,EAAE,CAAC;YACvD,MAAM,KAAK,GAAG,IAAA,4BAAgB,EAAC,KAAK,EAAE;gBAClC,kBAAkB,EAAE,gBAAgB;gBACpC,GAAG,OAAO;aACb,CAAC,CAAC;YACH,QAAQ,CAAC,KAAK,CAAC,CAAC;QACpB,CAAC;QACD,aAAa,GAAG,EAAE,CAAC;IACvB,CAAC;IACD,OAAO,gBAAgB,CAAC;AAC5B,CAAC;AAOD,SAAgB,iBAAiB,CAC7B,KAAQ,EACR,OAAuB;IAEvB,IAAI,gBAAgB,IAAI,uBAAuB,EAAE,CAAC;QAE9C,OAAO,IAAA,4BAAgB,EAAC,KAAK,EAAE,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC;IACzF,CAAC;SAAM,CAAC;QAEJ,IAAI,aAAa,GAAQ,IAAI,CAAC;QAE9B,MAAM,UAAU,GAAG,IAAI,KAAK,CAAC,EAAE,EAAE;YAC7B,GAAG,EAAE,UAAS,MAAM,EAAE,IAAI;gBACtB,IAAI,CAAC,aAAa,EAAE,CAAC;oBACjB,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAC;gBAC/E,CAAC;gBACD,OAAO,aAAa,CAAC,IAAI,CAAC,CAAC;YAC/B,CAAC;SACJ,CAAuB,CAAC;QAGzB,aAAa,CAAC,IAAI,CAAC;YACf,KAAK;YACL,OAAO;YACP,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE;gBAChB,aAAa,GAAG,KAAK,CAAC;YAC1B,CAAC;SACJ,CAAC,CAAC;QAEH,OAAO,UAAU,CAAC;IACtB,CAAC;AACL,CAAC;AAWD,SAAgB,8BAA8B,CAI1C,SAAgC,EAChC,QAAa,EACb,iBAA0B,EAC1B,OAAuB;IAEvB,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CACX,0EAA0E,CAC7E,CAAC;IACN,CAAC;IAED,OAAO,IAAA,yCAA6B,EAAC,SAAS,EAAE,QAAQ,EAAE,iBAAiB,EAAE;QACzE,kBAAkB,EAAE,gBAAgB;QACpC,GAAG,OAAO;KACb,CAAC,CAAC;AACP,CAAC","sourcesContent":["import { Connection, createConnection } from 'mongoose';\r\nimport {getDiscriminatorModelForClass, getModelForClass, ReturnModelType} from '@typegoose/typegoose';\r\nimport {AnyParamConstructor, IModelOptions} from \"@typegoose/typegoose/lib/types\";\r\n\r\nimport { applySoftDeletePlugin } from './plugins/soft-delete-plugin';\r\n\r\n// Apply the soft delete plugin globally before any connections or models\r\napplySoftDeletePlugin();\r\nlet globalConnection: Connection | undefined;\r\n\r\n// Track pending models that were requested before connection initialization\r\nlet pendingModels: Array<{\r\n Class: any,\r\n options?: IModelOptions,\r\n callback: (model: any) => void\r\n}> = [];\r\nlet isConnectionInitialized = false;\r\n\r\nexport async function initGlobalConnection(uri: string, environment: string) {\r\n if (!globalConnection) {\r\n globalConnection = createConnection(uri, {\r\n dbName: `lyxa-${environment}-global`,\r\n });\r\n\r\n // Connection is now initialized\r\n isConnectionInitialized = true;\r\n\r\n // Process any pending models\r\n for (const { Class, options, callback } of pendingModels) {\r\n const model = getModelForClass(Class, {\r\n existingConnection: globalConnection,\r\n ...options\r\n });\r\n callback(model);\r\n }\r\n pendingModels = [];\r\n }\r\n return globalConnection;\r\n}\r\n\r\n/**\r\n * Creates a Typegoose model from a class (with a single, global connection).\r\n * If connection is not yet initialized, returns a proxy that will resolve to the model\r\n * once the connection is established.\r\n */\r\nexport function createGlobalModel<T extends AnyParamConstructor<any>>(\r\n Class: T,\r\n options?: IModelOptions\r\n): ReturnModelType<T> {\r\n if (globalConnection && isConnectionInitialized) {\r\n // Normal behavior - create and return the model\r\n return getModelForClass(Class, { existingConnection: globalConnection, ...options });\r\n } else {\r\n // Connection not initialized yet - create a proxy object\r\n let modelInstance: any = null;\r\n\r\n const modelProxy = new Proxy({}, {\r\n get: function(target, prop) {\r\n if (!modelInstance) {\r\n throw new Error('Model not initialized yet. Database connection pending.');\r\n }\r\n return modelInstance[prop];\r\n }\r\n }) as ReturnModelType<T>;\r\n\r\n // Store this request to be processed when connection is initialized\r\n pendingModels.push({\r\n Class,\r\n options,\r\n callback: (model) => {\r\n modelInstance = model;\r\n }\r\n });\r\n\r\n return modelProxy;\r\n }\r\n}\r\n\r\n/**\r\n * Creates a Typegoose *discriminator* model from a base model,\r\n * using a derived sub-class & optional discriminator value.\r\n *\r\n * @param baseModel The ReturnModelType (model) for the base class\r\n * @param subClass The derived class constructor\r\n * @param discriminatorName A string to label this sub-class (e.g. \"DOG\")\r\n * @param options Additional model options\r\n */\r\nexport function createGlobalDiscriminatorModel<\r\n Base extends AnyParamConstructor<any>,\r\n Sub extends AnyParamConstructor<any>,\r\n>(\r\n baseModel: ReturnModelType<Base>,\r\n subClass: Sub,\r\n discriminatorName?: string,\r\n options?: IModelOptions,\r\n): ReturnModelType<Sub> {\r\n if (!globalConnection) {\r\n throw new Error(\r\n 'Global connection not initialized. Call initGlobalConnection(...) first.',\r\n );\r\n }\r\n\r\n return getDiscriminatorModelForClass(baseModel, subClass, discriminatorName, {\r\n existingConnection: globalConnection,\r\n ...options,\r\n });\r\n}\r\n"]}
@@ -19,7 +19,6 @@ let RedisService = class RedisService {
19
19
  _redisClient;
20
20
  constructor(redisURL) {
21
21
  this._redisClient = new ioredis_1.default(redisURL);
22
- this._redisClient.connect().then(r => console.log("Connected to Redis")).catch(e => console.log("Error whilst connecting to redis: " + e));
23
22
  }
24
23
  get redisClient() {
25
24
  return this._redisClient;
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"/","sources":["libraries/redis/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,sDAA2B;AAC3B,mCAA+B;AAGxB,IAAM,YAAY,GAAlB,MAAM,YAAY;IACb,YAAY,CAAS;IAE7B,YAAY,QAAgB;QACxB,IAAI,CAAC,YAAY,GAAG,IAAI,iBAAK,CAAC,QAAQ,CAAC,CAAC;QACxC,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAC3F,oCAAoC,GAAG,CAAC,CAC3C,CAAC,CAAA;IACN,CAAC;IAED,IAAI,WAAW;QACX,OAAO,IAAI,CAAC,YAAY,CAAC;IAC7B,CAAC;IAEM,KAAK,CAAC,eAAe,CAAI,GAAW;QACvC,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QAEtD,IAAG,aAAa,IAAI,SAAS,EAAE,CAAC;YAC5B,OAAO,IAAI,CAAC,KAAK,CAAC,aAAa,CAAM,CAAA;QACzC,CAAC;QAED,OAAO,SAAS,CAAC;IACrB,CAAC;IAEM,KAAK,CAAC,WAAW,CAAI,GAAW,EAAE,MAAS,EAAE,MAAe;QAC/D,MAAM,iBAAiB,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QAEjD,IAAG,MAAM,EAAE,CAAC;YACR,MAAM,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,iBAAiB,CAAC,CAAA;QACjE,CAAC;aAAM,CAAC;YACJ,MAAM,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,iBAAiB,CAAC,CAAA;QACvD,CAAC;QAED,OAAO,IAAI,CAAC;IAChB,CAAC;IAEM,KAAK,CAAC,kBAAkB,CAAC,GAAW;QACvC,MAAM,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACjC,OAAO,IAAI,CAAC;IAChB,CAAC;IAEM,KAAK,CAAC,sBAAsB,CAAI,KAAa,EAAE,GAAW;QAC7D,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,CAAA;QAE9D,IAAG,aAAa,IAAI,SAAS,EAAE,CAAC;YAC5B,OAAO,IAAI,CAAC,KAAK,CAAC,aAAa,CAAM,CAAA;QACzC,CAAC;QAED,OAAO,SAAS,CAAC;IACrB,CAAC;IAEM,KAAK,CAAC,sBAAsB,CAAI,KAAa,EAAE,GAAW,EAAE,MAAS;QACxE,MAAM,iBAAiB,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QACjD,MAAM,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,EAAE,iBAAiB,CAAC,CAAA;QAE3D,OAAO,IAAI,CAAC;IAChB,CAAC;IAEM,KAAK,CAAC,yBAAyB,CAAI,KAAa,EAAE,GAAW;QAChE,MAAM,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,CAAA;QACxC,OAAO,IAAI,CAAC;IAChB,CAAC;IAEM,WAAW,CAAC,GAAW;QAC1B,OAAO,GAAG,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;IAC3E,CAAC;CACJ,CAAA;AAlEY,oCAAY;uBAAZ,YAAY;IADxB,IAAA,gBAAO,GAAE;;GACG,YAAY,CAkExB","sourcesContent":["import Redis from \"ioredis\"\r\nimport {Service} from \"typedi\";\r\n\r\n@Service()\r\nexport class RedisService {\r\n private _redisClient!: Redis;\r\n\r\n constructor(redisURL: string) {\r\n this._redisClient = new Redis(redisURL);\r\n this._redisClient.connect().then(r => console.log(\"Connected to Redis\")).catch(e => console.log(\r\n \"Error whilst connecting to redis: \" + e\r\n ))\r\n }\r\n\r\n get redisClient(): Redis {\r\n return this._redisClient;\r\n }\r\n\r\n public async getCachedObject<T>(key: string): Promise<T | undefined> {\r\n const usageResponse = await this._redisClient.get(key)\r\n\r\n if(usageResponse != undefined) {\r\n return JSON.parse(usageResponse) as T\r\n }\r\n\r\n return undefined;\r\n }\r\n\r\n public async cacheObject<T>(key: string, object: T, expiry?: number): Promise<boolean> {\r\n const stringifiedObject = JSON.stringify(object);\r\n\r\n if(expiry) {\r\n await this._redisClient.setex(key, expiry, stringifiedObject)\r\n } else {\r\n await this._redisClient.set(key, stringifiedObject)\r\n }\r\n\r\n return true;\r\n }\r\n\r\n public async deleteCachedObject(key: string): Promise<boolean> {\r\n await this._redisClient.del(key);\r\n return true;\r\n }\r\n\r\n public async getCachedObjectByGroup<T>(group: string, key: string): Promise<T | undefined> {\r\n const usageResponse = await this._redisClient.hget(group, key)\r\n\r\n if(usageResponse != undefined) {\r\n return JSON.parse(usageResponse) as T\r\n }\r\n\r\n return undefined;\r\n }\r\n\r\n public async setCachedObjectByGroup<T>(group: string, key: string, object: T): Promise<boolean> {\r\n const stringifiedObject = JSON.stringify(object);\r\n await this._redisClient.hset(group, key, stringifiedObject)\r\n\r\n return true;\r\n }\r\n\r\n public async deleteCachedObjectByGroup<T>(group: string, key: string): Promise<boolean> {\r\n await this._redisClient.hdel(group, key)\r\n return true;\r\n }\r\n\r\n public sanitizeKey(key: string) {\r\n return key.replace(/[^\\w\\s]/gi, '').replace(/\\s+/g, '-').toLowerCase();\r\n }\r\n}\r\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"/","sources":["libraries/redis/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,sDAA2B;AAC3B,mCAA+B;AAGxB,IAAM,YAAY,GAAlB,MAAM,YAAY;IACb,YAAY,CAAS;IAE7B,YAAY,QAAgB;QACxB,IAAI,CAAC,YAAY,GAAG,IAAI,iBAAK,CAAC,QAAQ,CAAC,CAAC;IAC5C,CAAC;IAED,IAAI,WAAW;QACX,OAAO,IAAI,CAAC,YAAY,CAAC;IAC7B,CAAC;IAEM,KAAK,CAAC,eAAe,CAAI,GAAW;QACvC,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QAEtD,IAAG,aAAa,IAAI,SAAS,EAAE,CAAC;YAC5B,OAAO,IAAI,CAAC,KAAK,CAAC,aAAa,CAAM,CAAA;QACzC,CAAC;QAED,OAAO,SAAS,CAAC;IACrB,CAAC;IAEM,KAAK,CAAC,WAAW,CAAI,GAAW,EAAE,MAAS,EAAE,MAAe;QAC/D,MAAM,iBAAiB,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QAEjD,IAAG,MAAM,EAAE,CAAC;YACR,MAAM,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,iBAAiB,CAAC,CAAA;QACjE,CAAC;aAAM,CAAC;YACJ,MAAM,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,iBAAiB,CAAC,CAAA;QACvD,CAAC;QAED,OAAO,IAAI,CAAC;IAChB,CAAC;IAEM,KAAK,CAAC,kBAAkB,CAAC,GAAW;QACvC,MAAM,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACjC,OAAO,IAAI,CAAC;IAChB,CAAC;IAEM,KAAK,CAAC,sBAAsB,CAAI,KAAa,EAAE,GAAW;QAC7D,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,CAAA;QAE9D,IAAG,aAAa,IAAI,SAAS,EAAE,CAAC;YAC5B,OAAO,IAAI,CAAC,KAAK,CAAC,aAAa,CAAM,CAAA;QACzC,CAAC;QAED,OAAO,SAAS,CAAC;IACrB,CAAC;IAEM,KAAK,CAAC,sBAAsB,CAAI,KAAa,EAAE,GAAW,EAAE,MAAS;QACxE,MAAM,iBAAiB,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QACjD,MAAM,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,EAAE,iBAAiB,CAAC,CAAA;QAE3D,OAAO,IAAI,CAAC;IAChB,CAAC;IAEM,KAAK,CAAC,yBAAyB,CAAI,KAAa,EAAE,GAAW;QAChE,MAAM,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,CAAA;QACxC,OAAO,IAAI,CAAC;IAChB,CAAC;IAEM,WAAW,CAAC,GAAW;QAC1B,OAAO,GAAG,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;IAC3E,CAAC;CACJ,CAAA;AA/DY,oCAAY;uBAAZ,YAAY;IADxB,IAAA,gBAAO,GAAE;;GACG,YAAY,CA+DxB","sourcesContent":["import Redis from \"ioredis\"\r\nimport {Service} from \"typedi\";\r\n\r\n@Service()\r\nexport class RedisService {\r\n private _redisClient!: Redis;\r\n\r\n constructor(redisURL: string) {\r\n this._redisClient = new Redis(redisURL);\r\n }\r\n\r\n get redisClient(): Redis {\r\n return this._redisClient;\r\n }\r\n\r\n public async getCachedObject<T>(key: string): Promise<T | undefined> {\r\n const usageResponse = await this._redisClient.get(key)\r\n\r\n if(usageResponse != undefined) {\r\n return JSON.parse(usageResponse) as T\r\n }\r\n\r\n return undefined;\r\n }\r\n\r\n public async cacheObject<T>(key: string, object: T, expiry?: number): Promise<boolean> {\r\n const stringifiedObject = JSON.stringify(object);\r\n\r\n if(expiry) {\r\n await this._redisClient.setex(key, expiry, stringifiedObject)\r\n } else {\r\n await this._redisClient.set(key, stringifiedObject)\r\n }\r\n\r\n return true;\r\n }\r\n\r\n public async deleteCachedObject(key: string): Promise<boolean> {\r\n await this._redisClient.del(key);\r\n return true;\r\n }\r\n\r\n public async getCachedObjectByGroup<T>(group: string, key: string): Promise<T | undefined> {\r\n const usageResponse = await this._redisClient.hget(group, key)\r\n\r\n if(usageResponse != undefined) {\r\n return JSON.parse(usageResponse) as T\r\n }\r\n\r\n return undefined;\r\n }\r\n\r\n public async setCachedObjectByGroup<T>(group: string, key: string, object: T): Promise<boolean> {\r\n const stringifiedObject = JSON.stringify(object);\r\n await this._redisClient.hset(group, key, stringifiedObject)\r\n\r\n return true;\r\n }\r\n\r\n public async deleteCachedObjectByGroup<T>(group: string, key: string): Promise<boolean> {\r\n await this._redisClient.hdel(group, key)\r\n return true;\r\n }\r\n\r\n public sanitizeKey(key: string) {\r\n return key.replace(/[^\\w\\s]/gi, '').replace(/\\s+/g, '-').toLowerCase();\r\n }\r\n}\r\n"]}
@@ -1,6 +1,7 @@
1
1
  import { CreateHTTPContextOptions } from "@trpc/server/dist/adapters/standalone";
2
2
  import { AuthEntityType } from "../auth";
3
3
  import jwt from "jsonwebtoken";
4
+ import { TokenType } from "../auth";
4
5
  export interface EntityContext extends jwt.JwtPayload {
5
6
  id: string;
6
7
  type: AuthEntityType;
@@ -9,6 +10,9 @@ export interface EntityContext extends jwt.JwtPayload {
9
10
  export interface LyxaContextParams {
10
11
  entity?: EntityContext;
11
12
  requestId?: string;
13
+ tokenType?: TokenType;
14
+ usedRefreshToken?: boolean;
15
+ tokenRenewed?: boolean;
12
16
  }
13
17
  export declare const createLyxaContext: (opts: CreateHTTPContextOptions) => Promise<CreateHTTPContextOptions & LyxaContextParams>;
14
18
  export type LyxaContext = Awaited<ReturnType<typeof createLyxaContext>>;
@@ -5,7 +5,10 @@ const createLyxaContext = async (opts) => {
5
5
  return {
6
6
  ...opts,
7
7
  entity: undefined,
8
- requestId: undefined
8
+ requestId: undefined,
9
+ tokenType: undefined,
10
+ usedRefreshToken: false,
11
+ tokenRenewed: false
9
12
  };
10
13
  };
11
14
  exports.createLyxaContext = createLyxaContext;
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","sourceRoot":"/","sources":["libraries/trpc/context.ts"],"names":[],"mappings":";;;AAeO,MAAM,iBAAiB,GAAG,KAAK,EAAE,IAA8B,EAAyD,EAAE;IAC7H,OAAO;QACH,GAAG,IAAI;QACP,MAAM,EAAE,SAAS;QACjB,SAAS,EAAE,SAAS;KACvB,CAAC;AACN,CAAC,CAAA;AANY,QAAA,iBAAiB,qBAM7B","sourcesContent":["import {CreateHTTPContextOptions} from \"@trpc/server/dist/adapters/standalone\";\r\nimport {AuthEntityType} from \"../auth\";\r\nimport jwt from \"jsonwebtoken\";\r\n\r\nexport interface EntityContext extends jwt.JwtPayload {\r\n id: string,\r\n type: AuthEntityType,\r\n parameters: Record<string, unknown>\r\n}\r\n\r\nexport interface LyxaContextParams {\r\n entity?: EntityContext,\r\n requestId?: string,\r\n}\r\n\r\nexport const createLyxaContext = async (opts: CreateHTTPContextOptions): Promise<CreateHTTPContextOptions & LyxaContextParams> => {\r\n return {\r\n ...opts,\r\n entity: undefined,\r\n requestId: undefined\r\n };\r\n}\r\n\r\nexport type LyxaContext = Awaited<ReturnType<typeof createLyxaContext>>;\r\n"]}
1
+ {"version":3,"file":"context.js","sourceRoot":"/","sources":["libraries/trpc/context.ts"],"names":[],"mappings":";;;AAmBO,MAAM,iBAAiB,GAAG,KAAK,EAAE,IAA8B,EAAyD,EAAE;IAC7H,OAAO;QACH,GAAG,IAAI;QACP,MAAM,EAAE,SAAS;QACjB,SAAS,EAAE,SAAS;QACpB,SAAS,EAAE,SAAS;QACpB,gBAAgB,EAAE,KAAK;QACvB,YAAY,EAAE,KAAK;KACtB,CAAC;AACN,CAAC,CAAA;AATY,QAAA,iBAAiB,qBAS7B","sourcesContent":["import { CreateHTTPContextOptions } from \"@trpc/server/dist/adapters/standalone\";\r\nimport { AuthEntityType } from \"../auth\";\r\nimport jwt from \"jsonwebtoken\";\r\nimport { TokenType } from \"../auth\";\r\n\r\nexport interface EntityContext extends jwt.JwtPayload {\r\n id: string,\r\n type: AuthEntityType,\r\n parameters: Record<string, unknown>\r\n}\r\n\r\nexport interface LyxaContextParams {\r\n entity?: EntityContext,\r\n requestId?: string,\r\n tokenType?: TokenType,\r\n usedRefreshToken?: boolean,\r\n tokenRenewed?: boolean\r\n}\r\n\r\nexport const createLyxaContext = async (opts: CreateHTTPContextOptions): Promise<CreateHTTPContextOptions & LyxaContextParams> => {\r\n return {\r\n ...opts,\r\n entity: undefined,\r\n requestId: undefined,\r\n tokenType: undefined,\r\n usedRefreshToken: false,\r\n tokenRenewed: false\r\n };\r\n}\r\n\r\nexport type LyxaContext = Awaited<ReturnType<typeof createLyxaContext>>;\r\n"]}
@@ -1,5 +1,10 @@
1
- import { AuthEntityType } from "../../auth";
2
- export declare function createAuthenticatedProcedure(entityTypes: AuthEntityType[]): import("@trpc/server").ProcedureBuilder<{
1
+ import { AuthEntityType, TokenType } from "../../auth";
2
+ interface AuthOptions {
3
+ entityTypes: AuthEntityType[];
4
+ allowRefreshToken?: boolean;
5
+ autoRefresh?: boolean;
6
+ }
7
+ export declare function createAuthenticatedProcedure(options: AuthOptions): import("@trpc/server").ProcedureBuilder<{
3
8
  _config: import("@trpc/server").RootConfig<{
4
9
  ctx: import("@trpc/server/dist/adapters/standalone").CreateHTTPContextOptions & import("../context").LyxaContextParams;
5
10
  meta: object;
@@ -10,11 +15,15 @@ export declare function createAuthenticatedProcedure(entityTypes: AuthEntityType
10
15
  _ctx_out: {
11
16
  requestId: string | undefined;
12
17
  res: import("http").ServerResponse<import("http").IncomingMessage>;
18
+ tokenType: TokenType | undefined;
13
19
  entity: import("../context").EntityContext | undefined;
14
20
  req: import("http").IncomingMessage;
21
+ usedRefreshToken: boolean | undefined;
22
+ tokenRenewed: boolean | undefined;
15
23
  };
16
24
  _input_in: typeof import("@trpc/server").unsetMarker;
17
25
  _input_out: typeof import("@trpc/server").unsetMarker;
18
26
  _output_in: typeof import("@trpc/server").unsetMarker;
19
27
  _output_out: typeof import("@trpc/server").unsetMarker;
20
28
  }>;
29
+ export {};
@@ -4,21 +4,66 @@ exports.createAuthenticatedProcedure = createAuthenticatedProcedure;
4
4
  const server_1 = require("@trpc/server");
5
5
  const index_1 = require("../index");
6
6
  const auth_1 = require("../../auth");
7
- function createAuthenticatedProcedure(entityTypes) {
7
+ function createAuthenticatedProcedure(options) {
8
+ const { entityTypes, allowRefreshToken = true, autoRefresh = true } = options;
8
9
  return index_1.publicProcedure.use(async ({ ctx, next }) => {
9
10
  const authHeader = ctx.req.headers["authorization"];
11
+ const refreshTokenHeader = ctx.req.headers["x-refresh-token"];
10
12
  if (!authHeader) {
11
- throw new server_1.TRPCError({ code: "UNAUTHORIZED", message: "No header" });
13
+ throw new server_1.TRPCError({ code: "UNAUTHORIZED", message: "No authorization header" });
12
14
  }
13
- const token = authHeader.replace(/^Bearer\s+/, '');
14
- if (!token) {
15
- throw new server_1.TRPCError({ code: 'UNAUTHORIZED', message: 'Missing token' });
15
+ const accessToken = authHeader.replace(/^Bearer\s+/, '');
16
+ let refreshToken = "";
17
+ if (refreshTokenHeader) {
18
+ refreshToken = typeof refreshTokenHeader === 'string'
19
+ ? refreshTokenHeader.replace(/^Bearer\s+/, '')
20
+ : refreshTokenHeader[0].replace(/^Bearer\s+/, '');
16
21
  }
17
- const jwtToken = await (0, auth_1.verifyToken)(entityTypes, token);
18
- if (!jwtToken) {
19
- throw new server_1.TRPCError({ code: 'UNAUTHORIZED', message: 'Could not decode token based on given types' });
22
+ if (!accessToken) {
23
+ throw new server_1.TRPCError({ code: 'UNAUTHORIZED', message: 'Missing access token' });
24
+ }
25
+ let entity = await (0, auth_1.verifyAccessToken)(entityTypes, accessToken);
26
+ if (entity) {
27
+ ctx.entity = entity;
28
+ ctx.tokenType = auth_1.TokenType.ACCESS;
29
+ ctx.usedRefreshToken = false;
30
+ ctx.tokenRenewed = false;
31
+ return next({ ctx });
32
+ }
33
+ if (!allowRefreshToken || !refreshToken) {
34
+ throw new server_1.TRPCError({
35
+ code: 'UNAUTHORIZED',
36
+ message: 'Invalid access token'
37
+ });
38
+ }
39
+ const renewalResult = await (0, auth_1.handleTokenRenewal)(refreshToken);
40
+ if (!renewalResult.success) {
41
+ const errorMessage = renewalResult.tokenStatus === 'revoked'
42
+ ? 'Refresh token has been revoked'
43
+ : 'Invalid refresh token';
44
+ throw new server_1.TRPCError({
45
+ code: 'UNAUTHORIZED',
46
+ message: errorMessage
47
+ });
48
+ }
49
+ if (!entityTypes.includes(renewalResult.entity.type)) {
50
+ throw new server_1.TRPCError({
51
+ code: 'UNAUTHORIZED',
52
+ message: 'Unauthorized entity type'
53
+ });
54
+ }
55
+ ctx.entity = renewalResult.entity;
56
+ ctx.tokenType = auth_1.TokenType.REFRESH;
57
+ ctx.usedRefreshToken = true;
58
+ ctx.tokenRenewed = renewalResult.tokenStatus === 'expired';
59
+ if (autoRefresh && renewalResult.newTokens) {
60
+ if (renewalResult.newTokens.accessToken) {
61
+ ctx.res.setHeader('X-New-Access-Token', renewalResult.newTokens.accessToken);
62
+ }
63
+ if (renewalResult.newTokens.refreshToken) {
64
+ ctx.res.setHeader('X-New-Refresh-Token', renewalResult.newTokens.refreshToken);
65
+ }
20
66
  }
21
- ctx.entity = jwtToken;
22
67
  return next({ ctx });
23
68
  });
24
69
  }
@@ -1 +1 @@
1
- {"version":3,"file":"auth.js","sourceRoot":"/","sources":["libraries/trpc/middlewares/auth.ts"],"names":[],"mappings":";;AAQA,oEAwBC;AAhCD,yCAAuC;AACvC,oCAAyC;AACzC,qCAAuD;AAMvD,SAAgB,4BAA4B,CAAC,WAA6B;IACtE,OAAO,uBAAe,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE;QAC/C,MAAM,UAAU,GAAG,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;QAEpD,IAAI,CAAC,UAAU,EAAE,CAAC;YACd,MAAM,IAAI,kBAAS,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC,CAAC;QACxE,CAAC;QAED,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;QAEnD,IAAI,CAAC,KAAK,EAAE,CAAC;YACT,MAAM,IAAI,kBAAS,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,eAAe,EAAE,CAAC,CAAC;QAC5E,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,IAAA,kBAAW,EAAC,WAAW,EAAE,KAAK,CAAC,CAAC;QAEvD,IAAG,CAAC,QAAQ,EAAE,CAAC;YACX,MAAM,IAAI,kBAAS,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,6CAA6C,EAAE,CAAC,CAAC;QAC1G,CAAC;QAED,GAAG,CAAC,MAAM,GAAG,QAAQ,CAAC;QAEtB,OAAO,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC;IACzB,CAAC,CAAC,CAAC;AACP,CAAC","sourcesContent":["import {TRPCError} from '@trpc/server';\r\nimport {publicProcedure} from \"../index\";\r\nimport {AuthEntityType, verifyToken} from \"../../auth\";\r\n\r\n/**\r\n * A factory that creates an authenticated procedure, optionally restricting\r\n * the entity to a list of specific types (e.g. AuthEntityType.USER).\r\n */\r\nexport function createAuthenticatedProcedure(entityTypes: AuthEntityType[]) {\r\n return publicProcedure.use(async ({ ctx, next }) => {\r\n const authHeader = ctx.req.headers[\"authorization\"];\r\n\r\n if (!authHeader) {\r\n throw new TRPCError({ code: \"UNAUTHORIZED\", message: \"No header\" });\r\n }\r\n\r\n const token = authHeader.replace(/^Bearer\\s+/, '');\r\n\r\n if (!token) {\r\n throw new TRPCError({ code: 'UNAUTHORIZED', message: 'Missing token' });\r\n }\r\n\r\n const jwtToken = await verifyToken(entityTypes, token);\r\n\r\n if(!jwtToken) {\r\n throw new TRPCError({ code: 'UNAUTHORIZED', message: 'Could not decode token based on given types' });\r\n }\r\n\r\n ctx.entity = jwtToken;\r\n\r\n return next({ ctx });\r\n });\r\n}\r\n"]}
1
+ {"version":3,"file":"auth.js","sourceRoot":"/","sources":["libraries/trpc/middlewares/auth.ts"],"names":[],"mappings":"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","sourcesContent":["import { TRPCError } from '@trpc/server';\r\nimport { publicProcedure } from \"../index\";\r\nimport {\r\n AuthEntityType,\r\n verifyAccessToken,\r\n validateRefreshToken,\r\n signAccessToken,\r\n TokenType, refreshTokens, handleTokenRenewal\r\n} from \"../../auth\";\r\n\r\ninterface AuthOptions {\r\n entityTypes: AuthEntityType[];\r\n allowRefreshToken?: boolean;\r\n autoRefresh?: boolean;\r\n}\r\n\r\n/**\r\n * A factory that creates an authenticated procedure, optionally allowing\r\n * refresh tokens and auto-refreshing expired access tokens.\r\n */\r\nexport function createAuthenticatedProcedure(options: AuthOptions) {\r\n const { entityTypes, allowRefreshToken = true, autoRefresh = true } = options;\r\n\r\n return publicProcedure.use(async ({ ctx, next }) => {\r\n const authHeader = ctx.req.headers[\"authorization\"];\r\n const refreshTokenHeader = ctx.req.headers[\"x-refresh-token\"];\r\n\r\n if (!authHeader) {\r\n throw new TRPCError({ code: \"UNAUTHORIZED\", message: \"No authorization header\" });\r\n }\r\n\r\n const accessToken = authHeader.replace(/^Bearer\\s+/, '');\r\n let refreshToken = \"\";\r\n\r\n if (refreshTokenHeader) {\r\n refreshToken = typeof refreshTokenHeader === 'string'\r\n ? refreshTokenHeader.replace(/^Bearer\\s+/, '')\r\n : refreshTokenHeader[0].replace(/^Bearer\\s+/, '');\r\n }\r\n\r\n if (!accessToken) {\r\n throw new TRPCError({ code: 'UNAUTHORIZED', message: 'Missing access token' });\r\n }\r\n\r\n let entity = await verifyAccessToken(entityTypes, accessToken);\r\n\r\n if (entity) {\r\n ctx.entity = entity;\r\n ctx.tokenType = TokenType.ACCESS;\r\n ctx.usedRefreshToken = false;\r\n ctx.tokenRenewed = false;\r\n return next({ ctx });\r\n }\r\n\r\n if (!allowRefreshToken || !refreshToken) {\r\n throw new TRPCError({\r\n code: 'UNAUTHORIZED',\r\n message: 'Invalid access token'\r\n });\r\n }\r\n\r\n const renewalResult = await handleTokenRenewal(refreshToken);\r\n\r\n if (!renewalResult.success) {\r\n const errorMessage = renewalResult.tokenStatus === 'revoked'\r\n ? 'Refresh token has been revoked'\r\n : 'Invalid refresh token';\r\n\r\n throw new TRPCError({\r\n code: 'UNAUTHORIZED',\r\n message: errorMessage\r\n });\r\n }\r\n\r\n if (!entityTypes.includes(renewalResult.entity!.type)) {\r\n throw new TRPCError({\r\n code: 'UNAUTHORIZED',\r\n message: 'Unauthorized entity type'\r\n });\r\n }\r\n\r\n ctx.entity = renewalResult.entity;\r\n ctx.tokenType = TokenType.REFRESH;\r\n ctx.usedRefreshToken = true;\r\n\r\n ctx.tokenRenewed = renewalResult.tokenStatus === 'expired';\r\n\r\n if (autoRefresh && renewalResult.newTokens) {\r\n if (renewalResult.newTokens.accessToken) {\r\n ctx.res.setHeader('X-New-Access-Token', renewalResult.newTokens.accessToken);\r\n }\r\n\r\n if (renewalResult.newTokens.refreshToken) {\r\n ctx.res.setHeader('X-New-Refresh-Token', renewalResult.newTokens.refreshToken);\r\n }\r\n }\r\n\r\n return next({ ctx });\r\n });\r\n}\r\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lyxa.ai/core",
3
- "version": "1.0.10",
3
+ "version": "1.0.12",
4
4
  "description": "The Core system of the Lyxa services.",
5
5
  "author": "elie <42282499+Internalizable@users.noreply.github.com>",
6
6
  "license": "MIT",