# SessionExpired

> Re-authentication form for a session that ended: the same person, a password (and code) or a passkey away, with switch account and sign out.

Source: https://docs.nasaqui.com/components/session-expired

## Install

```bash
npx shadcn@latest add https://docs.nasaqui.com/r/session-expired.json
```

The form shown when a session ended: expired, revoked from another device, or the password changed. Who was signed in is
known, so only the secret is asked again. Put it in an `AuthLayout`. Use `LockScreen` when the session is still valid but locked.

## When to use

- A request came back 401 and the person must sign in again without losing their place.

## When not to use

- A locked but valid session: [`lock-screen`](https://docs.nasaqui.com/components/lock-screen).
- A first sign-in: [`login-form`](https://docs.nasaqui.com/components/login-form).

## Import

```tsx
import { AuthLayout, SessionExpired } from "@fadymondy/nasaq/web";
// inside this monorepo: "@nasaq/web"
```

## Quick start

```tsx
import { AuthLayout, SessionExpired } from "@fadymondy/nasaq/web";

export function Expired() {
  return (
    <AuthLayout title="Welcome back">
      <SessionExpired
        user={{ name: "Sara Nasser", email: "sara@example.com" }}
        keepsWork
        onSubmit={async ({ password }) => {
          const ok = await api.reauth(password);
          if (!ok) return { error: "Wrong password." };
        }}
        onSignOut={() => api.signOut()}
      />
    </AuthLayout>
  );
}

declare const api: { reauth(p: string): Promise<boolean>; signOut(): void };
```

## Anatomy

```
SessionExpired                data-slot="session-expired"  data-reason
├─ Alert (why)
├─ user row
├─ error summary
├─ password, optional code
├─ Sign in again · Use a passkey
└─ Use a different account · Sign out
```

## API

`form` props (except `onSubmit`, `children`) plus:

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `user` | `LockUser` | required | Who was signed in. |
| `reason` | `expired`, `revoked`, `password-changed` | `expired` | Picks the message. |
| `requireCode` | `boolean` | `false` | Ask for a 6 digit authenticator code. |
| `onSubmit` | `(values) => AuthSubmitResult` | required | Resolve nothing on success, or `{ error }` / `{ fieldErrors }`. |
| `onPasskey` | `() => Promise` | | Shows the passkey button where WebAuthn works. |
| `onSwitchAccount`, `onSignOut` | `() => void` | | Show their link buttons. |
| `keepsWork` | `boolean` | `false` | Tells people their unsaved changes are safe. |
| `footer`, `labels` | | | |

## Examples

**With a code and passkey**

```tsx
import { SessionExpired } from "@fadymondy/nasaq/web";

export const Strict = () => <SessionExpired user={{ name: "Omar" }} reason="revoked" requireCode onPasskey={() => api.passkey()} onSubmit={async () => undefined} />;

declare const api: { passkey(): Promise<void> };
```

## Accessibility

- Errors are summarised and focus moves to the summary. The password field is auto focused.
- A hidden username lets password managers fill the right entry.
- The passkey button appears only when the browser supports it.

## RTL & i18n

- English and Arabic built in; pass `labels`. Emails stay left to right.

## Styling & tokens

- Tokens only. Reuses `Alert`, `PasswordInput`, `OtpInput`, `Button`.

## Do / Don't

- Do say why the session ended.
- Do keep the person's place: return them to the page after success.
- Don't ask for the email again.

## Related

- [`lock-screen`](https://docs.nasaqui.com/components/lock-screen)
- [`idle-lock`](https://docs.nasaqui.com/components/idle-lock)

## Lab

https://docs.nasaqui.com/?path=/docs/components-auth-pages-session-expired--docs

## Code

### React

```tsx
import { AuthLayout, SessionExpired } from "@fadymondy/nasaq/web";

export function Expired() {
  return (
    <AuthLayout title="Welcome back">
      <SessionExpired
        user={{ name: "Sara Nasser", email: "sara@example.com" }}
        keepsWork
        onSubmit={async ({ password }) => {
          const ok = await api.reauth(password);
          if (!ok) return { error: "Wrong password." };
        }}
        onSignOut={() => api.signOut()}
      />
    </AuthLayout>
  );
}

declare const api: { reauth(p: string): Promise<boolean>; signOut(): void };
```

### shadcn

```tsx
import { AuthLayout } from "@/components/ui/auth-layout";
import { SessionExpired } from "@/components/ui/session-expired";

export function Expired() {
  return (
    <AuthLayout title="Welcome back">
      <SessionExpired
        user={{ name: "Sara Nasser", email: "sara@example.com" }}
        keepsWork
        onSubmit={async ({ password }) => {
          const ok = await api.reauth(password);
          if (!ok) return { error: "Wrong password." };
        }}
        onSignOut={() => api.signOut()}
      />
    </AuthLayout>
  );
}

declare const api: { reauth(p: string): Promise<boolean>; signOut(): void };
```

### Vue

```vue
<script setup lang="ts">
import { NqAuthLayout, NqSessionExpired } from "@fadymondy/nasaq/vue";

// Your API calls. Resolve { error } for a wrong password.
async function reauth({ password }: { password: string }) {
  const res = await fetch("/api/reauth", { method: "POST", body: JSON.stringify({ password }) });
  if (!res.ok) return { error: "Wrong password." };
}
function signOut() {
  void fetch("/api/sign-out", { method: "POST" });
}
</script>

<template>
  <NqAuthLayout title="Welcome back">
    <NqSessionExpired :user="{ name: 'Sara Nasser', email: 'sara@example.com' }" keeps-work :on-submit="reauth" :on-sign-out="signOut" />
  </NqAuthLayout>
</template>
```

### Blade

```blade
{{-- Your listener checks the password; resolve { error } for a wrong one (try "wrong"). --}}
<div class="w-80">
    <x-nq::session-expired :user="['name' => 'Nour Adel', 'email' => 'nour@example.com']" require-code keeps-work passkey switch-account sign-out
        x-on:nq-session-expired="$event.detail.waitUntil(new Promise((done) => setTimeout(() => done($event.detail.password === 'wrong' ? { error: 'That password is not right.' } : undefined), 300)))"
        x-on:nq-session-sign-out="location.assign('/logout')" />
</div>
```

### HTML + Alpine

```html
<div class="w-80">
    <form novalidate data-slot="session-expired" data-reason="expired" x-data="nqSessionExpired(JSON.parse('{\u0022requireCode\u0022:true,\u0022passkey\u0022:true,\u0022names\u0022:[\u0022password\u0022,\u0022code\u0022],\u0022fieldLabels\u0022:{\u0022password\u0022:\u0022Password\u0022,\u0022code\u0022:\u0022Authenticator code\u0022},\u0022errorTitle\u0022:\u0022Fix these to continue\u0022,\u0022failed\u0022:\u0022Something went wrong. Try again.\u0022,\u0022labels\u0022:{\u0022passwordRequired\u0022:\u0022Enter your password.\u0022,\u0022codeRequired\u0022:\u0022Enter all 6 digits of the code.\u0022,\u0022failed\u0022:\u0022Something went wrong. Try again.\u0022}}'))"
    x-bind:aria-busy="pending ? 'true' : null" x-on:submit.prevent="onSubmit()" x-on:input="clear($event.target.name)"
    x-on:nq-session-expired="$event.detail.waitUntil(new Promise((done) =&gt; setTimeout(() =&gt; done($event.detail.password === &#039;wrong&#039; ? { error: &#039;That password is not right.&#039; } : undefined), 300)))" x-on:nq-session-sign-out="location.assign(&#039;/logout&#039;)" class="flex w-full flex-col gap-4">
    <div data-slot="alert" data-tone="warning" role="alert"
        class="relative grid grid-cols-[auto_1fr_auto] items-start gap-x-3 rounded-card border p-3 text-start border-nq-warning/30 bg-nq-warning-soft">
    <svg aria-hidden="true" data-slot="alert-icon" class="mt-0.5 size-4 text-nq-warning-text" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <circle cx="12" cy="12" r="10"/>
  <path d="M12 6v6l4 2"/>
</svg>    <div data-slot="alert-body" class="flex min-w-0 flex-col gap-0.5">
                    <div data-slot="alert-title" class="text-label text-foreground">Your session expired</div>
                            <div data-slot="alert-description" class="text-body-sm text-muted-foreground">For your security we signed you out after a while. Sign in again to pick up where you left off.
         <span class="mt-1 block">Your unsaved changes are kept in this tab.</span></div>
            </div>
    </div>
    <div data-slot="session-expired-user" class="flex items-center gap-3 rounded-control border border-border bg-muted/50 p-3">
        <span data-slot="avatar" x-data="nqAvatar(0)"
    class="inline-flex shrink-0 select-none items-center justify-center overflow-hidden bg-secondary align-middle font-medium text-secondary-foreground size-8 text-caption rounded-full">
                    <span data-slot="avatar-fallback" class="flex size-full items-center justify-center" x-bind="fallback"
             role="img" aria-label="Nour Adel" >NA</span>
    </span>
        <div class="flex min-w-0 flex-col text-start">
            <span class="truncate text-label text-foreground">Nour Adel</span>
             <bdi dir="ltr" class="truncate text-caption text-muted-foreground">nour@example.com</bdi>         </div>
    </div>
    <div tabindex="-1" class="sr-only" x-show="! problems()"></div>
<div tabindex="-1" data-slot="auth-error-summary" class="outline-none" x-show="problems()" style="display: none">
    <div data-slot="alert" data-tone="danger" role="alert" class="relative grid grid-cols-[auto_1fr_auto] items-start gap-x-3 rounded-card border p-3 text-start border-nq-danger/30 bg-nq-danger-soft">
        <svg aria-hidden="true" data-slot="alert-icon" class="mt-0.5 size-4 text-nq-danger-text" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <circle cx="12" cy="12" r="10"/>
  <path d="m15 9-6 6"/>
  <path d="m9 9 6 6"/>
</svg>        <div data-slot="alert-body" class="flex min-w-0 flex-col gap-0.5">
            <div data-slot="alert-title" class="text-label text-foreground" x-text="summaryTitle()"></div>
            <div data-slot="alert-description" class="text-body-sm text-muted-foreground" x-show="! error && entries().length" style="display: none">
                <ul class="flex list-disc flex-col gap-0.5 ps-4">
                    <template x-for="e in entries()" :key="e.name">
                        <li><button type="button" class="text-start underline underline-offset-2" x-on:click="focusField(e.name)" x-text="(e.label ? e.label + ': ' : '') + e.message"></button></li>
                    </template>
                </ul>
            </div>
        </div>
    </div>
</div>
    <input type="text" name="username" autocomplete="username" value="nour@example.com" readonly hidden />
    <div data-slot="field" x-data="nqField(false)" x-modelable="invalid" x-id="['nq-field']"
         data-valid     x-model="bad.password" class="flex flex-col gap-1.5">
    <label data-slot="field-label"  for="nq-session-000001-password" class="text-label text-foreground data-disabled:opacity-50">Password</label>
        <div data-slot="password-input" x-data="nqPasswordInput('', JSON.parse('{\u0022visible\u0022:false,\u0022score\u0022:null,\u0022rules\u0022:null,\u0022levels\u0022:[\u0022Very weak\u0022,\u0022Weak\u0022,\u0022Fair\u0022,\u0022Good\u0022,\u0022Strong\u0022]}'))" x-modelable="value"
    class="flex w-full flex-col gap-2">
    <div role="group" data-slot="input-group"
    class="group/input-group flex h-control min-h-[var(--nq-touch-min,0px)] w-full min-w-0 items-center overflow-hidden rounded-control border border-input bg-card text-body text-foreground transition-colors duration-150 ease-nq focus-within:border-nq-focus focus-within:outline-1 focus-within:outline-nq-focus has-[[data-invalid]]:border-nq-danger has-[[aria-invalid=true]]:border-nq-danger has-[input:disabled]:cursor-not-allowed has-[input:disabled]:opacity-50"><input data-slot="input-group-input"     x-model="value" x-bind:type="shown ? &#039;text&#039; : &#039;password&#039;" type="password" value="" autocapitalize="none" autocorrect="off" spellcheck="false" id="nq-session-000001-password" name="password" autofocus="autofocus" autocomplete="current-password" aria-describedby="nq-session-000001-password-error" x-bind:aria-invalid="bad.password ? &#039;true&#039; : null" class="h-full min-w-0 flex-1 border-0 bg-transparent px-3 text-body text-foreground outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed pointer-coarse:text-[16px]" />
        <div data-slot="input-group-addon" data-align="end"
    class="flex h-full shrink-0 items-center gap-1.5 text-body-sm text-muted-foreground [&_svg]:size-4 order-last ps-1 pe-1.5"><button data-slot="password-input-toggle"
     type="button"                         x-bind="toggle" aria-label="Show password" aria-pressed="false" class="inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-control border border-transparent font-sans text-label transition-colors duration-150 ease-nq min-h-[var(--nq-touch-min,0px)] outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus disabled:pointer-events-none disabled:opacity-50 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 text-foreground hover:bg-nq-hover size-control-sm p-0">
        <svg x-show="shown" x-cloak="1" style="display: none" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49"/>
  <path d="M14.084 14.158a3 3 0 0 1-4.242-4.242"/>
  <path d="M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143"/>
  <path d="m2 2 20 20"/>
</svg>                <svg x-show="!shown" x-cloak="1" style="" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0"/>
  <circle cx="12" cy="12" r="3"/>
</svg></button></div></div>
        </div>
        <div data-slot="field-error" role="alert" x-show="invalid"  style="display: none"  id="nq-session-000001-password-error" class="text-caption text-nq-danger-text"><span x-text="fe.password"></span></div>
</div>
            <div class="flex flex-col gap-2">
            <span class="text-label text-foreground">Authenticator code</span>
            <div role="group" dir="ltr" data-slot="otp-input" x-data="nqOtpInput('', 6, 'numeric')" x-modelable="value"
    aria-label="Authenticator code" class="inline-flex items-center gap-2">
            <input data-slot="otp-input-box" x-bind="box(0)" type="text" value=""
                        aria-label="Digit 1 of 6"
            inputmode="numeric" autocomplete="one-time-code" autocapitalize="off" spellcheck="false"
                          class="size-control min-h-[var(--nq-touch-min,0px)] min-w-0 rounded-control border border-input bg-card p-0 text-center text-body font-medium tabular-nums text-foreground transition-colors duration-150 ease-nq outline-none focus-visible:border-nq-focus focus-visible:outline-1 focus-visible:outline-nq-focus data-invalid:border-nq-danger aria-invalid:border-nq-danger disabled:cursor-not-allowed disabled:opacity-50 pointer-coarse:text-[16px]" />
            <input data-slot="otp-input-box" x-bind="box(1)" type="text" value=""
                        aria-label="Digit 2 of 6"
            inputmode="numeric" autocomplete="one-time-code" autocapitalize="off" spellcheck="false"
                          class="size-control min-h-[var(--nq-touch-min,0px)] min-w-0 rounded-control border border-input bg-card p-0 text-center text-body font-medium tabular-nums text-foreground transition-colors duration-150 ease-nq outline-none focus-visible:border-nq-focus focus-visible:outline-1 focus-visible:outline-nq-focus data-invalid:border-nq-danger aria-invalid:border-nq-danger disabled:cursor-not-allowed disabled:opacity-50 pointer-coarse:text-[16px]" />
            <input data-slot="otp-input-box" x-bind="box(2)" type="text" value=""
                        aria-label="Digit 3 of 6"
            inputmode="numeric" autocomplete="one-time-code" autocapitalize="off" spellcheck="false"
                          class="size-control min-h-[var(--nq-touch-min,0px)] min-w-0 rounded-control border border-input bg-card p-0 text-center text-body font-medium tabular-nums text-foreground transition-colors duration-150 ease-nq outline-none focus-visible:border-nq-focus focus-visible:outline-1 focus-visible:outline-nq-focus data-invalid:border-nq-danger aria-invalid:border-nq-danger disabled:cursor-not-allowed disabled:opacity-50 pointer-coarse:text-[16px]" />
            <input data-slot="otp-input-box" x-bind="box(3)" type="text" value=""
                        aria-label="Digit 4 of 6"
            inputmode="numeric" autocomplete="one-time-code" autocapitalize="off" spellcheck="false"
                          class="size-control min-h-[var(--nq-touch-min,0px)] min-w-0 rounded-control border border-input bg-card p-0 text-center text-body font-medium tabular-nums text-foreground transition-colors duration-150 ease-nq outline-none focus-visible:border-nq-focus focus-visible:outline-1 focus-visible:outline-nq-focus data-invalid:border-nq-danger aria-invalid:border-nq-danger disabled:cursor-not-allowed disabled:opacity-50 pointer-coarse:text-[16px]" />
            <input data-slot="otp-input-box" x-bind="box(4)" type="text" value=""
                        aria-label="Digit 5 of 6"
            inputmode="numeric" autocomplete="one-time-code" autocapitalize="off" spellcheck="false"
                          class="size-control min-h-[var(--nq-touch-min,0px)] min-w-0 rounded-control border border-input bg-card p-0 text-center text-body font-medium tabular-nums text-foreground transition-colors duration-150 ease-nq outline-none focus-visible:border-nq-focus focus-visible:outline-1 focus-visible:outline-nq-focus data-invalid:border-nq-danger aria-invalid:border-nq-danger disabled:cursor-not-allowed disabled:opacity-50 pointer-coarse:text-[16px]" />
            <input data-slot="otp-input-box" x-bind="box(5)" type="text" value=""
                        aria-label="Digit 6 of 6"
            inputmode="numeric" autocomplete="one-time-code" autocapitalize="off" spellcheck="false"
                          class="size-control min-h-[var(--nq-touch-min,0px)] min-w-0 rounded-control border border-input bg-card p-0 text-center text-body font-medium tabular-nums text-foreground transition-colors duration-150 ease-nq outline-none focus-visible:border-nq-focus focus-visible:outline-1 focus-visible:outline-nq-focus data-invalid:border-nq-danger aria-invalid:border-nq-danger disabled:cursor-not-allowed disabled:opacity-50 pointer-coarse:text-[16px]" />
        <input type="hidden" name="code" :value="value" value="" /></div>
        </div>
        <button data-slot="button"
     type="submit"                         x-bind:disabled="pending || passkeyPending" x-bind:data-disabled="pending || passkeyPending ? &#039;&#039; : null" x-bind:aria-busy="pending ? &#039;true&#039; : null" class="inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-control border border-transparent font-sans transition-colors duration-150 ease-nq min-h-[var(--nq-touch-min,0px)] outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus disabled:pointer-events-none disabled:opacity-50 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 bg-primary text-primary-foreground hover:bg-[color-mix(in_oklab,var(--nq-action)_88%,var(--nq-fg))] h-[calc(var(--nq-control)+8px)] px-5 text-body">
        <template x-if="pending"><svg data-slot="spinner" aria-hidden="true" class="size-4 animate-spin motion-reduce:animate-none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M21 12a9 9 0 1 1-6.219-8.56"/>
</svg></template>
        Sign in again</button>
            <button data-slot="session-expired-passkey"
     type="button"                         style="display: none" x-show="passkeyOn" x-on:click="passkey()" x-bind:disabled="pending || passkeyPending" x-bind:data-disabled="pending || passkeyPending ? &#039;&#039; : null" x-bind:aria-busy="passkeyPending ? &#039;true&#039; : null" class="inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-control border font-sans transition-colors duration-150 ease-nq min-h-[var(--nq-touch-min,0px)] outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus disabled:pointer-events-none disabled:opacity-50 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 border-border bg-card text-foreground hover:bg-nq-hover h-[calc(var(--nq-control)+8px)] px-5 text-body">
        <template x-if="passkeyPending"><svg data-slot="spinner" aria-hidden="true" class="size-4 animate-spin motion-reduce:animate-none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M21 12a9 9 0 1 1-6.219-8.56"/>
</svg></template>
            <svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M2.586 17.414A2 2 0 0 0 2 18.828V21a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h1a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h.172a2 2 0 0 0 1.414-.586l.814-.814a6.5 6.5 0 1 0-4-4z"/>
  <circle cx="16.5" cy="7.5" r=".5" fill="currentColor"/>
</svg>            Use a passkey</button>
                <div class="flex flex-wrap items-center justify-between gap-2">
                            <button data-slot="button"
     type="button"                         x-on:click="switchAccount()" x-bind:disabled="busyAny()" x-bind:data-disabled="busyAny() ? &#039;&#039; : null" class="inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-control border border-transparent font-sans text-label transition-colors duration-150 ease-nq min-h-[var(--nq-touch-min,0px)] outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus disabled:pointer-events-none disabled:opacity-50 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 text-foreground underline decoration-nq-line underline-offset-4 hover:decoration-current h-auto px-0">
        Use a different account</button>
                                        <button data-slot="button"
     type="button"                         x-on:click="signOut()" x-bind:disabled="busyAny()" x-bind:data-disabled="busyAny() ? &#039;&#039; : null" class="inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-control border border-transparent font-sans text-label transition-colors duration-150 ease-nq min-h-[var(--nq-touch-min,0px)] outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus disabled:pointer-events-none disabled:opacity-50 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 text-foreground underline decoration-nq-line underline-offset-4 hover:decoration-current h-auto px-0">
        Sign out</button>
                    </div>
        </form>
</div>
```
