@atlasauth/svelte 0.16.1 → 0.16.3

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/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # @atlasauth/svelte changelog
2
2
 
3
+ ## 0.16.3 — 2026-11-06
4
+
5
+ - The second-factor step emails the code on arrival when that's the account's only way, and offers "Use a passkey", which continues on the hosted page. Picks up the passkey origin handling from `@atlasauth/js` 0.34.2.
6
+
7
+ ## 0.16.2 — 2026-11-06
8
+
9
+ - A username taken while the sign-up waited no longer shows a sign-in link beside the error. Picks up passkey promotion from `@atlasauth/js` 0.34.1.
10
+
3
11
  ## 0.16.1 — 2026-11-06
4
12
 
5
13
  - The sign-up code step shows a sign-in link beside the taken-address error.
@@ -127,6 +127,10 @@ export interface FlowController {
127
127
  sentTo?: string;
128
128
  error?: string;
129
129
  }>;
130
+ /** On arrival at the second factor: emails the code if the inbox is the only way. Resolves the masked address, or null. */
131
+ sendOnlyWayEmail(attemptId: string): Promise<string | null>;
132
+ /** Leaves for the hosted page to do the second factor with a passkey there. */
133
+ continueWithPasskey(attemptId: string): boolean;
130
134
  /** What the app lets the MFA steps offer (from {@link settings}). */
131
135
  mfaChoices(): MfaChoices;
132
136
  /**
package/dist/accessors.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { derived, get, readable, writable } from 'svelte/store';
2
- import { FapiClient, advanceWithCaptcha, atlasCaptchaLabels, attemptPasswordHoldProof, preparePasswordHoldProof, createCaptchaController, createPasskeySignInController, createPasskeySignUp, createSignUp, finishPasskeySignUp, fetchInstanceSettings, fetchLastSignInMethod, startSignInWithAtlas, flowNeedsCaptcha, signInCaptchaOptions, flowFromPending, pollAttempt, prepareFactor, shouldKeepPolling, createOrganizationAuditLog, createMfaEnrollment, acknowledgeExistingAccount, acknowledgeRecoveryCodes, pendingExistingAccountNotice, pendingRecoveryCodes, mfaChoicesFrom, prepareSecondFactorCode, resendSignUpVerification, PolicyAcceptanceController, loadPolicies as loadPolicyList, initialFlow as emptyFlow, } from '@atlasauth/js';
2
+ import { FapiClient, advanceWithCaptcha, atlasCaptchaLabels, attemptPasswordHoldProof, preparePasswordHoldProof, createCaptchaController, createPasskeySignInController, createPasskeySignUp, createSignUp, finishPasskeySignUp, fetchInstanceSettings, fetchLastSignInMethod, startSignInWithAtlas, flowNeedsCaptcha, signInCaptchaOptions, flowFromPending, pollAttempt, prepareFactor, shouldKeepPolling, createOrganizationAuditLog, createMfaEnrollment, acknowledgeExistingAccount, acknowledgeRecoveryCodes, pendingExistingAccountNotice, pendingRecoveryCodes, mfaChoicesFrom, prepareSecondFactorCode, sendOnlyWaySecondFactorCode, continueOnHostedPage, resendSignUpVerification, PolicyAcceptanceController, loadPolicies as loadPolicyList, initialFlow as emptyFlow, } from '@atlasauth/js';
3
3
  import { useAtlas } from './context';
4
4
  import { translate } from './i18n';
5
5
  import { evaluate } from './protect';
@@ -250,6 +250,8 @@ function createFlow(options = {}) {
250
250
  }
251
251
  const sendEmailSecondFactor = () => sendSecondFactorCode('email_code');
252
252
  const sendSmsSecondFactor = () => sendSecondFactorCode('sms');
253
+ const sendOnlyWayEmail = (attemptId) => sendOnlyWaySecondFactorCode(client, attemptId);
254
+ const continueWithPasskey = (attemptId) => continueOnHostedPage(mfaChoices(), attemptId, 'needs_second_factor');
253
255
  // The MFA enrolment step: started while the flow is at needs_mfa_enrollment.
254
256
  const mfaChoices = () => mfaChoicesFrom(get(settings)?.account);
255
257
  const enrollment = createMfaEnrollment(client, mfaChoices);
@@ -329,6 +331,8 @@ function createFlow(options = {}) {
329
331
  createPasskey,
330
332
  sendEmailSecondFactor,
331
333
  sendSmsSecondFactor,
334
+ sendOnlyWayEmail,
335
+ continueWithPasskey,
332
336
  mfaChoices,
333
337
  enrollment,
334
338
  enrollmentState: { subscribe: enrollmentState.subscribe },
@@ -25,6 +25,8 @@ export let emailDelivery = null;
25
25
  export let onPrepare = null;
26
26
  export let onEmailSecondFactor = null;
27
27
  export let onSmsSecondFactor = null;
28
+ export let onOnlyWayEmail = null;
29
+ export let onPasskeySecondFactor = null;
28
30
  export let enrollment = null;
29
31
  export let enrollmentState = null;
30
32
  export let matchCode = null;
@@ -77,6 +79,14 @@ async function emailSecondFactor() {
77
79
  const r = await onEmailSecondFactor();
78
80
  emailed = r.error ? { attemptId, error: r.error } : { attemptId, sentTo: r.sentTo ?? "" };
79
81
  }
82
+ let onlyWayFor = null;
83
+ $: if (step.kind === "collect_second_factor" && onOnlyWayEmail && attempt?.id && onlyWayFor !== attempt.id) {
84
+ const attemptId = attempt.id;
85
+ onlyWayFor = attemptId;
86
+ void onOnlyWayEmail(attemptId).then((sentTo) => {
87
+ if (sentTo) emailed = { attemptId, sentTo };
88
+ });
89
+ }
80
90
  let texted = null;
81
91
  $: textedNow = texted && texted.attemptId === (attempt?.id ?? null) ? texted : null;
82
92
  async function smsSecondFactor() {
@@ -171,7 +181,7 @@ function handleSubmit(event) {
171
181
  <h1 class={cls('headerTitle', 'atlas-title')}>{t(titleKey)}</h1>
172
182
 
173
183
  {#each errors.filter((e) => !e.param) as error (error.code)}
174
- <p class={cls('formFieldError', 'atlas-error')} role="alert">{errorText(error)}{#if signInHref && step.kind === 'collect_email_code' && error.code === 'IDENTIFIER_EXISTS'}{' '}<a class="atlas-link" href={signInHref}>{t('signIn.title')}</a>{/if}</p>
184
+ <p class={cls('formFieldError', 'atlas-error')} role="alert">{errorText(error)}{#if signInHref && step.kind === 'collect_email_code' && error.code === 'IDENTIFIER_EXISTS' && error.meta?.['identifier'] !== 'username'}{' '}<a class="atlas-link" href={signInHref}>{t('signIn.title')}</a>{/if}</p>
175
185
  {/each}
176
186
 
177
187
  <form on:submit={handleSubmit}>
@@ -399,6 +409,17 @@ function handleSubmit(event) {
399
409
  {/if}
400
410
  {/if}
401
411
  {/if}
412
+ {#if step.kind === 'collect_second_factor' && onPasskeySecondFactor && attempt?.id}
413
+ <button
414
+ class={cls('formButtonSecondary', 'atlas-button-secondary')}
415
+ type="button"
416
+ data-atlas="passkey-second-factor"
417
+ disabled={busy}
418
+ on:click={() => onPasskeySecondFactor?.(attempt?.id ?? '')}
419
+ >
420
+ {t('mfa.enrollPasskeyOption')}
421
+ </button>
422
+ {/if}
402
423
  {#if showEmailOptions}
403
424
  {#each emailOptions as strategy (strategy)}
404
425
  <button
@@ -43,6 +43,8 @@ declare const __propDef: {
43
43
  sentTo?: string;
44
44
  error?: string;
45
45
  }>) | null;
46
+ /** On arrival at the second factor: emails the code if the inbox is the only way (masked address, or null). */ onOnlyWayEmail?: ((attemptId: string) => Promise<string | null>) | null;
47
+ /** "Use a passkey" at the second factor: leaves for the hosted page. */ onPasskeySecondFactor?: ((attemptId: string) => void) | null;
46
48
  /**
47
49
  * The MFA enrolment step (`FlowController.enrollment` and its state): the
48
50
  * authenticator app's QR code and setup key, or a texted code. Null: the step
@@ -99,6 +99,8 @@ onMount(() => {
99
99
  emailVerification={$settings?.account.emailVerification ?? 'code'}
100
100
  onEmailSecondFactor={$settings?.account.emailSecondFactor ? flow.sendEmailSecondFactor : null}
101
101
  onSmsSecondFactor={$settings && flow.mfaChoices().sms ? flow.sendSmsSecondFactor : null}
102
+ onOnlyWayEmail={$settings && flow.mfaChoices().emailCode ? flow.sendOnlyWayEmail : null}
103
+ onPasskeySecondFactor={$settings && flow.mfaChoices().passkey ? flow.continueWithPasskey : null}
102
104
  enrollment={flow.enrollment}
103
105
  enrollmentState={$enrollmentState}
104
106
  emailDelivery={$settings?.emailDelivery ?? null}
@@ -82,6 +82,8 @@ onMount(() => {
82
82
  matchCode={$flowState.matchCode ?? null}
83
83
  onEmailSecondFactor={$settings?.account.emailSecondFactor ? flow.sendEmailSecondFactor : null}
84
84
  onSmsSecondFactor={$settings && flow.mfaChoices().sms ? flow.sendSmsSecondFactor : null}
85
+ onOnlyWayEmail={$settings && flow.mfaChoices().emailCode ? flow.sendOnlyWayEmail : null}
86
+ onPasskeySecondFactor={$settings && flow.mfaChoices().passkey ? flow.continueWithPasskey : null}
85
87
  enrollment={flow.enrollment}
86
88
  enrollmentState={$enrollmentState}
87
89
  onPolicyDecline={flow.declinePolicy}
package/dist/version.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  import type { AtlasSdkInfo } from '@atlasauth/js';
2
2
  /** This package's version, sent as `X-Atlas-SDK` (kept in step with package.json by a test). */
3
- export declare const ATLAS_SVELTE_VERSION = "0.16.1";
3
+ export declare const ATLAS_SVELTE_VERSION = "0.16.3";
4
4
  export declare const ATLAS_SVELTE_SDK: AtlasSdkInfo;
package/dist/version.js CHANGED
@@ -1,3 +1,3 @@
1
1
  /** This package's version, sent as `X-Atlas-SDK` (kept in step with package.json by a test). */
2
- export const ATLAS_SVELTE_VERSION = '0.16.1';
2
+ export const ATLAS_SVELTE_VERSION = '0.16.3';
3
3
  export const ATLAS_SVELTE_SDK = { name: '@atlasauth/svelte', version: ATLAS_SVELTE_VERSION };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlasauth/svelte",
3
- "version": "0.16.1",
3
+ "version": "0.16.3",
4
4
  "type": "module",
5
5
  "svelte": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -23,9 +23,9 @@
23
23
  },
24
24
  "dependencies": {
25
25
  "@atlasauth/design": "0.3.0",
26
- "@atlasauth/js": "0.34.0",
27
- "@atlasauth/authz": "0.3.0",
28
- "@atlasauth/backend": "0.39.0"
26
+ "@atlasauth/js": "0.34.2",
27
+ "@atlasauth/backend": "0.39.0",
28
+ "@atlasauth/authz": "0.3.0"
29
29
  },
30
30
  "peerDependencies": {
31
31
  "svelte": ">=4",