@memberjunction/server 2.90.0 → 2.91.0

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 (75) hide show
  1. package/dist/auth/AuthProviderFactory.d.ts +24 -0
  2. package/dist/auth/AuthProviderFactory.d.ts.map +1 -0
  3. package/dist/auth/AuthProviderFactory.js +82 -0
  4. package/dist/auth/AuthProviderFactory.js.map +1 -0
  5. package/dist/auth/BaseAuthProvider.d.ts +18 -0
  6. package/dist/auth/BaseAuthProvider.d.ts.map +1 -0
  7. package/dist/auth/BaseAuthProvider.js +42 -0
  8. package/dist/auth/BaseAuthProvider.js.map +1 -0
  9. package/dist/auth/IAuthProvider.d.ts +13 -0
  10. package/dist/auth/IAuthProvider.d.ts.map +1 -0
  11. package/dist/auth/IAuthProvider.js +2 -0
  12. package/dist/auth/IAuthProvider.js.map +1 -0
  13. package/dist/auth/__tests__/backward-compatibility.test.d.ts +2 -0
  14. package/dist/auth/__tests__/backward-compatibility.test.d.ts.map +1 -0
  15. package/dist/auth/__tests__/backward-compatibility.test.js +135 -0
  16. package/dist/auth/__tests__/backward-compatibility.test.js.map +1 -0
  17. package/dist/auth/index.d.ts +22 -7
  18. package/dist/auth/index.d.ts.map +1 -1
  19. package/dist/auth/index.js +65 -32
  20. package/dist/auth/index.js.map +1 -1
  21. package/dist/auth/initializeProviders.d.ts +2 -0
  22. package/dist/auth/initializeProviders.d.ts.map +1 -0
  23. package/dist/auth/initializeProviders.js +23 -0
  24. package/dist/auth/initializeProviders.js.map +1 -0
  25. package/dist/auth/providers/Auth0Provider.d.ts +9 -0
  26. package/dist/auth/providers/Auth0Provider.d.ts.map +1 -0
  27. package/dist/auth/providers/Auth0Provider.js +42 -0
  28. package/dist/auth/providers/Auth0Provider.js.map +1 -0
  29. package/dist/auth/providers/CognitoProvider.d.ts +9 -0
  30. package/dist/auth/providers/CognitoProvider.d.ts.map +1 -0
  31. package/dist/auth/providers/CognitoProvider.js +46 -0
  32. package/dist/auth/providers/CognitoProvider.js.map +1 -0
  33. package/dist/auth/providers/GoogleProvider.d.ts +9 -0
  34. package/dist/auth/providers/GoogleProvider.d.ts.map +1 -0
  35. package/dist/auth/providers/GoogleProvider.js +41 -0
  36. package/dist/auth/providers/GoogleProvider.js.map +1 -0
  37. package/dist/auth/providers/MSALProvider.d.ts +9 -0
  38. package/dist/auth/providers/MSALProvider.d.ts.map +1 -0
  39. package/dist/auth/providers/MSALProvider.js +42 -0
  40. package/dist/auth/providers/MSALProvider.js.map +1 -0
  41. package/dist/auth/providers/OktaProvider.d.ts +9 -0
  42. package/dist/auth/providers/OktaProvider.d.ts.map +1 -0
  43. package/dist/auth/providers/OktaProvider.js +42 -0
  44. package/dist/auth/providers/OktaProvider.js.map +1 -0
  45. package/dist/config.d.ts +97 -21
  46. package/dist/config.d.ts.map +1 -1
  47. package/dist/config.js +13 -6
  48. package/dist/config.js.map +1 -1
  49. package/dist/context.d.ts.map +1 -1
  50. package/dist/context.js +25 -17
  51. package/dist/context.js.map +1 -1
  52. package/dist/generated/generated.d.ts +3 -0
  53. package/dist/generated/generated.d.ts.map +1 -1
  54. package/dist/generated/generated.js +16 -0
  55. package/dist/generated/generated.js.map +1 -1
  56. package/dist/generic/ResolverBase.d.ts +1 -1
  57. package/dist/generic/ResolverBase.d.ts.map +1 -1
  58. package/dist/generic/ResolverBase.js +5 -4
  59. package/dist/generic/ResolverBase.js.map +1 -1
  60. package/package.json +39 -39
  61. package/src/auth/AuthProviderFactory.ts +152 -0
  62. package/src/auth/BaseAuthProvider.ts +71 -0
  63. package/src/auth/IAuthProvider.ts +49 -0
  64. package/src/auth/__tests__/backward-compatibility.test.ts +183 -0
  65. package/src/auth/index.ts +104 -36
  66. package/src/auth/initializeProviders.ts +31 -0
  67. package/src/auth/providers/Auth0Provider.ts +45 -0
  68. package/src/auth/providers/CognitoProvider.ts +50 -0
  69. package/src/auth/providers/GoogleProvider.ts +45 -0
  70. package/src/auth/providers/MSALProvider.ts +45 -0
  71. package/src/auth/providers/OktaProvider.ts +46 -0
  72. package/src/config.ts +14 -10
  73. package/src/context.ts +40 -17
  74. package/src/generated/generated.ts +10 -0
  75. package/src/generic/ResolverBase.ts +9 -4
package/src/config.ts CHANGED
@@ -102,6 +102,18 @@ const sqlLoggingSchema = z.object({
102
102
  sessionTimeout: z.number().optional().default(3600000), // 1 hour
103
103
  });
104
104
 
105
+ const authProviderSchema = z.object({
106
+ name: z.string(),
107
+ type: z.string(),
108
+ issuer: z.string(),
109
+ audience: z.string(),
110
+ jwksUri: z.string(),
111
+ clientId: z.string().optional(),
112
+ clientSecret: z.string().optional(),
113
+ tenantId: z.string().optional(),
114
+ domain: z.string().optional(),
115
+ }).passthrough(); // Allow additional provider-specific fields
116
+
105
117
  const configInfoSchema = z.object({
106
118
  userHandling: userHandlingInfoSchema,
107
119
  databaseSettings: databaseSettingsInfoSchema,
@@ -109,6 +121,7 @@ const configInfoSchema = z.object({
109
121
  restApiOptions: restApiOptionsSchema.optional().default({}),
110
122
  askSkip: askSkipInfoSchema.optional(),
111
123
  sqlLogging: sqlLoggingSchema.optional(),
124
+ authProviders: z.array(authProviderSchema).optional(),
112
125
 
113
126
  apiKey: z.string().optional(),
114
127
  baseUrl: z.string().default('http://localhost'),
@@ -131,17 +144,12 @@ const configInfoSchema = z.object({
131
144
  ___codeGenAPIPort: z.coerce.number().optional().default(3999),
132
145
  ___codeGenAPISubmissionDelay: z.coerce.number().optional().default(5000),
133
146
  graphqlRootPath: z.string().optional().default('/'),
134
- webClientID: z.string().optional(),
135
- tenantID: z.string().optional(),
136
147
  enableIntrospection: z.coerce.boolean().optional().default(false),
137
148
  websiteRunFromPackage: z.coerce.number().optional(),
138
149
  userEmailMap: z
139
150
  .string()
140
151
  .transform((val) => z.record(z.string()).parse(JSON.parse(val)))
141
152
  .optional(),
142
- auth0Domain: z.string().optional(),
143
- auth0WebClientID: z.string().optional(),
144
- auth0ClientSecret: z.string().optional(),
145
153
  mjCoreSchema: z.string(),
146
154
  });
147
155
 
@@ -152,6 +160,7 @@ export type RESTApiOptions = z.infer<typeof restApiOptionsSchema>;
152
160
  export type AskSkipInfo = z.infer<typeof askSkipInfoSchema>;
153
161
  export type SqlLoggingOptions = z.infer<typeof sqlLoggingOptionsSchema>;
154
162
  export type SqlLoggingInfo = z.infer<typeof sqlLoggingSchema>;
163
+ export type AuthProviderConfig = z.infer<typeof authProviderSchema>;
155
164
  export type ConfigInfo = z.infer<typeof configInfoSchema>;
156
165
 
157
166
  export const configInfo: ConfigInfo = loadConfig();
@@ -169,14 +178,9 @@ export const {
169
178
  ___codeGenAPIPort,
170
179
  ___codeGenAPISubmissionDelay,
171
180
  graphqlRootPath,
172
- webClientID,
173
- tenantID,
174
181
  enableIntrospection,
175
182
  websiteRunFromPackage,
176
183
  userEmailMap,
177
- auth0Domain,
178
- auth0WebClientID,
179
- auth0ClientSecret,
180
184
  apiKey,
181
185
  baseUrl,
182
186
  mjCoreSchema: mj_core_schema,
package/src/context.ts CHANGED
@@ -5,24 +5,33 @@ import 'reflect-metadata';
5
5
  import { Subject, firstValueFrom } from 'rxjs';
6
6
  import { AuthenticationError, AuthorizationError } from 'type-graphql';
7
7
  import sql from 'mssql';
8
- import { getSigningKeys, getSystemUser, validationOptions, verifyUserRecord } from './auth/index.js';
8
+ import { getSigningKeys, getSystemUser, getValidationOptions, verifyUserRecord, extractUserInfoFromPayload, TokenExpiredError } from './auth/index.js';
9
9
  import { authCache } from './cache.js';
10
10
  import { userEmailMap, apiKey, mj_core_schema } from './config.js';
11
11
  import { DataSourceInfo, UserPayload } from './types.js';
12
- import { TokenExpiredError } from './auth/index.js';
13
12
  import { GetReadOnlyDataSource, GetReadWriteDataSource } from './util.js';
14
13
  import { v4 as uuidv4 } from 'uuid';
15
14
  import e from 'express';
16
15
  import { DatabaseProviderBase } from '@memberjunction/core';
17
16
  import { SQLServerDataProvider, SQLServerProviderConfigData } from '@memberjunction/sqlserver-dataprovider';
17
+ import { AuthProviderFactory } from './auth/AuthProviderFactory.js';
18
18
 
19
- const verifyAsync = async (issuer: string, options: jwt.VerifyOptions, token: string): Promise<jwt.JwtPayload> =>
19
+ const verifyAsync = async (issuer: string, token: string): Promise<jwt.JwtPayload> =>
20
20
  new Promise((resolve, reject) => {
21
+ const options = getValidationOptions(issuer);
22
+
23
+ if (!options) {
24
+ reject(new Error(`No validation options found for issuer ${issuer}`));
25
+ return;
26
+ }
27
+
21
28
  jwt.verify(token, getSigningKeys(issuer), options, (err, jwt) => {
22
29
  if (jwt && typeof jwt !== 'string' && !err) {
23
30
  const payload = jwt.payload ?? jwt;
24
31
 
25
- console.log(`Valid token: ${payload.name} (${payload.email ? payload.email : payload.preferred_username})`); // temporary fix to check preferred_username if email is not present
32
+ // Use provider to extract user info for logging
33
+ const userInfo = extractUserInfoFromPayload(payload);
34
+ console.log(`Valid token: ${userInfo.fullName || 'Unknown'} (${userInfo.email || userInfo.preferredUsername || 'Unknown'})`);
26
35
  resolve(payload);
27
36
  } else {
28
37
  console.warn('Invalid token');
@@ -81,15 +90,28 @@ export const getUserPayload = async (
81
90
  throw new AuthenticationError('Missing issuer claim on token');
82
91
  }
83
92
 
84
- await verifyAsync(issuer, validationOptions[issuer], token);
93
+ // Verify issuer is supported
94
+ const factory = AuthProviderFactory.getInstance();
95
+ if (!factory.getByIssuer(issuer)) {
96
+ console.warn(`Unsupported issuer: ${issuer}`);
97
+ throw new AuthenticationError(`Unsupported authentication provider: ${issuer}`);
98
+ }
99
+
100
+ await verifyAsync(issuer, token);
85
101
  authCache.set(token, true);
86
102
  }
87
103
 
88
- const email = payload?.email ? ((userEmailMap ?? {})[payload?.email] ?? payload?.email) : payload?.preferred_username; // temporary fix to check preferred_username if email is not present
89
- const fullName = payload?.name;
90
- const firstName = payload?.given_name || fullName?.split(' ')[0];
91
- const lastName = payload?.family_name || fullName?.split(' ')[1] || fullName?.split(' ')[0];
92
- const userRecord = await verifyUserRecord(email, firstName, lastName, requestDomain, readWriteDataSource);
104
+ // Use provider to extract user information
105
+ const userInfo = extractUserInfoFromPayload(payload);
106
+ const email = userInfo.email ? ((userEmailMap ?? {})[userInfo.email] ?? userInfo.email) : userInfo.preferredUsername;
107
+
108
+ const userRecord = await verifyUserRecord(
109
+ email,
110
+ userInfo.firstName,
111
+ userInfo.lastName,
112
+ requestDomain,
113
+ readWriteDataSource
114
+ );
93
115
 
94
116
  if (!userRecord) {
95
117
  console.error(`User ${email} not found`);
@@ -99,12 +121,13 @@ export const getUserPayload = async (
99
121
  throw new AuthorizationError();
100
122
  }
101
123
 
102
- return { userRecord, email, sessionId };
103
- } catch (e) {
104
- console.error(e);
105
- if (e instanceof TokenExpiredError) {
106
- throw e;
107
- } else return {} as UserPayload;
124
+ return { userRecord, email: userRecord.Email, sessionId };
125
+ } catch (error) {
126
+ console.error(error);
127
+ if (error instanceof TokenExpiredError) {
128
+ throw error;
129
+ }
130
+ throw new AuthenticationError('Unable to authenticate user');
108
131
  }
109
132
  };
110
133
 
@@ -171,4 +194,4 @@ export const contextFunction =
171
194
  };
172
195
 
173
196
  return contextResult;
174
- };
197
+ };
@@ -39135,6 +39135,10 @@ export class ComponentLibrary_ {
39135
39135
  @MaxLength(10)
39136
39136
  _mj__UpdatedAt: Date;
39137
39137
 
39138
+ @Field({description: `Status of the component library. Active: fully supported; Deprecated: works but shows console warning; Disabled: throws error if used`})
39139
+ @MaxLength(40)
39140
+ Status: string;
39141
+
39138
39142
  @Field(() => [ComponentLibraryLink_])
39139
39143
  MJ_ComponentLibraryLinks_LibraryIDArray: ComponentLibraryLink_[]; // Link to MJ_ComponentLibraryLinks
39140
39144
 
@@ -39171,6 +39175,9 @@ export class CreateComponentLibraryInput {
39171
39175
 
39172
39176
  @Field({ nullable: true })
39173
39177
  Description: string | null;
39178
+
39179
+ @Field({ nullable: true })
39180
+ Status?: string;
39174
39181
  }
39175
39182
 
39176
39183
 
@@ -39206,6 +39213,9 @@ export class UpdateComponentLibraryInput {
39206
39213
  @Field({ nullable: true })
39207
39214
  Description?: string | null;
39208
39215
 
39216
+ @Field({ nullable: true })
39217
+ Status?: string;
39218
+
39209
39219
  @Field(() => [KeyValuePairInput], { nullable: true })
39210
39220
  OldValues___?: KeyValuePairInput[];
39211
39221
  }
@@ -139,7 +139,8 @@ export class ResolverBase {
139
139
  viewInput.AuditLogDescription,
140
140
  viewInput.ResultType,
141
141
  userPayload,
142
- viewInput.MaxRows
142
+ viewInput.MaxRows,
143
+ viewInput.StartRow
143
144
  );
144
145
  }
145
146
  else {
@@ -173,7 +174,8 @@ export class ResolverBase {
173
174
  viewInput.AuditLogDescription,
174
175
  viewInput.ResultType,
175
176
  userPayload,
176
- viewInput.MaxRows
177
+ viewInput.MaxRows,
178
+ viewInput.StartRow
177
179
  );
178
180
  } catch (err) {
179
181
  console.log(err);
@@ -210,7 +212,8 @@ export class ResolverBase {
210
212
  viewInput.AuditLogDescription,
211
213
  viewInput.ResultType,
212
214
  userPayload,
213
- viewInput.MaxRows
215
+ viewInput.MaxRows,
216
+ viewInput.StartRow
214
217
  );
215
218
  } catch (err) {
216
219
  console.log(err);
@@ -370,7 +373,8 @@ export class ResolverBase {
370
373
  auditLogDescription: string | undefined,
371
374
  resultType: string | undefined,
372
375
  userPayload: UserPayload | null,
373
- maxRows: number | undefined
376
+ maxRows: number | undefined,
377
+ startRow: number | undefined
374
378
  ) {
375
379
  try {
376
380
  if (!viewInfo || !userPayload) return null;
@@ -420,6 +424,7 @@ export class ResolverBase {
420
424
  ExcludeDataFromAllPriorViewRuns: excludeDataFromAllPriorViewRuns,
421
425
  IgnoreMaxRows: ignoreMaxRows,
422
426
  MaxRows: maxRows,
427
+ StartRow: startRow,
423
428
  ForceAuditLog: forceAuditLog,
424
429
  AuditLogDescription: auditLogDescription,
425
430
  ResultType: rt,