@nauth-toolkit/client 0.1.17 → 0.1.21

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/index.cjs CHANGED
@@ -165,12 +165,12 @@ var defaultEndpoints = {
165
165
  mfaPreferred: "/mfa/preferred-method",
166
166
  mfaBackupCodes: "/mfa/backup-codes/generate",
167
167
  mfaExemption: "/mfa/exemption",
168
- socialAuthUrl: "/social/auth-url",
169
- socialCallback: "/social/callback",
170
168
  socialLinked: "/social/linked",
171
169
  socialLink: "/social/link",
172
170
  socialUnlink: "/social/unlink",
173
171
  socialVerify: "/social/:provider/verify",
172
+ socialRedirectStart: "/social/:provider/redirect",
173
+ socialExchange: "/social/exchange",
174
174
  trustDevice: "/trust-device",
175
175
  isTrustedDevice: "/is-trusted-device",
176
176
  auditHistory: "/audit/history",
@@ -406,6 +406,9 @@ var BrowserStorage = class {
406
406
  async removeItem(key) {
407
407
  this.storage.removeItem(key);
408
408
  }
409
+ async clear() {
410
+ this.storage.clear();
411
+ }
409
412
  };
410
413
 
411
414
  // src/storage/memory.ts
@@ -422,6 +425,9 @@ var InMemoryStorage = class {
422
425
  async removeItem(key) {
423
426
  this.store.delete(key);
424
427
  }
428
+ async clear() {
429
+ this.store.clear();
430
+ }
425
431
  };
426
432
 
427
433
  // src/core/events.ts
@@ -542,9 +548,9 @@ var FetchAdapter = class {
542
548
  });
543
549
  if (!response.ok) {
544
550
  const errorData = typeof data === "object" && data !== null ? data : {};
545
- const code = typeof errorData["code"] === "string" ? errorData.code : "INTERNAL_ERROR" /* INTERNAL_ERROR */;
546
- const message = typeof errorData["message"] === "string" ? errorData.message : `Request failed with status ${status}`;
547
- const timestamp = typeof errorData["timestamp"] === "string" ? errorData.timestamp : void 0;
551
+ const code = typeof errorData["code"] === "string" ? errorData["code"] : "INTERNAL_ERROR" /* INTERNAL_ERROR */;
552
+ const message = typeof errorData["message"] === "string" ? errorData["message"] : `Request failed with status ${status}`;
553
+ const timestamp = typeof errorData["timestamp"] === "string" ? errorData["timestamp"] : void 0;
548
554
  const details = errorData["details"];
549
555
  throw new NAuthClientError(code, message, {
550
556
  statusCode: status,
@@ -966,126 +972,59 @@ var NAuthClient = class {
966
972
  // Social Authentication
967
973
  // ============================================================================
968
974
  /**
969
- * Start social OAuth flow with automatic state management.
975
+ * Start redirect-first social OAuth flow (web).
976
+ *
977
+ * This performs a browser navigation to:
978
+ * `GET {baseUrl}/social/:provider/redirect?returnTo=...&appState=...`
970
979
  *
971
- * Generates a secure state token, stores OAuth context, and redirects to the OAuth provider.
972
- * After OAuth callback, use `handleOAuthCallback()` to complete authentication.
980
+ * The backend:
981
+ * - generates and stores CSRF state (cluster-safe)
982
+ * - redirects the user to the provider
983
+ * - completes OAuth on callback and sets cookies (or issues an exchange token)
984
+ * - redirects back to `returnTo` with `appState` (and `exchangeToken` for json/hybrid)
973
985
  *
974
986
  * @param provider - OAuth provider ('google', 'apple', 'facebook')
975
- * @param options - Optional configuration
987
+ * @param options - Optional redirect options
976
988
  *
977
989
  * @example
978
990
  * ```typescript
979
- * // Simple usage
980
- * await client.loginWithSocial('google');
981
- *
982
- * // With custom redirect URI
983
- * await client.loginWithSocial('apple', {
984
- * redirectUri: 'https://example.com/auth/callback'
985
- * });
991
+ * await client.loginWithSocial('google', { returnTo: '/auth/callback', appState: '12345' });
986
992
  * ```
987
993
  */
988
- async loginWithSocial(provider, _options) {
994
+ async loginWithSocial(provider, options) {
989
995
  this.eventEmitter.emit({ type: "oauth:started", data: { provider }, timestamp: Date.now() });
990
- const { url } = await this.getSocialAuthUrl({ provider });
991
996
  if (hasWindow()) {
992
- window.location.href = url;
993
- }
994
- }
995
- /**
996
- * Auto-detect and handle OAuth callback.
997
- *
998
- * Call this on app initialization or in callback route.
999
- * Returns null if not an OAuth callback (no provider/code params).
1000
- *
1001
- * The SDK validates the state token, completes authentication via backend,
1002
- * and emits appropriate events.
1003
- *
1004
- * @param urlOrParams - Optional URL string or URLSearchParams (auto-detects from window.location if not provided)
1005
- * @returns AuthResponse if OAuth callback detected, null otherwise
1006
- *
1007
- * @example
1008
- * ```typescript
1009
- * // Auto-detect on app init
1010
- * const response = await client.handleOAuthCallback();
1011
- * if (response) {
1012
- * if (response.challengeName) {
1013
- * router.navigate(['/challenge', response.challengeName]);
1014
- * } else {
1015
- * router.navigate(['/']); // Navigate to your app's home route
1016
- * }
1017
- * }
1018
- *
1019
- * // In callback route
1020
- * const response = await client.handleOAuthCallback(window.location.search);
1021
- * ```
1022
- */
1023
- async handleOAuthCallback(urlOrParams) {
1024
- let params;
1025
- if (urlOrParams instanceof URLSearchParams) {
1026
- params = urlOrParams;
1027
- } else if (typeof urlOrParams === "string") {
1028
- params = new URLSearchParams(urlOrParams);
1029
- } else if (hasWindow()) {
1030
- params = new URLSearchParams(window.location.search);
1031
- } else {
1032
- return null;
1033
- }
1034
- const provider = params.get("provider");
1035
- const code = params.get("code");
1036
- const state = params.get("state");
1037
- const error = params.get("error");
1038
- if (!provider || !code && !error) {
1039
- return null;
1040
- }
1041
- this.eventEmitter.emit({ type: "oauth:callback", data: { provider }, timestamp: Date.now() });
1042
- try {
1043
- if (error) {
1044
- const authError = new NAuthClientError(
1045
- "SOCIAL_TOKEN_INVALID" /* SOCIAL_TOKEN_INVALID */,
1046
- params.get("error_description") || error,
1047
- { details: { error, provider } }
1048
- );
1049
- this.eventEmitter.emit({ type: "oauth:error", data: authError, timestamp: Date.now() });
1050
- throw authError;
1051
- }
1052
- if (!state) {
1053
- throw new NAuthClientError("CHALLENGE_INVALID" /* CHALLENGE_INVALID */, "Missing OAuth state parameter");
997
+ const startPath = this.config.endpoints.socialRedirectStart.replace(":provider", provider);
998
+ const base = this.config.baseUrl.replace(/\/$/, "");
999
+ const startUrl = new URL(`${base}${startPath}`);
1000
+ const returnTo = options?.returnTo ?? this.config.redirects?.success ?? "/";
1001
+ const action = options?.action ?? "login";
1002
+ startUrl.searchParams.set("returnTo", returnTo);
1003
+ startUrl.searchParams.set("action", action);
1004
+ if (options?.delivery === "cookies" || options?.delivery === "json") {
1005
+ startUrl.searchParams.set("delivery", options.delivery);
1054
1006
  }
1055
- const response = await this.handleSocialCallback({
1056
- provider,
1057
- code,
1058
- state
1059
- });
1060
- if (response.challengeName) {
1061
- this.eventEmitter.emit({ type: "auth:challenge", data: response, timestamp: Date.now() });
1062
- } else {
1063
- this.eventEmitter.emit({ type: "auth:success", data: response, timestamp: Date.now() });
1007
+ if (typeof options?.appState === "string" && options.appState.trim() !== "") {
1008
+ startUrl.searchParams.set("appState", options.appState);
1064
1009
  }
1065
- this.eventEmitter.emit({ type: "oauth:completed", data: response, timestamp: Date.now() });
1066
- return response;
1067
- } catch (error2) {
1068
- const authError = error2 instanceof NAuthClientError ? error2 : new NAuthClientError(
1069
- "SOCIAL_TOKEN_INVALID" /* SOCIAL_TOKEN_INVALID */,
1070
- error2.message || "OAuth callback failed"
1071
- );
1072
- this.eventEmitter.emit({ type: "oauth:error", data: authError, timestamp: Date.now() });
1073
- throw authError;
1010
+ window.location.href = startUrl.toString();
1074
1011
  }
1075
1012
  }
1076
1013
  /**
1077
- * Get social auth URL (low-level API).
1014
+ * Exchange an `exchangeToken` (from redirect callback URL) into an AuthResponse.
1078
1015
  *
1079
- * For most cases, use `loginWithSocial()` which handles state management automatically.
1080
- */
1081
- async getSocialAuthUrl(request) {
1082
- return this.post(this.config.endpoints.socialAuthUrl, request);
1083
- }
1084
- /**
1085
- * Handle social callback.
1016
+ * Used for `tokenDelivery: 'json'` or hybrid flows where the backend redirects back
1017
+ * with `exchangeToken` instead of setting cookies.
1018
+ *
1019
+ * @param exchangeToken - One-time exchange token from the callback URL
1020
+ * @returns AuthResponse
1086
1021
  */
1087
- async handleSocialCallback(request) {
1088
- const result = await this.post(this.config.endpoints.socialCallback, request);
1022
+ async exchangeSocialRedirect(exchangeToken) {
1023
+ const token = exchangeToken?.trim();
1024
+ if (!token) {
1025
+ throw new NAuthClientError("CHALLENGE_INVALID" /* CHALLENGE_INVALID */, "Missing exchangeToken");
1026
+ }
1027
+ const result = await this.post(this.config.endpoints.socialExchange, { exchangeToken: token });
1089
1028
  await this.handleAuthResponse(result);
1090
1029
  return result;
1091
1030
  }