@nauth-toolkit/client-angular 0.1.91 → 0.1.92

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.
@@ -1,7 +1,7 @@
1
1
  import { NAuthErrorCode, NAuthClientError, NAuthClient } from '@nauth-toolkit/client';
2
2
  export * from '@nauth-toolkit/client';
3
3
  import * as i0 from '@angular/core';
4
- import { InjectionToken, Injectable, Inject, Optional, inject, NgModule, PLATFORM_ID } from '@angular/core';
4
+ import { InjectionToken, Injectable, PLATFORM_ID, Inject, Optional, inject, NgModule } from '@angular/core';
5
5
  import { firstValueFrom, BehaviorSubject, Subject, from, switchMap, of, map, catchError, throwError, finalize, shareReplay } from 'rxjs';
6
6
  import { filter } from 'rxjs/operators';
7
7
  import * as i1 from '@angular/common/http';
@@ -136,6 +136,362 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
136
136
  type: Injectable
137
137
  }], ctorParameters: () => [{ type: i1.HttpClient }] });
138
138
 
139
+ /**
140
+ * Injection token for reCAPTCHA configuration
141
+ */
142
+ const RECAPTCHA_CONFIG = new InjectionToken('RECAPTCHA_CONFIG');
143
+ /**
144
+ * Google reCAPTCHA service for Angular applications.
145
+ *
146
+ * Provides lazy loading of reCAPTCHA script and platform-aware token generation.
147
+ * Automatically detects platform (web, Capacitor WebView, Capacitor native, SSR)
148
+ * and skips reCAPTCHA in environments where it's not supported or needed.
149
+ *
150
+ * Features:
151
+ * - Lazy script loading (only loads when needed)
152
+ * - v2 (checkbox) and v3 (invisible) support
153
+ * - Platform detection (web, Capacitor, SSR)
154
+ * - Automatic skip for Capacitor native mode
155
+ * - Automatic skip for SSR
156
+ *
157
+ * @example v3 Automatic Mode
158
+ * ```typescript
159
+ * constructor(private recaptcha: RecaptchaService) {}
160
+ *
161
+ * async login() {
162
+ * const token = await this.recaptcha.execute('login');
163
+ * await this.auth.login(email, password, token);
164
+ * }
165
+ * ```
166
+ *
167
+ * @example v2 Manual Mode
168
+ * ```typescript
169
+ * ngOnInit() {
170
+ * this.recaptcha.render('recaptcha-container', (token) => {
171
+ * this.recaptchaToken = token;
172
+ * });
173
+ * }
174
+ * ```
175
+ */
176
+ class RecaptchaService {
177
+ platformId;
178
+ config;
179
+ scriptLoaded = false;
180
+ scriptLoading = null;
181
+ platform;
182
+ widgetId = null;
183
+ constructor(platformId, config) {
184
+ this.platformId = platformId;
185
+ this.config = config;
186
+ this.platform = this.detectPlatform();
187
+ }
188
+ // ============================================================================
189
+ // Platform Detection
190
+ // ============================================================================
191
+ /**
192
+ * Detect the current platform environment.
193
+ *
194
+ * Detection priority:
195
+ * 1. SSR (not in browser) → 'ssr'
196
+ * 2. Capacitor native (no web view) → 'capacitor-native'
197
+ * 3. Capacitor WebView → 'capacitor-webview'
198
+ * 4. Web browser → 'web'
199
+ *
200
+ * @returns Detected platform type
201
+ *
202
+ * @example
203
+ * ```typescript
204
+ * const platform = this.detectPlatform();
205
+ * if (platform === 'capacitor-native') {
206
+ * // Skip reCAPTCHA, use device attestation
207
+ * }
208
+ * ```
209
+ */
210
+ detectPlatform() {
211
+ // SSR detection
212
+ if (!isPlatformBrowser(this.platformId)) {
213
+ return 'ssr';
214
+ }
215
+ // Capacitor detection (window.Capacitor exists)
216
+ const windowRef = window;
217
+ if (windowRef.Capacitor) {
218
+ // Capacitor native (iOS/Android app)
219
+ if (typeof windowRef.Capacitor.isNativePlatform === 'function' && windowRef.Capacitor.isNativePlatform()) {
220
+ return 'capacitor-native';
221
+ }
222
+ // Capacitor WebView
223
+ return 'capacitor-webview';
224
+ }
225
+ return 'web';
226
+ }
227
+ /**
228
+ * Get the current platform.
229
+ *
230
+ * @returns Current platform type
231
+ */
232
+ getPlatform() {
233
+ return this.platform;
234
+ }
235
+ /**
236
+ * Check if reCAPTCHA should be skipped for current platform.
237
+ *
238
+ * Skips for:
239
+ * - SSR (no window object)
240
+ * - Capacitor native (use device attestation instead)
241
+ *
242
+ * @returns True if should skip reCAPTCHA
243
+ */
244
+ shouldSkip() {
245
+ return this.platform === 'ssr' || this.platform === 'capacitor-native';
246
+ }
247
+ // ============================================================================
248
+ // Script Loading
249
+ // ============================================================================
250
+ /**
251
+ * Load Google reCAPTCHA script if not already loaded.
252
+ *
253
+ * Script URL format:
254
+ * - v2: https://www.google.com/recaptcha/api.js
255
+ * - v3: https://www.google.com/recaptcha/api.js?render={siteKey}
256
+ * - Enterprise: https://www.google.com/recaptcha/enterprise.js?render={siteKey}
257
+ *
258
+ * @returns Promise that resolves when script is loaded
259
+ *
260
+ * @throws Error if config is missing or script fails to load
261
+ */
262
+ async loadScript() {
263
+ // Skip in SSR or Capacitor native
264
+ if (this.shouldSkip()) {
265
+ return;
266
+ }
267
+ // Skip if disabled
268
+ if (!this.config?.enabled) {
269
+ return;
270
+ }
271
+ // Already loaded
272
+ if (this.scriptLoaded) {
273
+ return;
274
+ }
275
+ // Already loading (return existing promise)
276
+ if (this.scriptLoading) {
277
+ return this.scriptLoading;
278
+ }
279
+ // Validate config
280
+ if (!this.config.siteKey) {
281
+ throw new Error('[RecaptchaService] Site key is required');
282
+ }
283
+ // Start loading
284
+ this.scriptLoading = this.injectScript();
285
+ try {
286
+ await this.scriptLoading;
287
+ this.scriptLoaded = true;
288
+ }
289
+ finally {
290
+ this.scriptLoading = null;
291
+ }
292
+ }
293
+ /**
294
+ * Inject the reCAPTCHA script into the DOM.
295
+ *
296
+ * @returns Promise that resolves when script loads
297
+ */
298
+ injectScript() {
299
+ return new Promise((resolve, reject) => {
300
+ const script = document.createElement('script');
301
+ script.async = true;
302
+ script.defer = true;
303
+ // Set script URL based on version
304
+ if (this.config.version === 'enterprise') {
305
+ script.src = `https://www.google.com/recaptcha/enterprise.js?render=${this.config.siteKey}`;
306
+ }
307
+ else if (this.config.version === 'v3') {
308
+ script.src = `https://www.google.com/recaptcha/api.js?render=${this.config.siteKey}`;
309
+ }
310
+ else {
311
+ // v2 - load without render parameter
312
+ let url = 'https://www.google.com/recaptcha/api.js';
313
+ if (this.config.language) {
314
+ url += `?hl=${this.config.language}`;
315
+ }
316
+ script.src = url;
317
+ }
318
+ script.onload = () => resolve();
319
+ script.onerror = () => reject(new Error('[RecaptchaService] Failed to load reCAPTCHA script'));
320
+ document.head.appendChild(script);
321
+ });
322
+ }
323
+ // ============================================================================
324
+ // v3/Enterprise Methods (Invisible Challenge)
325
+ // ============================================================================
326
+ /**
327
+ * Execute reCAPTCHA v3/Enterprise challenge (invisible).
328
+ *
329
+ * Automatically loads script if needed and generates a token.
330
+ * Skips automatically for SSR and Capacitor native.
331
+ *
332
+ * @param action - Action name for v3 analytics (e.g., 'login', 'signup')
333
+ * @returns Promise resolving to reCAPTCHA token, or undefined if skipped
334
+ *
335
+ * @throws Error if version is v2, config missing, or execution fails
336
+ *
337
+ * @example
338
+ * ```typescript
339
+ * const token = await this.recaptcha.execute('login');
340
+ * if (token) {
341
+ * await this.auth.login(email, password, token);
342
+ * }
343
+ * ```
344
+ */
345
+ async execute(action) {
346
+ // Skip for platforms that don't support reCAPTCHA
347
+ if (this.shouldSkip()) {
348
+ return undefined;
349
+ }
350
+ // Skip if disabled
351
+ if (!this.config?.enabled) {
352
+ return undefined;
353
+ }
354
+ // v2 requires manual render
355
+ if (this.config.version === 'v2') {
356
+ throw new Error('[RecaptchaService] execute() is only for v3/Enterprise. Use render() for v2.');
357
+ }
358
+ // Load script if needed
359
+ await this.loadScript();
360
+ // Get grecaptcha object
361
+ const grecaptcha = window.grecaptcha;
362
+ if (!grecaptcha) {
363
+ throw new Error('[RecaptchaService] grecaptcha is not loaded');
364
+ }
365
+ // Execute reCAPTCHA
366
+ const actionName = action || this.config.action || 'submit';
367
+ try {
368
+ if (this.config.version === 'enterprise' && grecaptcha.enterprise?.execute) {
369
+ return await grecaptcha.enterprise.execute(this.config.siteKey, { action: actionName });
370
+ }
371
+ else if (grecaptcha.execute) {
372
+ return await grecaptcha.execute(this.config.siteKey, { action: actionName });
373
+ }
374
+ else {
375
+ throw new Error('[RecaptchaService] grecaptcha.execute is not available');
376
+ }
377
+ }
378
+ catch (error) {
379
+ throw new Error(`[RecaptchaService] Failed to execute reCAPTCHA: ${error instanceof Error ? error.message : 'unknown error'}`);
380
+ }
381
+ }
382
+ // ============================================================================
383
+ // v2 Methods (Visible Checkbox)
384
+ // ============================================================================
385
+ /**
386
+ * Render reCAPTCHA v2 checkbox widget.
387
+ *
388
+ * @param containerId - DOM element ID or element to render in
389
+ * @param callback - Callback when user completes challenge
390
+ * @returns Promise resolving to widget ID
391
+ *
392
+ * @throws Error if version is not v2, config missing, or render fails
393
+ *
394
+ * @example
395
+ * ```typescript
396
+ * ngAfterViewInit() {
397
+ * this.recaptcha.render('recaptcha-container', (token) => {
398
+ * this.recaptchaToken = token;
399
+ * this.loginForm.patchValue({ recaptchaToken: token });
400
+ * });
401
+ * }
402
+ * ```
403
+ */
404
+ async render(containerId, callback) {
405
+ // Skip for platforms that don't support reCAPTCHA
406
+ if (this.shouldSkip()) {
407
+ throw new Error('[RecaptchaService] reCAPTCHA v2 is not supported in SSR or Capacitor native');
408
+ }
409
+ // Skip if disabled
410
+ if (!this.config?.enabled) {
411
+ throw new Error('[RecaptchaService] reCAPTCHA is not enabled');
412
+ }
413
+ // Only for v2
414
+ if (this.config.version !== 'v2') {
415
+ throw new Error('[RecaptchaService] render() is only for v2. Use execute() for v3/Enterprise.');
416
+ }
417
+ // Load script if needed
418
+ await this.loadScript();
419
+ // Get grecaptcha object
420
+ const grecaptcha = window.grecaptcha;
421
+ if (!grecaptcha?.render) {
422
+ throw new Error('[RecaptchaService] grecaptcha.render is not available');
423
+ }
424
+ // Render widget
425
+ try {
426
+ this.widgetId = grecaptcha.render(containerId, {
427
+ sitekey: this.config.siteKey,
428
+ callback: callback,
429
+ });
430
+ return this.widgetId;
431
+ }
432
+ catch (error) {
433
+ throw new Error(`[RecaptchaService] Failed to render reCAPTCHA: ${error instanceof Error ? error.message : 'unknown error'}`);
434
+ }
435
+ }
436
+ /**
437
+ * Get response token from v2 widget.
438
+ *
439
+ * @param widgetId - Widget ID (optional, uses last rendered widget if not provided)
440
+ * @returns reCAPTCHA token or null if not completed
441
+ *
442
+ * @example
443
+ * ```typescript
444
+ * const token = this.recaptcha.getResponse();
445
+ * if (token) {
446
+ * await this.auth.login(email, password, token);
447
+ * }
448
+ * ```
449
+ */
450
+ getResponse(widgetId) {
451
+ const grecaptcha = window.grecaptcha;
452
+ if (!grecaptcha?.getResponse) {
453
+ return null;
454
+ }
455
+ const id = widgetId !== undefined ? widgetId : this.widgetId ?? undefined;
456
+ return grecaptcha.getResponse(id) || null;
457
+ }
458
+ /**
459
+ * Reset v2 widget (clear response).
460
+ *
461
+ * @param widgetId - Widget ID (optional, uses last rendered widget if not provided)
462
+ *
463
+ * @example
464
+ * ```typescript
465
+ * // After failed login
466
+ * this.recaptcha.reset();
467
+ * ```
468
+ */
469
+ reset(widgetId) {
470
+ const grecaptcha = window.grecaptcha;
471
+ if (!grecaptcha?.reset) {
472
+ return;
473
+ }
474
+ const id = widgetId !== undefined ? widgetId : this.widgetId ?? undefined;
475
+ grecaptcha.reset(id);
476
+ }
477
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: RecaptchaService, deps: [{ token: PLATFORM_ID }, { token: RECAPTCHA_CONFIG, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
478
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: RecaptchaService, providedIn: 'root' });
479
+ }
480
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: RecaptchaService, decorators: [{
481
+ type: Injectable,
482
+ args: [{
483
+ providedIn: 'root',
484
+ }]
485
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
486
+ type: Inject,
487
+ args: [PLATFORM_ID]
488
+ }] }, { type: undefined, decorators: [{
489
+ type: Optional
490
+ }, {
491
+ type: Inject,
492
+ args: [RECAPTCHA_CONFIG]
493
+ }] }] });
494
+
139
495
  /**
140
496
  * Angular wrapper around NAuthClient that provides promise-based auth methods and reactive state.
141
497
  *
@@ -170,6 +526,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
170
526
  */
171
527
  class AuthService {
172
528
  router;
529
+ recaptchaService;
173
530
  client;
174
531
  config;
175
532
  currentUserSubject = new BehaviorSubject(null);
@@ -181,9 +538,11 @@ class AuthService {
181
538
  * @param config - Injected client configuration (required)
182
539
  * @param httpAdapter - Angular HTTP adapter for making requests (required)
183
540
  * @param router - Angular Router (optional, automatically used for navigation if available)
541
+ * @param recaptchaService - RecaptchaService (optional, for automatic token generation)
184
542
  */
185
- constructor(config, httpAdapter, router) {
543
+ constructor(config, httpAdapter, router, recaptchaService) {
186
544
  this.router = router;
545
+ this.recaptchaService = recaptchaService;
187
546
  this.config = config;
188
547
  // Use provided httpAdapter (from config or injected)
189
548
  const adapter = config.httpAdapter ?? httpAdapter;
@@ -334,31 +693,39 @@ class AuthService {
334
693
  /**
335
694
  * Login with identifier and password.
336
695
  *
696
+ * Automatically generates reCAPTCHA token if configured (v3 only).
697
+ * For v2 manual mode, pass the token explicitly.
698
+ *
337
699
  * @param identifier - User email or username
338
700
  * @param password - User password
701
+ * @param recaptchaToken - Optional reCAPTCHA token (for v2 manual mode or when auto-generation is disabled)
339
702
  * @returns Promise with auth response or challenge
340
703
  *
341
- * @example
704
+ * @example Basic Login
342
705
  * ```typescript
343
706
  * const response = await this.auth.login('user@example.com', 'password');
344
- * if (response.challengeName) {
345
- * // Handle challenge
346
- * } else {
347
- * // Login successful
348
- * }
707
+ * ```
708
+ *
709
+ * @example With Manual reCAPTCHA (v2)
710
+ * ```typescript
711
+ * const response = await this.auth.login('user@example.com', 'password', recaptchaToken);
349
712
  * ```
350
713
  */
351
- async login(identifier, password) {
352
- const res = await this.client.login(identifier, password);
714
+ async login(identifier, password, recaptchaToken) {
715
+ const token = await this.getRecaptchaToken(recaptchaToken, 'login');
716
+ const res = await this.client.login(identifier, password, token);
353
717
  return this.updateChallengeState(res);
354
718
  }
355
719
  /**
356
720
  * Signup with credentials.
357
721
  *
722
+ * Automatically generates reCAPTCHA token if configured (v3 only).
723
+ * For v2 manual mode, include token in payload.
724
+ *
358
725
  * @param payload - Signup request payload
359
726
  * @returns Promise with auth response or challenge
360
727
  *
361
- * @example
728
+ * @example Basic Signup
362
729
  * ```typescript
363
730
  * const response = await this.auth.signup({
364
731
  * email: 'new@example.com',
@@ -366,8 +733,25 @@ class AuthService {
366
733
  * firstName: 'John',
367
734
  * });
368
735
  * ```
736
+ *
737
+ * @example With Manual reCAPTCHA (v2)
738
+ * ```typescript
739
+ * const response = await this.auth.signup({
740
+ * email: 'new@example.com',
741
+ * password: 'SecurePass123!',
742
+ * recaptchaToken: token,
743
+ * });
744
+ * ```
369
745
  */
370
746
  async signup(payload) {
747
+ // Auto-generate reCAPTCHA token for v3 if configured and not already provided
748
+ const payloadWithRecaptcha = payload;
749
+ if (!payloadWithRecaptcha.recaptchaToken) {
750
+ const token = await this.getRecaptchaToken(undefined, 'signup');
751
+ if (token) {
752
+ payload = { ...payload, recaptchaToken: token };
753
+ }
754
+ }
371
755
  const res = await this.client.signup(payload);
372
756
  return this.updateChallengeState(res);
373
757
  }
@@ -1029,7 +1413,58 @@ class AuthService {
1029
1413
  }
1030
1414
  return response;
1031
1415
  }
1032
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: AuthService, deps: [{ token: NAUTH_CLIENT_CONFIG }, { token: AngularHttpAdapter }, { token: i2.Router, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
1416
+ // ============================================================================
1417
+ // reCAPTCHA Helper
1418
+ // ============================================================================
1419
+ /**
1420
+ * Get reCAPTCHA token - auto-generate for v3 or use provided token.
1421
+ *
1422
+ * Handles platform detection:
1423
+ * - Web browser: Generate token if enabled and v3
1424
+ * - Capacitor native: Skip (use device attestation instead)
1425
+ * - SSR: Skip
1426
+ * - Manual mode (v2 or manualChallenge=true): Requires explicit token
1427
+ *
1428
+ * @param providedToken - Explicitly provided token (v2 manual mode)
1429
+ * @param action - Action name for v3 analytics
1430
+ * @returns reCAPTCHA token or undefined
1431
+ *
1432
+ * @private
1433
+ */
1434
+ async getRecaptchaToken(providedToken, action) {
1435
+ // If token explicitly provided, use it (v2 manual mode)
1436
+ if (providedToken) {
1437
+ return providedToken;
1438
+ }
1439
+ // No reCAPTCHA service available
1440
+ if (!this.recaptchaService) {
1441
+ return undefined;
1442
+ }
1443
+ // Check if reCAPTCHA is configured
1444
+ const recaptchaConfig = this.config.recaptcha;
1445
+ if (!recaptchaConfig?.enabled) {
1446
+ return undefined;
1447
+ }
1448
+ // Skip for platforms that don't support reCAPTCHA (SSR, Capacitor native)
1449
+ if (this.recaptchaService.shouldSkip()) {
1450
+ return undefined;
1451
+ }
1452
+ // v2 or manual mode - user must provide token explicitly
1453
+ if (recaptchaConfig.version === 'v2' || recaptchaConfig.manualChallenge) {
1454
+ return undefined;
1455
+ }
1456
+ // Auto-generate token for v3/Enterprise
1457
+ try {
1458
+ return await this.recaptchaService.execute(action);
1459
+ }
1460
+ catch (error) {
1461
+ // Log error but don't block authentication
1462
+ // Server will enforce if required
1463
+ console.warn('[AuthService] Failed to generate reCAPTCHA token:', error);
1464
+ return undefined;
1465
+ }
1466
+ }
1467
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: AuthService, deps: [{ token: NAUTH_CLIENT_CONFIG }, { token: AngularHttpAdapter }, { token: i2.Router, optional: true }, { token: RecaptchaService, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
1033
1468
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: AuthService });
1034
1469
  }
1035
1470
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: AuthService, decorators: [{
@@ -1039,6 +1474,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
1039
1474
  args: [NAUTH_CLIENT_CONFIG]
1040
1475
  }] }, { type: AngularHttpAdapter }, { type: i2.Router, decorators: [{
1041
1476
  type: Optional
1477
+ }] }, { type: RecaptchaService, decorators: [{
1478
+ type: Optional
1042
1479
  }] }] });
1043
1480
 
1044
1481
  /**
@@ -1557,6 +1994,7 @@ class AuthInterceptor {
1557
1994
  * - If `exchangeToken` exists: exchanges it via backend (SDK handles navigation automatically).
1558
1995
  * - If no `exchangeToken`: treat as cookie-success path (SDK handles navigation automatically).
1559
1996
  * - If `error` exists: redirects to oauthError route.
1997
+ * - If auto-redirect is disabled (redirectUrls set to null): returns true to activate the route.
1560
1998
  *
1561
1999
  * @example
1562
2000
  * ```typescript
@@ -1587,14 +2025,16 @@ const socialRedirectCallbackGuard = async () => {
1587
2025
  if (appState) {
1588
2026
  await auth.getClient().storeOauthState(appState);
1589
2027
  }
1590
- // Provider error: redirect to oauthError
2028
+ // Provider error: redirect to oauthError (or activate route if auto-redirect disabled)
1591
2029
  if (error) {
1592
2030
  await router.navigateToError('oauth');
1593
- return false;
2031
+ // Return true to activate route if oauthError redirect is disabled
2032
+ return router.isErrorRedirectDisabled('oauth');
1594
2033
  }
1595
2034
  // No exchangeToken: cookie success path; hydrate then navigate to success.
1596
2035
  //
1597
- // Note: we do not "activate" the callback route to avoid consumers needing to render a page.
2036
+ // Note: When auto-redirect is enabled, we do not "activate" the callback route to avoid
2037
+ // consumers needing to render a page. When disabled, we activate the route.
1598
2038
  if (!exchangeToken) {
1599
2039
  // ============================================================================
1600
2040
  // Cookies mode: hydrate user state before redirecting
@@ -1636,32 +2076,36 @@ const socialRedirectCallbackGuard = async () => {
1636
2076
  appState: appState ?? undefined,
1637
2077
  });
1638
2078
  }
1639
- catch (err) {
2079
+ catch (error) {
1640
2080
  // Only treat auth failures (401/403) as OAuth errors
1641
2081
  // Network errors or other issues might be temporary - still try success route
1642
- const isAuthError = err instanceof NAuthClientError &&
1643
- (err.statusCode === 401 ||
1644
- err.statusCode === 403 ||
1645
- err.code === NAuthErrorCode.AUTH_TOKEN_INVALID ||
1646
- err.code === NAuthErrorCode.AUTH_SESSION_EXPIRED ||
1647
- err.code === NAuthErrorCode.AUTH_SESSION_NOT_FOUND);
1648
- if (isAuthError) {
1649
- // Cookies weren't set properly - OAuth failed
1650
- await router.navigateToError('oauth');
1651
- }
1652
- else {
1653
- // For network errors or other issues, proceed to success route
1654
- // The auth guard will handle authentication state on the next route
1655
- await router.navigateToSuccess(appState ? { appState } : undefined);
2082
+ // Type guard: check if error is NAuthClientError
2083
+ if (error instanceof NAuthClientError) {
2084
+ const isAuthError = error.statusCode === 401 ||
2085
+ error.statusCode === 403 ||
2086
+ error.code === NAuthErrorCode.AUTH_TOKEN_INVALID ||
2087
+ error.code === NAuthErrorCode.AUTH_SESSION_EXPIRED ||
2088
+ error.code === NAuthErrorCode.AUTH_SESSION_NOT_FOUND;
2089
+ if (isAuthError) {
2090
+ // Cookies weren't set properly - OAuth failed
2091
+ await router.navigateToError('oauth');
2092
+ return router.isErrorRedirectDisabled('oauth');
2093
+ }
1656
2094
  }
2095
+ // For network errors or other non-auth issues, proceed to success route
2096
+ // The auth guard will handle authentication state on the next route
2097
+ await router.navigateToSuccess(appState ? { appState } : undefined);
1657
2098
  }
1658
- return false;
2099
+ // Return true if success redirect is disabled, allowing the callback component to render
2100
+ return router.isSuccessRedirectDisabled({ source: 'social', appState: appState ?? undefined });
1659
2101
  }
1660
2102
  // Exchange token - SDK handles navigation automatically
1661
2103
  // Note: appState will be passed via query params when navigateToSuccess is called
1662
2104
  // by the challenge router after successful exchange
1663
2105
  await auth.exchangeSocialRedirect(exchangeToken);
1664
- return false;
2106
+ // Return true if success redirect is disabled, allowing the callback component to render
2107
+ // We use 'social' as source since this is the social OAuth callback flow
2108
+ return router.isSuccessRedirectDisabled({ source: 'social', appState: appState ?? undefined });
1665
2109
  };
1666
2110
 
1667
2111
  /**
@@ -1676,5 +2120,5 @@ const socialRedirectCallbackGuard = async () => {
1676
2120
  * Generated bundle index. Do not edit.
1677
2121
  */
1678
2122
 
1679
- export { AngularHttpAdapter, AuthGuard, AuthInterceptor, AuthInterceptorClass, AuthService, NAUTH_CLIENT_CONFIG, NAuthModule, authGuard, authInterceptor, socialRedirectCallbackGuard };
2123
+ export { AngularHttpAdapter, AuthGuard, AuthInterceptor, AuthInterceptorClass, AuthService, NAUTH_CLIENT_CONFIG, NAuthModule, RECAPTCHA_CONFIG, RecaptchaService, authGuard, authInterceptor, socialRedirectCallbackGuard };
1680
2124
  //# sourceMappingURL=nauth-toolkit-client-angular.mjs.map