@cboxdk/id-js 0.17.0 → 0.19.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/README.md +315 -3
- package/dist/{chunk-WYTGLCMF.js → chunk-2E25IPBA.js} +4 -3
- package/dist/chunk-2E25IPBA.js.map +1 -0
- package/dist/chunk-QQW63PGU.js +25 -0
- package/dist/chunk-QQW63PGU.js.map +1 -0
- package/dist/{chunk-ASOQN47M.js → chunk-SEUD4TE4.js} +3 -23
- package/dist/chunk-SEUD4TE4.js.map +1 -0
- package/dist/{chunk-G3CD7X4U.js → chunk-YGLMLIKL.js} +197 -9
- package/dist/chunk-YGLMLIKL.js.map +1 -0
- package/dist/{client-CdwdigzG.d.ts → client-BT-jOm0p.d.cts} +154 -2
- package/dist/{client-CEXddiCR.d.cts → client-xlEJalu8.d.ts} +154 -2
- package/dist/errors-D61xljZE.d.cts +87 -0
- package/dist/errors-D61xljZE.d.ts +87 -0
- package/dist/index.cjs +267 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +84 -92
- package/dist/index.d.ts +84 -92
- package/dist/index.js +66 -13
- package/dist/index.js.map +1 -1
- package/dist/management.cjs +5092 -0
- package/dist/management.cjs.map +1 -0
- package/dist/management.d.cts +13454 -0
- package/dist/management.d.ts +13454 -0
- package/dist/management.js +5054 -0
- package/dist/management.js.map +1 -0
- package/dist/nextjs.cjs +136 -5
- package/dist/nextjs.cjs.map +1 -1
- package/dist/nextjs.d.cts +3 -2
- package/dist/nextjs.d.ts +3 -2
- package/dist/nextjs.js +4 -3
- package/dist/nextjs.js.map +1 -1
- package/dist/server.d.cts +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/server.js +3 -2
- package/dist/{types-BYZldock.d.cts → types-CC1bz68x.d.cts} +30 -1
- package/dist/{types-BYZldock.d.ts → types-CC1bz68x.d.ts} +30 -1
- package/package.json +9 -2
- package/dist/chunk-ASOQN47M.js.map +0 -1
- package/dist/chunk-G3CD7X4U.js.map +0 -1
- package/dist/chunk-WYTGLCMF.js.map +0 -1
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export {
|
|
1
|
+
export { A as AuthenticationError, C as CboxIdError, a as ConfigurationError, F as FrontendApiError, I as InvalidStateError } from './errors-D61xljZE.cjs';
|
|
2
|
+
export { a as CallbackParams, C as CboxIdClient, P as PipeConnectOptions, b as PipeLeaseDeniedError, c as PipeLeaseError, d as PipeNotConnectedError, e as PipeProvider, f as PipeReauthorizationRequiredError, g as PipeTemporarilyUnavailableError, h as PipeToken, S as StoreSecretInput, i as StoredAuthState, V as VaultClient, j as VaultLease, k as VaultSecretRef, l as VerifyStandardWebhookOptions, m as VerifyWebhookOptions, W as WebhookHeaders, p as pipeConnectUrl, v as verifyStandardWebhook, n as verifyWebhook, w as withConnectReturn } from './client-BT-jOm0p.cjs';
|
|
3
|
+
export { c as AuthorizationPrompt, b as AuthorizationRequest, A as AuthorizationRequestOptions, d as CboxActiveOrganization, e as CboxActor, a as CboxIdConfig, f as CboxOrganization, C as CboxUser, g as ClaimSource, D as DeviceAuthorization, h as DiscoveryDocument, F as FEATURE_FLAGS_SCOPE, O as OrganizationRole, R as RefreshedTokens, i as TokenResponse, T as TokenTypeHint, j as actor, k as featureFlags, l as hasFeature, m as hasPermission, n as hasRole, o as isSupportSession, p as organization, q as permissions, r as roles, s as sessionId } from './types-CC1bz68x.cjs';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* The handler on YOUR side of a legacy migration.
|
|
@@ -129,9 +130,35 @@ interface FrontendConfig {
|
|
|
129
130
|
end_session: string;
|
|
130
131
|
jwks: string;
|
|
131
132
|
};
|
|
133
|
+
/**
|
|
134
|
+
* The social buttons to draw, exactly as the hosted sign-in page would: the environment's
|
|
135
|
+
* own providers, or — with `config({ organization })` — that organization's.
|
|
136
|
+
*/
|
|
132
137
|
social: SocialProvider[];
|
|
133
138
|
appearance?: Appearance;
|
|
139
|
+
/**
|
|
140
|
+
* Which other sign-in methods are switched on, so a box does not draw a button whose
|
|
141
|
+
* endpoint answers 403. Absent from instances that predate it.
|
|
142
|
+
*/
|
|
143
|
+
methods?: {
|
|
144
|
+
passkeys: boolean;
|
|
145
|
+
magic_link: boolean;
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
/** Options for {@link CboxIdFrontend.config}. */
|
|
149
|
+
interface FrontendConfigOptions {
|
|
150
|
+
/**
|
|
151
|
+
* The organization the box is for, by id or slug — the way a hosted page is addressed.
|
|
152
|
+
* `social` is then exactly what that organization's sign-in page offers. An organization
|
|
153
|
+
* the environment does not have is answered as no hint at all.
|
|
154
|
+
*/
|
|
155
|
+
organization?: string;
|
|
134
156
|
}
|
|
157
|
+
/**
|
|
158
|
+
* A second factor the person can answer an `mfa_required` with. Kinds only — the server
|
|
159
|
+
* never says which number a text would go to before one is sent.
|
|
160
|
+
*/
|
|
161
|
+
type SecondFactorKind = 'totp' | 'sms' | 'recovery_code';
|
|
135
162
|
/** What a sign-in attempt produced. */
|
|
136
163
|
type SignInResult = {
|
|
137
164
|
status: 'ok';
|
|
@@ -140,6 +167,11 @@ type SignInResult = {
|
|
|
140
167
|
} | {
|
|
141
168
|
status: 'mfa_required';
|
|
142
169
|
mfaToken: string;
|
|
170
|
+
/**
|
|
171
|
+
* The factors this person can answer with (laravel-id 1.24+). Absent when the
|
|
172
|
+
* instance predates the list — then only an authenticator or a recovery code works.
|
|
173
|
+
*/
|
|
174
|
+
factors?: SecondFactorKind[];
|
|
143
175
|
} | {
|
|
144
176
|
status: 'otp_required';
|
|
145
177
|
mfaToken: string;
|
|
@@ -151,6 +183,30 @@ type SignInResult = {
|
|
|
151
183
|
status: 'rate_limited';
|
|
152
184
|
retryAfter: number | undefined;
|
|
153
185
|
};
|
|
186
|
+
/** What asking for a texted sign-in code produced. */
|
|
187
|
+
type SmsCodeResult =
|
|
188
|
+
/** Sent. `to` is the number masked (`+45 ******78`); the code lasts `expiresIn` seconds. */
|
|
189
|
+
{
|
|
190
|
+
status: 'sent';
|
|
191
|
+
to: string;
|
|
192
|
+
expiresIn: number;
|
|
193
|
+
}
|
|
194
|
+
/** Too soon, or a sending limit was hit. Waiting `retryAfter` seconds may help. */
|
|
195
|
+
| {
|
|
196
|
+
status: 'rate_limited';
|
|
197
|
+
retryAfter: number | undefined;
|
|
198
|
+
}
|
|
199
|
+
/** The text provider failed. Try again shortly, or offer another factor. */
|
|
200
|
+
| {
|
|
201
|
+
status: 'unavailable';
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Refused: an unknown or spent token, no number on file, a country texts are not sent
|
|
205
|
+
* to, or no attempts left. One answer for every reason, like a wrong password.
|
|
206
|
+
*/
|
|
207
|
+
| {
|
|
208
|
+
status: 'invalid';
|
|
209
|
+
};
|
|
154
210
|
/** What a browser needs to ask an authenticator for a passkey assertion. */
|
|
155
211
|
interface PasskeyOptions {
|
|
156
212
|
challenge: string;
|
|
@@ -196,7 +252,7 @@ declare class CboxIdFrontend {
|
|
|
196
252
|
* fetches of the same immutable document is a waste the SDK should absorb rather than
|
|
197
253
|
* ask every consumer to.
|
|
198
254
|
*/
|
|
199
|
-
private
|
|
255
|
+
private readonly configPromises;
|
|
200
256
|
constructor(options: FrontendClientOptions);
|
|
201
257
|
/** Whether this key drives real sign-ins. Handy for a "test mode" badge in your own UI. */
|
|
202
258
|
get isLive(): boolean;
|
|
@@ -206,8 +262,9 @@ declare class CboxIdFrontend {
|
|
|
206
262
|
* Fetched once per client instance. The document is small and changes when somebody
|
|
207
263
|
* edits it in the console, so a long-lived page picks changes up on its next full load
|
|
208
264
|
* rather than mid-session — which is the right trade for something that decides layout.
|
|
265
|
+
* Each `organization` is fetched and cached on its own.
|
|
209
266
|
*/
|
|
210
|
-
config(): Promise<FrontendConfig>;
|
|
267
|
+
config(options?: FrontendConfigOptions): Promise<FrontendConfig>;
|
|
211
268
|
/**
|
|
212
269
|
* Who is signed in, given an access token your app already holds.
|
|
213
270
|
*
|
|
@@ -255,8 +312,29 @@ declare class CboxIdFrontend {
|
|
|
255
312
|
*
|
|
256
313
|
* A wrong code costs an attempt, not the sign-in: five are allowed before the token
|
|
257
314
|
* dies and the person starts from the password again.
|
|
315
|
+
*
|
|
316
|
+
* `method` says which challenge the code answers: `'mfa'` for an authenticator or
|
|
317
|
+
* recovery code, `'otp'` for an emailed code (`otp_required`), `'sms'` for a code sent
|
|
318
|
+
* with {@link sendSmsCode}. A texted code is six digits like an authenticator's, so the
|
|
319
|
+
* server cannot tell them apart by shape — answering one as the other is refused.
|
|
258
320
|
*/
|
|
259
|
-
submitSecondFactor(mfaToken: string, code: string, method?: 'mfa' | 'otp'): Promise<SignInResult>;
|
|
321
|
+
submitSecondFactor(mfaToken: string, code: string, method?: 'mfa' | 'otp' | 'sms'): Promise<SignInResult>;
|
|
322
|
+
/**
|
|
323
|
+
* Text the person a sign-in code, when `mfa_required` listed `'sms'` in `factors`.
|
|
324
|
+
* Then finish with `submitSecondFactor(mfaToken, code, 'sms')`.
|
|
325
|
+
*
|
|
326
|
+
* Only call it when the person asks ("Text me a code"), never on page load: each send
|
|
327
|
+
* spends one of the token's five attempts — that is what bounds how many texts one
|
|
328
|
+
* proved password can cause — and every text costs the customer money.
|
|
329
|
+
*
|
|
330
|
+
* ```ts
|
|
331
|
+
* if (result.status === 'mfa_required' && result.factors?.includes('sms')) {
|
|
332
|
+
* const sent = await frontend.sendSmsCode(result.mfaToken)
|
|
333
|
+
* if (sent.status === 'sent') showCodeField(`We texted ${sent.to}`)
|
|
334
|
+
* }
|
|
335
|
+
* ```
|
|
336
|
+
*/
|
|
337
|
+
sendSmsCode(mfaToken: string): Promise<SmsCodeResult>;
|
|
260
338
|
/**
|
|
261
339
|
* The WebAuthn options for a passkey sign-in, and the handle that carries the challenge.
|
|
262
340
|
*
|
|
@@ -282,92 +360,6 @@ declare class CboxIdFrontend {
|
|
|
282
360
|
private attempt;
|
|
283
361
|
}
|
|
284
362
|
|
|
285
|
-
/**
|
|
286
|
-
* Base class for every error this SDK throws, so callers can `catch (e) { if (e
|
|
287
|
-
* instanceof CboxIdError) ... }` in one branch.
|
|
288
|
-
*/
|
|
289
|
-
declare class CboxIdError extends Error {
|
|
290
|
-
constructor(message: string);
|
|
291
|
-
}
|
|
292
|
-
/** The client is misconfigured (a required option is missing or empty). */
|
|
293
|
-
declare class ConfigurationError extends CboxIdError {
|
|
294
|
-
}
|
|
295
|
-
/**
|
|
296
|
-
* The login `state` did not match what was stored — the callback is forged or
|
|
297
|
-
* stale. Treat as a fresh start, not an error to surface to the user.
|
|
298
|
-
*/
|
|
299
|
-
declare class InvalidStateError extends CboxIdError {
|
|
300
|
-
}
|
|
301
|
-
/**
|
|
302
|
-
* Login could not be completed: the provider returned an error, the callback was
|
|
303
|
-
* missing a code, or a token / id_token failed verification.
|
|
304
|
-
*
|
|
305
|
-
* `error` carries the RFC 6749 §5.2 code the authorization server sent, when it sent one.
|
|
306
|
-
* It used to be discarded at every back-channel boundary, leaving a single message string
|
|
307
|
-
* for outcomes that need opposite responses: `invalid_grant` on a refresh means the
|
|
308
|
-
* session is over and the person has to sign in again, while `temporarily_unavailable`
|
|
309
|
-
* means try the same token in a moment. A caller reduced to matching on prose either
|
|
310
|
-
* retries what can never succeed, or signs out somebody who did not need to be.
|
|
311
|
-
*/
|
|
312
|
-
declare class AuthenticationError extends CboxIdError {
|
|
313
|
-
/** RFC 6749 §5.2 error code, when the server sent a parseable one. */
|
|
314
|
-
readonly error?: string | undefined;
|
|
315
|
-
/** The server's `error_description`, verbatim. Not end-user copy. */
|
|
316
|
-
readonly errorDescription?: string | undefined;
|
|
317
|
-
/** HTTP status, for the cases where the body says nothing useful. */
|
|
318
|
-
readonly status?: number | undefined;
|
|
319
|
-
/**
|
|
320
|
-
* Seconds to wait, off the `Retry-After` header — set only on a 429.
|
|
321
|
-
*
|
|
322
|
-
* The distinction it carries is the one a caller most needs and could least infer:
|
|
323
|
-
* a 429 is the ONLY back-channel failure where the same request will succeed
|
|
324
|
-
* unchanged if you simply wait. Every other one needs a different request, or a new
|
|
325
|
-
* sign-in. Without this the SDK reported "token refresh failed" and a caller with a
|
|
326
|
-
* retry loop hammered a limiter that was already telling it exactly how long to stop.
|
|
327
|
-
*/
|
|
328
|
-
readonly retryAfter?: number | undefined;
|
|
329
|
-
constructor(message: string,
|
|
330
|
-
/** RFC 6749 §5.2 error code, when the server sent a parseable one. */
|
|
331
|
-
error?: string | undefined,
|
|
332
|
-
/** The server's `error_description`, verbatim. Not end-user copy. */
|
|
333
|
-
errorDescription?: string | undefined,
|
|
334
|
-
/** HTTP status, for the cases where the body says nothing useful. */
|
|
335
|
-
status?: number | undefined,
|
|
336
|
-
/**
|
|
337
|
-
* Seconds to wait, off the `Retry-After` header — set only on a 429.
|
|
338
|
-
*
|
|
339
|
-
* The distinction it carries is the one a caller most needs and could least infer:
|
|
340
|
-
* a 429 is the ONLY back-channel failure where the same request will succeed
|
|
341
|
-
* unchanged if you simply wait. Every other one needs a different request, or a new
|
|
342
|
-
* sign-in. Without this the SDK reported "token refresh failed" and a caller with a
|
|
343
|
-
* retry loop hammered a limiter that was already telling it exactly how long to stop.
|
|
344
|
-
*/
|
|
345
|
-
retryAfter?: number | undefined);
|
|
346
|
-
/** Whether waiting `retryAfter` seconds and repeating the same request is worth it. */
|
|
347
|
-
get isRateLimited(): boolean;
|
|
348
|
-
}
|
|
349
|
-
/**
|
|
350
|
-
* The Frontend API refused, or could not be reached.
|
|
351
|
-
*
|
|
352
|
-
* Carries a `code` because the four ways this goes wrong need four different responses
|
|
353
|
-
* from the caller, and telling them apart from a message string is the kind of thing
|
|
354
|
-
* that works until somebody rewords the message:
|
|
355
|
-
*
|
|
356
|
-
* - `origin_not_allowed` — the overwhelmingly likely cause of a refusal, and a
|
|
357
|
-
* configuration problem the developer fixes once in the console.
|
|
358
|
-
* - `rate_limited` — back off; `retryAfter` says for how long, when the server said.
|
|
359
|
-
* - `unavailable` — the network, or a 5xx. Transient by assumption; already retried.
|
|
360
|
-
* - `malformed` — a 200 whose body is not the document we expect, which means something
|
|
361
|
-
* between the page and us is rewriting responses (a proxy, an extension, a captive
|
|
362
|
-
* portal). Worth distinguishing, because retrying it never helps.
|
|
363
|
-
*/
|
|
364
|
-
declare class FrontendApiError extends CboxIdError {
|
|
365
|
-
readonly code: 'origin_not_allowed' | 'rate_limited' | 'unavailable' | 'malformed';
|
|
366
|
-
readonly status?: number | undefined;
|
|
367
|
-
readonly retryAfter?: number | undefined;
|
|
368
|
-
constructor(message: string, code: 'origin_not_allowed' | 'rate_limited' | 'unavailable' | 'malformed', status?: number | undefined, retryAfter?: number | undefined);
|
|
369
|
-
}
|
|
370
|
-
|
|
371
363
|
/**
|
|
372
364
|
* PKCE (RFC 7636) and the random values the login flow needs, built on Web Crypto
|
|
373
365
|
* (`globalThis.crypto`) so the same code runs on Node, edge runtimes and browsers.
|
|
@@ -527,4 +519,4 @@ declare function buildManifest(declaration: AuthzDeclaration): Promise<AuthzMani
|
|
|
527
519
|
*/
|
|
528
520
|
declare function publishManifest(config: ManifestPublisherConfig, declaration: AuthzDeclaration): Promise<ManifestSyncSummary>;
|
|
529
521
|
|
|
530
|
-
export { type Appearance,
|
|
522
|
+
export { type Appearance, type AuthzDeclaration, type AuthzManifest, CboxIdFrontend, type FrontendClientOptions, type FrontendConfig, type FrontendConfigOptions, type FrontendSession, type LegacyUser, type LegacyVerifierOptions, type ManifestPermission, type ManifestPublisherConfig, type ManifestRole, type ManifestSyncSummary, type PasskeyOptions, type PermissionDefinition, type RoleDefinition, type SecondFactorKind, type SignInResult, type SmsCodeResult, type SocialProvider, buildManifest, challenge, createLegacyVerifier, createVerifier, defineAuthz, publishManifest, randomToken };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export {
|
|
1
|
+
export { A as AuthenticationError, C as CboxIdError, a as ConfigurationError, F as FrontendApiError, I as InvalidStateError } from './errors-D61xljZE.js';
|
|
2
|
+
export { a as CallbackParams, C as CboxIdClient, P as PipeConnectOptions, b as PipeLeaseDeniedError, c as PipeLeaseError, d as PipeNotConnectedError, e as PipeProvider, f as PipeReauthorizationRequiredError, g as PipeTemporarilyUnavailableError, h as PipeToken, S as StoreSecretInput, i as StoredAuthState, V as VaultClient, j as VaultLease, k as VaultSecretRef, l as VerifyStandardWebhookOptions, m as VerifyWebhookOptions, W as WebhookHeaders, p as pipeConnectUrl, v as verifyStandardWebhook, n as verifyWebhook, w as withConnectReturn } from './client-xlEJalu8.js';
|
|
3
|
+
export { c as AuthorizationPrompt, b as AuthorizationRequest, A as AuthorizationRequestOptions, d as CboxActiveOrganization, e as CboxActor, a as CboxIdConfig, f as CboxOrganization, C as CboxUser, g as ClaimSource, D as DeviceAuthorization, h as DiscoveryDocument, F as FEATURE_FLAGS_SCOPE, O as OrganizationRole, R as RefreshedTokens, i as TokenResponse, T as TokenTypeHint, j as actor, k as featureFlags, l as hasFeature, m as hasPermission, n as hasRole, o as isSupportSession, p as organization, q as permissions, r as roles, s as sessionId } from './types-CC1bz68x.js';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* The handler on YOUR side of a legacy migration.
|
|
@@ -129,9 +130,35 @@ interface FrontendConfig {
|
|
|
129
130
|
end_session: string;
|
|
130
131
|
jwks: string;
|
|
131
132
|
};
|
|
133
|
+
/**
|
|
134
|
+
* The social buttons to draw, exactly as the hosted sign-in page would: the environment's
|
|
135
|
+
* own providers, or — with `config({ organization })` — that organization's.
|
|
136
|
+
*/
|
|
132
137
|
social: SocialProvider[];
|
|
133
138
|
appearance?: Appearance;
|
|
139
|
+
/**
|
|
140
|
+
* Which other sign-in methods are switched on, so a box does not draw a button whose
|
|
141
|
+
* endpoint answers 403. Absent from instances that predate it.
|
|
142
|
+
*/
|
|
143
|
+
methods?: {
|
|
144
|
+
passkeys: boolean;
|
|
145
|
+
magic_link: boolean;
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
/** Options for {@link CboxIdFrontend.config}. */
|
|
149
|
+
interface FrontendConfigOptions {
|
|
150
|
+
/**
|
|
151
|
+
* The organization the box is for, by id or slug — the way a hosted page is addressed.
|
|
152
|
+
* `social` is then exactly what that organization's sign-in page offers. An organization
|
|
153
|
+
* the environment does not have is answered as no hint at all.
|
|
154
|
+
*/
|
|
155
|
+
organization?: string;
|
|
134
156
|
}
|
|
157
|
+
/**
|
|
158
|
+
* A second factor the person can answer an `mfa_required` with. Kinds only — the server
|
|
159
|
+
* never says which number a text would go to before one is sent.
|
|
160
|
+
*/
|
|
161
|
+
type SecondFactorKind = 'totp' | 'sms' | 'recovery_code';
|
|
135
162
|
/** What a sign-in attempt produced. */
|
|
136
163
|
type SignInResult = {
|
|
137
164
|
status: 'ok';
|
|
@@ -140,6 +167,11 @@ type SignInResult = {
|
|
|
140
167
|
} | {
|
|
141
168
|
status: 'mfa_required';
|
|
142
169
|
mfaToken: string;
|
|
170
|
+
/**
|
|
171
|
+
* The factors this person can answer with (laravel-id 1.24+). Absent when the
|
|
172
|
+
* instance predates the list — then only an authenticator or a recovery code works.
|
|
173
|
+
*/
|
|
174
|
+
factors?: SecondFactorKind[];
|
|
143
175
|
} | {
|
|
144
176
|
status: 'otp_required';
|
|
145
177
|
mfaToken: string;
|
|
@@ -151,6 +183,30 @@ type SignInResult = {
|
|
|
151
183
|
status: 'rate_limited';
|
|
152
184
|
retryAfter: number | undefined;
|
|
153
185
|
};
|
|
186
|
+
/** What asking for a texted sign-in code produced. */
|
|
187
|
+
type SmsCodeResult =
|
|
188
|
+
/** Sent. `to` is the number masked (`+45 ******78`); the code lasts `expiresIn` seconds. */
|
|
189
|
+
{
|
|
190
|
+
status: 'sent';
|
|
191
|
+
to: string;
|
|
192
|
+
expiresIn: number;
|
|
193
|
+
}
|
|
194
|
+
/** Too soon, or a sending limit was hit. Waiting `retryAfter` seconds may help. */
|
|
195
|
+
| {
|
|
196
|
+
status: 'rate_limited';
|
|
197
|
+
retryAfter: number | undefined;
|
|
198
|
+
}
|
|
199
|
+
/** The text provider failed. Try again shortly, or offer another factor. */
|
|
200
|
+
| {
|
|
201
|
+
status: 'unavailable';
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Refused: an unknown or spent token, no number on file, a country texts are not sent
|
|
205
|
+
* to, or no attempts left. One answer for every reason, like a wrong password.
|
|
206
|
+
*/
|
|
207
|
+
| {
|
|
208
|
+
status: 'invalid';
|
|
209
|
+
};
|
|
154
210
|
/** What a browser needs to ask an authenticator for a passkey assertion. */
|
|
155
211
|
interface PasskeyOptions {
|
|
156
212
|
challenge: string;
|
|
@@ -196,7 +252,7 @@ declare class CboxIdFrontend {
|
|
|
196
252
|
* fetches of the same immutable document is a waste the SDK should absorb rather than
|
|
197
253
|
* ask every consumer to.
|
|
198
254
|
*/
|
|
199
|
-
private
|
|
255
|
+
private readonly configPromises;
|
|
200
256
|
constructor(options: FrontendClientOptions);
|
|
201
257
|
/** Whether this key drives real sign-ins. Handy for a "test mode" badge in your own UI. */
|
|
202
258
|
get isLive(): boolean;
|
|
@@ -206,8 +262,9 @@ declare class CboxIdFrontend {
|
|
|
206
262
|
* Fetched once per client instance. The document is small and changes when somebody
|
|
207
263
|
* edits it in the console, so a long-lived page picks changes up on its next full load
|
|
208
264
|
* rather than mid-session — which is the right trade for something that decides layout.
|
|
265
|
+
* Each `organization` is fetched and cached on its own.
|
|
209
266
|
*/
|
|
210
|
-
config(): Promise<FrontendConfig>;
|
|
267
|
+
config(options?: FrontendConfigOptions): Promise<FrontendConfig>;
|
|
211
268
|
/**
|
|
212
269
|
* Who is signed in, given an access token your app already holds.
|
|
213
270
|
*
|
|
@@ -255,8 +312,29 @@ declare class CboxIdFrontend {
|
|
|
255
312
|
*
|
|
256
313
|
* A wrong code costs an attempt, not the sign-in: five are allowed before the token
|
|
257
314
|
* dies and the person starts from the password again.
|
|
315
|
+
*
|
|
316
|
+
* `method` says which challenge the code answers: `'mfa'` for an authenticator or
|
|
317
|
+
* recovery code, `'otp'` for an emailed code (`otp_required`), `'sms'` for a code sent
|
|
318
|
+
* with {@link sendSmsCode}. A texted code is six digits like an authenticator's, so the
|
|
319
|
+
* server cannot tell them apart by shape — answering one as the other is refused.
|
|
258
320
|
*/
|
|
259
|
-
submitSecondFactor(mfaToken: string, code: string, method?: 'mfa' | 'otp'): Promise<SignInResult>;
|
|
321
|
+
submitSecondFactor(mfaToken: string, code: string, method?: 'mfa' | 'otp' | 'sms'): Promise<SignInResult>;
|
|
322
|
+
/**
|
|
323
|
+
* Text the person a sign-in code, when `mfa_required` listed `'sms'` in `factors`.
|
|
324
|
+
* Then finish with `submitSecondFactor(mfaToken, code, 'sms')`.
|
|
325
|
+
*
|
|
326
|
+
* Only call it when the person asks ("Text me a code"), never on page load: each send
|
|
327
|
+
* spends one of the token's five attempts — that is what bounds how many texts one
|
|
328
|
+
* proved password can cause — and every text costs the customer money.
|
|
329
|
+
*
|
|
330
|
+
* ```ts
|
|
331
|
+
* if (result.status === 'mfa_required' && result.factors?.includes('sms')) {
|
|
332
|
+
* const sent = await frontend.sendSmsCode(result.mfaToken)
|
|
333
|
+
* if (sent.status === 'sent') showCodeField(`We texted ${sent.to}`)
|
|
334
|
+
* }
|
|
335
|
+
* ```
|
|
336
|
+
*/
|
|
337
|
+
sendSmsCode(mfaToken: string): Promise<SmsCodeResult>;
|
|
260
338
|
/**
|
|
261
339
|
* The WebAuthn options for a passkey sign-in, and the handle that carries the challenge.
|
|
262
340
|
*
|
|
@@ -282,92 +360,6 @@ declare class CboxIdFrontend {
|
|
|
282
360
|
private attempt;
|
|
283
361
|
}
|
|
284
362
|
|
|
285
|
-
/**
|
|
286
|
-
* Base class for every error this SDK throws, so callers can `catch (e) { if (e
|
|
287
|
-
* instanceof CboxIdError) ... }` in one branch.
|
|
288
|
-
*/
|
|
289
|
-
declare class CboxIdError extends Error {
|
|
290
|
-
constructor(message: string);
|
|
291
|
-
}
|
|
292
|
-
/** The client is misconfigured (a required option is missing or empty). */
|
|
293
|
-
declare class ConfigurationError extends CboxIdError {
|
|
294
|
-
}
|
|
295
|
-
/**
|
|
296
|
-
* The login `state` did not match what was stored — the callback is forged or
|
|
297
|
-
* stale. Treat as a fresh start, not an error to surface to the user.
|
|
298
|
-
*/
|
|
299
|
-
declare class InvalidStateError extends CboxIdError {
|
|
300
|
-
}
|
|
301
|
-
/**
|
|
302
|
-
* Login could not be completed: the provider returned an error, the callback was
|
|
303
|
-
* missing a code, or a token / id_token failed verification.
|
|
304
|
-
*
|
|
305
|
-
* `error` carries the RFC 6749 §5.2 code the authorization server sent, when it sent one.
|
|
306
|
-
* It used to be discarded at every back-channel boundary, leaving a single message string
|
|
307
|
-
* for outcomes that need opposite responses: `invalid_grant` on a refresh means the
|
|
308
|
-
* session is over and the person has to sign in again, while `temporarily_unavailable`
|
|
309
|
-
* means try the same token in a moment. A caller reduced to matching on prose either
|
|
310
|
-
* retries what can never succeed, or signs out somebody who did not need to be.
|
|
311
|
-
*/
|
|
312
|
-
declare class AuthenticationError extends CboxIdError {
|
|
313
|
-
/** RFC 6749 §5.2 error code, when the server sent a parseable one. */
|
|
314
|
-
readonly error?: string | undefined;
|
|
315
|
-
/** The server's `error_description`, verbatim. Not end-user copy. */
|
|
316
|
-
readonly errorDescription?: string | undefined;
|
|
317
|
-
/** HTTP status, for the cases where the body says nothing useful. */
|
|
318
|
-
readonly status?: number | undefined;
|
|
319
|
-
/**
|
|
320
|
-
* Seconds to wait, off the `Retry-After` header — set only on a 429.
|
|
321
|
-
*
|
|
322
|
-
* The distinction it carries is the one a caller most needs and could least infer:
|
|
323
|
-
* a 429 is the ONLY back-channel failure where the same request will succeed
|
|
324
|
-
* unchanged if you simply wait. Every other one needs a different request, or a new
|
|
325
|
-
* sign-in. Without this the SDK reported "token refresh failed" and a caller with a
|
|
326
|
-
* retry loop hammered a limiter that was already telling it exactly how long to stop.
|
|
327
|
-
*/
|
|
328
|
-
readonly retryAfter?: number | undefined;
|
|
329
|
-
constructor(message: string,
|
|
330
|
-
/** RFC 6749 §5.2 error code, when the server sent a parseable one. */
|
|
331
|
-
error?: string | undefined,
|
|
332
|
-
/** The server's `error_description`, verbatim. Not end-user copy. */
|
|
333
|
-
errorDescription?: string | undefined,
|
|
334
|
-
/** HTTP status, for the cases where the body says nothing useful. */
|
|
335
|
-
status?: number | undefined,
|
|
336
|
-
/**
|
|
337
|
-
* Seconds to wait, off the `Retry-After` header — set only on a 429.
|
|
338
|
-
*
|
|
339
|
-
* The distinction it carries is the one a caller most needs and could least infer:
|
|
340
|
-
* a 429 is the ONLY back-channel failure where the same request will succeed
|
|
341
|
-
* unchanged if you simply wait. Every other one needs a different request, or a new
|
|
342
|
-
* sign-in. Without this the SDK reported "token refresh failed" and a caller with a
|
|
343
|
-
* retry loop hammered a limiter that was already telling it exactly how long to stop.
|
|
344
|
-
*/
|
|
345
|
-
retryAfter?: number | undefined);
|
|
346
|
-
/** Whether waiting `retryAfter` seconds and repeating the same request is worth it. */
|
|
347
|
-
get isRateLimited(): boolean;
|
|
348
|
-
}
|
|
349
|
-
/**
|
|
350
|
-
* The Frontend API refused, or could not be reached.
|
|
351
|
-
*
|
|
352
|
-
* Carries a `code` because the four ways this goes wrong need four different responses
|
|
353
|
-
* from the caller, and telling them apart from a message string is the kind of thing
|
|
354
|
-
* that works until somebody rewords the message:
|
|
355
|
-
*
|
|
356
|
-
* - `origin_not_allowed` — the overwhelmingly likely cause of a refusal, and a
|
|
357
|
-
* configuration problem the developer fixes once in the console.
|
|
358
|
-
* - `rate_limited` — back off; `retryAfter` says for how long, when the server said.
|
|
359
|
-
* - `unavailable` — the network, or a 5xx. Transient by assumption; already retried.
|
|
360
|
-
* - `malformed` — a 200 whose body is not the document we expect, which means something
|
|
361
|
-
* between the page and us is rewriting responses (a proxy, an extension, a captive
|
|
362
|
-
* portal). Worth distinguishing, because retrying it never helps.
|
|
363
|
-
*/
|
|
364
|
-
declare class FrontendApiError extends CboxIdError {
|
|
365
|
-
readonly code: 'origin_not_allowed' | 'rate_limited' | 'unavailable' | 'malformed';
|
|
366
|
-
readonly status?: number | undefined;
|
|
367
|
-
readonly retryAfter?: number | undefined;
|
|
368
|
-
constructor(message: string, code: 'origin_not_allowed' | 'rate_limited' | 'unavailable' | 'malformed', status?: number | undefined, retryAfter?: number | undefined);
|
|
369
|
-
}
|
|
370
|
-
|
|
371
363
|
/**
|
|
372
364
|
* PKCE (RFC 7636) and the random values the login flow needs, built on Web Crypto
|
|
373
365
|
* (`globalThis.crypto`) so the same code runs on Node, edge runtimes and browsers.
|
|
@@ -527,4 +519,4 @@ declare function buildManifest(declaration: AuthzDeclaration): Promise<AuthzMani
|
|
|
527
519
|
*/
|
|
528
520
|
declare function publishManifest(config: ManifestPublisherConfig, declaration: AuthzDeclaration): Promise<ManifestSyncSummary>;
|
|
529
521
|
|
|
530
|
-
export { type Appearance,
|
|
522
|
+
export { type Appearance, type AuthzDeclaration, type AuthzManifest, CboxIdFrontend, type FrontendClientOptions, type FrontendConfig, type FrontendConfigOptions, type FrontendSession, type LegacyUser, type LegacyVerifierOptions, type ManifestPermission, type ManifestPublisherConfig, type ManifestRole, type ManifestSyncSummary, type PasskeyOptions, type PermissionDefinition, type RoleDefinition, type SecondFactorKind, type SignInResult, type SmsCodeResult, type SocialProvider, buildManifest, challenge, createLegacyVerifier, createVerifier, defineAuthz, publishManifest, randomToken };
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { Discovery } from './chunk-
|
|
2
|
-
export { CboxIdClient, VaultClient, actor, challenge, createVerifier, hasPermission, hasRole, isSupportSession, organization, permissions, randomToken, roles, sessionId, verifyWebhook } from './chunk-
|
|
3
|
-
import {
|
|
4
|
-
|
|
1
|
+
import { Discovery } from './chunk-YGLMLIKL.js';
|
|
2
|
+
export { CboxIdClient, FEATURE_FLAGS_SCOPE, PipeLeaseDeniedError, PipeLeaseError, PipeNotConnectedError, PipeReauthorizationRequiredError, PipeTemporarilyUnavailableError, VaultClient, actor, challenge, createVerifier, featureFlags, hasFeature, hasPermission, hasRole, isSupportSession, organization, permissions, pipeConnectUrl, randomToken, roles, sessionId, verifyStandardWebhook, verifyWebhook, withConnectReturn } from './chunk-YGLMLIKL.js';
|
|
3
|
+
import { assertSecureIssuer } from './chunk-QQW63PGU.js';
|
|
4
|
+
import { ConfigurationError, FrontendApiError, AuthenticationError } from './chunk-SEUD4TE4.js';
|
|
5
|
+
export { AuthenticationError, CboxIdError, ConfigurationError, FrontendApiError, InvalidStateError } from './chunk-SEUD4TE4.js';
|
|
5
6
|
|
|
6
7
|
// src/legacy.ts
|
|
7
8
|
function createLegacyVerifier(options) {
|
|
@@ -97,6 +98,7 @@ function json(body, status) {
|
|
|
97
98
|
}
|
|
98
99
|
|
|
99
100
|
// src/frontend.ts
|
|
101
|
+
var SECOND_FACTOR_KINDS = /* @__PURE__ */ new Set(["totp", "sms", "recovery_code"]);
|
|
100
102
|
var CboxIdFrontend = class {
|
|
101
103
|
issuer;
|
|
102
104
|
publishableKey;
|
|
@@ -111,7 +113,7 @@ var CboxIdFrontend = class {
|
|
|
111
113
|
* fetches of the same immutable document is a waste the SDK should absorb rather than
|
|
112
114
|
* ask every consumer to.
|
|
113
115
|
*/
|
|
114
|
-
|
|
116
|
+
configPromises = /* @__PURE__ */ new Map();
|
|
115
117
|
constructor(options) {
|
|
116
118
|
if (!options.issuer) {
|
|
117
119
|
throw new ConfigurationError("issuer is required.");
|
|
@@ -138,13 +140,20 @@ var CboxIdFrontend = class {
|
|
|
138
140
|
* Fetched once per client instance. The document is small and changes when somebody
|
|
139
141
|
* edits it in the console, so a long-lived page picks changes up on its next full load
|
|
140
142
|
* rather than mid-session — which is the right trade for something that decides layout.
|
|
143
|
+
* Each `organization` is fetched and cached on its own.
|
|
141
144
|
*/
|
|
142
|
-
async config() {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
145
|
+
async config(options = {}) {
|
|
146
|
+
const organization2 = options.organization ?? "";
|
|
147
|
+
let pending = this.configPromises.get(organization2);
|
|
148
|
+
if (pending === void 0) {
|
|
149
|
+
const path = organization2 === "" ? "/frontend/v1/config" : `/frontend/v1/config?${new URLSearchParams({ organization: organization2 }).toString()}`;
|
|
150
|
+
pending = this.get(path, {}, isFrontendConfig).catch((error) => {
|
|
151
|
+
this.configPromises.delete(organization2);
|
|
152
|
+
throw error;
|
|
153
|
+
});
|
|
154
|
+
this.configPromises.set(organization2, pending);
|
|
155
|
+
}
|
|
156
|
+
return pending;
|
|
148
157
|
}
|
|
149
158
|
/**
|
|
150
159
|
* Who is signed in, given an access token your app already holds.
|
|
@@ -206,8 +215,12 @@ var CboxIdFrontend = class {
|
|
|
206
215
|
if (body.status === "ok" && typeof body.login_ticket === "string") {
|
|
207
216
|
return { status: "ok", loginTicket: body.login_ticket, expiresIn: body.expires_in ?? 60 };
|
|
208
217
|
}
|
|
209
|
-
if (
|
|
210
|
-
|
|
218
|
+
if (body.status === "mfa_required" && typeof body.mfa_token === "string") {
|
|
219
|
+
const factors = Array.isArray(body.factors) ? body.factors.filter((f) => typeof f === "string" && SECOND_FACTOR_KINDS.has(f)) : void 0;
|
|
220
|
+
return { status: "mfa_required", mfaToken: body.mfa_token, ...factors ? { factors } : {} };
|
|
221
|
+
}
|
|
222
|
+
if (body.status === "otp_required" && typeof body.mfa_token === "string") {
|
|
223
|
+
return { status: "otp_required", mfaToken: body.mfa_token };
|
|
211
224
|
}
|
|
212
225
|
if (body.status === "sso_required") {
|
|
213
226
|
return { status: "sso_required" };
|
|
@@ -224,6 +237,11 @@ var CboxIdFrontend = class {
|
|
|
224
237
|
*
|
|
225
238
|
* A wrong code costs an attempt, not the sign-in: five are allowed before the token
|
|
226
239
|
* dies and the person starts from the password again.
|
|
240
|
+
*
|
|
241
|
+
* `method` says which challenge the code answers: `'mfa'` for an authenticator or
|
|
242
|
+
* recovery code, `'otp'` for an emailed code (`otp_required`), `'sms'` for a code sent
|
|
243
|
+
* with {@link sendSmsCode}. A texted code is six digits like an authenticator's, so the
|
|
244
|
+
* server cannot tell them apart by shape — answering one as the other is refused.
|
|
227
245
|
*/
|
|
228
246
|
async submitSecondFactor(mfaToken, code, method = "mfa") {
|
|
229
247
|
const response = await this.post("/frontend/v1/sign-in/factor", { mfa_token: mfaToken, code, method });
|
|
@@ -235,6 +253,41 @@ var CboxIdFrontend = class {
|
|
|
235
253
|
}
|
|
236
254
|
return body.status === "ok" && typeof body.login_ticket === "string" ? { status: "ok", loginTicket: body.login_ticket, expiresIn: body.expires_in ?? 60 } : { status: "invalid" };
|
|
237
255
|
}
|
|
256
|
+
/**
|
|
257
|
+
* Text the person a sign-in code, when `mfa_required` listed `'sms'` in `factors`.
|
|
258
|
+
* Then finish with `submitSecondFactor(mfaToken, code, 'sms')`.
|
|
259
|
+
*
|
|
260
|
+
* Only call it when the person asks ("Text me a code"), never on page load: each send
|
|
261
|
+
* spends one of the token's five attempts — that is what bounds how many texts one
|
|
262
|
+
* proved password can cause — and every text costs the customer money.
|
|
263
|
+
*
|
|
264
|
+
* ```ts
|
|
265
|
+
* if (result.status === 'mfa_required' && result.factors?.includes('sms')) {
|
|
266
|
+
* const sent = await frontend.sendSmsCode(result.mfaToken)
|
|
267
|
+
* if (sent.status === 'sent') showCodeField(`We texted ${sent.to}`)
|
|
268
|
+
* }
|
|
269
|
+
* ```
|
|
270
|
+
*/
|
|
271
|
+
async sendSmsCode(mfaToken) {
|
|
272
|
+
const response = await this.post("/frontend/v1/sign-in/factor/sms", { mfa_token: mfaToken });
|
|
273
|
+
let body;
|
|
274
|
+
try {
|
|
275
|
+
body = await response.json();
|
|
276
|
+
} catch {
|
|
277
|
+
throw new FrontendApiError("Cbox ID returned a body that is not JSON.", "malformed", response.status);
|
|
278
|
+
}
|
|
279
|
+
if (body.status === "sent" && typeof body.to === "string") {
|
|
280
|
+
return { status: "sent", to: body.to, expiresIn: typeof body.expires_in === "number" ? body.expires_in : 300 };
|
|
281
|
+
}
|
|
282
|
+
if (body.status === "rate_limited" || response.status === 429) {
|
|
283
|
+
const retryAfter = typeof body.retry_after === "number" ? body.retry_after : Number(response.headers.get("Retry-After") ?? "");
|
|
284
|
+
return { status: "rate_limited", retryAfter: Number.isFinite(retryAfter) && retryAfter > 0 ? retryAfter : void 0 };
|
|
285
|
+
}
|
|
286
|
+
if (body.status === "unavailable") {
|
|
287
|
+
return { status: "unavailable" };
|
|
288
|
+
}
|
|
289
|
+
return { status: "invalid" };
|
|
290
|
+
}
|
|
238
291
|
/**
|
|
239
292
|
* The WebAuthn options for a passkey sign-in, and the handle that carries the challenge.
|
|
240
293
|
*
|