# Build a Sign-in Mini App <Badge type="warning" text="Not yet released" />

The path from nothing to a Mini App that signs a person in with their Micromeet account and then calls Micromeet for them from its own backend. Every function is described in [Sign-in](https://docs.micromeet.ai/miniapp-sdk/auth.md), packaging in [Build and Sign](https://docs.micromeet.ai/miniapp-sdk/build.md), and every package rule in [Package Spec](https://docs.micromeet.ai/miniapp-sdk/package-spec.md).

## The whole flow

Who does what, from the first build to a person opening the Mini App:

```mermaid
flowchart TB
  subgraph dev["You"]
    build["vite build<br/>miniapp-package/"]
    verify["micromeet-miniapp verify"]
    zip["micromeet-miniapp zip<br/>signed zip"]
    file["serve the verification file<br/>at the backend's domain"]
  end
  subgraph mm["Micromeet"]
    reg["register the AppID<br/>client secret per region"]
    dom["verify the server domain"]
    sign["register and sign the version<br/>checks every file and domain"]
  end
  subgraph cw["Cowork"]
    install["install"]
    open{"signature valid<br/>and files unchanged?"}
    run["open the Mini App"]
    refuse["not certified:<br/>does not open"]
  end
  reg --> file --> dom
  build --> verify --> sign
  dom --> sign
  sign -->|signed manifest.json| zip --> install --> open
  open -->|yes| run
  open -->|no| refuse
```

Cowork checks the signature on every open, and every file whenever the page reads it; a changed file closes the Mini App.

Signing in, after the person clicks your button:

```mermaid
sequenceDiagram
  actor P as Person
  participant Page as Page
  participant C as Cowork
  participant M as Micromeet
  participant B as Your backend
  P->>Page: click Sign in
  Page->>C: auth.login()
  C->>M: who is this Mini App, what may it ask
  C->>P: card (first time): Mini App, scopes, account
  P->>C: Allow
  C->>M: a code for this version and person
  Note over M: AppID active, version registered,<br/>same files, verified server domain, scopes
  M-->>C: code (5 minutes, once)
  C-->>Page: code and region
  Page->>B: code and region
  B->>M: code and client secret
  M-->>B: access and refresh token, user.ref
  B->>M: profile with access token
  M-->>B: name, email, institution
  B-->>Page: your own session
```

The page never sees a password, a token or the client secret. The steps below build each part.

## 1. Ask Micromeet to register the Mini App

There is no self-registration. Send Micromeet:

| What | Example |
|---|---|
| AppID: a lower-case reverse domain | `ai.example.app` |
| Name (English; Chinese optional) and developer | Example · Example Ltd |
| Scopes the Mini App needs | `profile:read` |
| Regions of the people who will sign in (SG, HK, ID) | HK |
| Your backend's origin | `https://api.example.com` |

For each region you get a **client secret**, shown once: keep it in the backend's secret store. For each origin you get a verification file (step 2).

## 2. Prove you own the backend's domain

Serve the file at `https://api.example.com/micromeet-miniapp-<16 characters>.txt` over HTTPS, with status 200 and no redirect, and leave it there; Micromeet fetches it to verify the domain. During development the backend can run on `http://localhost:<port>` or `http://127.0.0.1:<port>`, which needs no file in the regions where Micromeet allows it. See [Server domains](https://docs.micromeet.ai/miniapp-sdk/build.md#server-domains).

## 3. Write the page

Install `@micromeet-ai/miniapp-sdk` (see [Build and Sign](https://docs.micromeet.ai/miniapp-sdk/build.md#build-it)), then:

```js
import { maestroSdk } from '@micromeet-ai/miniapp-sdk';

const BACKEND = 'https://api.example.com';      // a verified server domain

export async function signIn() {
  const sdk = maestroSdk();                     // null outside Cowork
  if (!sdk?.auth) return { error: 'open_in_cowork' };
  const login = await sdk.auth.login({ scopes: ['profile:read'] });
  if (!login.ok) return { error: login.error };
  const response = await fetch(`${BACKEND}/login`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ code: login.code, region: login.region })
  });
  return response.json();                       // { session, name }: send session with later requests
}
```

- Call `auth.login()` when the person clicks: Cowork shows its card only on the tab the person is looking at.
- `auth.status()` says whether the package is signed and whether someone is signed in to Cowork; use it to show or hide the button.
- Put every script in a file: inline `<script>` does not run.

## 4. Write the backend

A minimal Node.js backend:

```js
import { createServer } from 'node:http';
import { randomUUID } from 'node:crypto';
import { createMiniappAuthClient, MiniappAuthError } from '@micromeet-ai/miniapp-sdk/server';

const APP_ID = 'ai.example.app';
const PAGE = `miniapp://${APP_ID}`;             // the page's origin
// One client per region, each with that region's secret.
const clients = { HK: createMiniappAuthClient({ appId: APP_ID, clientSecret: process.env.MINIAPP_SECRET_HK }) };
const sessions = new Map();                     // keep these in your database

const reply = (res, status, body) => {
  res.writeHead(status, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': PAGE, Vary: 'Origin' });
  res.end(JSON.stringify(body));
};

createServer((req, res) => {
  if (req.method === 'OPTIONS') {
    res.writeHead(204, { 'Access-Control-Allow-Origin': PAGE, 'Access-Control-Allow-Methods': 'POST', 'Access-Control-Allow-Headers': 'Content-Type, Authorization' });
    return res.end();
  }
  if (req.method !== 'POST' || req.url !== '/login') return reply(res, 404, { error: 'not_found' });
  let raw = '';
  req.on('data', (part) => (raw += part));
  req.on('end', async () => {
    try {
      const { code, region } = JSON.parse(raw);
      const auth = clients[region];
      if (!auth) return reply(res, 400, { error: 'region_not_supported' });
      const tokens = await auth.exchangeCode({ code, region });
      const session = randomUUID();
      sessions.set(session, { ref: tokens.user.ref, region, refreshToken: tokens.refreshToken });
      reply(res, 200, { session, name: tokens.user.name });
    } catch (error) {
      reply(res, 400, { error: error instanceof MiniappAuthError ? error.code : 'bad_request' });
    }
  });
}).listen(8787);
```

- Answer `Access-Control-Allow-Origin: miniapp://<appId>`: that is the page's origin, and without it the request fails in the page before it reaches your code.
- Key your users by `user.ref`, keep the refresh token, and get new tokens with `refresh` when you call Micromeet later.
- The page gets only your own session; Micromeet's tokens stay in the backend.
- Run it on your verified server domain; during development, on localhost. A backend in another language makes the same calls (see [Sign-in](https://docs.micromeet.ai/miniapp-sdk/auth.md), HTTP calls).

## 5. Build, sign and install

1. Add `domains: ['https://api.example.com']` to the Vite plugin and run `vite build` (see [Build and Sign](https://docs.micromeet.ai/miniapp-sdk/build.md)).
2. Run `micromeet-miniapp verify miniapp-package`, then hand the package to Micromeet, which registers and signs this version in each region and returns the signed `manifest.json`: put it back in the package.
3. Run `micromeet-miniapp verify miniapp-package --signed`, then `micromeet-miniapp zip miniapp-package`.
4. In Cowork, ask the agent to install the zip, open the Mini App and sign in.

The released Cowork opens only versions Micromeet's production signed; there is no unsigned development mode.

## 6. Develop and test

- Screens and logic that do not need sign-in: `vite dev` in a browser. There `maestroSdk()` is `null`, so show a short note instead of the button.
- Only the page's files are signed: a change to the backend needs no new version. Keep the page simple and the logic in the backend.
- A change to any page file or to `domains` needs a new `version`, registered, signed and installed again.
- Testing sign-in end to end needs a signed version and a Cowork signed in to an account of a region where the Mini App is registered.
- A development version may list only a localhost domain. Leave localhost out of a release version: a region that does not allow localhost refuses its sign-in with `no_server_domain`.

## If something goes wrong

| Where | You see | Cause and fix |
|---|---|---|
| Cowork | The Mini App is not certified and does not open | The package is not signed, was signed by another Micromeet environment, or a file changed after signing: install the signed zip |
| Page | `app_not_available` | The AppID is not registered in the person's region, or it is disabled |
| Page | `version_not_available` | This version is not registered in that region, or it is disabled |
| Page | `no_server_domain` | The version has no verified server domain in that region, or the verification file is gone |
| Page | `scope_denied` | The page asked for a scope Micromeet did not allow this Mini App |
| Page | `fetch` fails with a CORS error | The backend does not answer `Access-Control-Allow-Origin: miniapp://<appId>` |
| Backend | `invalid_client` | The secret is wrong for this region, or it was replaced |
| Backend | `invalid_grant` | The code was used already, is older than 5 minutes, or is for another AppID |

## Related

- [Sign-in](https://docs.micromeet.ai/miniapp-sdk/auth.md): Every sign-in function, the backend helper and its errors
- [Build and Sign](https://docs.micromeet.ai/miniapp-sdk/build.md): Make the package and have Micromeet sign it
- [Package Spec](https://docs.micromeet.ai/miniapp-sdk/package-spec.md): Every rule a package must meet
