---
name: applicaster-login-flow
description: "Login Flow API integration: use when building or hardening a customer-hosted backend that implements the Applicaster Login Flow endpoints (login, register, refresh, reset, delete account) so the app's built-in Login UI can drive it (on-device surfaces such as Roku are one common driver). Works in any stack; asks whether the backend proxies an existing OIDC/OAuth2 provider (the usual ROPC case) or is custom auth, adds request AND response validation to every endpoint through the project's own validator, and ships a skill-local login-e2e tool that validates a running server against the contract."
---

# applicaster-login-flow

Implement or harden the **Login Flow API** — the customer-hosted backend the Applicaster
Login UI calls for `login`, `register`, `refresh`, `reset`, and `delete account`. The
deliverable is endpoints that satisfy the documented contract, with validation on **both**
the request boundary and the response boundary, plus an executable proof they do via the
skill-local [Login Flow E2E tool](tools/login-e2e/README.md).

A Login Flow backend authenticates credentials collected in the app's own UI, so it is
**most often a thin proxy in front of an existing OIDC/OAuth2 provider** (the same one the
app uses elsewhere), authenticating via the ROPC/password grant — and occasionally it is
custom auth over the customer's own user store. Which one you are building shapes every
endpoint, so it is the first thing to resolve. One common driver for needing it at all is
an on-device surface such as **Roku**, whose certification requires all sign-in to happen
on-device with no off-device/browser flow; but it is only one example, not the point of
the skill. See [provider-decision-guidance.md](references/provider-decision-guidance.md).

This is the **backend/API** side. For the customer-hosted **web UI** that renders the
login screens and hands tokens back to the app, use `applicaster-custom-login-ui`.

## Scope

- Use for a new or existing backend that must expose the Login Flow endpoints, whether it
  proxies an OIDC/OAuth2 IdP (Auth0, Cognito, Okta, Azure AD B2C, Cleeng, Keycloak, …) or
  authenticates against the customer's own user store.
- **Adapt to the target project's stack** — language, framework, package manager,
  validation library, HTTP client, test runner, config/secrets, and deployment
  conventions. Do not assume a JavaScript/zod implementation, and do not add a validation
  or HTTP dependency the project lacks a use for; use its existing tools.
- The contract is fixed and documented. Confirm live docs; do not invent request fields,
  response keys, or status codes.

## Non-Negotiable Gates

1. Every endpoint validates its **request** body before doing any work, and validates its
   **response** payload before returning it, using the target project's own validator. A
   request-only integration is incomplete — the response check is what stops a broken
   provider mapping reaching the device. See [validation-patterns.md](references/validation-patterns.md).
2. The token response returned by `login`, `register`, and `refresh` carries
   `{ access_token: string, refresh_token: string, expires_in: number-of-seconds }` at the
   top level. Do not wrap it, rename keys, or return `expires_in` in milliseconds or as a
   string. Extra top-level fields are allowed — `id_token` is commonly needed, since it is
   consumed elsewhere as a `quick-brick-login-flow.*` context key — but return them
   deliberately, never by passing the provider body through unexamined.
3. Do not report the work complete without a `## Zapp Configuration` section that both
   lists the exact endpoint URLs (Login, Register, Reset, Refresh, Delete Account) and the
   Delete Account context key `quick-brick-login-flow.access_token`, **and** walks the
   customer through placing the Login Flow screen in their layout per the Mobile Login guide.
4. Do not claim end-to-end validation without running the `login-e2e` tool against a
   running server, or, if a live server is unavailable, without naming that as the blocker.

## 1. Establish facts

1. Fetch the current Login Flow, Mobile Login, TV Login, and OAuth2/OIDC (incl. the Roku
   section) documentation. Read [login-flow-contract.md](references/login-flow-contract.md)
   alongside it and reconcile any drift toward the live docs.
2. Inspect the target project and determine, without asking, what the code reveals: its
   **stack** (language, web framework, validation library already in use, HTTP client,
   test/build commands), whether an **OIDC/OAuth2 provider is already integrated** (and
   which), the config/secrets convention, the deployment URL shape, and any existing
   login/register/refresh/reset/delete code. Only ask for stack details the project cannot
   answer yet (e.g. a greenfield service with no framework chosen). If there is no existing
   project to inspect at all, skip discovery, settle the proxy-vs-custom fork (§2 Q1) and
   the stack with the developer first, and default config/secrets to environment variables.
3. Read [provider-decision-guidance.md](references/provider-decision-guidance.md) and
   [validation-patterns.md](references/validation-patterns.md) for the portable
   implementation and validation shapes; realize them through the project's own tools.

Completion: the target stack, the provider/identity source, the endpoints to add or
change, and the project's validation and test conventions are evidenced by the code and
live docs.

## 2. Resolve developer decisions

Present a concise, sourced fact summary, then ask only what facts cannot settle. Ask one
question at a time with the discovered constraint, a recommended answer, and the
consequence of choosing otherwise. If a question exposes a missing fact, investigate and
return with a recommendation rather than asking the developer to decide blind. Use
[provider-decision-guidance.md](references/provider-decision-guidance.md) for any
provider, identity, or mapping branch.

Ask first, because it decides how every endpoint is built:

1. **Proxy or custom?** Is this backend a proxy to an existing OIDC/OAuth2 provider — the
   usual case — or custom auth over the customer's own user store? For a proxy, work
   through the provider-confirmation checklist (ROPC/password grant, dedicated client,
   refresh tokens, the per-endpoint grant mapping, and when to stop) in
   [provider-decision-guidance.md](references/provider-decision-guidance.md#the-first-fork-proxy-an-existing-oidcoauth2-provider-or-custom-auth).

Then resolve, only where facts leave real ambiguity:

2. The provider→contract token mapping (proxy) or token issuance (custom), and which extra
   values, if any, the app needs beyond the flat trio.
3. The **validation approach in this stack** — confirm the existing validator to use for
   both boundaries, and whether error copy must be localized (recommend plain messages
   unless the customer needs localization).
4. How `delete account` identifies the user from the `ctx` token and what "delete" means
   (provider erase API, hard delete, soft delete, anonymize).
5. Which endpoints are in scope. `login` and `refresh` are always in scope — do not ask
   about them. Ask only which of the three **optional** endpoints to include: `register`,
   `reset`, `delete account` (a three-way multi-select, so it never exceeds an ask tool's
   option cap — never present all five as options). Enable the matching `login-e2e` flags
   for whatever is chosen.

Completion: every non-discoverable choice affecting stack, auth, data, or behavior has a
recorded developer answer.

## 3. Confirm before edits

Present a short implementation contract:

- Proxy vs custom, and for a proxy the provider, grant types, and endpoints used.
- Each endpoint in scope, its request schema, its response/error mapping, its validator,
  and the file it lives in.
- The delete-account identification path.
- The exact `login-e2e` command (URLs, test account, which optional flags), or the
  specific missing prerequisite (e.g. no staging account, ROPC not yet enabled).

Wait for explicit approval. If a material decision changes later, ask one recommended
question and refresh the contract.

## 4. Implement the contract

1. For each in-scope endpoint: validate the request body first and return a `403`
   `{ formError, fieldErrors }` on failure; do the provider/store work; validate the
   response payload and return `200` with the token trio (or `200`/`201` for reset). Map
   credential/refresh-token failures to `403`, and a response that fails contract
   validation or an upstream provider failure to a sanitized `502` — never send a
   malformed token payload to the app.
2. For a proxy, call the provider's token endpoint (`grant_type=password` for login,
   `grant_type=refresh_token` for refresh) and map its response explicitly to the flat
   trio in seconds (Gate 2). Do not pass the provider body through unmapped.
3. Implement `delete account` by base64url-decoding `ctx`, reading
   `quick-brick-login-flow.access_token`, identifying the user, and deleting via the
   provider erase API or the customer's store. Return `200` on success, `>= 400` on
   failure. Decode as base64**url**, not plain base64.
4. Add sanitized logging/metrics that never emit the password, token payload, or `ctx`.
5. Read all client ids/secrets and endpoints from the project's existing config/secret
   convention. Make only the changes a compliant integration needs; report unrelated
   findings separately.

Completion: every in-scope endpoint validates both boundaries and returns the contract shape.

## 5. Verify and hand off

1. Run the project's formatter/typecheck and its own tests for the changed endpoints, in
   the project's stack.
2. Install and run the skill-local `login-e2e` tool against a running server, pointed at a
   disposable/staging account, for the endpoints in scope
   ([tool README](tools/login-e2e/README.md)). It is black-box and stack-agnostic — it
   asserts the request-boundary rejections, the response schema on
   `login`/`register`/`refresh`, refresh rejection, reset, and a register-then-delete
   cycle. Attach the `--json-report`. If no server can be run, report the tool command as
   blocked rather than claiming end-to-end validation.
3. Write a `## Zapp Configuration` section that hands the deployed backend off to the
   customer as steps they perform in Zapp Studio, following the **Mobile Login** guide
   (<https://docs.applicaster.com/using-zapp/auth/mobile-login>). Give them, in order:
   1. Install the **Login Flow** plugin (debug mode off) and **add the Login Flow screen
      to the app layout** — as a standalone screen reachable from settings/navigation, or
      as a **screen hook** gatekeeping locked content.
   2. In the screen config, fill the **API URLs** with the endpoints you deployed, one per
      field — map each explicitly so there is no ambiguity:

      | Screen field  | Your deployed URL |
      | ------------- | ----------------- |
      | login         | `<your login URL>` |
      | register      | `<your register URL>` |
      | resetPassword | `<your reset URL>` |
      | refresh       | `<your refresh URL>` |
   3. For account deletion (only if in scope): create a Zapp **Endpoint** with HTTP method
      `POST` and context key `quick-brick-login-flow.access_token`, wrap it in a **Feed**,
      and set that as the screen's **Delete account Endpoint** — using your deployed delete URL.
   4. Rebuild the app so the plugin takes effect.

   For a Roku integration, note the ROPC client requirement from the OAuth2 guide's Roku section.
4. Report files changed, verification commands and results, the `login-e2e` result or
   blocker, required secrets/config, and any remaining provider or deployment prerequisite.

Completion: both boundaries are validated in code, `login-e2e` passed (or its blocker is
named), and the developer has the exact Zapp values to configure.
