@culturefy/shared 1.0.32 → 1.0.34

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.
Files changed (33) hide show
  1. package/build/cjs/enums/secretKeys.enum.js +1 -0
  2. package/build/cjs/enums/secretKeys.enum.js.map +1 -1
  3. package/build/cjs/middlewares/token-validation.js +15 -462
  4. package/build/cjs/middlewares/token-validation.js.map +1 -1
  5. package/build/cjs/models/user.model.js +272 -366
  6. package/build/cjs/models/user.model.js.map +1 -1
  7. package/build/cjs/service/user.service.js +89 -84
  8. package/build/cjs/service/user.service.js.map +1 -1
  9. package/build/esm/enums/secretKeys.enum.js +1 -0
  10. package/build/esm/enums/secretKeys.enum.js.map +1 -1
  11. package/build/esm/middlewares/token-validation.js +16 -463
  12. package/build/esm/middlewares/token-validation.js.map +1 -1
  13. package/build/esm/models/user.model.js +272 -366
  14. package/build/esm/models/user.model.js.map +1 -1
  15. package/build/esm/service/user.service.js +89 -85
  16. package/build/esm/service/user.service.js.map +1 -1
  17. package/build/src/enums/secretKeys.enum.d.ts +2 -1
  18. package/build/src/enums/secretKeys.enum.js +1 -0
  19. package/build/src/enums/secretKeys.enum.js.map +1 -1
  20. package/build/src/middlewares/token-validation.d.ts +1 -1
  21. package/build/src/middlewares/token-validation.js +16 -314
  22. package/build/src/middlewares/token-validation.js.map +1 -1
  23. package/build/src/models/user.model.d.ts +0 -5
  24. package/build/src/models/user.model.js +266 -263
  25. package/build/src/models/user.model.js.map +1 -1
  26. package/build/src/service/user.service.d.ts +0 -6
  27. package/build/src/service/user.service.js +1 -107
  28. package/build/src/service/user.service.js.map +1 -1
  29. package/package.json +1 -1
  30. package/src/enums/secretKeys.enum.ts +1 -0
  31. package/src/middlewares/token-validation.ts +19 -432
  32. package/src/models/user.model.ts +265 -265
  33. package/src/service/user.service.ts +79 -77
@@ -1,118 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.UserService = void 0;
4
- const tslib_1 = require("tslib");
5
- const mongoose_1 = require("mongoose");
6
- const user_model_1 = require("../models/user.model");
7
4
  const shared_1 = require("@culturefy/shared");
8
5
  class UserService extends shared_1.Initializers {
6
+ // private readonly schema = UserModel.schema;
9
7
  constructor(context, dbUrl) {
10
8
  super(context, dbUrl);
11
- this.schema = user_model_1.UserModel.schema;
12
- }
13
- getModel() {
14
- const connection = this.getConnection();
15
- if (!connection) {
16
- throw new Error('Database connection not established');
17
- }
18
- return connection.model(user_model_1.UserModel.modelName, this.schema);
19
- }
20
- getUserById(userId) {
21
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
22
- try {
23
- let model = this.getModel();
24
- this.context.log("UserId:", JSON.stringify(userId));
25
- // Handle both string and SchemaObjectId inputs
26
- let objectId;
27
- if (typeof userId === 'string') {
28
- objectId = new mongoose_1.Types.ObjectId(userId);
29
- }
30
- else {
31
- // If it's already a SchemaObjectId, extract the string value
32
- const userIdString = userId.path || userId.toString();
33
- objectId = new mongoose_1.Types.ObjectId(userIdString);
34
- }
35
- this.context.log("ObjectId:", JSON.stringify(objectId));
36
- const user = yield model.findById(objectId);
37
- this.context.log("User:", JSON.stringify(user));
38
- return user;
39
- }
40
- catch (error) {
41
- this.context.error("Error in getUserById", error);
42
- throw error;
43
- }
44
- });
45
- }
46
- getUserByBusinessId(businessId, email) {
47
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
48
- try {
49
- let model = this.getModel();
50
- this.context.log("BusinessId:", JSON.stringify(businessId));
51
- this.context.log("Email:", JSON.stringify(email));
52
- if (!businessId)
53
- return null;
54
- if (!email)
55
- return null;
56
- businessId = businessId.toLowerCase();
57
- businessId = businessId.trim();
58
- email = email.toLowerCase();
59
- email = email.trim();
60
- // Handle both string and SchemaObjectId inputs
61
- let objectId;
62
- if (typeof businessId === 'string') {
63
- objectId = new mongoose_1.Types.ObjectId(businessId);
64
- }
65
- else {
66
- // If it's already a SchemaObjectId, extract the string value
67
- const businessIdString = businessId.path || businessId.toString();
68
- objectId = new mongoose_1.Types.ObjectId(businessIdString);
69
- }
70
- this.context.log("ObjectId:", JSON.stringify(objectId));
71
- const user = yield model.findOne({ businessId: objectId, email: email });
72
- this.context.log("User:", JSON.stringify(user));
73
- return user;
74
- }
75
- catch (error) {
76
- this.context.error("Error in getUserByBusinessId", error);
77
- throw error;
78
- }
79
- });
80
- }
81
- getUserByEmail(email) {
82
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
83
- try {
84
- let model = this.getModel();
85
- this.context.log("Email:", JSON.stringify(email));
86
- email = email.toLowerCase();
87
- email = email.trim();
88
- const user = yield model.findOne({ email: email });
89
- this.context.log("User:", JSON.stringify(user));
90
- return user;
91
- }
92
- catch (error) {
93
- this.context.error("Error in getUserByEmail", error);
94
- throw error;
95
- }
96
- });
97
9
  }
98
10
  }
99
11
  exports.UserService = UserService;
100
- tslib_1.__decorate([
101
- shared_1.WithDb,
102
- tslib_1.__metadata("design:type", Function),
103
- tslib_1.__metadata("design:paramtypes", [String]),
104
- tslib_1.__metadata("design:returntype", Promise)
105
- ], UserService.prototype, "getUserById", null);
106
- tslib_1.__decorate([
107
- shared_1.WithDb,
108
- tslib_1.__metadata("design:type", Function),
109
- tslib_1.__metadata("design:paramtypes", [String, String]),
110
- tslib_1.__metadata("design:returntype", Promise)
111
- ], UserService.prototype, "getUserByBusinessId", null);
112
- tslib_1.__decorate([
113
- shared_1.WithDb,
114
- tslib_1.__metadata("design:type", Function),
115
- tslib_1.__metadata("design:paramtypes", [String]),
116
- tslib_1.__metadata("design:returntype", Promise)
117
- ], UserService.prototype, "getUserByEmail", null);
118
12
  //# sourceMappingURL=user.service.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"user.service.js","sourceRoot":"","sources":["../../../src/service/user.service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"user.service.js","sourceRoot":"","sources":["../../../src/service/user.service.ts"],"names":[],"mappings":";;;AAKA,8CAAyD;AAEzD,MAAa,WAAY,SAAQ,qBAAY;IAEzC,8CAA8C;IAC9C,YAAY,OAA0B,EAAE,KAAY;QAChD,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAC1B,CAAC;CAwFJ;AA7FD,kCA6FC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@culturefy/shared",
3
3
  "description": "Shared utilities for culturefy serverless services",
4
- "version": "1.0.32",
4
+ "version": "1.0.34",
5
5
  "main": "build/cjs/index.js",
6
6
  "module": "build/esm/index.js",
7
7
  "types": "build/src/index.d.ts",
@@ -26,4 +26,5 @@ export enum AzureSecretKeysEnum {
26
26
  HMS_ACCESS_KEY="MEETING-HMS-ACCESS-KEY-APP-SECRET",
27
27
  MEETING_ROOM_APP_SECRET="MEETING-ROOM-APP-SECRET",
28
28
  BASE_DB_CLUSTER_CONNECTING_STRING_CHAT = "BASE-DB-CLUSTER-CONNECTING-STRING-CHAT",
29
+ AUTH_SERVICE_AUTHENTICATION_URL = "AUTH-SERVICE-AUTHENTICATION-URL",
29
30
  }
@@ -1,10 +1,8 @@
1
1
 
2
2
  import IUser from "../models/user.model";
3
3
  import { AzureSecretKeysEnum } from "../enums";
4
- import { UserService } from "../service/user.service";
4
+ import { getAzureVaultSecretByKey } from "../utils";
5
5
  import { HttpRequest, InvocationContext } from "@azure/functions";
6
- import { KeycloakAdminService } from "../service/keycloak.service";
7
- import { getAzureVaultSecretByKey, parseCookies, verifyJsonWebToken } from "../utils";
8
6
 
9
7
  interface TokenValidationResult {
10
8
  status: boolean;
@@ -20,437 +18,26 @@ interface TokenValidationResult {
20
18
  };
21
19
  }
22
20
 
23
- interface TokenClaims {
24
- iat?: number;
25
- exp?: number;
26
- sub: string;
27
- azp: string;
28
- iss: string;
29
- email: string;
30
- }
31
-
32
- export async function tokenValidation(request: HttpRequest, domain: string, context: InvocationContext): Promise<TokenValidationResult> {
33
- try {
34
- let cookies = parseCookies(request, context);
35
- let accessToken = cookies["culturefy-auth-token"];
36
- let refreshToken = cookies["culturefy-refresh-token"];
37
-
38
- let expiresIn, refreshExpiresIn;
39
-
40
- if (!accessToken) return { status: false, message: "Access token is required" };
41
-
42
- const keycloakService = await initializeKeycloakService(context);
43
-
44
- const tokenValidation = await validateToken(accessToken, context);
45
-
46
- if (!tokenValidation.success) {
47
- if (tokenValidation.expired) {
48
- const { data } = tokenValidation;
49
- if (!data) return { status: false, message: "Invalid access token." };
50
-
51
- let { userId, clientId, realm, email } = data;
52
-
53
- if (!clientId) return { status: false, message: "Invalid access token provided" };
54
- if (!userId) return { status: false, message: "Invalid access token provided" };
55
- if (!realm) return { status: false, message: "Invalid access token provided" };
56
-
57
- context.log("Refreshing token for user:", JSON.stringify({ userId, clientId, realm, email }));
58
-
59
- const refreshTokenResponse = await handleTokenRefresh(keycloakService, refreshToken, userId, clientId, realm, email, domain, context);
60
- if (!refreshTokenResponse.success) return { status: false, message: refreshTokenResponse.message };
61
-
62
- const { data: refreshTokenData } = refreshTokenResponse;
63
- if (!refreshTokenData) return { status: false, message: "Invalid refresh token." };
64
-
65
- context.log("Refreshed token for user:", JSON.stringify({ userId, clientId, realm, email }));
66
-
67
- accessToken = refreshTokenData.access_token;
68
- refreshToken = refreshTokenData.refresh_token;
69
- expiresIn = refreshTokenData.expires_in;
70
- refreshExpiresIn = refreshTokenData.refresh_expires_in;
71
-
72
- } else {
73
- return { status: false, message: tokenValidation.message };
74
- }
75
- }
76
-
77
- const { data } = tokenValidation;
78
-
79
- if (!data) return { status: false, message: "Invalid access token." };
80
-
81
- let { userId, clientId, realm, email } = data;
82
-
83
- if (!clientId) return { status: false, message: "Invalid access token provided" };
84
- if (!userId) return { status: false, message: "Invalid access token provided" };
85
- if (!realm) return { status: false, message: "Invalid access token provided" };
86
- if (!email) return { status: false, message: "Invalid access token provided" };
87
-
88
- context.log("Validating user:", JSON.stringify({ userId, clientId, realm, email }));
89
-
90
- let verifyFromDb;
91
- let userInfo;
92
-
93
- if(domain === "accounts.culturefy.app") {
94
- const introspectionValid = await validateTokenIntrospection(
95
- keycloakService,
96
- accessToken,
97
- realm,
98
- clientId,
99
- domain,
100
- context
101
- );
102
-
103
- if (!introspectionValid) return { status: false, message: "Token introspection failed" };
104
- context.log("Token introspection successful");
105
-
106
- realm = "superadmin";
107
- clientId = "cfy-superadmin-web";
108
-
109
- userInfo = await keycloakService.getUserByToken(realm, accessToken);
110
- context.log("User info-1:", JSON.stringify(userInfo));
111
-
112
- if(!userInfo.email) return { status: false, message: "User email not found" };
113
- if(userInfo.email !== email) return { status: false, message: "User email does not match" };
114
- email = userInfo.email;
115
- verifyFromDb = await validateUserByEmail(email, context);
116
- if (!verifyFromDb.success) return { status: false, message: verifyFromDb.message };
117
- } else {
118
- clientId = "cfy-web";
119
- verifyFromDb = await validateUserByRealm(realm, email, context);
120
- }
121
-
122
- if (!verifyFromDb.success) return { status: false, message: verifyFromDb.message };
123
-
124
- const user = verifyFromDb.user;
125
-
126
- if (!user) return { status: false, message: "User not found." };
127
- context.log("User:", JSON.stringify(user));
128
-
129
- const introspectionValid = await validateTokenIntrospection(
130
- keycloakService,
131
- accessToken,
132
- realm,
133
- clientId,
134
- domain,
135
- context
136
- );
137
-
138
- if (!introspectionValid) return { status: false, message: "Token introspection failed" };
139
- context.log("Token introspection successful");
140
-
141
- if (domain === "accounts.culturefy.app") {
142
- realm = "superadmin";
143
- clientId = "cfy-superadmin-web";
144
- } else {
145
- if(!user.businessId) return { status: false, message: "User not found" };
146
- realm = user.businessId.toString();
147
- clientId = "cfy-web";
148
- }
149
-
150
- if(!userInfo) {
151
- userInfo = await keycloakService.getUserByToken(realm, accessToken);
152
- context.log("User info-2:", JSON.stringify(userInfo));
153
- }
154
-
155
- if(!userInfo) return { status: false, message: "User info not found" };
156
-
157
- let updatedCookies: {
158
- access_token: string;
159
- refresh_token: string;
160
- expires_in?: number;
161
- refresh_expires_in?: number;
162
- } = {
163
- access_token: accessToken,
164
- refresh_token: refreshToken,
165
- };
166
-
167
- if(expiresIn) updatedCookies.expires_in = expiresIn;
168
- if(refreshExpiresIn) updatedCookies.refresh_expires_in = refreshExpiresIn;
169
-
170
- return {
171
- status: true,
172
- message: "Token is valid",
173
- data: {
174
- cookies: updatedCookies,
175
- user: user
176
- }
177
- };
178
-
179
- } catch (error) {
180
- context.error("Culturefy token validation error:", error);
181
- return { status: false, message: "Internal server error during culturefy token validation" };
182
- }
183
- }
184
-
185
- async function initializeKeycloakService(context: InvocationContext): Promise<KeycloakAdminService> {
186
- const [keycloakBaseUrl, keycloakAdminClientId, keycloakAdminClientSecret] = await Promise.all([
187
- getAzureVaultSecretByKey(
188
- context,
189
- process.env.AZURE_KEY_VAULT_NAME || "",
190
- AzureSecretKeysEnum.KEYCLOAK_BASE_URL
191
- ),
192
- getAzureVaultSecretByKey(
193
- context,
194
- process.env.AZURE_KEY_VAULT_NAME || "",
195
- AzureSecretKeysEnum.KEYCLOAK_ADMIN_CLIENT_ID
196
- ),
197
- getAzureVaultSecretByKey(
198
- context,
199
- process.env.AZURE_KEY_VAULT_NAME || "",
200
- AzureSecretKeysEnum.KEYCLOAK_ADMIN_CLIENT_SECRET
201
- )
202
- ]);
203
-
204
- return new KeycloakAdminService(context, {
205
- baseUrl: keycloakBaseUrl as string,
206
- adminClientId: keycloakAdminClientId as string,
207
- adminClientSecret: keycloakAdminClientSecret as string
208
- });
209
- }
210
-
211
- async function validateToken(
212
- accessToken: string,
213
- context: InvocationContext
214
- ): Promise<{
215
- success: boolean;
216
- message: string;
217
- expired?: boolean;
218
- data?: {
219
- userId: string;
220
- clientId: string;
221
- realm: string;
222
- email: string;
223
- };
224
- }> {
225
- const currentTime = Math.floor(Date.now() / 1000);
226
-
227
- const decoded = verifyJsonWebToken(accessToken);
228
-
229
- if (!decoded) return { success: false, message: "Invalid access token format" };
230
-
231
- let { iat, exp, sub: userId, azp: clientId, iss, email } = decoded as TokenClaims;
232
-
233
- if (!userId || !clientId || !iss) return { success: false, message: "Access token missing required claims (sub or azp or iss)" };
234
- context.log("Access token claims:", JSON.stringify(decoded));
235
-
236
- let realm = iss.split("/")[iss.split("/").length - 1];
237
- if(!realm) return { success: false, message: "Access token missing required claims (iss)" };
238
-
239
- if (exp && exp < currentTime) return { success: false, message: "Access token expired and refresh token not provided", expired: true, data: { userId, clientId, realm, email } };
240
-
241
- if (iat && iat > currentTime) return { success: false, message: "Invalid token issuance time" };
242
-
243
- return {
244
- success: true,
245
- message: "Token is valid",
246
- data: { userId, clientId, realm, email }
247
- };
248
- }
249
-
250
- async function handleTokenRefresh(
251
- keycloakService: KeycloakAdminService,
252
- refreshToken: string,
253
- userId: string,
254
- clientId: string,
255
- realm: string,
256
- email: string,
257
- domain: string,
258
- context: InvocationContext
259
- ): Promise<{
260
- success: boolean;
261
- message: string;
262
- data?: {
263
- access_token: string;
264
- expires_in: number;
265
- refresh_token: string;
266
- refresh_expires_in: number;
267
- };
268
- }> {
269
- const currentTime = Math.floor(Date.now() / 1000);
270
-
271
- if(!clientId) return { success: false, message: "Client ID is missing" };
272
- if(!userId) return { success: false, message: "User ID is missing" };
273
- if(!realm) return { success: false, message: "Realm is missing" };
274
- if(!refreshToken) return { success: false, message: "Refresh token is missing" };
275
- if(!email) return { success: false, message: "Email is missing" };
276
- if(!domain) return { success: false, message: "Domain is missing" };
277
-
278
- context.info("values:", {clientId, userId, realm, email, domain});
279
-
280
- const refreshTokenDecoded = verifyJsonWebToken(refreshToken);
281
- if (!refreshTokenDecoded) return { success: false, message: "Invalid refresh token format" };
282
-
283
- let { iss: refreshIss } = refreshTokenDecoded as TokenClaims;
284
- refreshIss = refreshIss.split("/")[refreshIss.split("/").length - 1];
285
-
286
- const { iat: refreshIat, exp: refreshExp, sub: refreshUserId, azp: refreshClientId, email: refreshEmail } = refreshTokenDecoded as TokenClaims;
287
-
288
- context.info("refreshTokenDecoded:", JSON.stringify({refreshUserId, refreshClientId, refreshIss, refreshEmail}));
289
-
290
- if (!refreshUserId || !refreshClientId || !refreshIss) return { success: false, message: "Refresh token missing required claims (sub or azp or iss)" };
291
-
292
- if (refreshExp && refreshExp < currentTime) return { success: false, message: "Refresh token has expired" };
293
-
294
- if (refreshIat && refreshIat > currentTime) return { success: false, message: "Invalid refresh token issuance time" };
295
-
296
- if (refreshUserId !== userId || refreshClientId !== clientId || refreshIss !== realm || refreshEmail !== email) return { success: false, message: "Refresh token does not match access token user" };
297
-
298
- if(domain === "accounts.culturefy.app") {
299
- realm = "superadmin";
300
- clientId = "cfy-superadmin-web";
301
- } else {
302
- realm = realm;
303
- clientId = "cfy-web";
304
- }
305
-
306
- const newToken = await keycloakService.refreshToken(realm, clientId, refreshToken);
307
- if (!newToken) return { success: false, message: "Failed to refresh access token" };
308
-
309
- const newTokenDecoded = verifyJsonWebToken(newToken.access_token);
310
- if (!newTokenDecoded) return { success: false, message: "Invalid new token format" };
311
-
312
- const { iat: newIat, exp: newExp, sub: newUserId, azp: newClientId, iss: newIss, email: newEmail } = newTokenDecoded as TokenClaims;
313
-
314
- if (!newUserId || !newClientId || !newIss || !newEmail) return { success: false, message: "New token missing required claims (sub or azp or iss or email)" };
315
-
316
- if (newExp && newExp < currentTime) return { success: false, message: "New token has expired" };
317
-
318
- if (newIat && newIat > currentTime) return { success: false, message: "Invalid new token issuance time" };
319
-
320
- context.info("Token refreshed successfully for user:", userId);
321
-
322
- return {
323
- success: true,
324
- message: "Token refreshed successfully",
325
- data: {
326
- access_token: newToken.access_token, expires_in: newToken.expires_in,
327
- refresh_token: newToken.refresh_token, refresh_expires_in: newToken.refresh_expires_in
328
- }
329
- };
330
- }
331
-
332
- async function validateUserByRealm(
333
- realm: string,
334
- email: string,
335
- context: InvocationContext
336
- ): Promise<{
337
- success: boolean;
338
- message: string;
339
- user?: any;
340
- }> {
341
- try {
342
- const authDbUrl = await getAzureVaultSecretByKey(
343
- context,
344
- process.env.AZURE_KEY_VAULT_NAME || "",
345
- AzureSecretKeysEnum.DB_CONNECTING_STRING_USER
346
- );
347
-
348
- const userService = new UserService(context, authDbUrl);
349
-
350
- let user = null;
351
- context.log("Getting user by realm:", realm);
352
- try {
353
- user = await userService.getUserByBusinessId(realm, email);
354
- } catch (err: any) {
355
- context.error(`Failed to get user by realm:`, err);
356
- return { success: false, message: "User not found.." };
357
- }
358
- context.log("User:", JSON.stringify(user));
359
-
360
- if (!user) return { success: false, message: "User not found..." };
361
-
362
- await userService.disconnect();
363
-
364
- return { success: true, message: "User validation successful", user };
365
-
366
- } catch (error) {
367
- context.error("User validation error:", error);
368
- return { success: false, message: "Error validating user information" };
369
- }
370
- }
371
-
372
- async function validateUserByEmail(
373
- email: string,
374
- context: InvocationContext
375
- ): Promise<{
376
- success: boolean;
377
- message: string;
378
- user?: any;
379
- }> {
21
+ export async function tokenValidation(request: HttpRequest, context: InvocationContext): Promise<TokenValidationResult> {
380
22
  try {
381
- const authDbUrl = await getAzureVaultSecretByKey(
382
- context,
383
- process.env.AZURE_KEY_VAULT_NAME || "",
384
- AzureSecretKeysEnum.DB_CONNECTING_STRING_USER
385
- );
386
-
387
- const userService = new UserService(context, authDbUrl);
388
-
389
- let user = null;
390
- context.log("Getting user by email:", email);
391
- try {
392
- user = await userService.getUserByEmail(email);
393
- } catch (err: any) {
394
- context.error(`Failed to get user by email:`, err);
395
- return { success: false, message: "User not found.." };
23
+ const url = await getAzureVaultSecretByKey(context, process.env.AZURE_KEY_VAULT_NAME || "", AzureSecretKeysEnum.AUTH_SERVICE_AUTHENTICATION_URL);
24
+ if(!url) {
25
+ return { status: false, message: "Auth service authenticaion API url not found" };
396
26
  }
397
- context.log("User:", JSON.stringify(user));
398
-
399
- if (!user) return { success: false, message: "User not found..." };
400
-
401
- await userService.disconnect();
402
-
403
- return { success: true, message: "User validation successful", user };
404
-
405
- } catch (error) {
406
- context.error("User validation error:", error);
407
- return { success: false, message: "Error validating user information" };
408
- }
409
- }
410
-
411
- async function validateTokenIntrospection(
412
- keycloakService: KeycloakAdminService,
413
- token: string,
414
- realm: string,
415
- clientId: string,
416
- domain: string,
417
- context: InvocationContext
418
- ): Promise<boolean> {
419
- try {
420
- if(!realm) return false;
421
- if(!clientId) return false;
422
- if(!token) return false;
27
+ context.log(`Auth service authentication API url: ${url}`);
423
28
 
424
- if (domain === "accounts.culturefy.app") {
425
- realm = "superadmin";
426
- clientId = "cfy-superadmin-web";
427
- } else {
428
- realm = realm;
429
- clientId = "cfy-web";
430
- }
431
-
432
- context.info("Validating token with Keycloak introspection");
433
- const introspection = await keycloakService.introspectToken(realm, clientId, token);
434
-
435
- if (!introspection.active) {
436
- context.warn("Token introspection returned inactive token");
437
- return false;
438
- }
439
-
440
- context.info("Token introspection successful - token is active");
441
- return true;
442
- } catch (error: any) {
443
- context.error("Token introspection error:", error);
444
-
445
- if (error.message?.includes('Client not allowed')) {
446
- context.warn("Admin-cli client does not have introspection permissions - this is expected");
447
- return true;
448
- }
449
-
450
- if (error.message?.includes('Invalid token')) return false;
451
-
452
- if (error.message?.includes('Token is not active')) return false;
453
-
454
- return false;
29
+ const response = await fetch(url, {
30
+ method: "GET",
31
+ headers: request.headers
32
+ });
33
+ const data = await response.json();
34
+ return { status: true, message: "Token validation successful", data: {
35
+ cookies: data.cookies,
36
+ user: data.user
37
+ } };
38
+
39
+ } catch (error:any) {
40
+ context.error("Culturefy token validation error:", error);
41
+ return { status: false, message: error.message || "Internal server error during culturefy token validation" };
455
42
  }
456
43
  }