cauthy-auth 0.18.0 → 0.20.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/dist/react.d.mts CHANGED
@@ -265,12 +265,34 @@ interface AuthMethods {
265
265
  * forms show it themselves.
266
266
  */
267
267
  captchaSiteKey?: string;
268
+ /**
269
+ * Whether the workspace signs some people in through their company's SSO,
270
+ * found by their work address (`signIn.ssoDiscover`, `signIn.sso`).
271
+ */
272
+ sso: boolean;
273
+ }
274
+ /** A company's sign-in (an SSO connection of the workspace). */
275
+ interface SSOConnection {
276
+ /** What `signIn.sso({ connection })` takes. */
277
+ id: string;
278
+ /** The company's name, for "Continue with …". */
279
+ name: string;
280
+ /** The address can sign in no other way: a password, link or provider answers `sso_required`. */
281
+ enforced: boolean;
268
282
  }
283
+ /**
284
+ * What holds a passkey, as its authenticator says: a security key (a YubiKey),
285
+ * one synced across the person's devices (iCloud Keychain, Google Password
286
+ * Manager), one on a single device (Windows Hello), or unknown (made before
287
+ * Cauthy kept this).
288
+ */
289
+ type PasskeyKind = 'security_key' | 'synced' | 'device' | 'unknown';
269
290
  /** A passkey of the signed-in account. Moments are UTC. */
270
291
  interface Passkey {
271
292
  id: string;
272
293
  /** What the person called it ("My laptop"), or "". */
273
294
  name: string;
295
+ kind: PasskeyKind;
274
296
  createdAt: string;
275
297
  /** When it last signed the person in, or null. */
276
298
  lastUsedAt: string | null;
@@ -444,10 +466,14 @@ interface AccountClient {
444
466
  * Makes a passkey on this device, on this page: the browser asks the person
445
467
  * for their fingerprint, face or screen lock. An account with a password
446
468
  * proves it again first. `cancelled`: the person closed the browser's prompt.
469
+ * `kind` sends the browser straight to one: `'device'`, this device's
470
+ * fingerprint, face or screen lock; `'security_key'`, a security key.
471
+ * Without it, the browser offers all.
447
472
  */
448
473
  addPasskey(form?: {
449
474
  password?: string;
450
475
  name?: string;
476
+ kind?: 'device' | 'security_key';
451
477
  }): Promise<AuthResult<'added', {
452
478
  passkey: Passkey;
453
479
  }>>;
@@ -565,6 +591,38 @@ interface AuthClient {
565
591
  social(provider: string, options?: {
566
592
  returnTo?: string;
567
593
  }): void;
594
+ /**
595
+ * Which company's sign-in a work address uses, if any (`found`), or
596
+ * `none`. It says so of the address's domain, never whether the address
597
+ * has an account.
598
+ */
599
+ ssoDiscover(form: {
600
+ email: string;
601
+ }): Promise<AuthResult<'found', {
602
+ connection: SSOConnection;
603
+ }> | AuthResult<'none'>>;
604
+ /**
605
+ * Sends the browser to the person's company (found by `email`, or named
606
+ * by `connection`); it comes back signed in, at `returnTo`. A sign-in the
607
+ * company refused comes back with `?auth_error=`. Any other way of
608
+ * signing in answers `sso_required` for an address its company holds to
609
+ * SSO: send it here then.
610
+ */
611
+ sso(target: {
612
+ email: string;
613
+ } | {
614
+ connection: string;
615
+ }, options?: {
616
+ returnTo?: string;
617
+ }): void;
618
+ /** Where `sso` sends the browser, for a plain link. */
619
+ ssoUrl(target: {
620
+ email: string;
621
+ } | {
622
+ connection: string;
623
+ }, options?: {
624
+ returnTo?: string;
625
+ }): string;
568
626
  /** Where `social` sends the browser, for a plain link. */
569
627
  socialUrl(provider: string, options?: {
570
628
  returnTo?: string;
@@ -687,7 +745,11 @@ interface AuthTexts {
687
745
  passkeysHelp: string;
688
746
  noPasskeys: string;
689
747
  passkeyName: string;
748
+ /** @deprecated Unused since 0.19.0: the account screen offers `addPasskeyDevice` and `addSecurityKey`. */
690
749
  addPasskey: string;
750
+ addPasskeyDevice: string;
751
+ addSecurityKey: string;
752
+ passkeyKinds: Record<'security_key' | 'synced' | 'device', string>;
691
753
  removePasskey: string;
692
754
  passkeyAdded: string;
693
755
  passkeyUnnamed: string;
@@ -707,6 +769,7 @@ interface AuthTexts {
707
769
  linkWith: string;
708
770
  unlink: string;
709
771
  linked: string;
772
+ ssoOnly: string;
710
773
  /** By error code; one missing shows what the server said. */
711
774
  errors: Record<string, string>;
712
775
  }
@@ -856,4 +919,4 @@ declare function useSession(): SessionContext;
856
919
  */
857
920
  declare function useAuth(): AuthClient & Pick<SessionContext, 'session' | 'status' | 'refresh'>;
858
921
 
859
- export { Account, type AccountClient, type Account$1 as AccountDetails, type AccountProps, type AccountSession, type AuthClient, type AuthError, type AuthFormProps, type AuthMethods, type AuthResult, type AuthTexts, CauthyProvider, type CauthyProviderProps, type ConnectedAgent, ForgotPassword, GoogleOneTap, type GoogleOneTapProps, type JsonValue, type LinkedIdentity, type Passkey, type PermissionOf, type Policy, type PublicSession, ResetPassword, type RoleOf, type SecondStep, type SessionContext, type SessionOrganization, type SessionUser, SignIn, SignUp, VerifyEmail, can, canAutofillPasskeys, canUsePasskeys, createAuthClient, definePolicy, useAuth, useSession };
922
+ export { Account, type AccountClient, type Account$1 as AccountDetails, type AccountProps, type AccountSession, type AuthClient, type AuthError, type AuthFormProps, type AuthMethods, type AuthResult, type AuthTexts, CauthyProvider, type CauthyProviderProps, type ConnectedAgent, ForgotPassword, GoogleOneTap, type GoogleOneTapProps, type JsonValue, type LinkedIdentity, type Passkey, type PasskeyKind, type PermissionOf, type Policy, type PublicSession, ResetPassword, type RoleOf, type SSOConnection, type SecondStep, type SessionContext, type SessionOrganization, type SessionUser, SignIn, SignUp, VerifyEmail, can, canAutofillPasskeys, canUsePasskeys, createAuthClient, definePolicy, useAuth, useSession };
package/dist/react.d.ts CHANGED
@@ -265,12 +265,34 @@ interface AuthMethods {
265
265
  * forms show it themselves.
266
266
  */
267
267
  captchaSiteKey?: string;
268
+ /**
269
+ * Whether the workspace signs some people in through their company's SSO,
270
+ * found by their work address (`signIn.ssoDiscover`, `signIn.sso`).
271
+ */
272
+ sso: boolean;
273
+ }
274
+ /** A company's sign-in (an SSO connection of the workspace). */
275
+ interface SSOConnection {
276
+ /** What `signIn.sso({ connection })` takes. */
277
+ id: string;
278
+ /** The company's name, for "Continue with …". */
279
+ name: string;
280
+ /** The address can sign in no other way: a password, link or provider answers `sso_required`. */
281
+ enforced: boolean;
268
282
  }
283
+ /**
284
+ * What holds a passkey, as its authenticator says: a security key (a YubiKey),
285
+ * one synced across the person's devices (iCloud Keychain, Google Password
286
+ * Manager), one on a single device (Windows Hello), or unknown (made before
287
+ * Cauthy kept this).
288
+ */
289
+ type PasskeyKind = 'security_key' | 'synced' | 'device' | 'unknown';
269
290
  /** A passkey of the signed-in account. Moments are UTC. */
270
291
  interface Passkey {
271
292
  id: string;
272
293
  /** What the person called it ("My laptop"), or "". */
273
294
  name: string;
295
+ kind: PasskeyKind;
274
296
  createdAt: string;
275
297
  /** When it last signed the person in, or null. */
276
298
  lastUsedAt: string | null;
@@ -444,10 +466,14 @@ interface AccountClient {
444
466
  * Makes a passkey on this device, on this page: the browser asks the person
445
467
  * for their fingerprint, face or screen lock. An account with a password
446
468
  * proves it again first. `cancelled`: the person closed the browser's prompt.
469
+ * `kind` sends the browser straight to one: `'device'`, this device's
470
+ * fingerprint, face or screen lock; `'security_key'`, a security key.
471
+ * Without it, the browser offers all.
447
472
  */
448
473
  addPasskey(form?: {
449
474
  password?: string;
450
475
  name?: string;
476
+ kind?: 'device' | 'security_key';
451
477
  }): Promise<AuthResult<'added', {
452
478
  passkey: Passkey;
453
479
  }>>;
@@ -565,6 +591,38 @@ interface AuthClient {
565
591
  social(provider: string, options?: {
566
592
  returnTo?: string;
567
593
  }): void;
594
+ /**
595
+ * Which company's sign-in a work address uses, if any (`found`), or
596
+ * `none`. It says so of the address's domain, never whether the address
597
+ * has an account.
598
+ */
599
+ ssoDiscover(form: {
600
+ email: string;
601
+ }): Promise<AuthResult<'found', {
602
+ connection: SSOConnection;
603
+ }> | AuthResult<'none'>>;
604
+ /**
605
+ * Sends the browser to the person's company (found by `email`, or named
606
+ * by `connection`); it comes back signed in, at `returnTo`. A sign-in the
607
+ * company refused comes back with `?auth_error=`. Any other way of
608
+ * signing in answers `sso_required` for an address its company holds to
609
+ * SSO: send it here then.
610
+ */
611
+ sso(target: {
612
+ email: string;
613
+ } | {
614
+ connection: string;
615
+ }, options?: {
616
+ returnTo?: string;
617
+ }): void;
618
+ /** Where `sso` sends the browser, for a plain link. */
619
+ ssoUrl(target: {
620
+ email: string;
621
+ } | {
622
+ connection: string;
623
+ }, options?: {
624
+ returnTo?: string;
625
+ }): string;
568
626
  /** Where `social` sends the browser, for a plain link. */
569
627
  socialUrl(provider: string, options?: {
570
628
  returnTo?: string;
@@ -687,7 +745,11 @@ interface AuthTexts {
687
745
  passkeysHelp: string;
688
746
  noPasskeys: string;
689
747
  passkeyName: string;
748
+ /** @deprecated Unused since 0.19.0: the account screen offers `addPasskeyDevice` and `addSecurityKey`. */
690
749
  addPasskey: string;
750
+ addPasskeyDevice: string;
751
+ addSecurityKey: string;
752
+ passkeyKinds: Record<'security_key' | 'synced' | 'device', string>;
691
753
  removePasskey: string;
692
754
  passkeyAdded: string;
693
755
  passkeyUnnamed: string;
@@ -707,6 +769,7 @@ interface AuthTexts {
707
769
  linkWith: string;
708
770
  unlink: string;
709
771
  linked: string;
772
+ ssoOnly: string;
710
773
  /** By error code; one missing shows what the server said. */
711
774
  errors: Record<string, string>;
712
775
  }
@@ -856,4 +919,4 @@ declare function useSession(): SessionContext;
856
919
  */
857
920
  declare function useAuth(): AuthClient & Pick<SessionContext, 'session' | 'status' | 'refresh'>;
858
921
 
859
- export { Account, type AccountClient, type Account$1 as AccountDetails, type AccountProps, type AccountSession, type AuthClient, type AuthError, type AuthFormProps, type AuthMethods, type AuthResult, type AuthTexts, CauthyProvider, type CauthyProviderProps, type ConnectedAgent, ForgotPassword, GoogleOneTap, type GoogleOneTapProps, type JsonValue, type LinkedIdentity, type Passkey, type PermissionOf, type Policy, type PublicSession, ResetPassword, type RoleOf, type SecondStep, type SessionContext, type SessionOrganization, type SessionUser, SignIn, SignUp, VerifyEmail, can, canAutofillPasskeys, canUsePasskeys, createAuthClient, definePolicy, useAuth, useSession };
922
+ export { Account, type AccountClient, type Account$1 as AccountDetails, type AccountProps, type AccountSession, type AuthClient, type AuthError, type AuthFormProps, type AuthMethods, type AuthResult, type AuthTexts, CauthyProvider, type CauthyProviderProps, type ConnectedAgent, ForgotPassword, GoogleOneTap, type GoogleOneTapProps, type JsonValue, type LinkedIdentity, type Passkey, type PasskeyKind, type PermissionOf, type Policy, type PublicSession, ResetPassword, type RoleOf, type SSOConnection, type SecondStep, type SessionContext, type SessionOrganization, type SessionUser, SignIn, SignUp, VerifyEmail, can, canAutofillPasskeys, canUsePasskeys, createAuthClient, definePolicy, useAuth, useSession };
package/dist/react.js CHANGED
@@ -268,6 +268,11 @@ function createAuthClient(options = {}) {
268
268
  }
269
269
  const linkUrl = (provider, o = {}) => `${base}/me/identities/link/${encodeURIComponent(provider)}${o.returnTo ? `?return_to=${encodeURIComponent(o.returnTo)}` : ""}`;
270
270
  const socialUrl = (provider, o = {}) => `${base}/sign-in/social/${encodeURIComponent(provider)}${o.returnTo ? `?return_to=${encodeURIComponent(o.returnTo)}` : ""}`;
271
+ const ssoUrl = (target, o = {}) => {
272
+ const q = new URLSearchParams("connection" in target ? { connection: target.connection } : { email: target.email.trim() });
273
+ if (o.returnTo) q.set("return_to", o.returnTo);
274
+ return `${base}/sign-in/sso?${q}`;
275
+ };
271
276
  const passkeyUrl = (o = {}) => {
272
277
  const q = new URLSearchParams({ method: "passkey" });
273
278
  if (o.returnTo) q.set("return_to", o.returnTo);
@@ -279,11 +284,11 @@ function createAuthClient(options = {}) {
279
284
  const res = await doFetch(`${base}/methods`, { credentials: "same-origin", headers: { accept: "application/json" } });
280
285
  if (res.ok) {
281
286
  const m = await res.json();
282
- return { providers: [], signUp: true, magicLink: false, passkey: false, ...m, passkeyHere: m.passkeyHere === true, passkeyOnCauthy: m.passkeyOnCauthy ?? m.passkey === true };
287
+ return { providers: [], signUp: true, magicLink: false, passkey: false, ...m, passkeyHere: m.passkeyHere === true, passkeyOnCauthy: m.passkeyOnCauthy ?? m.passkey === true, sso: m.sso === true };
283
288
  }
284
289
  } catch {
285
290
  }
286
- return { providers: [], signUp: true, magicLink: false, passkey: false, passkeyHere: false, passkeyOnCauthy: false };
291
+ return { providers: [], signUp: true, magicLink: false, passkey: false, passkeyHere: false, passkeyOnCauthy: false, sso: false };
287
292
  }
288
293
  async function ceremony(begin, finish, form, run, more = {}) {
289
294
  const begun = await post(begin, form, null);
@@ -381,6 +386,11 @@ function createAuthClient(options = {}) {
381
386
  socialUrl,
382
387
  social(provider, o) {
383
388
  window.location.assign(socialUrl(provider, o));
389
+ },
390
+ ssoDiscover: (f) => post("/sign-in/sso/discover", { email: f.email.trim() }, checkEmail(f.email)),
391
+ ssoUrl,
392
+ sso(target, o) {
393
+ window.location.assign(ssoUrl(target, o));
384
394
  }
385
395
  },
386
396
  signUp: {
@@ -439,7 +449,7 @@ function createAuthClient(options = {}) {
439
449
  },
440
450
  addPasskey(f = {}) {
441
451
  if (!canUsePasskeys()) return Promise.resolve({ ok: false, error: { code: "passkey_unavailable", message: "This browser cannot make passkeys." } });
442
- return ceremony("/me/passkeys/add/begin", "/me/passkeys/add/finish", { password: f.password ?? "" }, makePasskey, { name: f.name ?? "" });
452
+ return ceremony("/me/passkeys/add/begin", "/me/passkeys/add/finish", { password: f.password ?? "", ...f.kind ? { kind: f.kind } : {} }, makePasskey, { name: f.name ?? "" });
443
453
  },
444
454
  removePasskey: (id) => post("/me/passkeys/remove", { id }, null)
445
455
  },
@@ -580,6 +590,9 @@ var TEXTS = {
580
590
  passkeyName: "A name for this device (optional)",
581
591
  addPasskey: "Add a passkey on this device",
582
592
  removePasskey: "Remove",
593
+ addPasskeyDevice: "Add this device (fingerprint or face)",
594
+ addSecurityKey: "Add a security key",
595
+ passkeyKinds: { security_key: "Security key", synced: "Synced across your devices", device: "On this device only" },
583
596
  passkeyAdded: "The passkey was added. Next time, sign in with it.",
584
597
  passkeyUnnamed: "Passkey",
585
598
  sessions: "Where you are signed in",
@@ -598,6 +611,7 @@ var TEXTS = {
598
611
  linkWith: "Link",
599
612
  unlink: "Unlink",
600
613
  linked: "The account was linked.",
614
+ ssoOnly: "Your company signs you in to this account.",
601
615
  errors: {
602
616
  required: "This is required.",
603
617
  invalid_email: "That does not look like an email address.",
@@ -626,7 +640,15 @@ var TEXTS = {
626
640
  captcha_failed: "The check that you are not a bot did not pass. Try it again.",
627
641
  passkey_failed: "The passkey could not be used. Try again, or sign in another way.",
628
642
  passkey_exists: "This device already has a passkey for your account.",
629
- passkey_unavailable: "Passkeys cannot be used here."
643
+ passkey_unavailable: "Passkeys cannot be used here.",
644
+ sso_required: "This address signs in through its company.",
645
+ sso_not_found: "No company sign-in answers for this address.",
646
+ sso_failed: "The sign-in with your company could not be completed. Try again.",
647
+ sso_unavailable: "Your company's sign-in cannot be reached right now. Try again in a moment.",
648
+ invalid_assertion: "Your company's answer could not be checked. Try again.",
649
+ provisioning_disabled: "Your company's sign-in worked, but this workspace has no account for you. Ask its admin to add you.",
650
+ connection_inactive: "Your company's sign-in is turned off for this workspace.",
651
+ invalid_grant: "That sign-in expired. Try again."
630
652
  }
631
653
  },
632
654
  es: {
@@ -692,6 +714,9 @@ var TEXTS = {
692
714
  passkeyName: "Un nombre para este dispositivo (opcional)",
693
715
  addPasskey: "Agregar una llave en este dispositivo",
694
716
  removePasskey: "Quitar",
717
+ addPasskeyDevice: "Agregar este dispositivo (huella o rostro)",
718
+ addSecurityKey: "Agregar una llave f\xEDsica",
719
+ passkeyKinds: { security_key: "Llave f\xEDsica", synced: "Sincronizada entre tus dispositivos", device: "Solo en un dispositivo" },
695
720
  passkeyAdded: "La llave de acceso qued\xF3 agregada. La pr\xF3xima vez, entra con ella.",
696
721
  passkeyUnnamed: "Llave de acceso",
697
722
  sessions: "D\xF3nde tienes la sesi\xF3n abierta",
@@ -710,6 +735,7 @@ var TEXTS = {
710
735
  linkWith: "Vincular",
711
736
  unlink: "Desvincular",
712
737
  linked: "La cuenta qued\xF3 vinculada.",
738
+ ssoOnly: "A esta cuenta entras con el inicio de sesi\xF3n de tu empresa.",
713
739
  errors: {
714
740
  required: "Este dato es obligatorio.",
715
741
  invalid_email: "Eso no parece un correo.",
@@ -738,7 +764,15 @@ var TEXTS = {
738
764
  captcha_failed: "La verificaci\xF3n de que no eres un robot no pas\xF3. Int\xE9ntalo de nuevo.",
739
765
  passkey_failed: "No se pudo usar la llave de acceso. Intenta de nuevo o entra de otra forma.",
740
766
  passkey_exists: "Este dispositivo ya tiene una llave de acceso de tu cuenta.",
741
- passkey_unavailable: "Aqu\xED no se pueden usar llaves de acceso."
767
+ passkey_unavailable: "Aqu\xED no se pueden usar llaves de acceso.",
768
+ sso_required: "Este correo entra con el inicio de sesi\xF3n de su empresa.",
769
+ sso_not_found: "Ninguna empresa usa inicio de sesi\xF3n propio con este correo.",
770
+ sso_failed: "No se pudo completar el ingreso con tu empresa. Intenta de nuevo.",
771
+ sso_unavailable: "Ahora no se puede llegar al inicio de sesi\xF3n de tu empresa. Intenta en un momento.",
772
+ invalid_assertion: "No se pudo verificar la respuesta de tu empresa. Intenta de nuevo.",
773
+ provisioning_disabled: "El ingreso con tu empresa funcion\xF3, pero este workspace no tiene una cuenta para ti. P\xEDdele a su admin que te agregue.",
774
+ connection_inactive: "El inicio de sesi\xF3n de tu empresa est\xE1 apagado en este workspace.",
775
+ invalid_grant: "Ese ingreso venci\xF3. Intenta de nuevo."
742
776
  }
743
777
  }
744
778
  };
@@ -931,6 +965,25 @@ function SignIn(props) {
931
965
  };
932
966
  const autofill = (0, import_react.useRef)(null);
933
967
  const [offer, setOffer] = (0, import_react.useState)(0);
968
+ const [company, setCompany] = (0, import_react.useState)(null);
969
+ (0, import_react.useEffect)(() => {
970
+ if (!methods?.sso || checkEmail(email) !== null) return setCompany(null);
971
+ let current = true;
972
+ const t = setTimeout(() => {
973
+ void auth.signIn.ssoDiscover({ email }).then((r) => {
974
+ if (current) setCompany(r.ok && r.status === "found" ? r.connection : null);
975
+ });
976
+ }, 350);
977
+ return () => {
978
+ current = false;
979
+ clearTimeout(t);
980
+ };
981
+ }, [email, methods]);
982
+ const toCompany = () => {
983
+ autofill.current?.abort();
984
+ f.setBusy(true);
985
+ auth.signIn.sso({ email }, { returnTo: props.returnTo });
986
+ };
934
987
  (0, import_react.useEffect)(() => {
935
988
  void auth.methods().then(setMethods);
936
989
  const back = returnedError();
@@ -963,9 +1016,11 @@ function SignIn(props) {
963
1016
  autofill.current?.abort();
964
1017
  f.setBusy(true);
965
1018
  f.setError(null);
1019
+ if (step === "password" && company?.enforced) return toCompany();
966
1020
  const r = step === "code" ? await auth.signIn.mfa({ code }) : step === "sent" ? await auth.signIn.emailCode({ code }) : await auth.signIn.email({ email, password, captchaToken: challenge.token });
967
1021
  f.setBusy(false);
968
1022
  if (step === "password") challenge.renew();
1023
+ if (!r.ok && r.error.code === "sso_required") return toCompany();
969
1024
  if (!r.ok) {
970
1025
  setOffer((n) => n + 1);
971
1026
  return f.setError(r.error);
@@ -991,6 +1046,7 @@ function SignIn(props) {
991
1046
  const r = await auth.signIn.magicLink({ email, captchaToken: challenge.token, create: props.createAccounts, returnTo: props.returnTo });
992
1047
  f.setBusy(false);
993
1048
  challenge.renew();
1049
+ if (!r.ok && r.error.code === "sso_required") return toCompany();
994
1050
  if (!r.ok) return f.setError(r.error);
995
1051
  setStep("sent");
996
1052
  };
@@ -1025,23 +1081,31 @@ function SignIn(props) {
1025
1081
  methods?.passkey && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void usePasskey(), children: f.t.usePasskey }),
1026
1082
  ((methods?.providers.length ?? 0) > 0 || methods?.passkey) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("separator"), children: f.t.orWithEmail }),
1027
1083
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "email", type: "email", label: f.t.email, value: email, autoComplete: methods?.passkeyHere ? "username webauthn" : "username", required: true, onChange: setEmail }),
1028
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1029
- Field,
1030
- {
1031
- f,
1032
- name: "password",
1033
- type: "password",
1034
- label: f.t.password,
1035
- value: password,
1036
- autoComplete: "current-password",
1037
- required: true,
1038
- onChange: setPassword,
1039
- aside: props.links?.forgotPassword ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { className: f.cx("link"), href: props.links.forgotPassword, children: f.t.forgotPassword }) : void 0
1040
- }
1041
- ),
1042
- challenge.node,
1043
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.signIn }),
1044
- methods?.magicLink && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void sendLink(), children: f.t.emailMeALink }),
1084
+ company && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(f.Button, { type: "button", disabled: f.busy, className: f.cx(company.enforced ? "button" : "providerButton"), onClick: toCompany, children: [
1085
+ f.t.continueWith,
1086
+ " ",
1087
+ company.name
1088
+ ] }),
1089
+ company?.enforced && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("notice"), children: f.t.ssoOnly }),
1090
+ !company?.enforced && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1091
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1092
+ Field,
1093
+ {
1094
+ f,
1095
+ name: "password",
1096
+ type: "password",
1097
+ label: f.t.password,
1098
+ value: password,
1099
+ autoComplete: "current-password",
1100
+ required: true,
1101
+ onChange: setPassword,
1102
+ aside: props.links?.forgotPassword ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { className: f.cx("link"), href: props.links.forgotPassword, children: f.t.forgotPassword }) : void 0
1103
+ }
1104
+ ),
1105
+ challenge.node,
1106
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.signIn })
1107
+ ] }),
1108
+ methods?.magicLink && !company?.enforced && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void sendLink(), children: f.t.emailMeALink }),
1045
1109
  methods?.signUp !== false && props.links?.signUp && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: f.cx("footer"), children: [
1046
1110
  f.t.noAccount,
1047
1111
  " ",
@@ -1267,9 +1331,9 @@ function PasskeysPart({ props, account, accountUrl, when }) {
1267
1331
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { className: f.cx("link"), href: accountUrl, children: f.t.managePasskeys })
1268
1332
  ] });
1269
1333
  }
1270
- const add = async (e) => {
1271
- e.preventDefault();
1272
- const r = await run(() => auth.account.addPasskey({ name, password }));
1334
+ const add = async (e, kind = "device") => {
1335
+ e?.preventDefault();
1336
+ const r = await run(() => auth.account.addPasskey({ name, password, kind }));
1273
1337
  if (!r.ok) {
1274
1338
  if (r.error.code === "cancelled") f.setError(null);
1275
1339
  return;
@@ -1289,6 +1353,7 @@ function PasskeysPart({ props, account, accountUrl, when }) {
1289
1353
  general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
1290
1354
  list.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("notice"), children: f.t.noPasskeys }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: f.cx("sessions"), children: list.map((p) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { className: f.cx("session"), children: [
1291
1355
  p.name || f.t.passkeyUnnamed,
1356
+ p.kind !== "unknown" ? ` \xB7 ${f.t.passkeyKinds[p.kind]}` : "",
1292
1357
  " \xB7 ",
1293
1358
  when(p.createdAt),
1294
1359
  " ",
@@ -1296,7 +1361,8 @@ function PasskeysPart({ props, account, accountUrl, when }) {
1296
1361
  ] }, p.id)) }),
1297
1362
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "name", type: "text", label: f.t.passkeyName, value: name, autoComplete: "off", onChange: setName }),
1298
1363
  account.hasPassword && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "password", type: "password", label: f.t.currentPassword, value: password, autoComplete: "current-password", required: true, onChange: setPassword }),
1299
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.addPasskey }),
1364
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.addPasskeyDevice }),
1365
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void add(null, "security_key"), children: f.t.addSecurityKey }),
1300
1366
  done && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "status", className: f.cx("notice"), children: done })
1301
1367
  ] });
1302
1368
  }