Wiki/bridge/docs/baserow-auth.md
Corentin JOGUET 445dda260a feat(bridge): add Baserow user JWT auto-login for metadata endpoints — Patch 031
Service account pattern resolves 401 PERMISSION_DENIED on Baserow metadata
endpoints (/api/database/views/table/:id/, /api/database/tables/:id/) which
reject DB tokens. A dedicated Baserow user account logs in via token-auth,
JWT cached in memory with mutex-protected refresh before expiry.

Fallback graceful: if BASEROW_USER_EMAIL/PASSWORD absent, CRUD rows still work,
metadata endpoints return 500 BASEROW_USER_AUTH_NOT_CONFIGURED.

417 tests pass (was 392, +25). 0 TS errors.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 14:44:55 +02:00

143 lines
4.4 KiB
Markdown

# Baserow Authentication — Bridge Service
## Pourquoi deux types de tokens
Baserow expose deux mecanismes d'authentification aux comportements differents :
| Type | Header | Droits | Usages |
|------|--------|--------|--------|
| DB token | `Token brg_*` | CRUD rows uniquement | `listRows`, `createRow`, `updateRow`, `deleteRow` |
| User JWT | `JWT eyJ...` | Toutes les routes API | Metadata : `listViews`, `getTable`, `resolveTableIds` |
Le DB token (BASEROW_API_TOKEN) est suffisant pour 90% des cas mais Baserow renvoie
`401 PERMISSION_DENIED` sur les endpoints metadata. Ce n'est pas un bug : c'est la
conception Baserow qui distingue DB tokens (CRUD rows) des user sessions (lecture
full-API).
Le bridge utilise donc les deux :
- **DB token** : routes `/api/database/rows/table/:id/` — toujours actif
- **User JWT** : routes `/api/database/views/table/:id/`, `/api/database/tables/:id/` — actif si `BASEROW_USER_EMAIL` + `BASEROW_USER_PASSWORD` sont configures
## API Baserow — endpoints JWT
Source : docs.baserow.io + github.com/code-watch/baserow/blob/master/docs/getting-started/api.md
### Login
```
POST /api/user/token-auth/
Content-Type: application/json
{ "username": "email@example.com", "password": "..." }
```
Reponse :
```json
{ "token": "eyJ..." }
```
Le JWT est valide 60 minutes (valeur par defaut Baserow, configurable cote serveur).
### Refresh
```
POST /api/user/token-refresh/
Content-Type: application/json
{ "token": "eyJ..." (token courant) }
```
Reponse :
```json
{ "token": "eyJ..." (nouveau token) }
```
### Header d'autorisation
```
Authorization: JWT eyJ...
```
Note : le prefixe est `JWT`, pas `Bearer` ni `Token`.
## Creer le compte service Baserow
1. Ouvrir `http://<baserow-host>/admin/users/`
2. Cliquer "Add user"
3. Remplir :
- Email : `bridge-svc@interne.local` (ou equivalent interne)
- Password : generer un mot de passe fort (min 16 chars, stocker dans Vault ou secret manager)
- Is active : oui
- Is staff : non (pas besoin d'admin)
4. Ajouter ce user aux groupes (workspaces) ou il doit lire les tables
- Permission "Member" suffit pour lire views + tables
5. Verifier : `curl -X POST http://baserow/api/user/token-auth/ -d '{"username":"bridge-svc@...","password":"..."}'`
## Variables d'environnement
```bash
# Requis pour activer le JWT manager
BASEROW_USER_EMAIL=bridge-svc@interne.local
BASEROW_USER_PASSWORD=generated-strong-password
# Optionnel — refresh le JWT N secondes avant son expiration (defaut 60)
BASEROW_JWT_REFRESH_MARGIN=60
```
Si ces variables sont absentes, le bridge continue a fonctionner pour les CRUD rows.
Les routes views renvoient `500 BASEROW_USER_AUTH_NOT_CONFIGURED`.
## Verification que ca marche
### Via le health endpoint
```bash
curl -s http://localhost:4000/api/health
# { "status": "ok", "service": "bridge", "version": "0.1.0" }
```
### Via la route views
```bash
curl -s \
-H "Authorization: Bearer brg_<votre-token>" \
"http://localhost:4000/api/v1/views/table/personne"
# { "data": [...], "total": N }
```
Avant Patch 031, cette route retournait 401 de Baserow.
Apres Patch 031 avec JWT configure, elle retourne les vues de la table.
### Verifier les logs au boot
```
INFO: Baserow user JWT manager enabled (email: bridge-svc@interne.local)
```
Si vous voyez a la place :
```
INFO: Baserow user JWT manager disabled — metadata endpoints will return 503 if called
```
c'est que BASEROW_USER_EMAIL ou BASEROW_USER_PASSWORD est absent.
## Architecture interne
```
getToken() appel ─► BaserowJwtManagerImpl
├─ token cache frais ? → retour immediat
├─ token bientot expire ? → POST /api/user/token-refresh/
│ └─ echec ? → fallback POST /api/user/token-auth/
└─ pas de token ? → POST /api/user/token-auth/
Mutex : si 10 appels simultanes → 1 seul login/refresh, les 9 autres attendent.
```
`BaserowClient.listViewsWithJwt(tableId, jwt)` utilise `Authorization: JWT <jwt>` vers Baserow.
## Bonnes pratiques securite
- Le password est remplace par `***` dans les logs (voir `baserow-jwt-manager.ts`, appels `this.logger.error`)
- Le JWT reste en memoire uniquement (pas de Redis, pas de fichier disque)
- Le JWT n'est pas retransmis dans les reponses HTTP du bridge vers les clients
- En cas de rotation du mot de passe Baserow : redemarrer le bridge (re-login au premier appel)
- Stocker BASEROW_USER_PASSWORD dans un secret manager (Vault, Docker secrets, k8s Secret)