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>
143 lines
4.4 KiB
Markdown
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)
|