@atlasauth/svelte 0.12.0 → 0.13.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/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # @atlasauth/svelte changelog
2
2
 
3
+ ## 0.13.0 — 2026-10-30
4
+
5
+ - `<SignIn>` offers "Sign in with a passkey" and passkey autofill on the identifier field when the instance enables passkeys.
6
+
3
7
  ## 0.12.0 — 2026-10-28
4
8
 
5
9
  - `ManageCiba` component lists sign-in approvals (approve or deny) and manages the CIBA PIN; mounted in the user profile.
@@ -1,5 +1,5 @@
1
1
  import { type Readable } from 'svelte/store';
2
- import { type OrganizationAuditLogOptions, type OrganizationAuditLogState, type CaptchaController, type FlowState, type InstanceSettings, type ProviderToken, type SignOutOptions, type SignOutResult, PolicyAcceptanceController, type MfaChoices, type MfaEnrollmentController, type MfaEnrollmentState } from '@atlasauth/js';
2
+ import { type OrganizationAuditLogOptions, type OrganizationAuditLogState, type CaptchaController, type FlowState, type InstanceSettings, type ProviderToken, type SignOutOptions, type SignOutResult, PolicyAcceptanceController, type MfaChoices, type MfaEnrollmentController, type MfaEnrollmentState, type PasskeySignInController } from '@atlasauth/js';
3
3
  import { type AuthStatus } from './context';
4
4
  import { type ProtectCondition } from './protect';
5
5
  import type { AtlasOrganizationMembership, AtlasSession, AtlasUser } from './types';
@@ -174,6 +174,20 @@ export interface FlowController {
174
174
  loadPolicies(): Promise<void>;
175
175
  /** The user declined a policy a sign-in was waiting on: cancels the attempt and says why. */
176
176
  declinePolicy(title: string): void;
177
+ /**
178
+ * "Continue with Atlas" (when `settings.signInWithAtlas` is set): starts the
179
+ * SSO sign-in and sends the browser to Atlas. Resolves the server's message
180
+ * when it could not start.
181
+ */
182
+ signInWithAtlas(connectionId: string): Promise<{
183
+ error?: string;
184
+ }>;
185
+ /**
186
+ * Passkey sign-in (the "Sign in with a passkey" button and passkey autofill on
187
+ * the identifier field), offered when `settings.passkeySignIn` is on. The
188
+ * outcome goes through the same completion as every sign-in.
189
+ */
190
+ passkey: PasskeySignInController;
177
191
  /** Stop the magic-link poll loop and remove the captcha widgets — call from `onDestroy`. */
178
192
  destroy(): void;
179
193
  }
package/dist/accessors.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { derived, get, readable, writable } from 'svelte/store';
2
- import { FapiClient, advanceWithCaptcha, atlasCaptchaLabels, createCaptchaController, createPasskeySignUp, createSignUp, finishPasskeySignUp, fetchInstanceSettings, fetchLastSignInMethod, flowNeedsCaptcha, signInCaptchaOptions, flowFromPending, pollAttempt, prepareFactor, shouldKeepPolling, createOrganizationAuditLog, createMfaEnrollment, acknowledgeRecoveryCodes, pendingRecoveryCodes, mfaChoicesFrom, prepareSecondFactorCode, resendSignUpVerification, PolicyAcceptanceController, loadPolicies as loadPolicyList, initialFlow as emptyFlow, } from '@atlasauth/js';
2
+ import { FapiClient, advanceWithCaptcha, atlasCaptchaLabels, createCaptchaController, createPasskeySignInController, createPasskeySignUp, createSignUp, finishPasskeySignUp, fetchInstanceSettings, fetchLastSignInMethod, startSignInWithAtlas, flowNeedsCaptcha, signInCaptchaOptions, flowFromPending, pollAttempt, prepareFactor, shouldKeepPolling, createOrganizationAuditLog, createMfaEnrollment, acknowledgeRecoveryCodes, pendingRecoveryCodes, mfaChoicesFrom, prepareSecondFactorCode, 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';
@@ -261,7 +261,15 @@ function createFlow() {
261
261
  maybeStartPolling();
262
262
  return next.errors.length === 0;
263
263
  }
264
+ const passkey = createPasskeySignInController({
265
+ client,
266
+ onStart: () => state.set({ ...get(state), busy: true, errors: [] }),
267
+ onResult: finish,
268
+ captcha: () => captcha,
269
+ cancelledMessage: () => t('signIn.passkeyCancelled'),
270
+ });
264
271
  function destroy() {
272
+ passkey.stopAutofill();
265
273
  stopPolling();
266
274
  unsubscribeCaptcha();
267
275
  unsubscribeEnrollment();
@@ -269,6 +277,16 @@ function createFlow() {
269
277
  captcha.destroy();
270
278
  signUpCaptcha.destroy();
271
279
  }
280
+ async function signInWithAtlas(connectionId) {
281
+ if (typeof window === 'undefined')
282
+ return {};
283
+ const started = await startSignInWithAtlas(client, connectionId, window.location.href);
284
+ if ('url' in started) {
285
+ window.location.assign(started.url);
286
+ return {};
287
+ }
288
+ return { error: started.error };
289
+ }
272
290
  return {
273
291
  state: { subscribe: state.subscribe },
274
292
  submit,
@@ -290,6 +308,8 @@ function createFlow() {
290
308
  policies,
291
309
  loadPolicies,
292
310
  declinePolicy,
311
+ signInWithAtlas,
312
+ passkey,
293
313
  destroy,
294
314
  };
295
315
  }
@@ -85,7 +85,7 @@ function usePassword() {
85
85
  $: fields = (() => {
86
86
  switch (step.kind) {
87
87
  case "collect_identifier":
88
- return [{ name: "identifier", labelKey: "signIn.identifierLabel", type: "email" }];
88
+ return [{ name: "identifier", labelKey: "signIn.identifierLabel", type: "email", autocomplete: "username webauthn" }];
89
89
  case "collect_first_factor":
90
90
  if (sent === "email_code") {
91
91
  return [{ name: "code", labelKey: "signIn.emailCodeLabel", type: "text" }];
@@ -288,6 +288,7 @@ function handleSubmit(event) {
288
288
  class={cls('formFieldInput', 'atlas-input')}
289
289
  type={f.type}
290
290
  name={f.name}
291
+ autocomplete={f.autocomplete}
291
292
  value={values[f.name] ?? ''}
292
293
  on:input={(e) => (values[f.name] = e.currentTarget.value)}
293
294
  />
@@ -1,5 +1,5 @@
1
- <script>import { onDestroy } from "svelte";
2
- import { lastUsedMethodLabel, pendingRecoveryCodes, signUp as hostedSignUp } from "@atlasauth/js";
1
+ <script>import { onDestroy, onMount } from "svelte";
2
+ import { lastUsedMethodLabel, passkeysSupported, pendingRecoveryCodes, signUp as hostedSignUp } from "@atlasauth/js";
3
3
  import { signIn } from "../accessors";
4
4
  import { useAtlas } from "../context";
5
5
  import { classFor } from "../appearance";
@@ -34,6 +34,24 @@ $: recoveryCodes = pendingRecoveryCodes($flowState);
34
34
  $: lastLabel = !$flowState.attempt ? lastUsedMethodLabel($lastMethod, t) : null;
35
35
  const drawn = (..._deps) => flow.captcha.needed ? flow.captcha : null;
36
36
  $: captcha = drawn($settings, $flowState);
37
+ $: atlasConnection = !$flowState.attempt ? $settings?.signInWithAtlas?.connectionId ?? null : null;
38
+ let atlasError = null;
39
+ async function continueWithAtlas() {
40
+ if (!atlasConnection) return;
41
+ atlasError = null;
42
+ atlasError = (await flow.signInWithAtlas(atlasConnection)).error ?? null;
43
+ }
44
+ let supported = false;
45
+ onMount(() => {
46
+ supported = passkeysSupported();
47
+ });
48
+ $: passkeyOffered = supported && !$flowState.attempt && $settings?.passkeySignIn === true;
49
+ $: if (passkeyOffered) flow.passkey.armAutofill();
50
+ else flow.passkey.stopAutofill();
51
+ function submitIdentifier(values) {
52
+ flow.passkey.stopAutofill();
53
+ flow.submit(values);
54
+ }
37
55
  </script>
38
56
 
39
57
  {#if lastLabel}
@@ -42,6 +60,20 @@ $: captcha = drawn($settings, $flowState);
42
60
  </p>
43
61
  {/if}
44
62
 
63
+ {#if atlasConnection && !recoveryCodes}
64
+ <button
65
+ type="button"
66
+ class={classFor('socialButton', atlas.appearance, 'atlas-social-button')}
67
+ data-atlas-provider="atlas"
68
+ on:click={continueWithAtlas}
69
+ >
70
+ {t('signIn.continueWithAtlas')}
71
+ </button>
72
+ {#if atlasError}
73
+ <p class={classFor('formFieldError', atlas.appearance, 'atlas-error')} role="alert">{atlasError}</p>
74
+ {/if}
75
+ {/if}
76
+
45
77
  {#if recoveryCodes}
46
78
  <RecoveryCodes codes={recoveryCodes} busy={$flowState.busy} onSaved={flow.saveRecoveryCodes} />
47
79
  {:else}
@@ -51,7 +83,7 @@ $: captcha = drawn($settings, $flowState);
51
83
  errors={$flowState.errors}
52
84
  sessionKept={$flowState.sessionKept}
53
85
  titleKey="signIn.title"
54
- onSubmit={flow.submit}
86
+ onSubmit={submitIdentifier}
55
87
  onPrepare={flow.prepare}
56
88
  onEmailSecondFactor={$settings?.account.emailSecondFactor ? flow.sendEmailSecondFactor : null}
57
89
  onSmsSecondFactor={$settings && flow.mfaChoices().sms ? flow.sendSmsSecondFactor : null}
@@ -63,6 +95,18 @@ $: captcha = drawn($settings, $flowState);
63
95
  />
64
96
  {/if}
65
97
 
98
+ {#if passkeyOffered && !recoveryCodes}
99
+ <button
100
+ type="button"
101
+ class={classFor('socialButton', atlas.appearance, 'atlas-social-button atlas-passkey-button')}
102
+ data-atlas-passkey-sign-in=""
103
+ disabled={$flowState.busy}
104
+ on:click={() => flow.passkey.signIn()}
105
+ >
106
+ {t('signIn.passkeyButton')}
107
+ </button>
108
+ {/if}
109
+
66
110
  {#if promptHref}
67
111
  <p class={classFor('footerAction', atlas.appearance, 'atlas-footer')} data-atlas-sign-up-prompt="">
68
112
  {t('signIn.noAccount')} <a class="atlas-link" href={promptHref}>{t('signIn.signUpLink')}</a>
package/dist/i18n.js CHANGED
@@ -22,6 +22,7 @@ export const EN_US = {
22
22
  'signIn.signUpLink': 'Sign up',
23
23
  'signIn.orDivider': 'or',
24
24
  'signIn.socialButton': 'Continue with {provider}',
25
+ 'signIn.continueWithAtlas': 'Continue with Atlas',
25
26
  'signIn.emailCodeLabel': 'Verification code',
26
27
  'signIn.emailCodeOption': 'Email me a code',
27
28
  'signIn.emailLinkOption': 'Email me a sign-in link',
@@ -32,6 +33,8 @@ export const EN_US = {
32
33
  'signIn.resendCode': 'Resend code',
33
34
  'signIn.resendLink': 'Resend link',
34
35
  'signIn.passwordOption': 'Use your password instead',
36
+ 'signIn.passkeyButton': 'Sign in with a passkey',
37
+ 'signIn.passkeyCancelled': "Passkey sign-in didn't finish. Try again, or use another way to sign in.",
35
38
  'signIn.signedIn': 'You\'re signed in.',
36
39
  'signIn.sessionNotKept': 'You signed in, but this browser didn\'t keep the session. If it blocks third-party cookies, allow cookies for this site and try again.',
37
40
  'signIn.checkOtherTab': 'You can close this tab — you are signed in where you started.',
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.11.0";
3
+ export declare const ATLAS_SVELTE_VERSION = "0.13.0";
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.11.0';
2
+ export const ATLAS_SVELTE_VERSION = '0.13.0';
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.12.0",
3
+ "version": "0.13.0",
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.26.1",
26
+ "@atlasauth/js": "0.27.0",
27
27
  "@atlasauth/authz": "0.3.0",
28
- "@atlasauth/backend": "0.27.0"
28
+ "@atlasauth/backend": "0.28.0"
29
29
  },
30
30
  "peerDependencies": {
31
31
  "svelte": ">=4",