# TJWater Authentication and Metadata Management

## Ownership

Keycloak owns login identity, credentials, token issuance, and token expiry.
TJWater metadata stores only business snapshots and authorization data:

- `users.keycloak_id` is the stable identity binding.
- `users.username`, `users.email`, and `users.last_login_at` are Keycloak claim caches.
- `users.role`, `users.is_active`, and `users.is_superuser` control TJWater system access.
- `user_project_membership.project_role` controls project access.

The backend does not accept passwords, does not issue local JWTs, and does not
trust frontend-supplied user IDs.

## Login Snapshot Refresh

Every authenticated metadata-user resolution validates the Keycloak access token
and reads `sub`, `preferred_username` or `username`, and `email` claims. The
backend finds `users` by `keycloak_id = sub`, rejects inactive or missing users,
then refreshes `username`, `email`, and `last_login_at`.

This keeps local display data current without changing the identity binding.
There is no Keycloak webhook requirement; second-level user or permission sync is
out of scope unless explicitly requested later.

## Admin APIs

All admin APIs require metadata admin access: `users.is_superuser = true` or
`users.role = 'admin'`.

User and membership management:

- `GET /api/v1/admin/me`
- `POST /api/v1/admin/users/sync`
- `POST /api/v1/admin/users/sync/batch`
- `GET /api/v1/admin/users`
- `GET /api/v1/admin/users/{user_id}`
- `PATCH /api/v1/admin/users/{user_id}`
- `GET /api/v1/admin/projects/{project_id}/members`
- `POST /api/v1/admin/projects/{project_id}/members`
- `PATCH /api/v1/admin/projects/{project_id}/members/{user_id}`
- `DELETE /api/v1/admin/projects/{project_id}/members/{user_id}`

Project configuration:

- `GET /api/v1/admin/projects`
- `POST /api/v1/admin/projects`
- `PATCH /api/v1/admin/projects/{project_id}`
- `GET /api/v1/admin/projects/{project_id}/databases`
- `PUT /api/v1/admin/projects/{project_id}/databases`
- `DELETE /api/v1/admin/projects/{project_id}/databases/{db_role}`
- `POST /api/v1/admin/projects/{project_id}/databases/{db_role}/health`

## Secret Handling

Admins submit plaintext DSNs only through HTTPS admin APIs. Operators should not
write encrypted columns manually.

- `project_databases.dsn_encrypted` is encrypted with `DATABASE_ENCRYPTION_KEY`.
- Admin responses return only `has_dsn`.
- Audit logs record whether a secret was updated, but never store plaintext DSNs
  or other secrets.

Generate the database encryption key with:

```bash
python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
```

Keep keys stable for the lifetime of encrypted metadata. Rotating a key requires
decrypting with the old key and re-encrypting with the new key.

## Metadata Schema Patches

Apply metadata patches in order:

1. `resources/sql/004_metadata_auth_management.sql`
2. `resources/sql/005_metadata_project_configuration.sql`

`004` creates Keycloak-backed metadata users and project memberships. `005`
creates project and project database routing tables with uniqueness, role/type,
and pool-size constraints.

## Frontend System Management

`/system-admin` is shown only after `GET /api/v1/admin/me` confirms metadata
admin access. The page lets admins maintain metadata users, project members,
projects, project database routing for `biz_data` and `iot_data`, connection
health checks. This replaces direct SQL editing for normal project onboarding.
