@memberjunction/ng-auth-services 2.129.0 → 2.130.1

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 +179 -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 +225 -49
  8. package/dist/lib/mjexplorer-auth-base.service.d.ts.map +1 -1
  9. package/dist/lib/mjexplorer-auth-base.service.js +189 -54
  10. package/dist/lib/mjexplorer-auth-base.service.js.map +1 -1
  11. package/dist/lib/providers/mjexplorer-auth0-provider.service.d.ts +69 -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 +255 -75
  14. package/dist/lib/providers/mjexplorer-auth0-provider.service.js.map +1 -1
  15. package/dist/lib/providers/mjexplorer-msal-provider.service.d.ts +65 -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 +345 -113
  18. package/dist/lib/providers/mjexplorer-msal-provider.service.js.map +1 -1
  19. package/dist/lib/providers/mjexplorer-okta-provider.service.d.ts +63 -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 +273 -234
  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
@@ -1,14 +1,33 @@
1
1
  import { Observable, BehaviorSubject } from 'rxjs';
2
2
  import { IAngularAuthProvider, AngularAuthProviderConfig } from './IAuthProvider';
3
+ import { StandardUserInfo, StandardAuthToken, StandardAuthError, TokenRefreshResult } from './auth-types';
3
4
  import * as i0 from "@angular/core";
4
5
  /**
5
- * Base class for Angular authentication providers
6
- * Provides common functionality and enforces the provider interface
6
+ * Base class for Angular authentication providers - v3.0.0
7
+ *
8
+ * Provides common functionality and enforces the provider interface.
9
+ * All concrete providers (MSAL, Auth0, Okta) should extend this class.
10
+ *
11
+ * ## Key Improvements in v3.0:
12
+ * - Proper abstraction - no more leaky provider-specific logic
13
+ * - Standardized types - no more `any` types
14
+ * - Clean token access - no more `__raw || idToken` patterns
15
+ * - Semantic error handling - no more provider-specific error checks
16
+ *
17
+ * ## For Provider Implementers:
18
+ * Extend this class and implement the abstract methods:
19
+ * - `extractIdTokenInternal()` - Extract token from provider storage
20
+ * - `extractTokenInfoInternal()` - Extract full token info
21
+ * - `extractUserInfoInternal()` - Map claims to StandardUserInfo
22
+ * - `refreshTokenInternal()` - Implement refresh mechanism
23
+ * - `classifyErrorInternal()` - Map errors to AuthErrorType
24
+ *
25
+ * @version 3.0.0
7
26
  */
8
27
  export declare abstract class MJAuthBase implements IAngularAuthProvider {
9
28
  protected config: AngularAuthProviderConfig;
10
29
  protected isAuthenticated$: BehaviorSubject<boolean>;
11
- protected userProfile$: BehaviorSubject<any>;
30
+ protected userInfo$: BehaviorSubject<StandardUserInfo | null>;
12
31
  protected userEmail$: BehaviorSubject<string>;
13
32
  private _initialPath;
14
33
  private _initialSearch;
@@ -20,92 +39,249 @@ export declare abstract class MJAuthBase implements IAngularAuthProvider {
20
39
  * Contains the initial search/query string from window.location.search before any work was done by auth services
21
40
  */
22
41
  get initialSearch(): string | null;
23
- abstract type: string;
42
+ /**
43
+ * Provider type identifier
44
+ * Must be implemented by concrete providers
45
+ */
46
+ abstract readonly type: string;
24
47
  constructor(config: AngularAuthProviderConfig);
25
48
  /**
26
- * Initialize the authentication provider
27
- * Override in subclasses to setup provider-specific initialization
49
+ * Initialize the provider
50
+ *
51
+ * Subclasses should override to set up provider-specific initialization,
52
+ * handle redirect callbacks, restore sessions, etc.
28
53
  */
29
54
  abstract initialize(): Promise<void>;
30
55
  /**
31
- * Login the user - internal implementation
32
- * Override to implement provider-specific login flow
56
+ * Internal login implementation
57
+ *
58
+ * Subclasses implement provider-specific login flow.
59
+ * This is called by the public login() method.
33
60
  */
34
- protected abstract loginInternal(options?: any): Promise<void>;
61
+ protected abstract loginInternal(options?: Record<string, unknown>): Promise<void>;
35
62
  /**
36
- * Login the user - public API that returns Observable for backward compatibility
63
+ * Logout implementation
64
+ *
65
+ * Subclasses implement provider-specific logout flow.
37
66
  */
38
- login(options?: any): Observable<void>;
67
+ abstract logout(): Promise<void>;
39
68
  /**
40
- * Logout the user
41
- * Override to implement provider-specific logout flow
69
+ * Handle OAuth callback
70
+ *
71
+ * Subclasses implement provider-specific callback handling.
42
72
  */
43
- abstract logout(): Promise<void>;
73
+ abstract handleCallback(): Promise<void>;
44
74
  /**
45
- * Get the current access token
46
- * Override to retrieve token from provider-specific storage
75
+ * Extract ID token from provider-specific storage
76
+ *
77
+ * This is where providers hide their implementation details.
78
+ * - Auth0: Extracts from claims.__raw
79
+ * - MSAL: Extracts from response.idToken
80
+ * - Okta: Extracts from authState.idToken
81
+ *
82
+ * @returns Promise resolving to token string or null if not authenticated
47
83
  */
48
- abstract getToken(): Promise<string | null>;
84
+ protected abstract extractIdTokenInternal(): Promise<string | null>;
49
85
  /**
50
- * Handle callback after redirect
51
- * Override to process provider-specific callback logic
86
+ * Extract complete token info from provider-specific storage
87
+ *
88
+ * Maps provider-specific token structure to StandardAuthToken.
89
+ *
90
+ * @returns Promise resolving to StandardAuthToken or null if not authenticated
52
91
  */
53
- abstract handleCallback(): Promise<void>;
92
+ protected abstract extractTokenInfoInternal(): Promise<StandardAuthToken | null>;
54
93
  /**
55
- * Check if user is authenticated
94
+ * Extract user info from provider-specific claims
95
+ *
96
+ * Maps provider-specific claim structure to StandardUserInfo.
97
+ * This is where providers translate their claims (sub, email, name, etc.)
98
+ * into the standard structure.
99
+ *
100
+ * @returns Promise resolving to StandardUserInfo or null if not authenticated
56
101
  */
57
- isAuthenticated(): Observable<boolean>;
102
+ protected abstract extractUserInfoInternal(): Promise<StandardUserInfo | null>;
58
103
  /**
59
- * Get user profile information
104
+ * Refresh token using provider-specific mechanism
105
+ *
106
+ * Implements the provider's token refresh logic using whatever mechanism
107
+ * is appropriate (silent refresh with refresh tokens, iframe-based token
108
+ * acquisition, etc.).
109
+ *
110
+ * Should return success with token if refresh succeeds, or failure with
111
+ * appropriate error type (TOKEN_EXPIRED, INTERACTION_REQUIRED, etc.) if
112
+ * refresh fails.
113
+ *
114
+ * @returns Promise resolving to TokenRefreshResult indicating success/failure
60
115
  */
61
- getUserProfile(): Observable<any>;
116
+ protected abstract refreshTokenInternal(): Promise<TokenRefreshResult>;
62
117
  /**
63
- * Get user email
118
+ * Classify provider-specific error into standard error type
119
+ *
120
+ * Maps provider-specific errors to semantic AuthErrorType values.
121
+ * Examines error objects, error codes, and messages to determine the
122
+ * appropriate category (TOKEN_EXPIRED, INTERACTION_REQUIRED, NETWORK_ERROR, etc.).
123
+ *
124
+ * @param error The error to classify
125
+ * @returns StandardAuthError with categorized type and user-friendly message
64
126
  */
65
- getUserEmail(): Observable<string>;
127
+ protected abstract classifyErrorInternal(error: unknown): StandardAuthError;
66
128
  /**
67
- * Get provider-specific configuration requirements
68
- * Override to specify required config fields
129
+ * Get profile picture URL from auth provider
130
+ *
131
+ * Retrieves the user's profile picture using provider-specific mechanisms.
132
+ * Some providers include the URL in user claims, others require API calls
133
+ * to fetch the image.
134
+ *
135
+ * @returns Promise resolving to image URL or null if not available
69
136
  */
70
- getRequiredConfig(): string[];
137
+ protected abstract getProfilePictureUrlInternal(): Promise<string | null>;
71
138
  /**
72
- * Validate provider configuration
73
- * Override to add provider-specific validation
139
+ * Handle session expiry when silent refresh fails
140
+ *
141
+ * Called internally when silent token refresh fails with TOKEN_EXPIRED or
142
+ * INTERACTION_REQUIRED errors. Providers that support refresh tokens can
143
+ * implement this as a no-op. Providers that require interactive re-authentication
144
+ * should initiate the appropriate flow (redirect, popup, etc.).
145
+ *
146
+ * Note: If this method redirects the page, it may never return. The app will
147
+ * reload after authentication completes and re-initialize with a fresh token.
148
+ *
149
+ * @returns Promise that resolves if re-auth completed, or never returns if redirected
74
150
  */
75
- validateConfig(config: any): boolean;
151
+ protected abstract handleSessionExpiryInternal(): Promise<void>;
76
152
  /**
77
- * Helper method to update authentication state
153
+ * Public login method with Observable wrapper for backward compatibility
154
+ *
155
+ * Consumers can use either:
156
+ * - `await this.authBase.login()` (Promise)
157
+ * - `this.authBase.login().subscribe()` (Observable)
78
158
  */
79
- protected updateAuthState(isAuthenticated: boolean): void;
159
+ login(options?: Record<string, unknown>): Observable<void>;
160
+ /**
161
+ * Check if user is authenticated (Observable stream)
162
+ *
163
+ * Returns a reactive stream that emits authentication state changes.
164
+ * Consumers can subscribe to react to login/logout events.
165
+ */
166
+ isAuthenticated(): Observable<boolean>;
167
+ /**
168
+ * Get user info as Observable stream
169
+ *
170
+ * Returns standardized user info, hiding provider-specific claim structures.
171
+ * No more need for consumers to merge claims or check provider-specific fields!
172
+ */
173
+ getUserInfo(): Observable<StandardUserInfo | null>;
80
174
  /**
81
- * Helper method to update user profile
175
+ * Get user email as Observable stream
82
176
  */
83
- protected updateUserProfile(profile: any): void;
177
+ getUserEmail(): Observable<string>;
84
178
  /**
85
- * Legacy property for authentication state
179
+ * Get ID token string (primary token method)
180
+ *
181
+ * This is the clean abstraction - no provider-specific logic needed!
182
+ * Replaces the old pattern of: `claims?.__raw || claims?.idToken`
183
+ *
184
+ * @example
185
+ * ```typescript
186
+ * const token = await this.authBase.getIdToken();
187
+ * if (token) {
188
+ * setupGraphQLClient(token, apiUrl);
189
+ * }
190
+ * ```
86
191
  */
87
- get authenticated(): boolean;
192
+ getIdToken(): Promise<string | null>;
88
193
  /**
89
- * Legacy setter for authentication state
90
- * Used by MJExplorer for backward compatibility
194
+ * Get complete token information
195
+ *
196
+ * Returns full token details including expiration and scopes.
197
+ * Use this when you need more than just the token string.
91
198
  */
92
- set authenticated(value: boolean);
199
+ getTokenInfo(): Promise<StandardAuthToken | null>;
93
200
  /**
94
- * Legacy method to get user
201
+ * Refresh authentication token
202
+ *
203
+ * Attempts to obtain a fresh authentication token using the provider's
204
+ * refresh mechanism. If silent refresh fails due to session expiry, the
205
+ * provider will handle re-authentication automatically (which may involve
206
+ * redirecting to the auth provider's login page).
207
+ *
208
+ * Returns StandardAuthToken on success, or throws on complete failure.
209
+ *
210
+ * IMPORTANT: If the provider requires interactive re-authentication (redirect
211
+ * or popup), this method may never return. The app will reload after
212
+ * authentication completes and re-initialize with a fresh token.
213
+ *
214
+ * @returns Promise resolving to StandardAuthToken or throws on failure
215
+ *
216
+ * @example
217
+ * ```typescript
218
+ * const token = await this.authBase.refreshToken();
219
+ * return token.idToken; // Always succeeds or throws
220
+ * ```
95
221
  */
96
- getUser(): Promise<any>;
222
+ refreshToken(): Promise<StandardAuthToken>;
97
223
  /**
98
- * Legacy method to get user claims
224
+ * Classify an error into standard error type
225
+ *
226
+ * Converts provider-specific errors into semantic categories.
227
+ * Eliminates need for consumers to check error.name or error types.
228
+ *
229
+ * @example
230
+ * ```typescript
231
+ * const authError = this.authBase.classifyError(err);
232
+ * if (authError.type === AuthErrorType.TOKEN_EXPIRED) {
233
+ * this.showMessage(authError.userMessage);
234
+ * }
235
+ * ```
99
236
  */
100
- getUserClaims(): Promise<Observable<any>>;
237
+ classifyError(error: unknown): StandardAuthError;
101
238
  /**
102
- * Legacy method to check expired token error
239
+ * Get profile picture URL from auth provider
240
+ *
241
+ * Returns the user's profile picture URL if available from the auth provider.
242
+ * This abstracts away provider-specific logic:
243
+ * - Microsoft/MSAL: Fetches from Graph API
244
+ * - Auth0/Okta: Returns from user claims
245
+ *
246
+ * @returns Promise resolving to image URL or null if not available
247
+ *
248
+ * @example
249
+ * ```typescript
250
+ * const pictureUrl = await this.authBase.getProfilePictureUrl();
251
+ * if (pictureUrl) {
252
+ * this.userAvatar = pictureUrl;
253
+ * }
254
+ * ```
103
255
  */
104
- checkExpiredTokenError(error: string): boolean;
256
+ getProfilePictureUrl(): Promise<string | null>;
257
+ /**
258
+ * Update authentication state
259
+ *
260
+ * Subclasses should call this when authentication state changes
261
+ * (after login, logout, session check, etc.)
262
+ */
263
+ protected updateAuthState(isAuthenticated: boolean): void;
105
264
  /**
106
- * Legacy refresh method
265
+ * Update user info
266
+ *
267
+ * Subclasses should call this when user info is retrieved or updated.
268
+ * This automatically updates the email stream as well.
269
+ */
270
+ protected updateUserInfo(userInfo: StandardUserInfo | null): void;
271
+ /**
272
+ * Get required configuration fields
273
+ *
274
+ * Default implementation requires clientId.
275
+ * Subclasses can override to add provider-specific requirements.
276
+ */
277
+ getRequiredConfig(): string[];
278
+ /**
279
+ * Validate provider configuration
280
+ *
281
+ * Checks that all required fields are present and non-empty.
282
+ * Subclasses can override to add custom validation logic.
107
283
  */
108
- refresh(): Promise<Observable<any>>;
284
+ validateConfig(config: Record<string, unknown>): boolean;
109
285
  static ɵfac: i0.ɵɵFactoryDeclaration<MJAuthBase, never>;
110
286
  static ɵprov: i0.ɵɵInjectableDeclaration<MJAuthBase>;
111
287
  }
@@ -1 +1 @@
1
- {"version":3,"file":"mjexplorer-auth-base.service.d.ts","sourceRoot":"","sources":["../../src/lib/mjexplorer-auth-base.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,eAAe,EAAQ,MAAM,MAAM,CAAC;AACzD,OAAO,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAC;;AAElF;;;GAGG;AACH,8BACsB,UAAW,YAAW,oBAAoB;IAC9D,SAAS,CAAC,MAAM,EAAE,yBAAyB,CAAC;IAC5C,SAAS,CAAC,gBAAgB,2BAAuC;IACjE,SAAS,CAAC,YAAY,uBAAkC;IACxD,SAAS,CAAC,UAAU,0BAAmC;IAEvD,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,cAAc,CAAuB;IAC7C;;OAEG;IACH,IAAI,WAAW,IAAI,MAAM,GAAG,IAAI,CAE/B;IACD;;OAEG;IACH,IAAI,aAAa,IAAI,MAAM,GAAG,IAAI,CAEjC;IAED,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBAEV,MAAM,EAAE,yBAAyB;IAM7C;;;OAGG;IACH,QAAQ,CAAC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAEpC;;;OAGG;IACH,SAAS,CAAC,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IAE9D;;OAEG;IACH,KAAK,CAAC,OAAO,CAAC,EAAE,GAAG,GAAG,UAAU,CAAC,IAAI,CAAC;IAItC;;;OAGG;IACH,QAAQ,CAAC,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAEhC;;;OAGG;IACH,QAAQ,CAAC,QAAQ,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAE3C;;;OAGG;IACH,QAAQ,CAAC,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAExC;;OAEG;IACH,eAAe,IAAI,UAAU,CAAC,OAAO,CAAC;IAItC;;OAEG;IACH,cAAc,IAAI,UAAU,CAAC,GAAG,CAAC;IAIjC;;OAEG;IACH,YAAY,IAAI,UAAU,CAAC,MAAM,CAAC;IAIlC;;;OAGG;IACH,iBAAiB,IAAI,MAAM,EAAE;IAI7B;;;OAGG;IACH,cAAc,CAAC,MAAM,EAAE,GAAG,GAAG,OAAO;IAKpC;;OAEG;IACH,SAAS,CAAC,eAAe,CAAC,eAAe,EAAE,OAAO,GAAG,IAAI;IAIzD;;OAEG;IACH,SAAS,CAAC,iBAAiB,CAAC,OAAO,EAAE,GAAG,GAAG,IAAI;IAS/C;;OAEG;IACH,IAAI,aAAa,IAAI,OAAO,CAE3B;IAED;;;OAGG;IACH,IAAI,aAAa,CAAC,KAAK,EAAE,OAAO,EAE/B;IAED;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC;IAI7B;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;IAI/C;;OAEG;IACH,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAO9C;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;yCArKrB,UAAU;6CAAV,UAAU;CA0K/B"}
1
+ {"version":3,"file":"mjexplorer-auth-base.service.d.ts","sourceRoot":"","sources":["../../src/lib/mjexplorer-auth-base.service.ts"],"names":[],"mappings":"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"}
@@ -2,13 +2,32 @@ import { Injectable } from '@angular/core';
2
2
  import { BehaviorSubject, from } from 'rxjs';
3
3
  import * as i0 from "@angular/core";
4
4
  /**
5
- * Base class for Angular authentication providers
6
- * Provides common functionality and enforces the provider interface
5
+ * Base class for Angular authentication providers - v3.0.0
6
+ *
7
+ * Provides common functionality and enforces the provider interface.
8
+ * All concrete providers (MSAL, Auth0, Okta) should extend this class.
9
+ *
10
+ * ## Key Improvements in v3.0:
11
+ * - Proper abstraction - no more leaky provider-specific logic
12
+ * - Standardized types - no more `any` types
13
+ * - Clean token access - no more `__raw || idToken` patterns
14
+ * - Semantic error handling - no more provider-specific error checks
15
+ *
16
+ * ## For Provider Implementers:
17
+ * Extend this class and implement the abstract methods:
18
+ * - `extractIdTokenInternal()` - Extract token from provider storage
19
+ * - `extractTokenInfoInternal()` - Extract full token info
20
+ * - `extractUserInfoInternal()` - Map claims to StandardUserInfo
21
+ * - `refreshTokenInternal()` - Implement refresh mechanism
22
+ * - `classifyErrorInternal()` - Map errors to AuthErrorType
23
+ *
24
+ * @version 3.0.0
7
25
  */
8
26
  export class MJAuthBase {
9
27
  config;
28
+ // State management with proper types (no more 'any'!)
10
29
  isAuthenticated$ = new BehaviorSubject(false);
11
- userProfile$ = new BehaviorSubject(null);
30
+ userInfo$ = new BehaviorSubject(null);
12
31
  userEmail$ = new BehaviorSubject('');
13
32
  _initialPath = null;
14
33
  _initialSearch = null;
@@ -29,102 +48,218 @@ export class MJAuthBase {
29
48
  this._initialPath = window.location.pathname;
30
49
  this._initialSearch = window.location.search;
31
50
  }
51
+ // ============================================================================
52
+ // PUBLIC API (Concrete implementations using abstract internals)
53
+ // ============================================================================
32
54
  /**
33
- * Login the user - public API that returns Observable for backward compatibility
55
+ * Public login method with Observable wrapper for backward compatibility
56
+ *
57
+ * Consumers can use either:
58
+ * - `await this.authBase.login()` (Promise)
59
+ * - `this.authBase.login().subscribe()` (Observable)
34
60
  */
35
61
  login(options) {
36
62
  return from(this.loginInternal(options));
37
63
  }
38
64
  /**
39
- * Check if user is authenticated
65
+ * Check if user is authenticated (Observable stream)
66
+ *
67
+ * Returns a reactive stream that emits authentication state changes.
68
+ * Consumers can subscribe to react to login/logout events.
40
69
  */
41
70
  isAuthenticated() {
42
71
  return this.isAuthenticated$.asObservable();
43
72
  }
44
73
  /**
45
- * Get user profile information
74
+ * Get user info as Observable stream
75
+ *
76
+ * Returns standardized user info, hiding provider-specific claim structures.
77
+ * No more need for consumers to merge claims or check provider-specific fields!
46
78
  */
47
- getUserProfile() {
48
- return this.userProfile$.asObservable();
79
+ getUserInfo() {
80
+ return this.userInfo$.asObservable();
49
81
  }
50
82
  /**
51
- * Get user email
83
+ * Get user email as Observable stream
52
84
  */
53
85
  getUserEmail() {
54
86
  return this.userEmail$.asObservable();
55
87
  }
56
88
  /**
57
- * Get provider-specific configuration requirements
58
- * Override to specify required config fields
89
+ * Get ID token string (primary token method)
90
+ *
91
+ * This is the clean abstraction - no provider-specific logic needed!
92
+ * Replaces the old pattern of: `claims?.__raw || claims?.idToken`
93
+ *
94
+ * @example
95
+ * ```typescript
96
+ * const token = await this.authBase.getIdToken();
97
+ * if (token) {
98
+ * setupGraphQLClient(token, apiUrl);
99
+ * }
100
+ * ```
59
101
  */
60
- getRequiredConfig() {
61
- return ['clientId'];
102
+ async getIdToken() {
103
+ return this.extractIdTokenInternal();
62
104
  }
63
105
  /**
64
- * Validate provider configuration
65
- * Override to add provider-specific validation
106
+ * Get complete token information
107
+ *
108
+ * Returns full token details including expiration and scopes.
109
+ * Use this when you need more than just the token string.
66
110
  */
67
- validateConfig(config) {
68
- const requiredFields = this.getRequiredConfig();
69
- return requiredFields.every(field => config[field] !== undefined && config[field] !== '');
70
- }
71
- /**
72
- * Helper method to update authentication state
73
- */
74
- updateAuthState(isAuthenticated) {
75
- this.isAuthenticated$.next(isAuthenticated);
111
+ async getTokenInfo() {
112
+ return this.extractTokenInfoInternal();
76
113
  }
77
114
  /**
78
- * Helper method to update user profile
115
+ * Refresh authentication token
116
+ *
117
+ * Attempts to obtain a fresh authentication token using the provider's
118
+ * refresh mechanism. If silent refresh fails due to session expiry, the
119
+ * provider will handle re-authentication automatically (which may involve
120
+ * redirecting to the auth provider's login page).
121
+ *
122
+ * Returns StandardAuthToken on success, or throws on complete failure.
123
+ *
124
+ * IMPORTANT: If the provider requires interactive re-authentication (redirect
125
+ * or popup), this method may never return. The app will reload after
126
+ * authentication completes and re-initialize with a fresh token.
127
+ *
128
+ * @returns Promise resolving to StandardAuthToken or throws on failure
129
+ *
130
+ * @example
131
+ * ```typescript
132
+ * const token = await this.authBase.refreshToken();
133
+ * return token.idToken; // Always succeeds or throws
134
+ * ```
79
135
  */
80
- updateUserProfile(profile) {
81
- this.userProfile$.next(profile);
82
- if (profile?.email) {
83
- this.userEmail$.next(profile.email);
136
+ async refreshToken() {
137
+ // Try silent refresh
138
+ const result = await this.refreshTokenInternal();
139
+ if (result.success && result.token) {
140
+ // Update state if refresh succeeded
141
+ try {
142
+ const userInfo = await this.extractUserInfoInternal();
143
+ if (userInfo) {
144
+ this.updateUserInfo(userInfo);
145
+ }
146
+ }
147
+ catch {
148
+ // User info update failed, but token refresh succeeded
149
+ // Don't fail the entire refresh for this
150
+ }
151
+ return result.token;
84
152
  }
153
+ // Silent refresh failed - check if we can handle session expiry
154
+ const errorType = result.error?.type;
155
+ if (errorType === 'TOKEN_EXPIRED' || errorType === 'INTERACTION_REQUIRED') {
156
+ // Let provider handle session expiry (may redirect and never return)
157
+ await this.handleSessionExpiryInternal();
158
+ // If we reach here (didn't redirect), retry refresh once
159
+ const retryResult = await this.refreshTokenInternal();
160
+ if (retryResult.success && retryResult.token) {
161
+ // Update state with new token
162
+ try {
163
+ const userInfo = await this.extractUserInfoInternal();
164
+ if (userInfo) {
165
+ this.updateUserInfo(userInfo);
166
+ }
167
+ }
168
+ catch {
169
+ // Ignore user info update errors
170
+ }
171
+ return retryResult.token;
172
+ }
173
+ }
174
+ // Complete failure - throw error
175
+ throw new Error(result.error?.userMessage || 'Token refresh failed');
85
176
  }
86
- // Backward compatibility methods for existing code
87
177
  /**
88
- * Legacy property for authentication state
178
+ * Classify an error into standard error type
179
+ *
180
+ * Converts provider-specific errors into semantic categories.
181
+ * Eliminates need for consumers to check error.name or error types.
182
+ *
183
+ * @example
184
+ * ```typescript
185
+ * const authError = this.authBase.classifyError(err);
186
+ * if (authError.type === AuthErrorType.TOKEN_EXPIRED) {
187
+ * this.showMessage(authError.userMessage);
188
+ * }
189
+ * ```
89
190
  */
90
- get authenticated() {
91
- return this.isAuthenticated$.value;
191
+ classifyError(error) {
192
+ return this.classifyErrorInternal(error);
92
193
  }
93
194
  /**
94
- * Legacy setter for authentication state
95
- * Used by MJExplorer for backward compatibility
195
+ * Get profile picture URL from auth provider
196
+ *
197
+ * Returns the user's profile picture URL if available from the auth provider.
198
+ * This abstracts away provider-specific logic:
199
+ * - Microsoft/MSAL: Fetches from Graph API
200
+ * - Auth0/Okta: Returns from user claims
201
+ *
202
+ * @returns Promise resolving to image URL or null if not available
203
+ *
204
+ * @example
205
+ * ```typescript
206
+ * const pictureUrl = await this.authBase.getProfilePictureUrl();
207
+ * if (pictureUrl) {
208
+ * this.userAvatar = pictureUrl;
209
+ * }
210
+ * ```
96
211
  */
97
- set authenticated(value) {
98
- this.updateAuthState(value);
212
+ async getProfilePictureUrl() {
213
+ return this.getProfilePictureUrlInternal();
99
214
  }
215
+ // ============================================================================
216
+ // HELPER METHODS (For subclasses to update state)
217
+ // ============================================================================
100
218
  /**
101
- * Legacy method to get user
219
+ * Update authentication state
220
+ *
221
+ * Subclasses should call this when authentication state changes
222
+ * (after login, logout, session check, etc.)
102
223
  */
103
- async getUser() {
104
- return this.userProfile$.value;
224
+ updateAuthState(isAuthenticated) {
225
+ this.isAuthenticated$.next(isAuthenticated);
105
226
  }
106
227
  /**
107
- * Legacy method to get user claims
228
+ * Update user info
229
+ *
230
+ * Subclasses should call this when user info is retrieved or updated.
231
+ * This automatically updates the email stream as well.
108
232
  */
109
- async getUserClaims() {
110
- return this.userProfile$.asObservable();
233
+ updateUserInfo(userInfo) {
234
+ this.userInfo$.next(userInfo);
235
+ if (userInfo?.email) {
236
+ this.userEmail$.next(userInfo.email);
237
+ }
238
+ else {
239
+ this.userEmail$.next('');
240
+ }
111
241
  }
242
+ // ============================================================================
243
+ // CONFIGURATION & VALIDATION
244
+ // ============================================================================
112
245
  /**
113
- * Legacy method to check expired token error
246
+ * Get required configuration fields
247
+ *
248
+ * Default implementation requires clientId.
249
+ * Subclasses can override to add provider-specific requirements.
114
250
  */
115
- checkExpiredTokenError(error) {
116
- // Check for common token expiration error messages
117
- return error?.toLowerCase().includes('expired') ||
118
- error?.toLowerCase().includes('invalid token') ||
119
- error?.toLowerCase().includes('unauthorized');
251
+ getRequiredConfig() {
252
+ return ['clientId'];
120
253
  }
121
254
  /**
122
- * Legacy refresh method
255
+ * Validate provider configuration
256
+ *
257
+ * Checks that all required fields are present and non-empty.
258
+ * Subclasses can override to add custom validation logic.
123
259
  */
124
- async refresh() {
125
- // Try to get a new token
126
- await this.getToken();
127
- return this.userProfile$.asObservable();
260
+ validateConfig(config) {
261
+ const requiredFields = this.getRequiredConfig();
262
+ return requiredFields.every(field => config[field] !== undefined && config[field] !== '');
128
263
  }
129
264
  static ɵfac = function MJAuthBase_Factory(t) { i0.ɵɵinvalidFactory(); };
130
265
  static ɵprov = /*@__PURE__*/ i0.ɵɵdefineInjectable({ token: MJAuthBase, factory: MJAuthBase.ɵfac });