Skip to content

Account & Auth

Demo: 513b7de (Jul 16, 2026)Prod: 513b7de (Jul 16, 2026)

The single HDS app that owns authentication (sign-in, registration, password reset, MFA) and account management (profile, preferences, security, connected apps, data rights, audit). It is a fork of Pryv’s app-web-user-account and is the successor to the retired app-web-auth3-hds — one deployment serves both account hostnames on clean paths (no .html).

Your app does not build any of this itself. It links into the account app for the ceremony and the account pages, and reads account-level preferences through hds-lib.

PathPurpose
/signinSign-in (username or email + password)
/registerAccount creation
/reset-password, /change-passwordPassword recovery / change
/mfa-challengeInline MFA
/auth, /oauth2-authorizeThe Pryv auth ceremony / OAuth2 entry
/account/{profile,preferences,security,apps,data,audit-access/:id}Account management pages
/cmc-accept (alias /cmc/approve), /cmc-scope-updateCMC consent hand-offs (below)

A custom HDS app should build none of register / reset / change-password / profile / preferences — link to the account app instead.

Send the user to an account path and pass these optional query params (all carried across the app’s internal /signin bounce, so a signed-out deep-link keeps them):

  • username — a sign-in hint; the calling app usually already knows who the user is, so the sign-in form pre-fills it.
  • backUrl + backLabel — render a ← Back to {backLabel} affordance that returns the user to your app when they’re done.
  • pryvServiceInfoUrloptional and normally omitted. Each deployment already knows the Pryv platform it serves (from a settings.json served next to the bundle). The param stays authoritative when present (the auth ceremony passes it so one deployment can serve a caller on another platform), but apps linking in are not expected to send it.

Settings vs. profile — where preferences live

Section titled “Settings vs. profile — where preferences live”

Two distinct stores, both via hds-lib:

  • HDSSettingsper-app. Bound to an app’s baseStream, so each app gets its own private copy (e.g. theme, which is legitimately per-app).
  • HDSProfileaccount-level, shared across every HDS app. Holds identity (displayName, avatar, …) and the account preferences every app should agree on: preferredLocales, timezone, dateFormat, unitSystem. These live on the account’s own profile-preferences stream, which is what makes them interoperable. The account app owns writing them.

Read a shared preference through resolveAccountPreference(key) (from hds-lib), never by reading one store directly. Its precedence is: a genuinely stored profile value → the legacy per-app HDSSettings value → the profile default (incl. browser inference). This is what lets an app honour an account-level choice while still working for users who only ever set a per-app value.

A custom app needs no CMC consent UI of its own. /cmc-accept is the canonical path a requester points @pryv/cmc.requestAccept at; CmcApprove is app-agnostic and drives sign-in + consent + accept/decline.

Requester recipe:

import { requestAccept } from '@pryv/cmc';
requestAccept({
authUrl: 'https://account.hds.ngo/cmc-accept', // the account app owns the consent UI
capabilityUrl, // the offer to read (anonymous readOffer resolves it)
scopeStreamId, // optional — the stream scope being granted
pryvApi,
accessName, // optional — a label for the created access
returnUrl, // optional — where to send the result in redirect mode
mode, // 'popup' (default) | 'redirect'
});

Result return: the account app returns { ok, dataGrantApiEndpoint?, acceptEventId?, reason? }

  • popup mode: window.opener.postMessage({ type: 'cmc-accept-result', ...res });
  • redirect mode: navigates to returnUrl with ?cmcAcceptResult=<json>.

/cmc-scope-update mirrors the same shape for updating an existing grant’s scope.

Scope note (accept vs. revoke): /cmc-accept is accept/decline of a pending offer, and /cmc-scope-update updates a grant’s scope. Revoking an existing grant is a different action and lives in the account UI at /account/audit-access (accesses.delete), not on these hand-off routes.

Security properties to know (all inherent to @pryv/cmc, not HDS choices): in redirect mode the result carries a token-bearing dataGrantApiEndpoint on the URL query; popup mode posts with targetOrigin: '*' (there is no trustworthy requester web origin to pin); upstream’s requestAccept listener does not validate ev.origin. The requester is the intended recipient of the grant, so these are by design — but do not log or forward the result URL.

  • React 19 + TypeScript + Vite + Tailwind + React Router 7.
  • Pryv is reached only through hds-lib (never a direct pryv import).
  • Published as a static SPA on GitHub Pages; each deployment writes its own settings.json (dev/demo vs. production platform).