- docs/backend/BACKEND-HANDOFF.md: single entry point for a backend dev - reading order, verified infrastructure state (nginx running, Postgres inactive, no API on :8080, no TLS, no DNS automation, no CI runner), auth surface, and the day-one setup that is still outstanding - docs/PACKAGES-USAGE.md: install, required DI providers, full exported API for both auth mechanisms, and how to ship a package change - PACKAGE-EXTRACTION.md now covers build/release/infra only and points at the usage guide; CI section reflects the two real workflows Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
7.8 KiB
Using @marketplaces/auth and @marketplaces/payment
How to install and consume the shared packages in marketplaces or any other project. For why they exist see ADR-0001; for how they are built and released see PACKAGE-EXTRACTION.md.
1. Install
Both packages live on a private Verdaccio registry on the dev server, not npmjs. Two things are needed: a scope mapping and an auth token.
Scope mapping goes in the project's .npmrc (already committed in marketplaces):
@marketplaces:registry=http://127.0.0.1:4873/
The token is per-developer and never committed. Open a tunnel to the registry, then log in once:
ssh -L 4873:127.0.0.1:4873 seto@213.21.246.138
npm login --registry=http://127.0.0.1:4873/ --scope=@marketplaces
Then install normally:
npm install @marketplaces/auth
Versions are pinned exactly ("@marketplaces/auth": "0.1.0", no ^/~) — see ADR-0001 on registry-outage blast radius.
2. Required providers
@marketplaces/auth has no knowledge of any specific app's environment config. It reads two injection tokens, both provided by the consuming app in app.config.ts:
import { AUTH_API_URL, TELEGRAM_BOT_USERNAME } from '@marketplaces/auth';
import { environment } from '../environments/environment';
export const appConfig: ApplicationConfig = {
providers: [
{ provide: AUTH_API_URL, useValue: environment.authApiUrl },
{ provide: TELEGRAM_BOT_USERNAME, useValue: environment.telegramBot },
// ...
]
};
| Token | Required | Meaning |
|---|---|---|
AUTH_API_URL |
yes | Base URL of the auth backend, e.g. https://api.example.com. Both auth mechanisms build their endpoints from this. |
TELEGRAM_BOT_USERNAME |
no | Bot username for QR/deep-link login URLs. Falls back to a default if absent. |
Missing AUTH_API_URL produces NG0201: No provider found for InjectionToken @marketplaces/auth AUTH_API_URL at the first injection — including in unit tests, where any TestBed that constructs a component touching auth must provide it:
TestBed.configureTestingModule({
providers: [{ provide: AUTH_API_URL, useValue: 'https://test.local' }],
});
3. What is in the package
Two independent auth mechanisms. They deliberately share no state — a customer QR scan never authenticates an admin session or vice versa (distinct cookies, signals, guards, interceptors).
telegram/ — live today
Telegram QR/session auth against {AUTH_API_URL}/users/sessions. One backend endpoint set, used by both customer and admin login; only storage differs.
| Export | What it is |
|---|---|
AuthService |
Customer session. Signals: session, status, isAuthenticated, showLoginDialog, displayName. Methods: checkSession(), createWebSession(), requestLogin(), hideLogin(), logout(), onTelegramLoginComplete(), getTelegramAppLoginUrl(). Cookie webSessionID, SameSite=Lax. |
AdminAuthService |
Admin session. Same signal/method shape plus getAdminToken()/setAdminTokens()/clearAdminTokens() (reserved for when the backend issues admin JWTs) and devBypassLogin() (no-ops outside dev mode). Cookie adminSessionID, SameSite=Strict. |
TelegramSessionApiService |
Thin HTTP client + response normalization. Holds no state, writes no cookies. |
adminAuthGuard |
CanActivateFn — allows if the admin session is authenticated, otherwise opens the login dialog. |
adminAuthHeadersInterceptor |
Attaches AdminWebSessionID (and Authorization: Bearer when a token exists) to admin-gated paths only (/admin/, /backoffice/, /builder/, /media/). Never touches customer requests. |
AuthSession, WebSessionStart, AuthStatus, AdminAuthStatus |
Wire/state types. |
Typical usage:
import { AuthService, AdminAuthService, adminAuthGuard, adminAuthHeadersInterceptor } from '@marketplaces/auth';
// routes
{ path: 'backoffice', canActivate: [adminAuthGuard], loadComponent: ... }
// http
provideHttpClient(withInterceptors([adminAuthHeadersInterceptor, ...]))
// component
private readonly auth = inject(AuthService);
readonly isLoggedIn = this.auth.isAuthenticated; // signal
Security note: the Telegram session API has no concept of "admin." The frontend cannot distinguish an admin Telegram session from a regular one — it only decides where to store the result. Real admin authorization must be enforced server-side on every admin request. See TRACK-S.
ed25519/ — prepared, backend not shipped
Challenge/response admin auth: GET /api/admin/auth/challenge → sign nonce with a device-local non-extractable Ed25519 key → POST /api/admin/auth/verify → JWT pair. Calling these today 404s/connection-errors, which surfaces as the backend-unavailable error screen. Nothing is mocked.
| Export | What it is |
|---|---|
AuthFacade |
The surface components should use. isAuthenticated, status, role, loginPhase, lastError; login(redirectTo?), logout(redirectTo?), restoreSession(), can(permission). |
Ed25519AuthService |
Low-level flow orchestrator (exported under this name so it doesn't collide with the telegram AuthService). |
SessionService |
JWT/refresh pair + derived claims, auto-refresh before expiry. |
Ed25519KeypairService |
WebCrypto Ed25519 keypair in IndexedDB. Private key is non-extractable and never leaves the device. |
PermissionService |
Derives permissions from the JWT role claim. UI-only gate. |
JwtService |
Decode only, never verification — the frontend has no trusted key; signature checking is the backend's job on every request. |
Ed25519VerificationService / NoopEd25519VerificationService |
Abstract seam + fail-closed default binding. |
AdminRole, Permission, ROLE_PERMISSIONS, AuthChallenge, AuthTokenPair, JwtClaims, AuthError, AuthErrorCode, … |
Types and wire contracts. |
Bind the verification seam in app.config.ts:
{ provide: Ed25519VerificationService, useClass: NoopEd25519VerificationService },
4. What deliberately stayed in the app
AdminPermissionsService and requireAdminPermission live in marketplaces (src/app/core/admin-auth/), not in the package. They read this app's mock Users domain to derive a permission set — app-specific, not a portable auth concern. If another project needs permission gating it should use the package's PermissionService (JWT-claim-driven) instead.
5. @marketplaces/payment
Published at 0.1.0 but scaffold only — no implementation yet, nothing exported, and marketplaces does not depend on it. core/finance and core/pricing still live in the app. Payment business logic is server-side by design (see Phase 1 and Phase 7); the eventual package is a thin client for FX/pricing/checkout gateways.
6. Making a change to a package
- Clone vitanovaPackages.
- Edit under
packages/auth/src(orpackages/payment/src), export fromindex.ts. npx changesetat the repo root — pick the package and bump type, write one line about the change.- Commit, push, open a PR to
main. - On merge, CI opens a version-bump PR; merging that publishes the new version. (Currently blocked — see PACKAGE-EXTRACTION.md §2/§4 for the registry-reachability follow-up. Until then, publish manually through the tunnel.)
- In
marketplaces, bump the pinned version and run the build + test suite before merging.
Do not edit node_modules/@marketplaces/* directly — it is overwritten on every install.