@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.
- package/dist/lib/IAuthProvider.d.ts +179 -17
- package/dist/lib/IAuthProvider.d.ts.map +1 -1
- package/dist/lib/auth-types.d.ts +351 -0
- package/dist/lib/auth-types.d.ts.map +1 -0
- package/dist/lib/auth-types.js +98 -0
- package/dist/lib/auth-types.js.map +1 -0
- package/dist/lib/mjexplorer-auth-base.service.d.ts +225 -49
- package/dist/lib/mjexplorer-auth-base.service.d.ts.map +1 -1
- package/dist/lib/mjexplorer-auth-base.service.js +189 -54
- package/dist/lib/mjexplorer-auth-base.service.js.map +1 -1
- package/dist/lib/providers/mjexplorer-auth0-provider.service.d.ts +69 -17
- package/dist/lib/providers/mjexplorer-auth0-provider.service.d.ts.map +1 -1
- package/dist/lib/providers/mjexplorer-auth0-provider.service.js +255 -75
- package/dist/lib/providers/mjexplorer-auth0-provider.service.js.map +1 -1
- package/dist/lib/providers/mjexplorer-msal-provider.service.d.ts +65 -16
- package/dist/lib/providers/mjexplorer-msal-provider.service.d.ts.map +1 -1
- package/dist/lib/providers/mjexplorer-msal-provider.service.js +345 -113
- package/dist/lib/providers/mjexplorer-msal-provider.service.js.map +1 -1
- package/dist/lib/providers/mjexplorer-okta-provider.service.d.ts +63 -30
- package/dist/lib/providers/mjexplorer-okta-provider.service.d.ts.map +1 -1
- package/dist/lib/providers/mjexplorer-okta-provider.service.js +273 -234
- package/dist/lib/providers/mjexplorer-okta-provider.service.js.map +1 -1
- package/dist/public-api.d.ts +1 -0
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +2 -0
- package/dist/public-api.js.map +1 -1
- 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
|
-
*
|
|
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
|
|
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
|
-
|
|
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
|
|
27
|
-
*
|
|
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
|
-
*
|
|
32
|
-
*
|
|
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?:
|
|
61
|
+
protected abstract loginInternal(options?: Record<string, unknown>): Promise<void>;
|
|
35
62
|
/**
|
|
36
|
-
*
|
|
63
|
+
* Logout implementation
|
|
64
|
+
*
|
|
65
|
+
* Subclasses implement provider-specific logout flow.
|
|
37
66
|
*/
|
|
38
|
-
|
|
67
|
+
abstract logout(): Promise<void>;
|
|
39
68
|
/**
|
|
40
|
-
*
|
|
41
|
-
*
|
|
69
|
+
* Handle OAuth callback
|
|
70
|
+
*
|
|
71
|
+
* Subclasses implement provider-specific callback handling.
|
|
42
72
|
*/
|
|
43
|
-
abstract
|
|
73
|
+
abstract handleCallback(): Promise<void>;
|
|
44
74
|
/**
|
|
45
|
-
*
|
|
46
|
-
*
|
|
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
|
|
84
|
+
protected abstract extractIdTokenInternal(): Promise<string | null>;
|
|
49
85
|
/**
|
|
50
|
-
*
|
|
51
|
-
*
|
|
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
|
|
92
|
+
protected abstract extractTokenInfoInternal(): Promise<StandardAuthToken | null>;
|
|
54
93
|
/**
|
|
55
|
-
*
|
|
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
|
-
|
|
102
|
+
protected abstract extractUserInfoInternal(): Promise<StandardUserInfo | null>;
|
|
58
103
|
/**
|
|
59
|
-
*
|
|
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
|
-
|
|
116
|
+
protected abstract refreshTokenInternal(): Promise<TokenRefreshResult>;
|
|
62
117
|
/**
|
|
63
|
-
*
|
|
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
|
-
|
|
127
|
+
protected abstract classifyErrorInternal(error: unknown): StandardAuthError;
|
|
66
128
|
/**
|
|
67
|
-
* Get
|
|
68
|
-
*
|
|
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
|
-
|
|
137
|
+
protected abstract getProfilePictureUrlInternal(): Promise<string | null>;
|
|
71
138
|
/**
|
|
72
|
-
*
|
|
73
|
-
*
|
|
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
|
-
|
|
151
|
+
protected abstract handleSessionExpiryInternal(): Promise<void>;
|
|
76
152
|
/**
|
|
77
|
-
*
|
|
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
|
-
|
|
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
|
-
*
|
|
175
|
+
* Get user email as Observable stream
|
|
82
176
|
*/
|
|
83
|
-
|
|
177
|
+
getUserEmail(): Observable<string>;
|
|
84
178
|
/**
|
|
85
|
-
*
|
|
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
|
-
|
|
192
|
+
getIdToken(): Promise<string | null>;
|
|
88
193
|
/**
|
|
89
|
-
*
|
|
90
|
-
*
|
|
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
|
-
|
|
199
|
+
getTokenInfo(): Promise<StandardAuthToken | null>;
|
|
93
200
|
/**
|
|
94
|
-
*
|
|
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
|
-
|
|
222
|
+
refreshToken(): Promise<StandardAuthToken>;
|
|
97
223
|
/**
|
|
98
|
-
*
|
|
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
|
-
|
|
237
|
+
classifyError(error: unknown): StandardAuthError;
|
|
101
238
|
/**
|
|
102
|
-
*
|
|
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
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
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;;
|
|
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;AAClF,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,EACnB,MAAM,cAAc,CAAC;;AAEtB;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,8BACsB,UAAW,YAAW,oBAAoB;IAC9D,SAAS,CAAC,MAAM,EAAE,yBAAyB,CAAC;IAG5C,SAAS,CAAC,gBAAgB,2BAAuC;IACjE,SAAS,CAAC,SAAS,2CAAsD;IACzE,SAAS,CAAC,UAAU,0BAAmC;IAEvD,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,cAAc,CAAuB;IAE7C;;OAEG;IACH,IAAI,WAAW,IAAI,MAAM,GAAG,IAAI,CAE/B;IAED;;OAEG;IACH,IAAI,aAAa,IAAI,MAAM,GAAG,IAAI,CAEjC;IAED;;;OAGG;IACH,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBAEnB,MAAM,EAAE,yBAAyB;IAU7C;;;;;OAKG;IACH,QAAQ,CAAC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAEpC;;;;;OAKG;IACH,SAAS,CAAC,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAElF;;;;OAIG;IACH,QAAQ,CAAC,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAEhC;;;;OAIG;IACH,QAAQ,CAAC,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAExC;;;;;;;;;OASG;IACH,SAAS,CAAC,QAAQ,CAAC,sBAAsB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAEnE;;;;;;OAMG;IACH,SAAS,CAAC,QAAQ,CAAC,wBAAwB,IAAI,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC;IAEhF;;;;;;;;OAQG;IACH,SAAS,CAAC,QAAQ,CAAC,uBAAuB,IAAI,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC;IAE9E;;;;;;;;;;;;OAYG;IACH,SAAS,CAAC,QAAQ,CAAC,oBAAoB,IAAI,OAAO,CAAC,kBAAkB,CAAC;IAEtE;;;;;;;;;OASG;IACH,SAAS,CAAC,QAAQ,CAAC,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,iBAAiB;IAE3E;;;;;;;;OAQG;IACH,SAAS,CAAC,QAAQ,CAAC,4BAA4B,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAEzE;;;;;;;;;;;;OAYG;IACH,SAAS,CAAC,QAAQ,CAAC,2BAA2B,IAAI,OAAO,CAAC,IAAI,CAAC;IAM/D;;;;;;OAMG;IACH,KAAK,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,UAAU,CAAC,IAAI,CAAC;IAI1D;;;;;OAKG;IACH,eAAe,IAAI,UAAU,CAAC,OAAO,CAAC;IAItC;;;;;OAKG;IACH,WAAW,IAAI,UAAU,CAAC,gBAAgB,GAAG,IAAI,CAAC;IAIlD;;OAEG;IACH,YAAY,IAAI,UAAU,CAAC,MAAM,CAAC;IAIlC;;;;;;;;;;;;;OAaG;IACG,UAAU,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAI1C;;;;;OAKG;IACG,YAAY,IAAI,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC;IAIvD;;;;;;;;;;;;;;;;;;;;;OAqBG;IACG,YAAY,IAAI,OAAO,CAAC,iBAAiB,CAAC;IA8ChD;;;;;;;;;;;;;OAaG;IACH,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,iBAAiB;IAIhD;;;;;;;;;;;;;;;;;OAiBG;IACG,oBAAoB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAQpD;;;;;OAKG;IACH,SAAS,CAAC,eAAe,CAAC,eAAe,EAAE,OAAO,GAAG,IAAI;IAIzD;;;;;OAKG;IACH,SAAS,CAAC,cAAc,CAAC,QAAQ,EAAE,gBAAgB,GAAG,IAAI,GAAG,IAAI;IAcjE;;;;;OAKG;IACH,iBAAiB,IAAI,MAAM,EAAE;IAI7B;;;;;OAKG;IACH,cAAc,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO;yCAhYpC,UAAU;6CAAV,UAAU;CAsY/B"}
|
|
@@ -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
|
-
*
|
|
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
|
-
|
|
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
|
-
*
|
|
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
|
|
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
|
-
|
|
48
|
-
return this.
|
|
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
|
|
58
|
-
*
|
|
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
|
-
|
|
61
|
-
return
|
|
102
|
+
async getIdToken() {
|
|
103
|
+
return this.extractIdTokenInternal();
|
|
62
104
|
}
|
|
63
105
|
/**
|
|
64
|
-
*
|
|
65
|
-
*
|
|
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
|
-
|
|
68
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
91
|
-
return this.
|
|
191
|
+
classifyError(error) {
|
|
192
|
+
return this.classifyErrorInternal(error);
|
|
92
193
|
}
|
|
93
194
|
/**
|
|
94
|
-
*
|
|
95
|
-
*
|
|
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
|
-
|
|
98
|
-
this.
|
|
212
|
+
async getProfilePictureUrl() {
|
|
213
|
+
return this.getProfilePictureUrlInternal();
|
|
99
214
|
}
|
|
215
|
+
// ============================================================================
|
|
216
|
+
// HELPER METHODS (For subclasses to update state)
|
|
217
|
+
// ============================================================================
|
|
100
218
|
/**
|
|
101
|
-
*
|
|
219
|
+
* Update authentication state
|
|
220
|
+
*
|
|
221
|
+
* Subclasses should call this when authentication state changes
|
|
222
|
+
* (after login, logout, session check, etc.)
|
|
102
223
|
*/
|
|
103
|
-
|
|
104
|
-
|
|
224
|
+
updateAuthState(isAuthenticated) {
|
|
225
|
+
this.isAuthenticated$.next(isAuthenticated);
|
|
105
226
|
}
|
|
106
227
|
/**
|
|
107
|
-
*
|
|
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
|
-
|
|
110
|
-
|
|
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
|
-
*
|
|
246
|
+
* Get required configuration fields
|
|
247
|
+
*
|
|
248
|
+
* Default implementation requires clientId.
|
|
249
|
+
* Subclasses can override to add provider-specific requirements.
|
|
114
250
|
*/
|
|
115
|
-
|
|
116
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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 });
|