# App users - sign-in owned by your app

An app in `app` mode (`gipity project auth app`, or `both` to also offer Sign in with Gipity) has its own users: people sign in to **this app only**, with no Gipity account and no consent screen, and the app owns them. Use it for anything you put in front of the public; use Sign in with Gipity for your own tools and for AI apps where users pay for their AI use (see [app-auth](app-auth.md)).

## Using it

Turn it on in gipity.yaml (a top-level block, applied by every deploy):

```yaml
auth:
  mode: app        # or both, to offer Sign in with Gipity too
```

(`gipity project auth app` sets it at once and writes that block.) Then the same client code works, with no new endpoints to call; `Gipity.auth.signInAsGuest()` signs a browser in as its guest with no prompt (for "no sign-in" apps):

```js
const user = await Gipity.auth.signIn();   // opens the app's own sign-in page in a popup
// user: { guid, displayName, avatarUrl, email, provider }   (provider: 'google' | 'discord' | 'apple' | 'email')
await Gipity.auth.user();                  // the signed-in user, or null
await Gipity.auth.signOut();               // this browser
await Gipity.auth.signOutEverywhere();     // every device
```

The sign-in page is branded as your app (name, logo and accent color from `gipity brand` / the `auth_branding:` deploy phase) and offers each provider that is set up plus an email code. A signed-in browser holds an httpOnly session cookie for your app only (30 days, renewed on use, revocable): page JavaScript never sees a token, and another app's code can't use it.

**In functions** an app user is an ordinary signed-in user (`ctx.auth.userGuid`, `displayName`), plus `ctx.auth.identity = { provider, id, email }`. `email` is their verified address (null for Steam and guest players), so the app can email its own users with the `email()` service. Gipity-account callers have `identity: null` and no email.

**One person is one user per app.** A provider seen before signs in to its user; a new provider whose verified email matches an existing user links to that user (sign in with Google today, an email code tomorrow: same `userGuid`). An unverified email never links. The same person in two different apps is two separate users.

**Providers and credentials.** `gipity project auth` shows each provider's state.
- **Google, Discord:** work right away on Gipity's shared credentials; the provider's screen says "Gipity Apps". Fine while you build. Before the app goes public on a custom domain, set your own so the screen shows your app: `gipity project auth provider google` prints the redirect URI and steps, then `gipity project auth provider google --client-id <id> --client-secret <secret>`. Existing users carry over (Google ids are the same across credentials). `gipity project auth` warns when an app with a custom domain still uses shared credentials.
- **Apple:** needs your own credentials from day one (`gipity project auth provider apple`), because Apple ties user ids to the developer team.
- **Email codes:** always on in app mode; sent branded as your app, rate-limited per address.

**Lifecycle hooks.** Declare functions with `hooks:` in `gipity.yaml` (use `auth: member` so only the platform and you can run them):
- `user_created`: runs after a new user signs up, with `ctx.trigger = { type: 'user_created', userGuid, user: { provider, displayName, email } }`. Use it for a welcome email or a profile row. Sign-up never waits on it; failures show in `gipity logs fn <name>`.
- `user_deleted`: runs before a user is erased (they deleted their account with `DELETE /api/<PROJECT_GUID>/auth/player`, or you did); delete their rows. A failure stops the deletion with 502 `PLAYER_CLEANUP_FAILED`.

**Managing users** (owner): `gipity project users` lists and searches them, `gipity project users export --format csv -o users.csv` exports all, `gipity project users delete <guid>` erases one (running `user_deleted` hooks), and `gipity project users signout <guid>` ends all of their sessions.

**Native clients** (games, mobile, desktop) don't use the page or the cookie: they sign in over HTTPS and send the returned token as `Authorization: Bearer`. Steam and guest devices: see [steam-game](steam-game.md). Email codes: `POST /api/<PROJECT_GUID>/auth/email/start { email }`, then `POST /api/<PROJECT_GUID>/auth/email/verify { email, code }`, which returns `{ data: { token, expiresIn, user } }`.

| Endpoint | What |
|---|---|
| `GET /api/<PROJECT_GUID>/auth/player` | The signed-in app user (`guid`, `displayName`, `email`, `provider`, `providerUserId`) |
| `PATCH /api/<PROJECT_GUID>/auth/player` | Set or clear their display name (`{ displayName }`); `gipity project auth --unique-names on` makes names unique |
| `DELETE /api/<PROJECT_GUID>/auth/player` | Erase their account (runs `user_deleted` hooks) |
| `POST /api/<PROJECT_GUID>/auth/signout[?everywhere=1]` | End the browser session (send `X-Gipity-Logout: 1`); the SDK's `signOut()` does this |

**Limits.** App users have no Gipity credits, so `user_pays` services can't bill them; those calls are billed to you (owner pays). There are no passwords, MFA or organizations; if an app needs those, it needs a dedicated identity provider.


