Files
marketplaces/docs/PACKAGES-USAGE.md
sdarbinyan f6045a07b2
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
docs: backend handoff, package usage guide, finalized CI/CD
- 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>
2026-08-18 01:46:58 +04:00

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

  1. Clone vitanovaPackages.
  2. Edit under packages/auth/src (or packages/payment/src), export from index.ts.
  3. npx changeset at the repo root — pick the package and bump type, write one line about the change.
  4. Commit, push, open a PR to main.
  5. 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.)
  6. 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.