@easygov/react-components 0.4.6 → 0.4.7

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 CHANGED
@@ -74,6 +74,46 @@ and pass `--build-arg NEXT_PUBLIC_OIDC_AUTHORITY=...` (or the equivalent in
74
74
  your CI pipeline) when building the image — not only in the Deployment spec
75
75
  that runs it.
76
76
 
77
+ ### Reading the header context
78
+
79
+ The header resolves the signed-in user, the selected customer account and the running process
80
+ from core-backend and publishes them to consumers:
81
+
82
+ ```tsx
83
+ import { useEasygovHeaderContext } from '@easygov/react-components';
84
+
85
+ function ContactForm() {
86
+ const { claims } = useEasygovHeaderContext();
87
+ const account = claims?.customerAccount;
88
+
89
+ return (
90
+ <form>
91
+ <input defaultValue={claims?.currentUser.firstName ?? ''} />
92
+ <input defaultValue={account?.label ?? ''} />
93
+ {account?.accountType === 'ORGANIZATION' && <input defaultValue={account.uid ?? ''} />}
94
+ </form>
95
+ );
96
+ }
97
+ ```
98
+
99
+ `claims` is `null` until the first response lands and again after logout — render a fallback,
100
+ don't assume it is present on first paint. The hook reflects the last context an `EasygovHeader`
101
+ resolved; it never fetches on its own, and it is not cleared when the header unmounts. Claims
102
+ track the last context that resolved successfully, so if a later refetch fails, they can still
103
+ describe the previously selected account rather than resetting to `null`.
104
+
105
+ Outside React — including `<eg-easygov-header>` — listen for the event instead:
106
+
107
+ ```js
108
+ document.addEventListener('contextchange', (e) => {
109
+ const { contextJwt, claims } = e.detail;
110
+ });
111
+ ```
112
+
113
+ **The claims are decoded, not verified.** Use them for prefilling and rendering. Any backend
114
+ that receives `contextJwt` must verify its signature against core-backend's published JWKS
115
+ before trusting a claim.
116
+
77
117
  ## License
78
118
 
79
119
  MIT
@@ -306,6 +306,107 @@ type AuthStatus = 'initializing' | 'authenticated' | 'renewing' | 'signing-in' |
306
306
  declare function getAuthStatus(): AuthStatus;
307
307
  declare function requireSession(): void;
308
308
 
309
+ type ManagementRoleType = 'SUPER_AGENT' | 'AGENT';
310
+ /**
311
+ * Verification level of a customer account's identity, and — as `ServiceApp.requiredAccountStatus`
312
+ * — the minimum level a service demands. One type for both because the knockout rule compares
313
+ * them to each other.
314
+ */
315
+ type AccountVerificationStatus = 'NOT_VERIFIED' | 'VERIFIED';
316
+ /**
317
+ * Verification of a management-role grant. Same values as `AccountVerificationStatus` today, but
318
+ * a separate type deliberately: it verifies a different thing (the grant, not the identity) and
319
+ * the two are free to diverge on the wire.
320
+ */
321
+ type ManagementRoleStatus = 'NOT_VERIFIED' | 'VERIFIED';
322
+ type ProcessStatus = 'PRELIMINARY' | 'REQUEST_IN_PROGRESS' | 'REQUEST_COMPLETED' | 'SERVICE_PROVISION_PENDING' | 'FINISHED' | 'CANCELED';
323
+ /**
324
+ * The caller's management role on the selected account: SUPER_AGENT (Kontoverwalter/in,
325
+ * unrestricted) or AGENT (Mitwirkende/r, restricted by per-service permissions, which this
326
+ * context does NOT carry).
327
+ * @see HeaderContextClaims for unverified-claims boundary; roleType and status must not drive authorization.
328
+ */
329
+ interface ManagementRole {
330
+ roleType: ManagementRoleType;
331
+ /** Only present for SUPER_AGENT. */
332
+ status?: ManagementRoleStatus;
333
+ }
334
+ interface CustomerAccountBase {
335
+ id: string;
336
+ label: string;
337
+ status: AccountVerificationStatus;
338
+ managementRole: ManagementRole;
339
+ }
340
+ /**
341
+ * A customer account of type ORGANIZATION. Decoded from the context JWT, signature unverified: safe to render, never to authorize.
342
+ */
343
+ interface OrganizationAccount extends CustomerAccountBase {
344
+ accountType: 'ORGANIZATION';
345
+ legalForm: string;
346
+ /** Absent while the organization has no UID register entry. */
347
+ uid?: string;
348
+ }
349
+ /**
350
+ * A customer account of type INDIVIDUAL. Decoded from the context JWT, signature unverified: safe to render, never to authorize.
351
+ */
352
+ interface IndividualAccount extends CustomerAccountBase {
353
+ accountType: 'INDIVIDUAL';
354
+ }
355
+ /**
356
+ * Discriminated on `accountType` — narrow before reading organization-only fields.
357
+ * Decoded from the context JWT, signature unverified: safe to render, never to authorize.
358
+ */
359
+ type SelectedCustomerAccount = OrganizationAccount | IndividualAccount;
360
+ /**
361
+ * The selected Behördengang with its resolved ServiceApp.
362
+ * Decoded from the context JWT, signature unverified: safe to render, never to authorize.
363
+ */
364
+ interface SelectedProcess {
365
+ id: string;
366
+ status: ProcessStatus;
367
+ serviceApp: {
368
+ id: string;
369
+ /** Minimum `CustomerAccount.status` the service requires (knockout). */
370
+ requiredAccountStatus: AccountVerificationStatus;
371
+ individualServicePermissionModel: boolean;
372
+ };
373
+ }
374
+ /**
375
+ * The authenticated user's identity and profile information.
376
+ * Decoded from the context JWT, signature unverified: safe to render, never to authorize.
377
+ */
378
+ interface UserProfile {
379
+ id: string;
380
+ eiamIdentity: string;
381
+ eiamSSOsessionID: string;
382
+ qoA: number;
383
+ emailAddress?: string;
384
+ firstName?: string;
385
+ lastName?: string;
386
+ roles?: string[];
387
+ }
388
+ /**
389
+ * Decoded payload of `contextJwt`. The signature is NOT verified by EasygovHeader — the
390
+ * authenticated HTTPS fetch to core-backend is the trust boundary. Safe for prefilling forms and
391
+ * rendering decisions; never for authorization. A backend receiving this JWT MUST verify it
392
+ * against core-backend's published JWKS.
393
+ */
394
+ interface HeaderContextClaims {
395
+ currentUser: UserProfile;
396
+ customerAccount?: SelectedCustomerAccount;
397
+ process?: SelectedProcess;
398
+ }
399
+
400
+ interface EasygovHeaderContext {
401
+ contextJwt: string | null;
402
+ /**
403
+ * Decoded but NOT signature-verified — see HeaderContextClaims. Null until the first
404
+ * context resolves and again after sign-out; a failed refetch keeps the previously resolved
405
+ * claims.
406
+ */
407
+ claims: HeaderContextClaims | null;
408
+ }
409
+
309
410
  declare const dictionaries: {
310
411
  de: {
311
412
  readonly login: "Anmelden";
@@ -380,6 +481,21 @@ interface EasygovSession {
380
481
  }
381
482
  declare function useEasygovSession(): EasygovSession;
382
483
  declare function useEasygovAuthStatus(): AuthStatus;
484
+ /**
485
+ * The header's current context: the raw `contextJwt` and its decoded claims. Reflects the last
486
+ * context an EasygovHeader resolved — the hook never fetches on its own, returns empty values
487
+ * until that first resolution, and is NOT cleared when the header unmounts.
488
+ *
489
+ * Claims track the last context that resolved successfully: if a *re*fetch fails (non-2xx,
490
+ * network error), the previous claims stay in place rather than resetting to null, so they can
491
+ * still describe a previously selected account after the URL has moved on. Only sign-out (or a
492
+ * failed *first* fetch) leaves `claims` at `null`.
493
+ *
494
+ * IMPORTANT — trust boundary: `claims` is decoded, NOT signature-verified. Use it to prefill
495
+ * forms and drive rendering; never to authorize. A backend receiving `contextJwt` MUST verify it
496
+ * against core-backend's published JWKS.
497
+ */
498
+ declare function useEasygovHeaderContext(): EasygovHeaderContext;
383
499
  interface EasygovHeaderProps {
384
500
  readonly locale?: Locale;
385
501
  readonly onLanguageChange?: (locale: Locale) => void;
@@ -387,7 +503,16 @@ interface EasygovHeaderProps {
387
503
  /**
388
504
  * Called whenever a fresh `contextJwt` is obtained from core-backend's
389
505
  * `header-context` endpoint (mirrored by the `contextchange` CustomEvent
390
- * dispatched on the header element).
506
+ * dispatched on the header element), together with its decoded `claims`.
507
+ *
508
+ * Both are always present when this fires: a payload that fails to decode never produces a
509
+ * `contextJwt`, so a decode failure suppresses the call rather than delivering null claims.
510
+ *
511
+ * This is the push surface — the only one a `<eg-easygov-header>` consumer has, since function
512
+ * props cannot cross the custom-element boundary. React consumers can read the same pair
513
+ * reactively with `useEasygovHeaderContext()`, which re-renders them when the context changes;
514
+ * there `claims` IS nullable, because the store is empty before the first context resolves and
515
+ * again after sign-out.
391
516
  *
392
517
  * IMPORTANT — trust boundary: this JWT's signature is NOT verified by
393
518
  * EasygovHeader. The authenticated HTTPS fetch to core-backend is the
@@ -396,7 +521,7 @@ interface EasygovHeaderProps {
396
521
  * against core-backend's published JWKS before trusting any claim
397
522
  * decoded from it.
398
523
  */
399
- readonly onContextChange?: (contextJwt: string) => void;
524
+ readonly onContextChange?: (contextJwt: string, claims: HeaderContextClaims) => void;
400
525
  readonly onLogout?: () => void;
401
526
  }
402
527
  declare function EasygovHeader({ locale: localeProp, onLanguageChange, onNavigate, onContextChange, onLogout, }: EasygovHeaderProps): React__default.JSX.Element;
@@ -413,27 +538,4 @@ interface RequireCustomerAccountProps {
413
538
  }
414
539
  declare function RequireCustomerAccount({ children, onNavigate, }: RequireCustomerAccountProps): React__default.JSX.Element;
415
540
 
416
- interface UserProfile {
417
- id: string;
418
- emailAddress?: string | null;
419
- firstName?: string | null;
420
- lastName?: string | null;
421
- language: 'DE' | 'FR' | 'IT' | 'EN';
422
- eiamSSOsessionID: string;
423
- roles?: string[];
424
- }
425
- interface SelectedCustomerAccount {
426
- id: string;
427
- label: string;
428
- }
429
- /**
430
- * Decoded (not verified — see useHeaderContext) payload of `contextJwt`. Mirrors
431
- * core-backend's CoreContext claims plus the standard iss/iat/exp claims, which
432
- * EasygovHeader itself does not read.
433
- */
434
- interface HeaderContextClaims {
435
- currentUser: UserProfile;
436
- customerAccount?: SelectedCustomerAccount;
437
- }
438
-
439
- export { type AuthStatus, BrandBase, BrandEasygov, BrandFacebook, BrandKdk, BrandLinkedin, type BrandProps, BrandSeg, BrandSgv, BrandSsv, BrandSwiss, BrandXing, BrandYoutube, Button, type ButtonProps, type DesktopSpan, Dropdown, DropdownChip, DropdownChipsOverflow, type DropdownChipsOverflowProps, DropdownChipsTrigger, type DropdownChipsTriggerProps, DropdownCollection, DropdownEmpty, DropdownGroup, DropdownGroupLabel, DropdownHelperText, type DropdownHelperTextProps, DropdownInput, DropdownItem, type DropdownItemProps, DropdownItemText, DropdownLabel, type DropdownLabelProps, DropdownList, DropdownPopup, type DropdownPopupProps, DropdownSeparator, DropdownToggle, DropdownTrigger, type DropdownTriggerProps, DropdownValue, EasygovFooter, EasygovHeader, type EasygovHeaderProps, type EasygovSession, Grid, GridItem, type GridItemProps, type GridProps, type HeaderContextClaims, type UserProfile as HeaderContextUserProfile, Heading, type HeadingProps, Input, type InputProps, type Locale, type MobileSpan, NavigationMenu, NavigationMenuAnchor, type NavigationMenuAnchorProps, NavigationMenuContent, type NavigationMenuContentProps, NavigationMenuItem, type NavigationMenuItemProps, NavigationMenuLink, type NavigationMenuLinkProps, NavigationMenuList, type NavigationMenuListProps, type NavigationMenuProps, NavigationMenuTrigger, type NavigationMenuTriggerProps, RequireCustomerAccount, type RequireCustomerAccountProps, RequireSession, type RequireSessionProps, Select, SelectContent, type SelectContentProps, SelectGroup, SelectGroupLabel, type SelectGroupLabelProps, SelectHelperText, type SelectHelperTextProps, SelectItem, type SelectItemProps, SelectLabel, type SelectLabelProps, SelectSeparator, type SelectSeparatorProps, SelectTrigger, type SelectTriggerProps, type SelectedCustomerAccount, type SessionUser, type TabletSpan, Text, TextLink, type TextLinkProps, type TextProps, buttonVariants, getAuthStatus, headingVariants, requireSession, textLinkVariants, textVariants, useEasygovAuthStatus, useEasygovSession, useRequireCustomerAccount };
541
+ export { type AuthStatus, BrandBase, BrandEasygov, BrandFacebook, BrandKdk, BrandLinkedin, type BrandProps, BrandSeg, BrandSgv, BrandSsv, BrandSwiss, BrandXing, BrandYoutube, Button, type ButtonProps, type DesktopSpan, Dropdown, DropdownChip, DropdownChipsOverflow, type DropdownChipsOverflowProps, DropdownChipsTrigger, type DropdownChipsTriggerProps, DropdownCollection, DropdownEmpty, DropdownGroup, DropdownGroupLabel, DropdownHelperText, type DropdownHelperTextProps, DropdownInput, DropdownItem, type DropdownItemProps, DropdownItemText, DropdownLabel, type DropdownLabelProps, DropdownList, DropdownPopup, type DropdownPopupProps, DropdownSeparator, DropdownToggle, DropdownTrigger, type DropdownTriggerProps, DropdownValue, EasygovFooter, EasygovHeader, type EasygovHeaderContext, type EasygovHeaderProps, type EasygovSession, Grid, GridItem, type GridItemProps, type GridProps, type AccountVerificationStatus as HeaderContextAccountVerificationStatus, type HeaderContextClaims, type IndividualAccount as HeaderContextIndividualAccount, type ManagementRole as HeaderContextManagementRole, type ManagementRoleStatus as HeaderContextManagementRoleStatus, type ManagementRoleType as HeaderContextManagementRoleType, type OrganizationAccount as HeaderContextOrganizationAccount, type ProcessStatus as HeaderContextProcessStatus, type UserProfile as HeaderContextUserProfile, Heading, type HeadingProps, Input, type InputProps, type Locale, type MobileSpan, NavigationMenu, NavigationMenuAnchor, type NavigationMenuAnchorProps, NavigationMenuContent, type NavigationMenuContentProps, NavigationMenuItem, type NavigationMenuItemProps, NavigationMenuLink, type NavigationMenuLinkProps, NavigationMenuList, type NavigationMenuListProps, type NavigationMenuProps, NavigationMenuTrigger, type NavigationMenuTriggerProps, RequireCustomerAccount, type RequireCustomerAccountProps, RequireSession, type RequireSessionProps, Select, SelectContent, type SelectContentProps, SelectGroup, SelectGroupLabel, type SelectGroupLabelProps, SelectHelperText, type SelectHelperTextProps, SelectItem, type SelectItemProps, SelectLabel, type SelectLabelProps, SelectSeparator, type SelectSeparatorProps, SelectTrigger, type SelectTriggerProps, type SelectedCustomerAccount, type SelectedProcess, type SessionUser, type TabletSpan, Text, TextLink, type TextLinkProps, type TextProps, buttonVariants, getAuthStatus, headingVariants, requireSession, textLinkVariants, textVariants, useEasygovAuthStatus, useEasygovHeaderContext, useEasygovSession, useRequireCustomerAccount };