@obsidiane/auth-client-js 1.0.4 → 1.0.6
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.
- package/README.md +725 -105
- package/fesm2022/obsidiane-auth-client-js.mjs +961 -0
- package/fesm2022/obsidiane-auth-client-js.mjs.map +1 -0
- package/index.d.ts +361 -0
- package/obsidiane-auth-client-js-0.1.0.tgz +0 -0
- package/package.json +15 -40
- package/dist/index.d.ts +0 -9
- package/dist/index.js +0 -10
- package/dist/index.js.map +0 -1
- package/dist/src/lib/bridge/rest/api-platform.adapter.d.ts +0 -18
- package/dist/src/lib/bridge/rest/api-platform.adapter.js +0 -67
- package/dist/src/lib/bridge/rest/api-platform.adapter.js.map +0 -1
- package/dist/src/lib/bridge/rest/http-request.options.d.ts +0 -4
- package/dist/src/lib/bridge/rest/http-request.options.js +0 -17
- package/dist/src/lib/bridge/rest/http-request.options.js.map +0 -1
- package/dist/src/lib/bridge/rest/query-builder.d.ts +0 -3
- package/dist/src/lib/bridge/rest/query-builder.js +0 -41
- package/dist/src/lib/bridge/rest/query-builder.js.map +0 -1
- package/dist/src/lib/bridge/sse/eventsource-wrapper.d.ts +0 -17
- package/dist/src/lib/bridge/sse/eventsource-wrapper.js +0 -57
- package/dist/src/lib/bridge/sse/eventsource-wrapper.js.map +0 -1
- package/dist/src/lib/bridge/sse/mercure-topic.mapper.d.ts +0 -19
- package/dist/src/lib/bridge/sse/mercure-topic.mapper.js +0 -45
- package/dist/src/lib/bridge/sse/mercure-topic.mapper.js.map +0 -1
- package/dist/src/lib/bridge/sse/mercure-url.builder.d.ts +0 -7
- package/dist/src/lib/bridge/sse/mercure-url.builder.js +0 -18
- package/dist/src/lib/bridge/sse/mercure-url.builder.js.map +0 -1
- package/dist/src/lib/bridge/sse/mercure.adapter.d.ts +0 -37
- package/dist/src/lib/bridge/sse/mercure.adapter.js +0 -242
- package/dist/src/lib/bridge/sse/mercure.adapter.js.map +0 -1
- package/dist/src/lib/bridge/sse/ref-count-topic.registry.d.ts +0 -17
- package/dist/src/lib/bridge/sse/ref-count-topic.registry.js +0 -42
- package/dist/src/lib/bridge/sse/ref-count-topic.registry.js.map +0 -1
- package/dist/src/lib/bridge.types.d.ts +0 -27
- package/dist/src/lib/bridge.types.js +0 -8
- package/dist/src/lib/bridge.types.js.map +0 -1
- package/dist/src/lib/facades/bridge.facade.d.ts +0 -27
- package/dist/src/lib/facades/bridge.facade.js +0 -100
- package/dist/src/lib/facades/bridge.facade.js.map +0 -1
- package/dist/src/lib/facades/facade.factory.d.ts +0 -22
- package/dist/src/lib/facades/facade.factory.js +0 -36
- package/dist/src/lib/facades/facade.factory.js.map +0 -1
- package/dist/src/lib/facades/facade.interface.d.ts +0 -13
- package/dist/src/lib/facades/facade.interface.js +0 -2
- package/dist/src/lib/facades/facade.interface.js.map +0 -1
- package/dist/src/lib/facades/resource.facade.d.ts +0 -30
- package/dist/src/lib/facades/resource.facade.js +0 -61
- package/dist/src/lib/facades/resource.facade.js.map +0 -1
- package/dist/src/lib/interceptors/bridge-debug.interceptor.d.ts +0 -6
- package/dist/src/lib/interceptors/bridge-debug.interceptor.js +0 -27
- package/dist/src/lib/interceptors/bridge-debug.interceptor.js.map +0 -1
- package/dist/src/lib/interceptors/bridge-defaults.interceptor.d.ts +0 -5
- package/dist/src/lib/interceptors/bridge-defaults.interceptor.js +0 -43
- package/dist/src/lib/interceptors/bridge-defaults.interceptor.js.map +0 -1
- package/dist/src/lib/interceptors/content-type.interceptor.d.ts +0 -9
- package/dist/src/lib/interceptors/content-type.interceptor.js +0 -34
- package/dist/src/lib/interceptors/content-type.interceptor.js.map +0 -1
- package/dist/src/lib/interceptors/singleflight.interceptor.d.ts +0 -3
- package/dist/src/lib/interceptors/singleflight.interceptor.js +0 -42
- package/dist/src/lib/interceptors/singleflight.interceptor.js.map +0 -1
- package/dist/src/lib/ports/realtime.port.d.ts +0 -28
- package/dist/src/lib/ports/realtime.port.js +0 -2
- package/dist/src/lib/ports/realtime.port.js.map +0 -1
- package/dist/src/lib/ports/resource-repository.port.d.ts +0 -64
- package/dist/src/lib/ports/resource-repository.port.js +0 -2
- package/dist/src/lib/ports/resource-repository.port.js.map +0 -1
- package/dist/src/lib/provide-bridge.d.ts +0 -40
- package/dist/src/lib/provide-bridge.js +0 -81
- package/dist/src/lib/provide-bridge.js.map +0 -1
- package/dist/src/lib/tokens.d.ts +0 -14
- package/dist/src/lib/tokens.js +0 -14
- package/dist/src/lib/tokens.js.map +0 -1
- package/dist/src/lib/utils/url.d.ts +0 -6
- package/dist/src/lib/utils/url.js +0 -17
- package/dist/src/lib/utils/url.js.map +0 -1
- package/dist/src/models/Auth.d.ts +0 -4
- package/dist/src/models/Auth.js +0 -2
- package/dist/src/models/Auth.js.map +0 -1
- package/dist/src/models/AuthInviteCompleteInputInviteComplete.d.ts +0 -6
- package/dist/src/models/AuthInviteCompleteInputInviteComplete.js +0 -2
- package/dist/src/models/AuthInviteCompleteInputInviteComplete.js.map +0 -1
- package/dist/src/models/AuthInviteUserInputInviteSend.d.ts +0 -4
- package/dist/src/models/AuthInviteUserInputInviteSend.js +0 -2
- package/dist/src/models/AuthInviteUserInputInviteSend.js.map +0 -1
- package/dist/src/models/AuthLdJson.d.ts +0 -4
- package/dist/src/models/AuthLdJson.js +0 -2
- package/dist/src/models/AuthLdJson.js.map +0 -1
- package/dist/src/models/AuthPasswordForgotInputPasswordForgot.d.ts +0 -4
- package/dist/src/models/AuthPasswordForgotInputPasswordForgot.js +0 -2
- package/dist/src/models/AuthPasswordForgotInputPasswordForgot.js.map +0 -1
- package/dist/src/models/AuthPasswordResetInputPasswordReset.d.ts +0 -5
- package/dist/src/models/AuthPasswordResetInputPasswordReset.js +0 -2
- package/dist/src/models/AuthPasswordResetInputPasswordReset.js.map +0 -1
- package/dist/src/models/AuthRegisterUserInputUserRegister.d.ts +0 -5
- package/dist/src/models/AuthRegisterUserInputUserRegister.js +0 -2
- package/dist/src/models/AuthRegisterUserInputUserRegister.js.map +0 -1
- package/dist/src/models/FrontendConfig.d.ts +0 -11
- package/dist/src/models/FrontendConfig.js +0 -2
- package/dist/src/models/FrontendConfig.js.map +0 -1
- package/dist/src/models/InvitePreview.d.ts +0 -7
- package/dist/src/models/InvitePreview.js +0 -2
- package/dist/src/models/InvitePreview.js.map +0 -1
- package/dist/src/models/InviteUserInviteRead.d.ts +0 -8
- package/dist/src/models/InviteUserInviteRead.js +0 -2
- package/dist/src/models/InviteUserInviteRead.js.map +0 -1
- package/dist/src/models/Setup.d.ts +0 -4
- package/dist/src/models/Setup.js +0 -2
- package/dist/src/models/Setup.js.map +0 -1
- package/dist/src/models/SetupRegisterUserInputUserRegister.d.ts +0 -5
- package/dist/src/models/SetupRegisterUserInputUserRegister.js +0 -2
- package/dist/src/models/SetupRegisterUserInputUserRegister.js.map +0 -1
- package/dist/src/models/UserUpdateUserRolesInputUserRoles.d.ts +0 -4
- package/dist/src/models/UserUpdateUserRolesInputUserRoles.js +0 -2
- package/dist/src/models/UserUpdateUserRolesInputUserRoles.js.map +0 -1
- package/dist/src/models/UserUserRead.d.ts +0 -8
- package/dist/src/models/UserUserRead.js +0 -2
- package/dist/src/models/UserUserRead.js.map +0 -1
- package/dist/src/models/index.d.ts +0 -14
- package/dist/src/models/index.js +0 -2
- package/dist/src/models/index.js.map +0 -1
- package/dist/src/public-api.d.ts +0 -8
- package/dist/src/public-api.js +0 -9
- 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
|
-
|
|
3
|
+
Bridge Angular (runtime + models TypeScript) pour une API Platform (Hydra/JSON-LD), avec support Mercure/SSE optionnel.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
-
|
|
7
|
-
-
|
|
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
|
|
13
|
+
npm i @obsidiane/auth-client-js
|
|
13
14
|
```
|
|
14
15
|
|
|
15
|
-
|
|
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
|
-
|
|
18
|
+
## Compatibilité
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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: '
|
|
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
|
-
###
|
|
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
|
-
```
|
|
37
|
-
import {
|
|
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
|
-
|
|
42
|
-
|
|
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
|
-
|
|
45
|
-
|
|
46
|
-
|
|
143
|
+
readonly books: ResourceFacade<Book> = this.factory.create<Book>({url: '/api/books'});
|
|
144
|
+
}
|
|
145
|
+
```
|
|
47
146
|
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
|
|
53
|
-
|
|
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
|
-
##
|
|
176
|
+
## Realtime (Mercure/SSE)
|
|
59
177
|
|
|
60
|
-
|
|
178
|
+
Le realtime est inactif tant que `mercure.hubUrl` n’est pas fourni à `provideBridge()`.
|
|
61
179
|
|
|
62
|
-
|
|
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
|
-
|
|
65
|
-
Create resource facades for CRUD operations:
|
|
187
|
+
### Concurrence
|
|
66
188
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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
|
-
|
|
73
|
-
facade.getCollection$({ page: 1, itemsPerPage: 20 })
|
|
199
|
+
Pour désactiver :
|
|
74
200
|
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
|
|
83
|
-
|
|
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
|
-
|
|
86
|
-
const bridge = inject(BridgeFacade);
|
|
210
|
+
Note SSR : la connexion SSE ne s’ouvre que dans le navigateur.
|
|
87
211
|
|
|
88
|
-
|
|
89
|
-
bridge.get$<MyResponse>('/api/custom-endpoint')
|
|
212
|
+
## Models TypeScript
|
|
90
213
|
|
|
91
|
-
|
|
92
|
-
|
|
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
|
-
|
|
96
|
-
TypeScript types for all API resources:
|
|
221
|
+
En JSON-LD (`application/ld+json`), l’IRI est typiquement dans `model['@id']`.
|
|
97
222
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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
|
-
|
|
331
|
+
# Migrations
|
|
332
|
+
docker compose exec core php bin/console doctrine:migrations:migrate
|
|
333
|
+
```
|
|
108
334
|
|
|
109
|
-
###
|
|
335
|
+
### Commandes Makefile (à la racine)
|
|
110
336
|
|
|
111
|
-
|
|
337
|
+
Le projet fournit un Makefile avec des commandes pratiques pour le développement :
|
|
112
338
|
|
|
113
|
-
|
|
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
|
-
|
|
341
|
+
```bash
|
|
342
|
+
# Générer le bridge Angular depuis l'OpenAPI spec
|
|
343
|
+
make bridge
|
|
119
344
|
|
|
120
|
-
|
|
345
|
+
# Nettoyer les fichiers générés
|
|
346
|
+
make bridge-clean
|
|
347
|
+
```
|
|
121
348
|
|
|
122
|
-
|
|
349
|
+
#### SDK npm (Angular package)
|
|
123
350
|
|
|
124
351
|
```bash
|
|
125
|
-
#
|
|
352
|
+
# Générer le package Angular complet (quand l'API change)
|
|
126
353
|
make sdk-npm
|
|
127
354
|
|
|
128
|
-
#
|
|
129
|
-
|
|
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
|
-
|
|
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
|
-
|
|
138
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
152
|
-
- **Adds**: Meridiane bridge for consistency with the main app
|
|
458
|
+
---
|
|
153
459
|
|
|
154
|
-
|
|
460
|
+
## Intégration front (SPA)
|
|
155
461
|
|
|
156
|
-
|
|
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
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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
|
-
|
|
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
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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
|
-
|
|
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
|
-
|
|
801
|
+
Ce projet est distribué sous les termes de la licence indiquée dans [`LICENSE`](./LICENSE).
|