@nauth-toolkit/client 0.1.18 → 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.d.mts CHANGED
@@ -284,25 +284,34 @@ interface ConfirmForgotPasswordResponse {
284
284
  */
285
285
  type SocialProvider = 'google' | 'apple' | 'facebook';
286
286
  /**
287
- * Request to obtain social auth URL.
288
- */
289
- interface SocialAuthUrlRequest {
290
- provider: SocialProvider;
291
- state?: string;
292
- }
293
- /**
294
- * Response containing social auth URL.
295
- */
296
- interface SocialAuthUrlResponse {
297
- url: string;
298
- }
299
- /**
300
- * Social callback parameters.
287
+ * Options for starting a redirect-first social login flow.
288
+ *
289
+ * This is a web-first API:
290
+ * - Browser navigates to backend `/auth/social/:provider/redirect`
291
+ * - Backend completes OAuth, sets cookies (or returns exchange token), and redirects back
301
292
  */
302
- interface SocialCallbackRequest {
303
- provider: SocialProvider;
304
- code: string;
305
- state: string;
293
+ interface SocialLoginOptions {
294
+ /**
295
+ * Frontend route (recommended) or URL to redirect to after the backend callback completes.
296
+ * Default: config.redirects.success || '/'
297
+ */
298
+ returnTo?: string;
299
+ /**
300
+ * Optional application state to round-trip back to the frontend.
301
+ * Must be treated as non-secret.
302
+ */
303
+ appState?: string;
304
+ /**
305
+ * Optional flow action.
306
+ * Default: 'login'
307
+ */
308
+ action?: 'login' | 'link';
309
+ /**
310
+ * Optional delivery preference for redirect-first flows.
311
+ *
312
+ * Useful for hybrid deployments where provider callbacks do not contain a reliable `Origin`.
313
+ */
314
+ delivery?: 'cookies' | 'json';
306
315
  }
307
316
  /**
308
317
  * Linked social accounts response.
@@ -407,6 +416,10 @@ interface NAuthStorageAdapter {
407
416
  * Remove a stored value.
408
417
  */
409
418
  removeItem(key: string): Promise<void>;
419
+ /**
420
+ * Clear all stored values.
421
+ */
422
+ clear(): Promise<void>;
410
423
  }
411
424
 
412
425
  /**
@@ -529,12 +542,12 @@ interface NAuthEndpoints {
529
542
  mfaPreferred: string;
530
543
  mfaBackupCodes: string;
531
544
  mfaExemption: string;
532
- socialAuthUrl: string;
533
- socialCallback: string;
534
545
  socialLinked: string;
535
546
  socialLink: string;
536
547
  socialUnlink: string;
537
548
  socialVerify: string;
549
+ socialRedirectStart: string;
550
+ socialExchange: string;
538
551
  trustDevice: string;
539
552
  isTrustedDevice: string;
540
553
  auditHistory: string;
@@ -1180,69 +1193,36 @@ declare class NAuthClient {
1180
1193
  */
1181
1194
  off(event: AuthEventType | '*', listener: AuthEventListener): void;
1182
1195
  /**
1183
- * Start social OAuth flow with automatic state management.
1196
+ * Start redirect-first social OAuth flow (web).
1184
1197
  *
1185
- * Generates a secure state token, stores OAuth context, and redirects to the OAuth provider.
1186
- * After OAuth callback, use `handleOAuthCallback()` to complete authentication.
1198
+ * This performs a browser navigation to:
1199
+ * `GET {baseUrl}/social/:provider/redirect?returnTo=...&appState=...`
1200
+ *
1201
+ * The backend:
1202
+ * - generates and stores CSRF state (cluster-safe)
1203
+ * - redirects the user to the provider
1204
+ * - completes OAuth on callback and sets cookies (or issues an exchange token)
1205
+ * - redirects back to `returnTo` with `appState` (and `exchangeToken` for json/hybrid)
1187
1206
  *
1188
1207
  * @param provider - OAuth provider ('google', 'apple', 'facebook')
1189
- * @param options - Optional configuration
1208
+ * @param options - Optional redirect options
1190
1209
  *
1191
1210
  * @example
1192
1211
  * ```typescript
1193
- * // Simple usage
1194
- * await client.loginWithSocial('google');
1195
- *
1196
- * // With custom redirect URI
1197
- * await client.loginWithSocial('apple', {
1198
- * redirectUri: 'https://example.com/auth/callback'
1199
- * });
1212
+ * await client.loginWithSocial('google', { returnTo: '/auth/callback', appState: '12345' });
1200
1213
  * ```
1201
1214
  */
1202
- loginWithSocial(provider: SocialProvider, _options?: {
1203
- redirectUri?: string;
1204
- }): Promise<void>;
1215
+ loginWithSocial(provider: SocialProvider, options?: SocialLoginOptions): Promise<void>;
1205
1216
  /**
1206
- * Auto-detect and handle OAuth callback.
1207
- *
1208
- * Call this on app initialization or in callback route.
1209
- * Returns null if not an OAuth callback (no provider/code params).
1210
- *
1211
- * The SDK validates the state token, completes authentication via backend,
1212
- * and emits appropriate events.
1217
+ * Exchange an `exchangeToken` (from redirect callback URL) into an AuthResponse.
1213
1218
  *
1214
- * @param urlOrParams - Optional URL string or URLSearchParams (auto-detects from window.location if not provided)
1215
- * @returns AuthResponse if OAuth callback detected, null otherwise
1219
+ * Used for `tokenDelivery: 'json'` or hybrid flows where the backend redirects back
1220
+ * with `exchangeToken` instead of setting cookies.
1216
1221
  *
1217
- * @example
1218
- * ```typescript
1219
- * // Auto-detect on app init
1220
- * const response = await client.handleOAuthCallback();
1221
- * if (response) {
1222
- * if (response.challengeName) {
1223
- * router.navigate(['/challenge', response.challengeName]);
1224
- * } else {
1225
- * router.navigate(['/']); // Navigate to your app's home route
1226
- * }
1227
- * }
1228
- *
1229
- * // In callback route
1230
- * const response = await client.handleOAuthCallback(window.location.search);
1231
- * ```
1232
- */
1233
- handleOAuthCallback(urlOrParams?: string | URLSearchParams): Promise<AuthResponse | null>;
1234
- /**
1235
- * Get social auth URL (low-level API).
1236
- *
1237
- * For most cases, use `loginWithSocial()` which handles state management automatically.
1238
- */
1239
- getSocialAuthUrl(request: SocialAuthUrlRequest): Promise<{
1240
- url: string;
1241
- }>;
1242
- /**
1243
- * Handle social callback.
1222
+ * @param exchangeToken - One-time exchange token from the callback URL
1223
+ * @returns AuthResponse
1244
1224
  */
1245
- handleSocialCallback(request: SocialCallbackRequest): Promise<AuthResponse>;
1225
+ exchangeSocialRedirect(exchangeToken: string): Promise<AuthResponse>;
1246
1226
  /**
1247
1227
  * Verify native social token (mobile).
1248
1228
  */
@@ -1548,6 +1528,7 @@ declare class BrowserStorage implements NAuthStorageAdapter {
1548
1528
  getItem(key: string): Promise<string | null>;
1549
1529
  setItem(key: string, value: string): Promise<void>;
1550
1530
  removeItem(key: string): Promise<void>;
1531
+ clear(): Promise<void>;
1551
1532
  }
1552
1533
 
1553
1534
  /**
@@ -1558,6 +1539,7 @@ declare class InMemoryStorage implements NAuthStorageAdapter {
1558
1539
  getItem(key: string): Promise<string | null>;
1559
1540
  setItem(key: string, value: string): Promise<void>;
1560
1541
  removeItem(key: string): Promise<void>;
1542
+ clear(): Promise<void>;
1561
1543
  }
1562
1544
 
1563
1545
  /**
@@ -1590,4 +1572,4 @@ declare class FetchAdapter implements HttpAdapter {
1590
1572
  request<T>(config: HttpRequest): Promise<HttpResponse<T>>;
1591
1573
  }
1592
1574
 
1593
- export { type AuditHistoryResponse, type AuthAuditEvent, type AuthAuditEventStatus, AuthAuditEventType, AuthChallenge, type AuthChallengeEvent, type AuthErrorEvent, type AuthEvent, type AuthEventListener, type AuthEventType, type AuthLoginEvent, type AuthLogoutEvent, type AuthRefreshEvent, type AuthResponse, type AuthSignupEvent, type AuthSuccessEvent, type AuthUser, type AuthUserSummary, type BackupCodesResponse, type BaseChallengeResponse, BrowserStorage, type ChallengeResponse, type ChangePasswordRequest, type ConfirmForgotPasswordRequest, type ConfirmForgotPasswordResponse, EventEmitter, FetchAdapter, type ForceChangePasswordResponse, type ForgotPasswordRequest, type ForgotPasswordResponse, type GetChallengeDataRequest, type GetChallengeDataResponse, type GetSetupDataRequest, type GetSetupDataResponse, type HttpAdapter, type HttpRequest, type HttpResponse, InMemoryStorage, type LinkedAccountsResponse, type LoginRequest, type LogoutAllRequest, type LogoutRequest, type MFAChallengeMethod, type MFACodeResponse, type MFADevice, type MFADeviceMethod, type MFAMethod, type MFAPasskeyResponse, type MFASetupData, type MFASetupResponse, type MFAStatus, NAuthClient, type NAuthClientConfig, NAuthClientError, type NAuthEndpoints, type NAuthError, NAuthErrorCode, type NAuthStorageAdapter, type OAuthCallbackEvent, type OAuthCompletedEvent, type OAuthErrorEvent, type OAuthStartedEvent, type ResendCodeRequest, type ResolvedNAuthClientConfig, type SignupRequest, type SocialAuthUrlRequest, type SocialAuthUrlResponse, type SocialCallbackRequest, type SocialProvider, type SocialVerifyRequest, type TokenDeliveryMode, type TokenResponse, type UpdateProfileRequest, type VerifyEmailResponse, type VerifyPhoneCodeResponse, type VerifyPhoneCollectResponse, defaultEndpoints, getChallengeInstructions, getMFAMethod, getMaskedDestination, isOTPChallenge, requiresPhoneCollection, resolveConfig };
1575
+ export { type AuditHistoryResponse, type AuthAuditEvent, type AuthAuditEventStatus, AuthAuditEventType, AuthChallenge, type AuthChallengeEvent, type AuthErrorEvent, type AuthEvent, type AuthEventListener, type AuthEventType, type AuthLoginEvent, type AuthLogoutEvent, type AuthRefreshEvent, type AuthResponse, type AuthSignupEvent, type AuthSuccessEvent, type AuthUser, type AuthUserSummary, type BackupCodesResponse, type BaseChallengeResponse, BrowserStorage, type ChallengeResponse, type ChangePasswordRequest, type ConfirmForgotPasswordRequest, type ConfirmForgotPasswordResponse, EventEmitter, FetchAdapter, type ForceChangePasswordResponse, type ForgotPasswordRequest, type ForgotPasswordResponse, type GetChallengeDataRequest, type GetChallengeDataResponse, type GetSetupDataRequest, type GetSetupDataResponse, type HttpAdapter, type HttpRequest, type HttpResponse, InMemoryStorage, type LinkedAccountsResponse, type LoginRequest, type LogoutAllRequest, type LogoutRequest, type MFAChallengeMethod, type MFACodeResponse, type MFADevice, type MFADeviceMethod, type MFAMethod, type MFAPasskeyResponse, type MFASetupData, type MFASetupResponse, type MFAStatus, NAuthClient, type NAuthClientConfig, NAuthClientError, type NAuthEndpoints, type NAuthError, NAuthErrorCode, type NAuthStorageAdapter, type OAuthCallbackEvent, type OAuthCompletedEvent, type OAuthErrorEvent, type OAuthStartedEvent, type ResendCodeRequest, type ResolvedNAuthClientConfig, type SignupRequest, type SocialLoginOptions, type SocialProvider, type SocialVerifyRequest, type TokenDeliveryMode, type TokenResponse, type UpdateProfileRequest, type VerifyEmailResponse, type VerifyPhoneCodeResponse, type VerifyPhoneCollectResponse, defaultEndpoints, getChallengeInstructions, getMFAMethod, getMaskedDestination, isOTPChallenge, requiresPhoneCollection, resolveConfig };
package/dist/index.d.ts CHANGED
@@ -284,25 +284,34 @@ interface ConfirmForgotPasswordResponse {
284
284
  */
285
285
  type SocialProvider = 'google' | 'apple' | 'facebook';
286
286
  /**
287
- * Request to obtain social auth URL.
288
- */
289
- interface SocialAuthUrlRequest {
290
- provider: SocialProvider;
291
- state?: string;
292
- }
293
- /**
294
- * Response containing social auth URL.
295
- */
296
- interface SocialAuthUrlResponse {
297
- url: string;
298
- }
299
- /**
300
- * Social callback parameters.
287
+ * Options for starting a redirect-first social login flow.
288
+ *
289
+ * This is a web-first API:
290
+ * - Browser navigates to backend `/auth/social/:provider/redirect`
291
+ * - Backend completes OAuth, sets cookies (or returns exchange token), and redirects back
301
292
  */
302
- interface SocialCallbackRequest {
303
- provider: SocialProvider;
304
- code: string;
305
- state: string;
293
+ interface SocialLoginOptions {
294
+ /**
295
+ * Frontend route (recommended) or URL to redirect to after the backend callback completes.
296
+ * Default: config.redirects.success || '/'
297
+ */
298
+ returnTo?: string;
299
+ /**
300
+ * Optional application state to round-trip back to the frontend.
301
+ * Must be treated as non-secret.
302
+ */
303
+ appState?: string;
304
+ /**
305
+ * Optional flow action.
306
+ * Default: 'login'
307
+ */
308
+ action?: 'login' | 'link';
309
+ /**
310
+ * Optional delivery preference for redirect-first flows.
311
+ *
312
+ * Useful for hybrid deployments where provider callbacks do not contain a reliable `Origin`.
313
+ */
314
+ delivery?: 'cookies' | 'json';
306
315
  }
307
316
  /**
308
317
  * Linked social accounts response.
@@ -407,6 +416,10 @@ interface NAuthStorageAdapter {
407
416
  * Remove a stored value.
408
417
  */
409
418
  removeItem(key: string): Promise<void>;
419
+ /**
420
+ * Clear all stored values.
421
+ */
422
+ clear(): Promise<void>;
410
423
  }
411
424
 
412
425
  /**
@@ -529,12 +542,12 @@ interface NAuthEndpoints {
529
542
  mfaPreferred: string;
530
543
  mfaBackupCodes: string;
531
544
  mfaExemption: string;
532
- socialAuthUrl: string;
533
- socialCallback: string;
534
545
  socialLinked: string;
535
546
  socialLink: string;
536
547
  socialUnlink: string;
537
548
  socialVerify: string;
549
+ socialRedirectStart: string;
550
+ socialExchange: string;
538
551
  trustDevice: string;
539
552
  isTrustedDevice: string;
540
553
  auditHistory: string;
@@ -1180,69 +1193,36 @@ declare class NAuthClient {
1180
1193
  */
1181
1194
  off(event: AuthEventType | '*', listener: AuthEventListener): void;
1182
1195
  /**
1183
- * Start social OAuth flow with automatic state management.
1196
+ * Start redirect-first social OAuth flow (web).
1184
1197
  *
1185
- * Generates a secure state token, stores OAuth context, and redirects to the OAuth provider.
1186
- * After OAuth callback, use `handleOAuthCallback()` to complete authentication.
1198
+ * This performs a browser navigation to:
1199
+ * `GET {baseUrl}/social/:provider/redirect?returnTo=...&appState=...`
1200
+ *
1201
+ * The backend:
1202
+ * - generates and stores CSRF state (cluster-safe)
1203
+ * - redirects the user to the provider
1204
+ * - completes OAuth on callback and sets cookies (or issues an exchange token)
1205
+ * - redirects back to `returnTo` with `appState` (and `exchangeToken` for json/hybrid)
1187
1206
  *
1188
1207
  * @param provider - OAuth provider ('google', 'apple', 'facebook')
1189
- * @param options - Optional configuration
1208
+ * @param options - Optional redirect options
1190
1209
  *
1191
1210
  * @example
1192
1211
  * ```typescript
1193
- * // Simple usage
1194
- * await client.loginWithSocial('google');
1195
- *
1196
- * // With custom redirect URI
1197
- * await client.loginWithSocial('apple', {
1198
- * redirectUri: 'https://example.com/auth/callback'
1199
- * });
1212
+ * await client.loginWithSocial('google', { returnTo: '/auth/callback', appState: '12345' });
1200
1213
  * ```
1201
1214
  */
1202
- loginWithSocial(provider: SocialProvider, _options?: {
1203
- redirectUri?: string;
1204
- }): Promise<void>;
1215
+ loginWithSocial(provider: SocialProvider, options?: SocialLoginOptions): Promise<void>;
1205
1216
  /**
1206
- * Auto-detect and handle OAuth callback.
1207
- *
1208
- * Call this on app initialization or in callback route.
1209
- * Returns null if not an OAuth callback (no provider/code params).
1210
- *
1211
- * The SDK validates the state token, completes authentication via backend,
1212
- * and emits appropriate events.
1217
+ * Exchange an `exchangeToken` (from redirect callback URL) into an AuthResponse.
1213
1218
  *
1214
- * @param urlOrParams - Optional URL string or URLSearchParams (auto-detects from window.location if not provided)
1215
- * @returns AuthResponse if OAuth callback detected, null otherwise
1219
+ * Used for `tokenDelivery: 'json'` or hybrid flows where the backend redirects back
1220
+ * with `exchangeToken` instead of setting cookies.
1216
1221
  *
1217
- * @example
1218
- * ```typescript
1219
- * // Auto-detect on app init
1220
- * const response = await client.handleOAuthCallback();
1221
- * if (response) {
1222
- * if (response.challengeName) {
1223
- * router.navigate(['/challenge', response.challengeName]);
1224
- * } else {
1225
- * router.navigate(['/']); // Navigate to your app's home route
1226
- * }
1227
- * }
1228
- *
1229
- * // In callback route
1230
- * const response = await client.handleOAuthCallback(window.location.search);
1231
- * ```
1232
- */
1233
- handleOAuthCallback(urlOrParams?: string | URLSearchParams): Promise<AuthResponse | null>;
1234
- /**
1235
- * Get social auth URL (low-level API).
1236
- *
1237
- * For most cases, use `loginWithSocial()` which handles state management automatically.
1238
- */
1239
- getSocialAuthUrl(request: SocialAuthUrlRequest): Promise<{
1240
- url: string;
1241
- }>;
1242
- /**
1243
- * Handle social callback.
1222
+ * @param exchangeToken - One-time exchange token from the callback URL
1223
+ * @returns AuthResponse
1244
1224
  */
1245
- handleSocialCallback(request: SocialCallbackRequest): Promise<AuthResponse>;
1225
+ exchangeSocialRedirect(exchangeToken: string): Promise<AuthResponse>;
1246
1226
  /**
1247
1227
  * Verify native social token (mobile).
1248
1228
  */
@@ -1548,6 +1528,7 @@ declare class BrowserStorage implements NAuthStorageAdapter {
1548
1528
  getItem(key: string): Promise<string | null>;
1549
1529
  setItem(key: string, value: string): Promise<void>;
1550
1530
  removeItem(key: string): Promise<void>;
1531
+ clear(): Promise<void>;
1551
1532
  }
1552
1533
 
1553
1534
  /**
@@ -1558,6 +1539,7 @@ declare class InMemoryStorage implements NAuthStorageAdapter {
1558
1539
  getItem(key: string): Promise<string | null>;
1559
1540
  setItem(key: string, value: string): Promise<void>;
1560
1541
  removeItem(key: string): Promise<void>;
1542
+ clear(): Promise<void>;
1561
1543
  }
1562
1544
 
1563
1545
  /**
@@ -1590,4 +1572,4 @@ declare class FetchAdapter implements HttpAdapter {
1590
1572
  request<T>(config: HttpRequest): Promise<HttpResponse<T>>;
1591
1573
  }
1592
1574
 
1593
- export { type AuditHistoryResponse, type AuthAuditEvent, type AuthAuditEventStatus, AuthAuditEventType, AuthChallenge, type AuthChallengeEvent, type AuthErrorEvent, type AuthEvent, type AuthEventListener, type AuthEventType, type AuthLoginEvent, type AuthLogoutEvent, type AuthRefreshEvent, type AuthResponse, type AuthSignupEvent, type AuthSuccessEvent, type AuthUser, type AuthUserSummary, type BackupCodesResponse, type BaseChallengeResponse, BrowserStorage, type ChallengeResponse, type ChangePasswordRequest, type ConfirmForgotPasswordRequest, type ConfirmForgotPasswordResponse, EventEmitter, FetchAdapter, type ForceChangePasswordResponse, type ForgotPasswordRequest, type ForgotPasswordResponse, type GetChallengeDataRequest, type GetChallengeDataResponse, type GetSetupDataRequest, type GetSetupDataResponse, type HttpAdapter, type HttpRequest, type HttpResponse, InMemoryStorage, type LinkedAccountsResponse, type LoginRequest, type LogoutAllRequest, type LogoutRequest, type MFAChallengeMethod, type MFACodeResponse, type MFADevice, type MFADeviceMethod, type MFAMethod, type MFAPasskeyResponse, type MFASetupData, type MFASetupResponse, type MFAStatus, NAuthClient, type NAuthClientConfig, NAuthClientError, type NAuthEndpoints, type NAuthError, NAuthErrorCode, type NAuthStorageAdapter, type OAuthCallbackEvent, type OAuthCompletedEvent, type OAuthErrorEvent, type OAuthStartedEvent, type ResendCodeRequest, type ResolvedNAuthClientConfig, type SignupRequest, type SocialAuthUrlRequest, type SocialAuthUrlResponse, type SocialCallbackRequest, type SocialProvider, type SocialVerifyRequest, type TokenDeliveryMode, type TokenResponse, type UpdateProfileRequest, type VerifyEmailResponse, type VerifyPhoneCodeResponse, type VerifyPhoneCollectResponse, defaultEndpoints, getChallengeInstructions, getMFAMethod, getMaskedDestination, isOTPChallenge, requiresPhoneCollection, resolveConfig };
1575
+ export { type AuditHistoryResponse, type AuthAuditEvent, type AuthAuditEventStatus, AuthAuditEventType, AuthChallenge, type AuthChallengeEvent, type AuthErrorEvent, type AuthEvent, type AuthEventListener, type AuthEventType, type AuthLoginEvent, type AuthLogoutEvent, type AuthRefreshEvent, type AuthResponse, type AuthSignupEvent, type AuthSuccessEvent, type AuthUser, type AuthUserSummary, type BackupCodesResponse, type BaseChallengeResponse, BrowserStorage, type ChallengeResponse, type ChangePasswordRequest, type ConfirmForgotPasswordRequest, type ConfirmForgotPasswordResponse, EventEmitter, FetchAdapter, type ForceChangePasswordResponse, type ForgotPasswordRequest, type ForgotPasswordResponse, type GetChallengeDataRequest, type GetChallengeDataResponse, type GetSetupDataRequest, type GetSetupDataResponse, type HttpAdapter, type HttpRequest, type HttpResponse, InMemoryStorage, type LinkedAccountsResponse, type LoginRequest, type LogoutAllRequest, type LogoutRequest, type MFAChallengeMethod, type MFACodeResponse, type MFADevice, type MFADeviceMethod, type MFAMethod, type MFAPasskeyResponse, type MFASetupData, type MFASetupResponse, type MFAStatus, NAuthClient, type NAuthClientConfig, NAuthClientError, type NAuthEndpoints, type NAuthError, NAuthErrorCode, type NAuthStorageAdapter, type OAuthCallbackEvent, type OAuthCompletedEvent, type OAuthErrorEvent, type OAuthStartedEvent, type ResendCodeRequest, type ResolvedNAuthClientConfig, type SignupRequest, type SocialLoginOptions, type SocialProvider, type SocialVerifyRequest, type TokenDeliveryMode, type TokenResponse, type UpdateProfileRequest, type VerifyEmailResponse, type VerifyPhoneCodeResponse, type VerifyPhoneCollectResponse, defaultEndpoints, getChallengeInstructions, getMFAMethod, getMaskedDestination, isOTPChallenge, requiresPhoneCollection, resolveConfig };
package/dist/index.mjs CHANGED
@@ -126,12 +126,12 @@ var defaultEndpoints = {
126
126
  mfaPreferred: "/mfa/preferred-method",
127
127
  mfaBackupCodes: "/mfa/backup-codes/generate",
128
128
  mfaExemption: "/mfa/exemption",
129
- socialAuthUrl: "/social/auth-url",
130
- socialCallback: "/social/callback",
131
129
  socialLinked: "/social/linked",
132
130
  socialLink: "/social/link",
133
131
  socialUnlink: "/social/unlink",
134
132
  socialVerify: "/social/:provider/verify",
133
+ socialRedirectStart: "/social/:provider/redirect",
134
+ socialExchange: "/social/exchange",
135
135
  trustDevice: "/trust-device",
136
136
  isTrustedDevice: "/is-trusted-device",
137
137
  auditHistory: "/audit/history",
@@ -367,6 +367,9 @@ var BrowserStorage = class {
367
367
  async removeItem(key) {
368
368
  this.storage.removeItem(key);
369
369
  }
370
+ async clear() {
371
+ this.storage.clear();
372
+ }
370
373
  };
371
374
 
372
375
  // src/storage/memory.ts
@@ -383,6 +386,9 @@ var InMemoryStorage = class {
383
386
  async removeItem(key) {
384
387
  this.store.delete(key);
385
388
  }
389
+ async clear() {
390
+ this.store.clear();
391
+ }
386
392
  };
387
393
 
388
394
  // src/core/events.ts
@@ -503,9 +509,9 @@ var FetchAdapter = class {
503
509
  });
504
510
  if (!response.ok) {
505
511
  const errorData = typeof data === "object" && data !== null ? data : {};
506
- const code = typeof errorData["code"] === "string" ? errorData.code : "INTERNAL_ERROR" /* INTERNAL_ERROR */;
507
- const message = typeof errorData["message"] === "string" ? errorData.message : `Request failed with status ${status}`;
508
- const timestamp = typeof errorData["timestamp"] === "string" ? errorData.timestamp : void 0;
512
+ const code = typeof errorData["code"] === "string" ? errorData["code"] : "INTERNAL_ERROR" /* INTERNAL_ERROR */;
513
+ const message = typeof errorData["message"] === "string" ? errorData["message"] : `Request failed with status ${status}`;
514
+ const timestamp = typeof errorData["timestamp"] === "string" ? errorData["timestamp"] : void 0;
509
515
  const details = errorData["details"];
510
516
  throw new NAuthClientError(code, message, {
511
517
  statusCode: status,
@@ -927,126 +933,59 @@ var NAuthClient = class {
927
933
  // Social Authentication
928
934
  // ============================================================================
929
935
  /**
930
- * Start social OAuth flow with automatic state management.
936
+ * Start redirect-first social OAuth flow (web).
937
+ *
938
+ * This performs a browser navigation to:
939
+ * `GET {baseUrl}/social/:provider/redirect?returnTo=...&appState=...`
931
940
  *
932
- * Generates a secure state token, stores OAuth context, and redirects to the OAuth provider.
933
- * After OAuth callback, use `handleOAuthCallback()` to complete authentication.
941
+ * The backend:
942
+ * - generates and stores CSRF state (cluster-safe)
943
+ * - redirects the user to the provider
944
+ * - completes OAuth on callback and sets cookies (or issues an exchange token)
945
+ * - redirects back to `returnTo` with `appState` (and `exchangeToken` for json/hybrid)
934
946
  *
935
947
  * @param provider - OAuth provider ('google', 'apple', 'facebook')
936
- * @param options - Optional configuration
948
+ * @param options - Optional redirect options
937
949
  *
938
950
  * @example
939
951
  * ```typescript
940
- * // Simple usage
941
- * await client.loginWithSocial('google');
942
- *
943
- * // With custom redirect URI
944
- * await client.loginWithSocial('apple', {
945
- * redirectUri: 'https://example.com/auth/callback'
946
- * });
952
+ * await client.loginWithSocial('google', { returnTo: '/auth/callback', appState: '12345' });
947
953
  * ```
948
954
  */
949
- async loginWithSocial(provider, _options) {
955
+ async loginWithSocial(provider, options) {
950
956
  this.eventEmitter.emit({ type: "oauth:started", data: { provider }, timestamp: Date.now() });
951
- const { url } = await this.getSocialAuthUrl({ provider });
952
957
  if (hasWindow()) {
953
- window.location.href = url;
954
- }
955
- }
956
- /**
957
- * Auto-detect and handle OAuth callback.
958
- *
959
- * Call this on app initialization or in callback route.
960
- * Returns null if not an OAuth callback (no provider/code params).
961
- *
962
- * The SDK validates the state token, completes authentication via backend,
963
- * and emits appropriate events.
964
- *
965
- * @param urlOrParams - Optional URL string or URLSearchParams (auto-detects from window.location if not provided)
966
- * @returns AuthResponse if OAuth callback detected, null otherwise
967
- *
968
- * @example
969
- * ```typescript
970
- * // Auto-detect on app init
971
- * const response = await client.handleOAuthCallback();
972
- * if (response) {
973
- * if (response.challengeName) {
974
- * router.navigate(['/challenge', response.challengeName]);
975
- * } else {
976
- * router.navigate(['/']); // Navigate to your app's home route
977
- * }
978
- * }
979
- *
980
- * // In callback route
981
- * const response = await client.handleOAuthCallback(window.location.search);
982
- * ```
983
- */
984
- async handleOAuthCallback(urlOrParams) {
985
- let params;
986
- if (urlOrParams instanceof URLSearchParams) {
987
- params = urlOrParams;
988
- } else if (typeof urlOrParams === "string") {
989
- params = new URLSearchParams(urlOrParams);
990
- } else if (hasWindow()) {
991
- params = new URLSearchParams(window.location.search);
992
- } else {
993
- return null;
994
- }
995
- const provider = params.get("provider");
996
- const code = params.get("code");
997
- const state = params.get("state");
998
- const error = params.get("error");
999
- if (!provider || !code && !error) {
1000
- return null;
1001
- }
1002
- this.eventEmitter.emit({ type: "oauth:callback", data: { provider }, timestamp: Date.now() });
1003
- try {
1004
- if (error) {
1005
- const authError = new NAuthClientError(
1006
- "SOCIAL_TOKEN_INVALID" /* SOCIAL_TOKEN_INVALID */,
1007
- params.get("error_description") || error,
1008
- { details: { error, provider } }
1009
- );
1010
- this.eventEmitter.emit({ type: "oauth:error", data: authError, timestamp: Date.now() });
1011
- throw authError;
1012
- }
1013
- if (!state) {
1014
- throw new NAuthClientError("CHALLENGE_INVALID" /* CHALLENGE_INVALID */, "Missing OAuth state parameter");
958
+ const startPath = this.config.endpoints.socialRedirectStart.replace(":provider", provider);
959
+ const base = this.config.baseUrl.replace(/\/$/, "");
960
+ const startUrl = new URL(`${base}${startPath}`);
961
+ const returnTo = options?.returnTo ?? this.config.redirects?.success ?? "/";
962
+ const action = options?.action ?? "login";
963
+ startUrl.searchParams.set("returnTo", returnTo);
964
+ startUrl.searchParams.set("action", action);
965
+ if (options?.delivery === "cookies" || options?.delivery === "json") {
966
+ startUrl.searchParams.set("delivery", options.delivery);
1015
967
  }
1016
- const response = await this.handleSocialCallback({
1017
- provider,
1018
- code,
1019
- state
1020
- });
1021
- if (response.challengeName) {
1022
- this.eventEmitter.emit({ type: "auth:challenge", data: response, timestamp: Date.now() });
1023
- } else {
1024
- this.eventEmitter.emit({ type: "auth:success", data: response, timestamp: Date.now() });
968
+ if (typeof options?.appState === "string" && options.appState.trim() !== "") {
969
+ startUrl.searchParams.set("appState", options.appState);
1025
970
  }
1026
- this.eventEmitter.emit({ type: "oauth:completed", data: response, timestamp: Date.now() });
1027
- return response;
1028
- } catch (error2) {
1029
- const authError = error2 instanceof NAuthClientError ? error2 : new NAuthClientError(
1030
- "SOCIAL_TOKEN_INVALID" /* SOCIAL_TOKEN_INVALID */,
1031
- error2.message || "OAuth callback failed"
1032
- );
1033
- this.eventEmitter.emit({ type: "oauth:error", data: authError, timestamp: Date.now() });
1034
- throw authError;
971
+ window.location.href = startUrl.toString();
1035
972
  }
1036
973
  }
1037
974
  /**
1038
- * Get social auth URL (low-level API).
975
+ * Exchange an `exchangeToken` (from redirect callback URL) into an AuthResponse.
1039
976
  *
1040
- * For most cases, use `loginWithSocial()` which handles state management automatically.
1041
- */
1042
- async getSocialAuthUrl(request) {
1043
- return this.post(this.config.endpoints.socialAuthUrl, request);
1044
- }
1045
- /**
1046
- * Handle social callback.
977
+ * Used for `tokenDelivery: 'json'` or hybrid flows where the backend redirects back
978
+ * with `exchangeToken` instead of setting cookies.
979
+ *
980
+ * @param exchangeToken - One-time exchange token from the callback URL
981
+ * @returns AuthResponse
1047
982
  */
1048
- async handleSocialCallback(request) {
1049
- const result = await this.post(this.config.endpoints.socialCallback, request);
983
+ async exchangeSocialRedirect(exchangeToken) {
984
+ const token = exchangeToken?.trim();
985
+ if (!token) {
986
+ throw new NAuthClientError("CHALLENGE_INVALID" /* CHALLENGE_INVALID */, "Missing exchangeToken");
987
+ }
988
+ const result = await this.post(this.config.endpoints.socialExchange, { exchangeToken: token });
1050
989
  await this.handleAuthResponse(result);
1051
990
  return result;
1052
991
  }