# Authentication

URL: https://developers.uidu.org/docs/foundations/authentication

> The three ways a request reaches uidu — public token, account Bearer, custom-app session — and where each may run.

What an app *looks like* — a landing page, a blog, a dashboard, a booking tool — doesn't decide
how it authenticates. **Who the request acts for** does:

| Acting for                                        | Token                      | `createClient` option | Runs                            |
| ------------------------------------------------- | -------------------------- | --------------------- | ------------------------------- |
| **Anyone** — public reads                         | public token               | `publicToken`         | anywhere, including the browser |
| **The workspace** — every write, and private data | account **Bearer**         | `apiKey`              | your server or the CLI only     |
| **The signed-in member** — an app inside uidu     | session from the host page | `fromBridge(bridge)`  | the browser, inside uidu only   |

One app can mix the rows: public pages rendered on the server with the public token, a contact
form submitted through a server action with the Bearer, and a members' area that only works when
uidu frames it.

Every [domain page](https://developers.uidu.org/docs/domains/cms.md) says at the top which token its reads and writes need, and
the [client reference](https://developers.uidu.org/docs/reference/client.md) says it per function.

## Public token — reads for anyone

Read-only access to what a workspace publishes: CMS pages, events, stories, forms, courses, donation
campaigns, the help center. It is **safe to ship in browser code**.

Get it from *Workspace → Settings → Public tokens*.

```ts
import { createClient } from '@uidu/client';

export const uidu = createClient({
  workspace: process.env.UIDU_WORKSPACE!,
  publicToken: process.env.UIDU_PUBLIC_TOKEN,
});
```

Private data — contacts and deals, people, goals, bookings, campaigns — isn't readable with it: the
query succeeds and returns nothing (an empty list, or `null`). If a list is unexpectedly empty, check
the domain's header before debugging the query.

## Bearer — the workspace

An account access token, sent as `Authorization: Bearer …`. It acts as the account it belongs to, so
it can do whatever that account's role in the workspace allows: read private data, and run **every
write** — including the visitor-facing ones (form responses, donations, RSVPs, job applications).

```ts
// lib/uidu.ts — imported only from server code
import { createClient } from '@uidu/client';

export const uidu = createClient({
  workspace: process.env.UIDU_WORKSPACE!,
  publicToken: process.env.UIDU_PUBLIC_TOKEN,
  apiKey: process.env.UIDU_API_KEY, // server-only
});
```

<Callout type="warn" title="Never in the browser">
  Keep `apiKey` on the server: a Server Component, a server action, a route handler, a script, or the
  CLI. Don't prefix it `NEXT_PUBLIC_`, and don't pass it to `<UiduProvider>` (which has no `apiKey`
  prop for this reason). A visitor submitting a form goes through a **server action** that calls
  `createFormResponse` with this client — see [Forms](https://developers.uidu.org/docs/domains/forms.md).
</Callout>

Get one from *Workspace → Settings → API keys*, or run [`uidu login`](https://developers.uidu.org/docs/tools/cli/login.md), which
signs you in through the browser and stores a token in `~/.uidu/config.json`. Set it as
`UIDU_API_KEY` where your server runs. Writes and their
`errors` are covered in [Writes](https://developers.uidu.org/docs/foundations/writes.md).

## Session — the signed-in member, inside uidu

A [custom app](https://developers.uidu.org/docs/paths/custom-app.md) runs in an iframe inside uidu. The host page hands it a
short-lived session (five minutes, refreshed for you) over `postMessage`: the app can do what that
member can do, and no more. There is no token to configure.

```tsx
'use client';
import { UiduAppProvider } from '@uidu/react';

// Inside, useUiduClient() / useUiduApp() give you a session-backed client.
<UiduAppProvider>{children}</UiduAppProvider>;
```

Outside React: `connect()` from `@uidu/app-bridge`, then `createClient(fromBridge(bridge))`.

Three rules follow from where the session lives:

1. **It only exists in the browser.** It arrives after the page loads, so Server Components and
   server actions never have it — pages that show member data are `'use client'`.
2. **It only works from the app's own origin.** Don't forward it to a backend of yours to call uidu
   from there.
3. **It reaches less than a Bearer.** The app's own [Models](https://developers.uidu.org/docs/domains/models.md), and read-only the
   workspace's [goals](https://developers.uidu.org/docs/domains/goals.md). Everything else comes back as an error for now.

## Choosing, in one line each

* Rendering public content (site, blog, events, careers): **public token**, on the server or in the browser.
* Anything that writes, or reads private data: **Bearer**, on the server.
* An app members open inside uidu: **session**, in the browser.
