@dloizides/auth-web 1.2.0
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/CHANGELOG.md +120 -0
- package/LICENSE +21 -0
- package/README.md +229 -0
- package/dist/index.d.mts +806 -0
- package/dist/index.d.ts +806 -0
- package/dist/index.js +1413 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1373 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +110 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,806 @@
|
|
|
1
|
+
import { ReactElement, ReactNode } from 'react';
|
|
2
|
+
import { BffAuthClient, BffUser, BffLoginRequest, BffForgotPasswordRequest, BffResetPasswordRequest, BffOtpRequestResult, HttpClient } from '@dloizides/auth-client';
|
|
3
|
+
export { BffAuthClient, BffAuthClientOptions, BffForgotPasswordRequest, BffLoginRequest, BffOtpRequestRequest, BffOtpRequestResult, BffOtpVerifyRequest, BffPinLoginRequest, BffRegisterRequest, BffResetPasswordRequest, BffUser, HttpClient, HttpRequest, HttpResponse, createFetchHttpClient } from '@dloizides/auth-client';
|
|
4
|
+
import { UseMutationOptions, UseMutationResult } from '@tanstack/react-query';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Label bags for the auth components.
|
|
8
|
+
*
|
|
9
|
+
* `@dloizides/auth-web` ships **no** translation framework — it is i18n-agnostic
|
|
10
|
+
* by design (each app already owns its own i18n). Every user-facing string a
|
|
11
|
+
* component renders is supplied by the consuming app through a typed `labels`
|
|
12
|
+
* prop. The app passes strings it has already localised with `FM()` / `t()`.
|
|
13
|
+
*
|
|
14
|
+
* Each bag has a `DEFAULT_*` constant (English) so a component renders sensibly
|
|
15
|
+
* with no `labels` prop and a consuming app can spread-override only the keys
|
|
16
|
+
* it wants to change.
|
|
17
|
+
*/
|
|
18
|
+
/** Strings rendered by `<LoginForm>`. */
|
|
19
|
+
interface LoginFormLabels {
|
|
20
|
+
title: string;
|
|
21
|
+
subtitle: string;
|
|
22
|
+
usernameLabel: string;
|
|
23
|
+
usernamePlaceholder: string;
|
|
24
|
+
passwordLabel: string;
|
|
25
|
+
passwordPlaceholder: string;
|
|
26
|
+
submit: string;
|
|
27
|
+
/** Shown while the login request is in flight. */
|
|
28
|
+
submitting: string;
|
|
29
|
+
/** The "Forgot password?" link text. */
|
|
30
|
+
forgotPassword: string;
|
|
31
|
+
/** Generic credentials-rejected message. */
|
|
32
|
+
invalidCredentials: string;
|
|
33
|
+
/** Shown when one or both fields are empty on submit. */
|
|
34
|
+
missingFields: string;
|
|
35
|
+
}
|
|
36
|
+
/** Strings rendered by `<ForgotPasswordForm>`. */
|
|
37
|
+
interface ForgotPasswordFormLabels {
|
|
38
|
+
title: string;
|
|
39
|
+
/** Copy above the email field. */
|
|
40
|
+
description: string;
|
|
41
|
+
emailLabel: string;
|
|
42
|
+
emailPlaceholder: string;
|
|
43
|
+
submit: string;
|
|
44
|
+
submitting: string;
|
|
45
|
+
/** Generic confirmation, shown regardless of whether the email is registered. */
|
|
46
|
+
successMessage: string;
|
|
47
|
+
/** Shown on a network / 5xx failure. */
|
|
48
|
+
networkError: string;
|
|
49
|
+
/** Shown when the entered value is not a valid email. */
|
|
50
|
+
invalidEmail: string;
|
|
51
|
+
}
|
|
52
|
+
/** Strings rendered by `<OtpForm>` — the two-step email-OTP login form. */
|
|
53
|
+
interface OtpFormLabels {
|
|
54
|
+
/** Title shown on the request-a-code step. */
|
|
55
|
+
requestTitle: string;
|
|
56
|
+
/** Copy above the email field. */
|
|
57
|
+
requestDescription: string;
|
|
58
|
+
emailLabel: string;
|
|
59
|
+
emailPlaceholder: string;
|
|
60
|
+
/** The "send code" button. */
|
|
61
|
+
requestSubmit: string;
|
|
62
|
+
/** Shown while the request is in flight. */
|
|
63
|
+
requesting: string;
|
|
64
|
+
/** Shown when the entered value is not a valid email. */
|
|
65
|
+
invalidEmail: string;
|
|
66
|
+
/** Title shown on the enter-the-code step. */
|
|
67
|
+
verifyTitle: string;
|
|
68
|
+
/** Copy above the code field; `{identifier}` is replaced with the email. */
|
|
69
|
+
verifyDescription: string;
|
|
70
|
+
codeLabel: string;
|
|
71
|
+
codePlaceholder: string;
|
|
72
|
+
/** The "verify" button. */
|
|
73
|
+
verifySubmit: string;
|
|
74
|
+
/** Shown while verification is in flight. */
|
|
75
|
+
verifying: string;
|
|
76
|
+
/** Shown when the code field is empty on submit. */
|
|
77
|
+
missingCode: string;
|
|
78
|
+
/** Generic "that code was wrong / expired" message. */
|
|
79
|
+
invalidCode: string;
|
|
80
|
+
/** The "resend code" link text. */
|
|
81
|
+
resend: string;
|
|
82
|
+
/** Shown while a resend is in flight. */
|
|
83
|
+
resending: string;
|
|
84
|
+
/** The "use a different email" link text — returns to step 1. */
|
|
85
|
+
changeEmail: string;
|
|
86
|
+
}
|
|
87
|
+
/** Strings rendered by `<PinForm>` — the single-step event-PIN login form. */
|
|
88
|
+
interface PinFormLabels {
|
|
89
|
+
/** Title shown above the PIN field. */
|
|
90
|
+
title: string;
|
|
91
|
+
/** Copy above the PIN field. */
|
|
92
|
+
description: string;
|
|
93
|
+
pinLabel: string;
|
|
94
|
+
pinPlaceholder: string;
|
|
95
|
+
/** The "sign in" button. */
|
|
96
|
+
submit: string;
|
|
97
|
+
/** Shown while the PIN exchange is in flight. */
|
|
98
|
+
submitting: string;
|
|
99
|
+
/** Shown when the PIN field is empty on submit. */
|
|
100
|
+
missingPin: string;
|
|
101
|
+
/** Generic "that PIN was wrong / expired / locked out" message. */
|
|
102
|
+
invalidPin: string;
|
|
103
|
+
}
|
|
104
|
+
/** Strings rendered by `<ResetPasswordForm>`. */
|
|
105
|
+
interface ResetPasswordFormLabels {
|
|
106
|
+
title: string;
|
|
107
|
+
description: string;
|
|
108
|
+
newPasswordLabel: string;
|
|
109
|
+
newPasswordPlaceholder: string;
|
|
110
|
+
confirmPasswordLabel: string;
|
|
111
|
+
confirmPasswordPlaceholder: string;
|
|
112
|
+
submit: string;
|
|
113
|
+
submitting: string;
|
|
114
|
+
/** Error: one or both fields empty. */
|
|
115
|
+
errorEmpty: string;
|
|
116
|
+
/** Error: the new password fails the policy. */
|
|
117
|
+
errorWeakPassword: string;
|
|
118
|
+
/** Error: confirm does not match. */
|
|
119
|
+
errorMismatch: string;
|
|
120
|
+
/** Error: the reset token is missing / expired / consumed. */
|
|
121
|
+
errorTokenInvalid: string;
|
|
122
|
+
/** Error: a network / 5xx failure. */
|
|
123
|
+
errorNetwork: string;
|
|
124
|
+
}
|
|
125
|
+
declare const DEFAULT_LOGIN_LABELS: LoginFormLabels;
|
|
126
|
+
declare const DEFAULT_FORGOT_PASSWORD_LABELS: ForgotPasswordFormLabels;
|
|
127
|
+
declare const DEFAULT_OTP_LABELS: OtpFormLabels;
|
|
128
|
+
declare const DEFAULT_PIN_LABELS: PinFormLabels;
|
|
129
|
+
declare const DEFAULT_RESET_PASSWORD_LABELS: ResetPasswordFormLabels;
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* `AuthTheme` — the design-token contract every `@dloizides/auth-web` component
|
|
133
|
+
* is styled against.
|
|
134
|
+
*
|
|
135
|
+
* The package owns **no** brand. Katalogos, Erevna and Kefi each look different;
|
|
136
|
+
* each one maps its own theme system onto this flat token bag and passes it in
|
|
137
|
+
* (via the `theme` prop or `<AuthThemeProvider>`). The component code only ever
|
|
138
|
+
* reads these tokens — never an app-specific theme object — so the same
|
|
139
|
+
* `<LoginForm>` renders on-brand in three visually distinct apps.
|
|
140
|
+
*
|
|
141
|
+
* Keep this shape minimal and stable: it is a public API surface.
|
|
142
|
+
*/
|
|
143
|
+
/** Colour tokens — the only colours the auth components reference. */
|
|
144
|
+
interface AuthThemeColors {
|
|
145
|
+
/** Page / screen background behind the form card. */
|
|
146
|
+
background: string;
|
|
147
|
+
/** The form card surface colour. */
|
|
148
|
+
surface: string;
|
|
149
|
+
/** Primary text (titles, labels, input text). */
|
|
150
|
+
text: string;
|
|
151
|
+
/** Muted / secondary text (subtitles, helper copy). */
|
|
152
|
+
textSecondary: string;
|
|
153
|
+
/** Input + card border colour; also the disabled-button fill. */
|
|
154
|
+
border: string;
|
|
155
|
+
/** Brand primary — the submit button fill and link colour. */
|
|
156
|
+
primary: string;
|
|
157
|
+
/** Text drawn on top of `primary` (e.g. the submit button label). */
|
|
158
|
+
onPrimary: string;
|
|
159
|
+
/** Error text and error-state borders. */
|
|
160
|
+
danger: string;
|
|
161
|
+
/** Success text (e.g. the forgot-password confirmation). */
|
|
162
|
+
success: string;
|
|
163
|
+
}
|
|
164
|
+
/** Corner-radius tokens. */
|
|
165
|
+
interface AuthThemeRadii {
|
|
166
|
+
/** Inputs and buttons. */
|
|
167
|
+
input: number;
|
|
168
|
+
/** The form card. */
|
|
169
|
+
card: number;
|
|
170
|
+
}
|
|
171
|
+
/** Spacing scale (in density-independent pixels). */
|
|
172
|
+
interface AuthThemeSpacing {
|
|
173
|
+
/** Tight gap — e.g. label-to-input. */
|
|
174
|
+
xs: number;
|
|
175
|
+
/** Small gap. */
|
|
176
|
+
sm: number;
|
|
177
|
+
/** Default gap — between stacked fields. */
|
|
178
|
+
md: number;
|
|
179
|
+
/** Large gap — section separation. */
|
|
180
|
+
lg: number;
|
|
181
|
+
/** Card inner padding. */
|
|
182
|
+
xl: number;
|
|
183
|
+
}
|
|
184
|
+
/** Font-size tokens. */
|
|
185
|
+
interface AuthThemeTypography {
|
|
186
|
+
/** Form title. */
|
|
187
|
+
title: number;
|
|
188
|
+
/** Form subtitle. */
|
|
189
|
+
subtitle: number;
|
|
190
|
+
/** Field labels. */
|
|
191
|
+
label: number;
|
|
192
|
+
/** Input text and button text. */
|
|
193
|
+
body: number;
|
|
194
|
+
/** Helper / error / footer copy. */
|
|
195
|
+
caption: number;
|
|
196
|
+
}
|
|
197
|
+
/** The full token bag a consuming app supplies. */
|
|
198
|
+
interface AuthTheme {
|
|
199
|
+
colors: AuthThemeColors;
|
|
200
|
+
radii: AuthThemeRadii;
|
|
201
|
+
spacing: AuthThemeSpacing;
|
|
202
|
+
typography: AuthThemeTypography;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* A neutral, accessible light-mode theme. Apps are expected to override this —
|
|
206
|
+
* it exists so a component renders sensibly with zero configuration and so
|
|
207
|
+
* `<AuthThemeProvider>` has a default value.
|
|
208
|
+
*/
|
|
209
|
+
declare const defaultAuthTheme: AuthTheme;
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* `<LoginForm>` — the ready-made, themeable password-login form.
|
|
213
|
+
*
|
|
214
|
+
* The "themeable component" half of the package design: drop it in, pass a
|
|
215
|
+
* `client`, a `theme` (or wrap in `<AuthThemeProvider>`) and a localised
|
|
216
|
+
* `labels` bag, and you have a branded login surface. It is built on the
|
|
217
|
+
* headless `useBffAuth` hook, so the ready-made and custom-layout paths share
|
|
218
|
+
* one code path.
|
|
219
|
+
*
|
|
220
|
+
* The package ships no router and no i18n. `onSuccess` hands the signed-in
|
|
221
|
+
* `BffUser` back to the app, which decides where to navigate (typically via
|
|
222
|
+
* `resolvePostLoginRoute`). All copy comes from `labels`.
|
|
223
|
+
*/
|
|
224
|
+
|
|
225
|
+
interface LoginFormProps {
|
|
226
|
+
/** The same-origin BFF client (build it with `createBffAuthClient`). */
|
|
227
|
+
client: BffAuthClient;
|
|
228
|
+
/** Explicit theme; overrides any `<AuthThemeProvider>`. Falls back to the default theme. */
|
|
229
|
+
theme?: AuthTheme;
|
|
230
|
+
/** Localised copy. Partial — unspecified keys fall back to English defaults. */
|
|
231
|
+
labels?: Partial<LoginFormLabels>;
|
|
232
|
+
/** Called with the signed-in user after a successful login. */
|
|
233
|
+
onSuccess: (user: BffUser) => void;
|
|
234
|
+
/** Called when the user taps the "Forgot password?" link. Omit to hide the link. */
|
|
235
|
+
onForgotPassword?: () => void;
|
|
236
|
+
/** Prefix applied to every `testID` so multiple forms can share a screen. */
|
|
237
|
+
testIdPrefix?: string;
|
|
238
|
+
}
|
|
239
|
+
/** Themeable password-login form built on `useBffAuth`. */
|
|
240
|
+
declare function LoginForm({ client, theme: themeProp, labels: labelsProp, onSuccess, onForgotPassword, testIdPrefix, }: Readonly<LoginFormProps>): ReactElement;
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* `<ForgotPasswordForm>` — the ready-made, themeable "request a reset link"
|
|
244
|
+
* form.
|
|
245
|
+
*
|
|
246
|
+
* Captures an email and POSTs to `/bff/forgot-password` via the headless
|
|
247
|
+
* `useBffForgotPassword` hook. The backend answers 200 unconditionally (no
|
|
248
|
+
* email enumeration), so on success the form swaps to a generic confirmation
|
|
249
|
+
* message — it never reveals whether the address is registered.
|
|
250
|
+
*
|
|
251
|
+
* Extracted + generalised from `apps/katalogos-web/src/components/Auth/
|
|
252
|
+
* ForgotPasswordModal.tsx` — minus the modal shell (apps own the surface) and
|
|
253
|
+
* the app-specific `FM()` calls (copy now comes from `labels`).
|
|
254
|
+
*/
|
|
255
|
+
|
|
256
|
+
interface ForgotPasswordFormProps {
|
|
257
|
+
/** The same-origin BFF client (build it with `createBffAuthClient`). */
|
|
258
|
+
client: BffAuthClient;
|
|
259
|
+
/** Explicit theme; overrides any `<AuthThemeProvider>`. */
|
|
260
|
+
theme?: AuthTheme;
|
|
261
|
+
/** Localised copy. Partial — unspecified keys fall back to English defaults. */
|
|
262
|
+
labels?: Partial<ForgotPasswordFormLabels>;
|
|
263
|
+
/**
|
|
264
|
+
* Full URL with a `{token}` placeholder; forwarded to the backend so it can
|
|
265
|
+
* build the reset-email link without hardcoding any frontend host. Optional —
|
|
266
|
+
* apps that configure the template server-side can omit it.
|
|
267
|
+
*/
|
|
268
|
+
resetUrlTemplate?: string;
|
|
269
|
+
/** Called once the request succeeds (the generic-confirmation state). */
|
|
270
|
+
onSuccess?: () => void;
|
|
271
|
+
/** Prefix applied to every `testID`. */
|
|
272
|
+
testIdPrefix?: string;
|
|
273
|
+
}
|
|
274
|
+
/** Themeable "request a reset link" form built on `useBffForgotPassword`. */
|
|
275
|
+
declare function ForgotPasswordForm({ client, theme: themeProp, labels: labelsProp, resetUrlTemplate, onSuccess, testIdPrefix, }: Readonly<ForgotPasswordFormProps>): ReactElement;
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* `<ResetPasswordForm>` — the ready-made, themeable "choose a new password"
|
|
279
|
+
* form.
|
|
280
|
+
*
|
|
281
|
+
* Built on the headless `useResetPasswordForm` hook, which owns all the logic
|
|
282
|
+
* (shared password policy, confirm-match, backend-error mapping). The component
|
|
283
|
+
* is a thin render layer: two fields, one button, one error line.
|
|
284
|
+
*
|
|
285
|
+
* Extracted + generalised from `apps/katalogos-web/src/auth/useResetPasswordForm.ts`
|
|
286
|
+
* + the katalogos reset-password route — minus the app-specific `FM()` and
|
|
287
|
+
* router calls (copy is in `labels`, navigation is the app's `onSuccess`).
|
|
288
|
+
*/
|
|
289
|
+
|
|
290
|
+
interface ResetPasswordFormProps {
|
|
291
|
+
/** The same-origin BFF client (build it with `createBffAuthClient`). */
|
|
292
|
+
client: BffAuthClient;
|
|
293
|
+
/** The reset token, typically read from the deep-link URL by the app. */
|
|
294
|
+
token: string;
|
|
295
|
+
/** Explicit theme; overrides any `<AuthThemeProvider>`. */
|
|
296
|
+
theme?: AuthTheme;
|
|
297
|
+
/** Localised copy. Partial — unspecified keys fall back to English defaults. */
|
|
298
|
+
labels?: Partial<ResetPasswordFormLabels>;
|
|
299
|
+
/** Called once the password has been reset successfully. */
|
|
300
|
+
onSuccess: () => void;
|
|
301
|
+
/** Prefix applied to every `testID`. */
|
|
302
|
+
testIdPrefix?: string;
|
|
303
|
+
}
|
|
304
|
+
/** Themeable "choose a new password" form built on `useResetPasswordForm`. */
|
|
305
|
+
declare function ResetPasswordForm({ client, token, theme: themeProp, labels: labelsProp, onSuccess, testIdPrefix, }: Readonly<ResetPasswordFormProps>): ReactElement;
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* `<OtpForm>` — the ready-made, themeable two-step email-OTP login form.
|
|
309
|
+
*
|
|
310
|
+
* The "themeable component" half of the OTP design: drop it in, pass a
|
|
311
|
+
* `client`, a `theme` (or wrap in `<AuthThemeProvider>`) and a localised
|
|
312
|
+
* `labels` bag, and you have a branded email-OTP surface. It is built on the
|
|
313
|
+
* headless `useOtpLogin` hook, so the ready-made and custom-layout paths share
|
|
314
|
+
* one code path — the same pattern as `<LoginForm>`.
|
|
315
|
+
*
|
|
316
|
+
* Two steps, switched on `OtpLoginStep`:
|
|
317
|
+
* 1. `RequestCode` — `<OtpRequestStep>`: an email field + a "send code" button.
|
|
318
|
+
* 2. `EnterCode` — `<OtpVerifyStep>`: a code field + a "verify" button, plus
|
|
319
|
+
* "resend code" and "use a different email" affordances.
|
|
320
|
+
*
|
|
321
|
+
* The package ships no router and no i18n. `onSuccess` hands the signed-in
|
|
322
|
+
* `BffUser` back to the app; all copy comes from `labels`.
|
|
323
|
+
*/
|
|
324
|
+
|
|
325
|
+
interface OtpFormProps {
|
|
326
|
+
/** The same-origin BFF client (build it with `createBffAuthClient`). */
|
|
327
|
+
client: BffAuthClient;
|
|
328
|
+
/** Explicit theme; overrides any `<AuthThemeProvider>`. Falls back to the default theme. */
|
|
329
|
+
theme?: AuthTheme;
|
|
330
|
+
/** Localised copy. Partial — unspecified keys fall back to English defaults. */
|
|
331
|
+
labels?: Partial<OtpFormLabels>;
|
|
332
|
+
/** Called with the signed-in user after a successful code verification. */
|
|
333
|
+
onSuccess: (user: BffUser) => void;
|
|
334
|
+
/** Prefix applied to every `testID` so multiple forms can share a screen. */
|
|
335
|
+
testIdPrefix?: string;
|
|
336
|
+
}
|
|
337
|
+
/** Themeable two-step email-OTP login form built on `useOtpLogin`. */
|
|
338
|
+
declare function OtpForm({ client, theme: themeProp, labels: labelsProp, onSuccess, testIdPrefix, }: Readonly<OtpFormProps>): ReactElement;
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* `<PinForm>` — the ready-made, themeable single-step event-PIN login form.
|
|
342
|
+
*
|
|
343
|
+
* The "themeable component" half of the PIN design: drop it in, pass a
|
|
344
|
+
* `client`, the `eventExternalId` of the event the staff member is signing in
|
|
345
|
+
* to, a `theme` (or wrap in `<AuthThemeProvider>`) and a localised `labels`
|
|
346
|
+
* bag, and you have a branded event-PIN surface. It is built on the headless
|
|
347
|
+
* `usePinLogin` hook, so the ready-made and custom-layout paths share one code
|
|
348
|
+
* path — the same pattern as `<OtpForm>` / `<LoginForm>`.
|
|
349
|
+
*
|
|
350
|
+
* Unlike `<OtpForm>` the PIN flow is a single step: a PIN field + a "sign in"
|
|
351
|
+
* button. The `eventExternalId` is a prop, never typed by the user — the event
|
|
352
|
+
* context comes from the route/page the form is rendered on.
|
|
353
|
+
*
|
|
354
|
+
* The package ships no router and no i18n. `onSuccess` hands the signed-in
|
|
355
|
+
* `BffUser` back to the app; all copy comes from `labels`.
|
|
356
|
+
*/
|
|
357
|
+
|
|
358
|
+
interface PinFormProps {
|
|
359
|
+
/** The same-origin BFF client (build it with `createBffAuthClient`). */
|
|
360
|
+
client: BffAuthClient;
|
|
361
|
+
/**
|
|
362
|
+
* External id of the event the PIN is scoped to. Supplied by the route/page
|
|
363
|
+
* — the event context is never typed by the user.
|
|
364
|
+
*/
|
|
365
|
+
eventExternalId: string;
|
|
366
|
+
/** Explicit theme; overrides any `<AuthThemeProvider>`. Falls back to the default theme. */
|
|
367
|
+
theme?: AuthTheme;
|
|
368
|
+
/** Localised copy. Partial — unspecified keys fall back to English defaults. */
|
|
369
|
+
labels?: Partial<PinFormLabels>;
|
|
370
|
+
/** Called with the signed-in user after a successful PIN exchange. */
|
|
371
|
+
onSuccess: (user: BffUser) => void;
|
|
372
|
+
/** Prefix applied to every `testID` so multiple forms can share a screen. */
|
|
373
|
+
testIdPrefix?: string;
|
|
374
|
+
}
|
|
375
|
+
/** Themeable single-step event-PIN login form built on `usePinLogin`. */
|
|
376
|
+
declare function PinForm({ client, eventExternalId, theme: themeProp, labels: labelsProp, onSuccess, testIdPrefix, }: Readonly<PinFormProps>): ReactElement;
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* React context for the `AuthTheme`.
|
|
380
|
+
*
|
|
381
|
+
* Two ways to theme `@dloizides/auth-web` components:
|
|
382
|
+
*
|
|
383
|
+
* 1. Wrap the auth screens in `<AuthThemeProvider theme={appAuthTheme}>` —
|
|
384
|
+
* every component below reads the theme from context.
|
|
385
|
+
* 2. Pass `theme` directly as a prop to an individual component — the prop
|
|
386
|
+
* always wins over context.
|
|
387
|
+
*
|
|
388
|
+
* With neither, components fall back to `defaultAuthTheme`. `useAuthTheme()`
|
|
389
|
+
* implements that precedence (prop → context → default) so each component does
|
|
390
|
+
* not re-derive it.
|
|
391
|
+
*/
|
|
392
|
+
|
|
393
|
+
interface AuthThemeProviderProps {
|
|
394
|
+
/** The token bag every descendant auth component is styled against. */
|
|
395
|
+
theme: AuthTheme;
|
|
396
|
+
children: ReactNode;
|
|
397
|
+
}
|
|
398
|
+
/** Provides an `AuthTheme` to every `@dloizides/auth-web` component below it. */
|
|
399
|
+
declare function AuthThemeProvider({ theme, children, }: Readonly<AuthThemeProviderProps>): ReactElement;
|
|
400
|
+
/**
|
|
401
|
+
* Resolve the effective theme for a component.
|
|
402
|
+
*
|
|
403
|
+
* Precedence: an explicit `themeProp` (if given) wins over the context value,
|
|
404
|
+
* which itself defaults to `defaultAuthTheme` when no provider is mounted.
|
|
405
|
+
*/
|
|
406
|
+
declare function useAuthTheme(themeProp?: AuthTheme): AuthTheme;
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Stable `testID` values for every interactive element the auth components
|
|
410
|
+
* render. Exported so E2E suites and unit tests reference one source of truth
|
|
411
|
+
* rather than hard-coding string literals.
|
|
412
|
+
*
|
|
413
|
+
* Each component also accepts a `testIdPrefix` prop; when set, these base IDs
|
|
414
|
+
* are prefixed so two forms can coexist on one screen without ID collisions.
|
|
415
|
+
*
|
|
416
|
+
* `sonarjs/no-hardcoded-passwords` is disabled for this file: it flags keys
|
|
417
|
+
* such as `loginPasswordInput`, but every value here is a UI test ID, not a
|
|
418
|
+
* credential. There are no secrets in this module.
|
|
419
|
+
*/
|
|
420
|
+
declare const AuthTestIds: {
|
|
421
|
+
readonly loginForm: "auth-login-form";
|
|
422
|
+
readonly loginUsernameInput: "auth-login-username";
|
|
423
|
+
readonly loginPasswordInput: "auth-login-password";
|
|
424
|
+
readonly loginSubmitButton: "auth-login-submit";
|
|
425
|
+
readonly loginForgotLink: "auth-login-forgot-link";
|
|
426
|
+
readonly loginError: "auth-login-error";
|
|
427
|
+
readonly forgotPasswordForm: "auth-forgot-form";
|
|
428
|
+
readonly forgotPasswordEmailInput: "auth-forgot-email";
|
|
429
|
+
readonly forgotPasswordSubmitButton: "auth-forgot-submit";
|
|
430
|
+
readonly forgotPasswordError: "auth-forgot-error";
|
|
431
|
+
readonly forgotPasswordSuccess: "auth-forgot-success";
|
|
432
|
+
readonly resetPasswordForm: "auth-reset-form";
|
|
433
|
+
readonly resetPasswordNewInput: "auth-reset-new";
|
|
434
|
+
readonly resetPasswordConfirmInput: "auth-reset-confirm";
|
|
435
|
+
readonly resetPasswordSubmitButton: "auth-reset-submit";
|
|
436
|
+
readonly resetPasswordError: "auth-reset-error";
|
|
437
|
+
readonly otpForm: "auth-otp-form";
|
|
438
|
+
readonly otpEmailInput: "auth-otp-email";
|
|
439
|
+
readonly otpRequestButton: "auth-otp-request";
|
|
440
|
+
readonly otpCodeInput: "auth-otp-code";
|
|
441
|
+
readonly otpVerifyButton: "auth-otp-verify";
|
|
442
|
+
readonly otpResendButton: "auth-otp-resend";
|
|
443
|
+
readonly otpChangeEmailButton: "auth-otp-change-email";
|
|
444
|
+
readonly otpError: "auth-otp-error";
|
|
445
|
+
readonly pinForm: "auth-pin-form";
|
|
446
|
+
readonly pinInput: "auth-pin-input";
|
|
447
|
+
readonly pinSubmitButton: "auth-pin-submit";
|
|
448
|
+
readonly pinError: "auth-pin-error";
|
|
449
|
+
};
|
|
450
|
+
/** Apply an optional prefix to a base test ID. */
|
|
451
|
+
declare function withTestIdPrefix(baseId: string, prefix?: string): string;
|
|
452
|
+
|
|
453
|
+
/** Lifecycle status of the auth session. */
|
|
454
|
+
declare const enum BffAuthStatus {
|
|
455
|
+
/** The initial `/bff/me` probe has not finished yet. */
|
|
456
|
+
Loading = "loading",
|
|
457
|
+
/** A live session exists; `user` is populated. */
|
|
458
|
+
Authenticated = "authenticated",
|
|
459
|
+
/** No session; `user` is `null`. */
|
|
460
|
+
Unauthenticated = "unauthenticated"
|
|
461
|
+
}
|
|
462
|
+
interface UseBffAuthOptions {
|
|
463
|
+
/** The same-origin BFF client (build it with `createBffAuthClient`). */
|
|
464
|
+
client: BffAuthClient;
|
|
465
|
+
/**
|
|
466
|
+
* When `true` (default), the hook probes `GET /bff/me` on mount to bootstrap
|
|
467
|
+
* `status`/`user`. Set `false` to skip the probe (e.g. on a public page).
|
|
468
|
+
*/
|
|
469
|
+
probeOnMount?: boolean;
|
|
470
|
+
}
|
|
471
|
+
interface UseBffAuthResult {
|
|
472
|
+
/** The signed-in user, or `null` when there is no session. */
|
|
473
|
+
user: BffUser | null;
|
|
474
|
+
/** Session lifecycle status. */
|
|
475
|
+
status: BffAuthStatus;
|
|
476
|
+
/** `true` while a `login` or `logout` call is in flight. */
|
|
477
|
+
isSubmitting: boolean;
|
|
478
|
+
/** The last `login`/`logout`/`refresh` error, cleared on the next attempt. */
|
|
479
|
+
error: Error | null;
|
|
480
|
+
/** ROPC password login via `POST /bff/login`. Resolves to the user; rejects on failure. */
|
|
481
|
+
login: (request: BffLoginRequest) => Promise<BffUser>;
|
|
482
|
+
/** End the session via `POST /bff/logout`. Always resolves; never throws to the caller. */
|
|
483
|
+
logout: () => Promise<void>;
|
|
484
|
+
/** Re-read `GET /bff/me` and resync `user`/`status`. */
|
|
485
|
+
refresh: () => Promise<void>;
|
|
486
|
+
}
|
|
487
|
+
/**
|
|
488
|
+
* Headless BFF auth. Returns the current user, the session status, and
|
|
489
|
+
* `login` / `logout` / `refresh` callbacks. No rendering — the consumer (or
|
|
490
|
+
* `<LoginForm>`) wires it to UI.
|
|
491
|
+
*/
|
|
492
|
+
declare function useBffAuth(options: UseBffAuthOptions): UseBffAuthResult;
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* React Query mutation hooks for the BFF password-reset flow.
|
|
496
|
+
*
|
|
497
|
+
* Headless counterparts to `<ForgotPasswordForm>` / `<ResetPasswordForm>` —
|
|
498
|
+
* apps with a custom layout use these directly. They POST to the same-origin
|
|
499
|
+
* `/bff/forgot-password` and `/bff/reset-password`, which the BFF proxies to
|
|
500
|
+
* TenantService.
|
|
501
|
+
*
|
|
502
|
+
* Extracted + generalised from `apps/katalogos-web/src/auth/bffPasswordHooks.ts`.
|
|
503
|
+
* Difference: the katalogos version closed over a module-level singleton
|
|
504
|
+
* `bffAuthClient`; the package version takes the `client` in the options so it
|
|
505
|
+
* carries no global state.
|
|
506
|
+
*
|
|
507
|
+
* The mutation result type is `undefined` (not `void`): the BFF endpoints
|
|
508
|
+
* return no body, and `undefined` is a valid generic argument where the lint
|
|
509
|
+
* config rejects `void`.
|
|
510
|
+
*/
|
|
511
|
+
|
|
512
|
+
type UseBffForgotPasswordOptions = Omit<UseMutationOptions<undefined, Error, BffForgotPasswordRequest>, 'mutationFn'> & {
|
|
513
|
+
/** The same-origin BFF client. */
|
|
514
|
+
client: BffAuthClient;
|
|
515
|
+
};
|
|
516
|
+
/**
|
|
517
|
+
* Mutation that POSTs to `/bff/forgot-password`.
|
|
518
|
+
*
|
|
519
|
+
* The backend returns 200 unconditionally (no email enumeration); the UI
|
|
520
|
+
* should show the same "if that email exists, we sent a link" message whether
|
|
521
|
+
* `onSuccess` or `onError` fires.
|
|
522
|
+
*/
|
|
523
|
+
declare function useBffForgotPassword(options: UseBffForgotPasswordOptions): UseMutationResult<undefined, Error, BffForgotPasswordRequest>;
|
|
524
|
+
type UseBffResetPasswordOptions = Omit<UseMutationOptions<undefined, Error, BffResetPasswordRequest>, 'mutationFn'> & {
|
|
525
|
+
/** The same-origin BFF client. */
|
|
526
|
+
client: BffAuthClient;
|
|
527
|
+
};
|
|
528
|
+
/**
|
|
529
|
+
* Mutation that POSTs to `/bff/reset-password`. A `400` (invalid / expired
|
|
530
|
+
* token) surfaces as a rejected mutation with the status in the error message.
|
|
531
|
+
*/
|
|
532
|
+
declare function useBffResetPassword(options: UseBffResetPasswordOptions): UseMutationResult<undefined, Error, BffResetPasswordRequest>;
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* The discrete failure states the reset-password flow can surface.
|
|
536
|
+
*
|
|
537
|
+
* Each member is the stable key a consuming app maps onto a localised message.
|
|
538
|
+
* `useResetPasswordForm` reports exactly one of these at a time, preferring the
|
|
539
|
+
* earliest-fixable issue so the user resolves one thing at a time.
|
|
540
|
+
*/
|
|
541
|
+
declare const enum ResetPasswordError {
|
|
542
|
+
/** One or both password fields are empty. */
|
|
543
|
+
Empty = "empty",
|
|
544
|
+
/** The new password fails the shared password policy. */
|
|
545
|
+
WeakPassword = "weakPassword",
|
|
546
|
+
/** The confirm field does not match the new password. */
|
|
547
|
+
Mismatch = "mismatch",
|
|
548
|
+
/** The reset token is missing, expired, or already consumed. */
|
|
549
|
+
TokenInvalid = "tokenInvalid",
|
|
550
|
+
/** A network or 5xx failure prevented the reset from completing. */
|
|
551
|
+
Network = "network"
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
interface UseResetPasswordFormArgs {
|
|
555
|
+
/** The same-origin BFF client. */
|
|
556
|
+
client: BffAuthClient;
|
|
557
|
+
/** The reset token, typically read from the deep-link URL. */
|
|
558
|
+
token: string;
|
|
559
|
+
/** Invoked once the password has been reset successfully. */
|
|
560
|
+
onSuccess: () => void;
|
|
561
|
+
}
|
|
562
|
+
interface UseResetPasswordFormResult {
|
|
563
|
+
newPassword: string;
|
|
564
|
+
confirmPassword: string;
|
|
565
|
+
setNewPassword: (value: string) => void;
|
|
566
|
+
setConfirmPassword: (value: string) => void;
|
|
567
|
+
/** `true` while the reset request is in flight. */
|
|
568
|
+
isSubmitting: boolean;
|
|
569
|
+
/** The single active error, or `null` when the form is clean. */
|
|
570
|
+
errorKey: ResetPasswordError | null;
|
|
571
|
+
/** `true` once a token-related failure is detected (drives a "request a new link" UI). */
|
|
572
|
+
hasInvalidToken: boolean;
|
|
573
|
+
/** Validate, then submit. */
|
|
574
|
+
submit: () => void;
|
|
575
|
+
}
|
|
576
|
+
/**
|
|
577
|
+
* Headless reset-password form logic. Returns the field values, setters, the
|
|
578
|
+
* single active error, and a `submit` callback.
|
|
579
|
+
*/
|
|
580
|
+
declare function useResetPasswordForm({ client, token, onSuccess, }: UseResetPasswordFormArgs): UseResetPasswordFormResult;
|
|
581
|
+
|
|
582
|
+
/**
|
|
583
|
+
* The two discrete steps of the email-OTP login flow.
|
|
584
|
+
*
|
|
585
|
+
* `useOtpLogin` is at exactly one of these at a time, and `<OtpForm>` renders a
|
|
586
|
+
* different surface per step. The flow is strictly forward: a request moves
|
|
587
|
+
* `RequestCode` → `EnterCode`; a resend stays on `EnterCode`. Each member is a
|
|
588
|
+
* stable key — `<OtpForm>` switches on it, never on a string literal.
|
|
589
|
+
*/
|
|
590
|
+
declare const enum OtpLoginStep {
|
|
591
|
+
/** Step 1: collect the email and ask the BFF to send a code. */
|
|
592
|
+
RequestCode = "requestCode",
|
|
593
|
+
/** Step 2: the code is on its way — collect and verify it. */
|
|
594
|
+
EnterCode = "enterCode"
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
interface UseOtpLoginOptions {
|
|
598
|
+
/** The same-origin BFF client (build it with `createBffAuthClient`). */
|
|
599
|
+
client: BffAuthClient;
|
|
600
|
+
}
|
|
601
|
+
interface UseOtpLoginResult {
|
|
602
|
+
/** Which of the two steps the flow is currently on. */
|
|
603
|
+
step: OtpLoginStep;
|
|
604
|
+
/** The identifier (email) the code was last requested for; `''` before step 1. */
|
|
605
|
+
identifier: string;
|
|
606
|
+
/**
|
|
607
|
+
* The latest `POST /bff/otp/request` result, or `null` before any request.
|
|
608
|
+
* Carries `expiresIn` for a countdown — never depend on `code` being present.
|
|
609
|
+
*/
|
|
610
|
+
lastRequest: BffOtpRequestResult | null;
|
|
611
|
+
/** `true` while a `requestCode` / `verifyCode` / `resend` call is in flight. */
|
|
612
|
+
isSubmitting: boolean;
|
|
613
|
+
/** The last error, cleared at the start of the next attempt. */
|
|
614
|
+
error: Error | null;
|
|
615
|
+
/**
|
|
616
|
+
* Step 1 — ask the BFF to email a code to `identifier`. On success the flow
|
|
617
|
+
* advances to `EnterCode`. Rejects (and stays on `RequestCode`) on failure.
|
|
618
|
+
*/
|
|
619
|
+
requestCode: (identifier: string) => Promise<BffOtpRequestResult>;
|
|
620
|
+
/**
|
|
621
|
+
* Step 2 — verify the entered `otp` for the stored identifier. Resolves to the
|
|
622
|
+
* signed-in `BffUser`; rejects on a bad / expired code (the flow stays on
|
|
623
|
+
* `EnterCode` so the user can retry or resend).
|
|
624
|
+
*/
|
|
625
|
+
verifyCode: (otp: string) => Promise<BffUser>;
|
|
626
|
+
/**
|
|
627
|
+
* Re-send a code to the stored identifier without leaving `EnterCode`. A
|
|
628
|
+
* convenience wrapper over `requestOtp` for the step-2 "resend" affordance.
|
|
629
|
+
*/
|
|
630
|
+
resend: () => Promise<BffOtpRequestResult>;
|
|
631
|
+
/** Return to step 1 (e.g. the user mistyped their email). Clears the error. */
|
|
632
|
+
reset: () => void;
|
|
633
|
+
}
|
|
634
|
+
/**
|
|
635
|
+
* Headless email-OTP login. Returns the current step, the in-flight status, and
|
|
636
|
+
* `requestCode` / `verifyCode` / `resend` / `reset` callbacks. No rendering —
|
|
637
|
+
* the consumer (or `<OtpForm>`) wires it to UI.
|
|
638
|
+
*/
|
|
639
|
+
declare function useOtpLogin(options: UseOtpLoginOptions): UseOtpLoginResult;
|
|
640
|
+
|
|
641
|
+
interface UsePinLoginOptions {
|
|
642
|
+
/** The same-origin BFF client (build it with `createBffAuthClient`). */
|
|
643
|
+
client: BffAuthClient;
|
|
644
|
+
/**
|
|
645
|
+
* External id of the event the PIN is scoped to. Supplied by the page/route
|
|
646
|
+
* — the event context is never typed by the user.
|
|
647
|
+
*/
|
|
648
|
+
eventExternalId: string;
|
|
649
|
+
}
|
|
650
|
+
interface UsePinLoginResult {
|
|
651
|
+
/** `true` while a `submit` call is in flight. */
|
|
652
|
+
isSubmitting: boolean;
|
|
653
|
+
/** The last error, cleared at the start of the next attempt. */
|
|
654
|
+
error: Error | null;
|
|
655
|
+
/**
|
|
656
|
+
* Exchange the entered `pin` (for the hook's `eventExternalId`) for a
|
|
657
|
+
* session. Resolves to the signed-in `BffUser`; rejects on a bad / expired /
|
|
658
|
+
* locked-out PIN (the consumer can let the user retry).
|
|
659
|
+
*/
|
|
660
|
+
submit: (pin: string) => Promise<BffUser>;
|
|
661
|
+
/** Clear the current error so the form can be retried cleanly. */
|
|
662
|
+
reset: () => void;
|
|
663
|
+
}
|
|
664
|
+
/**
|
|
665
|
+
* Headless event-scoped PIN login. Returns the in-flight status, the last
|
|
666
|
+
* error, and `submit` / `reset` callbacks. No rendering — the consumer (or
|
|
667
|
+
* `<PinForm>`) wires it to UI.
|
|
668
|
+
*/
|
|
669
|
+
declare function usePinLogin(options: UsePinLoginOptions): UsePinLoginResult;
|
|
670
|
+
|
|
671
|
+
/**
|
|
672
|
+
* `createBffAuthClient` — the one-line wiring of a same-origin `BffAuthClient`.
|
|
673
|
+
*
|
|
674
|
+
* `BffAuthClient` (from `@dloizides/auth-client`) needs an `HttpClient`. Every
|
|
675
|
+
* app wired it the same way — `createFetchHttpClient(fetch)` — and every app
|
|
676
|
+
* hit the same trap: binding `fetch` at module load throws in a non-browser
|
|
677
|
+
* runtime (the Jest/jsdom test environment has no `fetch` global). The lesson
|
|
678
|
+
* of Phase 1 was "copy-pasted auth adapters ship the same bug N times", so the
|
|
679
|
+
* correct wiring lives here once.
|
|
680
|
+
*
|
|
681
|
+
* `fetch` is resolved **lazily, per request** — module load stays
|
|
682
|
+
* side-effect-free. `baseUrl` is omitted by default → same-origin: every
|
|
683
|
+
* `/bff/*` call goes to the SPA's own host, which is fronted by the per-app
|
|
684
|
+
* BFF (`bff-katalogos`, `bff-erevna`, ...).
|
|
685
|
+
*
|
|
686
|
+
* Extracted + generalised from `apps/katalogos-web/src/auth/bffAuthClient.ts`.
|
|
687
|
+
*/
|
|
688
|
+
|
|
689
|
+
interface CreateBffAuthClientOptions {
|
|
690
|
+
/**
|
|
691
|
+
* BFF origin. Omit (the production default) for same-origin — the SPA's own
|
|
692
|
+
* host is the BFF. An explicit origin is only useful for a non-same-origin
|
|
693
|
+
* BFF or for tests.
|
|
694
|
+
*/
|
|
695
|
+
baseUrl?: string;
|
|
696
|
+
/**
|
|
697
|
+
* Override the HTTP transport. Defaults to a lazily-resolved native `fetch`.
|
|
698
|
+
* Tests pass a fake `HttpClient` here; production never needs to.
|
|
699
|
+
*/
|
|
700
|
+
http?: HttpClient;
|
|
701
|
+
}
|
|
702
|
+
/**
|
|
703
|
+
* Build a same-origin `BffAuthClient`. With no arguments this is the exact
|
|
704
|
+
* production wiring: same-origin, lazy `fetch`, no token handling.
|
|
705
|
+
*/
|
|
706
|
+
declare function createBffAuthClient(options?: CreateBffAuthClientOptions): BffAuthClient;
|
|
707
|
+
|
|
708
|
+
/**
|
|
709
|
+
* `resolvePostLoginRoute` — the role-based post-login router helper.
|
|
710
|
+
*
|
|
711
|
+
* The unified-auth plan's secondary goal is to kill the "too many login links"
|
|
712
|
+
* problem (KUCY V2 has seven, one per role). One login surface; after a
|
|
713
|
+
* successful login the app reads the user's role from the token claims and
|
|
714
|
+
* routes to the right dashboard.
|
|
715
|
+
*
|
|
716
|
+
* This helper is intentionally pure and app-agnostic. The package does NOT
|
|
717
|
+
* know any app's routes — the consuming app supplies a `RoleRouteTable`
|
|
718
|
+
* mapping role → path. The helper picks the highest-priority matching role.
|
|
719
|
+
*
|
|
720
|
+
* Role priority: a user can hold several roles (`superUser` + `admin` + ...).
|
|
721
|
+
* The table is consulted in the order its entries are listed — the FIRST
|
|
722
|
+
* entry whose role the user holds wins. So an app lists the most privileged
|
|
723
|
+
* role first. A `fallback` covers a user whose roles match no entry.
|
|
724
|
+
*/
|
|
725
|
+
|
|
726
|
+
/** One role → route mapping. */
|
|
727
|
+
interface RoleRoute {
|
|
728
|
+
/** The Keycloak role name to match against the user's claims. */
|
|
729
|
+
role: string;
|
|
730
|
+
/** The route/path to send a user holding that role to. */
|
|
731
|
+
route: string;
|
|
732
|
+
}
|
|
733
|
+
/** An app-supplied, ordered role-routing table. */
|
|
734
|
+
interface RoleRouteTable {
|
|
735
|
+
/**
|
|
736
|
+
* Ordered list of role → route entries. Order is priority: the first entry
|
|
737
|
+
* whose `role` the user holds is chosen. List the most privileged role first.
|
|
738
|
+
*/
|
|
739
|
+
routes: RoleRoute[];
|
|
740
|
+
/**
|
|
741
|
+
* Route used when the user holds none of the listed roles. When omitted and
|
|
742
|
+
* nothing matches, `resolvePostLoginRoute` returns `null` and the caller
|
|
743
|
+
* decides what to do (e.g. show an "no access" screen).
|
|
744
|
+
*/
|
|
745
|
+
fallback?: string;
|
|
746
|
+
}
|
|
747
|
+
/**
|
|
748
|
+
* Collect every role on a `BffUser` — both the flat `roles` array and the
|
|
749
|
+
* nested `realm_access.roles` — into a de-duplicated set. The BFF may surface
|
|
750
|
+
* roles in either shape; this normalises both.
|
|
751
|
+
*/
|
|
752
|
+
declare function collectUserRoles(user: BffUser): string[];
|
|
753
|
+
/**
|
|
754
|
+
* Resolve the post-login route for a user against an app-supplied table.
|
|
755
|
+
*
|
|
756
|
+
* Returns the route of the first table entry whose role the user holds; if no
|
|
757
|
+
* entry matches, returns the table's `fallback`, or `null` when there is none.
|
|
758
|
+
*/
|
|
759
|
+
declare function resolvePostLoginRoute(user: BffUser, table: RoleRouteTable): string | null;
|
|
760
|
+
|
|
761
|
+
/**
|
|
762
|
+
* The discrete ways a password can fail the shared client-side policy.
|
|
763
|
+
*
|
|
764
|
+
* Each member is the stable key a consuming app maps onto a localised message —
|
|
765
|
+
* the package never ships translated copy.
|
|
766
|
+
*/
|
|
767
|
+
declare const enum PasswordPolicyError {
|
|
768
|
+
/** Shorter than `PASSWORD_MIN_LENGTH`. */
|
|
769
|
+
TooShort = "tooShort",
|
|
770
|
+
/** Longer than `PASSWORD_MAX_LENGTH`. */
|
|
771
|
+
TooLong = "tooLong",
|
|
772
|
+
/** Contains no `A-Z` character. */
|
|
773
|
+
MissingUppercase = "missingUppercase",
|
|
774
|
+
/** Contains no `a-z` character. */
|
|
775
|
+
MissingLowercase = "missingLowercase",
|
|
776
|
+
/** Contains no `0-9` character. */
|
|
777
|
+
MissingDigit = "missingDigit"
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
/**
|
|
781
|
+
* Client-side password policy that mirrors the backend
|
|
782
|
+
* (TenantService `ResetPasswordRequestValidator`):
|
|
783
|
+
*
|
|
784
|
+
* - 8 ≤ length ≤ 128
|
|
785
|
+
* - at least one uppercase letter
|
|
786
|
+
* - at least one lowercase letter
|
|
787
|
+
* - at least one digit
|
|
788
|
+
*
|
|
789
|
+
* Shared so every app's `<ResetPasswordForm>` (and any future password input)
|
|
790
|
+
* rejects ~99% of bad passwords before a network round-trip. Extracted from
|
|
791
|
+
* `apps/katalogos-web/src/auth/passwordPolicy.ts`.
|
|
792
|
+
*/
|
|
793
|
+
|
|
794
|
+
/** Minimum acceptable password length. */
|
|
795
|
+
declare const PASSWORD_MIN_LENGTH = 8;
|
|
796
|
+
/** Maximum acceptable password length. */
|
|
797
|
+
declare const PASSWORD_MAX_LENGTH = 128;
|
|
798
|
+
/**
|
|
799
|
+
* Return every policy rule the given password violates. An empty array means
|
|
800
|
+
* the password is acceptable.
|
|
801
|
+
*/
|
|
802
|
+
declare function validatePasswordPolicy(password: string): PasswordPolicyError[];
|
|
803
|
+
/** `true` when the password satisfies every policy rule. */
|
|
804
|
+
declare function isPasswordValid(password: string): boolean;
|
|
805
|
+
|
|
806
|
+
export { AuthTestIds, type AuthTheme, type AuthThemeColors, AuthThemeProvider, type AuthThemeProviderProps, type AuthThemeRadii, type AuthThemeSpacing, type AuthThemeTypography, BffAuthStatus, type CreateBffAuthClientOptions, DEFAULT_FORGOT_PASSWORD_LABELS, DEFAULT_LOGIN_LABELS, DEFAULT_OTP_LABELS, DEFAULT_PIN_LABELS, DEFAULT_RESET_PASSWORD_LABELS, ForgotPasswordForm, type ForgotPasswordFormLabels, type ForgotPasswordFormProps, LoginForm, type LoginFormLabels, type LoginFormProps, OtpForm, type OtpFormLabels, type OtpFormProps, OtpLoginStep, PASSWORD_MAX_LENGTH, PASSWORD_MIN_LENGTH, PasswordPolicyError, PinForm, type PinFormLabels, type PinFormProps, ResetPasswordError, ResetPasswordForm, type ResetPasswordFormLabels, type ResetPasswordFormProps, type RoleRoute, type RoleRouteTable, type UseBffAuthOptions, type UseBffAuthResult, type UseBffForgotPasswordOptions, type UseBffResetPasswordOptions, type UseOtpLoginOptions, type UseOtpLoginResult, type UsePinLoginOptions, type UsePinLoginResult, type UseResetPasswordFormArgs, type UseResetPasswordFormResult, collectUserRoles, createBffAuthClient, defaultAuthTheme, isPasswordValid, resolvePostLoginRoute, useAuthTheme, useBffAuth, useBffForgotPassword, useBffResetPassword, useOtpLogin, usePinLogin, useResetPasswordForm, validatePasswordPolicy, withTestIdPrefix };
|