@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
@@ -0,0 +1,183 @@
1
+ import { describe, it, expect, beforeEach, afterEach, jest } from '@jest/globals';
2
+ import { AuthProviderFactory } from '../AuthProviderFactory';
3
+ import { IAuthProvider } from '../IAuthProvider';
4
+ import { initializeAuthProviders } from '../initializeProviders';
5
+
6
+ /**
7
+ * Test suite for backward compatibility of the new auth provider system
8
+ */
9
+ describe('Authentication Provider Backward Compatibility', () => {
10
+ let factory: AuthProviderFactory;
11
+
12
+ beforeEach(() => {
13
+ factory = AuthProviderFactory.getInstance();
14
+ factory.clear();
15
+ });
16
+
17
+ afterEach(() => {
18
+ factory.clear();
19
+ });
20
+
21
+ describe('Legacy Configuration Support', () => {
22
+ it('should create MSAL provider from legacy config', () => {
23
+ // Simulate legacy environment variables
24
+ process.env.TENANT_ID = 'test-tenant-id';
25
+ process.env.WEB_CLIENT_ID = 'test-client-id';
26
+
27
+ // Initialize with legacy config
28
+ initializeAuthProviders();
29
+
30
+ // Check that MSAL provider was created
31
+ const msalProvider = factory.getByName('msal');
32
+ expect(msalProvider).toBeDefined();
33
+ expect(msalProvider?.issuer).toContain('test-tenant-id');
34
+ expect(msalProvider?.audience).toBe('test-client-id');
35
+ });
36
+
37
+ it('should create Auth0 provider from legacy config', () => {
38
+ // Simulate legacy environment variables
39
+ process.env.AUTH0_DOMAIN = 'test.auth0.com';
40
+ process.env.AUTH0_CLIENT_ID = 'auth0-client-id';
41
+ process.env.AUTH0_CLIENT_SECRET = 'auth0-secret';
42
+
43
+ // Initialize with legacy config
44
+ initializeAuthProviders();
45
+
46
+ // Check that Auth0 provider was created
47
+ const auth0Provider = factory.getByName('auth0');
48
+ expect(auth0Provider).toBeDefined();
49
+ expect(auth0Provider?.issuer).toBe('https://test.auth0.com/');
50
+ expect(auth0Provider?.audience).toBe('auth0-client-id');
51
+ });
52
+ });
53
+
54
+
55
+ describe('Provider Registry Functionality', () => {
56
+ it('should find providers by issuer with different formats', () => {
57
+ // Register a test provider
58
+ const testProvider = {
59
+ name: 'test',
60
+ issuer: 'https://test.provider.com/oauth2',
61
+ audience: 'test-audience',
62
+ jwksUri: 'https://test.provider.com/.well-known/jwks.json',
63
+ validateConfig: () => true,
64
+ getSigningKey: jest.fn(),
65
+ extractUserInfo: jest.fn(),
66
+ matchesIssuer: (issuer: string) => {
67
+ const normalized = issuer.toLowerCase().replace(/\/$/, '');
68
+ return normalized === 'https://test.provider.com/oauth2';
69
+ }
70
+ } as IAuthProvider;
71
+
72
+ factory.register(testProvider);
73
+
74
+ // Test with exact match
75
+ expect(factory.getByIssuer('https://test.provider.com/oauth2')).toBe(testProvider);
76
+
77
+ // Test with trailing slash
78
+ expect(factory.getByIssuer('https://test.provider.com/oauth2/')).toBe(testProvider);
79
+
80
+ // Test with different case
81
+ expect(factory.getByIssuer('https://TEST.PROVIDER.COM/oauth2')).toBe(testProvider);
82
+ });
83
+
84
+ it('should cache issuer lookups for performance', () => {
85
+ const testProvider = {
86
+ name: 'test',
87
+ issuer: 'https://test.provider.com',
88
+ audience: 'test',
89
+ jwksUri: 'https://test.provider.com/jwks',
90
+ validateConfig: () => true,
91
+ getSigningKey: jest.fn(),
92
+ extractUserInfo: jest.fn(),
93
+ matchesIssuer: jest.fn((issuer: string): boolean => issuer === 'https://test.provider.com')
94
+ } as IAuthProvider;
95
+
96
+ factory.register(testProvider);
97
+
98
+ // First lookup
99
+ factory.getByIssuer('https://test.provider.com');
100
+ expect(testProvider.matchesIssuer).toHaveBeenCalledTimes(1);
101
+
102
+ // Second lookup should use cache
103
+ factory.getByIssuer('https://test.provider.com');
104
+ expect(testProvider.matchesIssuer).toHaveBeenCalledTimes(1);
105
+ });
106
+ });
107
+
108
+ describe('User Info Extraction', () => {
109
+ it('should extract user info from different token formats', () => {
110
+ // Test MSAL token format
111
+ const msalPayload = {
112
+ iss: 'https://login.microsoftonline.com/tenant/v2.0',
113
+ email: 'user@example.com',
114
+ given_name: 'John',
115
+ family_name: 'Doe',
116
+ name: 'John Doe',
117
+ preferred_username: 'john.doe@example.com'
118
+ };
119
+
120
+ // Test Auth0 token format
121
+ const auth0Payload = {
122
+ iss: 'https://test.auth0.com/',
123
+ email: 'user@example.com',
124
+ given_name: 'Jane',
125
+ family_name: 'Smith',
126
+ name: 'Jane Smith'
127
+ };
128
+
129
+ // Test Okta token format
130
+ const oktaPayload = {
131
+ iss: 'https://test.okta.com/oauth2/default',
132
+ email: 'user@example.com',
133
+ given_name: 'Bob',
134
+ family_name: 'Johnson',
135
+ name: 'Bob Johnson',
136
+ preferred_username: 'bob.johnson'
137
+ };
138
+
139
+ // Initialize providers
140
+ initializeAuthProviders();
141
+
142
+ // Test extraction for each provider type
143
+ const msalProvider = factory.getByIssuer(msalPayload.iss);
144
+ if (msalProvider) {
145
+ const msalUserInfo = msalProvider.extractUserInfo(msalPayload);
146
+ expect(msalUserInfo.email).toBe('user@example.com');
147
+ expect(msalUserInfo.firstName).toBe('John');
148
+ expect(msalUserInfo.lastName).toBe('Doe');
149
+ }
150
+
151
+ const auth0Provider = factory.getByIssuer(auth0Payload.iss);
152
+ if (auth0Provider) {
153
+ const auth0UserInfo = auth0Provider.extractUserInfo(auth0Payload);
154
+ expect(auth0UserInfo.email).toBe('user@example.com');
155
+ expect(auth0UserInfo.firstName).toBe('Jane');
156
+ expect(auth0UserInfo.lastName).toBe('Smith');
157
+ }
158
+ });
159
+ });
160
+
161
+ describe('Error Handling', () => {
162
+ it('should handle missing provider gracefully', () => {
163
+ const unknownIssuer = 'https://unknown.provider.com';
164
+ const provider = factory.getByIssuer(unknownIssuer);
165
+ expect(provider).toBeUndefined();
166
+ });
167
+
168
+ it('should validate provider configuration', () => {
169
+ const invalidProvider = {
170
+ name: 'invalid',
171
+ issuer: '', // Invalid: empty issuer
172
+ audience: 'test',
173
+ jwksUri: 'https://test.com/jwks',
174
+ validateConfig: () => false,
175
+ getSigningKey: jest.fn(),
176
+ extractUserInfo: jest.fn(),
177
+ matchesIssuer: jest.fn()
178
+ } as IAuthProvider;
179
+
180
+ expect(() => factory.register(invalidProvider)).toThrow();
181
+ });
182
+ });
183
+ });
package/src/auth/index.ts CHANGED
@@ -1,33 +1,28 @@
1
- import { JwtHeader, SigningKeyCallback } from 'jsonwebtoken';
2
- import jwksClient from 'jwks-rsa';
3
- import { auth0Domain, auth0WebClientID, configInfo, tenantID, webClientID } from '../config.js';
1
+ import { JwtHeader, SigningKeyCallback, JwtPayload } from 'jsonwebtoken';
2
+ import { configInfo } from '../config.js';
4
3
  import { UserCache } from '@memberjunction/sqlserver-dataprovider';
5
4
  import sql from 'mssql';
6
5
  import { Metadata, RoleInfo, UserInfo } from '@memberjunction/core';
7
6
  import { NewUserBase } from './newUsers.js';
8
7
  import { MJGlobal } from '@memberjunction/global';
9
8
  import { UserEntity, UserEntityType } from '@memberjunction/core-entities';
9
+ import { AuthProviderFactory } from './AuthProviderFactory.js';
10
+ import { initializeAuthProviders } from './initializeProviders.js';
10
11
 
11
12
  export { TokenExpiredError } from './tokenExpiredError.js';
12
-
13
- const missingAzureConfig = !tenantID || !webClientID;
14
- const missingAuth0Config = !auth0Domain || !auth0WebClientID;
13
+ export { IAuthProvider } from './IAuthProvider.js';
14
+ export { AuthProviderFactory } from './AuthProviderFactory.js';
15
15
 
16
16
  // This is a hard-coded forever constant due to internal migrations
17
17
  const SYSTEM_USER_ID = 'ecafccec-6a37-ef11-86d4-000d3a4e707e';
18
18
 
19
19
  class MissingAuthError extends Error {
20
20
  constructor() {
21
- super('Could not find authentication configuration for either MSAL or Auth0 in the server environment variables.');
21
+ super('No authentication providers configured. Please configure at least one auth provider in mj.config.cjs');
22
22
  this.name = 'MissingAuthError';
23
23
  }
24
24
  }
25
25
 
26
- const issuers = {
27
- azure: `https://login.microsoftonline.com/${tenantID}/v2.0`,
28
- auth0: `https://${auth0Domain}/`,
29
- };
30
-
31
26
  const refreshUserCache = async (dataSource?: sql.ConnectionPool) => {
32
27
  const startTime: number = Date.now();
33
28
  await UserCache.Instance.Refresh(dataSource);
@@ -51,16 +46,40 @@ const refreshUserCache = async (dataSource?: sql.ConnectionPool) => {
51
46
  );
52
47
  };
53
48
 
54
- export const validationOptions = {
55
- [issuers.auth0]: {
56
- audience: auth0WebClientID,
57
- jwksUri: `https://${auth0Domain}/.well-known/jwks.json`,
49
+ /**
50
+ * Gets validation options for a specific issuer
51
+ * This maintains backward compatibility with the old structure
52
+ */
53
+ export const getValidationOptions = (issuer: string): { audience: string; jwksUri: string } | undefined => {
54
+ const factory = AuthProviderFactory.getInstance();
55
+ const provider = factory.getByIssuer(issuer);
56
+
57
+ if (!provider) {
58
+ return undefined;
59
+ }
60
+
61
+ return {
62
+ audience: provider.audience,
63
+ jwksUri: provider.jwksUri
64
+ };
65
+ };
66
+
67
+ /**
68
+ * Backward compatible validationOptions object
69
+ * @deprecated Use getValidationOptions() or AuthProviderRegistry instead
70
+ */
71
+ export const validationOptions: Record<string, { audience: string; jwksUri: string }> = new Proxy({}, {
72
+ get: (target, prop: string) => {
73
+ return getValidationOptions(prop);
58
74
  },
59
- [issuers.azure]: {
60
- audience: webClientID,
61
- jwksUri: `https://login.microsoftonline.com/${tenantID}/discovery/v2.0/keys`,
75
+ has: (target, prop: string) => {
76
+ return getValidationOptions(prop) !== undefined;
62
77
  },
63
- };
78
+ ownKeys: () => {
79
+ const factory = AuthProviderFactory.getInstance();
80
+ return factory.getAllProviders().map(p => p.issuer);
81
+ }
82
+ });
64
83
 
65
84
  export class UserPayload {
66
85
  aio?: string;
@@ -78,31 +97,77 @@ export class UserPayload {
78
97
  tid?: string;
79
98
  uti?: string;
80
99
  ver?: string;
81
- // what about an array of roles???
100
+ email?: string;
101
+ given_name?: string;
102
+ family_name?: string;
103
+ [key: string]: unknown; // Allow additional claims
82
104
  }
83
105
 
106
+ /**
107
+ * Gets signing keys for JWT validation
108
+ */
84
109
  export const getSigningKeys = (issuer: string) => (header: JwtHeader, cb: SigningKeyCallback) => {
85
- if (!validationOptions[issuer]) {
86
- throw new Error(`No validation options found for issuer ${issuer}`);
110
+ const factory = AuthProviderFactory.getInstance();
111
+
112
+ // Initialize providers if not already done
113
+ if (!factory.hasProviders()) {
114
+ initializeAuthProviders();
87
115
  }
88
116
 
89
- const jwksUri = validationOptions[issuer].jwksUri;
90
- if (missingAuth0Config && missingAzureConfig) {
91
- throw new MissingAuthError();
117
+ const provider = factory.getByIssuer(issuer);
118
+
119
+ if (!provider) {
120
+ // Check if we have any providers at all
121
+ if (!factory.hasProviders()) {
122
+ throw new MissingAuthError();
123
+ }
124
+ throw new Error(`No authentication provider found for issuer: ${issuer}`);
92
125
  }
93
- if (missingAuth0Config) {
94
- console.warn('Auth0 configuration not found in environment variables');
126
+
127
+ provider.getSigningKey(header, cb);
128
+ };
129
+
130
+ /**
131
+ * Extracts user information from JWT payload using the appropriate provider
132
+ */
133
+ export const extractUserInfoFromPayload = (payload: JwtPayload): {
134
+ email?: string;
135
+ firstName?: string;
136
+ lastName?: string;
137
+ fullName?: string;
138
+ preferredUsername?: string;
139
+ } => {
140
+ const factory = AuthProviderFactory.getInstance();
141
+ const issuer = payload.iss;
142
+
143
+ if (!issuer) {
144
+ // Fallback to default extraction
145
+ const preferredUsername = payload.preferred_username as string | undefined;
146
+ return {
147
+ email: payload.email as string | undefined || preferredUsername,
148
+ firstName: payload.given_name as string | undefined,
149
+ lastName: payload.family_name as string | undefined,
150
+ fullName: payload.name as string | undefined,
151
+ preferredUsername
152
+ };
95
153
  }
96
- if (missingAzureConfig) {
97
- console.warn('MSAL configuration not found in environment variables');
154
+
155
+ const provider = factory.getByIssuer(issuer);
156
+
157
+ if (!provider) {
158
+ // Fallback to default extraction
159
+ const fullName = payload.name as string | undefined;
160
+ const preferredUsername = payload.preferred_username as string | undefined;
161
+ return {
162
+ email: payload.email as string | undefined || preferredUsername,
163
+ firstName: payload.given_name as string | undefined || fullName?.split(' ')[0],
164
+ lastName: payload.family_name as string | undefined || fullName?.split(' ')[1] || fullName?.split(' ')[0],
165
+ fullName,
166
+ preferredUsername
167
+ };
98
168
  }
99
169
 
100
- jwksClient({ jwksUri })
101
- .getSigningKey(header.kid)
102
- .then((key) => {
103
- cb(null, 'publicKey' in key ? key.publicKey : key.rsaPublicKey);
104
- })
105
- .catch((err) => console.error(err));
170
+ return provider.extractUserInfo(payload);
106
171
  };
107
172
 
108
173
  export const getSystemUser = async (dataSource?: sql.ConnectionPool, attemptCacheUpdateIfNeeded: boolean = true): Promise<UserInfo> => {
@@ -200,3 +265,6 @@ export const verifyUserRecord = async (
200
265
 
201
266
  return user;
202
267
  };
268
+
269
+ // Initialize providers on module load
270
+ initializeAuthProviders();
@@ -0,0 +1,31 @@
1
+ import { configInfo } from '../config.js';
2
+ import { AuthProviderConfig, LogError, LogStatus } from '@memberjunction/core';
3
+ import { AuthProviderFactory } from './AuthProviderFactory.js';
4
+
5
+ /**
6
+ * Initialize authentication providers from configuration
7
+ */
8
+ export function initializeAuthProviders(): void {
9
+ const factory = AuthProviderFactory.getInstance();
10
+
11
+ // Clear any existing providers
12
+ factory.clear();
13
+
14
+ // Initialize providers from authProviders config
15
+ if (configInfo.authProviders && configInfo.authProviders.length > 0) {
16
+ for (const providerConfig of configInfo.authProviders) {
17
+ try {
18
+ const provider = AuthProviderFactory.createProvider(providerConfig as AuthProviderConfig);
19
+ factory.register(provider);
20
+ LogStatus(`Registered auth provider: ${provider.name} (type: ${providerConfig.type})`);
21
+ } catch (error) {
22
+ LogError(`Failed to initialize auth provider ${providerConfig.name}: ${error}`);
23
+ }
24
+ }
25
+ }
26
+
27
+ // Validate we have at least one provider
28
+ if (!factory.hasProviders()) {
29
+ LogError('No authentication providers configured. Please configure authProviders array in mj.config.cjs');
30
+ }
31
+ }
@@ -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
+ }