@poly-x/core 0.1.0-alpha.1 → 0.1.0-alpha.11
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 +228 -4
- package/dist/index.d.cts +173 -2
- package/dist/index.d.mts +173 -2
- package/dist/index.mjs +227 -5
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -193,7 +193,16 @@ function resolveConfig(input) {
|
|
|
193
193
|
* against poly-auth/src/api/constants/auth-responses.ts (2026-07-06). Unknown
|
|
194
194
|
* shapes become a typed UNRECOGNIZED_RESPONSE — never a guess.
|
|
195
195
|
*/
|
|
196
|
-
|
|
196
|
+
/**
|
|
197
|
+
* Terminal: the session is gone and no retry brings it back. The platform revokes every session
|
|
198
|
+
* for a user on permission change, suspension, and password reset — `SESSION_REVOKED` for the
|
|
199
|
+
* revocation itself, `SESSION_MISMATCH` when the presented token no longer matches its session.
|
|
200
|
+
*/
|
|
201
|
+
const REVOKED_CODES = /* @__PURE__ */ new Set([
|
|
202
|
+
"REFRESH_TOKEN_REUSE",
|
|
203
|
+
"SESSION_REVOKED",
|
|
204
|
+
"SESSION_MISMATCH"
|
|
205
|
+
]);
|
|
197
206
|
const EXPIRED_CODES = /* @__PURE__ */ new Set([
|
|
198
207
|
"SESSION_EXPIRED",
|
|
199
208
|
"REFRESH_TOKEN_REQUIRED",
|
|
@@ -708,8 +717,41 @@ const ENDPOINTS = {
|
|
|
708
717
|
authorize: "/v1/idp/authorize",
|
|
709
718
|
token: "/v1/idp/token",
|
|
710
719
|
refresh: "/v1/auth/refresh-token",
|
|
720
|
+
/**
|
|
721
|
+
* Ecosystem (SSO) logout. Resolves the session from the *browser's* ecosystem cookie,
|
|
722
|
+
* so it only revokes when called from a context that carries it — a top-level hop.
|
|
723
|
+
* A server-to-server call reaches it without the cookie and revokes nothing (it still
|
|
724
|
+
* answers 200), which is why BFF sign-out must use the bearer-authenticated pair below.
|
|
725
|
+
*/
|
|
711
726
|
logout: "/v1/idp/logout",
|
|
712
|
-
|
|
727
|
+
/**
|
|
728
|
+
* Bearer-authenticated revocation: the access token identifies the session, so these
|
|
729
|
+
* work from any context that holds one — notably the BFF, where the token is sealed in
|
|
730
|
+
* the session cookie and no browser cookie reaches poly-auth. `logout` revokes this
|
|
731
|
+
* session; `logoutAll` revokes every session for the user.
|
|
732
|
+
*/
|
|
733
|
+
authLogout: "/v1/auth/logout",
|
|
734
|
+
authLogoutAll: "/v1/auth/logout-all",
|
|
735
|
+
orgResolve: "/v1/idp/org-resolve",
|
|
736
|
+
/**
|
|
737
|
+
* Completes the admin temp-password flow: the user signed in with a temporary
|
|
738
|
+
* password, poly-auth answered `403 {userPasswordSet, userId}`, and this sets
|
|
739
|
+
* the real one. Only succeeds while the account is flagged `mustChangePassword`.
|
|
740
|
+
*/
|
|
741
|
+
setPassword: "/v1/auth/set-password",
|
|
742
|
+
/**
|
|
743
|
+
* Server-mediated self-service reset (poly-x v5 / F040). `forgotPassword` starts it
|
|
744
|
+
* — always a uniform 202, the token is emailed to the mailbox, never returned.
|
|
745
|
+
* `resetPassword` redeems that opaque token single-use and sets the new password.
|
|
746
|
+
*/
|
|
747
|
+
forgotPassword: "/v1/idp/forgot-password",
|
|
748
|
+
resetPassword: "/v1/idp/reset-password",
|
|
749
|
+
/**
|
|
750
|
+
* Public, pk_-scoped login-page branding (F037 / FR-BRND). GET with `client_id`
|
|
751
|
+
* (+ optional `organizationCode`) → the bounded `{ tokens }` set. Never 5xx's /
|
|
752
|
+
* never blocks login; the SDK falls back to its built-in defaults on any failure.
|
|
753
|
+
*/
|
|
754
|
+
branding: "/v1/idp/branding"
|
|
713
755
|
};
|
|
714
756
|
//#endregion
|
|
715
757
|
//#region src/auth/outcomes.ts
|
|
@@ -747,6 +789,33 @@ function parseAuthorizeOutcome(status, body) {
|
|
|
747
789
|
if (statusField === "login_required" || b.code === "LOGIN_REQUIRED") return { type: "login_required" };
|
|
748
790
|
return { type: "login_required" };
|
|
749
791
|
}
|
|
792
|
+
function parsePasswordOutcome(status, body) {
|
|
793
|
+
const b = asRecord$1(body);
|
|
794
|
+
const statusField = typeof b.status === "string" ? b.status : void 0;
|
|
795
|
+
if (statusField === "authorized" && typeof b.code === "string") return {
|
|
796
|
+
type: "authorized",
|
|
797
|
+
code: b.code,
|
|
798
|
+
state: typeof b.state === "string" ? b.state : "",
|
|
799
|
+
redirectUri: typeof b.redirectUri === "string" ? b.redirectUri : void 0
|
|
800
|
+
};
|
|
801
|
+
if (statusField === "select_tenant") return {
|
|
802
|
+
type: "select_tenant",
|
|
803
|
+
state: typeof b.state === "string" ? b.state : "",
|
|
804
|
+
tenants: parseTenants(b.tenants)
|
|
805
|
+
};
|
|
806
|
+
if (status === 403 && b.userPasswordSet === true && typeof b.userId === "string") return {
|
|
807
|
+
type: "password_change_required",
|
|
808
|
+
userId: b.userId,
|
|
809
|
+
ticket: typeof b.ticket === "string" ? b.ticket : void 0
|
|
810
|
+
};
|
|
811
|
+
if (status === 403) return { type: "no_access" };
|
|
812
|
+
return { type: "invalid_credentials" };
|
|
813
|
+
}
|
|
814
|
+
function parseResetOutcome(status, body) {
|
|
815
|
+
if (status === 200) return "ok";
|
|
816
|
+
if (asRecord$1(body).status === "weak_password") return "weak_password";
|
|
817
|
+
return "invalid_or_expired";
|
|
818
|
+
}
|
|
750
819
|
//#endregion
|
|
751
820
|
//#region src/auth/normalize.ts
|
|
752
821
|
const FALLBACK_TTL_MS = 5 * 6e4;
|
|
@@ -772,7 +841,35 @@ function toTokenSet(accessToken, refreshToken, now) {
|
|
|
772
841
|
function asRecord(value) {
|
|
773
842
|
return value && typeof value === "object" ? value : {};
|
|
774
843
|
}
|
|
775
|
-
/**
|
|
844
|
+
/**
|
|
845
|
+
* Display-claim caps (ADR-0006). These values are user-controlled and unbounded in the
|
|
846
|
+
* platform's schema, and they ride in a sealed ~4KB cookie whose failure mode is the browser
|
|
847
|
+
* silently dropping it. Cap at the boundary rather than trusting the input.
|
|
848
|
+
*/
|
|
849
|
+
const MAX_DISPLAY_NAME = 128;
|
|
850
|
+
const MAX_AVATAR_URL = 512;
|
|
851
|
+
/** A non-blank string, trimmed and capped — otherwise undefined. The shim never guesses. */
|
|
852
|
+
function displayString(value, max) {
|
|
853
|
+
if (typeof value !== "string") return void 0;
|
|
854
|
+
const trimmed = value.trim();
|
|
855
|
+
if (trimmed.length === 0) return void 0;
|
|
856
|
+
return trimmed.slice(0, max);
|
|
857
|
+
}
|
|
858
|
+
/**
|
|
859
|
+
* The best human label the token response can give us: full name, else username, else email.
|
|
860
|
+
* Each part is checked independently so a user with only one of them never renders "undefined".
|
|
861
|
+
*/
|
|
862
|
+
function deriveDisplayName(user) {
|
|
863
|
+
return displayString([displayString(user.firstName, MAX_DISPLAY_NAME), displayString(user.lastName, MAX_DISPLAY_NAME)].filter(Boolean).join(" "), MAX_DISPLAY_NAME) ?? displayString(user.username, MAX_DISPLAY_NAME) ?? displayString(user.email, MAX_DISPLAY_NAME);
|
|
864
|
+
}
|
|
865
|
+
/**
|
|
866
|
+
* `POST /idp/token` → StoredSession. Claims carry identity plus the bounded display subset
|
|
867
|
+
* (ADR-0006); AUTHZ populates roles/permissions later.
|
|
868
|
+
*
|
|
869
|
+
* `organizationId` comes from the exchange's `tenantId`, never from the user document: a
|
|
870
|
+
* cross-membership login resolves a tenant that is deliberately *not* the user's home
|
|
871
|
+
* organization, and reading it off the user would silently sign them into the wrong one.
|
|
872
|
+
*/
|
|
776
873
|
function normalizeExchange(body, now) {
|
|
777
874
|
const data = asRecord(asRecord(body).data);
|
|
778
875
|
const user = asRecord(data.user);
|
|
@@ -782,7 +879,10 @@ function normalizeExchange(body, now) {
|
|
|
782
879
|
userId,
|
|
783
880
|
organizationId: data.tenantId != null ? String(data.tenantId) : void 0,
|
|
784
881
|
roles: [],
|
|
785
|
-
permissions: []
|
|
882
|
+
permissions: [],
|
|
883
|
+
displayName: deriveDisplayName(user),
|
|
884
|
+
email: displayString(user.email, MAX_DISPLAY_NAME),
|
|
885
|
+
avatarUrl: displayString(user.profilePicture, MAX_AVATAR_URL)
|
|
786
886
|
};
|
|
787
887
|
return {
|
|
788
888
|
tokens: toTokenSet(String(data.accessToken ?? ""), stringOrUndefined(data.refreshToken), now),
|
|
@@ -859,6 +959,86 @@ var AuthClient = class {
|
|
|
859
959
|
if (response.status >= 500) throw this.toError(response);
|
|
860
960
|
return parseAuthorizeOutcome(response.status, response.body);
|
|
861
961
|
}
|
|
962
|
+
/**
|
|
963
|
+
* The embedded `<SignIn/>` credential flow (D2b): POST `/idp/authorize` with
|
|
964
|
+
* email/password + PKCE. On `authorized` the caller exchanges the returned
|
|
965
|
+
* `code` (server-side, via the BFF) for a session — no redirect to a hosted
|
|
966
|
+
* login page. Creds live only for the duration of this request; the SDK never
|
|
967
|
+
* stores them. 5xx surfaces as a typed error; every other status is a typed
|
|
968
|
+
* outcome the UI branches on.
|
|
969
|
+
*/
|
|
970
|
+
async authorizeWithPassword(params) {
|
|
971
|
+
const response = await this.transport.request({
|
|
972
|
+
method: "POST",
|
|
973
|
+
url: this.config.baseUrl + ENDPOINTS.authorize,
|
|
974
|
+
body: {
|
|
975
|
+
response_type: "code",
|
|
976
|
+
client_id: this.config.key.raw,
|
|
977
|
+
redirect_uri: params.redirectUri,
|
|
978
|
+
state: params.state,
|
|
979
|
+
code_challenge: params.challenge,
|
|
980
|
+
code_challenge_method: "S256",
|
|
981
|
+
email: params.email,
|
|
982
|
+
password: params.password,
|
|
983
|
+
...params.organizationCode ? { organizationCode: params.organizationCode } : {},
|
|
984
|
+
...params.tenantId ? { tenantId: params.tenantId } : {}
|
|
985
|
+
}
|
|
986
|
+
});
|
|
987
|
+
if (response.status >= 500) throw this.toError(response);
|
|
988
|
+
return parsePasswordOutcome(response.status, response.body);
|
|
989
|
+
}
|
|
990
|
+
/**
|
|
991
|
+
* Set the real password for a user the platform has flagged `mustChangePassword`
|
|
992
|
+
* (the admin temp-password flow). Call this only after `authorizeWithPassword`
|
|
993
|
+
* has returned `password_change_required` for that same user — that response IS
|
|
994
|
+
* the proof the caller knows the temporary password.
|
|
995
|
+
*/
|
|
996
|
+
async setPassword(params) {
|
|
997
|
+
const response = await this.transport.request({
|
|
998
|
+
method: "PATCH",
|
|
999
|
+
url: this.config.baseUrl + ENDPOINTS.setPassword,
|
|
1000
|
+
body: {
|
|
1001
|
+
userId: params.userId,
|
|
1002
|
+
newPassword: params.newPassword,
|
|
1003
|
+
confirmPassword: params.confirmPassword,
|
|
1004
|
+
...params.ticket ? { ticket: params.ticket } : {}
|
|
1005
|
+
}
|
|
1006
|
+
});
|
|
1007
|
+
if (response.status !== 200) throw this.toError(response);
|
|
1008
|
+
}
|
|
1009
|
+
/**
|
|
1010
|
+
* Start a server-mediated self-service reset (F040). POSTs the email + this app's
|
|
1011
|
+
* publishable key; the backend resolves the account, mints a single-use token, and
|
|
1012
|
+
* emails the link ITSELF — nothing comes back but a uniform 202. Resolves on success;
|
|
1013
|
+
* a 4xx (e.g. a misconfigured key) surfaces as a typed error for the developer.
|
|
1014
|
+
*/
|
|
1015
|
+
async requestPasswordReset(params) {
|
|
1016
|
+
const response = await this.transport.request({
|
|
1017
|
+
method: "POST",
|
|
1018
|
+
url: this.config.baseUrl + ENDPOINTS.forgotPassword,
|
|
1019
|
+
body: {
|
|
1020
|
+
email: params.email,
|
|
1021
|
+
client_id: this.config.key.raw
|
|
1022
|
+
}
|
|
1023
|
+
});
|
|
1024
|
+
if (response.status >= 400) throw this.toError(response);
|
|
1025
|
+
}
|
|
1026
|
+
/**
|
|
1027
|
+
* Redeem a reset token (the opaque value from the emailed link) single-use and set
|
|
1028
|
+
* the new password. Returns a typed outcome the UI branches on; only a 5xx throws.
|
|
1029
|
+
*/
|
|
1030
|
+
async resetPassword(params) {
|
|
1031
|
+
const response = await this.transport.request({
|
|
1032
|
+
method: "POST",
|
|
1033
|
+
url: this.config.baseUrl + ENDPOINTS.resetPassword,
|
|
1034
|
+
body: {
|
|
1035
|
+
token: params.token,
|
|
1036
|
+
newPassword: params.newPassword
|
|
1037
|
+
}
|
|
1038
|
+
});
|
|
1039
|
+
if (response.status >= 500) throw this.toError(response);
|
|
1040
|
+
return parseResetOutcome(response.status, response.body);
|
|
1041
|
+
}
|
|
862
1042
|
async exchangeCode(params) {
|
|
863
1043
|
const response = await this.transport.request({
|
|
864
1044
|
method: "POST",
|
|
@@ -874,6 +1054,27 @@ var AuthClient = class {
|
|
|
874
1054
|
if (response.status !== 200) throw this.toError(response);
|
|
875
1055
|
return normalizeExchange(response.body, this.clock.now());
|
|
876
1056
|
}
|
|
1057
|
+
/**
|
|
1058
|
+
* Fetch this app's login-page branding (public, pk_-scoped). NEVER throws and never
|
|
1059
|
+
* blocks the sign-in form: any non-200, malformed body, or network failure resolves
|
|
1060
|
+
* to an empty token set so the caller keeps its built-in defaults (FR-BRND-3).
|
|
1061
|
+
*/
|
|
1062
|
+
async fetchBranding(params = {}) {
|
|
1063
|
+
try {
|
|
1064
|
+
const url = new URL(this.config.baseUrl + ENDPOINTS.branding);
|
|
1065
|
+
url.searchParams.set("client_id", this.config.key.raw);
|
|
1066
|
+
if (params.organizationCode) url.searchParams.set("organizationCode", params.organizationCode);
|
|
1067
|
+
const response = await this.transport.request({
|
|
1068
|
+
method: "GET",
|
|
1069
|
+
url: url.toString()
|
|
1070
|
+
});
|
|
1071
|
+
if (response.status !== 200) return {};
|
|
1072
|
+
const tokens = response.body?.tokens;
|
|
1073
|
+
return tokens && typeof tokens === "object" ? tokens : {};
|
|
1074
|
+
} catch {
|
|
1075
|
+
return {};
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
877
1078
|
/** The `RefreshTokens` function the SessionEngine (F003) injects. */
|
|
878
1079
|
createRefresher() {
|
|
879
1080
|
return async (current) => {
|
|
@@ -886,6 +1087,11 @@ var AuthClient = class {
|
|
|
886
1087
|
return normalizeRefresh(response.body, current, this.clock.now());
|
|
887
1088
|
};
|
|
888
1089
|
}
|
|
1090
|
+
/**
|
|
1091
|
+
* Ecosystem logout — only revokes when the caller carries the browser's ecosystem
|
|
1092
|
+
* cookie (a top-level hop). From a server-to-server context it clears nothing and
|
|
1093
|
+
* still answers 200; use `revokeSession` there.
|
|
1094
|
+
*/
|
|
889
1095
|
async logout(scope = "device") {
|
|
890
1096
|
const response = await this.transport.request({
|
|
891
1097
|
method: "POST",
|
|
@@ -894,6 +1100,22 @@ var AuthClient = class {
|
|
|
894
1100
|
});
|
|
895
1101
|
if (response.status >= 400) throw this.toError(response);
|
|
896
1102
|
}
|
|
1103
|
+
/**
|
|
1104
|
+
* Revoke a session using the access token as proof — the only logout that works
|
|
1105
|
+
* server-side, where no browser cookie reaches poly-auth. `device` ends this session;
|
|
1106
|
+
* `everywhere` ends every session for the user (FR-SSO-5).
|
|
1107
|
+
*
|
|
1108
|
+
* The token goes in the Authorization header and nowhere else: repeating it in the
|
|
1109
|
+
* body would only widen where it can leak.
|
|
1110
|
+
*/
|
|
1111
|
+
async revokeSession(accessToken, scope = "device") {
|
|
1112
|
+
const response = await this.transport.request({
|
|
1113
|
+
method: "POST",
|
|
1114
|
+
url: this.config.baseUrl + (scope === "everywhere" ? ENDPOINTS.authLogoutAll : ENDPOINTS.authLogout),
|
|
1115
|
+
headers: { Authorization: `Bearer ${accessToken}` }
|
|
1116
|
+
});
|
|
1117
|
+
if (response.status >= 400) throw this.toError(response);
|
|
1118
|
+
}
|
|
897
1119
|
async resolveOrg(email) {
|
|
898
1120
|
const response = await this.transport.request({
|
|
899
1121
|
method: "POST",
|
|
@@ -955,7 +1177,9 @@ exports.mapPlatformError = mapPlatformError;
|
|
|
955
1177
|
exports.normalizeExchange = normalizeExchange;
|
|
956
1178
|
exports.normalizeRefresh = normalizeRefresh;
|
|
957
1179
|
exports.parseAuthorizeOutcome = parseAuthorizeOutcome;
|
|
1180
|
+
exports.parsePasswordOutcome = parsePasswordOutcome;
|
|
958
1181
|
exports.parsePolyXKey = parsePolyXKey;
|
|
1182
|
+
exports.parseResetOutcome = parseResetOutcome;
|
|
959
1183
|
exports.randomState = randomState;
|
|
960
1184
|
exports.redactSensitive = redactSensitive;
|
|
961
1185
|
exports.reduceSessionState = reduce;
|
package/dist/index.d.cts
CHANGED
|
@@ -203,11 +203,24 @@ interface TokenSet {
|
|
|
203
203
|
/** Epoch milliseconds when the access token expires, per the server. */
|
|
204
204
|
accessTokenExpiresAt: number;
|
|
205
205
|
}
|
|
206
|
+
/**
|
|
207
|
+
* The verified identity a session carries. Per ADR-0006 this holds identity plus a **bounded
|
|
208
|
+
* display subset** — enough for a signed-in user surface — and never bulk authorization data
|
|
209
|
+
* (module graphs, team lists). Claims are sealed into a ~4KB cookie that fails by being
|
|
210
|
+
* silently dropped, so a field earns its place by being bounded and small, not by being useful.
|
|
211
|
+
*
|
|
212
|
+
* The display fields are optional: they are derived defensively from whatever the platform
|
|
213
|
+
* returns, and their absence is normal, not an error.
|
|
214
|
+
*/
|
|
206
215
|
interface SessionClaims {
|
|
207
216
|
userId: string;
|
|
208
217
|
organizationId?: string;
|
|
209
218
|
roles: readonly string[];
|
|
210
219
|
permissions: readonly string[];
|
|
220
|
+
/** Best available human label: full name → username → email. */
|
|
221
|
+
displayName?: string;
|
|
222
|
+
email?: string;
|
|
223
|
+
avatarUrl?: string;
|
|
211
224
|
}
|
|
212
225
|
interface Session {
|
|
213
226
|
claims: SessionClaims;
|
|
@@ -390,8 +403,41 @@ declare const ENDPOINTS: {
|
|
|
390
403
|
readonly authorize: "/v1/idp/authorize";
|
|
391
404
|
readonly token: "/v1/idp/token";
|
|
392
405
|
readonly refresh: "/v1/auth/refresh-token";
|
|
406
|
+
/**
|
|
407
|
+
* Ecosystem (SSO) logout. Resolves the session from the *browser's* ecosystem cookie,
|
|
408
|
+
* so it only revokes when called from a context that carries it — a top-level hop.
|
|
409
|
+
* A server-to-server call reaches it without the cookie and revokes nothing (it still
|
|
410
|
+
* answers 200), which is why BFF sign-out must use the bearer-authenticated pair below.
|
|
411
|
+
*/
|
|
393
412
|
readonly logout: "/v1/idp/logout";
|
|
413
|
+
/**
|
|
414
|
+
* Bearer-authenticated revocation: the access token identifies the session, so these
|
|
415
|
+
* work from any context that holds one — notably the BFF, where the token is sealed in
|
|
416
|
+
* the session cookie and no browser cookie reaches poly-auth. `logout` revokes this
|
|
417
|
+
* session; `logoutAll` revokes every session for the user.
|
|
418
|
+
*/
|
|
419
|
+
readonly authLogout: "/v1/auth/logout";
|
|
420
|
+
readonly authLogoutAll: "/v1/auth/logout-all";
|
|
394
421
|
readonly orgResolve: "/v1/idp/org-resolve";
|
|
422
|
+
/**
|
|
423
|
+
* Completes the admin temp-password flow: the user signed in with a temporary
|
|
424
|
+
* password, poly-auth answered `403 {userPasswordSet, userId}`, and this sets
|
|
425
|
+
* the real one. Only succeeds while the account is flagged `mustChangePassword`.
|
|
426
|
+
*/
|
|
427
|
+
readonly setPassword: "/v1/auth/set-password";
|
|
428
|
+
/**
|
|
429
|
+
* Server-mediated self-service reset (poly-x v5 / F040). `forgotPassword` starts it
|
|
430
|
+
* — always a uniform 202, the token is emailed to the mailbox, never returned.
|
|
431
|
+
* `resetPassword` redeems that opaque token single-use and sets the new password.
|
|
432
|
+
*/
|
|
433
|
+
readonly forgotPassword: "/v1/idp/forgot-password";
|
|
434
|
+
readonly resetPassword: "/v1/idp/reset-password";
|
|
435
|
+
/**
|
|
436
|
+
* Public, pk_-scoped login-page branding (F037 / FR-BRND). GET with `client_id`
|
|
437
|
+
* (+ optional `organizationCode`) → the bounded `{ tokens }` set. Never 5xx's /
|
|
438
|
+
* never blocks login; the SDK falls back to its built-in defaults on any failure.
|
|
439
|
+
*/
|
|
440
|
+
readonly branding: "/v1/idp/branding";
|
|
395
441
|
};
|
|
396
442
|
//#endregion
|
|
397
443
|
//#region src/auth/outcomes.d.ts
|
|
@@ -421,10 +467,50 @@ type AuthorizeOutcome = {
|
|
|
421
467
|
type: "login_required";
|
|
422
468
|
};
|
|
423
469
|
declare function parseAuthorizeOutcome(status: number, body: unknown): AuthorizeOutcome;
|
|
470
|
+
/**
|
|
471
|
+
* Outcome of a PASSWORD authorize (`POST /idp/authorize` with credentials) — the
|
|
472
|
+
* embedded `<SignIn/>` flow. Distinguishes invalid-credentials and the
|
|
473
|
+
* must-set-password step from the membership outcomes (the GET warm-hop parser
|
|
474
|
+
* collapses those into login_required).
|
|
475
|
+
*/
|
|
476
|
+
type PasswordAuthOutcome = {
|
|
477
|
+
type: "authorized";
|
|
478
|
+
code: string;
|
|
479
|
+
state: string;
|
|
480
|
+
redirectUri?: string;
|
|
481
|
+
} | {
|
|
482
|
+
type: "select_tenant";
|
|
483
|
+
state: string;
|
|
484
|
+
tenants: TenantOption[];
|
|
485
|
+
} | {
|
|
486
|
+
type: "invalid_credentials";
|
|
487
|
+
} | {
|
|
488
|
+
type: "password_change_required";
|
|
489
|
+
userId: string;
|
|
490
|
+
ticket?: string;
|
|
491
|
+
} | {
|
|
492
|
+
type: "no_access";
|
|
493
|
+
};
|
|
494
|
+
declare function parsePasswordOutcome(status: number, body: unknown): PasswordAuthOutcome;
|
|
495
|
+
/**
|
|
496
|
+
* The outcome of redeeming a reset token (`POST /idp/reset-password`, F040):
|
|
497
|
+
* `ok` on success, `weak_password` when it fails the platform floor, and a
|
|
498
|
+
* uniform `invalid_or_expired` for an unknown/used/expired token (the backend
|
|
499
|
+
* does not distinguish unknown from expired to the caller).
|
|
500
|
+
*/
|
|
501
|
+
type PasswordResetOutcome = "ok" | "weak_password" | "invalid_or_expired";
|
|
502
|
+
declare function parseResetOutcome(status: number, body: unknown): PasswordResetOutcome;
|
|
424
503
|
//#endregion
|
|
425
504
|
//#region src/auth/normalize.d.ts
|
|
426
505
|
declare function decodeJwtExp(token: string): number | null;
|
|
427
|
-
/**
|
|
506
|
+
/**
|
|
507
|
+
* `POST /idp/token` → StoredSession. Claims carry identity plus the bounded display subset
|
|
508
|
+
* (ADR-0006); AUTHZ populates roles/permissions later.
|
|
509
|
+
*
|
|
510
|
+
* `organizationId` comes from the exchange's `tenantId`, never from the user document: a
|
|
511
|
+
* cross-membership login resolves a tenant that is deliberately *not* the user's home
|
|
512
|
+
* organization, and reading it off the user would silently sign them into the wrong one.
|
|
513
|
+
*/
|
|
428
514
|
declare function normalizeExchange(body: unknown, now: number): StoredSession;
|
|
429
515
|
/** `POST /auth/refresh-token` → StoredSession. No user in the response, so claims carry forward. */
|
|
430
516
|
declare function normalizeRefresh(body: unknown, current: StoredSession, now: number): StoredSession;
|
|
@@ -472,6 +558,38 @@ interface ExchangeCodeParams {
|
|
|
472
558
|
codeVerifier: string;
|
|
473
559
|
redirectUri: string;
|
|
474
560
|
}
|
|
561
|
+
interface SetPasswordParams {
|
|
562
|
+
userId: string;
|
|
563
|
+
newPassword: string;
|
|
564
|
+
confirmPassword: string;
|
|
565
|
+
/**
|
|
566
|
+
* Single-use proof-of-possession ticket from the `password_change_required`
|
|
567
|
+
* outcome. When present the platform trusts the ticket over `userId`; when the
|
|
568
|
+
* backend enforces tickets, it's required. Omit against older backends.
|
|
569
|
+
*/
|
|
570
|
+
ticket?: string;
|
|
571
|
+
}
|
|
572
|
+
interface AuthorizeWithPasswordParams {
|
|
573
|
+
email: string;
|
|
574
|
+
password: string;
|
|
575
|
+
redirectUri: string;
|
|
576
|
+
state: string;
|
|
577
|
+
challenge: string;
|
|
578
|
+
organizationCode?: string;
|
|
579
|
+
/** Set on the re-attempt after the user picks a workspace (select_tenant). */
|
|
580
|
+
tenantId?: string;
|
|
581
|
+
}
|
|
582
|
+
/** The bounded login-page branding token set (FR-BRND-1). Every field is optional. */
|
|
583
|
+
interface BrandingTokens {
|
|
584
|
+
displayName?: string;
|
|
585
|
+
logoUrl?: string;
|
|
586
|
+
primaryColor?: string;
|
|
587
|
+
backgroundColor?: string;
|
|
588
|
+
}
|
|
589
|
+
interface FetchBrandingParams {
|
|
590
|
+
/** Refine app-level branding with an org's when the org is already known. */
|
|
591
|
+
organizationCode?: string;
|
|
592
|
+
}
|
|
475
593
|
declare class AuthClient {
|
|
476
594
|
private readonly config;
|
|
477
595
|
private readonly transport;
|
|
@@ -489,10 +607,63 @@ declare class AuthClient {
|
|
|
489
607
|
buildSignInUrl(params: BuildAuthorizeUrlParams): string;
|
|
490
608
|
/** Probe the warm hop (GET /idp/authorize) and return the typed outcome. */
|
|
491
609
|
completeAuthorize(params: BuildAuthorizeUrlParams): Promise<AuthorizeOutcome>;
|
|
610
|
+
/**
|
|
611
|
+
* The embedded `<SignIn/>` credential flow (D2b): POST `/idp/authorize` with
|
|
612
|
+
* email/password + PKCE. On `authorized` the caller exchanges the returned
|
|
613
|
+
* `code` (server-side, via the BFF) for a session — no redirect to a hosted
|
|
614
|
+
* login page. Creds live only for the duration of this request; the SDK never
|
|
615
|
+
* stores them. 5xx surfaces as a typed error; every other status is a typed
|
|
616
|
+
* outcome the UI branches on.
|
|
617
|
+
*/
|
|
618
|
+
authorizeWithPassword(params: AuthorizeWithPasswordParams): Promise<PasswordAuthOutcome>;
|
|
619
|
+
/**
|
|
620
|
+
* Set the real password for a user the platform has flagged `mustChangePassword`
|
|
621
|
+
* (the admin temp-password flow). Call this only after `authorizeWithPassword`
|
|
622
|
+
* has returned `password_change_required` for that same user — that response IS
|
|
623
|
+
* the proof the caller knows the temporary password.
|
|
624
|
+
*/
|
|
625
|
+
setPassword(params: SetPasswordParams): Promise<void>;
|
|
626
|
+
/**
|
|
627
|
+
* Start a server-mediated self-service reset (F040). POSTs the email + this app's
|
|
628
|
+
* publishable key; the backend resolves the account, mints a single-use token, and
|
|
629
|
+
* emails the link ITSELF — nothing comes back but a uniform 202. Resolves on success;
|
|
630
|
+
* a 4xx (e.g. a misconfigured key) surfaces as a typed error for the developer.
|
|
631
|
+
*/
|
|
632
|
+
requestPasswordReset(params: {
|
|
633
|
+
email: string;
|
|
634
|
+
}): Promise<void>;
|
|
635
|
+
/**
|
|
636
|
+
* Redeem a reset token (the opaque value from the emailed link) single-use and set
|
|
637
|
+
* the new password. Returns a typed outcome the UI branches on; only a 5xx throws.
|
|
638
|
+
*/
|
|
639
|
+
resetPassword(params: {
|
|
640
|
+
token: string;
|
|
641
|
+
newPassword: string;
|
|
642
|
+
}): Promise<PasswordResetOutcome>;
|
|
492
643
|
exchangeCode(params: ExchangeCodeParams): Promise<StoredSession>;
|
|
644
|
+
/**
|
|
645
|
+
* Fetch this app's login-page branding (public, pk_-scoped). NEVER throws and never
|
|
646
|
+
* blocks the sign-in form: any non-200, malformed body, or network failure resolves
|
|
647
|
+
* to an empty token set so the caller keeps its built-in defaults (FR-BRND-3).
|
|
648
|
+
*/
|
|
649
|
+
fetchBranding(params?: FetchBrandingParams): Promise<BrandingTokens>;
|
|
493
650
|
/** The `RefreshTokens` function the SessionEngine (F003) injects. */
|
|
494
651
|
createRefresher(): RefreshTokens;
|
|
652
|
+
/**
|
|
653
|
+
* Ecosystem logout — only revokes when the caller carries the browser's ecosystem
|
|
654
|
+
* cookie (a top-level hop). From a server-to-server context it clears nothing and
|
|
655
|
+
* still answers 200; use `revokeSession` there.
|
|
656
|
+
*/
|
|
495
657
|
logout(scope?: "device" | "everywhere"): Promise<void>;
|
|
658
|
+
/**
|
|
659
|
+
* Revoke a session using the access token as proof — the only logout that works
|
|
660
|
+
* server-side, where no browser cookie reaches poly-auth. `device` ends this session;
|
|
661
|
+
* `everywhere` ends every session for the user (FR-SSO-5).
|
|
662
|
+
*
|
|
663
|
+
* The token goes in the Authorization header and nowhere else: repeating it in the
|
|
664
|
+
* body would only widen where it can leak.
|
|
665
|
+
*/
|
|
666
|
+
revokeSession(accessToken: string, scope?: "device" | "everywhere"): Promise<void>;
|
|
496
667
|
resolveOrg(email: string): Promise<{
|
|
497
668
|
resolved: boolean;
|
|
498
669
|
organizationCode?: string;
|
|
@@ -507,4 +678,4 @@ declare class AuthClient {
|
|
|
507
678
|
declare const PACKAGE_NAME = "@poly-x/core";
|
|
508
679
|
declare const version = "0.0.0";
|
|
509
680
|
//#endregion
|
|
510
|
-
export { AuthClient, type AuthClientOptions, type AuthorizeOutcome, type BuildAuthorizeUrlParams, type Clock, DEFAULT_RETRY_POLICY, DEFAULT_TIMEOUT_MS, ENDPOINTS, type ExchangeCodeParams, FetchTransport, type FieldError, ForbiddenError, type HttpMethod, INITIAL_STATE as INITIAL_SESSION_STATE, InMemoryLock, InMemoryPkceStore, InMemorySessionChannel, InMemorySessionStore, type KeyEnv, type KeyKind, type Lock, type MockRoute, MockTransport, PACKAGE_NAME, type ParsedKey, type PkceEntry, type PkcePair, type PkceStore, type PlatformResponseLike, PolyXConfigError, PolyXError, type PolyXErrorOptions, RateLimitedError, type RefreshTokens, type ResolveConfigInput, type ResolvedConfig, type RetryPolicy, type RuntimeContext, type Session, type SessionChannel, type SessionChannelEvent, type SessionClaims, SessionEngine, type SessionEngineOptions, type SessionEvent, SessionExpiredError, SessionRevokedError, type SessionState, type SessionStore, type SignedOutReason, type StoredSession, SystemClock, type TenantOption, type TimerHandle, type TokenSet, type Transport, type TransportRequest, type TransportResponse, UnauthenticatedError, UpstreamError, ValidationError, computeChallenge, createSessionEngine, decodeJwtExp, generatePkce, isTransientStatus, mapPlatformError, normalizeExchange, normalizeRefresh, parseAuthorizeOutcome, parsePolyXKey, randomState, redactSensitive, reduce as reduceSessionState, resolveConfig, version };
|
|
681
|
+
export { AuthClient, type AuthClientOptions, type AuthorizeOutcome, type AuthorizeWithPasswordParams, type BrandingTokens, type BuildAuthorizeUrlParams, type Clock, DEFAULT_RETRY_POLICY, DEFAULT_TIMEOUT_MS, ENDPOINTS, type ExchangeCodeParams, type FetchBrandingParams, FetchTransport, type FieldError, ForbiddenError, type HttpMethod, INITIAL_STATE as INITIAL_SESSION_STATE, InMemoryLock, InMemoryPkceStore, InMemorySessionChannel, InMemorySessionStore, type KeyEnv, type KeyKind, type Lock, type MockRoute, MockTransport, PACKAGE_NAME, type ParsedKey, type PasswordAuthOutcome, type PasswordResetOutcome, type PkceEntry, type PkcePair, type PkceStore, type PlatformResponseLike, PolyXConfigError, PolyXError, type PolyXErrorOptions, RateLimitedError, type RefreshTokens, type ResolveConfigInput, type ResolvedConfig, type RetryPolicy, type RuntimeContext, type Session, type SessionChannel, type SessionChannelEvent, type SessionClaims, SessionEngine, type SessionEngineOptions, type SessionEvent, SessionExpiredError, SessionRevokedError, type SessionState, type SessionStore, type SetPasswordParams, type SignedOutReason, type StoredSession, SystemClock, type TenantOption, type TimerHandle, type TokenSet, type Transport, type TransportRequest, type TransportResponse, UnauthenticatedError, UpstreamError, ValidationError, computeChallenge, createSessionEngine, decodeJwtExp, generatePkce, isTransientStatus, mapPlatformError, normalizeExchange, normalizeRefresh, parseAuthorizeOutcome, parsePasswordOutcome, parsePolyXKey, parseResetOutcome, randomState, redactSensitive, reduce as reduceSessionState, resolveConfig, version };
|
package/dist/index.d.mts
CHANGED
|
@@ -203,11 +203,24 @@ interface TokenSet {
|
|
|
203
203
|
/** Epoch milliseconds when the access token expires, per the server. */
|
|
204
204
|
accessTokenExpiresAt: number;
|
|
205
205
|
}
|
|
206
|
+
/**
|
|
207
|
+
* The verified identity a session carries. Per ADR-0006 this holds identity plus a **bounded
|
|
208
|
+
* display subset** — enough for a signed-in user surface — and never bulk authorization data
|
|
209
|
+
* (module graphs, team lists). Claims are sealed into a ~4KB cookie that fails by being
|
|
210
|
+
* silently dropped, so a field earns its place by being bounded and small, not by being useful.
|
|
211
|
+
*
|
|
212
|
+
* The display fields are optional: they are derived defensively from whatever the platform
|
|
213
|
+
* returns, and their absence is normal, not an error.
|
|
214
|
+
*/
|
|
206
215
|
interface SessionClaims {
|
|
207
216
|
userId: string;
|
|
208
217
|
organizationId?: string;
|
|
209
218
|
roles: readonly string[];
|
|
210
219
|
permissions: readonly string[];
|
|
220
|
+
/** Best available human label: full name → username → email. */
|
|
221
|
+
displayName?: string;
|
|
222
|
+
email?: string;
|
|
223
|
+
avatarUrl?: string;
|
|
211
224
|
}
|
|
212
225
|
interface Session {
|
|
213
226
|
claims: SessionClaims;
|
|
@@ -390,8 +403,41 @@ declare const ENDPOINTS: {
|
|
|
390
403
|
readonly authorize: "/v1/idp/authorize";
|
|
391
404
|
readonly token: "/v1/idp/token";
|
|
392
405
|
readonly refresh: "/v1/auth/refresh-token";
|
|
406
|
+
/**
|
|
407
|
+
* Ecosystem (SSO) logout. Resolves the session from the *browser's* ecosystem cookie,
|
|
408
|
+
* so it only revokes when called from a context that carries it — a top-level hop.
|
|
409
|
+
* A server-to-server call reaches it without the cookie and revokes nothing (it still
|
|
410
|
+
* answers 200), which is why BFF sign-out must use the bearer-authenticated pair below.
|
|
411
|
+
*/
|
|
393
412
|
readonly logout: "/v1/idp/logout";
|
|
413
|
+
/**
|
|
414
|
+
* Bearer-authenticated revocation: the access token identifies the session, so these
|
|
415
|
+
* work from any context that holds one — notably the BFF, where the token is sealed in
|
|
416
|
+
* the session cookie and no browser cookie reaches poly-auth. `logout` revokes this
|
|
417
|
+
* session; `logoutAll` revokes every session for the user.
|
|
418
|
+
*/
|
|
419
|
+
readonly authLogout: "/v1/auth/logout";
|
|
420
|
+
readonly authLogoutAll: "/v1/auth/logout-all";
|
|
394
421
|
readonly orgResolve: "/v1/idp/org-resolve";
|
|
422
|
+
/**
|
|
423
|
+
* Completes the admin temp-password flow: the user signed in with a temporary
|
|
424
|
+
* password, poly-auth answered `403 {userPasswordSet, userId}`, and this sets
|
|
425
|
+
* the real one. Only succeeds while the account is flagged `mustChangePassword`.
|
|
426
|
+
*/
|
|
427
|
+
readonly setPassword: "/v1/auth/set-password";
|
|
428
|
+
/**
|
|
429
|
+
* Server-mediated self-service reset (poly-x v5 / F040). `forgotPassword` starts it
|
|
430
|
+
* — always a uniform 202, the token is emailed to the mailbox, never returned.
|
|
431
|
+
* `resetPassword` redeems that opaque token single-use and sets the new password.
|
|
432
|
+
*/
|
|
433
|
+
readonly forgotPassword: "/v1/idp/forgot-password";
|
|
434
|
+
readonly resetPassword: "/v1/idp/reset-password";
|
|
435
|
+
/**
|
|
436
|
+
* Public, pk_-scoped login-page branding (F037 / FR-BRND). GET with `client_id`
|
|
437
|
+
* (+ optional `organizationCode`) → the bounded `{ tokens }` set. Never 5xx's /
|
|
438
|
+
* never blocks login; the SDK falls back to its built-in defaults on any failure.
|
|
439
|
+
*/
|
|
440
|
+
readonly branding: "/v1/idp/branding";
|
|
395
441
|
};
|
|
396
442
|
//#endregion
|
|
397
443
|
//#region src/auth/outcomes.d.ts
|
|
@@ -421,10 +467,50 @@ type AuthorizeOutcome = {
|
|
|
421
467
|
type: "login_required";
|
|
422
468
|
};
|
|
423
469
|
declare function parseAuthorizeOutcome(status: number, body: unknown): AuthorizeOutcome;
|
|
470
|
+
/**
|
|
471
|
+
* Outcome of a PASSWORD authorize (`POST /idp/authorize` with credentials) — the
|
|
472
|
+
* embedded `<SignIn/>` flow. Distinguishes invalid-credentials and the
|
|
473
|
+
* must-set-password step from the membership outcomes (the GET warm-hop parser
|
|
474
|
+
* collapses those into login_required).
|
|
475
|
+
*/
|
|
476
|
+
type PasswordAuthOutcome = {
|
|
477
|
+
type: "authorized";
|
|
478
|
+
code: string;
|
|
479
|
+
state: string;
|
|
480
|
+
redirectUri?: string;
|
|
481
|
+
} | {
|
|
482
|
+
type: "select_tenant";
|
|
483
|
+
state: string;
|
|
484
|
+
tenants: TenantOption[];
|
|
485
|
+
} | {
|
|
486
|
+
type: "invalid_credentials";
|
|
487
|
+
} | {
|
|
488
|
+
type: "password_change_required";
|
|
489
|
+
userId: string;
|
|
490
|
+
ticket?: string;
|
|
491
|
+
} | {
|
|
492
|
+
type: "no_access";
|
|
493
|
+
};
|
|
494
|
+
declare function parsePasswordOutcome(status: number, body: unknown): PasswordAuthOutcome;
|
|
495
|
+
/**
|
|
496
|
+
* The outcome of redeeming a reset token (`POST /idp/reset-password`, F040):
|
|
497
|
+
* `ok` on success, `weak_password` when it fails the platform floor, and a
|
|
498
|
+
* uniform `invalid_or_expired` for an unknown/used/expired token (the backend
|
|
499
|
+
* does not distinguish unknown from expired to the caller).
|
|
500
|
+
*/
|
|
501
|
+
type PasswordResetOutcome = "ok" | "weak_password" | "invalid_or_expired";
|
|
502
|
+
declare function parseResetOutcome(status: number, body: unknown): PasswordResetOutcome;
|
|
424
503
|
//#endregion
|
|
425
504
|
//#region src/auth/normalize.d.ts
|
|
426
505
|
declare function decodeJwtExp(token: string): number | null;
|
|
427
|
-
/**
|
|
506
|
+
/**
|
|
507
|
+
* `POST /idp/token` → StoredSession. Claims carry identity plus the bounded display subset
|
|
508
|
+
* (ADR-0006); AUTHZ populates roles/permissions later.
|
|
509
|
+
*
|
|
510
|
+
* `organizationId` comes from the exchange's `tenantId`, never from the user document: a
|
|
511
|
+
* cross-membership login resolves a tenant that is deliberately *not* the user's home
|
|
512
|
+
* organization, and reading it off the user would silently sign them into the wrong one.
|
|
513
|
+
*/
|
|
428
514
|
declare function normalizeExchange(body: unknown, now: number): StoredSession;
|
|
429
515
|
/** `POST /auth/refresh-token` → StoredSession. No user in the response, so claims carry forward. */
|
|
430
516
|
declare function normalizeRefresh(body: unknown, current: StoredSession, now: number): StoredSession;
|
|
@@ -472,6 +558,38 @@ interface ExchangeCodeParams {
|
|
|
472
558
|
codeVerifier: string;
|
|
473
559
|
redirectUri: string;
|
|
474
560
|
}
|
|
561
|
+
interface SetPasswordParams {
|
|
562
|
+
userId: string;
|
|
563
|
+
newPassword: string;
|
|
564
|
+
confirmPassword: string;
|
|
565
|
+
/**
|
|
566
|
+
* Single-use proof-of-possession ticket from the `password_change_required`
|
|
567
|
+
* outcome. When present the platform trusts the ticket over `userId`; when the
|
|
568
|
+
* backend enforces tickets, it's required. Omit against older backends.
|
|
569
|
+
*/
|
|
570
|
+
ticket?: string;
|
|
571
|
+
}
|
|
572
|
+
interface AuthorizeWithPasswordParams {
|
|
573
|
+
email: string;
|
|
574
|
+
password: string;
|
|
575
|
+
redirectUri: string;
|
|
576
|
+
state: string;
|
|
577
|
+
challenge: string;
|
|
578
|
+
organizationCode?: string;
|
|
579
|
+
/** Set on the re-attempt after the user picks a workspace (select_tenant). */
|
|
580
|
+
tenantId?: string;
|
|
581
|
+
}
|
|
582
|
+
/** The bounded login-page branding token set (FR-BRND-1). Every field is optional. */
|
|
583
|
+
interface BrandingTokens {
|
|
584
|
+
displayName?: string;
|
|
585
|
+
logoUrl?: string;
|
|
586
|
+
primaryColor?: string;
|
|
587
|
+
backgroundColor?: string;
|
|
588
|
+
}
|
|
589
|
+
interface FetchBrandingParams {
|
|
590
|
+
/** Refine app-level branding with an org's when the org is already known. */
|
|
591
|
+
organizationCode?: string;
|
|
592
|
+
}
|
|
475
593
|
declare class AuthClient {
|
|
476
594
|
private readonly config;
|
|
477
595
|
private readonly transport;
|
|
@@ -489,10 +607,63 @@ declare class AuthClient {
|
|
|
489
607
|
buildSignInUrl(params: BuildAuthorizeUrlParams): string;
|
|
490
608
|
/** Probe the warm hop (GET /idp/authorize) and return the typed outcome. */
|
|
491
609
|
completeAuthorize(params: BuildAuthorizeUrlParams): Promise<AuthorizeOutcome>;
|
|
610
|
+
/**
|
|
611
|
+
* The embedded `<SignIn/>` credential flow (D2b): POST `/idp/authorize` with
|
|
612
|
+
* email/password + PKCE. On `authorized` the caller exchanges the returned
|
|
613
|
+
* `code` (server-side, via the BFF) for a session — no redirect to a hosted
|
|
614
|
+
* login page. Creds live only for the duration of this request; the SDK never
|
|
615
|
+
* stores them. 5xx surfaces as a typed error; every other status is a typed
|
|
616
|
+
* outcome the UI branches on.
|
|
617
|
+
*/
|
|
618
|
+
authorizeWithPassword(params: AuthorizeWithPasswordParams): Promise<PasswordAuthOutcome>;
|
|
619
|
+
/**
|
|
620
|
+
* Set the real password for a user the platform has flagged `mustChangePassword`
|
|
621
|
+
* (the admin temp-password flow). Call this only after `authorizeWithPassword`
|
|
622
|
+
* has returned `password_change_required` for that same user — that response IS
|
|
623
|
+
* the proof the caller knows the temporary password.
|
|
624
|
+
*/
|
|
625
|
+
setPassword(params: SetPasswordParams): Promise<void>;
|
|
626
|
+
/**
|
|
627
|
+
* Start a server-mediated self-service reset (F040). POSTs the email + this app's
|
|
628
|
+
* publishable key; the backend resolves the account, mints a single-use token, and
|
|
629
|
+
* emails the link ITSELF — nothing comes back but a uniform 202. Resolves on success;
|
|
630
|
+
* a 4xx (e.g. a misconfigured key) surfaces as a typed error for the developer.
|
|
631
|
+
*/
|
|
632
|
+
requestPasswordReset(params: {
|
|
633
|
+
email: string;
|
|
634
|
+
}): Promise<void>;
|
|
635
|
+
/**
|
|
636
|
+
* Redeem a reset token (the opaque value from the emailed link) single-use and set
|
|
637
|
+
* the new password. Returns a typed outcome the UI branches on; only a 5xx throws.
|
|
638
|
+
*/
|
|
639
|
+
resetPassword(params: {
|
|
640
|
+
token: string;
|
|
641
|
+
newPassword: string;
|
|
642
|
+
}): Promise<PasswordResetOutcome>;
|
|
492
643
|
exchangeCode(params: ExchangeCodeParams): Promise<StoredSession>;
|
|
644
|
+
/**
|
|
645
|
+
* Fetch this app's login-page branding (public, pk_-scoped). NEVER throws and never
|
|
646
|
+
* blocks the sign-in form: any non-200, malformed body, or network failure resolves
|
|
647
|
+
* to an empty token set so the caller keeps its built-in defaults (FR-BRND-3).
|
|
648
|
+
*/
|
|
649
|
+
fetchBranding(params?: FetchBrandingParams): Promise<BrandingTokens>;
|
|
493
650
|
/** The `RefreshTokens` function the SessionEngine (F003) injects. */
|
|
494
651
|
createRefresher(): RefreshTokens;
|
|
652
|
+
/**
|
|
653
|
+
* Ecosystem logout — only revokes when the caller carries the browser's ecosystem
|
|
654
|
+
* cookie (a top-level hop). From a server-to-server context it clears nothing and
|
|
655
|
+
* still answers 200; use `revokeSession` there.
|
|
656
|
+
*/
|
|
495
657
|
logout(scope?: "device" | "everywhere"): Promise<void>;
|
|
658
|
+
/**
|
|
659
|
+
* Revoke a session using the access token as proof — the only logout that works
|
|
660
|
+
* server-side, where no browser cookie reaches poly-auth. `device` ends this session;
|
|
661
|
+
* `everywhere` ends every session for the user (FR-SSO-5).
|
|
662
|
+
*
|
|
663
|
+
* The token goes in the Authorization header and nowhere else: repeating it in the
|
|
664
|
+
* body would only widen where it can leak.
|
|
665
|
+
*/
|
|
666
|
+
revokeSession(accessToken: string, scope?: "device" | "everywhere"): Promise<void>;
|
|
496
667
|
resolveOrg(email: string): Promise<{
|
|
497
668
|
resolved: boolean;
|
|
498
669
|
organizationCode?: string;
|
|
@@ -507,4 +678,4 @@ declare class AuthClient {
|
|
|
507
678
|
declare const PACKAGE_NAME = "@poly-x/core";
|
|
508
679
|
declare const version = "0.0.0";
|
|
509
680
|
//#endregion
|
|
510
|
-
export { AuthClient, type AuthClientOptions, type AuthorizeOutcome, type BuildAuthorizeUrlParams, type Clock, DEFAULT_RETRY_POLICY, DEFAULT_TIMEOUT_MS, ENDPOINTS, type ExchangeCodeParams, FetchTransport, type FieldError, ForbiddenError, type HttpMethod, INITIAL_STATE as INITIAL_SESSION_STATE, InMemoryLock, InMemoryPkceStore, InMemorySessionChannel, InMemorySessionStore, type KeyEnv, type KeyKind, type Lock, type MockRoute, MockTransport, PACKAGE_NAME, type ParsedKey, type PkceEntry, type PkcePair, type PkceStore, type PlatformResponseLike, PolyXConfigError, PolyXError, type PolyXErrorOptions, RateLimitedError, type RefreshTokens, type ResolveConfigInput, type ResolvedConfig, type RetryPolicy, type RuntimeContext, type Session, type SessionChannel, type SessionChannelEvent, type SessionClaims, SessionEngine, type SessionEngineOptions, type SessionEvent, SessionExpiredError, SessionRevokedError, type SessionState, type SessionStore, type SignedOutReason, type StoredSession, SystemClock, type TenantOption, type TimerHandle, type TokenSet, type Transport, type TransportRequest, type TransportResponse, UnauthenticatedError, UpstreamError, ValidationError, computeChallenge, createSessionEngine, decodeJwtExp, generatePkce, isTransientStatus, mapPlatformError, normalizeExchange, normalizeRefresh, parseAuthorizeOutcome, parsePolyXKey, randomState, redactSensitive, reduce as reduceSessionState, resolveConfig, version };
|
|
681
|
+
export { AuthClient, type AuthClientOptions, type AuthorizeOutcome, type AuthorizeWithPasswordParams, type BrandingTokens, type BuildAuthorizeUrlParams, type Clock, DEFAULT_RETRY_POLICY, DEFAULT_TIMEOUT_MS, ENDPOINTS, type ExchangeCodeParams, type FetchBrandingParams, FetchTransport, type FieldError, ForbiddenError, type HttpMethod, INITIAL_STATE as INITIAL_SESSION_STATE, InMemoryLock, InMemoryPkceStore, InMemorySessionChannel, InMemorySessionStore, type KeyEnv, type KeyKind, type Lock, type MockRoute, MockTransport, PACKAGE_NAME, type ParsedKey, type PasswordAuthOutcome, type PasswordResetOutcome, type PkceEntry, type PkcePair, type PkceStore, type PlatformResponseLike, PolyXConfigError, PolyXError, type PolyXErrorOptions, RateLimitedError, type RefreshTokens, type ResolveConfigInput, type ResolvedConfig, type RetryPolicy, type RuntimeContext, type Session, type SessionChannel, type SessionChannelEvent, type SessionClaims, SessionEngine, type SessionEngineOptions, type SessionEvent, SessionExpiredError, SessionRevokedError, type SessionState, type SessionStore, type SetPasswordParams, type SignedOutReason, type StoredSession, SystemClock, type TenantOption, type TimerHandle, type TokenSet, type Transport, type TransportRequest, type TransportResponse, UnauthenticatedError, UpstreamError, ValidationError, computeChallenge, createSessionEngine, decodeJwtExp, generatePkce, isTransientStatus, mapPlatformError, normalizeExchange, normalizeRefresh, parseAuthorizeOutcome, parsePasswordOutcome, parsePolyXKey, parseResetOutcome, randomState, redactSensitive, reduce as reduceSessionState, resolveConfig, version };
|
package/dist/index.mjs
CHANGED
|
@@ -192,7 +192,16 @@ function resolveConfig(input) {
|
|
|
192
192
|
* against poly-auth/src/api/constants/auth-responses.ts (2026-07-06). Unknown
|
|
193
193
|
* shapes become a typed UNRECOGNIZED_RESPONSE — never a guess.
|
|
194
194
|
*/
|
|
195
|
-
|
|
195
|
+
/**
|
|
196
|
+
* Terminal: the session is gone and no retry brings it back. The platform revokes every session
|
|
197
|
+
* for a user on permission change, suspension, and password reset — `SESSION_REVOKED` for the
|
|
198
|
+
* revocation itself, `SESSION_MISMATCH` when the presented token no longer matches its session.
|
|
199
|
+
*/
|
|
200
|
+
const REVOKED_CODES = /* @__PURE__ */ new Set([
|
|
201
|
+
"REFRESH_TOKEN_REUSE",
|
|
202
|
+
"SESSION_REVOKED",
|
|
203
|
+
"SESSION_MISMATCH"
|
|
204
|
+
]);
|
|
196
205
|
const EXPIRED_CODES = /* @__PURE__ */ new Set([
|
|
197
206
|
"SESSION_EXPIRED",
|
|
198
207
|
"REFRESH_TOKEN_REQUIRED",
|
|
@@ -707,8 +716,41 @@ const ENDPOINTS = {
|
|
|
707
716
|
authorize: "/v1/idp/authorize",
|
|
708
717
|
token: "/v1/idp/token",
|
|
709
718
|
refresh: "/v1/auth/refresh-token",
|
|
719
|
+
/**
|
|
720
|
+
* Ecosystem (SSO) logout. Resolves the session from the *browser's* ecosystem cookie,
|
|
721
|
+
* so it only revokes when called from a context that carries it — a top-level hop.
|
|
722
|
+
* A server-to-server call reaches it without the cookie and revokes nothing (it still
|
|
723
|
+
* answers 200), which is why BFF sign-out must use the bearer-authenticated pair below.
|
|
724
|
+
*/
|
|
710
725
|
logout: "/v1/idp/logout",
|
|
711
|
-
|
|
726
|
+
/**
|
|
727
|
+
* Bearer-authenticated revocation: the access token identifies the session, so these
|
|
728
|
+
* work from any context that holds one — notably the BFF, where the token is sealed in
|
|
729
|
+
* the session cookie and no browser cookie reaches poly-auth. `logout` revokes this
|
|
730
|
+
* session; `logoutAll` revokes every session for the user.
|
|
731
|
+
*/
|
|
732
|
+
authLogout: "/v1/auth/logout",
|
|
733
|
+
authLogoutAll: "/v1/auth/logout-all",
|
|
734
|
+
orgResolve: "/v1/idp/org-resolve",
|
|
735
|
+
/**
|
|
736
|
+
* Completes the admin temp-password flow: the user signed in with a temporary
|
|
737
|
+
* password, poly-auth answered `403 {userPasswordSet, userId}`, and this sets
|
|
738
|
+
* the real one. Only succeeds while the account is flagged `mustChangePassword`.
|
|
739
|
+
*/
|
|
740
|
+
setPassword: "/v1/auth/set-password",
|
|
741
|
+
/**
|
|
742
|
+
* Server-mediated self-service reset (poly-x v5 / F040). `forgotPassword` starts it
|
|
743
|
+
* — always a uniform 202, the token is emailed to the mailbox, never returned.
|
|
744
|
+
* `resetPassword` redeems that opaque token single-use and sets the new password.
|
|
745
|
+
*/
|
|
746
|
+
forgotPassword: "/v1/idp/forgot-password",
|
|
747
|
+
resetPassword: "/v1/idp/reset-password",
|
|
748
|
+
/**
|
|
749
|
+
* Public, pk_-scoped login-page branding (F037 / FR-BRND). GET with `client_id`
|
|
750
|
+
* (+ optional `organizationCode`) → the bounded `{ tokens }` set. Never 5xx's /
|
|
751
|
+
* never blocks login; the SDK falls back to its built-in defaults on any failure.
|
|
752
|
+
*/
|
|
753
|
+
branding: "/v1/idp/branding"
|
|
712
754
|
};
|
|
713
755
|
//#endregion
|
|
714
756
|
//#region src/auth/outcomes.ts
|
|
@@ -746,6 +788,33 @@ function parseAuthorizeOutcome(status, body) {
|
|
|
746
788
|
if (statusField === "login_required" || b.code === "LOGIN_REQUIRED") return { type: "login_required" };
|
|
747
789
|
return { type: "login_required" };
|
|
748
790
|
}
|
|
791
|
+
function parsePasswordOutcome(status, body) {
|
|
792
|
+
const b = asRecord$1(body);
|
|
793
|
+
const statusField = typeof b.status === "string" ? b.status : void 0;
|
|
794
|
+
if (statusField === "authorized" && typeof b.code === "string") return {
|
|
795
|
+
type: "authorized",
|
|
796
|
+
code: b.code,
|
|
797
|
+
state: typeof b.state === "string" ? b.state : "",
|
|
798
|
+
redirectUri: typeof b.redirectUri === "string" ? b.redirectUri : void 0
|
|
799
|
+
};
|
|
800
|
+
if (statusField === "select_tenant") return {
|
|
801
|
+
type: "select_tenant",
|
|
802
|
+
state: typeof b.state === "string" ? b.state : "",
|
|
803
|
+
tenants: parseTenants(b.tenants)
|
|
804
|
+
};
|
|
805
|
+
if (status === 403 && b.userPasswordSet === true && typeof b.userId === "string") return {
|
|
806
|
+
type: "password_change_required",
|
|
807
|
+
userId: b.userId,
|
|
808
|
+
ticket: typeof b.ticket === "string" ? b.ticket : void 0
|
|
809
|
+
};
|
|
810
|
+
if (status === 403) return { type: "no_access" };
|
|
811
|
+
return { type: "invalid_credentials" };
|
|
812
|
+
}
|
|
813
|
+
function parseResetOutcome(status, body) {
|
|
814
|
+
if (status === 200) return "ok";
|
|
815
|
+
if (asRecord$1(body).status === "weak_password") return "weak_password";
|
|
816
|
+
return "invalid_or_expired";
|
|
817
|
+
}
|
|
749
818
|
//#endregion
|
|
750
819
|
//#region src/auth/normalize.ts
|
|
751
820
|
const FALLBACK_TTL_MS = 5 * 6e4;
|
|
@@ -771,7 +840,35 @@ function toTokenSet(accessToken, refreshToken, now) {
|
|
|
771
840
|
function asRecord(value) {
|
|
772
841
|
return value && typeof value === "object" ? value : {};
|
|
773
842
|
}
|
|
774
|
-
/**
|
|
843
|
+
/**
|
|
844
|
+
* Display-claim caps (ADR-0006). These values are user-controlled and unbounded in the
|
|
845
|
+
* platform's schema, and they ride in a sealed ~4KB cookie whose failure mode is the browser
|
|
846
|
+
* silently dropping it. Cap at the boundary rather than trusting the input.
|
|
847
|
+
*/
|
|
848
|
+
const MAX_DISPLAY_NAME = 128;
|
|
849
|
+
const MAX_AVATAR_URL = 512;
|
|
850
|
+
/** A non-blank string, trimmed and capped — otherwise undefined. The shim never guesses. */
|
|
851
|
+
function displayString(value, max) {
|
|
852
|
+
if (typeof value !== "string") return void 0;
|
|
853
|
+
const trimmed = value.trim();
|
|
854
|
+
if (trimmed.length === 0) return void 0;
|
|
855
|
+
return trimmed.slice(0, max);
|
|
856
|
+
}
|
|
857
|
+
/**
|
|
858
|
+
* The best human label the token response can give us: full name, else username, else email.
|
|
859
|
+
* Each part is checked independently so a user with only one of them never renders "undefined".
|
|
860
|
+
*/
|
|
861
|
+
function deriveDisplayName(user) {
|
|
862
|
+
return displayString([displayString(user.firstName, MAX_DISPLAY_NAME), displayString(user.lastName, MAX_DISPLAY_NAME)].filter(Boolean).join(" "), MAX_DISPLAY_NAME) ?? displayString(user.username, MAX_DISPLAY_NAME) ?? displayString(user.email, MAX_DISPLAY_NAME);
|
|
863
|
+
}
|
|
864
|
+
/**
|
|
865
|
+
* `POST /idp/token` → StoredSession. Claims carry identity plus the bounded display subset
|
|
866
|
+
* (ADR-0006); AUTHZ populates roles/permissions later.
|
|
867
|
+
*
|
|
868
|
+
* `organizationId` comes from the exchange's `tenantId`, never from the user document: a
|
|
869
|
+
* cross-membership login resolves a tenant that is deliberately *not* the user's home
|
|
870
|
+
* organization, and reading it off the user would silently sign them into the wrong one.
|
|
871
|
+
*/
|
|
775
872
|
function normalizeExchange(body, now) {
|
|
776
873
|
const data = asRecord(asRecord(body).data);
|
|
777
874
|
const user = asRecord(data.user);
|
|
@@ -781,7 +878,10 @@ function normalizeExchange(body, now) {
|
|
|
781
878
|
userId,
|
|
782
879
|
organizationId: data.tenantId != null ? String(data.tenantId) : void 0,
|
|
783
880
|
roles: [],
|
|
784
|
-
permissions: []
|
|
881
|
+
permissions: [],
|
|
882
|
+
displayName: deriveDisplayName(user),
|
|
883
|
+
email: displayString(user.email, MAX_DISPLAY_NAME),
|
|
884
|
+
avatarUrl: displayString(user.profilePicture, MAX_AVATAR_URL)
|
|
785
885
|
};
|
|
786
886
|
return {
|
|
787
887
|
tokens: toTokenSet(String(data.accessToken ?? ""), stringOrUndefined(data.refreshToken), now),
|
|
@@ -858,6 +958,86 @@ var AuthClient = class {
|
|
|
858
958
|
if (response.status >= 500) throw this.toError(response);
|
|
859
959
|
return parseAuthorizeOutcome(response.status, response.body);
|
|
860
960
|
}
|
|
961
|
+
/**
|
|
962
|
+
* The embedded `<SignIn/>` credential flow (D2b): POST `/idp/authorize` with
|
|
963
|
+
* email/password + PKCE. On `authorized` the caller exchanges the returned
|
|
964
|
+
* `code` (server-side, via the BFF) for a session — no redirect to a hosted
|
|
965
|
+
* login page. Creds live only for the duration of this request; the SDK never
|
|
966
|
+
* stores them. 5xx surfaces as a typed error; every other status is a typed
|
|
967
|
+
* outcome the UI branches on.
|
|
968
|
+
*/
|
|
969
|
+
async authorizeWithPassword(params) {
|
|
970
|
+
const response = await this.transport.request({
|
|
971
|
+
method: "POST",
|
|
972
|
+
url: this.config.baseUrl + ENDPOINTS.authorize,
|
|
973
|
+
body: {
|
|
974
|
+
response_type: "code",
|
|
975
|
+
client_id: this.config.key.raw,
|
|
976
|
+
redirect_uri: params.redirectUri,
|
|
977
|
+
state: params.state,
|
|
978
|
+
code_challenge: params.challenge,
|
|
979
|
+
code_challenge_method: "S256",
|
|
980
|
+
email: params.email,
|
|
981
|
+
password: params.password,
|
|
982
|
+
...params.organizationCode ? { organizationCode: params.organizationCode } : {},
|
|
983
|
+
...params.tenantId ? { tenantId: params.tenantId } : {}
|
|
984
|
+
}
|
|
985
|
+
});
|
|
986
|
+
if (response.status >= 500) throw this.toError(response);
|
|
987
|
+
return parsePasswordOutcome(response.status, response.body);
|
|
988
|
+
}
|
|
989
|
+
/**
|
|
990
|
+
* Set the real password for a user the platform has flagged `mustChangePassword`
|
|
991
|
+
* (the admin temp-password flow). Call this only after `authorizeWithPassword`
|
|
992
|
+
* has returned `password_change_required` for that same user — that response IS
|
|
993
|
+
* the proof the caller knows the temporary password.
|
|
994
|
+
*/
|
|
995
|
+
async setPassword(params) {
|
|
996
|
+
const response = await this.transport.request({
|
|
997
|
+
method: "PATCH",
|
|
998
|
+
url: this.config.baseUrl + ENDPOINTS.setPassword,
|
|
999
|
+
body: {
|
|
1000
|
+
userId: params.userId,
|
|
1001
|
+
newPassword: params.newPassword,
|
|
1002
|
+
confirmPassword: params.confirmPassword,
|
|
1003
|
+
...params.ticket ? { ticket: params.ticket } : {}
|
|
1004
|
+
}
|
|
1005
|
+
});
|
|
1006
|
+
if (response.status !== 200) throw this.toError(response);
|
|
1007
|
+
}
|
|
1008
|
+
/**
|
|
1009
|
+
* Start a server-mediated self-service reset (F040). POSTs the email + this app's
|
|
1010
|
+
* publishable key; the backend resolves the account, mints a single-use token, and
|
|
1011
|
+
* emails the link ITSELF — nothing comes back but a uniform 202. Resolves on success;
|
|
1012
|
+
* a 4xx (e.g. a misconfigured key) surfaces as a typed error for the developer.
|
|
1013
|
+
*/
|
|
1014
|
+
async requestPasswordReset(params) {
|
|
1015
|
+
const response = await this.transport.request({
|
|
1016
|
+
method: "POST",
|
|
1017
|
+
url: this.config.baseUrl + ENDPOINTS.forgotPassword,
|
|
1018
|
+
body: {
|
|
1019
|
+
email: params.email,
|
|
1020
|
+
client_id: this.config.key.raw
|
|
1021
|
+
}
|
|
1022
|
+
});
|
|
1023
|
+
if (response.status >= 400) throw this.toError(response);
|
|
1024
|
+
}
|
|
1025
|
+
/**
|
|
1026
|
+
* Redeem a reset token (the opaque value from the emailed link) single-use and set
|
|
1027
|
+
* the new password. Returns a typed outcome the UI branches on; only a 5xx throws.
|
|
1028
|
+
*/
|
|
1029
|
+
async resetPassword(params) {
|
|
1030
|
+
const response = await this.transport.request({
|
|
1031
|
+
method: "POST",
|
|
1032
|
+
url: this.config.baseUrl + ENDPOINTS.resetPassword,
|
|
1033
|
+
body: {
|
|
1034
|
+
token: params.token,
|
|
1035
|
+
newPassword: params.newPassword
|
|
1036
|
+
}
|
|
1037
|
+
});
|
|
1038
|
+
if (response.status >= 500) throw this.toError(response);
|
|
1039
|
+
return parseResetOutcome(response.status, response.body);
|
|
1040
|
+
}
|
|
861
1041
|
async exchangeCode(params) {
|
|
862
1042
|
const response = await this.transport.request({
|
|
863
1043
|
method: "POST",
|
|
@@ -873,6 +1053,27 @@ var AuthClient = class {
|
|
|
873
1053
|
if (response.status !== 200) throw this.toError(response);
|
|
874
1054
|
return normalizeExchange(response.body, this.clock.now());
|
|
875
1055
|
}
|
|
1056
|
+
/**
|
|
1057
|
+
* Fetch this app's login-page branding (public, pk_-scoped). NEVER throws and never
|
|
1058
|
+
* blocks the sign-in form: any non-200, malformed body, or network failure resolves
|
|
1059
|
+
* to an empty token set so the caller keeps its built-in defaults (FR-BRND-3).
|
|
1060
|
+
*/
|
|
1061
|
+
async fetchBranding(params = {}) {
|
|
1062
|
+
try {
|
|
1063
|
+
const url = new URL(this.config.baseUrl + ENDPOINTS.branding);
|
|
1064
|
+
url.searchParams.set("client_id", this.config.key.raw);
|
|
1065
|
+
if (params.organizationCode) url.searchParams.set("organizationCode", params.organizationCode);
|
|
1066
|
+
const response = await this.transport.request({
|
|
1067
|
+
method: "GET",
|
|
1068
|
+
url: url.toString()
|
|
1069
|
+
});
|
|
1070
|
+
if (response.status !== 200) return {};
|
|
1071
|
+
const tokens = response.body?.tokens;
|
|
1072
|
+
return tokens && typeof tokens === "object" ? tokens : {};
|
|
1073
|
+
} catch {
|
|
1074
|
+
return {};
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
876
1077
|
/** The `RefreshTokens` function the SessionEngine (F003) injects. */
|
|
877
1078
|
createRefresher() {
|
|
878
1079
|
return async (current) => {
|
|
@@ -885,6 +1086,11 @@ var AuthClient = class {
|
|
|
885
1086
|
return normalizeRefresh(response.body, current, this.clock.now());
|
|
886
1087
|
};
|
|
887
1088
|
}
|
|
1089
|
+
/**
|
|
1090
|
+
* Ecosystem logout — only revokes when the caller carries the browser's ecosystem
|
|
1091
|
+
* cookie (a top-level hop). From a server-to-server context it clears nothing and
|
|
1092
|
+
* still answers 200; use `revokeSession` there.
|
|
1093
|
+
*/
|
|
888
1094
|
async logout(scope = "device") {
|
|
889
1095
|
const response = await this.transport.request({
|
|
890
1096
|
method: "POST",
|
|
@@ -893,6 +1099,22 @@ var AuthClient = class {
|
|
|
893
1099
|
});
|
|
894
1100
|
if (response.status >= 400) throw this.toError(response);
|
|
895
1101
|
}
|
|
1102
|
+
/**
|
|
1103
|
+
* Revoke a session using the access token as proof — the only logout that works
|
|
1104
|
+
* server-side, where no browser cookie reaches poly-auth. `device` ends this session;
|
|
1105
|
+
* `everywhere` ends every session for the user (FR-SSO-5).
|
|
1106
|
+
*
|
|
1107
|
+
* The token goes in the Authorization header and nowhere else: repeating it in the
|
|
1108
|
+
* body would only widen where it can leak.
|
|
1109
|
+
*/
|
|
1110
|
+
async revokeSession(accessToken, scope = "device") {
|
|
1111
|
+
const response = await this.transport.request({
|
|
1112
|
+
method: "POST",
|
|
1113
|
+
url: this.config.baseUrl + (scope === "everywhere" ? ENDPOINTS.authLogoutAll : ENDPOINTS.authLogout),
|
|
1114
|
+
headers: { Authorization: `Bearer ${accessToken}` }
|
|
1115
|
+
});
|
|
1116
|
+
if (response.status >= 400) throw this.toError(response);
|
|
1117
|
+
}
|
|
896
1118
|
async resolveOrg(email) {
|
|
897
1119
|
const response = await this.transport.request({
|
|
898
1120
|
method: "POST",
|
|
@@ -922,4 +1144,4 @@ var AuthClient = class {
|
|
|
922
1144
|
const PACKAGE_NAME = "@poly-x/core";
|
|
923
1145
|
const version = "0.0.0";
|
|
924
1146
|
//#endregion
|
|
925
|
-
export { AuthClient, DEFAULT_RETRY_POLICY, DEFAULT_TIMEOUT_MS, ENDPOINTS, FetchTransport, ForbiddenError, INITIAL_STATE as INITIAL_SESSION_STATE, InMemoryLock, InMemoryPkceStore, InMemorySessionChannel, InMemorySessionStore, MockTransport, PACKAGE_NAME, PolyXConfigError, PolyXError, RateLimitedError, SessionEngine, SessionExpiredError, SessionRevokedError, SystemClock, UnauthenticatedError, UpstreamError, ValidationError, computeChallenge, createSessionEngine, decodeJwtExp, generatePkce, isTransientStatus, mapPlatformError, normalizeExchange, normalizeRefresh, parseAuthorizeOutcome, parsePolyXKey, randomState, redactSensitive, reduce as reduceSessionState, resolveConfig, version };
|
|
1147
|
+
export { AuthClient, DEFAULT_RETRY_POLICY, DEFAULT_TIMEOUT_MS, ENDPOINTS, FetchTransport, ForbiddenError, INITIAL_STATE as INITIAL_SESSION_STATE, InMemoryLock, InMemoryPkceStore, InMemorySessionChannel, InMemorySessionStore, MockTransport, PACKAGE_NAME, PolyXConfigError, PolyXError, RateLimitedError, SessionEngine, SessionExpiredError, SessionRevokedError, SystemClock, UnauthenticatedError, UpstreamError, ValidationError, computeChallenge, createSessionEngine, decodeJwtExp, generatePkce, isTransientStatus, mapPlatformError, normalizeExchange, normalizeRefresh, parseAuthorizeOutcome, parsePasswordOutcome, parsePolyXKey, parseResetOutcome, randomState, redactSensitive, reduce as reduceSessionState, resolveConfig, version };
|