@obsidiane/auth-client-js 1.0.4 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/README.md +725 -105
  2. package/fesm2022/obsidiane-auth-client-js.mjs +961 -0
  3. package/fesm2022/obsidiane-auth-client-js.mjs.map +1 -0
  4. package/index.d.ts +361 -0
  5. package/obsidiane-auth-client-js-0.1.0.tgz +0 -0
  6. package/package.json +15 -40
  7. package/dist/index.d.ts +0 -9
  8. package/dist/index.js +0 -10
  9. package/dist/index.js.map +0 -1
  10. package/dist/src/lib/bridge/rest/api-platform.adapter.d.ts +0 -18
  11. package/dist/src/lib/bridge/rest/api-platform.adapter.js +0 -67
  12. package/dist/src/lib/bridge/rest/api-platform.adapter.js.map +0 -1
  13. package/dist/src/lib/bridge/rest/http-request.options.d.ts +0 -4
  14. package/dist/src/lib/bridge/rest/http-request.options.js +0 -17
  15. package/dist/src/lib/bridge/rest/http-request.options.js.map +0 -1
  16. package/dist/src/lib/bridge/rest/query-builder.d.ts +0 -3
  17. package/dist/src/lib/bridge/rest/query-builder.js +0 -41
  18. package/dist/src/lib/bridge/rest/query-builder.js.map +0 -1
  19. package/dist/src/lib/bridge/sse/eventsource-wrapper.d.ts +0 -17
  20. package/dist/src/lib/bridge/sse/eventsource-wrapper.js +0 -57
  21. package/dist/src/lib/bridge/sse/eventsource-wrapper.js.map +0 -1
  22. package/dist/src/lib/bridge/sse/mercure-topic.mapper.d.ts +0 -19
  23. package/dist/src/lib/bridge/sse/mercure-topic.mapper.js +0 -45
  24. package/dist/src/lib/bridge/sse/mercure-topic.mapper.js.map +0 -1
  25. package/dist/src/lib/bridge/sse/mercure-url.builder.d.ts +0 -7
  26. package/dist/src/lib/bridge/sse/mercure-url.builder.js +0 -18
  27. package/dist/src/lib/bridge/sse/mercure-url.builder.js.map +0 -1
  28. package/dist/src/lib/bridge/sse/mercure.adapter.d.ts +0 -37
  29. package/dist/src/lib/bridge/sse/mercure.adapter.js +0 -242
  30. package/dist/src/lib/bridge/sse/mercure.adapter.js.map +0 -1
  31. package/dist/src/lib/bridge/sse/ref-count-topic.registry.d.ts +0 -17
  32. package/dist/src/lib/bridge/sse/ref-count-topic.registry.js +0 -42
  33. package/dist/src/lib/bridge/sse/ref-count-topic.registry.js.map +0 -1
  34. package/dist/src/lib/bridge.types.d.ts +0 -27
  35. package/dist/src/lib/bridge.types.js +0 -8
  36. package/dist/src/lib/bridge.types.js.map +0 -1
  37. package/dist/src/lib/facades/bridge.facade.d.ts +0 -27
  38. package/dist/src/lib/facades/bridge.facade.js +0 -100
  39. package/dist/src/lib/facades/bridge.facade.js.map +0 -1
  40. package/dist/src/lib/facades/facade.factory.d.ts +0 -22
  41. package/dist/src/lib/facades/facade.factory.js +0 -36
  42. package/dist/src/lib/facades/facade.factory.js.map +0 -1
  43. package/dist/src/lib/facades/facade.interface.d.ts +0 -13
  44. package/dist/src/lib/facades/facade.interface.js +0 -2
  45. package/dist/src/lib/facades/facade.interface.js.map +0 -1
  46. package/dist/src/lib/facades/resource.facade.d.ts +0 -30
  47. package/dist/src/lib/facades/resource.facade.js +0 -61
  48. package/dist/src/lib/facades/resource.facade.js.map +0 -1
  49. package/dist/src/lib/interceptors/bridge-debug.interceptor.d.ts +0 -6
  50. package/dist/src/lib/interceptors/bridge-debug.interceptor.js +0 -27
  51. package/dist/src/lib/interceptors/bridge-debug.interceptor.js.map +0 -1
  52. package/dist/src/lib/interceptors/bridge-defaults.interceptor.d.ts +0 -5
  53. package/dist/src/lib/interceptors/bridge-defaults.interceptor.js +0 -43
  54. package/dist/src/lib/interceptors/bridge-defaults.interceptor.js.map +0 -1
  55. package/dist/src/lib/interceptors/content-type.interceptor.d.ts +0 -9
  56. package/dist/src/lib/interceptors/content-type.interceptor.js +0 -34
  57. package/dist/src/lib/interceptors/content-type.interceptor.js.map +0 -1
  58. package/dist/src/lib/interceptors/singleflight.interceptor.d.ts +0 -3
  59. package/dist/src/lib/interceptors/singleflight.interceptor.js +0 -42
  60. package/dist/src/lib/interceptors/singleflight.interceptor.js.map +0 -1
  61. package/dist/src/lib/ports/realtime.port.d.ts +0 -28
  62. package/dist/src/lib/ports/realtime.port.js +0 -2
  63. package/dist/src/lib/ports/realtime.port.js.map +0 -1
  64. package/dist/src/lib/ports/resource-repository.port.d.ts +0 -64
  65. package/dist/src/lib/ports/resource-repository.port.js +0 -2
  66. package/dist/src/lib/ports/resource-repository.port.js.map +0 -1
  67. package/dist/src/lib/provide-bridge.d.ts +0 -40
  68. package/dist/src/lib/provide-bridge.js +0 -81
  69. package/dist/src/lib/provide-bridge.js.map +0 -1
  70. package/dist/src/lib/tokens.d.ts +0 -14
  71. package/dist/src/lib/tokens.js +0 -14
  72. package/dist/src/lib/tokens.js.map +0 -1
  73. package/dist/src/lib/utils/url.d.ts +0 -6
  74. package/dist/src/lib/utils/url.js +0 -17
  75. package/dist/src/lib/utils/url.js.map +0 -1
  76. package/dist/src/models/Auth.d.ts +0 -4
  77. package/dist/src/models/Auth.js +0 -2
  78. package/dist/src/models/Auth.js.map +0 -1
  79. package/dist/src/models/AuthInviteCompleteInputInviteComplete.d.ts +0 -6
  80. package/dist/src/models/AuthInviteCompleteInputInviteComplete.js +0 -2
  81. package/dist/src/models/AuthInviteCompleteInputInviteComplete.js.map +0 -1
  82. package/dist/src/models/AuthInviteUserInputInviteSend.d.ts +0 -4
  83. package/dist/src/models/AuthInviteUserInputInviteSend.js +0 -2
  84. package/dist/src/models/AuthInviteUserInputInviteSend.js.map +0 -1
  85. package/dist/src/models/AuthLdJson.d.ts +0 -4
  86. package/dist/src/models/AuthLdJson.js +0 -2
  87. package/dist/src/models/AuthLdJson.js.map +0 -1
  88. package/dist/src/models/AuthPasswordForgotInputPasswordForgot.d.ts +0 -4
  89. package/dist/src/models/AuthPasswordForgotInputPasswordForgot.js +0 -2
  90. package/dist/src/models/AuthPasswordForgotInputPasswordForgot.js.map +0 -1
  91. package/dist/src/models/AuthPasswordResetInputPasswordReset.d.ts +0 -5
  92. package/dist/src/models/AuthPasswordResetInputPasswordReset.js +0 -2
  93. package/dist/src/models/AuthPasswordResetInputPasswordReset.js.map +0 -1
  94. package/dist/src/models/AuthRegisterUserInputUserRegister.d.ts +0 -5
  95. package/dist/src/models/AuthRegisterUserInputUserRegister.js +0 -2
  96. package/dist/src/models/AuthRegisterUserInputUserRegister.js.map +0 -1
  97. package/dist/src/models/FrontendConfig.d.ts +0 -11
  98. package/dist/src/models/FrontendConfig.js +0 -2
  99. package/dist/src/models/FrontendConfig.js.map +0 -1
  100. package/dist/src/models/InvitePreview.d.ts +0 -7
  101. package/dist/src/models/InvitePreview.js +0 -2
  102. package/dist/src/models/InvitePreview.js.map +0 -1
  103. package/dist/src/models/InviteUserInviteRead.d.ts +0 -8
  104. package/dist/src/models/InviteUserInviteRead.js +0 -2
  105. package/dist/src/models/InviteUserInviteRead.js.map +0 -1
  106. package/dist/src/models/Setup.d.ts +0 -4
  107. package/dist/src/models/Setup.js +0 -2
  108. package/dist/src/models/Setup.js.map +0 -1
  109. package/dist/src/models/SetupRegisterUserInputUserRegister.d.ts +0 -5
  110. package/dist/src/models/SetupRegisterUserInputUserRegister.js +0 -2
  111. package/dist/src/models/SetupRegisterUserInputUserRegister.js.map +0 -1
  112. package/dist/src/models/UserUpdateUserRolesInputUserRoles.d.ts +0 -4
  113. package/dist/src/models/UserUpdateUserRolesInputUserRoles.js +0 -2
  114. package/dist/src/models/UserUpdateUserRolesInputUserRoles.js.map +0 -1
  115. package/dist/src/models/UserUserRead.d.ts +0 -8
  116. package/dist/src/models/UserUserRead.js +0 -2
  117. package/dist/src/models/UserUserRead.js.map +0 -1
  118. package/dist/src/models/index.d.ts +0 -14
  119. package/dist/src/models/index.js +0 -2
  120. package/dist/src/models/index.js.map +0 -1
  121. package/dist/src/public-api.d.ts +0 -8
  122. package/dist/src/public-api.js +0 -9
  123. package/dist/src/public-api.js.map +0 -1
package/README.md CHANGED
@@ -1,181 +1,801 @@
1
1
  # @obsidiane/auth-client-js
2
2
 
3
- Obsidiane Auth API Client for Angular/TypeScript.
3
+ Bridge Angular (runtime + models TypeScript) pour une API Platform (Hydra/JSON-LD), avec support Mercure/SSE optionnel.
4
4
 
5
- A lightweight SDK for interacting with the Obsidiane Auth service, featuring:
6
- - **Bridge from Meridiane**: Auto-generated HTTP client, facades, and TypeScript models from OpenAPI spec
7
- - **Zero runtime dependencies**: Uses native browser APIs and Angular's built-in HTTP client
5
+ Le package expose une API Angular volontairement minimaliste :
6
+ - `provideBridge()` pour configurer le bridge (HTTP + tokens + Mercure) ;
7
+ - `FacadeFactory` / `ResourceFacade<T>` pour une API orientée ressource ;
8
+ - `BridgeFacade` pour des appels ad-hoc.
8
9
 
9
10
  ## Installation
10
11
 
11
12
  ```bash
12
- npm install @obsidiane/auth-client-js
13
+ npm i @obsidiane/auth-client-js
13
14
  ```
14
15
 
15
- ## Quick Start
16
+ Le bridge est conçu pour être installé dans une application Angular. Les dépendances Angular et RxJS sont des `peerDependencies`.
16
17
 
17
- ### 1. Setup in your Angular app
18
+ ## Compatibilité
18
19
 
19
- ```typescript
20
- // app.config.ts
21
- import { provideBridge } from '@obsidiane/auth-client-js';
20
+ - Angular `@angular/*` `^20.1.0`
21
+ - RxJS `^7.8.0`
22
+
23
+ ## Démarrage rapide
24
+
25
+ Configurez le bridge au démarrage de l’application avec `provideBridge()` (requis).
26
+
27
+ ### Application standalone (recommandé)
28
+
29
+ ```ts
30
+ // app.config.ts (ou main.ts)
31
+ import {ApplicationConfig} from '@angular/core';
32
+ import {provideBridge} from '@obsidiane/auth-client-js';
22
33
 
23
34
  export const appConfig: ApplicationConfig = {
24
35
  providers: [
25
- // Provide the Meridiane bridge (HTTP client + facades)
26
36
  provideBridge({
27
- baseUrl: 'http://localhost:9000',
37
+ baseUrl: 'https://api.example.com',
38
+ mercure: {hubUrl: 'https://api.example.com/.well-known/mercure'},
28
39
  }),
29
-
30
40
  ],
31
41
  };
32
42
  ```
33
43
 
34
- ### 2. Use in your services
44
+ ### Application NgModule
45
+
46
+ ```ts
47
+ import {NgModule} from '@angular/core';
48
+ import {provideBridge} from '@obsidiane/auth-client-js';
49
+
50
+ @NgModule({
51
+ providers: [
52
+ provideBridge({baseUrl: 'https://api.example.com'}),
53
+ ],
54
+ })
55
+ export class AppModule {}
56
+ ```
57
+
58
+ ## Configuration
59
+
60
+ `provideBridge({ ... })` accepte notamment :
61
+ - `baseUrl` (requis) ;
62
+ - `auth` (Bearer ou interceptor custom) ;
63
+ - `mercure` (hubUrl + options SSE) ;
64
+ - `defaults` (headers/timeout/retries) ;
65
+ - `singleFlight` (déduplication “in-flight” des requêtes HTTP identiques `GET/HEAD/OPTIONS`) ;
66
+ - `debug` (logs runtime) ;
67
+ - `extraInterceptors` (interceptors Angular additionnels).
68
+
69
+ ### Cookies / `withCredentials`
70
+
71
+ Le bridge peut envoyer des cookies (sessions) côté HTTP et SSE.
72
+
73
+ Le comportement `withCredentials` par défaut est déduit de `mercure.init` :
74
+ - `credentials: 'include'` (défaut) → cookies envoyés
75
+ - `credentials: 'omit'` → cookies non envoyés
76
+
77
+ Vous pouvez aussi surcharger au cas par cas via `opts.withCredentials` sur les appels HTTP.
78
+
79
+ ```ts
80
+ import {provideBridge} from '@obsidiane/auth-client-js';
81
+
82
+ provideBridge({
83
+ baseUrl: 'https://api.example.com',
84
+ mercure: {
85
+ hubUrl: 'https://api.example.com/.well-known/mercure',
86
+ init: {credentials: 'omit'},
87
+ },
88
+ });
89
+ ```
90
+
91
+ ### Auth (Bearer)
92
+
93
+ `auth` accepte :
94
+ - une string (token Bearer),
95
+ - `{ type: 'bearer', token }`,
96
+ - `{ type: 'bearer', getToken }` (sync ou async),
97
+ - ou un `HttpInterceptorFn` custom.
35
98
 
36
- ```typescript
37
- import { Injectable, inject } from '@angular/core';
38
- import { FacadeFactory } from '@obsidiane/auth-client-js';
39
- import type { UserUserRead } from '@obsidiane/auth-client-js';
99
+ ```ts
100
+ import {provideBridge} from '@obsidiane/auth-client-js';
40
101
 
41
- @Injectable({ providedIn: 'root' })
42
- export class AuthService {
102
+ provideBridge({
103
+ baseUrl: 'https://api.example.com',
104
+ auth: {type: 'bearer', getToken: () => localStorage.getItem('token') ?? undefined},
105
+ });
106
+ ```
107
+
108
+ ### Defaults (headers / timeout / retries)
109
+
110
+ ```ts
111
+ import {provideBridge} from '@obsidiane/auth-client-js';
112
+
113
+ provideBridge({
114
+ baseUrl: 'https://api.example.com',
115
+ defaults: {
116
+ headers: {'X-Requested-With': 'fetch'},
117
+ timeoutMs: 15_000,
118
+ retries: {count: 2, delayMs: 250, methods: ['GET']},
119
+ },
120
+ });
121
+ ```
122
+
123
+ ### Debug
124
+
125
+ ```ts
126
+ import {provideBridge} from '@obsidiane/auth-client-js';
127
+
128
+ provideBridge({baseUrl: 'https://api.example.com', debug: true});
129
+ ```
130
+
131
+ ## Appeler l’API
132
+
133
+ ### Style orienté ressource (`FacadeFactory` + `ResourceFacade<T>`)
134
+
135
+ ```ts
136
+ import {inject} from '@angular/core';
137
+ import {FacadeFactory, ResourceFacade, Item} from '@obsidiane/auth-client-js';
138
+
139
+ type Book = Item & {title?: string};
140
+
141
+ export class BooksService {
43
142
  private readonly factory = inject(FacadeFactory);
44
- private readonly users = this.factory.create<UserUserRead>({
45
- url: '/api/users',
46
- });
143
+ readonly books: ResourceFacade<Book> = this.factory.create<Book>({url: '/api/books'});
144
+ }
145
+ ```
47
146
 
48
- getUsers() {
49
- return this.users.getCollection$();
50
- }
147
+ Exemples :
148
+
149
+ ```ts
150
+ // collection Hydra
151
+ this.books.getCollection$({page: 1, itemsPerPage: 20, filters: {title: 'Dune'}});
152
+
153
+ // item (souvent via @id)
154
+ this.books.get$(book['@id']!);
51
155
 
52
- getUser(id: string) {
53
- return this.users.get$(id);
156
+ // write
157
+ this.books.post$({title: 'Neuromancer'});
158
+ this.books.patch$(book['@id']!, {title: 'Count Zero'});
159
+ this.books.delete$(book['@id']!);
160
+ ```
161
+
162
+ ### Style ad-hoc (`BridgeFacade`)
163
+
164
+ ```ts
165
+ import {inject} from '@angular/core';
166
+ import {BridgeFacade} from '@obsidiane/auth-client-js';
167
+
168
+ export class HealthService {
169
+ private readonly bridge = inject(BridgeFacade);
170
+ getHealth$() {
171
+ return this.bridge.get$<{status: string}>('/health');
54
172
  }
55
173
  }
56
174
  ```
57
175
 
58
- ## API Reference
176
+ ## Realtime (Mercure/SSE)
59
177
 
60
- ### Bridge Exports (from Meridiane)
178
+ Le realtime est inactif tant que `mercure.hubUrl` n’est pas fourni à `provideBridge()`.
61
179
 
62
- The bridge provides:
180
+ ```ts
181
+ provideBridge({
182
+ baseUrl: 'https://api.example.com',
183
+ mercure: {hubUrl: 'https://api.example.com/.well-known/mercure', topicMode: 'url'},
184
+ });
185
+ ```
63
186
 
64
- #### **FacadeFactory**
65
- Create resource facades for CRUD operations:
187
+ ### Concurrence
66
188
 
67
- ```typescript
68
- const facade = factory.create<MyResource>({
69
- url: '/api/resources',
70
- });
189
+ Le bridge maintient une seule connexion SSE (une seule `EventSource`) et mutualise les topics :
190
+ - regarder plusieurs fois la même ressource ne crée pas plusieurs connexions ;
191
+ - un même topic est dédoublonné et géré par ref-count (unsubscribe effectif quand plus personne n’écoute).
192
+
193
+ Côté HTTP, le bridge déduplique les requêtes identiques tant qu’elles sont en cours (single-flight, activé par défaut) :
194
+ - `GET` / `HEAD` / `OPTIONS` : deux appels identiques partagent le même appel réseau et reçoivent la même réponse ;
195
+ - les méthodes avec body (`POST`/`PUT`/`PATCH`/`DELETE`) ne sont pas dédupliquées.
196
+
197
+ Ce n’est pas un cache : une fois la requête terminée, un nouvel appel identique relance un nouvel appel réseau.
71
198
 
72
- // Collection operations
73
- facade.getCollection$({ page: 1, itemsPerPage: 20 })
199
+ Pour désactiver :
74
200
 
75
- // Item operations
76
- facade.get$(id)
77
- facade.post$(item)
78
- facade.patch$(id, partialItem)
79
- facade.delete$(id)
201
+ ```ts
202
+ provideBridge({baseUrl: 'https://api.example.com', singleFlight: false});
80
203
  ```
81
204
 
82
- #### **BridgeFacade**
83
- Low-level HTTP client for custom endpoints:
205
+ API :
206
+ - `ResourceFacade<T>.watch$(iri | iri[])` / `unwatch(iri | iri[])`
207
+ - `ResourceFacade<T>.watchSubResource$(iri | iri[], 'field.path')`
208
+ - `BridgeFacade.watch$(iri | iri[])` / `unwatch(iri | iri[])`
84
209
 
85
- ```typescript
86
- const bridge = inject(BridgeFacade);
210
+ Note SSR : la connexion SSE ne s’ouvre que dans le navigateur.
87
211
 
88
- // Custom GET
89
- bridge.get$<MyResponse>('/api/custom-endpoint')
212
+ ## Models TypeScript
90
213
 
91
- // Custom POST
92
- bridge.post$<MyResponse>('/api/custom-endpoint', { payload })
214
+ Les models générés (si présents) sont exportés au même niveau que le runtime :
215
+
216
+ ```ts
217
+ import type {Item} from '@obsidiane/auth-client-js';
218
+ // import type {Book} from '@obsidiane/auth-client-js';
93
219
  ```
94
220
 
95
- #### **Models**
96
- TypeScript types for all API resources:
221
+ En JSON-LD (`application/ld+json`), l’IRI est typiquement dans `model['@id']`.
97
222
 
98
- ```typescript
99
- import type {
100
- UserUserRead,
101
- Auth,
102
- InviteUserInviteRead,
103
- // ... all other models
104
- } from '@obsidiane/auth-client-js';
105
- ```
223
+ ---
224
+
225
+ ## Documentation du projet
226
+
227
+ # Obsidiane Auth
228
+
229
+ Service d’authentification **stateless** pour applications web & SPA, basé sur **Lexik JWT (HS256)** et **Gesdinet
230
+ Refresh Tokens**.
231
+ Il fournit un login centré **cookies HttpOnly** (`__Secure-at` / `__Host-rt` en prod, `at` / `rt` en dev compose), des endpoints API simples et une
232
+ validation **Origin/Referer** (Same Origin).
233
+
234
+ > UI Angular (dossier `/webfront`), tokens en cookies sécurisés, refresh rotatif, vérification d’email.
235
+
236
+ ---
237
+
238
+ ## Sommaire
239
+
240
+ - [Vue d’ensemble](#vue-densemble)
241
+ - [Fonctionnalités](#fonctionnalités)
242
+ - [Architecture](#architecture)
243
+ - [Démarrage rapide](#démarrage-rapide)
244
+ - [API & flux principaux](#api--flux-principaux)
245
+ - [Intégration front (SPA)](#intégration-front-spa)
246
+ - [Configuration & déploiement](#configuration--déploiement)
247
+ - [SDKs](#sdks)
248
+ - [Bridge Meridiane](#bridge-meridiane)
249
+ - [Notes de sécurité](#notes-de-sécurité)
250
+ - [Contribuer](#contribuer)
251
+ - [Licence](#licence)
252
+
253
+ ---
254
+
255
+ ## Vue d’ensemble
256
+
257
+ - Service d’auth centré **cookies HttpOnly** : access token JWT (`__Secure-at`) + refresh opaque (`__Host-rt`) en prod.
258
+ - Deux usages possibles :
259
+ - UI Angular : `/login`, `/register`, `/reset-password`, `/reset-password/confirm`, `/verify-email`, `/invite/complete`, `/setup`.
260
+ - API JSON : `/api/auth/...` pour front SPA, mobile, backends.
261
+ - Sécurité intégrée : validation Origin/Referer, vérification d’email, rate limiting, redirections allowlistées.
262
+ - Au premier démarrage, si aucun user n’existe, tout redirige vers `/setup` pour créer l’admin.
263
+ - Interface et emails uniquement **en français**.
264
+
265
+ ---
266
+
267
+ ## Fonctionnalités
268
+
269
+ - **UI Angular**
270
+ - `/login`, `/register`, `/reset-password`, `/reset-password/confirm`.
271
+ - `/setup` pour créer l’admin initial.
272
+ - `/invite/complete` pour finaliser une invitation.
273
+ - `/verify-email` pour la vérification d’email (appelle l’API).
274
+
275
+ - **API JSON principale**
276
+ - Auth : `POST /api/auth/login`, `GET /api/auth/me`, `POST /api/auth/refresh`, `POST /api/auth/logout`.
277
+ - Inscription & mot de passe : `POST /api/auth/register`, `/api/auth/password/forgot`, `/api/auth/password/reset`.
278
+ - Invitation : `POST /api/auth/invite`, `POST /api/auth/invite/complete`.
279
+ - Setup : `POST /api/setup/admin`.
280
+
281
+ - **Cookies & tokens**
282
+ - `__Secure-at` : access token JWT (HttpOnly, prod).
283
+ - `__Host-rt` : refresh token opaque, single-use (HttpOnly, prod).
284
+ - En dev local via `docker compose` : `at` / `rt` (HTTP).
285
+ - Validation Origin/Referer (Same Origin) sur les requêtes sensibles.
286
+
287
+ ---
288
+
289
+ ## Architecture
290
+
291
+ - **Access token (JWT)**
292
+ - Claims standard (`iss`, `aud`, `sub`, `iat`, `nbf`, `exp`, `jti`).
293
+ - Stocké en cookie HttpOnly `__Secure-at`.
294
+
295
+ - **Refresh token (opaque)**
296
+ - Stocké en base + cookie HttpOnly `__Host-rt` (host-only, single-use).
297
+
298
+ - **Origin/Referer**
299
+ - Validation Same Origin sur les requêtes sensibles.
300
+
301
+ - **Vérification d’email**
302
+ - L’inscription envoie un lien vers `/verify-email?...` (front), qui appelle `/api/auth/verify-email`.
303
+ - Tant que l’email n’est pas confirmé, le login est refusé (`EMAIL_NOT_VERIFIED`).
304
+
305
+ ---
306
+
307
+ ## Démarrage rapide
308
+
309
+ Par défaut, `docker compose` expose une entrée unique sur `http://localhost:8000` (Caddy dans le core).
310
+ `/api` est routé vers Symfony, le reste vers le webfront (Angular via `ng serve`).
311
+ Le routing Caddy est dans `@obsidiane/caddy/Caddyfile` et le bloc frontend est injecté via `webfront.caddy`
312
+ (`@obsidiane/caddy/webfront.dev.caddy` en dev, `@obsidiane/caddy/webfront.prod.caddy` en prod).
313
+
314
+ La documentation OpenAPI générée par API Platform est disponible sur `http://localhost:8000/api/docs`.
315
+
316
+ ### Installation
317
+
318
+ ```bash
319
+ # Dépendances PHP (dans le dossier core/)
320
+ cd core && composer install && cd ..
321
+
322
+ # Démarrer le core
323
+ docker compose up -d
324
+
325
+ # Installer les dépendances du webfront (premier lancement)
326
+ docker compose run --rm webfront npm install
327
+
328
+ # Lancer le webfront (si besoin)
329
+ docker compose up -d webfront
106
330
 
107
- ## How It Works
331
+ # Migrations
332
+ docker compose exec core php bin/console doctrine:migrations:migrate
333
+ ```
108
334
 
109
- ### Bridge Layer (Meridiane)
335
+ ### Commandes Makefile (à la racine)
110
336
 
111
- The bridge is auto-generated from the OpenAPI spec (`/api/docs.json`) using Meridiane. It includes:
337
+ Le projet fournit un Makefile avec des commandes pratiques pour le développement :
112
338
 
113
- - **HTTP Client**: Optimized fetch wrapper with automatic retries and deduplication
114
- - **Facades**: Resource-based API for common CRUD operations
115
- - **Models**: TypeScript interfaces for all API request/response types
116
- - **Interceptors**: Built-in handling for headers, errors, and single-flight requests
339
+ #### Génération du Bridge
117
340
 
118
- ## Development
341
+ ```bash
342
+ # Générer le bridge Angular depuis l'OpenAPI spec
343
+ make bridge
119
344
 
120
- ### Regenerate from OpenAPI
345
+ # Nettoyer les fichiers générés
346
+ make bridge-clean
347
+ ```
121
348
 
122
- The bridge is regenerated from the backend OpenAPI spec:
349
+ #### SDK npm (Angular package)
123
350
 
124
351
  ```bash
125
- # From project root
352
+ # Générer le package Angular complet (quand l'API change)
126
353
  make sdk-npm
127
354
 
128
- # Or manually
129
- npx meridiane generate "@obsidiane/auth-client-js" \
130
- --spec http://localhost:9000/api/docs.json \
131
- --formats "application/ld+json"
355
+ # Nettoyer les fichiers générés
356
+ make sdk-npm-clean
132
357
  ```
133
358
 
134
- ### Build
359
+ **Workflow de publication:**
360
+ 1. Quand l'API change: `make sdk-npm` pour régénérer le package complet
361
+ 2. Committez le package: `git add packages/auth-client-js/ && git commit -m "chore: regenerate SDK"`
362
+ 3. Push sur master: Le CI publie automatiquement le package committé sur npmjs.com
363
+
364
+ Le package est construit avec **meridiane build** (ng-packagr) et **entièrement committé** dans le repo, prêt à publier.
365
+
366
+ #### Build & Tests
135
367
 
136
368
  ```bash
137
- npm run build
138
- # Output: ./dist/
369
+ # Nettoyer le dossier dist
370
+ make clean
371
+
372
+ # Linter le code (webfront uniquement)
373
+ make lint
374
+
375
+ # Build development
376
+ make build
377
+
378
+ # Build production
379
+ make build-prod
380
+
381
+ # Checks rapides (lint + build dev)
382
+ make check
383
+
384
+ # Tests complets production (lint + build prod + PHPStan)
385
+ make check-prod
386
+ # ou
387
+ make test
139
388
  ```
140
389
 
141
- ### Publish
390
+ **Avant de push :** Lancez `make test` pour vérifier que tout passe (lint, build production, PHPStan).`
391
+
392
+ ### URLs utiles (dev)
393
+
394
+ * UI Angular :
395
+
396
+ * `http://localhost:8000/login`
397
+ * `http://localhost:8000/register`
398
+ * `http://localhost:8000/reset-password`
399
+ * `http://localhost:8000/reset-password/confirm?token=...`
400
+ * `http://localhost:8000/verify-email?...`
401
+ * `http://localhost:8000/invite/complete?token=...`
402
+ * `http://localhost:8000/setup` (tant que la base ne contient aucun user).
403
+ * API :
404
+
405
+ * `http://localhost:8000/api/auth/login`
406
+ * `http://localhost:8000/api/auth/me`
407
+ * `http://localhost:8000/api/auth/refresh`
408
+
409
+ ### Exemple minimal avec `curl`
142
410
 
143
411
  ```bash
144
- npm publish
412
+ # Login
413
+ curl -i \
414
+ -c cookiejar.txt \
415
+ -H 'Content-Type: application/json' \
416
+ -H "Origin: http://localhost:8000" \
417
+ -d '{"email":"userexample.com","password":"Secret123!"}' \
418
+ http://localhost:8000/api/auth/login
419
+
420
+ # Profil courant
421
+ curl -i -b cookiejar.txt http://localhost:8000/api/auth/me
422
+
423
+ # Refresh
424
+ curl -i -b cookiejar.txt -H "Origin: http://localhost:8000" -X POST http://localhost:8000/api/auth/refresh
145
425
  ```
146
426
 
147
- ## Migration from Old SDK
427
+ ---
428
+
429
+ ## API & flux principaux
430
+
431
+ ### Vue d’ensemble
432
+
433
+ | Méthode | Route | Description |
434
+ |--------:|-----------------------------|-------------------------------------------|
435
+ | POST | `/api/setup/admin` | Créer l’admin initial |
436
+ | POST | `/api/auth/login` | Login (cookies access + refresh) |
437
+ | GET | `/api/auth/me` | Utilisateur courant |
438
+ | POST | `/api/auth/refresh` | Refresh JWT via cookie `__Host-rt` |
439
+ | POST | `/api/auth/register` | Inscription |
440
+ | POST | `/api/auth/logout` | Logout + invalidation tokens |
441
+ | POST | `/api/auth/password/forgot` | Demande de reset (email) |
442
+ | POST | `/api/auth/password/reset` | Réinitialisation via token |
443
+ | GET | `/api/auth/verify-email` | Validation d’email via lien signé |
444
+ | POST | `/api/auth/invite` | Inviter un utilisateur (admin) |
445
+ | POST | `/api/auth/invite/complete` | Compléter une invitation |
446
+
447
+ Les payloads détaillés, codes de réponse et schémas sont disponibles dans `http://<APP_BASE_URL>/api/docs` (OpenAPI).
448
+
449
+ ---
450
+
451
+ ## Origin/Referer
452
+
453
+ Tous les endpoints sensibles (login, register, reset, logout, setup, invitation) valident l’**Origin/Referer** :
148
454
 
149
- The old SDK (`packages/auth-client-js.backup/`) had 758 lines of custom HTTP client code. This new version:
455
+ - Le backend vérifie le Same Origin via les en-têtes `Origin` ou `Referer`.
456
+ - Cela implique d’utiliser un reverse-proxy pour servir `/api` et le front sous le même domaine.
150
457
 
151
- - **Removes**: Custom HTTP client, request builders, response decoders
152
- - **Adds**: Meridiane bridge for consistency with the main app
458
+ ---
153
459
 
154
- The API remains similar but cleaner:
460
+ ## Intégration front (SPA)
155
461
 
156
- ```typescript
157
- // Old way (custom client)
158
- import { AuthClient } from '@obsidiane/auth-client-js';
159
- const client = new AuthClient({ baseUrl: 'http://localhost:9000' });
160
- client.auth.login(email, password);
462
+ ### Cookies
161
463
 
162
- // New way (Meridiane bridge)
163
- import { BridgeFacade } from '@obsidiane/auth-client-js';
164
- const bridge = inject(BridgeFacade);
165
- bridge.post$('/api/auth/login', { email, password });
464
+ * Toujours activer `credentials: 'include'` côté client (`fetch`, Axios, Angular `HttpClient`, …).
465
+ * Côté navigateur, **aucun stockage manuel de token** :
466
+ * pas de `localStorage` / `sessionStorage` ;
467
+ * le serveur lit directement le cookie `__Secure-at`.
468
+
469
+ ### Refresh silencieux
470
+
471
+ * Appeler régulièrement `POST /api/auth/refresh` (avec `credentials: 'include'`) avant l’expiration (`exp`).
472
+ * Aucun token supplémentaire n’est requis sur ce endpoint (Origin/Referer uniquement).
473
+
474
+ ---
475
+
476
+ ## Configuration & déploiement
477
+
478
+ ### `.env` & Docker
479
+
480
+ * `core/.env` fournit des valeurs par défaut **orientées production** (cookies sécurisés). Ne mets aucun secret sensible dans ce fichier versionné.
481
+ * Le `.env` racine sert à la substitution de variables pour `docker compose` (ex: `NOTIFUSE_*`).
482
+ * En local, les valeurs **dev** sont définies directement dans `compose.yaml` (APP_ENV=dev, cookies non Secure, CORS localhost, DB `database:3306`).
483
+ * `docker compose` lit automatiquement `.env` ; toute variable peut être surchargée par l’environnement du runtime/compose.
484
+ * L’entrypoint génère `APP_SECRET` et `JWT_SECRET` si absents, mais ces valeurs tournent à chaque redémarrage : renseigne-les pour un déploiement réel.
485
+
486
+ ### Compose prod local
487
+
488
+ Pour un run prod-like local (front statique inclus dans l’image) :
489
+
490
+ ```bash
491
+ docker compose -f compose.prod.yaml up -d --build
166
492
  ```
167
493
 
168
- ## Architecture
494
+ ### Variables d’environnement importantes
495
+
496
+ Les variables ci-dessous couvrent 95 % des cas. Copie/colle ce bloc puis adapte-le à ton infra.
497
+
498
+ Variables **critiques** vérifiées au démarrage (entrypoint) : `APP_BASE_URL`, `FRONTEND_REDIRECT_URL`, `APP_SECRET`, `DATABASE_URL`, `JWT_SECRET`, `NOTIFUSE_API_BASE_URL`, `NOTIFUSE_WORKSPACE_ID`, `NOTIFUSE_API_KEY`.
499
+
500
+ **Bloc prêt à copier-coller (prod typique)**
501
+
502
+ ```env
503
+ APP_BASE_URL=https://auth.example.com
504
+ FRONTEND_REDIRECT_URL=https://app.example.com/
505
+
506
+ APP_SECRET=change-me
507
+ JWT_SECRET=change-me-too
508
+ DATABASE_URL="mysql://app:!ChangeMe!database:3306/app?serverVersion=10.11.2-MariaDB&charset=utf8mb4"
509
+
510
+ JWT_ISSUER=${APP_BASE_URL}
511
+ JWT_AUDIENCE=core-api
512
+ JWT_ACCESS_TTL=600
513
+ JWT_REFRESH_TTL=2592000
514
+ ALLOWED_ORIGINS="^https?://example.com(:[0-9]+)?$"
515
+
516
+ ACCESS_COOKIE_DOMAIN=".example.com"
517
+
518
+ REGISTRATION_ENABLED=1
519
+ PASSWORD_STRENGTH_LEVEL=2
520
+ API_DOCS_ENABLED=0
521
+
522
+ # Token S2S (Authorization: Bearer ...) pour les services internes
523
+ SERVICE_AUTH_TOKEN=change-me
524
+
525
+ NOTIFUSE_API_BASE_URL=https://notifuse.example.com
526
+ NOTIFUSE_WORKSPACE_ID=prod-workspace
527
+ NOTIFUSE_API_KEY=change-me
528
+ NOTIFUSE_TEMPLATE_WELCOME=welcome
529
+ NOTIFUSE_TEMPLATE_RESET_PASSWORD=resetpass
530
+ ```
531
+
532
+ Variables complémentaires (généralement à garder telles quelles) :
533
+
534
+ - `JWT_ALGORITHM` (HS256), `JWT_AUDIENCE`, `JWT_ACCESS_TTL`, `JWT_REFRESH_TTL`
535
+ - `ACCESS_COOKIE_NAME`, `ACCESS_COOKIE_PATH`, `ACCESS_COOKIE_SAMESITE`, `ACCESS_COOKIE_SECURE`
536
+ - `BRANDING_NAME`, `API_DOCS_ENABLED`
537
+ - Rate limiting : `RATE_LOGIN_LIMIT`, `RATE_LOGIN_INTERVAL`, `RATE_LOGIN_GLOBAL_LIMIT`
538
+
539
+ ### Valeurs par défaut des variables d’environnement
540
+
541
+ #### Core (defaults de `core/.env`)
542
+
543
+ | Variable | Valeur par défaut |
544
+ | --- | --- |
545
+ | `APP_ENV` | `prod` |
546
+ | `APP_DEBUG` | `0` |
547
+ | `APP_SECRET` | `` |
548
+ | `APP_BASE_URL` | `` |
549
+ | `DATABASE_URL` | `` |
550
+ | `ALLOWED_ORIGINS` | `^https?://example.com(:[0-9]+)?$` |
551
+ | `JWT_ALGORITHM` | `HS256` |
552
+ | `JWT_SECRET` | `` |
553
+ | `JWT_ISSUER` | `${APP_BASE_URL}` |
554
+ | `JWT_AUDIENCE` | `core-api` |
555
+ | `JWT_ACCESS_TTL` | `600` |
556
+ | `JWT_REFRESH_TTL` | `2592000` |
557
+ | `ACCESS_COOKIE_NAME` | `__Secure-at` |
558
+ | `ACCESS_COOKIE_DOMAIN` | `` |
559
+ | `ACCESS_COOKIE_PATH` | `/` |
560
+ | `ACCESS_COOKIE_SAMESITE` | `lax` |
561
+ | `ACCESS_COOKIE_SECURE` | `1` |
562
+ | `REFRESH_COOKIE_NAME` | `__Host-rt` |
563
+ | `REFRESH_COOKIE_SECURE` | `1` |
564
+ | `FRONTEND_REDIRECT_URL` | `` |
565
+ | `TRUSTED_PROXIES` | `127.0.0.1` |
566
+ | `REGISTRATION_ENABLED` | `1` |
567
+ | `BRANDING_NAME` | `Obsidiane Auth` |
568
+ | `PASSWORD_STRENGTH_LEVEL` | `2` |
569
+ | `FRONTEND_THEME_MODE` | `dark` |
570
+ | `FRONTEND_THEME_COLOR` | `base` |
571
+ | `FRONTEND_THEME_COLORS` | `base,red,blue,orange,yellow,green,violet` |
572
+ | `RATE_LOGIN_LIMIT` | `5` |
573
+ | `RATE_LOGIN_INTERVAL` | `60 seconds` |
574
+ | `RATE_LOGIN_GLOBAL_LIMIT` | `25` |
575
+ | `API_DOCS_ENABLED` | `0` |
576
+ | `NOTIFUSE_API_BASE_URL` | `` |
577
+ | `NOTIFUSE_WORKSPACE_ID` | `` |
578
+ | `NOTIFUSE_API_KEY` | `` |
579
+ | `NOTIFUSE_TEMPLATE_WELCOME` | `welcome` |
580
+ | `NOTIFUSE_TEMPLATE_RESET_PASSWORD` | `resetpass` |
581
+
582
+ #### Racine (defaults de `.env` pour `docker compose`)
583
+
584
+ | Variable | Valeur par défaut |
585
+ | --- | -- |
586
+ | `NOTIFUSE_API_BASE_URL` | `https://relay.obsidiane.fr` |
587
+ | `NOTIFUSE_API_KEY` | `` |
588
+
589
+ #### Dev local (`compose.yaml` -> service `core`)
590
+
591
+ | Variable | Valeur par défaut |
592
+ | --- | --- |
593
+ | `APP_BASE_URL` | `http://localhost:${CADDY_HTTP_PORT:-8000}` |
594
+ | `APP_SECRET` | `secret` |
595
+ | `APP_ENV` | `dev` |
596
+ | `ACCESS_COOKIE_NAME` | `at` |
597
+ | `ACCESS_COOKIE_DOMAIN` | `localhost` |
598
+ | `ACCESS_COOKIE_SECURE` | `0` |
599
+ | `REFRESH_COOKIE_NAME` | `rt` |
600
+ | `REFRESH_COOKIE_SECURE` | `0` |
601
+ | `XDEBUG_MODE` | `off` |
602
+ | `ALLOWED_ORIGINS` | `^https?://(localhost|127\.0\.0\.1)(:[0-9]+)?$` |
603
+ | `DATABASE_URL` | `mysql://app:ChangeMe@database:3306/app` |
604
+ | `JWT_SECRET` | `!ChangeThisMercureHubJWTSecretKey!` |
605
+ | `FRONTEND_REDIRECT_URL` | `http://localhost:4200/` |
606
+ | `NOTIFUSE_API_BASE_URL` | `${NOTIFUSE_API_BASE_URL:-}` |
607
+ | `NOTIFUSE_WORKSPACE_ID` | `obsidiane` |
608
+ | `NOTIFUSE_API_KEY` | `${NOTIFUSE_API_KEY:-}` |
609
+ | `BRANDING_NAME` | `Obsidiane` |
610
+ | `NOTIFUSE_TEMPLATE_WELCOME` | `welcome` |
611
+ | `NOTIFUSE_TEMPLATE_RESET_PASSWORD` | `resetpass` |
612
+ | `API_DOCS_ENABLED` | `1` |
613
+ | `REGISTRATION_ENABLED` | `1` |
614
+
615
+ #### Dev local (`compose.yaml` -> service `database`)
616
+
617
+ | Variable | Valeur par défaut |
618
+ | --- | --- |
619
+ | `MYSQL_DATABASE` | `${MYSQL_DATABASE:-app}` |
620
+ | `MYSQL_USER` | `${MYSQL_USER:-app}` |
621
+ | `MYSQL_PASSWORD` | `${MYSQL_PASSWORD:-ChangeMe}` |
622
+ | `MYSQL_RANDOM_ROOT_PASSWORD` | `true` |
623
+
624
+ #### Tests/optionnels (non définis par défaut)
625
+
626
+ | Variable | Valeur par défaut |
627
+ | --- | --- |
628
+ | `SERVICE_AUTH_TOKEN` | `` |
629
+ | `SERVICE_AUTH_TOKEN_NEXT` | `` |
630
+ | `TEST_TOKEN` | `` |
631
+
632
+ ---
633
+
634
+ ### Configuration frontend (`/api/config`)
635
+
636
+ Le frontend consomme `/api/config` (public) pour piloter l’UI et la politique de mots de passe.
637
+
638
+ | Variable | Champ `/api/config` | Effet côté UI |
639
+ | --- | --- | --- |
640
+ | `APP_ENV` | `environment` | `dev` affiche le ThemeSwitcher + persistance locale; tout autre env masque le switcher et force le thème fourni. |
641
+ | `REGISTRATION_ENABLED` | `registrationEnabled` | Active l’inscription (route `/register`). |
642
+ | `PASSWORD_STRENGTH_LEVEL` | `passwordStrengthLevel` | Niveau 1–4 (weak → very strong) pour validation + jauge de force. |
643
+ | `BRANDING_NAME` | `brandingName` | Nom affiché dans l’UI et utilisé dans les emails. |
644
+ | `FRONTEND_REDIRECT_URL` | `frontendRedirectUrl` | Redirection après login si fournie. |
645
+ | `FRONTEND_THEME_MODE` | `themeMode` | `light` ou `dark` (appliqué en non‑dev). |
646
+ | `FRONTEND_THEME_COLOR` | `themeColor` | Couleur principale (`base`, `red`, `blue`, `orange`, `yellow`, `green`, `violet`, `cyan`, `rose`). |
647
+
648
+ En environnement non‑dev, le thème est **forcé** par `/api/config` (pas de lecture `localStorage`).
649
+
650
+ ---
651
+
652
+ ## Architecture du code
653
+
654
+ ### Structure du projet
655
+
656
+ ```
657
+ obsidiane-auth/
658
+ ├── core/ # Backend Symfony (API Platform)
659
+ │ ├── src/
660
+ │ │ ├── Auth/ # Use cases d'authentification
661
+ │ │ ├── Controller/ # Controllers API
662
+ │ │ ├── Entity/ # Entités Doctrine
663
+ │ │ ├── Dto/ # DTOs pour validation
664
+ │ │ ├── Repository/ # Repositories Doctrine
665
+ │ │ ├── Security/ # Voters, EmailVerifier, JWT
666
+ │ │ ├── EventSubscriber/ # Event subscribers
667
+ │ │ └── ...
668
+ │ ├── config/ # Configuration Symfony
669
+ │ ├── migrations/ # Migrations Doctrine
670
+ │ └── phpstan.neon.dist # Configuration PHPStan
671
+
672
+ ├── webfront/ # Frontend Angular
673
+ │ ├── src/app/
674
+ │ │ ├── core/ # Services, repositories, guards
675
+ │ │ ├── modules/ # Modules fonctionnels (auth, error)
676
+ │ │ ├── shared/ # Composants partagés
677
+ │ │ └── ...
678
+ │ ├── bridge/ # Bridge généré par Meridiane
679
+ │ ├── .eslintrc.json # Configuration ESLint
680
+ │ └── angular.json # Configuration Angular
681
+
682
+ ├── packages/ # SDKs clients
683
+ │ ├── auth-client-php/ # SDK PHP (Symfony)
684
+ │ └── auth-client-js/ # SDK JavaScript/TypeScript
685
+
686
+ ├── @obsidiane/ # Configuration partagée
687
+ │ ├── caddy/ # Configuration Caddy
688
+ │ └── docs/ # Documentation technique
689
+
690
+ ├── Makefile # Commandes de développement
691
+ ├── compose.yaml # Docker Compose (dev)
692
+ └── compose.prod.yaml # Docker Compose (prod)
693
+ ```
694
+
695
+ ### Standards de code
696
+
697
+ #### Backend (PHP/Symfony)
698
+
699
+ - **PSR-12** : Standard de code PHP
700
+ - **PHPStan Level 6** : Analyse statique stricte
701
+ - **Type hints stricts** : `declare(strict_types=1)` dans tous les fichiers
702
+ - **Injection de dépendances** : Constructor injection via autowiring
703
+ - **DTOs** : Validation avec Symfony Validator
704
+ - **Readonly classes** : Favoriser l'immutabilité (PHP 8.2+)
705
+
706
+ Tous les fichiers PHP doivent passer PHPStan sans erreur :
707
+ ```bash
708
+ cd core && vendor/bin/phpstan analyse -c phpstan.neon.dist
709
+ ```
710
+
711
+ #### Frontend (Angular/TypeScript)
169
712
 
713
+ - **Angular 19+** : Standalone components, signals, inject()
714
+ - **TypeScript strict mode** : Tous les flags stricts activés
715
+ - **ESLint** : Configuration custom avec règles Angular
716
+ - **Prefer inject()** : Utiliser `inject()` au lieu de constructor injection
717
+ - **Control flow** : Utiliser `@if`/`@for` au lieu de `*ngIf`/`*ngFor`
718
+ - **Signals** : Favoriser les signals pour la réactivité
719
+ - **Standalone** : Tous les composants sont standalone
720
+
721
+ Tous les fichiers doivent passer le linter :
722
+ ```bash
723
+ cd webfront && npm run lint
170
724
  ```
171
- @obsidiane/auth-client-js
172
- ├── Bridge (Meridiane-generated)
173
- │ ├── HTTP client (fetch wrapper)
174
- │ ├── Facades (FacadeFactory, BridgeFacade)
175
- │ ├── Models (TypeScript interfaces)
176
- │ └── Interceptors
725
+
726
+ ### Qualité du code
727
+
728
+ Le projet maintient une qualité de code stricte :
729
+
730
+ - ✅ **85 règles ESLint** appliquées sur le frontend
731
+ - ✅ **Zero erreur PHPStan** sur le backend (Level 6)
732
+ - ✅ **TypeScript strict** avec tous les flags activés
733
+ - ✅ **Tests automatisés** via `make test`
734
+
735
+ ---
736
+
737
+ ## Tests & SDKs
738
+
739
+ ### Tests end-to-end – `tests/e2e.sh`
740
+
741
+ Un script Bash est fourni pour tester rapidement les principaux parcours (setup initial, login/logout, inscription + vérification d’email, reset password, invitation) :
742
+
743
+ ```bash
744
+ ./tests/e2e.sh
177
745
  ```
178
746
 
179
- ## License
747
+ - Le script est interactif : il te demande la base URL, les emails/mots de passe à utiliser pour l’admin, l’utilisateur d’inscription et l’utilisateur invité.
748
+ - À chaque étape nécessitant une action sur l’email (clic sur `/verify-email?...`, `/reset-password/confirm?token=...`, `/invite/complete?...`), il affiche un message du type :
749
+ - `Attente de confirmation d’email… Ouvrez Maildev/Notifuse et cliquez sur le lien`, puis attend `ENTER`.
750
+ - Il envoie les en-têtes `Origin` nécessaires à la validation Same Origin.
751
+
752
+ ### Client JS – `@obsidiane/auth-client-js`
753
+
754
+ * Consomme l’API (login, me, refresh, logout, register, reset password) depuis navigateur ou Node/SSR.
755
+ * Sources & doc : `packages/auth-client-js`.
756
+
757
+ ### Bundle PHP – `obsidiane/auth-sdk`
758
+
759
+ * Client HTTP Symfony pour ce service d’authentification.
760
+ * Sources & doc : `packages/auth-client-php`.
761
+
762
+ ---
763
+
764
+ ## Bridge Meridiane
765
+
766
+ Un bridge Angular peut être généré depuis la spec OpenAPI (API Platform) via le Makefile racine :
767
+
768
+ ```bash
769
+ make bridge
770
+ ```
771
+
772
+ La documentation d’usage et les bonnes pratiques frontend sont dans `@obsidiane/docs/meridiane.md`.
773
+
774
+ ---
775
+
776
+ ## Notes de sécurité
777
+
778
+ * Toujours déployer derrière **HTTPS** avec cookies `Secure`.
779
+ * Adapter `SameSite` selon l’architecture (monolithe, sous-domaines, front séparé).
780
+ * Configurer le rate limiting en fonction de votre exposition publique.
781
+ * Ne jamais exposer les tokens dans le JS côté client (pas de `localStorage`).
782
+ * Ajouter des en-têtes de sécurité côté reverse-proxy (CSP, HSTS, `X-Content-Type-Options`, `Referrer-Policy`, etc.).
783
+
784
+ ---
785
+
786
+ ## Contribuer
787
+
788
+ Les contributions sont les bienvenues ❤️
789
+
790
+ 1. Ouvrez une issue pour décrire un bug ou une feature.
791
+ 2. Forkez le dépôt.
792
+ 3. Ouvrez une PR avec :
793
+
794
+ * les tests adaptés,
795
+ * une mise à jour de la doc si nécessaire.
796
+
797
+ ---
798
+
799
+ ## Licence
180
800
 
181
- MIT
801
+ Ce projet est distribué sous les termes de la licence indiquée dans [`LICENSE`](./LICENSE).