@memberjunction/server 2.89.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.
- package/dist/auth/AuthProviderFactory.d.ts +24 -0
- package/dist/auth/AuthProviderFactory.d.ts.map +1 -0
- package/dist/auth/AuthProviderFactory.js +82 -0
- package/dist/auth/AuthProviderFactory.js.map +1 -0
- package/dist/auth/BaseAuthProvider.d.ts +18 -0
- package/dist/auth/BaseAuthProvider.d.ts.map +1 -0
- package/dist/auth/BaseAuthProvider.js +42 -0
- package/dist/auth/BaseAuthProvider.js.map +1 -0
- package/dist/auth/IAuthProvider.d.ts +13 -0
- package/dist/auth/IAuthProvider.d.ts.map +1 -0
- package/dist/auth/IAuthProvider.js +2 -0
- package/dist/auth/IAuthProvider.js.map +1 -0
- package/dist/auth/__tests__/backward-compatibility.test.d.ts +2 -0
- package/dist/auth/__tests__/backward-compatibility.test.d.ts.map +1 -0
- package/dist/auth/__tests__/backward-compatibility.test.js +135 -0
- package/dist/auth/__tests__/backward-compatibility.test.js.map +1 -0
- package/dist/auth/index.d.ts +22 -7
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +65 -32
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/initializeProviders.d.ts +2 -0
- package/dist/auth/initializeProviders.d.ts.map +1 -0
- package/dist/auth/initializeProviders.js +23 -0
- package/dist/auth/initializeProviders.js.map +1 -0
- package/dist/auth/providers/Auth0Provider.d.ts +9 -0
- package/dist/auth/providers/Auth0Provider.d.ts.map +1 -0
- package/dist/auth/providers/Auth0Provider.js +42 -0
- package/dist/auth/providers/Auth0Provider.js.map +1 -0
- package/dist/auth/providers/CognitoProvider.d.ts +9 -0
- package/dist/auth/providers/CognitoProvider.d.ts.map +1 -0
- package/dist/auth/providers/CognitoProvider.js +46 -0
- package/dist/auth/providers/CognitoProvider.js.map +1 -0
- package/dist/auth/providers/GoogleProvider.d.ts +9 -0
- package/dist/auth/providers/GoogleProvider.d.ts.map +1 -0
- package/dist/auth/providers/GoogleProvider.js +41 -0
- package/dist/auth/providers/GoogleProvider.js.map +1 -0
- package/dist/auth/providers/MSALProvider.d.ts +9 -0
- package/dist/auth/providers/MSALProvider.d.ts.map +1 -0
- package/dist/auth/providers/MSALProvider.js +42 -0
- package/dist/auth/providers/MSALProvider.js.map +1 -0
- package/dist/auth/providers/OktaProvider.d.ts +9 -0
- package/dist/auth/providers/OktaProvider.d.ts.map +1 -0
- package/dist/auth/providers/OktaProvider.js +42 -0
- package/dist/auth/providers/OktaProvider.js.map +1 -0
- package/dist/config.d.ts +97 -21
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +13 -6
- package/dist/config.js.map +1 -1
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +25 -17
- package/dist/context.js.map +1 -1
- package/dist/generated/generated.d.ts +12 -0
- package/dist/generated/generated.d.ts.map +1 -1
- package/dist/generated/generated.js +61 -0
- package/dist/generated/generated.js.map +1 -1
- package/dist/generic/ResolverBase.d.ts +2 -2
- package/dist/generic/ResolverBase.d.ts.map +1 -1
- package/dist/generic/ResolverBase.js +5 -4
- package/dist/generic/ResolverBase.js.map +1 -1
- package/dist/generic/RunViewResolver.js.map +1 -1
- package/dist/resolvers/AskSkipResolver.d.ts.map +1 -1
- package/dist/resolvers/AskSkipResolver.js +3 -0
- package/dist/resolvers/AskSkipResolver.js.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
- package/dist/resolvers/RunAIPromptResolver.js.map +1 -1
- package/dist/types.d.ts +2 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +39 -39
- package/src/auth/AuthProviderFactory.ts +152 -0
- package/src/auth/BaseAuthProvider.ts +71 -0
- package/src/auth/IAuthProvider.ts +49 -0
- package/src/auth/__tests__/backward-compatibility.test.ts +183 -0
- package/src/auth/index.ts +104 -36
- package/src/auth/initializeProviders.ts +31 -0
- package/src/auth/providers/Auth0Provider.ts +45 -0
- package/src/auth/providers/CognitoProvider.ts +50 -0
- package/src/auth/providers/GoogleProvider.ts +45 -0
- package/src/auth/providers/MSALProvider.ts +45 -0
- package/src/auth/providers/OktaProvider.ts +46 -0
- package/src/config.ts +14 -10
- package/src/context.ts +40 -17
- package/src/generated/generated.ts +37 -0
- package/src/generic/ResolverBase.ts +18 -13
- package/src/generic/RunViewResolver.ts +4 -4
- package/src/resolvers/AskSkipResolver.ts +3 -0
- package/src/resolvers/RunAIAgentResolver.ts +3 -3
- package/src/resolvers/RunAIPromptResolver.ts +2 -2
- package/src/types.ts +2 -4
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { JwtPayload } from 'jsonwebtoken';
|
|
2
|
+
import { RegisterClass } from '@memberjunction/global';
|
|
3
|
+
import { AuthProviderConfig, AuthUserInfo } from '@memberjunction/core';
|
|
4
|
+
import { BaseAuthProvider } from '../BaseAuthProvider.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Auth0 authentication provider implementation
|
|
8
|
+
*/
|
|
9
|
+
@RegisterClass(BaseAuthProvider, 'auth0')
|
|
10
|
+
export class Auth0Provider extends BaseAuthProvider {
|
|
11
|
+
constructor(config: AuthProviderConfig) {
|
|
12
|
+
super(config);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Extracts user information from Auth0 JWT payload
|
|
17
|
+
*/
|
|
18
|
+
extractUserInfo(payload: JwtPayload): AuthUserInfo {
|
|
19
|
+
// Auth0 uses standard OIDC claims
|
|
20
|
+
const email = payload.email as string | undefined;
|
|
21
|
+
const fullName = payload.name as string | undefined;
|
|
22
|
+
const firstName = payload.given_name as string | undefined;
|
|
23
|
+
const lastName = payload.family_name as string | undefined;
|
|
24
|
+
const preferredUsername = payload.preferred_username as string | undefined || email;
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
email,
|
|
28
|
+
firstName: firstName || fullName?.split(' ')[0],
|
|
29
|
+
lastName: lastName || fullName?.split(' ')[1] || fullName?.split(' ')[0],
|
|
30
|
+
fullName,
|
|
31
|
+
preferredUsername
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Validates Auth0-specific configuration
|
|
37
|
+
*/
|
|
38
|
+
validateConfig(): boolean {
|
|
39
|
+
const baseValid = super.validateConfig();
|
|
40
|
+
const hasClientId = !!this.config.clientId;
|
|
41
|
+
const hasDomain = !!this.config.domain;
|
|
42
|
+
|
|
43
|
+
return baseValid && hasClientId && hasDomain;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { JwtPayload } from 'jsonwebtoken';
|
|
2
|
+
import { RegisterClass } from '@memberjunction/global';
|
|
3
|
+
import { AuthProviderConfig, AuthUserInfo } from '@memberjunction/core';
|
|
4
|
+
import { BaseAuthProvider } from '../BaseAuthProvider.js';
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* AWS Cognito authentication provider implementation
|
|
9
|
+
*/
|
|
10
|
+
@RegisterClass(BaseAuthProvider, 'cognito')
|
|
11
|
+
export class CognitoProvider extends BaseAuthProvider {
|
|
12
|
+
constructor(config: AuthProviderConfig) {
|
|
13
|
+
super(config);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Extracts user information from Cognito JWT payload
|
|
18
|
+
*/
|
|
19
|
+
extractUserInfo(payload: JwtPayload): AuthUserInfo {
|
|
20
|
+
// Cognito uses custom claims with 'cognito:' prefix for some fields
|
|
21
|
+
const email = payload.email as string | undefined ||
|
|
22
|
+
payload['cognito:username'] as string | undefined;
|
|
23
|
+
const fullName = payload.name as string | undefined;
|
|
24
|
+
const firstName = payload.given_name as string | undefined;
|
|
25
|
+
const lastName = payload.family_name as string | undefined;
|
|
26
|
+
const preferredUsername = payload['cognito:username'] as string | undefined ||
|
|
27
|
+
payload.preferred_username as string | undefined ||
|
|
28
|
+
email;
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
email,
|
|
32
|
+
firstName: firstName || fullName?.split(' ')[0],
|
|
33
|
+
lastName: lastName || fullName?.split(' ')[1] || fullName?.split(' ')[0],
|
|
34
|
+
fullName,
|
|
35
|
+
preferredUsername
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Validates Cognito-specific configuration
|
|
41
|
+
*/
|
|
42
|
+
validateConfig(): boolean {
|
|
43
|
+
const baseValid = super.validateConfig();
|
|
44
|
+
const hasClientId = !!this.config.clientId;
|
|
45
|
+
const hasRegion = !!this.config.region;
|
|
46
|
+
const hasUserPoolId = !!this.config.userPoolId;
|
|
47
|
+
|
|
48
|
+
return baseValid && hasClientId && hasRegion && hasUserPoolId;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { JwtPayload } from 'jsonwebtoken';
|
|
2
|
+
import { RegisterClass } from '@memberjunction/global';
|
|
3
|
+
import { AuthProviderConfig, AuthUserInfo } from '@memberjunction/core';
|
|
4
|
+
import { BaseAuthProvider } from '../BaseAuthProvider.js';
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Google Identity Platform authentication provider implementation
|
|
9
|
+
*/
|
|
10
|
+
@RegisterClass(BaseAuthProvider, 'google')
|
|
11
|
+
export class GoogleProvider extends BaseAuthProvider {
|
|
12
|
+
constructor(config: AuthProviderConfig) {
|
|
13
|
+
super(config);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Extracts user information from Google JWT payload
|
|
18
|
+
*/
|
|
19
|
+
extractUserInfo(payload: JwtPayload): AuthUserInfo {
|
|
20
|
+
// Google uses standard OIDC claims
|
|
21
|
+
const email = payload.email as string | undefined;
|
|
22
|
+
const fullName = payload.name as string | undefined;
|
|
23
|
+
const firstName = payload.given_name as string | undefined;
|
|
24
|
+
const lastName = payload.family_name as string | undefined;
|
|
25
|
+
const preferredUsername = email; // Google typically uses email as username
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
email,
|
|
29
|
+
firstName: firstName || fullName?.split(' ')[0],
|
|
30
|
+
lastName: lastName || fullName?.split(' ')[1] || fullName?.split(' ')[0],
|
|
31
|
+
fullName,
|
|
32
|
+
preferredUsername
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Validates Google-specific configuration
|
|
38
|
+
*/
|
|
39
|
+
validateConfig(): boolean {
|
|
40
|
+
const baseValid = super.validateConfig();
|
|
41
|
+
const hasClientId = !!this.config.clientId;
|
|
42
|
+
|
|
43
|
+
return baseValid && hasClientId;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { JwtPayload } from 'jsonwebtoken';
|
|
2
|
+
import { RegisterClass } from '@memberjunction/global';
|
|
3
|
+
import { AuthProviderConfig, AuthUserInfo } from '@memberjunction/core';
|
|
4
|
+
import { BaseAuthProvider } from '../BaseAuthProvider.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Microsoft Authentication Library (MSAL) provider implementation
|
|
8
|
+
*/
|
|
9
|
+
@RegisterClass(BaseAuthProvider, 'msal')
|
|
10
|
+
export class MSALProvider extends BaseAuthProvider {
|
|
11
|
+
constructor(config: AuthProviderConfig) {
|
|
12
|
+
super(config);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Extracts user information from MSAL/Azure AD JWT payload
|
|
17
|
+
*/
|
|
18
|
+
extractUserInfo(payload: JwtPayload): AuthUserInfo {
|
|
19
|
+
// MSAL/Azure AD uses some custom claims
|
|
20
|
+
const email = payload.email as string | undefined || payload.preferred_username as string | undefined;
|
|
21
|
+
const fullName = payload.name as string | undefined;
|
|
22
|
+
const firstName = payload.given_name as string | undefined;
|
|
23
|
+
const lastName = payload.family_name as string | undefined;
|
|
24
|
+
const preferredUsername = payload.preferred_username as string | undefined;
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
email,
|
|
28
|
+
firstName: firstName || fullName?.split(' ')[0],
|
|
29
|
+
lastName: lastName || fullName?.split(' ')[1] || fullName?.split(' ')[0],
|
|
30
|
+
fullName,
|
|
31
|
+
preferredUsername
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Validates MSAL-specific configuration
|
|
37
|
+
*/
|
|
38
|
+
validateConfig(): boolean {
|
|
39
|
+
const baseValid = super.validateConfig();
|
|
40
|
+
const hasClientId = !!this.config.clientId;
|
|
41
|
+
const hasTenantId = !!this.config.tenantId;
|
|
42
|
+
|
|
43
|
+
return baseValid && hasClientId && hasTenantId;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { JwtPayload } from 'jsonwebtoken';
|
|
2
|
+
import { RegisterClass } from '@memberjunction/global';
|
|
3
|
+
import { AuthProviderConfig, AuthUserInfo } from '@memberjunction/core';
|
|
4
|
+
import { BaseAuthProvider } from '../BaseAuthProvider.js';
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Okta authentication provider implementation
|
|
9
|
+
*/
|
|
10
|
+
@RegisterClass(BaseAuthProvider, 'okta')
|
|
11
|
+
export class OktaProvider extends BaseAuthProvider {
|
|
12
|
+
constructor(config: AuthProviderConfig) {
|
|
13
|
+
super(config);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Extracts user information from Okta JWT payload
|
|
18
|
+
*/
|
|
19
|
+
extractUserInfo(payload: JwtPayload): AuthUserInfo {
|
|
20
|
+
// Okta uses standard OIDC claims plus some custom ones
|
|
21
|
+
const email = payload.email as string | undefined || payload.preferred_username as string | undefined;
|
|
22
|
+
const fullName = payload.name as string | undefined;
|
|
23
|
+
const firstName = payload.given_name as string | undefined;
|
|
24
|
+
const lastName = payload.family_name as string | undefined;
|
|
25
|
+
const preferredUsername = payload.preferred_username as string | undefined || email;
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
email,
|
|
29
|
+
firstName: firstName || fullName?.split(' ')[0],
|
|
30
|
+
lastName: lastName || fullName?.split(' ')[1] || fullName?.split(' ')[0],
|
|
31
|
+
fullName,
|
|
32
|
+
preferredUsername
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Validates Okta-specific configuration
|
|
38
|
+
*/
|
|
39
|
+
validateConfig(): boolean {
|
|
40
|
+
const baseValid = super.validateConfig();
|
|
41
|
+
const hasClientId = !!this.config.clientId;
|
|
42
|
+
const hasDomain = !!this.config.domain;
|
|
43
|
+
|
|
44
|
+
return baseValid && hasClientId && hasDomain;
|
|
45
|
+
}
|
|
46
|
+
}
|
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,
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
89
|
-
const
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
const userRecord = await verifyUserRecord(
|
|
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 (
|
|
104
|
-
console.error(
|
|
105
|
-
if (
|
|
106
|
-
throw
|
|
107
|
-
}
|
|
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
|
+
};
|
|
@@ -37957,6 +37957,15 @@ export class Component_ {
|
|
|
37957
37957
|
@Field(() => Int, {description: `Number of component dependencies defined in the specification. Used to assess component complexity.`})
|
|
37958
37958
|
DependencyCount: number;
|
|
37959
37959
|
|
|
37960
|
+
@Field({nullable: true, description: `The ID of the AI model used to generate the vector embedding for the technical design`})
|
|
37961
|
+
TechnicalDesignVectorEmbeddingModelID?: string;
|
|
37962
|
+
|
|
37963
|
+
@Field({nullable: true, description: `The ID of the AI model used to generate the vector embedding for the functional requirements`})
|
|
37964
|
+
FunctionalRequirementsVectorEmbeddingModelID?: string;
|
|
37965
|
+
|
|
37966
|
+
@Field(() => Boolean, {description: `Indicates whether the component has any custom properties that are marked as required. This is auto-calculated based on the component's properties array to identify components with mandatory custom configuration.`})
|
|
37967
|
+
HasRequiredCustomProps: boolean;
|
|
37968
|
+
|
|
37960
37969
|
@Field({nullable: true})
|
|
37961
37970
|
@MaxLength(510)
|
|
37962
37971
|
SourceRegistry?: string;
|
|
@@ -38048,6 +38057,15 @@ export class CreateComponentInput {
|
|
|
38048
38057
|
|
|
38049
38058
|
@Field(() => Int, { nullable: true })
|
|
38050
38059
|
DependencyCount?: number;
|
|
38060
|
+
|
|
38061
|
+
@Field({ nullable: true })
|
|
38062
|
+
TechnicalDesignVectorEmbeddingModelID: string | null;
|
|
38063
|
+
|
|
38064
|
+
@Field({ nullable: true })
|
|
38065
|
+
FunctionalRequirementsVectorEmbeddingModelID: string | null;
|
|
38066
|
+
|
|
38067
|
+
@Field(() => Boolean, { nullable: true })
|
|
38068
|
+
HasRequiredCustomProps?: boolean;
|
|
38051
38069
|
}
|
|
38052
38070
|
|
|
38053
38071
|
|
|
@@ -38128,6 +38146,15 @@ export class UpdateComponentInput {
|
|
|
38128
38146
|
@Field(() => Int, { nullable: true })
|
|
38129
38147
|
DependencyCount?: number;
|
|
38130
38148
|
|
|
38149
|
+
@Field({ nullable: true })
|
|
38150
|
+
TechnicalDesignVectorEmbeddingModelID?: string | null;
|
|
38151
|
+
|
|
38152
|
+
@Field({ nullable: true })
|
|
38153
|
+
FunctionalRequirementsVectorEmbeddingModelID?: string | null;
|
|
38154
|
+
|
|
38155
|
+
@Field(() => Boolean, { nullable: true })
|
|
38156
|
+
HasRequiredCustomProps?: boolean;
|
|
38157
|
+
|
|
38131
38158
|
@Field(() => [KeyValuePairInput], { nullable: true })
|
|
38132
38159
|
OldValues___?: KeyValuePairInput[];
|
|
38133
38160
|
}
|
|
@@ -39108,6 +39135,10 @@ export class ComponentLibrary_ {
|
|
|
39108
39135
|
@MaxLength(10)
|
|
39109
39136
|
_mj__UpdatedAt: Date;
|
|
39110
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
|
+
|
|
39111
39142
|
@Field(() => [ComponentLibraryLink_])
|
|
39112
39143
|
MJ_ComponentLibraryLinks_LibraryIDArray: ComponentLibraryLink_[]; // Link to MJ_ComponentLibraryLinks
|
|
39113
39144
|
|
|
@@ -39144,6 +39175,9 @@ export class CreateComponentLibraryInput {
|
|
|
39144
39175
|
|
|
39145
39176
|
@Field({ nullable: true })
|
|
39146
39177
|
Description: string | null;
|
|
39178
|
+
|
|
39179
|
+
@Field({ nullable: true })
|
|
39180
|
+
Status?: string;
|
|
39147
39181
|
}
|
|
39148
39182
|
|
|
39149
39183
|
|
|
@@ -39179,6 +39213,9 @@ export class UpdateComponentLibraryInput {
|
|
|
39179
39213
|
@Field({ nullable: true })
|
|
39180
39214
|
Description?: string | null;
|
|
39181
39215
|
|
|
39216
|
+
@Field({ nullable: true })
|
|
39217
|
+
Status?: string;
|
|
39218
|
+
|
|
39182
39219
|
@Field(() => [KeyValuePairInput], { nullable: true })
|
|
39183
39220
|
OldValues___?: KeyValuePairInput[];
|
|
39184
39221
|
}
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
RunViewResult,
|
|
17
17
|
UserInfo,
|
|
18
18
|
} from '@memberjunction/core';
|
|
19
|
-
import { AuditLogEntity, ErrorLogEntity,
|
|
19
|
+
import { AuditLogEntity, ErrorLogEntity, UserViewEntityExtended } from '@memberjunction/core-entities';
|
|
20
20
|
import { SQLServerDataProvider, UserCache } from '@memberjunction/sqlserver-dataprovider';
|
|
21
21
|
import { PubSubEngine } from 'type-graphql';
|
|
22
22
|
import { GraphQLError } from 'graphql';
|
|
@@ -117,7 +117,7 @@ export class ResolverBase {
|
|
|
117
117
|
async RunViewByNameGeneric(viewInput: RunViewByNameInput, provider: DatabaseProviderBase, userPayload: UserPayload, pubSub: PubSubEngine) {
|
|
118
118
|
try {
|
|
119
119
|
const rv = provider as any as IRunViewProvider;
|
|
120
|
-
const result = await rv.RunView<
|
|
120
|
+
const result = await rv.RunView<UserViewEntityExtended>({
|
|
121
121
|
EntityName: 'User Views',
|
|
122
122
|
ExtraFilter: "Name='" + viewInput.ViewName + "'",
|
|
123
123
|
}, userPayload.userRecord);
|
|
@@ -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 {
|
|
@@ -155,7 +156,7 @@ export class ResolverBase {
|
|
|
155
156
|
async RunViewByIDGeneric(viewInput: RunViewByIDInput, provider: DatabaseProviderBase, userPayload: UserPayload, pubSub: PubSubEngine) {
|
|
156
157
|
try {
|
|
157
158
|
const contextUser = this.GetUserFromPayload(userPayload);
|
|
158
|
-
const viewInfo = await provider.GetEntityObject<
|
|
159
|
+
const viewInfo = await provider.GetEntityObject<UserViewEntityExtended>('User Views', contextUser);
|
|
159
160
|
await viewInfo.Load(viewInput.ViewID);
|
|
160
161
|
return this.RunViewGenericInternal(
|
|
161
162
|
provider,
|
|
@@ -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);
|
|
@@ -187,12 +189,12 @@ export class ResolverBase {
|
|
|
187
189
|
const entity = md.Entities.find((e) => e.Name === viewInput.EntityName);
|
|
188
190
|
if (!entity) throw new Error(`Entity ${viewInput.EntityName} not found in metadata`);
|
|
189
191
|
|
|
190
|
-
const viewInfo:
|
|
192
|
+
const viewInfo: UserViewEntityExtended = {
|
|
191
193
|
ID: '',
|
|
192
194
|
Entity: viewInput.EntityName,
|
|
193
195
|
EntityID: entity.ID,
|
|
194
196
|
EntityBaseView: entity.BaseView as string,
|
|
195
|
-
} as
|
|
197
|
+
} as UserViewEntityExtended; // only providing a few bits of data here, but it's enough to get the view to run
|
|
196
198
|
|
|
197
199
|
return this.RunViewGenericInternal(
|
|
198
200
|
provider,
|
|
@@ -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);
|
|
@@ -228,12 +231,12 @@ export class ResolverBase {
|
|
|
228
231
|
let params: RunViewGenericParams[] = [];
|
|
229
232
|
for (const viewInput of viewInputs) {
|
|
230
233
|
try {
|
|
231
|
-
let viewInfo:
|
|
234
|
+
let viewInfo: UserViewEntityExtended | null = null;
|
|
232
235
|
|
|
233
236
|
if (viewInput.ViewName) {
|
|
234
237
|
viewInfo = this.safeFirstArrayElement(await this.findBy(provider, 'User Views', { Name: viewInput.ViewName }, userPayload.userRecord));
|
|
235
238
|
} else if (viewInput.ViewID) {
|
|
236
|
-
viewInfo = await provider.GetEntityObject<
|
|
239
|
+
viewInfo = await provider.GetEntityObject<UserViewEntityExtended>('User Views', contextUser);
|
|
237
240
|
await viewInfo.Load(viewInput.ViewID);
|
|
238
241
|
} else if (viewInput.EntityName) {
|
|
239
242
|
const entity = md.Entities.find((e) => e.Name === viewInput.EntityName);
|
|
@@ -247,7 +250,7 @@ export class ResolverBase {
|
|
|
247
250
|
Entity: viewInput.EntityName,
|
|
248
251
|
EntityID: entity.ID,
|
|
249
252
|
EntityBaseView: entity.BaseView,
|
|
250
|
-
} as
|
|
253
|
+
} as UserViewEntityExtended;
|
|
251
254
|
} else {
|
|
252
255
|
throw new Error('Unable to determine input type');
|
|
253
256
|
}
|
|
@@ -356,7 +359,7 @@ export class ResolverBase {
|
|
|
356
359
|
*/
|
|
357
360
|
protected async RunViewGenericInternal(
|
|
358
361
|
provider: DatabaseProviderBase,
|
|
359
|
-
viewInfo:
|
|
362
|
+
viewInfo: UserViewEntityExtended,
|
|
360
363
|
extraFilter: string,
|
|
361
364
|
orderBy: string,
|
|
362
365
|
userSearchString: string,
|
|
@@ -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,
|
|
@@ -4,7 +4,7 @@ import { ResolverBase } from './ResolverBase.js';
|
|
|
4
4
|
import { LogError, LogStatus } from '@memberjunction/core';
|
|
5
5
|
import { RequireSystemUser } from '../directives/RequireSystemUser.js';
|
|
6
6
|
import { GetReadOnlyProvider } from '../util.js';
|
|
7
|
-
import {
|
|
7
|
+
import { UserViewEntityExtended } from '@memberjunction/core-entities';
|
|
8
8
|
|
|
9
9
|
/********************************************************************************
|
|
10
10
|
* The PURPOSE of this resolver is to provide a generic way to run a view and return the results.
|
|
@@ -468,7 +468,7 @@ export class RunViewResolver extends ResolverBase {
|
|
|
468
468
|
if (rawData === null)
|
|
469
469
|
return null;
|
|
470
470
|
|
|
471
|
-
const viewInfo = super.safeFirstArrayElement<
|
|
471
|
+
const viewInfo = super.safeFirstArrayElement<UserViewEntityExtended>(await super.findBy<UserViewEntityExtended>(provider, "User Views", { Name: input.ViewName }, userPayload.userRecord));
|
|
472
472
|
const returnData = this.processRawData(rawData.Results, viewInfo.EntityID);
|
|
473
473
|
return {
|
|
474
474
|
Results: returnData,
|
|
@@ -495,7 +495,7 @@ export class RunViewResolver extends ResolverBase {
|
|
|
495
495
|
if (rawData === null)
|
|
496
496
|
return null;
|
|
497
497
|
|
|
498
|
-
const viewInfo = super.safeFirstArrayElement<
|
|
498
|
+
const viewInfo = super.safeFirstArrayElement<UserViewEntityExtended>(await super.findBy<UserViewEntityExtended>(provider, "User Views", { ID: input.ViewID }, userPayload.userRecord));
|
|
499
499
|
const returnData = this.processRawData(rawData.Results, viewInfo.EntityID);
|
|
500
500
|
return {
|
|
501
501
|
Results: returnData,
|
|
@@ -613,7 +613,7 @@ export class RunViewResolver extends ResolverBase {
|
|
|
613
613
|
const rawData = await super.RunViewByIDGeneric(input, provider, userPayload, pubSub);
|
|
614
614
|
if (rawData === null) return null;
|
|
615
615
|
|
|
616
|
-
const viewInfo = super.safeFirstArrayElement<
|
|
616
|
+
const viewInfo = super.safeFirstArrayElement<UserViewEntityExtended>(await super.findBy<UserViewEntityExtended>(provider, "User Views", { ID: input.ViewID }, userPayload.userRecord));
|
|
617
617
|
const returnData = this.processRawData(rawData.Results, viewInfo.EntityID);
|
|
618
618
|
return {
|
|
619
619
|
Results: returnData,
|
|
@@ -1441,6 +1441,9 @@ cycle.`);
|
|
|
1441
1441
|
createdAt: q.__mj_CreatedAt,
|
|
1442
1442
|
updatedAt: q.__mj_UpdatedAt,
|
|
1443
1443
|
categoryID: q.CategoryID,
|
|
1444
|
+
embeddingVector: q.EmbeddingVector,
|
|
1445
|
+
embeddingModelID: q.EmbeddingModelID,
|
|
1446
|
+
embeddingModelName: q.EmbeddingModel,
|
|
1444
1447
|
fields: q.Fields.map((f) => {
|
|
1445
1448
|
return {
|
|
1446
1449
|
id: f.ID,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Resolver, Mutation, Arg, Ctx, ObjectType, Field, PubSub, PubSubEngine, Subscription, Root, ResolverFilterData, ID } from 'type-graphql';
|
|
2
2
|
import { UserPayload } from '../types.js';
|
|
3
3
|
import { LogError, LogStatus } from '@memberjunction/core';
|
|
4
|
-
import {
|
|
4
|
+
import { AIAgentEntityExtended } from '@memberjunction/core-entities';
|
|
5
5
|
import { AgentRunner } from '@memberjunction/ai-agents';
|
|
6
6
|
import { ExecuteAgentResult } from '@memberjunction/ai-core-plus';
|
|
7
7
|
import { AIEngine } from '@memberjunction/aiengine';
|
|
@@ -169,12 +169,12 @@ export class RunAIAgentResolver extends ResolverBase {
|
|
|
169
169
|
/**
|
|
170
170
|
* Validate the agent entity
|
|
171
171
|
*/
|
|
172
|
-
private async validateAgent(agentId: string, currentUser: any): Promise<
|
|
172
|
+
private async validateAgent(agentId: string, currentUser: any): Promise<AIAgentEntityExtended> {
|
|
173
173
|
// Use AIEngine to get cached agent data
|
|
174
174
|
await AIEngine.Instance.Config(false, currentUser);
|
|
175
175
|
|
|
176
176
|
// Find agent in cached collection
|
|
177
|
-
const agentEntity = AIEngine.Instance.Agents.find((a:
|
|
177
|
+
const agentEntity = AIEngine.Instance.Agents.find((a: AIAgentEntityExtended) => a.ID === agentId);
|
|
178
178
|
|
|
179
179
|
if (!agentEntity) {
|
|
180
180
|
throw new Error(`AI Agent with ID ${agentId} not found`);
|