# ElvixDeviceApproval

> The browser surface a CLI or headless device sends the user to in order to authorize itself (OAuth 2.0 Device Authorization Grant, RFC 8628). Host it on your own domain so the user approves on a page they trust.

`<ElvixDeviceApproval>` is the human-approval page for the device grant. Mount it on a route on YOUR OWN domain (for example `yourapp.com/device`). It renders `<ElvixSignInForm>` under the hood, so it shows exactly the sign-in methods you enabled in the Console (Google, passkey, email) with your brand, and updates live when you change them. The user signs in, the component approves the device in place, and the CLI continues. The whole flow stays on your domain.

<!-- app/device/page.tsx -->
```tsx
"use client";

// app/device/page.tsx -- host this on YOUR OWN domain (e.g. yourapp.com/device).
import { ElvixDeviceApproval } from "@elvix.is/sdk/react";
import "@elvix.is/sdk/styles.css";

export default function DevicePage() {
  return (
    <main className="grid min-h-screen place-items-center px-6">
      <ElvixDeviceApproval
        clientId={process.env.NEXT_PUBLIC_ELVIX_CLIENT_ID!}
        // baseUrl defaults to https://elvix.is; point it at your dev mirror in dev.
      />
    </main>
  );
}
```

> **Note:** Host it on your own domain, not elvix.is. The user trusts a page they recognise, and the sign-in methods complete in-frame there (your origin is allow-listed, so passkey and Google return to your page instead of bouncing). `elvix.is/device` exists as a generic fallback for apps with no domain, but email is the only reliable method there.

## How the flow works

1. Your CLI calls `requestDeviceCode` and gets a `device_code`, a short `user_code`, and a `verification_uri_complete` that points at YOUR `/device` page with `?code=` pre-filled. 2. The CLI prints that URL and code. 3. The user opens it, confirms the code matches their terminal, and signs in with any enabled method. 4. The component POSTs `device/approve` with the session token. 5. The CLI's `pollDeviceToken` returns an `eak_` token bound to the approving user.

<!-- cli/login.ts -->
```ts
// Your CLI: request a code, send the user to the verification URL, poll for the token.
import { requestDeviceCode, pollDeviceToken } from "@elvix.is/sdk/server";

const code = await requestDeviceCode({ clientId: CLIENT_ID });
// verificationUriComplete is YOUR /device page with ?code= pre-filled.
console.log("Open " + code.verificationUriComplete);
console.log("Confirm the code: " + code.userCode);

const r = await pollDeviceToken({
  clientId: CLIENT_ID,
  deviceCode: code.deviceCode,
  interval: code.interval,
  expiresIn: code.expiresIn,
});
if (r.ok) save(r.accessToken); // an eak_ token bound to the approving user
```

> **Note:** elvix resolves `verification_uri` to your app's primary domain automatically (env-matched: dev mirror in dev, prod in prod). You do not configure a URL; just host `<ElvixDeviceApproval>` at `/device`.

## Props

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `clientId` | `string` | required | none | Your app's public elvix `client_id`. |
| `code` | `string` | optional | `?code=` in the URL | The short `user_code`. The CLI normally deep-links the user here with it pre-filled, so you rarely pass it. |
| `baseUrl` | `string` | optional | `https://elvix.is` | elvix origin. Point at your dev mirror in development. |
| `theme` | `"light" \| "dark" \| "system"` | optional | Console default | Theme override. |
| `onApproved` | `() => void` | optional | none | Fires after the device is approved. Render your own confirmation if you want. |
| `onError` | `(error: string) => void` | optional | none | Fires on an approval failure. |

## Why on your own domain

A device grant authorizes a CLI to act as the user on YOUR app. Approving that on a third-party page (elvix.is) reads as lower trust. On your own domain the user sees your brand, the page is allow-listed so every method completes in-frame (no redirect bounce), and the `eak_` token is still minted and verified by elvix. Best of both: your trust surface, elvix's identity.


## Related

- [ElvixSignInForm](/docs/components/elvix-sign-in-form.md): The full branded sign-in surface (card, header, brand wash, every Console-enabled method, Secured-by-elvix badge). Exported as both `ElvixSignInForm` and the short alias `ElvixSignIn` — same component.
- [ElvixProvider](/docs/components/elvix-provider.md): Top-level context. Fetches the brand chord, factor config, and copy from elvix; pipes session state and the event channel to every nested <Elvix*> component.
