@poly-x/core 0.1.0-alpha.8 → 0.1.0
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 +107 -4
- package/dist/index.d.cts +83 -2
- package/dist/index.d.mts +83 -2
- package/dist/index.mjs +107 -4
- 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,7 +717,21 @@ 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",
|
|
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",
|
|
712
735
|
orgResolve: "/v1/idp/org-resolve",
|
|
713
736
|
/**
|
|
714
737
|
* Completes the admin temp-password flow: the user signed in with a temporary
|
|
@@ -722,7 +745,13 @@ const ENDPOINTS = {
|
|
|
722
745
|
* `resetPassword` redeems that opaque token single-use and sets the new password.
|
|
723
746
|
*/
|
|
724
747
|
forgotPassword: "/v1/idp/forgot-password",
|
|
725
|
-
resetPassword: "/v1/idp/reset-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"
|
|
726
755
|
};
|
|
727
756
|
//#endregion
|
|
728
757
|
//#region src/auth/outcomes.ts
|
|
@@ -812,7 +841,35 @@ function toTokenSet(accessToken, refreshToken, now) {
|
|
|
812
841
|
function asRecord(value) {
|
|
813
842
|
return value && typeof value === "object" ? value : {};
|
|
814
843
|
}
|
|
815
|
-
/**
|
|
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
|
+
*/
|
|
816
873
|
function normalizeExchange(body, now) {
|
|
817
874
|
const data = asRecord(asRecord(body).data);
|
|
818
875
|
const user = asRecord(data.user);
|
|
@@ -822,7 +879,10 @@ function normalizeExchange(body, now) {
|
|
|
822
879
|
userId,
|
|
823
880
|
organizationId: data.tenantId != null ? String(data.tenantId) : void 0,
|
|
824
881
|
roles: [],
|
|
825
|
-
permissions: []
|
|
882
|
+
permissions: [],
|
|
883
|
+
displayName: deriveDisplayName(user),
|
|
884
|
+
email: displayString(user.email, MAX_DISPLAY_NAME),
|
|
885
|
+
avatarUrl: displayString(user.profilePicture, MAX_AVATAR_URL)
|
|
826
886
|
};
|
|
827
887
|
return {
|
|
828
888
|
tokens: toTokenSet(String(data.accessToken ?? ""), stringOrUndefined(data.refreshToken), now),
|
|
@@ -983,6 +1043,7 @@ var AuthClient = class {
|
|
|
983
1043
|
const response = await this.transport.request({
|
|
984
1044
|
method: "POST",
|
|
985
1045
|
url: this.config.baseUrl + ENDPOINTS.token,
|
|
1046
|
+
...params.forwardedHeaders ? { headers: params.forwardedHeaders } : {},
|
|
986
1047
|
body: {
|
|
987
1048
|
grant_type: "authorization_code",
|
|
988
1049
|
code: params.code,
|
|
@@ -994,6 +1055,27 @@ var AuthClient = class {
|
|
|
994
1055
|
if (response.status !== 200) throw this.toError(response);
|
|
995
1056
|
return normalizeExchange(response.body, this.clock.now());
|
|
996
1057
|
}
|
|
1058
|
+
/**
|
|
1059
|
+
* Fetch this app's login-page branding (public, pk_-scoped). NEVER throws and never
|
|
1060
|
+
* blocks the sign-in form: any non-200, malformed body, or network failure resolves
|
|
1061
|
+
* to an empty token set so the caller keeps its built-in defaults (FR-BRND-3).
|
|
1062
|
+
*/
|
|
1063
|
+
async fetchBranding(params = {}) {
|
|
1064
|
+
try {
|
|
1065
|
+
const url = new URL(this.config.baseUrl + ENDPOINTS.branding);
|
|
1066
|
+
url.searchParams.set("client_id", this.config.key.raw);
|
|
1067
|
+
if (params.organizationCode) url.searchParams.set("organizationCode", params.organizationCode);
|
|
1068
|
+
const response = await this.transport.request({
|
|
1069
|
+
method: "GET",
|
|
1070
|
+
url: url.toString()
|
|
1071
|
+
});
|
|
1072
|
+
if (response.status !== 200) return {};
|
|
1073
|
+
const tokens = response.body?.tokens;
|
|
1074
|
+
return tokens && typeof tokens === "object" ? tokens : {};
|
|
1075
|
+
} catch {
|
|
1076
|
+
return {};
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
997
1079
|
/** The `RefreshTokens` function the SessionEngine (F003) injects. */
|
|
998
1080
|
createRefresher() {
|
|
999
1081
|
return async (current) => {
|
|
@@ -1006,6 +1088,11 @@ var AuthClient = class {
|
|
|
1006
1088
|
return normalizeRefresh(response.body, current, this.clock.now());
|
|
1007
1089
|
};
|
|
1008
1090
|
}
|
|
1091
|
+
/**
|
|
1092
|
+
* Ecosystem logout — only revokes when the caller carries the browser's ecosystem
|
|
1093
|
+
* cookie (a top-level hop). From a server-to-server context it clears nothing and
|
|
1094
|
+
* still answers 200; use `revokeSession` there.
|
|
1095
|
+
*/
|
|
1009
1096
|
async logout(scope = "device") {
|
|
1010
1097
|
const response = await this.transport.request({
|
|
1011
1098
|
method: "POST",
|
|
@@ -1014,6 +1101,22 @@ var AuthClient = class {
|
|
|
1014
1101
|
});
|
|
1015
1102
|
if (response.status >= 400) throw this.toError(response);
|
|
1016
1103
|
}
|
|
1104
|
+
/**
|
|
1105
|
+
* Revoke a session using the access token as proof — the only logout that works
|
|
1106
|
+
* server-side, where no browser cookie reaches poly-auth. `device` ends this session;
|
|
1107
|
+
* `everywhere` ends every session for the user (FR-SSO-5).
|
|
1108
|
+
*
|
|
1109
|
+
* The token goes in the Authorization header and nowhere else: repeating it in the
|
|
1110
|
+
* body would only widen where it can leak.
|
|
1111
|
+
*/
|
|
1112
|
+
async revokeSession(accessToken, scope = "device") {
|
|
1113
|
+
const response = await this.transport.request({
|
|
1114
|
+
method: "POST",
|
|
1115
|
+
url: this.config.baseUrl + (scope === "everywhere" ? ENDPOINTS.authLogoutAll : ENDPOINTS.authLogout),
|
|
1116
|
+
headers: { Authorization: `Bearer ${accessToken}` }
|
|
1117
|
+
});
|
|
1118
|
+
if (response.status >= 400) throw this.toError(response);
|
|
1119
|
+
}
|
|
1017
1120
|
async resolveOrg(email) {
|
|
1018
1121
|
const response = await this.transport.request({
|
|
1019
1122
|
method: "POST",
|
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,7 +403,21 @@ 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";
|
|
395
422
|
/**
|
|
396
423
|
* Completes the admin temp-password flow: the user signed in with a temporary
|
|
@@ -405,6 +432,12 @@ declare const ENDPOINTS: {
|
|
|
405
432
|
*/
|
|
406
433
|
readonly forgotPassword: "/v1/idp/forgot-password";
|
|
407
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";
|
|
408
441
|
};
|
|
409
442
|
//#endregion
|
|
410
443
|
//#region src/auth/outcomes.d.ts
|
|
@@ -470,7 +503,14 @@ declare function parseResetOutcome(status: number, body: unknown): PasswordReset
|
|
|
470
503
|
//#endregion
|
|
471
504
|
//#region src/auth/normalize.d.ts
|
|
472
505
|
declare function decodeJwtExp(token: string): number | null;
|
|
473
|
-
/**
|
|
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
|
+
*/
|
|
474
514
|
declare function normalizeExchange(body: unknown, now: number): StoredSession;
|
|
475
515
|
/** `POST /auth/refresh-token` → StoredSession. No user in the response, so claims carry forward. */
|
|
476
516
|
declare function normalizeRefresh(body: unknown, current: StoredSession, now: number): StoredSession;
|
|
@@ -517,6 +557,16 @@ interface ExchangeCodeParams {
|
|
|
517
557
|
code: string;
|
|
518
558
|
codeVerifier: string;
|
|
519
559
|
redirectUri: string;
|
|
560
|
+
/**
|
|
561
|
+
* Browser context to attach to the token exchange (the call that creates the
|
|
562
|
+
* session). In a BFF deployment this request originates from Node, so without
|
|
563
|
+
* these the platform records the server's user-agent/IP ("node"/Unknown) instead
|
|
564
|
+
* of the end user's device. The BFF forwards a curated allowlist of the browser's
|
|
565
|
+
* request headers (user-agent, accept-language, the sec-ch-ua* client hints, and
|
|
566
|
+
* the observed client IP as x-forwarded-for). Optional and additive — omitting it
|
|
567
|
+
* preserves the prior behavior.
|
|
568
|
+
*/
|
|
569
|
+
forwardedHeaders?: Record<string, string>;
|
|
520
570
|
}
|
|
521
571
|
interface SetPasswordParams {
|
|
522
572
|
userId: string;
|
|
@@ -539,6 +589,17 @@ interface AuthorizeWithPasswordParams {
|
|
|
539
589
|
/** Set on the re-attempt after the user picks a workspace (select_tenant). */
|
|
540
590
|
tenantId?: string;
|
|
541
591
|
}
|
|
592
|
+
/** The bounded login-page branding token set (FR-BRND-1). Every field is optional. */
|
|
593
|
+
interface BrandingTokens {
|
|
594
|
+
displayName?: string;
|
|
595
|
+
logoUrl?: string;
|
|
596
|
+
primaryColor?: string;
|
|
597
|
+
backgroundColor?: string;
|
|
598
|
+
}
|
|
599
|
+
interface FetchBrandingParams {
|
|
600
|
+
/** Refine app-level branding with an org's when the org is already known. */
|
|
601
|
+
organizationCode?: string;
|
|
602
|
+
}
|
|
542
603
|
declare class AuthClient {
|
|
543
604
|
private readonly config;
|
|
544
605
|
private readonly transport;
|
|
@@ -590,9 +651,29 @@ declare class AuthClient {
|
|
|
590
651
|
newPassword: string;
|
|
591
652
|
}): Promise<PasswordResetOutcome>;
|
|
592
653
|
exchangeCode(params: ExchangeCodeParams): Promise<StoredSession>;
|
|
654
|
+
/**
|
|
655
|
+
* Fetch this app's login-page branding (public, pk_-scoped). NEVER throws and never
|
|
656
|
+
* blocks the sign-in form: any non-200, malformed body, or network failure resolves
|
|
657
|
+
* to an empty token set so the caller keeps its built-in defaults (FR-BRND-3).
|
|
658
|
+
*/
|
|
659
|
+
fetchBranding(params?: FetchBrandingParams): Promise<BrandingTokens>;
|
|
593
660
|
/** The `RefreshTokens` function the SessionEngine (F003) injects. */
|
|
594
661
|
createRefresher(): RefreshTokens;
|
|
662
|
+
/**
|
|
663
|
+
* Ecosystem logout — only revokes when the caller carries the browser's ecosystem
|
|
664
|
+
* cookie (a top-level hop). From a server-to-server context it clears nothing and
|
|
665
|
+
* still answers 200; use `revokeSession` there.
|
|
666
|
+
*/
|
|
595
667
|
logout(scope?: "device" | "everywhere"): Promise<void>;
|
|
668
|
+
/**
|
|
669
|
+
* Revoke a session using the access token as proof — the only logout that works
|
|
670
|
+
* server-side, where no browser cookie reaches poly-auth. `device` ends this session;
|
|
671
|
+
* `everywhere` ends every session for the user (FR-SSO-5).
|
|
672
|
+
*
|
|
673
|
+
* The token goes in the Authorization header and nowhere else: repeating it in the
|
|
674
|
+
* body would only widen where it can leak.
|
|
675
|
+
*/
|
|
676
|
+
revokeSession(accessToken: string, scope?: "device" | "everywhere"): Promise<void>;
|
|
596
677
|
resolveOrg(email: string): Promise<{
|
|
597
678
|
resolved: boolean;
|
|
598
679
|
organizationCode?: string;
|
|
@@ -607,4 +688,4 @@ declare class AuthClient {
|
|
|
607
688
|
declare const PACKAGE_NAME = "@poly-x/core";
|
|
608
689
|
declare const version = "0.0.0";
|
|
609
690
|
//#endregion
|
|
610
|
-
export { AuthClient, type AuthClientOptions, type AuthorizeOutcome, type AuthorizeWithPasswordParams, 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 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 };
|
|
691
|
+
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,7 +403,21 @@ 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";
|
|
395
422
|
/**
|
|
396
423
|
* Completes the admin temp-password flow: the user signed in with a temporary
|
|
@@ -405,6 +432,12 @@ declare const ENDPOINTS: {
|
|
|
405
432
|
*/
|
|
406
433
|
readonly forgotPassword: "/v1/idp/forgot-password";
|
|
407
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";
|
|
408
441
|
};
|
|
409
442
|
//#endregion
|
|
410
443
|
//#region src/auth/outcomes.d.ts
|
|
@@ -470,7 +503,14 @@ declare function parseResetOutcome(status: number, body: unknown): PasswordReset
|
|
|
470
503
|
//#endregion
|
|
471
504
|
//#region src/auth/normalize.d.ts
|
|
472
505
|
declare function decodeJwtExp(token: string): number | null;
|
|
473
|
-
/**
|
|
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
|
+
*/
|
|
474
514
|
declare function normalizeExchange(body: unknown, now: number): StoredSession;
|
|
475
515
|
/** `POST /auth/refresh-token` → StoredSession. No user in the response, so claims carry forward. */
|
|
476
516
|
declare function normalizeRefresh(body: unknown, current: StoredSession, now: number): StoredSession;
|
|
@@ -517,6 +557,16 @@ interface ExchangeCodeParams {
|
|
|
517
557
|
code: string;
|
|
518
558
|
codeVerifier: string;
|
|
519
559
|
redirectUri: string;
|
|
560
|
+
/**
|
|
561
|
+
* Browser context to attach to the token exchange (the call that creates the
|
|
562
|
+
* session). In a BFF deployment this request originates from Node, so without
|
|
563
|
+
* these the platform records the server's user-agent/IP ("node"/Unknown) instead
|
|
564
|
+
* of the end user's device. The BFF forwards a curated allowlist of the browser's
|
|
565
|
+
* request headers (user-agent, accept-language, the sec-ch-ua* client hints, and
|
|
566
|
+
* the observed client IP as x-forwarded-for). Optional and additive — omitting it
|
|
567
|
+
* preserves the prior behavior.
|
|
568
|
+
*/
|
|
569
|
+
forwardedHeaders?: Record<string, string>;
|
|
520
570
|
}
|
|
521
571
|
interface SetPasswordParams {
|
|
522
572
|
userId: string;
|
|
@@ -539,6 +589,17 @@ interface AuthorizeWithPasswordParams {
|
|
|
539
589
|
/** Set on the re-attempt after the user picks a workspace (select_tenant). */
|
|
540
590
|
tenantId?: string;
|
|
541
591
|
}
|
|
592
|
+
/** The bounded login-page branding token set (FR-BRND-1). Every field is optional. */
|
|
593
|
+
interface BrandingTokens {
|
|
594
|
+
displayName?: string;
|
|
595
|
+
logoUrl?: string;
|
|
596
|
+
primaryColor?: string;
|
|
597
|
+
backgroundColor?: string;
|
|
598
|
+
}
|
|
599
|
+
interface FetchBrandingParams {
|
|
600
|
+
/** Refine app-level branding with an org's when the org is already known. */
|
|
601
|
+
organizationCode?: string;
|
|
602
|
+
}
|
|
542
603
|
declare class AuthClient {
|
|
543
604
|
private readonly config;
|
|
544
605
|
private readonly transport;
|
|
@@ -590,9 +651,29 @@ declare class AuthClient {
|
|
|
590
651
|
newPassword: string;
|
|
591
652
|
}): Promise<PasswordResetOutcome>;
|
|
592
653
|
exchangeCode(params: ExchangeCodeParams): Promise<StoredSession>;
|
|
654
|
+
/**
|
|
655
|
+
* Fetch this app's login-page branding (public, pk_-scoped). NEVER throws and never
|
|
656
|
+
* blocks the sign-in form: any non-200, malformed body, or network failure resolves
|
|
657
|
+
* to an empty token set so the caller keeps its built-in defaults (FR-BRND-3).
|
|
658
|
+
*/
|
|
659
|
+
fetchBranding(params?: FetchBrandingParams): Promise<BrandingTokens>;
|
|
593
660
|
/** The `RefreshTokens` function the SessionEngine (F003) injects. */
|
|
594
661
|
createRefresher(): RefreshTokens;
|
|
662
|
+
/**
|
|
663
|
+
* Ecosystem logout — only revokes when the caller carries the browser's ecosystem
|
|
664
|
+
* cookie (a top-level hop). From a server-to-server context it clears nothing and
|
|
665
|
+
* still answers 200; use `revokeSession` there.
|
|
666
|
+
*/
|
|
595
667
|
logout(scope?: "device" | "everywhere"): Promise<void>;
|
|
668
|
+
/**
|
|
669
|
+
* Revoke a session using the access token as proof — the only logout that works
|
|
670
|
+
* server-side, where no browser cookie reaches poly-auth. `device` ends this session;
|
|
671
|
+
* `everywhere` ends every session for the user (FR-SSO-5).
|
|
672
|
+
*
|
|
673
|
+
* The token goes in the Authorization header and nowhere else: repeating it in the
|
|
674
|
+
* body would only widen where it can leak.
|
|
675
|
+
*/
|
|
676
|
+
revokeSession(accessToken: string, scope?: "device" | "everywhere"): Promise<void>;
|
|
596
677
|
resolveOrg(email: string): Promise<{
|
|
597
678
|
resolved: boolean;
|
|
598
679
|
organizationCode?: string;
|
|
@@ -607,4 +688,4 @@ declare class AuthClient {
|
|
|
607
688
|
declare const PACKAGE_NAME = "@poly-x/core";
|
|
608
689
|
declare const version = "0.0.0";
|
|
609
690
|
//#endregion
|
|
610
|
-
export { AuthClient, type AuthClientOptions, type AuthorizeOutcome, type AuthorizeWithPasswordParams, 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 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 };
|
|
691
|
+
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,7 +716,21 @@ 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",
|
|
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",
|
|
711
734
|
orgResolve: "/v1/idp/org-resolve",
|
|
712
735
|
/**
|
|
713
736
|
* Completes the admin temp-password flow: the user signed in with a temporary
|
|
@@ -721,7 +744,13 @@ const ENDPOINTS = {
|
|
|
721
744
|
* `resetPassword` redeems that opaque token single-use and sets the new password.
|
|
722
745
|
*/
|
|
723
746
|
forgotPassword: "/v1/idp/forgot-password",
|
|
724
|
-
resetPassword: "/v1/idp/reset-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"
|
|
725
754
|
};
|
|
726
755
|
//#endregion
|
|
727
756
|
//#region src/auth/outcomes.ts
|
|
@@ -811,7 +840,35 @@ function toTokenSet(accessToken, refreshToken, now) {
|
|
|
811
840
|
function asRecord(value) {
|
|
812
841
|
return value && typeof value === "object" ? value : {};
|
|
813
842
|
}
|
|
814
|
-
/**
|
|
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
|
+
*/
|
|
815
872
|
function normalizeExchange(body, now) {
|
|
816
873
|
const data = asRecord(asRecord(body).data);
|
|
817
874
|
const user = asRecord(data.user);
|
|
@@ -821,7 +878,10 @@ function normalizeExchange(body, now) {
|
|
|
821
878
|
userId,
|
|
822
879
|
organizationId: data.tenantId != null ? String(data.tenantId) : void 0,
|
|
823
880
|
roles: [],
|
|
824
|
-
permissions: []
|
|
881
|
+
permissions: [],
|
|
882
|
+
displayName: deriveDisplayName(user),
|
|
883
|
+
email: displayString(user.email, MAX_DISPLAY_NAME),
|
|
884
|
+
avatarUrl: displayString(user.profilePicture, MAX_AVATAR_URL)
|
|
825
885
|
};
|
|
826
886
|
return {
|
|
827
887
|
tokens: toTokenSet(String(data.accessToken ?? ""), stringOrUndefined(data.refreshToken), now),
|
|
@@ -982,6 +1042,7 @@ var AuthClient = class {
|
|
|
982
1042
|
const response = await this.transport.request({
|
|
983
1043
|
method: "POST",
|
|
984
1044
|
url: this.config.baseUrl + ENDPOINTS.token,
|
|
1045
|
+
...params.forwardedHeaders ? { headers: params.forwardedHeaders } : {},
|
|
985
1046
|
body: {
|
|
986
1047
|
grant_type: "authorization_code",
|
|
987
1048
|
code: params.code,
|
|
@@ -993,6 +1054,27 @@ var AuthClient = class {
|
|
|
993
1054
|
if (response.status !== 200) throw this.toError(response);
|
|
994
1055
|
return normalizeExchange(response.body, this.clock.now());
|
|
995
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
|
+
}
|
|
996
1078
|
/** The `RefreshTokens` function the SessionEngine (F003) injects. */
|
|
997
1079
|
createRefresher() {
|
|
998
1080
|
return async (current) => {
|
|
@@ -1005,6 +1087,11 @@ var AuthClient = class {
|
|
|
1005
1087
|
return normalizeRefresh(response.body, current, this.clock.now());
|
|
1006
1088
|
};
|
|
1007
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
|
+
*/
|
|
1008
1095
|
async logout(scope = "device") {
|
|
1009
1096
|
const response = await this.transport.request({
|
|
1010
1097
|
method: "POST",
|
|
@@ -1013,6 +1100,22 @@ var AuthClient = class {
|
|
|
1013
1100
|
});
|
|
1014
1101
|
if (response.status >= 400) throw this.toError(response);
|
|
1015
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
|
+
}
|
|
1016
1119
|
async resolveOrg(email) {
|
|
1017
1120
|
const response = await this.transport.request({
|
|
1018
1121
|
method: "POST",
|