@nauth-toolkit/client 0.1.18 → 0.1.22
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/angular/index.cjs +381 -166
- package/dist/angular/index.cjs.map +1 -1
- package/dist/angular/index.d.mts +331 -127
- package/dist/angular/index.d.ts +331 -127
- package/dist/angular/index.mjs +380 -165
- package/dist/angular/index.mjs.map +1 -1
- package/dist/index.cjs +60 -110
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +65 -72
- package/dist/index.d.ts +65 -72
- package/dist/index.mjs +60 -110
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
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
|
|
507
|
-
const message = typeof errorData["message"] === "string" ? errorData
|
|
508
|
-
const timestamp = typeof errorData["timestamp"] === "string" ? errorData
|
|
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,
|
|
@@ -817,7 +823,9 @@ var NAuthClient = class {
|
|
|
817
823
|
*/
|
|
818
824
|
async confirmForgotPassword(identifier, code, newPassword) {
|
|
819
825
|
const payload = { identifier, code, newPassword };
|
|
820
|
-
|
|
826
|
+
const result = await this.post(this.config.endpoints.confirmForgotPassword, payload);
|
|
827
|
+
await this.clearAuthState(false);
|
|
828
|
+
return result;
|
|
821
829
|
}
|
|
822
830
|
/**
|
|
823
831
|
* Request password change (must change on next login).
|
|
@@ -927,126 +935,57 @@ var NAuthClient = class {
|
|
|
927
935
|
// Social Authentication
|
|
928
936
|
// ============================================================================
|
|
929
937
|
/**
|
|
930
|
-
* Start social OAuth flow
|
|
938
|
+
* Start redirect-first social OAuth flow (web).
|
|
931
939
|
*
|
|
932
|
-
*
|
|
933
|
-
*
|
|
940
|
+
* This performs a browser navigation to:
|
|
941
|
+
* `GET {baseUrl}/social/:provider/redirect?returnTo=...&appState=...`
|
|
942
|
+
*
|
|
943
|
+
* The backend:
|
|
944
|
+
* - generates and stores CSRF state (cluster-safe)
|
|
945
|
+
* - redirects the user to the provider
|
|
946
|
+
* - completes OAuth on callback and sets cookies (or issues an exchange token)
|
|
947
|
+
* - redirects back to `returnTo` with `appState` (and `exchangeToken` for json/hybrid)
|
|
934
948
|
*
|
|
935
949
|
* @param provider - OAuth provider ('google', 'apple', 'facebook')
|
|
936
|
-
* @param options - Optional
|
|
950
|
+
* @param options - Optional redirect options
|
|
937
951
|
*
|
|
938
952
|
* @example
|
|
939
953
|
* ```typescript
|
|
940
|
-
*
|
|
941
|
-
* await client.loginWithSocial('google');
|
|
942
|
-
*
|
|
943
|
-
* // With custom redirect URI
|
|
944
|
-
* await client.loginWithSocial('apple', {
|
|
945
|
-
* redirectUri: 'https://example.com/auth/callback'
|
|
946
|
-
* });
|
|
954
|
+
* await client.loginWithSocial('google', { returnTo: '/auth/callback', appState: '12345' });
|
|
947
955
|
* ```
|
|
948
956
|
*/
|
|
949
|
-
async loginWithSocial(provider,
|
|
957
|
+
async loginWithSocial(provider, options) {
|
|
950
958
|
this.eventEmitter.emit({ type: "oauth:started", data: { provider }, timestamp: Date.now() });
|
|
951
|
-
const { url } = await this.getSocialAuthUrl({ provider });
|
|
952
959
|
if (hasWindow()) {
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
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;
|
|
960
|
+
const startPath = this.config.endpoints.socialRedirectStart.replace(":provider", provider);
|
|
961
|
+
const base = this.config.baseUrl.replace(/\/$/, "");
|
|
962
|
+
const startUrl = new URL(`${base}${startPath}`);
|
|
963
|
+
const returnTo = options?.returnTo ?? this.config.redirects?.success ?? "/";
|
|
964
|
+
startUrl.searchParams.set("returnTo", returnTo);
|
|
965
|
+
if (options?.action === "link") {
|
|
966
|
+
startUrl.searchParams.set("action", "link");
|
|
1012
967
|
}
|
|
1013
|
-
if (
|
|
1014
|
-
|
|
968
|
+
if (typeof options?.appState === "string" && options.appState.trim() !== "") {
|
|
969
|
+
startUrl.searchParams.set("appState", options.appState);
|
|
1015
970
|
}
|
|
1016
|
-
|
|
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() });
|
|
1025
|
-
}
|
|
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
|
-
*
|
|
975
|
+
* Exchange an `exchangeToken` (from redirect callback URL) into an AuthResponse.
|
|
1039
976
|
*
|
|
1040
|
-
*
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
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
|
|
1049
|
-
const
|
|
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
|
}
|
|
@@ -1235,7 +1174,9 @@ var NAuthClient = class {
|
|
|
1235
1174
|
await this.setDeviceToken(response.deviceToken);
|
|
1236
1175
|
}
|
|
1237
1176
|
if (response.user) {
|
|
1238
|
-
|
|
1177
|
+
const user = response.user;
|
|
1178
|
+
user.sessionAuthMethod = response.authMethod ?? null;
|
|
1179
|
+
await this.setUser(user);
|
|
1239
1180
|
}
|
|
1240
1181
|
await this.clearChallenge();
|
|
1241
1182
|
}
|
|
@@ -1307,6 +1248,15 @@ var NAuthClient = class {
|
|
|
1307
1248
|
headers["Authorization"] = `Bearer ${accessToken}`;
|
|
1308
1249
|
}
|
|
1309
1250
|
}
|
|
1251
|
+
if (this.config.tokenDelivery === "json") {
|
|
1252
|
+
try {
|
|
1253
|
+
const deviceToken = await this.config.storage.getItem(this.config.deviceTrust.storageKey);
|
|
1254
|
+
if (deviceToken) {
|
|
1255
|
+
headers[this.config.deviceTrust.headerName] = deviceToken;
|
|
1256
|
+
}
|
|
1257
|
+
} catch {
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1310
1260
|
if (this.config.tokenDelivery === "cookies" && hasWindow()) {
|
|
1311
1261
|
const csrfToken = this.getCsrfToken();
|
|
1312
1262
|
if (csrfToken) {
|