@poly-x/core 0.1.0-alpha.0 → 0.1.0-alpha.2
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 +77 -4
- package/dist/index.d.cts +63 -1
- package/dist/index.d.mts +63 -1
- package/dist/index.mjs +77 -5
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -167,11 +167,21 @@ function resolveConfig(input) {
|
|
|
167
167
|
}
|
|
168
168
|
baseUrl = normalizeBaseUrl(input.baseUrl);
|
|
169
169
|
} else baseUrl = `https://${normalizeBaseUrl(key.host)}`;
|
|
170
|
+
let signInUrl;
|
|
171
|
+
if (input.signInUrl !== void 0) {
|
|
172
|
+
try {
|
|
173
|
+
new URL(input.signInUrl);
|
|
174
|
+
} catch {
|
|
175
|
+
throw new PolyXConfigError("The signInUrl is not a valid URL.", { code: "SIGN_IN_URL_INVALID" });
|
|
176
|
+
}
|
|
177
|
+
signInUrl = normalizeBaseUrl(input.signInUrl);
|
|
178
|
+
}
|
|
170
179
|
return {
|
|
171
180
|
key,
|
|
172
181
|
kind: key.kind,
|
|
173
182
|
env: key.env,
|
|
174
183
|
baseUrl,
|
|
184
|
+
signInUrl,
|
|
175
185
|
context
|
|
176
186
|
};
|
|
177
187
|
}
|
|
@@ -737,6 +747,27 @@ function parseAuthorizeOutcome(status, body) {
|
|
|
737
747
|
if (statusField === "login_required" || b.code === "LOGIN_REQUIRED") return { type: "login_required" };
|
|
738
748
|
return { type: "login_required" };
|
|
739
749
|
}
|
|
750
|
+
function parsePasswordOutcome(status, body) {
|
|
751
|
+
const b = asRecord$1(body);
|
|
752
|
+
const statusField = typeof b.status === "string" ? b.status : void 0;
|
|
753
|
+
if (statusField === "authorized" && typeof b.code === "string") return {
|
|
754
|
+
type: "authorized",
|
|
755
|
+
code: b.code,
|
|
756
|
+
state: typeof b.state === "string" ? b.state : "",
|
|
757
|
+
redirectUri: typeof b.redirectUri === "string" ? b.redirectUri : void 0
|
|
758
|
+
};
|
|
759
|
+
if (statusField === "select_tenant") return {
|
|
760
|
+
type: "select_tenant",
|
|
761
|
+
state: typeof b.state === "string" ? b.state : "",
|
|
762
|
+
tenants: parseTenants(b.tenants)
|
|
763
|
+
};
|
|
764
|
+
if (status === 403 && b.userPasswordSet === true && typeof b.userId === "string") return {
|
|
765
|
+
type: "password_change_required",
|
|
766
|
+
userId: b.userId
|
|
767
|
+
};
|
|
768
|
+
if (status === 403) return { type: "no_access" };
|
|
769
|
+
return { type: "invalid_credentials" };
|
|
770
|
+
}
|
|
740
771
|
//#endregion
|
|
741
772
|
//#region src/auth/normalize.ts
|
|
742
773
|
const FALLBACK_TTL_MS = 5 * 6e4;
|
|
@@ -815,10 +846,9 @@ var AuthClient = class {
|
|
|
815
846
|
this.transport = options.transport;
|
|
816
847
|
this.clock = options.clock ?? new SystemClock();
|
|
817
848
|
}
|
|
818
|
-
|
|
819
|
-
const url = new URL(this.config.baseUrl + ENDPOINTS.authorize);
|
|
849
|
+
appendAuthorizeParams(url, params) {
|
|
820
850
|
url.searchParams.set("response_type", "code");
|
|
821
|
-
url.searchParams.set("
|
|
851
|
+
url.searchParams.set("client_id", this.config.key.raw);
|
|
822
852
|
url.searchParams.set("redirect_uri", params.redirectUri);
|
|
823
853
|
url.searchParams.set("state", params.state);
|
|
824
854
|
url.searchParams.set("code_challenge", params.challenge);
|
|
@@ -827,6 +857,20 @@ var AuthClient = class {
|
|
|
827
857
|
if (params.prompt) url.searchParams.set("prompt", params.prompt);
|
|
828
858
|
return url.toString();
|
|
829
859
|
}
|
|
860
|
+
/** The API `GET /v1/idp/authorize` URL — used for the silent warm-hop probe. */
|
|
861
|
+
buildAuthorizeUrl(params) {
|
|
862
|
+
return this.appendAuthorizeParams(new URL(this.config.baseUrl + ENDPOINTS.authorize), params);
|
|
863
|
+
}
|
|
864
|
+
/**
|
|
865
|
+
* The URL the interactive sign-in popup/redirect opens. Targets the HOSTED login
|
|
866
|
+
* PAGE (`config.signInUrl`) — which renders the form, POSTs the API, and redirects
|
|
867
|
+
* to `redirect_uri` with the code. Falls back to the API authorize URL when no
|
|
868
|
+
* `signInUrl` is configured (deployments where the API host serves the page).
|
|
869
|
+
*/
|
|
870
|
+
buildSignInUrl(params) {
|
|
871
|
+
if (!this.config.signInUrl) return this.buildAuthorizeUrl(params);
|
|
872
|
+
return this.appendAuthorizeParams(new URL(this.config.signInUrl), params);
|
|
873
|
+
}
|
|
830
874
|
/** Probe the warm hop (GET /idp/authorize) and return the typed outcome. */
|
|
831
875
|
async completeAuthorize(params) {
|
|
832
876
|
const response = await this.transport.request({
|
|
@@ -836,6 +880,34 @@ var AuthClient = class {
|
|
|
836
880
|
if (response.status >= 500) throw this.toError(response);
|
|
837
881
|
return parseAuthorizeOutcome(response.status, response.body);
|
|
838
882
|
}
|
|
883
|
+
/**
|
|
884
|
+
* The embedded `<SignIn/>` credential flow (D2b): POST `/idp/authorize` with
|
|
885
|
+
* email/password + PKCE. On `authorized` the caller exchanges the returned
|
|
886
|
+
* `code` (server-side, via the BFF) for a session — no redirect to a hosted
|
|
887
|
+
* login page. Creds live only for the duration of this request; the SDK never
|
|
888
|
+
* stores them. 5xx surfaces as a typed error; every other status is a typed
|
|
889
|
+
* outcome the UI branches on.
|
|
890
|
+
*/
|
|
891
|
+
async authorizeWithPassword(params) {
|
|
892
|
+
const response = await this.transport.request({
|
|
893
|
+
method: "POST",
|
|
894
|
+
url: this.config.baseUrl + ENDPOINTS.authorize,
|
|
895
|
+
body: {
|
|
896
|
+
response_type: "code",
|
|
897
|
+
client_id: this.config.key.raw,
|
|
898
|
+
redirect_uri: params.redirectUri,
|
|
899
|
+
state: params.state,
|
|
900
|
+
code_challenge: params.challenge,
|
|
901
|
+
code_challenge_method: "S256",
|
|
902
|
+
email: params.email,
|
|
903
|
+
password: params.password,
|
|
904
|
+
...params.organizationCode ? { organizationCode: params.organizationCode } : {},
|
|
905
|
+
...params.tenantId ? { tenantId: params.tenantId } : {}
|
|
906
|
+
}
|
|
907
|
+
});
|
|
908
|
+
if (response.status >= 500) throw this.toError(response);
|
|
909
|
+
return parsePasswordOutcome(response.status, response.body);
|
|
910
|
+
}
|
|
839
911
|
async exchangeCode(params) {
|
|
840
912
|
const response = await this.transport.request({
|
|
841
913
|
method: "POST",
|
|
@@ -845,7 +917,7 @@ var AuthClient = class {
|
|
|
845
917
|
code: params.code,
|
|
846
918
|
code_verifier: params.codeVerifier,
|
|
847
919
|
redirect_uri: params.redirectUri,
|
|
848
|
-
|
|
920
|
+
client_id: this.config.key.raw
|
|
849
921
|
}
|
|
850
922
|
});
|
|
851
923
|
if (response.status !== 200) throw this.toError(response);
|
|
@@ -932,6 +1004,7 @@ exports.mapPlatformError = mapPlatformError;
|
|
|
932
1004
|
exports.normalizeExchange = normalizeExchange;
|
|
933
1005
|
exports.normalizeRefresh = normalizeRefresh;
|
|
934
1006
|
exports.parseAuthorizeOutcome = parseAuthorizeOutcome;
|
|
1007
|
+
exports.parsePasswordOutcome = parsePasswordOutcome;
|
|
935
1008
|
exports.parsePolyXKey = parsePolyXKey;
|
|
936
1009
|
exports.randomState = randomState;
|
|
937
1010
|
exports.redactSensitive = redactSensitive;
|
package/dist/index.d.cts
CHANGED
|
@@ -27,6 +27,14 @@ interface ResolveConfigInput {
|
|
|
27
27
|
key: string;
|
|
28
28
|
/** Wins over the key-embedded host. */
|
|
29
29
|
baseUrl?: string;
|
|
30
|
+
/**
|
|
31
|
+
* URL of the HOSTED sign-in page (the account portal that renders the login
|
|
32
|
+
* form) — distinct from the API `baseUrl` the key encodes. The interactive
|
|
33
|
+
* sign-in popup/redirect targets this; the silent warm-hop probe still hits the
|
|
34
|
+
* API. When unset, sign-in falls back to the API's `/v1/idp/authorize` (for
|
|
35
|
+
* deployments where the API host also serves the login page).
|
|
36
|
+
*/
|
|
37
|
+
signInUrl?: string;
|
|
30
38
|
/** Defaults to runtime auto-detection. */
|
|
31
39
|
context?: RuntimeContext;
|
|
32
40
|
}
|
|
@@ -35,6 +43,8 @@ interface ResolvedConfig {
|
|
|
35
43
|
kind: KeyKind;
|
|
36
44
|
env: KeyEnv;
|
|
37
45
|
baseUrl: string;
|
|
46
|
+
/** Hosted sign-in page URL (see ResolveConfigInput.signInUrl); undefined = use the API. */
|
|
47
|
+
signInUrl?: string;
|
|
38
48
|
context: RuntimeContext;
|
|
39
49
|
}
|
|
40
50
|
declare function resolveConfig(input: ResolveConfigInput): ResolvedConfig;
|
|
@@ -411,6 +421,30 @@ type AuthorizeOutcome = {
|
|
|
411
421
|
type: "login_required";
|
|
412
422
|
};
|
|
413
423
|
declare function parseAuthorizeOutcome(status: number, body: unknown): AuthorizeOutcome;
|
|
424
|
+
/**
|
|
425
|
+
* Outcome of a PASSWORD authorize (`POST /idp/authorize` with credentials) — the
|
|
426
|
+
* embedded `<SignIn/>` flow. Distinguishes invalid-credentials and the
|
|
427
|
+
* must-set-password step from the membership outcomes (the GET warm-hop parser
|
|
428
|
+
* collapses those into login_required).
|
|
429
|
+
*/
|
|
430
|
+
type PasswordAuthOutcome = {
|
|
431
|
+
type: "authorized";
|
|
432
|
+
code: string;
|
|
433
|
+
state: string;
|
|
434
|
+
redirectUri?: string;
|
|
435
|
+
} | {
|
|
436
|
+
type: "select_tenant";
|
|
437
|
+
state: string;
|
|
438
|
+
tenants: TenantOption[];
|
|
439
|
+
} | {
|
|
440
|
+
type: "invalid_credentials";
|
|
441
|
+
} | {
|
|
442
|
+
type: "password_change_required";
|
|
443
|
+
userId: string;
|
|
444
|
+
} | {
|
|
445
|
+
type: "no_access";
|
|
446
|
+
};
|
|
447
|
+
declare function parsePasswordOutcome(status: number, body: unknown): PasswordAuthOutcome;
|
|
414
448
|
//#endregion
|
|
415
449
|
//#region src/auth/normalize.d.ts
|
|
416
450
|
declare function decodeJwtExp(token: string): number | null;
|
|
@@ -462,14 +496,42 @@ interface ExchangeCodeParams {
|
|
|
462
496
|
codeVerifier: string;
|
|
463
497
|
redirectUri: string;
|
|
464
498
|
}
|
|
499
|
+
interface AuthorizeWithPasswordParams {
|
|
500
|
+
email: string;
|
|
501
|
+
password: string;
|
|
502
|
+
redirectUri: string;
|
|
503
|
+
state: string;
|
|
504
|
+
challenge: string;
|
|
505
|
+
organizationCode?: string;
|
|
506
|
+
/** Set on the re-attempt after the user picks a workspace (select_tenant). */
|
|
507
|
+
tenantId?: string;
|
|
508
|
+
}
|
|
465
509
|
declare class AuthClient {
|
|
466
510
|
private readonly config;
|
|
467
511
|
private readonly transport;
|
|
468
512
|
private readonly clock;
|
|
469
513
|
constructor(options: AuthClientOptions);
|
|
514
|
+
private appendAuthorizeParams;
|
|
515
|
+
/** The API `GET /v1/idp/authorize` URL — used for the silent warm-hop probe. */
|
|
470
516
|
buildAuthorizeUrl(params: BuildAuthorizeUrlParams): string;
|
|
517
|
+
/**
|
|
518
|
+
* The URL the interactive sign-in popup/redirect opens. Targets the HOSTED login
|
|
519
|
+
* PAGE (`config.signInUrl`) — which renders the form, POSTs the API, and redirects
|
|
520
|
+
* to `redirect_uri` with the code. Falls back to the API authorize URL when no
|
|
521
|
+
* `signInUrl` is configured (deployments where the API host serves the page).
|
|
522
|
+
*/
|
|
523
|
+
buildSignInUrl(params: BuildAuthorizeUrlParams): string;
|
|
471
524
|
/** Probe the warm hop (GET /idp/authorize) and return the typed outcome. */
|
|
472
525
|
completeAuthorize(params: BuildAuthorizeUrlParams): Promise<AuthorizeOutcome>;
|
|
526
|
+
/**
|
|
527
|
+
* The embedded `<SignIn/>` credential flow (D2b): POST `/idp/authorize` with
|
|
528
|
+
* email/password + PKCE. On `authorized` the caller exchanges the returned
|
|
529
|
+
* `code` (server-side, via the BFF) for a session — no redirect to a hosted
|
|
530
|
+
* login page. Creds live only for the duration of this request; the SDK never
|
|
531
|
+
* stores them. 5xx surfaces as a typed error; every other status is a typed
|
|
532
|
+
* outcome the UI branches on.
|
|
533
|
+
*/
|
|
534
|
+
authorizeWithPassword(params: AuthorizeWithPasswordParams): Promise<PasswordAuthOutcome>;
|
|
473
535
|
exchangeCode(params: ExchangeCodeParams): Promise<StoredSession>;
|
|
474
536
|
/** The `RefreshTokens` function the SessionEngine (F003) injects. */
|
|
475
537
|
createRefresher(): RefreshTokens;
|
|
@@ -488,4 +550,4 @@ declare class AuthClient {
|
|
|
488
550
|
declare const PACKAGE_NAME = "@poly-x/core";
|
|
489
551
|
declare const version = "0.0.0";
|
|
490
552
|
//#endregion
|
|
491
|
-
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 };
|
|
553
|
+
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 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, parsePasswordOutcome, parsePolyXKey, randomState, redactSensitive, reduce as reduceSessionState, resolveConfig, version };
|
package/dist/index.d.mts
CHANGED
|
@@ -27,6 +27,14 @@ interface ResolveConfigInput {
|
|
|
27
27
|
key: string;
|
|
28
28
|
/** Wins over the key-embedded host. */
|
|
29
29
|
baseUrl?: string;
|
|
30
|
+
/**
|
|
31
|
+
* URL of the HOSTED sign-in page (the account portal that renders the login
|
|
32
|
+
* form) — distinct from the API `baseUrl` the key encodes. The interactive
|
|
33
|
+
* sign-in popup/redirect targets this; the silent warm-hop probe still hits the
|
|
34
|
+
* API. When unset, sign-in falls back to the API's `/v1/idp/authorize` (for
|
|
35
|
+
* deployments where the API host also serves the login page).
|
|
36
|
+
*/
|
|
37
|
+
signInUrl?: string;
|
|
30
38
|
/** Defaults to runtime auto-detection. */
|
|
31
39
|
context?: RuntimeContext;
|
|
32
40
|
}
|
|
@@ -35,6 +43,8 @@ interface ResolvedConfig {
|
|
|
35
43
|
kind: KeyKind;
|
|
36
44
|
env: KeyEnv;
|
|
37
45
|
baseUrl: string;
|
|
46
|
+
/** Hosted sign-in page URL (see ResolveConfigInput.signInUrl); undefined = use the API. */
|
|
47
|
+
signInUrl?: string;
|
|
38
48
|
context: RuntimeContext;
|
|
39
49
|
}
|
|
40
50
|
declare function resolveConfig(input: ResolveConfigInput): ResolvedConfig;
|
|
@@ -411,6 +421,30 @@ type AuthorizeOutcome = {
|
|
|
411
421
|
type: "login_required";
|
|
412
422
|
};
|
|
413
423
|
declare function parseAuthorizeOutcome(status: number, body: unknown): AuthorizeOutcome;
|
|
424
|
+
/**
|
|
425
|
+
* Outcome of a PASSWORD authorize (`POST /idp/authorize` with credentials) — the
|
|
426
|
+
* embedded `<SignIn/>` flow. Distinguishes invalid-credentials and the
|
|
427
|
+
* must-set-password step from the membership outcomes (the GET warm-hop parser
|
|
428
|
+
* collapses those into login_required).
|
|
429
|
+
*/
|
|
430
|
+
type PasswordAuthOutcome = {
|
|
431
|
+
type: "authorized";
|
|
432
|
+
code: string;
|
|
433
|
+
state: string;
|
|
434
|
+
redirectUri?: string;
|
|
435
|
+
} | {
|
|
436
|
+
type: "select_tenant";
|
|
437
|
+
state: string;
|
|
438
|
+
tenants: TenantOption[];
|
|
439
|
+
} | {
|
|
440
|
+
type: "invalid_credentials";
|
|
441
|
+
} | {
|
|
442
|
+
type: "password_change_required";
|
|
443
|
+
userId: string;
|
|
444
|
+
} | {
|
|
445
|
+
type: "no_access";
|
|
446
|
+
};
|
|
447
|
+
declare function parsePasswordOutcome(status: number, body: unknown): PasswordAuthOutcome;
|
|
414
448
|
//#endregion
|
|
415
449
|
//#region src/auth/normalize.d.ts
|
|
416
450
|
declare function decodeJwtExp(token: string): number | null;
|
|
@@ -462,14 +496,42 @@ interface ExchangeCodeParams {
|
|
|
462
496
|
codeVerifier: string;
|
|
463
497
|
redirectUri: string;
|
|
464
498
|
}
|
|
499
|
+
interface AuthorizeWithPasswordParams {
|
|
500
|
+
email: string;
|
|
501
|
+
password: string;
|
|
502
|
+
redirectUri: string;
|
|
503
|
+
state: string;
|
|
504
|
+
challenge: string;
|
|
505
|
+
organizationCode?: string;
|
|
506
|
+
/** Set on the re-attempt after the user picks a workspace (select_tenant). */
|
|
507
|
+
tenantId?: string;
|
|
508
|
+
}
|
|
465
509
|
declare class AuthClient {
|
|
466
510
|
private readonly config;
|
|
467
511
|
private readonly transport;
|
|
468
512
|
private readonly clock;
|
|
469
513
|
constructor(options: AuthClientOptions);
|
|
514
|
+
private appendAuthorizeParams;
|
|
515
|
+
/** The API `GET /v1/idp/authorize` URL — used for the silent warm-hop probe. */
|
|
470
516
|
buildAuthorizeUrl(params: BuildAuthorizeUrlParams): string;
|
|
517
|
+
/**
|
|
518
|
+
* The URL the interactive sign-in popup/redirect opens. Targets the HOSTED login
|
|
519
|
+
* PAGE (`config.signInUrl`) — which renders the form, POSTs the API, and redirects
|
|
520
|
+
* to `redirect_uri` with the code. Falls back to the API authorize URL when no
|
|
521
|
+
* `signInUrl` is configured (deployments where the API host serves the page).
|
|
522
|
+
*/
|
|
523
|
+
buildSignInUrl(params: BuildAuthorizeUrlParams): string;
|
|
471
524
|
/** Probe the warm hop (GET /idp/authorize) and return the typed outcome. */
|
|
472
525
|
completeAuthorize(params: BuildAuthorizeUrlParams): Promise<AuthorizeOutcome>;
|
|
526
|
+
/**
|
|
527
|
+
* The embedded `<SignIn/>` credential flow (D2b): POST `/idp/authorize` with
|
|
528
|
+
* email/password + PKCE. On `authorized` the caller exchanges the returned
|
|
529
|
+
* `code` (server-side, via the BFF) for a session — no redirect to a hosted
|
|
530
|
+
* login page. Creds live only for the duration of this request; the SDK never
|
|
531
|
+
* stores them. 5xx surfaces as a typed error; every other status is a typed
|
|
532
|
+
* outcome the UI branches on.
|
|
533
|
+
*/
|
|
534
|
+
authorizeWithPassword(params: AuthorizeWithPasswordParams): Promise<PasswordAuthOutcome>;
|
|
473
535
|
exchangeCode(params: ExchangeCodeParams): Promise<StoredSession>;
|
|
474
536
|
/** The `RefreshTokens` function the SessionEngine (F003) injects. */
|
|
475
537
|
createRefresher(): RefreshTokens;
|
|
@@ -488,4 +550,4 @@ declare class AuthClient {
|
|
|
488
550
|
declare const PACKAGE_NAME = "@poly-x/core";
|
|
489
551
|
declare const version = "0.0.0";
|
|
490
552
|
//#endregion
|
|
491
|
-
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 };
|
|
553
|
+
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 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, parsePasswordOutcome, parsePolyXKey, randomState, redactSensitive, reduce as reduceSessionState, resolveConfig, version };
|
package/dist/index.mjs
CHANGED
|
@@ -166,11 +166,21 @@ function resolveConfig(input) {
|
|
|
166
166
|
}
|
|
167
167
|
baseUrl = normalizeBaseUrl(input.baseUrl);
|
|
168
168
|
} else baseUrl = `https://${normalizeBaseUrl(key.host)}`;
|
|
169
|
+
let signInUrl;
|
|
170
|
+
if (input.signInUrl !== void 0) {
|
|
171
|
+
try {
|
|
172
|
+
new URL(input.signInUrl);
|
|
173
|
+
} catch {
|
|
174
|
+
throw new PolyXConfigError("The signInUrl is not a valid URL.", { code: "SIGN_IN_URL_INVALID" });
|
|
175
|
+
}
|
|
176
|
+
signInUrl = normalizeBaseUrl(input.signInUrl);
|
|
177
|
+
}
|
|
169
178
|
return {
|
|
170
179
|
key,
|
|
171
180
|
kind: key.kind,
|
|
172
181
|
env: key.env,
|
|
173
182
|
baseUrl,
|
|
183
|
+
signInUrl,
|
|
174
184
|
context
|
|
175
185
|
};
|
|
176
186
|
}
|
|
@@ -736,6 +746,27 @@ function parseAuthorizeOutcome(status, body) {
|
|
|
736
746
|
if (statusField === "login_required" || b.code === "LOGIN_REQUIRED") return { type: "login_required" };
|
|
737
747
|
return { type: "login_required" };
|
|
738
748
|
}
|
|
749
|
+
function parsePasswordOutcome(status, body) {
|
|
750
|
+
const b = asRecord$1(body);
|
|
751
|
+
const statusField = typeof b.status === "string" ? b.status : void 0;
|
|
752
|
+
if (statusField === "authorized" && typeof b.code === "string") return {
|
|
753
|
+
type: "authorized",
|
|
754
|
+
code: b.code,
|
|
755
|
+
state: typeof b.state === "string" ? b.state : "",
|
|
756
|
+
redirectUri: typeof b.redirectUri === "string" ? b.redirectUri : void 0
|
|
757
|
+
};
|
|
758
|
+
if (statusField === "select_tenant") return {
|
|
759
|
+
type: "select_tenant",
|
|
760
|
+
state: typeof b.state === "string" ? b.state : "",
|
|
761
|
+
tenants: parseTenants(b.tenants)
|
|
762
|
+
};
|
|
763
|
+
if (status === 403 && b.userPasswordSet === true && typeof b.userId === "string") return {
|
|
764
|
+
type: "password_change_required",
|
|
765
|
+
userId: b.userId
|
|
766
|
+
};
|
|
767
|
+
if (status === 403) return { type: "no_access" };
|
|
768
|
+
return { type: "invalid_credentials" };
|
|
769
|
+
}
|
|
739
770
|
//#endregion
|
|
740
771
|
//#region src/auth/normalize.ts
|
|
741
772
|
const FALLBACK_TTL_MS = 5 * 6e4;
|
|
@@ -814,10 +845,9 @@ var AuthClient = class {
|
|
|
814
845
|
this.transport = options.transport;
|
|
815
846
|
this.clock = options.clock ?? new SystemClock();
|
|
816
847
|
}
|
|
817
|
-
|
|
818
|
-
const url = new URL(this.config.baseUrl + ENDPOINTS.authorize);
|
|
848
|
+
appendAuthorizeParams(url, params) {
|
|
819
849
|
url.searchParams.set("response_type", "code");
|
|
820
|
-
url.searchParams.set("
|
|
850
|
+
url.searchParams.set("client_id", this.config.key.raw);
|
|
821
851
|
url.searchParams.set("redirect_uri", params.redirectUri);
|
|
822
852
|
url.searchParams.set("state", params.state);
|
|
823
853
|
url.searchParams.set("code_challenge", params.challenge);
|
|
@@ -826,6 +856,20 @@ var AuthClient = class {
|
|
|
826
856
|
if (params.prompt) url.searchParams.set("prompt", params.prompt);
|
|
827
857
|
return url.toString();
|
|
828
858
|
}
|
|
859
|
+
/** The API `GET /v1/idp/authorize` URL — used for the silent warm-hop probe. */
|
|
860
|
+
buildAuthorizeUrl(params) {
|
|
861
|
+
return this.appendAuthorizeParams(new URL(this.config.baseUrl + ENDPOINTS.authorize), params);
|
|
862
|
+
}
|
|
863
|
+
/**
|
|
864
|
+
* The URL the interactive sign-in popup/redirect opens. Targets the HOSTED login
|
|
865
|
+
* PAGE (`config.signInUrl`) — which renders the form, POSTs the API, and redirects
|
|
866
|
+
* to `redirect_uri` with the code. Falls back to the API authorize URL when no
|
|
867
|
+
* `signInUrl` is configured (deployments where the API host serves the page).
|
|
868
|
+
*/
|
|
869
|
+
buildSignInUrl(params) {
|
|
870
|
+
if (!this.config.signInUrl) return this.buildAuthorizeUrl(params);
|
|
871
|
+
return this.appendAuthorizeParams(new URL(this.config.signInUrl), params);
|
|
872
|
+
}
|
|
829
873
|
/** Probe the warm hop (GET /idp/authorize) and return the typed outcome. */
|
|
830
874
|
async completeAuthorize(params) {
|
|
831
875
|
const response = await this.transport.request({
|
|
@@ -835,6 +879,34 @@ var AuthClient = class {
|
|
|
835
879
|
if (response.status >= 500) throw this.toError(response);
|
|
836
880
|
return parseAuthorizeOutcome(response.status, response.body);
|
|
837
881
|
}
|
|
882
|
+
/**
|
|
883
|
+
* The embedded `<SignIn/>` credential flow (D2b): POST `/idp/authorize` with
|
|
884
|
+
* email/password + PKCE. On `authorized` the caller exchanges the returned
|
|
885
|
+
* `code` (server-side, via the BFF) for a session — no redirect to a hosted
|
|
886
|
+
* login page. Creds live only for the duration of this request; the SDK never
|
|
887
|
+
* stores them. 5xx surfaces as a typed error; every other status is a typed
|
|
888
|
+
* outcome the UI branches on.
|
|
889
|
+
*/
|
|
890
|
+
async authorizeWithPassword(params) {
|
|
891
|
+
const response = await this.transport.request({
|
|
892
|
+
method: "POST",
|
|
893
|
+
url: this.config.baseUrl + ENDPOINTS.authorize,
|
|
894
|
+
body: {
|
|
895
|
+
response_type: "code",
|
|
896
|
+
client_id: this.config.key.raw,
|
|
897
|
+
redirect_uri: params.redirectUri,
|
|
898
|
+
state: params.state,
|
|
899
|
+
code_challenge: params.challenge,
|
|
900
|
+
code_challenge_method: "S256",
|
|
901
|
+
email: params.email,
|
|
902
|
+
password: params.password,
|
|
903
|
+
...params.organizationCode ? { organizationCode: params.organizationCode } : {},
|
|
904
|
+
...params.tenantId ? { tenantId: params.tenantId } : {}
|
|
905
|
+
}
|
|
906
|
+
});
|
|
907
|
+
if (response.status >= 500) throw this.toError(response);
|
|
908
|
+
return parsePasswordOutcome(response.status, response.body);
|
|
909
|
+
}
|
|
838
910
|
async exchangeCode(params) {
|
|
839
911
|
const response = await this.transport.request({
|
|
840
912
|
method: "POST",
|
|
@@ -844,7 +916,7 @@ var AuthClient = class {
|
|
|
844
916
|
code: params.code,
|
|
845
917
|
code_verifier: params.codeVerifier,
|
|
846
918
|
redirect_uri: params.redirectUri,
|
|
847
|
-
|
|
919
|
+
client_id: this.config.key.raw
|
|
848
920
|
}
|
|
849
921
|
});
|
|
850
922
|
if (response.status !== 200) throw this.toError(response);
|
|
@@ -899,4 +971,4 @@ var AuthClient = class {
|
|
|
899
971
|
const PACKAGE_NAME = "@poly-x/core";
|
|
900
972
|
const version = "0.0.0";
|
|
901
973
|
//#endregion
|
|
902
|
-
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 };
|
|
974
|
+
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, randomState, redactSensitive, reduce as reduceSessionState, resolveConfig, version };
|