@atlasauth/js 0.23.1 → 0.24.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.
Files changed (90) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/dist/app-launcher.d.ts +11 -0
  3. package/dist/app-launcher.js +12 -0
  4. package/dist/app-launcher.js.map +1 -0
  5. package/dist/attempt.d.ts +52 -1
  6. package/dist/attempt.js +2 -0
  7. package/dist/attempt.js.map +1 -1
  8. package/dist/attestation.d.ts +52 -0
  9. package/dist/attestation.js +48 -0
  10. package/dist/attestation.js.map +1 -0
  11. package/dist/compat.d.ts +1 -0
  12. package/dist/compat.js +5 -1
  13. package/dist/compat.js.map +1 -1
  14. package/dist/connect.d.ts +6 -0
  15. package/dist/connect.js +22 -0
  16. package/dist/connect.js.map +1 -1
  17. package/dist/connections.d.ts +43 -0
  18. package/dist/connections.js +53 -0
  19. package/dist/connections.js.map +1 -0
  20. package/dist/credentials.d.ts +47 -0
  21. package/dist/credentials.js +95 -0
  22. package/dist/credentials.js.map +1 -0
  23. package/dist/custom-challenge.d.ts +13 -0
  24. package/dist/custom-challenge.js +27 -0
  25. package/dist/custom-challenge.js.map +1 -0
  26. package/dist/delegated-ui.d.ts +93 -0
  27. package/dist/delegated-ui.js +62 -0
  28. package/dist/delegated-ui.js.map +1 -0
  29. package/dist/dev-browser.d.ts +9 -0
  30. package/dist/dev-browser.js +21 -1
  31. package/dist/dev-browser.js.map +1 -1
  32. package/dist/device-key.d.ts +65 -0
  33. package/dist/device-key.js +64 -0
  34. package/dist/device-key.js.map +1 -0
  35. package/dist/email-addresses.d.ts +24 -0
  36. package/dist/email-addresses.js +36 -0
  37. package/dist/email-addresses.js.map +1 -0
  38. package/dist/environment.d.ts +8 -0
  39. package/dist/environment.js +6 -0
  40. package/dist/environment.js.map +1 -1
  41. package/dist/external-passkeys.d.ts +34 -0
  42. package/dist/external-passkeys.js +31 -0
  43. package/dist/external-passkeys.js.map +1 -0
  44. package/dist/fapi.d.ts +36 -0
  45. package/dist/fapi.js +75 -4
  46. package/dist/fapi.js.map +1 -1
  47. package/dist/identity-verification.d.ts +40 -0
  48. package/dist/identity-verification.js +40 -0
  49. package/dist/identity-verification.js.map +1 -0
  50. package/dist/index.d.ts +17 -1
  51. package/dist/index.js +24 -1
  52. package/dist/index.js.map +1 -1
  53. package/dist/journey-redirect.d.ts +8 -0
  54. package/dist/journey-redirect.js +32 -0
  55. package/dist/journey-redirect.js.map +1 -0
  56. package/dist/marketing.d.ts +81 -0
  57. package/dist/marketing.js +162 -0
  58. package/dist/marketing.js.map +1 -0
  59. package/dist/mfa-enrollment.d.ts +2 -0
  60. package/dist/mfa-enrollment.js +1 -0
  61. package/dist/mfa-enrollment.js.map +1 -1
  62. package/dist/mfa-factors.d.ts +109 -0
  63. package/dist/mfa-factors.js +98 -0
  64. package/dist/mfa-factors.js.map +1 -0
  65. package/dist/migration-browser-sessions.d.ts +118 -0
  66. package/dist/migration-browser-sessions.js +404 -0
  67. package/dist/migration-browser-sessions.js.map +1 -0
  68. package/dist/organization-admin.d.ts +192 -0
  69. package/dist/organization-admin.js +53 -0
  70. package/dist/organization-admin.js.map +1 -0
  71. package/dist/passkey.d.ts +71 -2
  72. package/dist/passkey.js +97 -0
  73. package/dist/passkey.js.map +1 -1
  74. package/dist/password-reset.d.ts +16 -1
  75. package/dist/password-reset.js +19 -4
  76. package/dist/password-reset.js.map +1 -1
  77. package/dist/pricing.d.ts +80 -0
  78. package/dist/pricing.js +71 -0
  79. package/dist/pricing.js.map +1 -0
  80. package/dist/profile.d.ts +73 -1
  81. package/dist/profile.js +23 -0
  82. package/dist/profile.js.map +1 -1
  83. package/dist/redirect.js +1 -1
  84. package/dist/redirect.js.map +1 -1
  85. package/dist/telegram.d.ts +23 -0
  86. package/dist/telegram.js +8 -0
  87. package/dist/telegram.js.map +1 -1
  88. package/dist/version.d.ts +1 -1
  89. package/dist/version.js +1 -1
  90. package/package.json +1 -1
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ /**
3
+ * Wallet-side helpers for Atlas verifiable credentials (OpenID4VCI / OpenID4VP,
4
+ * SD-JWT VC): redeem a credential offer, hold the credential with its device
5
+ * key, and build a selective presentation. For web wallets, tests and demos.
6
+ * Uses WebCrypto (ES256) only. mdoc credentials are received as-is
7
+ * (`format: 'mso_mdoc'`); building an mdoc DeviceResponse is a native-wallet job.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.parseCredentialOffer = parseCredentialOffer;
11
+ exports.generateHolderKey = generateHolderKey;
12
+ exports.receiveCredential = receiveCredential;
13
+ exports.presentSdJwt = presentSdJwt;
14
+ const PRE_AUTH = 'urn:ietf:params:oauth:grant-type:pre-authorized_code';
15
+ /** The offer inside an `openid-credential-offer://` link (or an https link carrying `credential_offer`). */
16
+ function parseCredentialOffer(link) {
17
+ const raw = new URL(link).searchParams.get('credential_offer');
18
+ if (!raw)
19
+ throw new Error('The link does not carry a credential_offer.');
20
+ const offer = JSON.parse(raw);
21
+ if (typeof offer.credential_issuer !== 'string' || !Array.isArray(offer.credential_configuration_ids)) {
22
+ throw new Error('Malformed credential offer.');
23
+ }
24
+ return offer;
25
+ }
26
+ const enc = new TextEncoder();
27
+ const b64u = (b) => {
28
+ const bytes = b instanceof Uint8Array ? b : new Uint8Array(b);
29
+ let s = '';
30
+ for (const x of bytes)
31
+ s += String.fromCharCode(x);
32
+ return btoa(s).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
33
+ };
34
+ const b64uJson = (v) => b64u(enc.encode(JSON.stringify(v)));
35
+ /** A fresh P-256 device key. */
36
+ async function generateHolderKey() {
37
+ const pair = await crypto.subtle.generateKey({ name: 'ECDSA', namedCurve: 'P-256' }, true, ['sign', 'verify']);
38
+ const publicJwk = await crypto.subtle.exportKey('jwk', pair.publicKey);
39
+ return { privateJwk: await crypto.subtle.exportKey('jwk', pair.privateKey), publicJwk: { ...publicJwk, alg: 'ES256' } };
40
+ }
41
+ async function signJwt(header, payload, privateJwk) {
42
+ const key = await crypto.subtle.importKey('jwk', privateJwk, { name: 'ECDSA', namedCurve: 'P-256' }, false, ['sign']);
43
+ const input = `${b64uJson({ alg: 'ES256', ...header })}.${b64uJson(payload)}`;
44
+ const sig = await crypto.subtle.sign({ name: 'ECDSA', hash: 'SHA-256' }, key, enc.encode(input));
45
+ return `${input}.${b64u(sig)}`;
46
+ }
47
+ async function json(res) {
48
+ const body = (await res.json().catch(() => ({})));
49
+ if (!res.ok)
50
+ throw new Error(body.error_description ?? body.error ?? `Request failed (${res.status}).`);
51
+ return body;
52
+ }
53
+ /** Redeem an offer (pre-authorized code flow) and receive the credential bound to a new device key. */
54
+ async function receiveCredential(offer, opts = {}) {
55
+ const f = opts.fetch ?? ((...a) => fetch(...a));
56
+ const code = offer.grants?.[PRE_AUTH]?.['pre-authorized_code'];
57
+ if (!code)
58
+ throw new Error('The offer has no pre-authorized code.');
59
+ const issuer = offer.credential_issuer.replace(/\/$/, '');
60
+ const meta = await json(await f(`${issuer}/.well-known/openid-credential-issuer`));
61
+ const asBase = (meta.authorization_servers?.[0] ?? issuer).replace(/\/$/, '');
62
+ const as = await json(await f(`${asBase}/.well-known/oauth-authorization-server`));
63
+ const configurationId = opts.configurationId ?? offer.credential_configuration_ids[0];
64
+ const token = await json(await f(as.token_endpoint, {
65
+ method: 'POST',
66
+ headers: { 'content-type': 'application/x-www-form-urlencoded' },
67
+ body: new URLSearchParams({ grant_type: PRE_AUTH, 'pre-authorized_code': code, ...(opts.txCode ? { tx_code: opts.txCode } : {}) }).toString(),
68
+ }));
69
+ const key = opts.key ?? (await generateHolderKey());
70
+ const proof = await signJwt({ typ: 'openid4vci-proof+jwt', jwk: { kty: key.publicJwk.kty, crv: key.publicJwk.crv, x: key.publicJwk.x, y: key.publicJwk.y, alg: 'ES256' } }, { aud: issuer, iat: Math.floor(Date.now() / 1000), nonce: token.c_nonce }, key.privateJwk);
71
+ const issued = await json(await f(meta.credential_endpoint, {
72
+ method: 'POST',
73
+ headers: { 'content-type': 'application/json', authorization: `Bearer ${token.access_token}` },
74
+ body: JSON.stringify({ credential_configuration_id: configurationId, proof: { proof_type: 'jwt', jwt: proof } }),
75
+ }));
76
+ return { ...key, credential: issued.credential, format: issued.format ?? meta.credential_configurations_supported?.[configurationId]?.format ?? 'dc+sd-jwt' };
77
+ }
78
+ /**
79
+ * Build an SD-JWT VC presentation revealing only the claims in `reveal`, with a
80
+ * key-binding JWT over `aud` and `nonce` (from the verifier's request object).
81
+ */
82
+ async function presentSdJwt(input) {
83
+ const [jwt, ...rest] = input.credential.split('~').filter(Boolean);
84
+ if (!jwt)
85
+ throw new Error('Not an SD-JWT credential.');
86
+ const shown = rest.filter((d) => {
87
+ const name = JSON.parse(atob(d.replace(/-/g, '+').replace(/_/g, '/')));
88
+ return input.reveal.includes(String(name[1]));
89
+ });
90
+ const base = `${[jwt, ...shown].join('~')}~`;
91
+ const digest = await crypto.subtle.digest('SHA-256', enc.encode(base));
92
+ const kb = await signJwt({ typ: 'kb+jwt' }, { aud: input.aud, nonce: input.nonce, iat: Math.floor(Date.now() / 1000), sd_hash: b64u(digest) }, input.privateJwk);
93
+ return `${base}${kb}`;
94
+ }
95
+ //# sourceMappingURL=credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials.js","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { FapiClient } from './fapi';
2
+ /**
3
+ * The application's own second factor (a question, a code sent elsewhere, an
4
+ * identity check): ask Atlas to run the application's challenge for this
5
+ * sign-in attempt, show the prompt, then submit the answer as the attempt's
6
+ * `code` with `strategy: 'custom_challenge'` (e.g. `submit({ code, strategy:
7
+ * 'custom_challenge' })`). Offered when `account.customSecondFactor` is true.
8
+ */
9
+ export declare function prepareCustomChallenge(client: FapiClient, attemptId: string): Promise<{
10
+ prompt: string;
11
+ hint: string | null;
12
+ expiresAt: number;
13
+ }>;
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.prepareCustomChallenge = prepareCustomChallenge;
4
+ /**
5
+ * The application's own second factor (a question, a code sent elsewhere, an
6
+ * identity check): ask Atlas to run the application's challenge for this
7
+ * sign-in attempt, show the prompt, then submit the answer as the attempt's
8
+ * `code` with `strategy: 'custom_challenge'` (e.g. `submit({ code, strategy:
9
+ * 'custom_challenge' })`). Offered when `account.customSecondFactor` is true.
10
+ */
11
+ async function prepareCustomChallenge(client, attemptId) {
12
+ const response = await client.post(`/v1/client/sign_ins/${encodeURIComponent(attemptId)}/prepare_second_factor`, { strategy: 'custom_challenge' });
13
+ if (!response.ok) {
14
+ const first = response.errors[0];
15
+ const error = new Error(first?.message ?? 'Could not start the challenge.');
16
+ if (first?.code)
17
+ error.code = first.code;
18
+ throw error;
19
+ }
20
+ const d = response.data;
21
+ return {
22
+ prompt: typeof d?.prompt === 'string' ? d.prompt : '',
23
+ hint: typeof d?.hint === 'string' ? d.hint : null,
24
+ expiresAt: typeof d?.expires_at === 'number' ? d.expires_at : 0,
25
+ };
26
+ }
27
+ //# sourceMappingURL=custom-challenge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custom-challenge.js","sourceRoot":"","sources":["../src/custom-challenge.ts"],"names":[],"mappings":";;AASA,wDAoBC;AA3BD;;;;;;GAMG;AACI,KAAK,UAAU,sBAAsB,CAC1C,MAAkB,EAClB,SAAiB;IAEjB,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,IAAI,CAChC,uBAAuB,kBAAkB,CAAC,SAAS,CAAC,wBAAwB,EAC5E,EAAE,QAAQ,EAAE,kBAAkB,EAAE,CACjC,CAAC;IACF,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACjC,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,KAAK,EAAE,OAAO,IAAI,gCAAgC,CAA8B,CAAC;QACzG,IAAI,KAAK,EAAE,IAAI;YAAE,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;QACzC,MAAM,KAAK,CAAC;IACd,CAAC;IACD,MAAM,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC;IACxB,OAAO;QACL,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE;QACrD,IAAI,EAAE,OAAO,CAAC,EAAE,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI;QACjD,SAAS,EAAE,OAAO,CAAC,EAAE,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;KAChE,CAAC;AACJ,CAAC"}
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Bring-your-own login and consent pages. Atlas sends the browser to your page
3
+ * with `?login_challenge=` or `?consent_challenge=`. Reading, accepting and
4
+ * rejecting the challenge needs a secret key, so your page talks to YOUR
5
+ * backend (see `delegatedUiProxy` in @atlasauth/backend), and then follows the
6
+ * `redirect_to` Atlas returned.
7
+ */
8
+ export type DelegatedChallenge = {
9
+ type: 'login' | 'consent';
10
+ challenge: string;
11
+ };
12
+ /** The challenge on the current page URL, or null when the page was not reached from Atlas. */
13
+ export declare function readDelegatedChallenge(href: string | URL | {
14
+ href: string;
15
+ }): DelegatedChallenge | null;
16
+ export interface DelegatedLoginRequest {
17
+ client: {
18
+ id: string;
19
+ client_id: string;
20
+ name: string;
21
+ };
22
+ requested_scope: string[];
23
+ audience: string | null;
24
+ page: 'sign-in' | 'sign-up';
25
+ session_subject: string | null;
26
+ ui_locales: string | null;
27
+ login_hint: string | null;
28
+ expires_at: number;
29
+ }
30
+ export interface DelegatedConsentRequest {
31
+ subject: string;
32
+ client: {
33
+ id: string;
34
+ client_id: string;
35
+ name: string;
36
+ first_party?: boolean;
37
+ };
38
+ requested_scope: string[];
39
+ audience: string | null;
40
+ authorization_details: unknown[];
41
+ organization_id: string | null;
42
+ redirect_uri: string;
43
+ expires_at: number;
44
+ }
45
+ export interface DelegatedUiClientOptions {
46
+ /** Base URL of your backend routes that proxy the Atlas calls, e.g. `/atlas/oauth`. */
47
+ baseUrl: string;
48
+ fetch?: typeof fetch;
49
+ headers?: Record<string, string>;
50
+ }
51
+ /**
52
+ * Calls your backend's proxy routes (`GET {baseUrl}/login`, `POST {baseUrl}/login/accept`, …),
53
+ * each taking the challenge as a query parameter. Errors throw with the server's message.
54
+ */
55
+ export declare function createDelegatedUiClient(opts: DelegatedUiClientOptions): {
56
+ getLoginRequest: (challenge: string) => Promise<DelegatedLoginRequest>;
57
+ /** `subject` is the Atlas user id your backend authenticated the visitor as. */
58
+ acceptLogin: (challenge: string, body: {
59
+ subject: string;
60
+ remember?: boolean;
61
+ amr?: string[];
62
+ acr?: string;
63
+ }) => Promise<{
64
+ redirect_to: string;
65
+ }>;
66
+ rejectLogin: (challenge: string, body?: {
67
+ error?: string;
68
+ error_description?: string;
69
+ }) => Promise<{
70
+ redirect_to: string;
71
+ }>;
72
+ getConsentRequest: (challenge: string) => Promise<DelegatedConsentRequest>;
73
+ acceptConsent: (challenge: string, body?: {
74
+ grant_scope?: string[];
75
+ remember?: boolean;
76
+ }) => Promise<{
77
+ redirect_to: string;
78
+ }>;
79
+ rejectConsent: (challenge: string, body?: {
80
+ error?: string;
81
+ error_description?: string;
82
+ }) => Promise<{
83
+ redirect_to: string;
84
+ }>;
85
+ };
86
+ /**
87
+ * Send the browser on to the `redirect_to` Atlas returned. Refuses anything that
88
+ * is not https (or an allowed origin), so a tampered response cannot redirect elsewhere.
89
+ */
90
+ export declare function continueDelegatedFlow(redirectTo: string, opts?: {
91
+ allowedOrigins?: string[];
92
+ navigate?: (url: string) => void;
93
+ }): void;
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ /**
3
+ * Bring-your-own login and consent pages. Atlas sends the browser to your page
4
+ * with `?login_challenge=` or `?consent_challenge=`. Reading, accepting and
5
+ * rejecting the challenge needs a secret key, so your page talks to YOUR
6
+ * backend (see `delegatedUiProxy` in @atlasauth/backend), and then follows the
7
+ * `redirect_to` Atlas returned.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.readDelegatedChallenge = readDelegatedChallenge;
11
+ exports.createDelegatedUiClient = createDelegatedUiClient;
12
+ exports.continueDelegatedFlow = continueDelegatedFlow;
13
+ /** The challenge on the current page URL, or null when the page was not reached from Atlas. */
14
+ function readDelegatedChallenge(href) {
15
+ const url = new URL(typeof href === 'string' ? href : href instanceof URL ? href.href : href.href);
16
+ const login = url.searchParams.get('login_challenge');
17
+ if (login)
18
+ return { type: 'login', challenge: login };
19
+ const consent = url.searchParams.get('consent_challenge');
20
+ return consent ? { type: 'consent', challenge: consent } : null;
21
+ }
22
+ /**
23
+ * Calls your backend's proxy routes (`GET {baseUrl}/login`, `POST {baseUrl}/login/accept`, …),
24
+ * each taking the challenge as a query parameter. Errors throw with the server's message.
25
+ */
26
+ function createDelegatedUiClient(opts) {
27
+ const f = opts.fetch ?? ((...a) => fetch(...a));
28
+ const base = opts.baseUrl.replace(/\/$/, '');
29
+ async function call(method, path, kind, challenge, body) {
30
+ const res = await f(`${base}/${path}?${kind}_challenge=${encodeURIComponent(challenge)}`, {
31
+ method,
32
+ credentials: 'same-origin',
33
+ headers: { ...(body !== undefined ? { 'content-type': 'application/json' } : {}), ...opts.headers },
34
+ ...(body !== undefined ? { body: JSON.stringify(body) } : {}),
35
+ });
36
+ const json = (await res.json().catch(() => ({})));
37
+ if (!res.ok)
38
+ throw new Error(json.error?.message ?? json.message ?? `Request failed (${res.status}).`);
39
+ return json;
40
+ }
41
+ return {
42
+ getLoginRequest: (challenge) => call('GET', 'login', 'login', challenge),
43
+ /** `subject` is the Atlas user id your backend authenticated the visitor as. */
44
+ acceptLogin: (challenge, body) => call('POST', 'login/accept', 'login', challenge, body),
45
+ rejectLogin: (challenge, body = {}) => call('POST', 'login/reject', 'login', challenge, body),
46
+ getConsentRequest: (challenge) => call('GET', 'consent', 'consent', challenge),
47
+ acceptConsent: (challenge, body = {}) => call('POST', 'consent/accept', 'consent', challenge, body),
48
+ rejectConsent: (challenge, body = {}) => call('POST', 'consent/reject', 'consent', challenge, body),
49
+ };
50
+ }
51
+ /**
52
+ * Send the browser on to the `redirect_to` Atlas returned. Refuses anything that
53
+ * is not https (or an allowed origin), so a tampered response cannot redirect elsewhere.
54
+ */
55
+ function continueDelegatedFlow(redirectTo, opts = {}) {
56
+ const url = new URL(redirectTo);
57
+ const ok = opts.allowedOrigins ? opts.allowedOrigins.includes(url.origin) : url.protocol === 'https:';
58
+ if (!ok)
59
+ throw new Error('Refusing to follow an unexpected redirect.');
60
+ (opts.navigate ?? ((u) => window.location.assign(u)))(url.toString());
61
+ }
62
+ //# sourceMappingURL=delegated-ui.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delegated-ui.js","sourceRoot":"","sources":["../src/delegated-ui.ts"],"names":[],"mappings":"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"}
@@ -21,6 +21,15 @@
21
21
  * The cookies are still set and sent where the browser allows them; the
22
22
  * header is only read when there is no cookie.
23
23
  */
24
+ /**
25
+ * Where the dev-browser refresh token is kept: `local` (default, survives restarts and is shared
26
+ * by every tab), `session` (this tab only) or `memory` (this page load only, nothing written).
27
+ */
28
+ export type PersistenceMode = 'local' | 'session' | 'memory';
29
+ /** Choose the storage for this page. The FAPI client calls it from its `persistence` option. */
30
+ export declare function setPersistence(next: PersistenceMode | undefined): void;
31
+ /** The storage in force (`local` unless set). Exposed for tests. */
32
+ export declare function getPersistence(): PersistenceMode;
24
33
  /** Whether the dev browser applies: a development instance's key, in a browser with storage. */
25
34
  export declare function devBrowserEnabled(publishableKey: string): boolean;
26
35
  /** The stored refresh token for a development instance, or null. */
@@ -23,6 +23,8 @@
23
23
  * header is only read when there is no cookie.
24
24
  */
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.setPersistence = setPersistence;
27
+ exports.getPersistence = getPersistence;
26
28
  exports.devBrowserEnabled = devBrowserEnabled;
27
29
  exports.devBrowserToken = devBrowserToken;
28
30
  exports.forgetDevBrowserToken = forgetDevBrowserToken;
@@ -32,9 +34,27 @@ const EXCHANGE = '/v1/client/tickets/exchange';
32
34
  const BOOT = '/v1/client';
33
35
  const REFRESH = /^\/v1\/client\/sessions\/[^/]+\/tokens$/;
34
36
  const REVOKE = /^\/v1\/client\/sessions\/[^/]+\/revoke$/;
37
+ const memory = new Map();
38
+ const memoryStore = {
39
+ getItem: (k) => memory.get(k) ?? null,
40
+ setItem: (k, v) => void memory.set(k, v),
41
+ removeItem: (k) => void memory.delete(k),
42
+ };
43
+ let mode = 'local';
44
+ /** Choose the storage for this page. The FAPI client calls it from its `persistence` option. */
45
+ function setPersistence(next) {
46
+ mode = next ?? 'local';
47
+ }
48
+ /** The storage in force (`local` unless set). Exposed for tests. */
49
+ function getPersistence() {
50
+ return mode;
51
+ }
35
52
  function storage() {
53
+ if (mode === 'memory')
54
+ return memoryStore;
36
55
  try {
37
- const s = globalThis.localStorage;
56
+ const g = globalThis;
57
+ const s = mode === 'session' ? g.sessionStorage : g.localStorage;
38
58
  return s && typeof s.getItem === 'function' ? s : null;
39
59
  }
40
60
  catch {
@@ -1 +1 @@
1
- {"version":3,"file":"dev-browser.js","sourceRoot":"","sources":["../src/dev-browser.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;;AAoBH,8CAEC;AAGD,0CAMC;AAuBD,sDAOC;AAgCD,wCAgDC;AA3ID,MAAM,cAAc,GAAG,sBAAsB,CAAC;AAC9C,MAAM,QAAQ,GAAG,6BAA6B,CAAC;AAC/C,MAAM,IAAI,GAAG,YAAY,CAAC;AAC1B,MAAM,OAAO,GAAG,yCAAyC,CAAC;AAC1D,MAAM,MAAM,GAAG,yCAAyC,CAAC;AAIzD,SAAS,OAAO;IACd,IAAI,CAAC;QACH,MAAM,CAAC,GAAI,UAAuC,CAAC,YAAY,CAAC;QAChE,OAAO,CAAC,IAAI,OAAO,CAAC,CAAC,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACzD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,gGAAgG;AAChG,SAAgB,iBAAiB,CAAC,cAAsB;IACtD,OAAO,cAAc,CAAC,UAAU,CAAC,UAAU,CAAC,IAAI,OAAO,EAAE,KAAK,IAAI,CAAC;AACrE,CAAC;AAED,oEAAoE;AACpE,SAAgB,eAAe,CAAC,cAAsB;IACpD,IAAI,CAAC;QACH,OAAO,OAAO,EAAE,EAAE,OAAO,CAAC,cAAc,GAAG,cAAc,CAAC,IAAI,IAAI,CAAC;IACrE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,cAAc,GAAG,MAAM,CAAC;AAE9B,SAAS,QAAQ,CAAC,cAAsB,EAAE,KAAc,EAAE,SAAkB;IAC1E,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK;QAAE,OAAO;IAChD,IAAI,CAAC;QACH,OAAO,EAAE,EAAE,OAAO,CAAC,cAAc,GAAG,cAAc,EAAE,KAAK,CAAC,CAAC;QAC3D,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,SAAS;YAAE,OAAO,EAAE,EAAE,OAAO,CAAC,cAAc,GAAG,cAAc,GAAG,cAAc,EAAE,SAAS,CAAC,CAAC;IAClI,CAAC;IAAC,MAAM,CAAC;QACP,oFAAoF;IACtF,CAAC;AACH,CAAC;AAED,SAAS,eAAe,CAAC,cAAsB;IAC7C,IAAI,CAAC;QACH,OAAO,OAAO,EAAE,EAAE,OAAO,CAAC,cAAc,GAAG,cAAc,GAAG,cAAc,CAAC,IAAI,IAAI,CAAC;IACtF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,yDAAyD;AACzD,SAAgB,qBAAqB,CAAC,cAAsB;IAC1D,IAAI,CAAC;QACH,OAAO,EAAE,EAAE,UAAU,CAAC,cAAc,GAAG,cAAc,CAAC,CAAC;QACvD,OAAO,EAAE,EAAE,UAAU,CAAC,cAAc,GAAG,cAAc,GAAG,cAAc,CAAC,CAAC;IAC1E,CAAC;IAAC,MAAM,CAAC;QACP,oBAAoB;IACtB,CAAC;AACH,CAAC;AAED,SAAS,MAAM,CAAC,KAAwB;IACtC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,YAAY,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC;QAC9F,OAAO,IAAI,GAAG,CAAC,GAAG,EAAE,yBAAyB,CAAC,CAAC,QAAQ,CAAC;IAC1D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,qDAAqD;AACrD,SAAS,WAAW,CAAC,IAAY;IAC/B,MAAM,CAAC,GAAG,oCAAoC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1D,OAAO,CAAC,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC9C,CAAC;AAED,KAAK,UAAU,MAAM,CAAC,QAAkB;IACtC,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,CAAC,MAAM,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,CAAY,CAAC;QACrD,OAAO,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAE,CAA6B,CAAC,CAAC,CAAC,IAAI,CAAC;IACjG,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;AAE/C,SAAgB,cAAc,CAAC,cAAsB,EAAE,SAAuB;IAC5E,kFAAkF;IAClF,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC,UAAU,CAAC,IAAK,SAAgD,CAAC,OAAO,CAAC;QAAE,OAAO,SAAS,CAAC;IAC3H,MAAM,OAAO,GAAG,KAAK,EAAE,KAAwB,EAAE,IAAkB,EAAqB,EAAE;QACxF,IAAI,CAAC,iBAAiB,CAAC,cAAc,CAAC;YAAE,OAAO,SAAS,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QACtE,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QAC3B,MAAM,MAAM,GAAG,CAAC,IAAI,EAAE,MAAM,IAAI,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;QACrD,IAAI,QAAQ,GAAG,IAAI,CAAC;QACpB,IAAI,SAAS,GAAG,KAAK,CAAC;QAEtB,IAAI,IAAI,KAAK,QAAQ,IAAI,MAAM,KAAK,MAAM,IAAI,OAAO,IAAI,EAAE,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC7E,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAY,CAAC;gBAC9C,IAAI,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;oBAC7D,QAAQ,GAAG,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,GAAI,IAAgC,EAAE,oBAAoB,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;gBACrH,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,6BAA6B;YAC/B,CAAC;QACH,CAAC;QACD,MAAM,KAAK,GAAG,eAAe,CAAC,cAAc,CAAC,CAAC;QAC9C,IAAI,KAAK,IAAI,CAAC,CAAC,IAAI,KAAK,IAAI,IAAI,MAAM,KAAK,KAAK,CAAC,IAAI,CAAC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,MAAM,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC;YACxG,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YAC/C,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;gBAAE,OAAO,CAAC,GAAG,CAAC,iBAAiB,EAAE,KAAK,CAAC,CAAC;YAC3E,QAAQ,GAAG,EAAE,GAAG,QAAQ,EAAE,OAAO,EAAE,CAAC;YACpC,SAAS,GAAG,IAAI,CAAC;QACnB,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QAClD,IAAI,CAAC,IAAI;YAAE,OAAO,QAAQ,CAAC;QAC3B,IAAI,IAAI,KAAK,QAAQ,IAAI,QAAQ,CAAC,EAAE,EAAE,CAAC;YACrC,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC;YACpC,QAAQ,CAAC,cAAc,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;QAC1D,CAAC;aAAM,IAAI,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACtC,IAAI,QAAQ,CAAC,EAAE;gBAAE,QAAQ,CAAC,cAAc,EAAE,CAAC,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,aAAa,EAAE,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;iBACjG,IAAI,SAAS,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG;gBAAE,qBAAqB,CAAC,cAAc,CAAC,CAAC;QACvF,CAAC;aAAM,IAAI,IAAI,KAAK,IAAI,IAAI,SAAS,IAAI,QAAQ,CAAC,EAAE,EAAE,CAAC;YACrD,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC;YACpC,IAAI,IAAI,IAAI,IAAI,CAAC,OAAO,KAAK,IAAI;gBAAE,qBAAqB,CAAC,cAAc,CAAC,CAAC;QAC3E,CAAC;aAAM,IAAI,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,CAAC,EAAE,CAAC;YACjF,+EAA+E;YAC/E,MAAM,GAAG,GAAG,eAAe,CAAC,cAAc,CAAC,CAAC;YAC5C,IAAI,CAAC,GAAG,IAAI,GAAG,KAAK,WAAW,CAAC,IAAI,CAAC;gBAAE,qBAAqB,CAAC,cAAc,CAAC,CAAC;QAC/E,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC,CAAC;IACD,OAA8C,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;IAChE,OAAO,OAAuB,CAAC;AACjC,CAAC"}
1
+ {"version":3,"file":"dev-browser.js","sourceRoot":"","sources":["../src/dev-browser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Device-bound biometric sign-in — the client half.
3
+ *
4
+ * A native app keeps an ES256 keypair in the OS secure hardware (Secure Enclave
5
+ * / Android Keystore), unlocked by Face ID / fingerprint. The public half is
6
+ * registered once while signed in; later the app signs in by signing a server
7
+ * challenge with the private half. The keystore is the app's: it supplies a
8
+ * {@link DeviceKeySigner} (`createKey` + `sign` behind a biometric prompt).
9
+ * {@link webCryptoSigner} is a software signer for tests and web/RN-polyfill use.
10
+ *
11
+ * Server routes: POST /v1/client/me/device_keys/challenge → /device_keys,
12
+ * POST /v1/client/sign_ins/device_key/challenge → /finish.
13
+ */
14
+ import type { FapiClient, FapiResponse } from './fapi';
15
+ export interface DeviceKeyJwk {
16
+ kty: 'EC';
17
+ crv: 'P-256';
18
+ x: string;
19
+ y: string;
20
+ }
21
+ export interface DeviceKeySigner {
22
+ /** Creates the keypair and returns its public JWK. `biometric` asks for a key that needs the OS biometric prompt to use. */
23
+ createKey(opts: {
24
+ biometric: boolean;
25
+ }): Promise<DeviceKeyJwk>;
26
+ /** Signs the UTF-8 message with ES256, returning base64url IEEE-P1363 (r||s). Shows the biometric prompt for a biometric key. */
27
+ sign(message: string): Promise<string>;
28
+ }
29
+ export type DeviceKeyPurpose = 'register' | 'sign_in';
30
+ /** The exact string the device signs; mirrors the server so a signature is bound to its use, tenant and key. */
31
+ export declare function deviceKeyMessage(input: {
32
+ purpose: DeviceKeyPurpose;
33
+ instanceId: string;
34
+ keyId: string;
35
+ nonce: string;
36
+ }): string;
37
+ export interface RegisterDeviceKeyOptions {
38
+ client: FapiClient;
39
+ signer: DeviceKeySigner;
40
+ /** Your instance id (`ins_…`), as shown in the dashboard. */
41
+ instanceId: string;
42
+ label?: string;
43
+ platform?: 'ios' | 'android' | 'other';
44
+ /** Whether the key needs biometrics to use. Default true. */
45
+ biometric?: boolean;
46
+ }
47
+ /** Registers this device's key for the signed-in user (needs a recent re-authentication). Returns the `device_key` resource. */
48
+ export declare function registerDeviceKey(opts: RegisterDeviceKeyOptions): Promise<FapiResponse<{
49
+ id: string;
50
+ }>>;
51
+ export interface SignInWithDeviceKeyOptions {
52
+ client: FapiClient;
53
+ signer: DeviceKeySigner;
54
+ instanceId: string;
55
+ /** The `id` returned by {@link registerDeviceKey}, which the app stores. */
56
+ deviceKeyId: string;
57
+ }
58
+ /**
59
+ * Signs in with the registered device key. The response is the usual sign-in
60
+ * attempt: `complete` with a session, or `needs_second_factor` when the instance
61
+ * does not let a device key stand in for MFA.
62
+ */
63
+ export declare function signInWithDeviceKey(opts: SignInWithDeviceKeyOptions): Promise<FapiResponse<Record<string, unknown>>>;
64
+ /** A software signer on WebCrypto — no hardware or biometric binding; for tests and prototypes. */
65
+ export declare function webCryptoSigner(subtle?: SubtleCrypto): DeviceKeySigner;
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.deviceKeyMessage = deviceKeyMessage;
4
+ exports.registerDeviceKey = registerDeviceKey;
5
+ exports.signInWithDeviceKey = signInWithDeviceKey;
6
+ exports.webCryptoSigner = webCryptoSigner;
7
+ /** The exact string the device signs; mirrors the server so a signature is bound to its use, tenant and key. */
8
+ function deviceKeyMessage(input) {
9
+ return `atlas-device-key:v1:${input.purpose}:${input.instanceId}:${input.keyId}:${input.nonce}`;
10
+ }
11
+ /** Registers this device's key for the signed-in user (needs a recent re-authentication). Returns the `device_key` resource. */
12
+ async function registerDeviceKey(opts) {
13
+ const { client, signer, instanceId } = opts;
14
+ const biometric = opts.biometric ?? true;
15
+ const challenge = await client.post('/v1/client/me/device_keys/challenge', {});
16
+ if (!challenge.ok || !challenge.data)
17
+ return challenge;
18
+ const jwk = await signer.createKey({ biometric });
19
+ // Proof of possession: the register message names the key by its x coordinate.
20
+ const signature = await signer.sign(deviceKeyMessage({ purpose: 'register', instanceId, keyId: jwk.x, nonce: challenge.data.nonce }));
21
+ return client.post('/v1/client/me/device_keys', {
22
+ nonce: challenge.data.nonce,
23
+ public_key: jwk,
24
+ signature,
25
+ biometric_bound: biometric,
26
+ ...(opts.label ? { label: opts.label } : {}),
27
+ ...(opts.platform ? { platform: opts.platform } : {}),
28
+ });
29
+ }
30
+ /**
31
+ * Signs in with the registered device key. The response is the usual sign-in
32
+ * attempt: `complete` with a session, or `needs_second_factor` when the instance
33
+ * does not let a device key stand in for MFA.
34
+ */
35
+ async function signInWithDeviceKey(opts) {
36
+ const { client, signer, instanceId, deviceKeyId } = opts;
37
+ const challenge = await client.post('/v1/client/sign_ins/device_key/challenge', { device_key_id: deviceKeyId });
38
+ if (!challenge.ok || !challenge.data)
39
+ return challenge;
40
+ const signature = await signer.sign(deviceKeyMessage({ purpose: 'sign_in', instanceId, keyId: deviceKeyId, nonce: challenge.data.nonce }));
41
+ return client.post('/v1/client/sign_ins/device_key/finish', {
42
+ device_key_id: deviceKeyId,
43
+ nonce: challenge.data.nonce,
44
+ signature,
45
+ });
46
+ }
47
+ const toB64u = (bytes) => btoa(String.fromCharCode(...new Uint8Array(bytes))).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
48
+ /** A software signer on WebCrypto — no hardware or biometric binding; for tests and prototypes. */
49
+ function webCryptoSigner(subtle = globalThis.crypto.subtle) {
50
+ let pair;
51
+ return {
52
+ async createKey() {
53
+ pair = await subtle.generateKey({ name: 'ECDSA', namedCurve: 'P-256' }, false, ['sign', 'verify']);
54
+ const jwk = await subtle.exportKey('jwk', pair.publicKey);
55
+ return { kty: 'EC', crv: 'P-256', x: jwk.x, y: jwk.y };
56
+ },
57
+ async sign(message) {
58
+ if (!pair)
59
+ throw new Error('No device key has been created.');
60
+ return toB64u(await subtle.sign({ name: 'ECDSA', hash: 'SHA-256' }, pair.privateKey, new TextEncoder().encode(message)));
61
+ },
62
+ };
63
+ }
64
+ //# sourceMappingURL=device-key.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"device-key.js","sourceRoot":"","sources":["../src/device-key.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import type { FapiClient } from './fapi';
2
+ /**
3
+ * Secure primary-email change. Making a different verified address primary
4
+ * answers `status: 'pending_old_address_confirmation'` when the instance sets
5
+ * `accountUx.emailChange.requireOldAddressConfirm`: a code was mailed to the
6
+ * current primary address, and {@link confirmPrimaryEmailAddress} finishes the switch.
7
+ */
8
+ export interface PrimaryEmailResult {
9
+ object: 'email_address';
10
+ id: string;
11
+ primary: boolean;
12
+ status?: 'pending_old_address_confirmation';
13
+ /** Masked current primary address that received the code. */
14
+ old_address?: string;
15
+ }
16
+ export declare class EmailAddressRequestError extends Error {
17
+ readonly status: number;
18
+ readonly code: string;
19
+ constructor(status: number, code: string, message: string);
20
+ }
21
+ /** Make a verified address primary. Check `result.status` for a pending old-address confirmation. */
22
+ export declare function setPrimaryEmailAddress(client: FapiClient, id: string): Promise<PrimaryEmailResult>;
23
+ /** Finish a pending switch with the code mailed to the old primary address. */
24
+ export declare function confirmPrimaryEmailAddress(client: FapiClient, id: string, code: string): Promise<PrimaryEmailResult>;
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EmailAddressRequestError = void 0;
4
+ exports.setPrimaryEmailAddress = setPrimaryEmailAddress;
5
+ exports.confirmPrimaryEmailAddress = confirmPrimaryEmailAddress;
6
+ class EmailAddressRequestError extends Error {
7
+ status;
8
+ code;
9
+ constructor(status, code, message) {
10
+ super(message);
11
+ this.status = status;
12
+ this.code = code;
13
+ this.name = 'EmailAddressRequestError';
14
+ }
15
+ }
16
+ exports.EmailAddressRequestError = EmailAddressRequestError;
17
+ async function call(client, id, action, body) {
18
+ const res = await client.request(`/v1/client/me/email_addresses/${encodeURIComponent(id)}/${action}`, {
19
+ method: 'POST',
20
+ ...(body === undefined ? {} : { body: JSON.stringify(body) }),
21
+ });
22
+ if (!res.ok || !res.data) {
23
+ const first = res.errors[0];
24
+ throw new EmailAddressRequestError(res.status, first?.code ?? 'UNKNOWN', first?.message ?? 'The request failed.');
25
+ }
26
+ return res.data;
27
+ }
28
+ /** Make a verified address primary. Check `result.status` for a pending old-address confirmation. */
29
+ function setPrimaryEmailAddress(client, id) {
30
+ return call(client, id, 'primary');
31
+ }
32
+ /** Finish a pending switch with the code mailed to the old primary address. */
33
+ function confirmPrimaryEmailAddress(client, id, code) {
34
+ return call(client, id, 'confirm_primary', { code });
35
+ }
36
+ //# sourceMappingURL=email-addresses.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"email-addresses.js","sourceRoot":"","sources":["../src/email-addresses.ts"],"names":[],"mappings":";;;AAqCA,wDAEC;AAGD,gEAEC;AA3BD,MAAa,wBAAyB,SAAQ,KAAK;IAC5B;IAAyB;IAA9C,YAAqB,MAAc,EAAW,IAAY,EAAE,OAAe;QACzE,KAAK,CAAC,OAAO,CAAC,CAAC;QADI,WAAM,GAAN,MAAM,CAAQ;QAAW,SAAI,GAAJ,IAAI,CAAQ;QAExD,IAAI,CAAC,IAAI,GAAG,0BAA0B,CAAC;IACzC,CAAC;CACF;AALD,4DAKC;AAED,KAAK,UAAU,IAAI,CAAC,MAAkB,EAAE,EAAU,EAAE,MAAc,EAAE,IAAc;IAChF,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,OAAO,CAAqB,iCAAiC,kBAAkB,CAAC,EAAE,CAAC,IAAI,MAAM,EAAE,EAAE;QACxH,MAAM,EAAE,MAAM;QACd,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;KAC9D,CAAC,CAAC;IACH,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAC5B,MAAM,IAAI,wBAAwB,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,IAAI,SAAS,EAAE,KAAK,EAAE,OAAO,IAAI,qBAAqB,CAAC,CAAC;IACpH,CAAC;IACD,OAAO,GAAG,CAAC,IAAI,CAAC;AAClB,CAAC;AAED,qGAAqG;AACrG,SAAgB,sBAAsB,CAAC,MAAkB,EAAE,EAAU;IACnE,OAAO,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,SAAS,CAAC,CAAC;AACrC,CAAC;AAED,+EAA+E;AAC/E,SAAgB,0BAA0B,CAAC,MAAkB,EAAE,EAAU,EAAE,IAAY;IACrF,OAAO,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,iBAAiB,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;AACvD,CAAC"}
@@ -61,6 +61,8 @@ export interface AccountUx {
61
61
  passwordReset: EmailProofDelivery;
62
62
  /** The second-factor step may offer "email me a code" ({@link prepareEmailSecondFactor}). */
63
63
  emailSecondFactor: boolean;
64
+ /** The instance offers the custom challenge second factor (set only when true). */
65
+ customSecondFactor?: boolean;
64
66
  /**
65
67
  * Passkey-only sign-up ({@link startPasskeySignUp}), when the instance offers
66
68
  * it: whether the form collects an email address. Null when not offered.
@@ -79,6 +81,12 @@ export interface AccountUx {
79
81
  totp: boolean;
80
82
  sms: boolean;
81
83
  totpCodes: 1 | 2;
84
+ /** Counter tokens, YubiKeys, Duo, recovery questions and smart card sign-in. False/absent from an older server. */
85
+ hotp?: boolean;
86
+ yubikey?: boolean;
87
+ duo?: boolean;
88
+ securityQuestions?: boolean;
89
+ smartCard?: boolean;
82
90
  };
83
91
  }
84
92
  /**