@nauth-toolkit/client-angular 0.1.58 → 0.1.59

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,13 +1,14 @@
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, NgModule, inject, PLATFORM_ID } from '@angular/core';
4
+ import { InjectionToken, Injectable, Inject, inject, Optional, NgModule, PLATFORM_ID } from '@angular/core';
5
5
  import { firstValueFrom, BehaviorSubject, Subject, catchError, from, switchMap, throwError, filter as filter$1, take } from 'rxjs';
6
6
  import { filter } from 'rxjs/operators';
7
7
  import * as i1 from '@angular/common/http';
8
8
  import { HttpErrorResponse, HTTP_INTERCEPTORS, HttpClientModule, HttpClient } from '@angular/common/http';
9
9
  import * as i3 from '@angular/router';
10
10
  import { Router } from '@angular/router';
11
+ import { __decorate, __param } from 'tslib';
11
12
  import { isPlatformBrowser } from '@angular/common';
12
13
 
13
14
  /**
@@ -266,6 +267,21 @@ class AuthService {
266
267
  getCurrentChallenge() {
267
268
  return this.challengeSubject.value;
268
269
  }
270
+ /**
271
+ * Get challenge router for manual navigation control.
272
+ * Useful for guards that need to handle errors or build custom URLs.
273
+ *
274
+ * @returns ChallengeRouter instance
275
+ *
276
+ * @example
277
+ * ```typescript
278
+ * const router = this.auth.getChallengeRouter();
279
+ * await router.navigateToError('oauth');
280
+ * ```
281
+ */
282
+ getChallengeRouter() {
283
+ return this.client.getChallengeRouter();
284
+ }
269
285
  // ============================================================================
270
286
  // Core Auth Methods
271
287
  // ============================================================================
@@ -1008,6 +1024,118 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
1008
1024
  args: [NAUTH_CLIENT_CONFIG]
1009
1025
  }] }, { type: i1.HttpClient }, { type: AuthService }, { type: i3.Router }] });
1010
1026
 
1027
+ /**
1028
+ * Functional route guard for authentication (Angular 17+).
1029
+ *
1030
+ * Protects routes by checking if user is authenticated.
1031
+ * Redirects to configured session expired route (or login) if not authenticated.
1032
+ *
1033
+ * @param redirectTo - Optional path to redirect to if not authenticated. If not provided, uses `redirects.sessionExpired` from config (defaults to '/login')
1034
+ * @returns CanActivateFn guard function
1035
+ *
1036
+ * @example
1037
+ * ```typescript
1038
+ * // In route configuration - uses config.redirects.sessionExpired
1039
+ * const routes: Routes = [
1040
+ * {
1041
+ * path: 'home',
1042
+ * component: HomeComponent,
1043
+ * canActivate: [authGuard()]
1044
+ * }
1045
+ * ];
1046
+ *
1047
+ * // Override with custom route
1048
+ * const routes: Routes = [
1049
+ * {
1050
+ * path: 'admin',
1051
+ * component: AdminComponent,
1052
+ * canActivate: [authGuard('/admin/login')]
1053
+ * }
1054
+ * ];
1055
+ * ```
1056
+ */
1057
+ function authGuard(redirectTo) {
1058
+ return () => {
1059
+ const auth = inject(AuthService);
1060
+ const router = inject(Router);
1061
+ const config = inject(NAUTH_CLIENT_CONFIG, { optional: true });
1062
+ if (auth.isAuthenticated()) {
1063
+ return true;
1064
+ }
1065
+ // Use provided redirectTo, or config.redirects.sessionExpired, or default to '/login'
1066
+ const redirectPath = redirectTo ?? config?.redirects?.sessionExpired ?? '/login';
1067
+ return router.createUrlTree([redirectPath]);
1068
+ };
1069
+ }
1070
+ /**
1071
+ * Class-based authentication guard for NgModule compatibility.
1072
+ *
1073
+ * **Note:** When using `NAuthModule.forRoot()`, `AuthGuard` is automatically provided
1074
+ * and has access to the configuration. You don't need to add it to your module's providers.
1075
+ *
1076
+ * @example
1077
+ * ```typescript
1078
+ * // app.module.ts - AuthGuard is automatically provided by NAuthModule.forRoot()
1079
+ * @NgModule({
1080
+ * imports: [
1081
+ * NAuthModule.forRoot({
1082
+ * baseUrl: 'https://api.example.com/auth',
1083
+ * tokenDelivery: 'cookies',
1084
+ * redirects: {
1085
+ * sessionExpired: '/login?expired=true',
1086
+ * },
1087
+ * }),
1088
+ * RouterModule.forRoot([
1089
+ * {
1090
+ * path: 'home',
1091
+ * component: HomeComponent,
1092
+ * canActivate: [AuthGuard], // Uses config.redirects.sessionExpired
1093
+ * },
1094
+ * ]),
1095
+ * ],
1096
+ * })
1097
+ * export class AppModule {}
1098
+ *
1099
+ * // Or provide manually in a feature module (still has access to root config)
1100
+ * @NgModule({
1101
+ * providers: [AuthGuard],
1102
+ * })
1103
+ * export class FeatureModule {}
1104
+ * ```
1105
+ */
1106
+ let AuthGuard = class AuthGuard {
1107
+ auth;
1108
+ router;
1109
+ config;
1110
+ /**
1111
+ * @param auth - Authentication service
1112
+ * @param router - Angular router
1113
+ * @param config - Optional client configuration (injected automatically)
1114
+ */
1115
+ constructor(auth, router, config) {
1116
+ this.auth = auth;
1117
+ this.router = router;
1118
+ this.config = config;
1119
+ }
1120
+ /**
1121
+ * Check if route can be activated.
1122
+ *
1123
+ * @returns True if authenticated, otherwise redirects to configured session expired route (or '/login')
1124
+ */
1125
+ canActivate() {
1126
+ if (this.auth.isAuthenticated()) {
1127
+ return true;
1128
+ }
1129
+ // Use config.redirects.sessionExpired or default to '/login'
1130
+ const redirectPath = this.config?.redirects?.sessionExpired ?? '/login';
1131
+ return this.router.createUrlTree([redirectPath]);
1132
+ }
1133
+ };
1134
+ AuthGuard = __decorate([
1135
+ __param(2, Optional()),
1136
+ __param(2, Inject(NAUTH_CLIENT_CONFIG))
1137
+ ], AuthGuard);
1138
+
1011
1139
  /**
1012
1140
  * NgModule for nauth-toolkit Angular integration.
1013
1141
  *
@@ -1051,6 +1179,8 @@ class NAuthModule {
1051
1179
  useClass: AuthInterceptorClass,
1052
1180
  multi: true,
1053
1181
  },
1182
+ // Provide AuthGuard so it has access to NAUTH_CLIENT_CONFIG
1183
+ AuthGuard,
1054
1184
  ],
1055
1185
  };
1056
1186
  }
@@ -1217,86 +1347,6 @@ class AuthInterceptor {
1217
1347
  }
1218
1348
  }
1219
1349
 
1220
- /**
1221
- * Functional route guard for authentication (Angular 17+).
1222
- *
1223
- * Protects routes by checking if user is authenticated.
1224
- * Redirects to login page if not authenticated.
1225
- *
1226
- * @param redirectTo - Path to redirect to if not authenticated (default: '/login')
1227
- * @returns CanActivateFn guard function
1228
- *
1229
- * @example
1230
- * ```typescript
1231
- * // In route configuration
1232
- * const routes: Routes = [
1233
- * {
1234
- * path: 'home',
1235
- * component: HomeComponent,
1236
- * canActivate: [authGuard()]
1237
- * },
1238
- * {
1239
- * path: 'admin',
1240
- * component: AdminComponent,
1241
- * canActivate: [authGuard('/admin/login')]
1242
- * }
1243
- * ];
1244
- * ```
1245
- */
1246
- function authGuard(redirectTo = '/login') {
1247
- return () => {
1248
- const auth = inject(AuthService);
1249
- const router = inject(Router);
1250
- if (auth.isAuthenticated()) {
1251
- return true;
1252
- }
1253
- return router.createUrlTree([redirectTo]);
1254
- };
1255
- }
1256
- /**
1257
- * Class-based authentication guard for NgModule compatibility.
1258
- *
1259
- * @example
1260
- * ```typescript
1261
- * // In route configuration (NgModule)
1262
- * const routes: Routes = [
1263
- * {
1264
- * path: 'home',
1265
- * component: HomeComponent,
1266
- * canActivate: [AuthGuard]
1267
- * }
1268
- * ];
1269
- *
1270
- * // In module providers
1271
- * @NgModule({
1272
- * providers: [AuthGuard]
1273
- * })
1274
- * ```
1275
- */
1276
- class AuthGuard {
1277
- auth;
1278
- router;
1279
- /**
1280
- * @param auth - Authentication service
1281
- * @param router - Angular router
1282
- */
1283
- constructor(auth, router) {
1284
- this.auth = auth;
1285
- this.router = router;
1286
- }
1287
- /**
1288
- * Check if route can be activated.
1289
- *
1290
- * @returns True if authenticated, otherwise redirects to login
1291
- */
1292
- canActivate() {
1293
- if (this.auth.isAuthenticated()) {
1294
- return true;
1295
- }
1296
- return this.router.createUrlTree(['/login']);
1297
- }
1298
- }
1299
-
1300
1350
  /**
1301
1351
  * Social redirect callback route guard.
1302
1352
  *
@@ -1307,8 +1357,8 @@ class AuthGuard {
1307
1357
  * - `error` / `error_description` (provider errors)
1308
1358
  *
1309
1359
  * Behavior:
1310
- * - If `exchangeToken` exists: exchanges it via backend and redirects to success or challenge routes.
1311
- * - If no `exchangeToken`: treat as cookie-success path and redirect to success route.
1360
+ * - If `exchangeToken` exists: exchanges it via backend (SDK handles navigation automatically).
1361
+ * - If no `exchangeToken`: treat as cookie-success path (SDK handles navigation automatically).
1312
1362
  * - If `error` exists: redirects to oauthError route.
1313
1363
  *
1314
1364
  * @example
@@ -1322,7 +1372,6 @@ class AuthGuard {
1322
1372
  */
1323
1373
  const socialRedirectCallbackGuard = async () => {
1324
1374
  const auth = inject(AuthService);
1325
- const config = inject(NAUTH_CLIENT_CONFIG);
1326
1375
  const platformId = inject(PLATFORM_ID);
1327
1376
  const isBrowser = isPlatformBrowser(platformId);
1328
1377
  if (!isBrowser) {
@@ -1331,13 +1380,13 @@ const socialRedirectCallbackGuard = async () => {
1331
1380
  const params = new URLSearchParams(window.location.search);
1332
1381
  const error = params.get('error');
1333
1382
  const exchangeToken = params.get('exchangeToken');
1383
+ const router = auth.getChallengeRouter();
1334
1384
  // Provider error: redirect to oauthError
1335
1385
  if (error) {
1336
- const errorUrl = config.redirects?.oauthError || '/login';
1337
- window.location.replace(errorUrl);
1386
+ await router.navigateToError('oauth');
1338
1387
  return false;
1339
1388
  }
1340
- // No exchangeToken: cookie success path; redirect to success.
1389
+ // No exchangeToken: cookie success path; hydrate then navigate to success.
1341
1390
  //
1342
1391
  // Note: we do not "activate" the callback route to avoid consumers needing to render a page.
1343
1392
  if (!exchangeToken) {
@@ -1351,26 +1400,31 @@ const socialRedirectCallbackGuard = async () => {
1351
1400
  // `currentUser` is still null even though cookies were set successfully.
1352
1401
  try {
1353
1402
  await auth.getProfile();
1403
+ await router.navigateToSuccess();
1354
1404
  }
1355
- catch {
1356
- const errorUrl = config.redirects?.oauthError || '/login';
1357
- window.location.replace(errorUrl);
1358
- return false;
1405
+ catch (err) {
1406
+ // Only treat auth failures (401/403) as OAuth errors
1407
+ // Network errors or other issues might be temporary - still try success route
1408
+ const isAuthError = err instanceof NAuthClientError &&
1409
+ (err.statusCode === 401 ||
1410
+ err.statusCode === 403 ||
1411
+ err.code === NAuthErrorCode.AUTH_TOKEN_INVALID ||
1412
+ err.code === NAuthErrorCode.AUTH_SESSION_EXPIRED ||
1413
+ err.code === NAuthErrorCode.AUTH_SESSION_NOT_FOUND);
1414
+ if (isAuthError) {
1415
+ // Cookies weren't set properly - OAuth failed
1416
+ await router.navigateToError('oauth');
1417
+ }
1418
+ else {
1419
+ // For network errors or other issues, proceed to success route
1420
+ // The auth guard will handle authentication state on the next route
1421
+ await router.navigateToSuccess();
1422
+ }
1359
1423
  }
1360
- const successUrl = config.redirects?.success || '/';
1361
- window.location.replace(successUrl);
1362
- return false;
1363
- }
1364
- // Exchange token and route accordingly
1365
- const response = await auth.exchangeSocialRedirect(exchangeToken);
1366
- if (response.challengeName) {
1367
- const challengeBase = config.redirects?.challengeBase || '/auth/challenge';
1368
- const challengeRoute = response.challengeName.toLowerCase().replace(/_/g, '-');
1369
- window.location.replace(`${challengeBase}/${challengeRoute}`);
1370
1424
  return false;
1371
1425
  }
1372
- const successUrl = config.redirects?.success || '/';
1373
- window.location.replace(successUrl);
1426
+ // Exchange token - SDK handles navigation automatically
1427
+ await auth.exchangeSocialRedirect(exchangeToken);
1374
1428
  return false;
1375
1429
  };
1376
1430