# Sign-in

A Mini App can sign in the person using Cowork, with their Micromeet account, and then call Micromeet on their behalf from its own backend. The page never sees a password, a token or the Mini App's secret: Cowork gives the page a one-time code, and the Mini App's backend exchanges that code for tokens.

Only an installed Mini App whose package Micromeet signed, with at least one verified server domain, can sign a person in (see [Build and Sign](https://docs.micromeet.ai/miniapp-sdk/build.md)). The module is missing on built-in Mini Apps.

## MAESTROSDK.auth.login(params?) <Badge type="warning" text="Not yet released" />

Asks Cowork for a one-time code for this Mini App and the person signed in to Cowork.

### Parameters

| Field | Type | Required | Description |
|---|---|---|---|
| `scopes` | `string[]` | No | What the Mini App asks for, for example `['profile:read']`. Leave it out to ask for everything Micromeet allowed this Mini App |

The first time, Cowork shows the person a card with the Mini App's name and developer, what it asks for, and the account it will use; **Allow** or **Cancel**, Cancel by default. The card shows only on the tab the person is looking at. After the person allowed it once, later calls for the same scopes give a code without a card.

### Returns

`Promise<{ ok: true; code: string; region: 'SG' | 'HK' | 'ID'; expiresIn: number } | { ok: false; error: string }>`

The code works once, for 5 minutes (`expiresIn` in seconds). Send `code` and `region` to your backend.

| `error` | What happened |
|---|---|
| `unsigned` | The package is not signed by Micromeet, or the page is not an installed Mini App |
| `tampered` | A file of the Mini App changed after it was installed; Cowork closed it |
| `host_signed_out` | Nobody is signed in to Cowork, or the page was opened for another account. Cowork opens its sign-in if this is the tab the person is looking at |
| `user_cancelled` | The person cancelled, the tab was in the background, or the account changed while the card was up |
| `app_not_available` | The AppID is not registered in this region, or it is disabled |
| `version_not_available` | This version is not registered, or it was disabled: update the Mini App |
| `no_server_domain` | This version has no verified server domain, or one of its domains is no longer verified (see [Server domains](https://docs.micromeet.ai/miniapp-sdk/build.md#server-domains)) |
| `scope_denied` | A scope Micromeet did not allow for this Mini App, or `scopes` is not a list of strings |
| `rate_limited` | Too many codes in a minute; try again shortly |
| `network` | Cowork could not reach Micromeet |

## MAESTROSDK.auth.status() <Badge type="warning" text="Not yet released" />

`Promise<{ signed: boolean; host: 'cowork'; hostSignedIn: boolean }>`. `signed` is `true` when the package's signature is valid and none of its files changed; `hostSignedIn` when someone is signed in to Cowork. Use it to decide whether to show a sign-in button.

## In the Mini App's backend

The backend keeps the client secret Micromeet gave when it registered the AppID, one for each region the Mini App is registered in; never put it in the page or the package. It runs on one of the Mini App's verified server domains (see [Server domains](https://docs.micromeet.ai/miniapp-sdk/build.md#server-domains)). A step-by-step example is in [Build a Sign-in Mini App](https://docs.micromeet.ai/miniapp-sdk/sign-in-guide.md).

### Requests from the page

The page's origin is `miniapp://<appId>`. Answer its requests with `Access-Control-Allow-Origin: miniapp://<appId>`, and its preflight (`OPTIONS`) with the methods and headers it sends; otherwise they fail in the page before they reach your code. Keep Micromeet's tokens in the backend and give the page a session of your own.

### The helper

`@micromeet-ai/miniapp-sdk/server` works from ESM or CommonJS on Node.js 18 or later.

```js
import { createMiniappAuthClient, MiniappAuthError } from '@micromeet-ai/miniapp-sdk/server';

const auth = createMiniappAuthClient({ appId: 'ai.example.app', clientSecret: process.env.MINIAPP_SECRET_HK });
const tokens = await auth.exchangeCode({ code, region });
const me = await auth.profile({ accessToken: tokens.accessToken, region: tokens.region });
```

| Option | Required | Description |
|---|---|---|
| `appId` | Yes | The AppID |
| `clientSecret` | Yes | The secret of the region this client calls; create one client per region |
| `coreUrl` | No | `(region) => address`: which Micromeet to call. Default: production, by region |
| `fetch` | No | Replaces the global `fetch` |
| `timeoutMs` | No | Per request; default 15000 |

| Function | Returns |
|---|---|
| `exchangeCode({ code, region })` | Tokens: `accessToken`, `expiresIn` (seconds), `refreshToken`, `scope` (a list), `user: { ref, name, email }`, `institution: { iid, name }`, `region` |
| `refresh({ refreshToken, region })` | A new set of tokens; the old refresh token stops working, keep the new one |
| `revoke({ refreshToken, region })` | Nothing; signs the person out of the Mini App |
| `profile({ accessToken, region })` | `{ ref, name, email, institution: { iid, name }, region }` (`profile:read`) |
| `call({ accessToken, region, path, body? })` | The `data` of a `POST` to a Micromeet route that accepts Mini App tokens |

On failure every function throws `MiniappAuthError`, with Micromeet's `code` and the HTTP `status` (see Errors below).

### Tokens

| Token | Lifetime | Use |
|---|---|---|
| Access token | 15 minutes | `Authorization: Bearer` on the routes that accept Mini App tokens |
| Refresh token | 30 days | `refresh` gives a new pair; each refresh token works once |

- Use the region the code came with for every later call; a token works only in its region.
- A token is for the person and for the institution they had selected in Cowork when they signed in.
- `user.ref` is a stable id for this person in this Mini App (another Mini App gets another one): use it as your user key.
- A changed password, signing out everywhere, leaving the institution, a disabled version or a disabled Mini App stops the tokens at their next request.

### Scopes and routes

| Scope | Route | Returns |
|---|---|---|
| `profile:read` | `POST /share/miniapp/profile` | `{ ref, name, email, institution: { iid, name }, region }` |

A token works only on the routes in this table, for the scopes the person allowed, and never beyond what the person may do in Micromeet.

### Errors

| `code` | HTTP | What happened |
|---|---|---|
| `invalid_client` | 401 | The AppID or the secret is wrong for this region, or the secret was replaced |
| `invalid_grant` | 400 | The code is used, older than 5 minutes or for another AppID; the refresh token is used, revoked or older than 30 days; or the person changed the password, signed out everywhere or left the institution: sign in again |
| `app_not_available` | 400 | The Mini App is disabled in this region |
| `version_not_available` | 400 | The version the person signed in from was disabled: update the Mini App |
| `needLogin` | 401 | On a route: the access token expired or no longer holds. Call `refresh`; if that fails, sign in again |
| `forbidden` | 403 | On a route: the token does not hold the route's scope, or the person may not do this in Micromeet |
| `network` | — | The helper could not reach Micromeet (`status` 0) |
| `params_invalid` | 400, or — | A wrong `grant_type`; or, in the helper, an unknown region or a `path` that is not a route (`status` 0, no request sent) |

### HTTP calls

A backend in another language makes the same calls. Each is a `POST` with a JSON body to the Micromeet of the code's region:

| Region | Address |
|---|---|
| SG | `https://crms-api.micromeet.ai` |
| HK | `https://crms-api-hk.micromeet.ai` |
| ID | `https://crms-api-id.micromeet.ai` |

| Path | Body | `data` in the answer |
|---|---|---|
| `/miniapp-open/auth/token` | `{ "grant_type": "authorization_code", "app_id", "client_secret", "code" }` | `{ access_token, token_type: "Bearer", expires_in, refresh_token, scope, user: { ref, name, email }, institution: { iid, name }, region }`; `scope` is space-separated |
| `/miniapp-open/auth/token` | `{ "grant_type": "refresh_token", "app_id", "client_secret", "refresh_token" }` | The same |
| `/miniapp-open/auth/revoke` | `{ "app_id", "client_secret", "refresh_token" }` | `{ revoked: true }` |
| `/share/miniapp/profile` | `{}`, with `Authorization: Bearer <access_token>` | `{ ref, name, email, institution: { iid, name }, region }` |

A success answers `{ "code": 0, "success": true, "data": … }`. A failure answers `{ "code": "<error>", "msg": "…", "success": false }` with the HTTP status in Errors above.

## Related

- [Build and Sign](https://docs.micromeet.ai/miniapp-sdk/build.md): Package the Mini App and have Micromeet sign it
- [Overview](https://docs.micromeet.ai/miniapp-sdk/overview.md): All modules of the Mini App SDK
