@nauth-toolkit/client 0.1.88 → 0.1.90
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 +756 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +1245 -18
- package/dist/index.d.ts +1245 -18
- package/dist/index.mjs +754 -23
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -22,6 +22,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
22
22
|
// src/index.ts
|
|
23
23
|
var index_exports = {};
|
|
24
24
|
__export(index_exports, {
|
|
25
|
+
AdminOperations: () => AdminOperations,
|
|
25
26
|
AuthAuditEventType: () => AuthAuditEventType,
|
|
26
27
|
AuthChallenge: () => AuthChallenge,
|
|
27
28
|
BrowserStorage: () => BrowserStorage,
|
|
@@ -32,6 +33,7 @@ __export(index_exports, {
|
|
|
32
33
|
NAuthClient: () => NAuthClient,
|
|
33
34
|
NAuthClientError: () => NAuthClientError,
|
|
34
35
|
NAuthErrorCode: () => NAuthErrorCode,
|
|
36
|
+
defaultAdminEndpoints: () => defaultAdminEndpoints,
|
|
35
37
|
defaultEndpoints: () => defaultEndpoints,
|
|
36
38
|
getChallengeInstructions: () => getChallengeInstructions,
|
|
37
39
|
getMFAMethod: () => getMFAMethod,
|
|
@@ -176,11 +178,45 @@ var defaultEndpoints = {
|
|
|
176
178
|
auditHistory: "/audit/history",
|
|
177
179
|
updateProfile: "/profile"
|
|
178
180
|
};
|
|
181
|
+
var defaultAdminEndpoints = {
|
|
182
|
+
signup: "/signup",
|
|
183
|
+
signupSocial: "/signup-social",
|
|
184
|
+
getUsers: "/users",
|
|
185
|
+
getUser: "/users/:sub",
|
|
186
|
+
deleteUser: "/users/:sub",
|
|
187
|
+
disableUser: "/users/:sub/disable",
|
|
188
|
+
enableUser: "/users/:sub/enable",
|
|
189
|
+
forcePasswordChange: "/users/:sub/force-password-change",
|
|
190
|
+
setPassword: "/set-password",
|
|
191
|
+
resetPasswordInitiate: "/reset-password/initiate",
|
|
192
|
+
getUserSessions: "/users/:sub/sessions",
|
|
193
|
+
logoutAll: "/users/:sub/logout-all",
|
|
194
|
+
getMfaStatus: "/users/:sub/mfa/status",
|
|
195
|
+
setPreferredMfaMethod: "/mfa/preferred-method",
|
|
196
|
+
removeMfaDevices: "/mfa/remove-devices",
|
|
197
|
+
setMfaExemption: "/mfa/exemption",
|
|
198
|
+
getAuditHistory: "/audit/history"
|
|
199
|
+
};
|
|
179
200
|
var resolveConfig = (config, defaultAdapter) => {
|
|
180
201
|
const resolvedEndpoints = {
|
|
181
202
|
...defaultEndpoints,
|
|
182
203
|
...config.endpoints ?? {}
|
|
183
204
|
};
|
|
205
|
+
let resolvedAdmin;
|
|
206
|
+
if (config.admin) {
|
|
207
|
+
const resolvedAdminEndpoints = {
|
|
208
|
+
...defaultAdminEndpoints,
|
|
209
|
+
...config.admin.endpoints ?? {}
|
|
210
|
+
};
|
|
211
|
+
resolvedAdmin = {
|
|
212
|
+
pathPrefix: config.admin.pathPrefix ?? "/admin",
|
|
213
|
+
endpoints: resolvedAdminEndpoints,
|
|
214
|
+
headers: {
|
|
215
|
+
...config.headers,
|
|
216
|
+
...config.admin.headers ?? {}
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
}
|
|
184
220
|
return {
|
|
185
221
|
...config,
|
|
186
222
|
csrf: {
|
|
@@ -195,7 +231,8 @@ var resolveConfig = (config, defaultAdapter) => {
|
|
|
195
231
|
timeout: config.timeout ?? 3e4,
|
|
196
232
|
endpoints: resolvedEndpoints,
|
|
197
233
|
storage: config.storage,
|
|
198
|
-
httpAdapter: config.httpAdapter ?? defaultAdapter
|
|
234
|
+
httpAdapter: config.httpAdapter ?? defaultAdapter,
|
|
235
|
+
admin: resolvedAdmin
|
|
199
236
|
};
|
|
200
237
|
};
|
|
201
238
|
|
|
@@ -568,8 +605,9 @@ var FetchAdapter = class {
|
|
|
568
605
|
// src/core/challenge-router.ts
|
|
569
606
|
var OAUTH_STATE_KEY = "nauth_oauth_state";
|
|
570
607
|
var ChallengeRouter = class {
|
|
571
|
-
constructor(config) {
|
|
608
|
+
constructor(config, oauthStorage) {
|
|
572
609
|
this.config = config;
|
|
610
|
+
this.oauthStorage = oauthStorage;
|
|
573
611
|
}
|
|
574
612
|
/**
|
|
575
613
|
* Handle auth response - either call callback or auto-navigate.
|
|
@@ -585,10 +623,39 @@ var ChallengeRouter = class {
|
|
|
585
623
|
if (response.challengeName) {
|
|
586
624
|
await this.navigateToChallenge(response);
|
|
587
625
|
} else {
|
|
588
|
-
const queryParams = await this.getStoredOauthState();
|
|
589
|
-
await this.navigateToSuccess(queryParams);
|
|
626
|
+
const queryParams = context.appState ? { appState: context.appState } : await this.getStoredOauthState();
|
|
627
|
+
await this.navigateToSuccess(queryParams, context);
|
|
590
628
|
}
|
|
591
629
|
}
|
|
630
|
+
/**
|
|
631
|
+
* Resolve the configured success URL based on context and explicit override keys.
|
|
632
|
+
*
|
|
633
|
+
* IMPORTANT:
|
|
634
|
+
* - `null` explicitly disables auto-navigation (caller should rely on framework events / custom routing).
|
|
635
|
+
* - `undefined` / missing keys fall back to other configured values or defaults.
|
|
636
|
+
* - Falls back to legacy `redirects.success` when new keys are not set.
|
|
637
|
+
*
|
|
638
|
+
* @param context - Auth response context
|
|
639
|
+
* @returns URL string, or null when auto-navigation is disabled
|
|
640
|
+
*/
|
|
641
|
+
resolveSuccessUrl(context) {
|
|
642
|
+
const redirects = this.config.redirects;
|
|
643
|
+
if (!redirects) {
|
|
644
|
+
return "/";
|
|
645
|
+
}
|
|
646
|
+
const isSignup = context?.source === "signup";
|
|
647
|
+
if (isSignup) {
|
|
648
|
+
if (redirects.signupSuccess === null) return null;
|
|
649
|
+
if (typeof redirects.signupSuccess === "string") return redirects.signupSuccess;
|
|
650
|
+
}
|
|
651
|
+
if (!isSignup) {
|
|
652
|
+
if (redirects.loginSuccess === null) return null;
|
|
653
|
+
if (typeof redirects.loginSuccess === "string") return redirects.loginSuccess;
|
|
654
|
+
}
|
|
655
|
+
if (redirects.success === null) return null;
|
|
656
|
+
if (typeof redirects.success === "string") return redirects.success;
|
|
657
|
+
return "/";
|
|
658
|
+
}
|
|
592
659
|
/**
|
|
593
660
|
* Navigate to appropriate challenge route.
|
|
594
661
|
*
|
|
@@ -602,14 +669,19 @@ var ChallengeRouter = class {
|
|
|
602
669
|
* Navigate to success URL.
|
|
603
670
|
*
|
|
604
671
|
* @param queryParams - Optional query parameters to append to the success URL
|
|
672
|
+
* @param context - Optional auth context for selecting the success route
|
|
605
673
|
*
|
|
606
674
|
* @example
|
|
607
675
|
* ```typescript
|
|
608
676
|
* await router.navigateToSuccess({ appState: 'invite-code-123' });
|
|
609
677
|
* ```
|
|
610
678
|
*/
|
|
611
|
-
async navigateToSuccess(queryParams) {
|
|
612
|
-
|
|
679
|
+
async navigateToSuccess(queryParams, context) {
|
|
680
|
+
const resolved = this.resolveSuccessUrl(context);
|
|
681
|
+
if (resolved === null) {
|
|
682
|
+
return;
|
|
683
|
+
}
|
|
684
|
+
let url = resolved;
|
|
613
685
|
if (queryParams && Object.keys(queryParams).length > 0) {
|
|
614
686
|
const searchParams = new URLSearchParams();
|
|
615
687
|
Object.entries(queryParams).forEach(([key, value]) => {
|
|
@@ -623,15 +695,18 @@ var ChallengeRouter = class {
|
|
|
623
695
|
await this.navigate(url);
|
|
624
696
|
}
|
|
625
697
|
/**
|
|
626
|
-
* Retrieve stored OAuth appState from
|
|
698
|
+
* Retrieve stored OAuth appState from sessionStorage.
|
|
699
|
+
*
|
|
700
|
+
* NOTE: This method does NOT clear the storage. Only the public getLastOauthState() API clears it.
|
|
701
|
+
* This allows the SDK to read appState for auto-navigation while still making it available to
|
|
702
|
+
* consumers via the public API.
|
|
627
703
|
*
|
|
628
704
|
* @returns Query params object with appState if present, undefined otherwise
|
|
629
705
|
*/
|
|
630
706
|
async getStoredOauthState() {
|
|
631
707
|
try {
|
|
632
|
-
const stored = await this.
|
|
708
|
+
const stored = await this.oauthStorage.getItem(OAUTH_STATE_KEY);
|
|
633
709
|
if (stored) {
|
|
634
|
-
await this.config.storage.removeItem(OAUTH_STATE_KEY);
|
|
635
710
|
return { appState: stored };
|
|
636
711
|
}
|
|
637
712
|
} catch {
|
|
@@ -644,7 +719,12 @@ var ChallengeRouter = class {
|
|
|
644
719
|
* @param type - Type of error (oauth or session)
|
|
645
720
|
*/
|
|
646
721
|
async navigateToError(type) {
|
|
647
|
-
const
|
|
722
|
+
const redirects = this.config.redirects;
|
|
723
|
+
const raw = type === "oauth" ? redirects?.oauthError : redirects?.sessionExpired;
|
|
724
|
+
if (raw === null) {
|
|
725
|
+
return;
|
|
726
|
+
}
|
|
727
|
+
const url = raw ?? "/login";
|
|
648
728
|
await this.navigate(url);
|
|
649
729
|
}
|
|
650
730
|
/**
|
|
@@ -749,13 +829,625 @@ var ChallengeRouter = class {
|
|
|
749
829
|
}
|
|
750
830
|
};
|
|
751
831
|
|
|
832
|
+
// src/core/admin-operations.ts
|
|
833
|
+
var hasWindow = () => typeof globalThis !== "undefined" && typeof globalThis.window !== "undefined";
|
|
834
|
+
var AdminOperations = class {
|
|
835
|
+
/**
|
|
836
|
+
* Create admin operations instance.
|
|
837
|
+
*
|
|
838
|
+
* @param config - Resolved client configuration
|
|
839
|
+
*/
|
|
840
|
+
constructor(config) {
|
|
841
|
+
__publicField(this, "config");
|
|
842
|
+
__publicField(this, "adminEndpoints");
|
|
843
|
+
__publicField(this, "adminPathPrefix");
|
|
844
|
+
__publicField(this, "adminHeaders");
|
|
845
|
+
if (!config.admin) {
|
|
846
|
+
throw new Error("Admin operations require admin configuration");
|
|
847
|
+
}
|
|
848
|
+
this.config = config;
|
|
849
|
+
this.adminEndpoints = config.admin.endpoints;
|
|
850
|
+
this.adminPathPrefix = config.admin.pathPrefix;
|
|
851
|
+
this.adminHeaders = config.admin.headers;
|
|
852
|
+
}
|
|
853
|
+
// ============================================================================
|
|
854
|
+
// User Management
|
|
855
|
+
// ============================================================================
|
|
856
|
+
/**
|
|
857
|
+
* Create a new user (admin operation)
|
|
858
|
+
*
|
|
859
|
+
* Allows creating users with:
|
|
860
|
+
* - Pre-verified email/phone
|
|
861
|
+
* - Auto-generated passwords
|
|
862
|
+
* - Force password change flag
|
|
863
|
+
*
|
|
864
|
+
* @param request - User creation request
|
|
865
|
+
* @returns Created user and optional generated password
|
|
866
|
+
* @throws {NAuthClientError} If creation fails
|
|
867
|
+
*
|
|
868
|
+
* @example
|
|
869
|
+
* ```typescript
|
|
870
|
+
* const result = await client.admin.createUser({
|
|
871
|
+
* email: 'user@example.com',
|
|
872
|
+
* password: 'SecurePass123!',
|
|
873
|
+
* isEmailVerified: true,
|
|
874
|
+
* });
|
|
875
|
+
*
|
|
876
|
+
* // With auto-generated password
|
|
877
|
+
* const result = await client.admin.createUser({
|
|
878
|
+
* email: 'user@example.com',
|
|
879
|
+
* generatePassword: true,
|
|
880
|
+
* mustChangePassword: true,
|
|
881
|
+
* });
|
|
882
|
+
* console.log('Generated password:', result.generatedPassword);
|
|
883
|
+
* ```
|
|
884
|
+
*/
|
|
885
|
+
async createUser(request) {
|
|
886
|
+
const path = this.buildAdminUrl(this.adminEndpoints.signup);
|
|
887
|
+
return this.post(path, request);
|
|
888
|
+
}
|
|
889
|
+
/**
|
|
890
|
+
* Import social user (admin operation)
|
|
891
|
+
*
|
|
892
|
+
* Imports existing social users from external platforms (e.g., Cognito, Auth0)
|
|
893
|
+
* with social account linkage.
|
|
894
|
+
*
|
|
895
|
+
* @param request - Social user import request
|
|
896
|
+
* @returns Created user and social account info
|
|
897
|
+
* @throws {NAuthClientError} If import fails
|
|
898
|
+
*
|
|
899
|
+
* @example
|
|
900
|
+
* ```typescript
|
|
901
|
+
* const result = await client.admin.importSocialUser({
|
|
902
|
+
* email: 'user@example.com',
|
|
903
|
+
* provider: 'google',
|
|
904
|
+
* providerId: 'google_12345',
|
|
905
|
+
* providerEmail: 'user@gmail.com',
|
|
906
|
+
* });
|
|
907
|
+
* ```
|
|
908
|
+
*/
|
|
909
|
+
async importSocialUser(request) {
|
|
910
|
+
const path = this.buildAdminUrl(this.adminEndpoints.signupSocial);
|
|
911
|
+
return this.post(path, request);
|
|
912
|
+
}
|
|
913
|
+
/**
|
|
914
|
+
* Get users with filters and pagination
|
|
915
|
+
*
|
|
916
|
+
* @param params - Filter and pagination params
|
|
917
|
+
* @returns Paginated user list
|
|
918
|
+
* @throws {NAuthClientError} If request fails
|
|
919
|
+
*
|
|
920
|
+
* @example
|
|
921
|
+
* ```typescript
|
|
922
|
+
* const result = await client.admin.getUsers({
|
|
923
|
+
* page: 1,
|
|
924
|
+
* limit: 20,
|
|
925
|
+
* isEmailVerified: true,
|
|
926
|
+
* mfaEnabled: false,
|
|
927
|
+
* sortBy: 'createdAt',
|
|
928
|
+
* sortOrder: 'DESC',
|
|
929
|
+
* });
|
|
930
|
+
* ```
|
|
931
|
+
*/
|
|
932
|
+
async getUsers(params = {}) {
|
|
933
|
+
const path = this.buildAdminUrl(this.adminEndpoints.getUsers);
|
|
934
|
+
const queryString = this.buildQueryString(params);
|
|
935
|
+
return this.get(`${path}${queryString}`);
|
|
936
|
+
}
|
|
937
|
+
/**
|
|
938
|
+
* Get user by sub (UUID)
|
|
939
|
+
*
|
|
940
|
+
* @param sub - User UUID
|
|
941
|
+
* @returns User object
|
|
942
|
+
* @throws {NAuthClientError} If user not found
|
|
943
|
+
*
|
|
944
|
+
* @example
|
|
945
|
+
* ```typescript
|
|
946
|
+
* const user = await client.admin.getUser('a21b654c-2746-4168-acee-c175083a65cd');
|
|
947
|
+
* ```
|
|
948
|
+
*/
|
|
949
|
+
async getUser(sub) {
|
|
950
|
+
const path = this.buildAdminUrl(this.adminEndpoints.getUser, { sub });
|
|
951
|
+
return this.get(path);
|
|
952
|
+
}
|
|
953
|
+
/**
|
|
954
|
+
* Delete user with cascade cleanup
|
|
955
|
+
*
|
|
956
|
+
* @param sub - User UUID
|
|
957
|
+
* @returns Deletion confirmation with cascade counts
|
|
958
|
+
* @throws {NAuthClientError} If deletion fails
|
|
959
|
+
*
|
|
960
|
+
* @example
|
|
961
|
+
* ```typescript
|
|
962
|
+
* const result = await client.admin.deleteUser('user-uuid');
|
|
963
|
+
* console.log('Deleted records:', result.deletedRecords);
|
|
964
|
+
* ```
|
|
965
|
+
*/
|
|
966
|
+
async deleteUser(sub) {
|
|
967
|
+
const path = this.buildAdminUrl(this.adminEndpoints.deleteUser, { sub });
|
|
968
|
+
return this.delete(path);
|
|
969
|
+
}
|
|
970
|
+
/**
|
|
971
|
+
* Disable user account (permanent lock)
|
|
972
|
+
*
|
|
973
|
+
* @param sub - User UUID
|
|
974
|
+
* @param reason - Optional reason for disabling
|
|
975
|
+
* @returns Disable confirmation with revoked session count
|
|
976
|
+
* @throws {NAuthClientError} If operation fails
|
|
977
|
+
*
|
|
978
|
+
* @example
|
|
979
|
+
* ```typescript
|
|
980
|
+
* const result = await client.admin.disableUser(
|
|
981
|
+
* 'user-uuid',
|
|
982
|
+
* 'Account compromised'
|
|
983
|
+
* );
|
|
984
|
+
* console.log('Revoked sessions:', result.revokedSessions);
|
|
985
|
+
* ```
|
|
986
|
+
*/
|
|
987
|
+
async disableUser(sub, reason) {
|
|
988
|
+
const path = this.buildAdminUrl(this.adminEndpoints.disableUser, { sub });
|
|
989
|
+
return this.post(path, { reason });
|
|
990
|
+
}
|
|
991
|
+
/**
|
|
992
|
+
* Enable (unlock) user account
|
|
993
|
+
*
|
|
994
|
+
* @param sub - User UUID
|
|
995
|
+
* @returns Enable confirmation with updated user
|
|
996
|
+
* @throws {NAuthClientError} If operation fails
|
|
997
|
+
*
|
|
998
|
+
* @example
|
|
999
|
+
* ```typescript
|
|
1000
|
+
* const result = await client.admin.enableUser('user-uuid');
|
|
1001
|
+
* console.log('User enabled:', result.user);
|
|
1002
|
+
* ```
|
|
1003
|
+
*/
|
|
1004
|
+
async enableUser(sub) {
|
|
1005
|
+
const path = this.buildAdminUrl(this.adminEndpoints.enableUser, { sub });
|
|
1006
|
+
return this.post(path, {});
|
|
1007
|
+
}
|
|
1008
|
+
/**
|
|
1009
|
+
* Force password change on next login
|
|
1010
|
+
*
|
|
1011
|
+
* @param sub - User UUID
|
|
1012
|
+
* @returns Success confirmation
|
|
1013
|
+
* @throws {NAuthClientError} If operation fails
|
|
1014
|
+
*
|
|
1015
|
+
* @example
|
|
1016
|
+
* ```typescript
|
|
1017
|
+
* await client.admin.forcePasswordChange('user-uuid');
|
|
1018
|
+
* ```
|
|
1019
|
+
*/
|
|
1020
|
+
async forcePasswordChange(sub) {
|
|
1021
|
+
const path = this.buildAdminUrl(this.adminEndpoints.forcePasswordChange, { sub });
|
|
1022
|
+
return this.post(path, {});
|
|
1023
|
+
}
|
|
1024
|
+
// ============================================================================
|
|
1025
|
+
// Password Management
|
|
1026
|
+
// ============================================================================
|
|
1027
|
+
/**
|
|
1028
|
+
* Set password for any user (admin operation)
|
|
1029
|
+
*
|
|
1030
|
+
* @param identifier - User email, username, or phone
|
|
1031
|
+
* @param newPassword - New password
|
|
1032
|
+
* @returns Success confirmation
|
|
1033
|
+
* @throws {NAuthClientError} If operation fails
|
|
1034
|
+
*
|
|
1035
|
+
* @example
|
|
1036
|
+
* ```typescript
|
|
1037
|
+
* await client.admin.setPassword('user@example.com', 'NewSecurePass123!');
|
|
1038
|
+
* ```
|
|
1039
|
+
*/
|
|
1040
|
+
async setPassword(identifier, newPassword) {
|
|
1041
|
+
const path = this.buildAdminUrl(this.adminEndpoints.setPassword);
|
|
1042
|
+
return this.post(path, { identifier, newPassword });
|
|
1043
|
+
}
|
|
1044
|
+
/**
|
|
1045
|
+
* Initiate password reset workflow (sends code/link to user)
|
|
1046
|
+
*
|
|
1047
|
+
* @param request - Password reset request
|
|
1048
|
+
* @returns Reset confirmation with delivery details
|
|
1049
|
+
* @throws {NAuthClientError} If operation fails
|
|
1050
|
+
*
|
|
1051
|
+
* @example
|
|
1052
|
+
* ```typescript
|
|
1053
|
+
* const result = await client.admin.initiatePasswordReset({
|
|
1054
|
+
* sub: 'user-uuid',
|
|
1055
|
+
* deliveryMethod: 'email',
|
|
1056
|
+
* baseUrl: 'https://myapp.com/reset-password',
|
|
1057
|
+
* reason: 'User requested password reset',
|
|
1058
|
+
* });
|
|
1059
|
+
* console.log('Code sent to:', result.destination);
|
|
1060
|
+
* ```
|
|
1061
|
+
*/
|
|
1062
|
+
async initiatePasswordReset(request) {
|
|
1063
|
+
const path = this.buildAdminUrl(this.adminEndpoints.resetPasswordInitiate);
|
|
1064
|
+
return this.post(path, request);
|
|
1065
|
+
}
|
|
1066
|
+
// ============================================================================
|
|
1067
|
+
// Session Management
|
|
1068
|
+
// ============================================================================
|
|
1069
|
+
/**
|
|
1070
|
+
* Get all sessions for a user
|
|
1071
|
+
*
|
|
1072
|
+
* @param sub - User UUID
|
|
1073
|
+
* @returns User sessions
|
|
1074
|
+
* @throws {NAuthClientError} If request fails
|
|
1075
|
+
*
|
|
1076
|
+
* @example
|
|
1077
|
+
* ```typescript
|
|
1078
|
+
* const result = await client.admin.getUserSessions('user-uuid');
|
|
1079
|
+
* console.log('Active sessions:', result.sessions);
|
|
1080
|
+
* ```
|
|
1081
|
+
*/
|
|
1082
|
+
async getUserSessions(sub) {
|
|
1083
|
+
const path = this.buildAdminUrl(this.adminEndpoints.getUserSessions, { sub });
|
|
1084
|
+
return this.get(path);
|
|
1085
|
+
}
|
|
1086
|
+
/**
|
|
1087
|
+
* Logout all sessions for a user (admin-initiated)
|
|
1088
|
+
*
|
|
1089
|
+
* @param sub - User UUID
|
|
1090
|
+
* @param forgetDevices - If true, also revokes all trusted devices
|
|
1091
|
+
* @returns Number of sessions revoked
|
|
1092
|
+
* @throws {NAuthClientError} If operation fails
|
|
1093
|
+
*
|
|
1094
|
+
* @example
|
|
1095
|
+
* ```typescript
|
|
1096
|
+
* const result = await client.admin.logoutAllSessions('user-uuid', true);
|
|
1097
|
+
* console.log(`Revoked ${result.revokedCount} sessions`);
|
|
1098
|
+
* ```
|
|
1099
|
+
*/
|
|
1100
|
+
async logoutAllSessions(sub, forgetDevices = false) {
|
|
1101
|
+
const path = this.buildAdminUrl(this.adminEndpoints.logoutAll, { sub });
|
|
1102
|
+
return this.post(path, { forgetDevices });
|
|
1103
|
+
}
|
|
1104
|
+
// ============================================================================
|
|
1105
|
+
// MFA Management
|
|
1106
|
+
// ============================================================================
|
|
1107
|
+
/**
|
|
1108
|
+
* Get MFA status for a user
|
|
1109
|
+
*
|
|
1110
|
+
* @param sub - User UUID
|
|
1111
|
+
* @returns MFA status
|
|
1112
|
+
* @throws {NAuthClientError} If request fails
|
|
1113
|
+
*
|
|
1114
|
+
* @example
|
|
1115
|
+
* ```typescript
|
|
1116
|
+
* const status = await client.admin.getMfaStatus('user-uuid');
|
|
1117
|
+
* console.log('MFA enabled:', status.enabled);
|
|
1118
|
+
* ```
|
|
1119
|
+
*/
|
|
1120
|
+
async getMfaStatus(sub) {
|
|
1121
|
+
const path = this.buildAdminUrl(this.adminEndpoints.getMfaStatus, { sub });
|
|
1122
|
+
return this.get(path);
|
|
1123
|
+
}
|
|
1124
|
+
/**
|
|
1125
|
+
* Set preferred MFA method for a user
|
|
1126
|
+
*
|
|
1127
|
+
* @param sub - User UUID
|
|
1128
|
+
* @param method - MFA method to set as preferred
|
|
1129
|
+
* @returns Success message
|
|
1130
|
+
* @throws {NAuthClientError} If operation fails
|
|
1131
|
+
*
|
|
1132
|
+
* @example
|
|
1133
|
+
* ```typescript
|
|
1134
|
+
* await client.admin.setPreferredMfaMethod('user-uuid', 'totp');
|
|
1135
|
+
* ```
|
|
1136
|
+
*/
|
|
1137
|
+
async setPreferredMfaMethod(sub, method) {
|
|
1138
|
+
const path = this.buildAdminUrl(this.adminEndpoints.setPreferredMfaMethod);
|
|
1139
|
+
return this.post(path, { sub, method });
|
|
1140
|
+
}
|
|
1141
|
+
/**
|
|
1142
|
+
* Remove MFA devices for a user
|
|
1143
|
+
*
|
|
1144
|
+
* @param sub - User UUID
|
|
1145
|
+
* @param method - MFA method to remove
|
|
1146
|
+
* @returns Success message
|
|
1147
|
+
* @throws {NAuthClientError} If operation fails
|
|
1148
|
+
*
|
|
1149
|
+
* @example
|
|
1150
|
+
* ```typescript
|
|
1151
|
+
* await client.admin.removeMfaDevices('user-uuid', 'sms');
|
|
1152
|
+
* ```
|
|
1153
|
+
*/
|
|
1154
|
+
async removeMfaDevices(sub, method) {
|
|
1155
|
+
const path = this.buildAdminUrl(this.adminEndpoints.removeMfaDevices);
|
|
1156
|
+
return this.post(path, { sub, method });
|
|
1157
|
+
}
|
|
1158
|
+
/**
|
|
1159
|
+
* Grant or revoke MFA exemption for a user
|
|
1160
|
+
*
|
|
1161
|
+
* @param sub - User UUID
|
|
1162
|
+
* @param exempt - True to exempt from MFA, false to require
|
|
1163
|
+
* @param reason - Optional reason for exemption
|
|
1164
|
+
* @returns Success message
|
|
1165
|
+
* @throws {NAuthClientError} If operation fails
|
|
1166
|
+
*
|
|
1167
|
+
* @example
|
|
1168
|
+
* ```typescript
|
|
1169
|
+
* await client.admin.setMfaExemption('user-uuid', true, 'Service account');
|
|
1170
|
+
* ```
|
|
1171
|
+
*/
|
|
1172
|
+
async setMfaExemption(sub, exempt, reason) {
|
|
1173
|
+
const path = this.buildAdminUrl(this.adminEndpoints.setMfaExemption);
|
|
1174
|
+
return this.post(path, { sub, exempt, reason });
|
|
1175
|
+
}
|
|
1176
|
+
// ============================================================================
|
|
1177
|
+
// Audit
|
|
1178
|
+
// ============================================================================
|
|
1179
|
+
/**
|
|
1180
|
+
* Get audit history for a user
|
|
1181
|
+
*
|
|
1182
|
+
* @param params - Audit history request params
|
|
1183
|
+
* @returns Paginated audit events
|
|
1184
|
+
* @throws {NAuthClientError} If request fails
|
|
1185
|
+
*
|
|
1186
|
+
* @example
|
|
1187
|
+
* ```typescript
|
|
1188
|
+
* const history = await client.admin.getAuditHistory({
|
|
1189
|
+
* sub: 'user-uuid',
|
|
1190
|
+
* page: 1,
|
|
1191
|
+
* limit: 50,
|
|
1192
|
+
* eventType: 'LOGIN_SUCCESS',
|
|
1193
|
+
* });
|
|
1194
|
+
* ```
|
|
1195
|
+
*/
|
|
1196
|
+
async getAuditHistory(params) {
|
|
1197
|
+
const path = this.buildAdminUrl(this.adminEndpoints.getAuditHistory);
|
|
1198
|
+
const queryString = this.buildQueryString(params);
|
|
1199
|
+
return this.get(`${path}${queryString}`);
|
|
1200
|
+
}
|
|
1201
|
+
// ============================================================================
|
|
1202
|
+
// Private Helper Methods
|
|
1203
|
+
// ============================================================================
|
|
1204
|
+
/**
|
|
1205
|
+
* Build admin endpoint URL with path parameter replacement
|
|
1206
|
+
*
|
|
1207
|
+
* Path construction order:
|
|
1208
|
+
* 1. Start with endpoint: '/users/:sub'
|
|
1209
|
+
* 2. Replace params: '/users/uuid-123'
|
|
1210
|
+
* 3. Apply adminPathPrefix: '/admin/users/uuid-123'
|
|
1211
|
+
* 4. Apply authPathPrefix if exists: '/auth/admin/users/uuid-123'
|
|
1212
|
+
* 5. Combine with baseUrl: 'https://api.example.com/auth/admin/users/uuid-123'
|
|
1213
|
+
*
|
|
1214
|
+
* @param endpointPath - Endpoint path (may contain :sub, :provider, etc.)
|
|
1215
|
+
* @param pathParams - Path parameters to replace
|
|
1216
|
+
* @returns Full URL
|
|
1217
|
+
* @private
|
|
1218
|
+
*/
|
|
1219
|
+
buildAdminUrl(endpointPath, pathParams) {
|
|
1220
|
+
let path = endpointPath;
|
|
1221
|
+
if (pathParams) {
|
|
1222
|
+
Object.entries(pathParams).forEach(([key, value]) => {
|
|
1223
|
+
path = path.replace(`:${key}`, encodeURIComponent(value));
|
|
1224
|
+
});
|
|
1225
|
+
}
|
|
1226
|
+
const prefix = this.adminPathPrefix.startsWith("/") ? this.adminPathPrefix : `/${this.adminPathPrefix}`;
|
|
1227
|
+
path = `${prefix}${path.startsWith("/") ? "" : "/"}${path}`;
|
|
1228
|
+
if (this.config.authPathPrefix) {
|
|
1229
|
+
const authPrefix = this.config.authPathPrefix.startsWith("/") ? this.config.authPathPrefix : `/${this.config.authPathPrefix}`;
|
|
1230
|
+
path = `${authPrefix}${path}`;
|
|
1231
|
+
}
|
|
1232
|
+
const normalizedBase = this.config.baseUrl.endsWith("/") ? this.config.baseUrl.slice(0, -1) : this.config.baseUrl;
|
|
1233
|
+
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
|
|
1234
|
+
return `${normalizedBase}${normalizedPath}`;
|
|
1235
|
+
}
|
|
1236
|
+
/**
|
|
1237
|
+
* Build query string from params object
|
|
1238
|
+
*
|
|
1239
|
+
* Handles:
|
|
1240
|
+
* - Simple values (string, number, boolean)
|
|
1241
|
+
* - Arrays (multiple values for same key)
|
|
1242
|
+
* - Nested objects (e.g., createdAt[operator], createdAt[value])
|
|
1243
|
+
* - Dates (converted to ISO string)
|
|
1244
|
+
*
|
|
1245
|
+
* @param params - Query parameters
|
|
1246
|
+
* @returns Query string (e.g., '?page=1&limit=20')
|
|
1247
|
+
* @private
|
|
1248
|
+
*/
|
|
1249
|
+
buildQueryString(params) {
|
|
1250
|
+
const searchParams = new URLSearchParams();
|
|
1251
|
+
for (const [key, rawValue] of Object.entries(params)) {
|
|
1252
|
+
if (rawValue === void 0 || rawValue === null) {
|
|
1253
|
+
continue;
|
|
1254
|
+
}
|
|
1255
|
+
if (Array.isArray(rawValue)) {
|
|
1256
|
+
for (const item of rawValue) {
|
|
1257
|
+
searchParams.append(key, String(item));
|
|
1258
|
+
}
|
|
1259
|
+
continue;
|
|
1260
|
+
}
|
|
1261
|
+
if (typeof rawValue === "object" && rawValue !== null && !(rawValue instanceof Date)) {
|
|
1262
|
+
const nestedObj = rawValue;
|
|
1263
|
+
for (const [nestedKey, nestedValue] of Object.entries(nestedObj)) {
|
|
1264
|
+
const nestedParamKey = `${key}[${nestedKey}]`;
|
|
1265
|
+
const valueToAppend = nestedValue instanceof Date ? nestedValue.toISOString() : String(nestedValue);
|
|
1266
|
+
searchParams.append(nestedParamKey, valueToAppend);
|
|
1267
|
+
}
|
|
1268
|
+
continue;
|
|
1269
|
+
}
|
|
1270
|
+
if (rawValue instanceof Date) {
|
|
1271
|
+
searchParams.append(key, rawValue.toISOString());
|
|
1272
|
+
continue;
|
|
1273
|
+
}
|
|
1274
|
+
searchParams.append(key, String(rawValue));
|
|
1275
|
+
}
|
|
1276
|
+
const query = searchParams.toString();
|
|
1277
|
+
return query ? `?${query}` : "";
|
|
1278
|
+
}
|
|
1279
|
+
/**
|
|
1280
|
+
* Build request headers for authentication
|
|
1281
|
+
*
|
|
1282
|
+
* @param auth - Whether to include authentication headers
|
|
1283
|
+
* @param method - HTTP method
|
|
1284
|
+
* @returns Headers object
|
|
1285
|
+
* @private
|
|
1286
|
+
*/
|
|
1287
|
+
async buildHeaders(auth, method = "GET") {
|
|
1288
|
+
const headers = {
|
|
1289
|
+
...this.config.headers,
|
|
1290
|
+
...this.adminHeaders
|
|
1291
|
+
};
|
|
1292
|
+
if (method !== "GET") {
|
|
1293
|
+
headers["Content-Type"] = "application/json";
|
|
1294
|
+
}
|
|
1295
|
+
if (auth && this.config.tokenDelivery === "json") {
|
|
1296
|
+
try {
|
|
1297
|
+
const ACCESS_TOKEN_KEY2 = "nauth_access_token";
|
|
1298
|
+
const token = await this.config.storage.getItem(ACCESS_TOKEN_KEY2);
|
|
1299
|
+
if (token) {
|
|
1300
|
+
headers["Authorization"] = `Bearer ${token}`;
|
|
1301
|
+
}
|
|
1302
|
+
} catch {
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
if (this.config.tokenDelivery === "json") {
|
|
1306
|
+
try {
|
|
1307
|
+
const deviceToken = await this.config.storage.getItem(this.config.deviceTrust.storageKey);
|
|
1308
|
+
if (deviceToken) {
|
|
1309
|
+
headers[this.config.deviceTrust.headerName] = deviceToken;
|
|
1310
|
+
}
|
|
1311
|
+
} catch {
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
const mutatingMethods = [
|
|
1315
|
+
"POST",
|
|
1316
|
+
"PUT",
|
|
1317
|
+
"PATCH",
|
|
1318
|
+
"DELETE"
|
|
1319
|
+
];
|
|
1320
|
+
if (this.config.tokenDelivery === "cookies" && hasWindow() && mutatingMethods.includes(method)) {
|
|
1321
|
+
const csrfToken = this.getCsrfToken();
|
|
1322
|
+
if (csrfToken) {
|
|
1323
|
+
headers[this.config.csrf.headerName] = csrfToken;
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
return headers;
|
|
1327
|
+
}
|
|
1328
|
+
/**
|
|
1329
|
+
* Get CSRF token from cookie (browser only)
|
|
1330
|
+
*
|
|
1331
|
+
* @returns CSRF token or null
|
|
1332
|
+
* @private
|
|
1333
|
+
*/
|
|
1334
|
+
getCsrfToken() {
|
|
1335
|
+
if (!hasWindow() || typeof document === "undefined") return null;
|
|
1336
|
+
const match = document.cookie.match(
|
|
1337
|
+
new RegExp(`(^| )${this.config.csrf.cookieName}=([^;]+)`)
|
|
1338
|
+
);
|
|
1339
|
+
return match ? decodeURIComponent(match[2]) : null;
|
|
1340
|
+
}
|
|
1341
|
+
/**
|
|
1342
|
+
* Execute GET request
|
|
1343
|
+
*
|
|
1344
|
+
* @param path - Full URL path
|
|
1345
|
+
* @returns Response data
|
|
1346
|
+
* @private
|
|
1347
|
+
*/
|
|
1348
|
+
async get(path) {
|
|
1349
|
+
const headers = await this.buildHeaders(true, "GET");
|
|
1350
|
+
const credentials = this.config.tokenDelivery === "cookies" ? "include" : "omit";
|
|
1351
|
+
try {
|
|
1352
|
+
const response = await this.config.httpAdapter.request({
|
|
1353
|
+
method: "GET",
|
|
1354
|
+
url: path,
|
|
1355
|
+
headers,
|
|
1356
|
+
credentials
|
|
1357
|
+
});
|
|
1358
|
+
return response.data;
|
|
1359
|
+
} catch (error) {
|
|
1360
|
+
throw this.handleError(error);
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
/**
|
|
1364
|
+
* Execute POST request
|
|
1365
|
+
*
|
|
1366
|
+
* @param path - Full URL path
|
|
1367
|
+
* @param body - Request body
|
|
1368
|
+
* @returns Response data
|
|
1369
|
+
* @private
|
|
1370
|
+
*/
|
|
1371
|
+
async post(path, body) {
|
|
1372
|
+
const headers = await this.buildHeaders(true, "POST");
|
|
1373
|
+
const credentials = this.config.tokenDelivery === "cookies" ? "include" : "omit";
|
|
1374
|
+
try {
|
|
1375
|
+
const response = await this.config.httpAdapter.request({
|
|
1376
|
+
method: "POST",
|
|
1377
|
+
url: path,
|
|
1378
|
+
headers,
|
|
1379
|
+
body,
|
|
1380
|
+
credentials
|
|
1381
|
+
});
|
|
1382
|
+
return response.data;
|
|
1383
|
+
} catch (error) {
|
|
1384
|
+
throw this.handleError(error);
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
/**
|
|
1388
|
+
* Execute DELETE request
|
|
1389
|
+
*
|
|
1390
|
+
* @param path - Full URL path
|
|
1391
|
+
* @returns Response data
|
|
1392
|
+
* @private
|
|
1393
|
+
*/
|
|
1394
|
+
async delete(path) {
|
|
1395
|
+
const headers = await this.buildHeaders(true, "DELETE");
|
|
1396
|
+
const credentials = this.config.tokenDelivery === "cookies" ? "include" : "omit";
|
|
1397
|
+
try {
|
|
1398
|
+
const response = await this.config.httpAdapter.request({
|
|
1399
|
+
method: "DELETE",
|
|
1400
|
+
url: path,
|
|
1401
|
+
headers,
|
|
1402
|
+
credentials
|
|
1403
|
+
});
|
|
1404
|
+
return response.data;
|
|
1405
|
+
} catch (error) {
|
|
1406
|
+
throw this.handleError(error);
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
/**
|
|
1410
|
+
* Handle HTTP errors and convert to NAuthClientError
|
|
1411
|
+
*
|
|
1412
|
+
* @param error - Error from HTTP adapter
|
|
1413
|
+
* @returns NAuthClientError
|
|
1414
|
+
* @private
|
|
1415
|
+
*/
|
|
1416
|
+
handleError(error) {
|
|
1417
|
+
if (error instanceof NAuthClientError) {
|
|
1418
|
+
return error;
|
|
1419
|
+
}
|
|
1420
|
+
if (error && typeof error === "object" && "response" in error) {
|
|
1421
|
+
const httpError = error;
|
|
1422
|
+
const status = httpError.response?.status ?? 500;
|
|
1423
|
+
const errorData = typeof httpError.response?.data === "object" && httpError.response.data !== null ? httpError.response.data : {};
|
|
1424
|
+
const code = typeof errorData["code"] === "string" ? errorData["code"] : "INTERNAL_ERROR" /* INTERNAL_ERROR */;
|
|
1425
|
+
const message = typeof errorData["message"] === "string" ? errorData["message"] : `Request failed with status ${status}`;
|
|
1426
|
+
return new NAuthClientError(code, message, {
|
|
1427
|
+
statusCode: status,
|
|
1428
|
+
details: errorData
|
|
1429
|
+
});
|
|
1430
|
+
}
|
|
1431
|
+
return new NAuthClientError(
|
|
1432
|
+
"INTERNAL_ERROR" /* INTERNAL_ERROR */,
|
|
1433
|
+
error instanceof Error ? error.message : "Unknown error"
|
|
1434
|
+
);
|
|
1435
|
+
}
|
|
1436
|
+
};
|
|
1437
|
+
|
|
752
1438
|
// src/core/client.ts
|
|
753
1439
|
var USER_KEY2 = "nauth_user";
|
|
754
1440
|
var CHALLENGE_KEY2 = "nauth_challenge_session";
|
|
755
1441
|
var OAUTH_STATE_KEY2 = "nauth_oauth_state";
|
|
756
|
-
var
|
|
1442
|
+
var hasWindow2 = () => typeof globalThis !== "undefined" && typeof globalThis.window !== "undefined";
|
|
1443
|
+
var getOauthStorage = (mainStorage) => {
|
|
1444
|
+
if (hasWindow2() && typeof window.sessionStorage !== "undefined") {
|
|
1445
|
+
return new BrowserStorage(window.sessionStorage);
|
|
1446
|
+
}
|
|
1447
|
+
return mainStorage;
|
|
1448
|
+
};
|
|
757
1449
|
var defaultStorage = () => {
|
|
758
|
-
if (
|
|
1450
|
+
if (hasWindow2() && typeof window.localStorage !== "undefined") {
|
|
759
1451
|
return new BrowserStorage();
|
|
760
1452
|
}
|
|
761
1453
|
return new InMemoryStorage();
|
|
@@ -771,7 +1463,34 @@ var NAuthClient = class {
|
|
|
771
1463
|
__publicField(this, "tokenManager");
|
|
772
1464
|
__publicField(this, "eventEmitter");
|
|
773
1465
|
__publicField(this, "challengeRouter");
|
|
1466
|
+
__publicField(this, "oauthStorage");
|
|
774
1467
|
__publicField(this, "currentUser", null);
|
|
1468
|
+
/**
|
|
1469
|
+
* Admin operations (available if admin config provided).
|
|
1470
|
+
*
|
|
1471
|
+
* Provides admin-level user management methods:
|
|
1472
|
+
* - User CRUD operations
|
|
1473
|
+
* - Password management
|
|
1474
|
+
* - Session management
|
|
1475
|
+
* - MFA management
|
|
1476
|
+
* - Audit history
|
|
1477
|
+
*
|
|
1478
|
+
* @example
|
|
1479
|
+
* ```typescript
|
|
1480
|
+
* const client = new NAuthClient({
|
|
1481
|
+
* baseUrl: 'https://api.example.com/auth',
|
|
1482
|
+
* tokenDelivery: 'cookies',
|
|
1483
|
+
* admin: {
|
|
1484
|
+
* pathPrefix: '/admin',
|
|
1485
|
+
* },
|
|
1486
|
+
* });
|
|
1487
|
+
*
|
|
1488
|
+
* // Use admin operations
|
|
1489
|
+
* const users = await client.admin.getUsers({ page: 1 });
|
|
1490
|
+
* await client.admin.deleteUser('user-uuid');
|
|
1491
|
+
* ```
|
|
1492
|
+
*/
|
|
1493
|
+
__publicField(this, "admin");
|
|
775
1494
|
/**
|
|
776
1495
|
* Handle cross-tab storage updates.
|
|
777
1496
|
*/
|
|
@@ -789,8 +1508,12 @@ var NAuthClient = class {
|
|
|
789
1508
|
this.config = resolveConfig({ ...userConfig, storage }, defaultAdapter);
|
|
790
1509
|
this.tokenManager = new TokenManager(storage);
|
|
791
1510
|
this.eventEmitter = new EventEmitter();
|
|
792
|
-
this.
|
|
793
|
-
|
|
1511
|
+
this.oauthStorage = getOauthStorage(storage);
|
|
1512
|
+
this.challengeRouter = new ChallengeRouter(this.config, this.oauthStorage);
|
|
1513
|
+
if (this.config.admin) {
|
|
1514
|
+
this.admin = new AdminOperations(this.config);
|
|
1515
|
+
}
|
|
1516
|
+
if (hasWindow2()) {
|
|
794
1517
|
window.addEventListener("storage", this.handleStorageEvent);
|
|
795
1518
|
}
|
|
796
1519
|
}
|
|
@@ -798,7 +1521,7 @@ var NAuthClient = class {
|
|
|
798
1521
|
* Clean up resources.
|
|
799
1522
|
*/
|
|
800
1523
|
dispose() {
|
|
801
|
-
if (
|
|
1524
|
+
if (hasWindow2()) {
|
|
802
1525
|
window.removeEventListener("storage", this.handleStorageEvent);
|
|
803
1526
|
}
|
|
804
1527
|
}
|
|
@@ -1246,11 +1969,12 @@ var NAuthClient = class {
|
|
|
1246
1969
|
*/
|
|
1247
1970
|
async loginWithSocial(provider, options) {
|
|
1248
1971
|
this.eventEmitter.emit({ type: "oauth:started", data: { provider }, timestamp: Date.now() });
|
|
1249
|
-
if (
|
|
1972
|
+
if (hasWindow2()) {
|
|
1250
1973
|
const startPath = this.config.endpoints.socialRedirectStart.replace(":provider", provider);
|
|
1251
1974
|
const fullUrl = this.buildUrl(startPath);
|
|
1252
1975
|
const startUrl = new URL(fullUrl);
|
|
1253
|
-
const
|
|
1976
|
+
const redirects = this.config.redirects;
|
|
1977
|
+
const returnTo = options?.returnTo ?? redirects?.loginSuccess ?? redirects?.success ?? "/";
|
|
1254
1978
|
startUrl.searchParams.set("returnTo", returnTo);
|
|
1255
1979
|
if (options?.action === "link") {
|
|
1256
1980
|
startUrl.searchParams.set("action", "link");
|
|
@@ -1277,7 +2001,11 @@ var NAuthClient = class {
|
|
|
1277
2001
|
}
|
|
1278
2002
|
const result = await this.post(this.config.endpoints.socialExchange, { exchangeToken: token });
|
|
1279
2003
|
await this.handleAuthResponse(result);
|
|
1280
|
-
await this.
|
|
2004
|
+
const appState = await this.oauthStorage.getItem(OAUTH_STATE_KEY2);
|
|
2005
|
+
await this.challengeRouter.handleAuthResponse(result, {
|
|
2006
|
+
source: "social",
|
|
2007
|
+
appState: appState ?? void 0
|
|
2008
|
+
});
|
|
1281
2009
|
return result;
|
|
1282
2010
|
}
|
|
1283
2011
|
/**
|
|
@@ -1563,7 +2291,7 @@ var NAuthClient = class {
|
|
|
1563
2291
|
}
|
|
1564
2292
|
}
|
|
1565
2293
|
const mutatingMethods = ["POST", "PUT", "PATCH", "DELETE"];
|
|
1566
|
-
if (this.config.tokenDelivery === "cookies" &&
|
|
2294
|
+
if (this.config.tokenDelivery === "cookies" && hasWindow2() && mutatingMethods.includes(method)) {
|
|
1567
2295
|
const csrfToken = this.getCsrfToken();
|
|
1568
2296
|
if (csrfToken) {
|
|
1569
2297
|
headers[this.config.csrf.headerName] = csrfToken;
|
|
@@ -1576,7 +2304,7 @@ var NAuthClient = class {
|
|
|
1576
2304
|
* @private
|
|
1577
2305
|
*/
|
|
1578
2306
|
getCsrfToken() {
|
|
1579
|
-
if (!
|
|
2307
|
+
if (!hasWindow2() || typeof document === "undefined") return null;
|
|
1580
2308
|
const match = document.cookie.match(new RegExp(`(^| )${this.config.csrf.cookieName}=([^;]+)`));
|
|
1581
2309
|
return match ? decodeURIComponent(match[2]) : null;
|
|
1582
2310
|
}
|
|
@@ -1668,6 +2396,8 @@ var NAuthClient = class {
|
|
|
1668
2396
|
* when appState is present in the callback URL. The stored state can
|
|
1669
2397
|
* be retrieved using getLastOauthState().
|
|
1670
2398
|
*
|
|
2399
|
+
* Stores in sessionStorage (ephemeral) for better security.
|
|
2400
|
+
*
|
|
1671
2401
|
* @param appState - OAuth appState value from callback URL
|
|
1672
2402
|
*
|
|
1673
2403
|
* @example
|
|
@@ -1677,7 +2407,7 @@ var NAuthClient = class {
|
|
|
1677
2407
|
*/
|
|
1678
2408
|
async storeOauthState(appState) {
|
|
1679
2409
|
if (appState && appState.trim() !== "") {
|
|
1680
|
-
await this.
|
|
2410
|
+
await this.oauthStorage.setItem(OAUTH_STATE_KEY2, appState);
|
|
1681
2411
|
}
|
|
1682
2412
|
}
|
|
1683
2413
|
/**
|
|
@@ -1688,6 +2418,7 @@ var NAuthClient = class {
|
|
|
1688
2418
|
* applying invite codes, or tracking referral information.
|
|
1689
2419
|
*
|
|
1690
2420
|
* The state is automatically cleared after retrieval to prevent reuse.
|
|
2421
|
+
* Stored in sessionStorage (ephemeral) for better security.
|
|
1691
2422
|
*
|
|
1692
2423
|
* @returns The stored appState, or null if none exists
|
|
1693
2424
|
*
|
|
@@ -1701,9 +2432,9 @@ var NAuthClient = class {
|
|
|
1701
2432
|
* ```
|
|
1702
2433
|
*/
|
|
1703
2434
|
async getLastOauthState() {
|
|
1704
|
-
const stored = await this.
|
|
2435
|
+
const stored = await this.oauthStorage.getItem(OAUTH_STATE_KEY2);
|
|
1705
2436
|
if (stored) {
|
|
1706
|
-
await this.
|
|
2437
|
+
await this.oauthStorage.removeItem(OAUTH_STATE_KEY2);
|
|
1707
2438
|
return stored;
|
|
1708
2439
|
}
|
|
1709
2440
|
return null;
|
|
@@ -1753,6 +2484,7 @@ function isOTPChallenge(challenge) {
|
|
|
1753
2484
|
}
|
|
1754
2485
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1755
2486
|
0 && (module.exports = {
|
|
2487
|
+
AdminOperations,
|
|
1756
2488
|
AuthAuditEventType,
|
|
1757
2489
|
AuthChallenge,
|
|
1758
2490
|
BrowserStorage,
|
|
@@ -1763,6 +2495,7 @@ function isOTPChallenge(challenge) {
|
|
|
1763
2495
|
NAuthClient,
|
|
1764
2496
|
NAuthClientError,
|
|
1765
2497
|
NAuthErrorCode,
|
|
2498
|
+
defaultAdminEndpoints,
|
|
1766
2499
|
defaultEndpoints,
|
|
1767
2500
|
getChallengeInstructions,
|
|
1768
2501
|
getMFAMethod,
|