# auth.vitrap

Authentication service for Vitrap: sign-up, login, logout, token refresh,
email verification and password reset. It issues short-lived ES256 access
tokens (JWT) plus a refresh token cookie, and publishes its public key at
`/.well-known/jwks.json` so the other services (api, cdn, admin-api) can verify
those tokens.

Built with AdonisJS 7 on Node.js 24, using the MySQL database it shares with
api.vitrap.

## Prerequisites

- **Node.js 24 or higher** (AdonisJS 7 requires it): `node -v`
- **Docker** (Docker Desktop on Windows/macOS), for MySQL and Mailpit
- The **db.vitrap** repository next to this one (`D:\projects\db.vitrap`), which
  runs the local MySQL

## Local services

| Service    | URL                     | Comes from                       |
| ---------- | ----------------------- | -------------------------------- |
| auth       | http://localhost:4000   | this repo                        |
| MySQL      | localhost:3306          | db.vitrap                        |
| phpMyAdmin | http://localhost:8888   | db.vitrap                        |
| Mailpit    | http://localhost:8025   | this repo's docker-compose.dev.yml (SMTP on 1025) |

## 1. Configure the environment

```bash
cp .env.example .env
```

Fill in the three empty secrets in `.env`:

1. `APP_KEY`: run `node ace generate:key`; it writes the key into `.env`.
2. `JWT_PRIVATE_KEY_BASE64` and `JWT_PUBLIC_KEY_BASE64`: run `npm run keys:gen`
   and copy the first two lines (ignore the `BEARER_PUBLIC_KEY` line, nothing
   uses it). Each key is one long line; copy it **in full**.
3. Check the keys: `npm run keys:test` should show a ✓ for both.

Every other value in `.env.example` already matches the local setup. The
comments in that file explain what each variable does.

## 2. Start the database and Mailpit

```bash
# MySQL + phpMyAdmin
cd ../db.vitrap
docker compose up -d

# Mailpit (catches every mail auth sends)
cd ../auth.vitrap
docker compose -f docker-compose.dev.yml up -d mailpit
```

The MySQL container creates the `vitrap` database and the `vitrap`/`vitrap`
user on first start, but **no tables**. The schema comes from **api.vitrap's
knex migrations** (users, user_pref, roles, refresh tokens, login codes, …).
Run those to create the tables; see api.vitrap. auth starts without the tables,
but sign-up and login fail until they exist.

Don't run auth's own `node ace migration:run`: the files in
`database/migrations` are older and overlap with api.vitrap's migrations.

## 3a. Run it from a terminal

```bash
npm install
npm run dev
```

auth runs at http://localhost:4000 with hot reload. Check that it works by
opening http://localhost:4000/.well-known/jwks.json: it should show one key.

## 3b. Run it in Docker

Stop the terminal server first (both use port 4000).

```bash
cp .env.docker.example .env.docker
docker compose -f docker-compose.dev.yml up --build
```

This starts auth and Mailpit. The container reads `.env` and then
`.env.docker`, which overrides three values for life inside a container:

| Variable    | Value                  | Why                                            |
| ----------- | ---------------------- | ---------------------------------------------- |
| `HOST`      | `0.0.0.0`              | listen outside the container's own localhost   |
| `DB_HOST`   | `host.docker.internal` | MySQL runs on the host machine (db.vitrap)     |
| `SMTP_HOST` | `mailpit`              | Mailpit is a service in the same compose file  |

On Linux, `host.docker.internal` only works when the service has
`extra_hosts: ["host.docker.internal:host-gateway"]` in the compose file.
Docker Desktop on Windows and macOS provides it automatically.

## Scripts

| Command             | What it does                                   |
| ------------------- | ---------------------------------------------- |
| `npm run dev`       | development server with hot reload             |
| `npm run build`     | production build into `build/`                 |
| `npm start`         | run the production build                       |
| `npm run keys:gen`  | generate a new JWT key pair                    |
| `npm run keys:test` | check that the keys in `.env` load             |
| `npm run lint`      | ESLint                                         |
| `npm run typecheck` | TypeScript check                               |
| `npm test`          | Japa tests (in-memory SQLite, see `.env.test`) |

## Production

`docker-compose.prod.yml` builds the `prod` stage of the Dockerfile and reads
its configuration from `.env.production` (never committed). Production differs
from local in a few values: `NODE_ENV=production`, `COOKIE_DOMAIN=.vitrap.com`,
the real `https://` URLs in `APP_URL`, `FRONTEND_URL` and `TRUSTED_ORIGINS`, a
real SMTP server and its own key pair.

## Troubleshooting

- **The app exits at start-up about a missing environment variable**: a
  required variable in `.env` is missing or empty. Compare with `.env.example`.
- **`npm run keys:test` fails, or tokens are rejected**: a JWT key was cut off
  when it was copied. Generate a new pair and copy each line in full.
- **Login works but you're logged out right away / no `vtrap_rt` cookie**:
  check `NODE_ENV=development` (production makes the cookie https-only) and
  that `COOKIE_DOMAIN` isn't set locally.
- **CORS errors in the browser**: the frontend's origin must be in
  `TRUSTED_ORIGINS`, including `http://`.
- **"Table doesn't exist" errors**: run api.vitrap's migrations (step 2).
- **Code changes don't reload in Docker on Windows**: file changes on a Windows
  drive often don't reach the container. Restart it, or keep the repo inside
  WSL.
- **`Failed to load native binding` / native module errors**: `node_modules`
  was installed on another operating system. Delete it and run `npm install`
  again on the machine where you run the app. (Docker installs its own copy.)
