Authentication

Sign users in with email + password, magic codes or social providers. <u-signin-root> drives a multi-step flow: you declare one <u-signin-step> per state, the SDK decides which one is active — and <u-conditional-render> adapts each step to what the account actually supports.

View this guide as Markdown

Complete sign-in flow

One u-signin-root covering the whole journey: email → verification (password, magic code, or reset), plus the magic-code, reset-password and missing-fields steps. Conditions like auth.passwordEnabled and auth.magicCodeEnabled make the same markup work for every account type — passwordless accounts are offered a password reset instead of a dead end.

  • Multi-step state machine — one <u-signin-step> per state, SDK-controlled
  • Adaptive verification — password / magic code / reset offered per account
  • Missing fields — collects newly-required profile fields at sign-in
  • Auto-hide when signed in — the form disappears once authenticated
<!-- The root manages the flow; each u-signin-step shows when its step is active.
     The whole form hides automatically once the user is signed in. -->
<u-signin-root id="sign-in-root" class="flex flex-col gap-2">
  <!-- Step 1: email -->
  <u-signin-step name="email">
    <u-email-field
      placeholder="Enter your email"
      class-name="px-4 py-2 mb-2 border border-border rounded-lg w-full"></u-email-field>
    <u-error-message for="email" class-name="mb-2 text-sm text-danger"></u-error-message>
    <u-submit-button
      for="email"
      text="Continue with email"
      class-name="px-4 py-2 text-white bg-accent hover:bg-accent-hover rounded-lg disabled:opacity-50 w-full cursor-pointer"
    ></u-submit-button>
    <u-error-message for="general" class-name="mt-2 text-sm text-danger"></u-error-message>
  </u-signin-step>

  <!-- Step 2: verification — offers exactly what the account supports -->
  <u-signin-step name="verification">
    <u-back-button class-name="mb-3 text-sm text-primary cursor-pointer">← Back</u-back-button>

    <u-conditional-render when="auth.passwordEnabled">
      <u-password-field
        placeholder="Enter your password"
        class-name="px-4 py-2 mb-1 border border-border rounded-lg w-full"></u-password-field>
      <u-reset-password-button class-name="mb-3 text-sm text-primary cursor-pointer"
      ></u-reset-password-button>
      <u-error-message for="password" class-name="mb-2 text-sm text-danger"></u-error-message>
      <u-error-message for="resetPassword" class-name="mb-2 text-sm text-danger"></u-error-message>
      <u-submit-button
        for="password"
        class-name="px-4 py-2 text-white bg-accent hover:bg-accent-hover rounded-lg w-full cursor-pointer"
      ></u-submit-button>
    </u-conditional-render>

    <u-conditional-render when="auth.magicCodeEnabled">
      <u-conditional-render when="auth.magicCodeSent" is="false">
        <u-send-magic-code-button
          text="Send me a magic code instead"
          class-name="mt-3 px-4 py-2 text-primary bg-primary-soft rounded-lg text-center w-full cursor-pointer disabled:opacity-50"
        ></u-send-magic-code-button>
      </u-conditional-render>
    </u-conditional-render>

    <!-- Passwordless account? Offer to set one. -->
    <u-conditional-render when="auth.passwordEnabled" not>
      <u-reset-password-button class-name="mt-3 text-sm text-primary cursor-pointer">
        Set a password for this account
      </u-reset-password-button>
    </u-conditional-render>
  </u-signin-step>

  <!-- Step 3 (magic code path) -->
  <u-signin-step name="magic-code">
    <p class="mb-3 text-center text-sm text-text-light">Enter the code we sent to your email</p>
    <u-magic-code-field class-name="px-4 py-2 mb-2 rounded-lg"></u-magic-code-field>
    <u-error-message for="magicCode" class-name="mb-2 text-center text-sm text-danger"
    ></u-error-message>
    <u-send-magic-code-button
      text="Resend magic code"
      class-name="px-4 py-2 text-primary bg-primary-soft rounded-lg text-center w-full cursor-pointer disabled:opacity-50"
    ></u-send-magic-code-button>
  </u-signin-step>

  <!-- Reset-password path (opened via u-reset-password-button email link) -->
  <u-signin-step name="reset-password">
    <p class="mb-3 text-center text-sm text-text-light">Choose a new password</p>
    <u-password-field
      for="new-password"
      placeholder="New password"
      class-name="px-4 py-2 mb-2 border border-border rounded-lg w-full"></u-password-field>
    <u-password-field
      for="password-confirmation"
      placeholder="Confirm password"
      class-name="px-4 py-2 mb-2 border border-border rounded-lg w-full"></u-password-field>
    <u-error-message for="resetPassword" class-name="mb-2 text-sm text-danger"></u-error-message>
    <u-submit-button
      for="resetPassword"
      class-name="px-4 py-2 text-white bg-accent hover:bg-accent-hover rounded-lg w-full cursor-pointer"
    ></u-submit-button>
  </u-signin-step>

  <!-- Shown when the tenant requires profile fields the account is missing -->
  <u-signin-step name="missing-fields">
    <p class="mb-3 text-center text-sm text-text-light">One last step — complete your profile</p>
    <u-missing-field></u-missing-field>
    <u-missing-fields-submit-button></u-missing-fields-submit-button>
  </u-signin-step>

  <!-- Unknown email → the flow switches to this step -->
  <u-signin-step name="registration">
    <u-back-button class-name="mb-3 text-sm text-primary cursor-pointer">← Back</u-back-button>
    <p class="text-sm text-text-light">
      No account found for this email. See the
      <a href="/registration" class="text-primary underline">registration flow demo</a>
      for the full multi-step sign-up.
    </p>
  </u-signin-step>
</u-signin-root>

<u-signed-in>
  <div class="rounded-lg bg-success-bg p-4 text-sm text-success-text">
    You are signed in — the sign-in form hides itself automatically.
  </div>
</u-signed-in>
Live demo
← Back Set a password for this account

Enter the code we sent to your email

Choose a new password

One last step — complete your profile

← Back

No account found for this email. See the registration flow demo for the full multi-step sign-up.

You are signed in — the sign-in form hides itself automatically.

Social login

u-social-login-button starts the provider's OAuth flow in a Unidy-hosted dialog. Providers are configured per tenant; custom providers and custom button icons are supported via the provider attribute and icon slot.

  • Google, LinkedIn, Apple, … — one attribute per provider
  • Custom providers — any OAuth provider configured in your tenant
  • Theming — light/dark themes, icon-only mode, icon slot
<!-- Placed directly inside u-signin-root (outside any step), the buttons are
     always available while the user is signed out -->
<u-signin-root class="flex flex-col gap-3">
  <u-social-login-button text="Continue with Google" provider="google" theme="dark"
  ></u-social-login-button>
  <u-social-login-button text="Continue with LinkedIn" provider="linkedin"></u-social-login-button>
  <u-social-login-button text="Continue with Apple" provider="apple" theme="dark"
  ></u-social-login-button>

  <!-- Custom provider configured in your Unidy tenant -->
  <u-social-login-button text="Continue with Some Provider" provider="some-provider"
  ></u-social-login-button>

  <!-- Custom icon via the icon slot -->
  <u-social-login-button text="Continue with Unidy" provider="unidy" theme="dark" icon-only="true">
    <img
      slot="icon"
      src="https://www.unidy.io/unidy-logo-white.svg"
      alt="Unidy Logo"
      class="w-20 h-6"
    />
  </u-social-login-button>
</u-signin-root>

<u-signed-in>
  <p class="text-sm text-success-text">Signed in — social login buttons are hidden.</p>
</u-signed-in>
Live demo
Unidy Logo

Signed in — social login buttons are hidden.

Passkey sign-in

u-passkey performs WebAuthn discoverable-credential sign-in. Disabled on this staging demo: WebAuthn binds passkeys to the tenant's domain, so this section activates once the integration runs on the customer's final URL.

  • One-tap sign-in — Face ID, fingerprint, or device PIN
  • Discoverable credentials — works without typing an email first
Live demo
Les passkeys nécessitent que la démo tourne sur le domaine du client (exigence WebAuthn). Cette section sera activée sur l'URL finale.

Auth events

Every sign-in root emits DOM events you can wire up with plain addEventListener: authEvent on successful sign-in, errorEvent on failures, and logout from u-logout-button. This is how you sync your own UI (header state, redirects, analytics) with the SDK.

  • authEvent / errorEvent / logout — plain DOM events, no SDK API needed
  • Live event log — sign in above and watch the events arrive
<div class="rounded-lg border border-border bg-background-light p-4">
  <p class="mb-2 text-sm font-medium">Event log</p>
  <ul id="auth-event-log" class="flex flex-col gap-1 font-mono text-xs text-text-light">
    <li>Waiting for auth events… (use the sign-in flow above)</li>
  </ul>
</div>

<script is:inline>
  (() => {
    const log = (message) => {
      const entry = document.createElement("li");
      entry.textContent = `${new Date().toLocaleTimeString()} ${message}`;
      document.getElementById("auth-event-log")?.prepend(entry);
    };

    // Events bubble from any u-signin-root on the page
    document.querySelectorAll("u-signin-root").forEach((root) => {
      root.addEventListener("authEvent", (event) => {
        const d = event.detail ?? {};
        const jwt = d.jwt ?? d.access_token ?? d.token;
        const summary = jwt
          ? `jwt: ${jwt.slice(0, 20)}…`
          : Object.keys(d).filter(k => !["jwt","access_token","token","refresh_token"].includes(k)).map(k => `${k}: ${d[k]}`).join(", ") || "authenticated";
        log(`authEvent — user signed in (${summary})`);
      });
      root.addEventListener("errorEvent", (event) => {
        log(`errorEvent — ${event.detail?.error ?? "unknown error"}`);
      });
    });

    // Logout is emitted by u-logout-button
    document.querySelectorAll("u-logout-button").forEach((button) => {
      button.addEventListener("logout", () => log("logout"));
    });
  })();
</script>
Live demo

Event log

  • Waiting for auth events… (use the sign-in flow above)