@memberjunction/ng-auth-services 2.128.0 → 2.130.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 (27) hide show
  1. package/dist/lib/IAuthProvider.d.ts +177 -17
  2. package/dist/lib/IAuthProvider.d.ts.map +1 -1
  3. package/dist/lib/auth-types.d.ts +351 -0
  4. package/dist/lib/auth-types.d.ts.map +1 -0
  5. package/dist/lib/auth-types.js +98 -0
  6. package/dist/lib/auth-types.js.map +1 -0
  7. package/dist/lib/mjexplorer-auth-base.service.d.ts +204 -49
  8. package/dist/lib/mjexplorer-auth-base.service.d.ts.map +1 -1
  9. package/dist/lib/mjexplorer-auth-base.service.js +173 -67
  10. package/dist/lib/mjexplorer-auth-base.service.js.map +1 -1
  11. package/dist/lib/providers/mjexplorer-auth0-provider.service.d.ts +61 -17
  12. package/dist/lib/providers/mjexplorer-auth0-provider.service.d.ts.map +1 -1
  13. package/dist/lib/providers/mjexplorer-auth0-provider.service.js +245 -76
  14. package/dist/lib/providers/mjexplorer-auth0-provider.service.js.map +1 -1
  15. package/dist/lib/providers/mjexplorer-msal-provider.service.d.ts +57 -16
  16. package/dist/lib/providers/mjexplorer-msal-provider.service.d.ts.map +1 -1
  17. package/dist/lib/providers/mjexplorer-msal-provider.service.js +311 -114
  18. package/dist/lib/providers/mjexplorer-msal-provider.service.js.map +1 -1
  19. package/dist/lib/providers/mjexplorer-okta-provider.service.d.ts +55 -30
  20. package/dist/lib/providers/mjexplorer-okta-provider.service.d.ts.map +1 -1
  21. package/dist/lib/providers/mjexplorer-okta-provider.service.js +263 -235
  22. package/dist/lib/providers/mjexplorer-okta-provider.service.js.map +1 -1
  23. package/dist/public-api.d.ts +1 -0
  24. package/dist/public-api.d.ts.map +1 -1
  25. package/dist/public-api.js +2 -0
  26. package/dist/public-api.js.map +1 -1
  27. package/package.json +3 -3
@@ -7,15 +7,19 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
7
7
  var MJOktaProvider_1;
8
8
  import { Injectable, Inject } from '@angular/core';
9
9
  import { RegisterClass } from '@memberjunction/global';
10
- import { BehaviorSubject, from } from 'rxjs';
11
10
  import { MJAuthBase } from '../mjexplorer-auth-base.service';
12
11
  import OktaAuth from '@okta/okta-auth-js';
12
+ import { AuthErrorType } from '../auth-types';
13
13
  import * as i0 from "@angular/core";
14
14
  // Prevent tree-shaking by explicitly referencing the class
15
15
  export function LoadMJOktaProvider() {
16
16
  }
17
17
  /**
18
- * Okta authentication provider for MemberJunction Explorer
18
+ * Okta authentication provider implementation - v3.0.0
19
+ *
20
+ * Implements the abstract methods from MJAuthBase to hide Okta-specific details.
21
+ * The key abstraction is that Okta stores the JWT in IDToken.idToken,
22
+ * but consumers never need to know this detail.
19
23
  */
20
24
  let MJOktaProvider = class MJOktaProvider extends MJAuthBase {
21
25
  static { MJOktaProvider_1 = this; }
@@ -23,8 +27,7 @@ let MJOktaProvider = class MJOktaProvider extends MJAuthBase {
23
27
  static PROVIDER_TYPE = 'okta';
24
28
  type = MJOktaProvider_1.PROVIDER_TYPE;
25
29
  oktaAuth;
26
- userClaims$ = new BehaviorSubject(null);
27
- isRefreshing = false; // Flag to prevent re-triggering during refresh
30
+ isRefreshing = false;
28
31
  /**
29
32
  * Factory function to provide Angular dependencies required by Okta
30
33
  * Stored as a static property for the factory to access without instantiation
@@ -33,11 +36,11 @@ let MJOktaProvider = class MJOktaProvider extends MJAuthBase {
33
36
  {
34
37
  provide: 'oktaConfig',
35
38
  useValue: {
36
- clientId: environment.OKTA_CLIENTID,
37
- domain: environment.OKTA_DOMAIN,
38
- issuer: environment.OKTA_ISSUER || `https://${environment.OKTA_DOMAIN}/oauth2/default`,
39
- redirectUri: environment.OKTA_REDIRECT_URI || window.location.origin,
40
- scopes: environment.OKTA_SCOPES || ['openid', 'profile', 'email']
39
+ clientId: environment['OKTA_CLIENTID'],
40
+ domain: environment['OKTA_DOMAIN'],
41
+ issuer: environment['OKTA_ISSUER'] || `https://${environment['OKTA_DOMAIN']}/oauth2/default`,
42
+ redirectUri: environment['OKTA_REDIRECT_URI'] || window.location.origin,
43
+ scopes: environment['OKTA_SCOPES'] || ['openid', 'profile', 'email']
41
44
  }
42
45
  }
43
46
  ];
@@ -48,7 +51,7 @@ let MJOktaProvider = class MJOktaProvider extends MJAuthBase {
48
51
  };
49
52
  super(config);
50
53
  this.oktaConfig = oktaConfig;
51
- // Build configuration with defaults first, then spread oktaConfig to override
54
+ // Build configuration with defaults
52
55
  const oktaAuthConfig = {
53
56
  clientId: this.oktaConfig.clientId,
54
57
  redirectUri: this.oktaConfig.redirectUri || window.location.origin + '/callback',
@@ -61,25 +64,24 @@ let MJOktaProvider = class MJOktaProvider extends MJAuthBase {
61
64
  this.oktaAuth = new OktaAuth(oktaAuthConfig);
62
65
  // Listen for token events
63
66
  this.oktaAuth.authStateManager.subscribe((authState) => {
64
- this.updateAuthState(authState.isAuthenticated || false);
65
- // Don't update claims if we're in the middle of a refresh operation
66
- // to avoid triggering handleLogin in app.component
67
+ const state = authState;
68
+ this.updateAuthState(state.isAuthenticated || false);
69
+ // Don't update user info if we're in the middle of a refresh
67
70
  if (!this.isRefreshing) {
68
- if (authState.isAuthenticated && authState.idToken) {
69
- this.userClaims$.next(authState.idToken);
71
+ if (state.isAuthenticated && state.idToken) {
72
+ const userInfo = this.mapOktaTokenToStandard(state.idToken);
73
+ this.updateUserInfo(userInfo);
70
74
  }
71
75
  else {
72
- this.userClaims$.next(null);
76
+ this.updateUserInfo(null);
73
77
  }
74
78
  }
75
79
  });
76
- // Initialize Okta authentication state
77
- this.initializeOkta();
78
80
  }
79
- async initializeOkta() {
80
- // Start with unauthenticated state
81
- this.authenticated = false;
82
- this.updateAuthState(false);
81
+ // ============================================================================
82
+ // LIFECYCLE METHODS
83
+ // ============================================================================
84
+ async initialize() {
83
85
  // Check URL for logout indicator
84
86
  const urlParams = new URLSearchParams(window.location.search);
85
87
  const isPostLogout = urlParams.has('logout');
@@ -96,65 +98,42 @@ let MJOktaProvider = class MJOktaProvider extends MJAuthBase {
96
98
  const authState = await this.oktaAuth.authStateManager.getAuthState();
97
99
  if (authState?.isAuthenticated) {
98
100
  this.updateAuthState(true);
99
- this.authenticated = true;
100
- this.isAuthenticated$.next(true);
101
- // Get and emit the ID token with proper format
101
+ // Get and update user info
102
102
  const idToken = await this.oktaAuth.tokenManager.get('idToken');
103
- const accessToken = await this.oktaAuth.tokenManager.get('accessToken');
104
103
  if (idToken) {
105
- const claims = {
106
- ...idToken.claims,
107
- idToken: idToken.idToken, // The actual token string
108
- accessToken: accessToken?.accessToken,
109
- };
110
- this.userClaims$.next(claims);
104
+ const userInfo = this.mapOktaTokenToStandard(idToken);
105
+ this.updateUserInfo(userInfo);
111
106
  }
112
107
  }
113
108
  }
114
109
  catch (error) {
115
- console.error('Okta initialization redirect handling error:', error);
110
+ console.error('[Okta] Initialization redirect handling error:', error);
116
111
  }
117
112
  return; // Don't check for existing session after handling redirect
118
113
  }
119
114
  // Only check for existing session if not a redirect
120
- // This prevents auto-login after logout
121
115
  try {
122
- const isAuthenticated = this.oktaAuth ? await this.oktaAuth.isAuthenticated() : false;
123
- if (isAuthenticated && this.oktaAuth) {
116
+ const isAuthenticated = await this.oktaAuth.isAuthenticated();
117
+ if (isAuthenticated) {
124
118
  // Double-check we actually have valid tokens
125
119
  const idToken = await this.oktaAuth.tokenManager.get('idToken');
126
- const accessToken = await this.oktaAuth.tokenManager.get('accessToken');
127
- if (idToken && idToken.idToken) {
128
- // Format claims to match what app.component expects
129
- const claims = {
130
- ...idToken.claims,
131
- idToken: idToken.idToken, // The actual token string
132
- accessToken: accessToken?.accessToken,
133
- };
134
- this.userClaims$.next(claims);
135
- // Ensure the authenticated state is properly set
136
- this.authenticated = true;
120
+ if (idToken?.idToken) {
137
121
  this.updateAuthState(true);
138
- // Force the BehaviorSubject to emit the new value
139
- this.isAuthenticated$.next(true);
122
+ // Update user info from cached token
123
+ const userInfo = this.mapOktaTokenToStandard(idToken);
124
+ this.updateUserInfo(userInfo);
140
125
  }
141
126
  else {
142
127
  // No valid tokens, stay logged out
143
- this.authenticated = false;
144
128
  this.updateAuthState(false);
145
129
  }
146
130
  }
147
131
  }
148
132
  catch (error) {
149
- // If there's an error checking authentication, stay logged out
150
- console.warn('Error checking Okta authentication status:', error);
133
+ console.warn('[Okta] Error checking authentication status:', error);
151
134
  }
152
135
  }
153
- login(options) {
154
- // Convert Promise to Observable
155
- return from(this.loginAsync(options));
156
- }
157
- async loginAsync(options) {
136
+ async loginInternal(options) {
158
137
  try {
159
138
  // Check if we're in a redirect callback
160
139
  if (this.oktaAuth.isLoginRedirect()) {
@@ -163,12 +142,12 @@ let MJOktaProvider = class MJOktaProvider extends MJAuthBase {
163
142
  }
164
143
  // Start the login flow
165
144
  await this.oktaAuth.signInWithRedirect({
166
- originalUri: options?.targetUrl || '/',
145
+ originalUri: options?.['targetUrl'] || '/',
167
146
  ...options
168
147
  });
169
148
  }
170
149
  catch (error) {
171
- console.error('Okta login error:', error);
150
+ console.error('[Okta] Login error:', error);
172
151
  throw error;
173
152
  }
174
153
  }
@@ -176,22 +155,10 @@ let MJOktaProvider = class MJOktaProvider extends MJAuthBase {
176
155
  try {
177
156
  // Clear the local authentication state immediately
178
157
  this.updateAuthState(false);
179
- this.authenticated = false;
180
- this.isAuthenticated$.next(false);
181
- this.userClaims$.next(null);
158
+ this.updateUserInfo(null);
182
159
  // Clear all tokens from local storage
183
160
  await this.oktaAuth.tokenManager.clear();
184
- // Get the ID token to pass to logout (needed for proper logout)
185
- let idToken;
186
- try {
187
- const token = await this.oktaAuth.tokenManager.get('idToken');
188
- idToken = token?.idToken;
189
- }
190
- catch {
191
- // Token might already be cleared
192
- }
193
161
  // Sign out from Okta completely
194
- // The clearTokensBeforeRedirect ensures tokens are cleared before redirect
195
162
  await this.oktaAuth.signOut({
196
163
  postLogoutRedirectUri: window.location.origin,
197
164
  clearTokensBeforeRedirect: true
@@ -199,220 +166,281 @@ let MJOktaProvider = class MJOktaProvider extends MJAuthBase {
199
166
  // Note: The signOut call will redirect the browser, so code after this won't execute
200
167
  }
201
168
  catch (error) {
202
- console.error('Okta logout error:', error);
169
+ console.error('[Okta] Logout error:', error);
203
170
  // If logout fails, at least clear local state and reload
204
171
  window.location.href = window.location.origin;
205
172
  }
206
173
  }
207
- async refresh() {
174
+ async handleCallback() {
208
175
  try {
209
- // Set flag to prevent authStateManager from updating userClaims$
210
- this.isRefreshing = true;
211
- // First check if we're authenticated
212
- const isAuthenticated = await this.oktaAuth.isAuthenticated();
213
- if (!isAuthenticated) {
214
- // Not authenticated, can't refresh - return empty observable
215
- console.warn('Cannot refresh tokens - user is not authenticated');
216
- this.isRefreshing = false;
217
- // Don't update the claims observable to avoid triggering handleLogin
218
- return from([null]);
219
- }
220
- // Check if tokens exist and are not expired
221
- const idToken = await this.oktaAuth.tokenManager.get('idToken');
222
- const accessToken = await this.oktaAuth.tokenManager.get('accessToken');
223
- if (!idToken || !accessToken) {
224
- console.warn('No tokens available to refresh');
225
- this.isRefreshing = false;
226
- // Don't update the claims observable to avoid triggering handleLogin
227
- return from([null]);
228
- }
229
- // Attempt to renew tokens using the refresh token if available
230
- // Note: For PKCE flow (which we're using), Okta will try to use refresh tokens if configured
231
- // If refresh tokens aren't available, it will attempt silent authentication via iframe
232
- const renewedTokens = await this.oktaAuth.token.renewTokens();
233
- // Store the renewed tokens - this will trigger authStateManager but we'll ignore it
234
- if (renewedTokens.idToken) {
235
- this.oktaAuth.tokenManager.setTokens(renewedTokens);
236
- // Update user claims with renewed token - but DON'T emit to userClaims$
237
- // to avoid triggering handleLogin in app.component
238
- const newIdToken = renewedTokens.idToken;
239
- const claims = {
240
- ...newIdToken.claims,
241
- idToken: newIdToken.idToken,
242
- accessToken: renewedTokens.accessToken?.accessToken,
243
- };
244
- // Wait a moment before resetting the flag to ensure authStateManager event is handled
245
- await new Promise(resolve => setTimeout(resolve, 100));
246
- // Reset flag after tokens are set
247
- this.isRefreshing = false;
248
- // Return the claims directly without updating the BehaviorSubject
249
- // This prevents the app component from re-initializing GraphQL
250
- return from([claims]);
176
+ if (this.oktaAuth.isLoginRedirect()) {
177
+ await this.oktaAuth.handleLoginRedirect();
178
+ // After handling redirect, check if we're authenticated
179
+ const authState = await this.oktaAuth.authStateManager.getAuthState();
180
+ if (authState?.isAuthenticated) {
181
+ // Do a controlled reload after successful login
182
+ setTimeout(() => {
183
+ window.location.href = window.location.origin;
184
+ }, 100);
185
+ }
251
186
  }
252
- this.isRefreshing = false;
253
- return from([null]);
254
187
  }
255
188
  catch (error) {
256
- console.error('Okta token refresh error:', error);
257
- this.isRefreshing = false; // Reset flag on error
258
- // Check if the error is due to expired session or no prompt allowed
259
- if (error?.errorCode === 'login_required' ||
260
- error?.message?.includes('not to prompt') ||
261
- error?.message?.includes('login_required')) {
262
- // Session has expired, user needs to re-authenticate
263
- console.warn('Session expired - user needs to re-authenticate');
264
- // Don't update the claims observable to avoid triggering handleLogin
265
- return from([null]);
266
- }
267
- // For other errors, still throw
189
+ console.error('[Okta] Callback handling error:', error);
268
190
  throw error;
269
191
  }
270
192
  }
271
- isAuthenticated() {
272
- // Return the BehaviorSubject as an observable to ensure consistency
273
- // This way all consumers get the same state
274
- return this.isAuthenticated$.asObservable();
275
- }
276
- async getUser() {
193
+ // ============================================================================
194
+ // ABSTRACT METHOD IMPLEMENTATIONS (v3.0.0)
195
+ // ============================================================================
196
+ /**
197
+ * Extract ID token from Okta's storage
198
+ *
199
+ * Okta stores the JWT in IDToken.idToken
200
+ * This is the key abstraction - consumers never need to know about Okta's structure!
201
+ */
202
+ async extractIdTokenInternal() {
277
203
  try {
278
- const isAuth = await this.oktaAuth.isAuthenticated();
279
- if (!isAuth) {
280
- return null;
281
- }
282
- // Get user info from Okta
283
- const user = await this.oktaAuth.getUser();
284
- return user;
204
+ const idToken = await this.oktaAuth.tokenManager.get('idToken');
205
+ // Okta-specific detail: JWT is in idToken property
206
+ return idToken?.idToken || null;
285
207
  }
286
208
  catch (error) {
287
- console.error('Okta get user error:', error);
209
+ console.error('[Okta] Error extracting ID token:', error);
288
210
  return null;
289
211
  }
290
212
  }
291
- async getUserClaims() {
213
+ /**
214
+ * Extract complete token info from Okta
215
+ *
216
+ * Maps Okta's token structure to StandardAuthToken
217
+ */
218
+ async extractTokenInfoInternal() {
292
219
  try {
293
- const authState = await this.oktaAuth.authStateManager.getAuthState();
294
- if (authState?.isAuthenticated && authState.idToken) {
295
- const idToken = authState.idToken;
296
- // Format the claims to match what the app expects
297
- // The app.component expects claims.idToken to contain the actual token string
298
- const claims = {
299
- ...idToken.claims,
300
- idToken: idToken.idToken, // Add the actual token string
301
- accessToken: authState.accessToken?.accessToken,
302
- };
303
- this.userClaims$.next(claims);
304
- }
305
- else {
306
- this.userClaims$.next(null);
220
+ const idToken = await this.oktaAuth.tokenManager.get('idToken');
221
+ const accessToken = await this.oktaAuth.tokenManager.get('accessToken');
222
+ if (!idToken?.idToken) {
223
+ return null;
307
224
  }
308
- return this.userClaims$.asObservable();
225
+ return {
226
+ idToken: idToken.idToken,
227
+ accessToken: accessToken?.accessToken,
228
+ expiresAt: idToken.expiresAt ? idToken.expiresAt * 1000 : 0, // Convert to milliseconds
229
+ scopes: idToken.scopes
230
+ };
309
231
  }
310
232
  catch (error) {
311
- console.error('Okta get user claims error:', error);
312
- return from([null]);
313
- }
314
- }
315
- checkExpiredTokenError(error) {
316
- // Check for Okta-specific token expiration errors
317
- if (!error || typeof error !== 'string') {
318
- return false;
233
+ console.error('[Okta] Error extracting token info:', error);
234
+ return null;
319
235
  }
320
- const errorLower = error.toLowerCase();
321
- return errorLower.includes('token expired') ||
322
- errorLower.includes('invalid_token') ||
323
- errorLower.includes('token_expired') ||
324
- errorLower.includes('unauthorized');
325
236
  }
326
237
  /**
327
- * Handle callback after redirect from Okta
238
+ * Extract user info from Okta claims
239
+ *
240
+ * Maps Okta's IDToken structure to StandardUserInfo
328
241
  */
329
- async handleCallback() {
242
+ async extractUserInfoInternal() {
330
243
  try {
331
- if (this.oktaAuth.isLoginRedirect()) {
332
- await this.oktaAuth.handleLoginRedirect();
333
- // After handling redirect, check if we're authenticated
334
- const authState = await this.oktaAuth.authStateManager.getAuthState();
335
- if (authState?.isAuthenticated) {
336
- // Do a controlled reload after successful login
337
- // This ensures the app fully reinitializes with the authenticated state
338
- setTimeout(() => {
339
- window.location.href = window.location.origin;
340
- }, 100);
341
- }
244
+ const idToken = await this.oktaAuth.tokenManager.get('idToken');
245
+ if (!idToken) {
246
+ return null;
342
247
  }
248
+ return this.mapOktaTokenToStandard(idToken);
343
249
  }
344
250
  catch (error) {
345
- console.error('Okta callback handling error:', error);
346
- throw error;
251
+ console.error('[Okta] Error extracting user info:', error);
252
+ return null;
347
253
  }
348
254
  }
349
255
  /**
350
- * Get the access token for API calls
256
+ * Refresh token using Okta's token renewal
257
+ *
258
+ * Uses renewTokens() to get new tokens silently
351
259
  */
352
- async getAccessToken() {
260
+ async refreshTokenInternal() {
353
261
  try {
262
+ console.log('[Okta] Attempting to refresh token...');
263
+ // Set flag to prevent authStateManager from triggering updates
264
+ this.isRefreshing = true;
265
+ // First check if we're authenticated
266
+ const isAuthenticated = await this.oktaAuth.isAuthenticated();
267
+ if (!isAuthenticated) {
268
+ this.isRefreshing = false;
269
+ return {
270
+ success: false,
271
+ error: {
272
+ type: AuthErrorType.NO_ACTIVE_SESSION,
273
+ message: 'User is not authenticated',
274
+ userMessage: 'You need to log in to continue.'
275
+ }
276
+ };
277
+ }
278
+ // Check if tokens exist
279
+ const idToken = await this.oktaAuth.tokenManager.get('idToken');
354
280
  const accessToken = await this.oktaAuth.tokenManager.get('accessToken');
355
- return accessToken?.accessToken;
281
+ if (!idToken || !accessToken) {
282
+ this.isRefreshing = false;
283
+ return {
284
+ success: false,
285
+ error: {
286
+ type: AuthErrorType.NO_ACTIVE_SESSION,
287
+ message: 'No tokens available to refresh',
288
+ userMessage: 'Session not found. Please log in again.'
289
+ }
290
+ };
291
+ }
292
+ // Attempt to renew tokens
293
+ const renewedTokens = await this.oktaAuth.token.renewTokens();
294
+ if (renewedTokens.idToken) {
295
+ // Store the renewed tokens
296
+ this.oktaAuth.tokenManager.setTokens(renewedTokens);
297
+ // Wait a moment before resetting the flag
298
+ await new Promise(resolve => setTimeout(resolve, 100));
299
+ this.isRefreshing = false;
300
+ const newIdToken = renewedTokens.idToken;
301
+ console.log('[Okta] Token refresh successful', {
302
+ expiresAt: newIdToken.expiresAt ? new Date(newIdToken.expiresAt * 1000).toISOString() : 'N/A'
303
+ });
304
+ const token = {
305
+ idToken: newIdToken.idToken || '',
306
+ accessToken: renewedTokens.accessToken?.accessToken,
307
+ expiresAt: newIdToken.expiresAt ? newIdToken.expiresAt * 1000 : 0,
308
+ scopes: newIdToken.scopes
309
+ };
310
+ return {
311
+ success: true,
312
+ token
313
+ };
314
+ }
315
+ this.isRefreshing = false;
316
+ return {
317
+ success: false,
318
+ error: {
319
+ type: AuthErrorType.NO_ACTIVE_SESSION,
320
+ message: 'Token renewal succeeded but no ID token returned',
321
+ userMessage: 'Session refresh failed. Please log in again.'
322
+ }
323
+ };
356
324
  }
357
325
  catch (error) {
358
- console.error('Error getting access token:', error);
359
- return undefined;
326
+ console.error('[Okta] Token refresh failed:', error);
327
+ this.isRefreshing = false;
328
+ return {
329
+ success: false,
330
+ error: this.classifyErrorInternal(error)
331
+ };
360
332
  }
361
333
  }
362
334
  /**
363
- * Get the ID token
335
+ * Classify Okta-specific errors into semantic types
336
+ *
337
+ * Maps Okta error patterns to AuthErrorType enum
364
338
  */
365
- async getIdToken() {
366
- try {
367
- const idToken = await this.oktaAuth.tokenManager.get('idToken');
368
- return idToken?.idToken;
339
+ classifyErrorInternal(error) {
340
+ const errorObj = error;
341
+ const message = errorObj?.['message'] || 'Unknown error';
342
+ const errorCode = errorObj?.['errorCode'] || '';
343
+ // Check for specific Okta error patterns
344
+ if (errorCode === 'login_required' || message.includes('login_required')) {
345
+ return {
346
+ type: AuthErrorType.NO_ACTIVE_SESSION,
347
+ message,
348
+ originalError: error,
349
+ userMessage: 'You need to log in to continue.'
350
+ };
369
351
  }
370
- catch (error) {
371
- console.error('Error getting ID token:', error);
372
- return undefined;
352
+ if (message.includes('not to prompt') || message.includes('consent_required')) {
353
+ return {
354
+ type: AuthErrorType.INTERACTION_REQUIRED,
355
+ message,
356
+ originalError: error,
357
+ userMessage: 'Additional authentication is required. Please log in again.'
358
+ };
359
+ }
360
+ if (errorCode === 'user_cancelled' || message.includes('user cancelled')) {
361
+ return {
362
+ type: AuthErrorType.USER_CANCELLED,
363
+ message,
364
+ originalError: error,
365
+ userMessage: 'Login was cancelled.'
366
+ };
373
367
  }
368
+ if (message.includes('token expired') || message.includes('invalid_token') || message.includes('unauthorized')) {
369
+ return {
370
+ type: AuthErrorType.TOKEN_EXPIRED,
371
+ message,
372
+ originalError: error,
373
+ userMessage: 'Your session has expired. Please log in again.'
374
+ };
375
+ }
376
+ if (message.includes('network') || message.includes('fetch')) {
377
+ return {
378
+ type: AuthErrorType.NETWORK_ERROR,
379
+ message,
380
+ originalError: error,
381
+ userMessage: 'Network error. Please check your connection and try again.'
382
+ };
383
+ }
384
+ // Default to unknown error
385
+ return {
386
+ type: AuthErrorType.UNKNOWN_ERROR,
387
+ message,
388
+ originalError: error,
389
+ userMessage: 'An unexpected error occurred. Please try again.'
390
+ };
374
391
  }
375
- // Add required methods for the new interface
376
- async initialize() {
377
- // Check if we're returning from a redirect
378
- if (this.oktaAuth.isLoginRedirect()) {
379
- try {
380
- await this.oktaAuth.handleLoginRedirect();
381
- // After handling redirect, check if we're authenticated
382
- const authState = await this.oktaAuth.authStateManager.getAuthState();
383
- if (authState?.isAuthenticated) {
384
- this.updateAuthState(true);
385
- // Do a controlled reload after successful login
386
- // This ensures the app fully reinitializes with the authenticated state
387
- setTimeout(() => {
388
- window.location.href = window.location.origin;
389
- }, 100);
390
- return; // Exit early since we're reloading
391
- }
392
+ // ============================================================================
393
+ // HELPER METHODS
394
+ // ============================================================================
395
+ /**
396
+ * Map Okta IDToken to StandardUserInfo
397
+ */
398
+ mapOktaTokenToStandard(idToken) {
399
+ const claims = idToken.claims;
400
+ return {
401
+ id: claims.sub || '',
402
+ email: claims.email || '',
403
+ name: claims.name || '',
404
+ givenName: claims.given_name,
405
+ familyName: claims.family_name,
406
+ preferredUsername: claims.preferred_username,
407
+ emailVerified: claims.email_verified
408
+ };
409
+ }
410
+ /**
411
+ * Get profile picture URL from Okta
412
+ *
413
+ * Okta may include picture URL in user claims, similar to Auth0.
414
+ * If available, we can also fetch from Okta's /userinfo endpoint.
415
+ */
416
+ async getProfilePictureUrlInternal() {
417
+ try {
418
+ const idToken = await this.oktaAuth.tokenManager.get('idToken');
419
+ if (!idToken) {
420
+ return null;
392
421
  }
393
- catch (error) {
394
- console.error('Okta initialization redirect handling error:', error);
422
+ // Check if picture is in claims
423
+ const pictureUrl = idToken.claims.picture;
424
+ if (pictureUrl) {
425
+ return pictureUrl;
395
426
  }
427
+ // Alternatively, fetch from userinfo endpoint
428
+ const user = await this.oktaAuth.getUser();
429
+ return user?.picture || null;
430
+ }
431
+ catch (error) {
432
+ console.error('[Okta] Error getting profile picture:', error);
433
+ return null;
396
434
  }
397
- // Check authentication status
398
- const isAuthenticated = this.oktaAuth ? await this.oktaAuth.isAuthenticated() : false;
399
- this.updateAuthState(isAuthenticated);
400
- }
401
- async loginInternal(options) {
402
- await this.loginAsync(options);
403
- }
404
- async getToken() {
405
- // For Okta, we need to return the ID token (not access token) for backend authentication
406
- // The ID token contains the user claims that the backend expects
407
- const idToken = await this.getIdToken();
408
- return idToken || null;
409
435
  }
436
+ // ============================================================================
437
+ // CONFIGURATION
438
+ // ============================================================================
410
439
  getRequiredConfig() {
411
440
  return ['clientId', 'domain'];
412
441
  }
413
- validateConfig(_config) {
414
- // Prefix with underscore to indicate intentionally unused
415
- return _config.clientId && (_config.domain || _config.issuer);
442
+ validateConfig(config) {
443
+ return !!(config['clientId'] && (config['domain'] || config['issuer']));
416
444
  }
417
445
  static ɵfac = function MJOktaProvider_Factory(t) { return new (t || MJOktaProvider)(i0.ɵɵinject('oktaConfig')); };
418
446
  static ɵprov = /*@__PURE__*/ i0.ɵɵdefineInjectable({ token: MJOktaProvider, factory: MJOktaProvider.ɵfac, providedIn: 'root' });