feat: extract auth into @marketplaces/auth package, add backoffice admin provisioning spec

- ADR-0001: decision to extract auth/payment into shared @marketplaces/* packages
- Scaffold packages/auth, packages/payment; @marketplaces/auth now holds the real
  telegram (customer+admin QR/session) and ed25519 (future admin challenge/response)
  auth implementation, pushed to sources.vitanova.network/sdarbinyan/vitanovaPackages
- Rewire ~30 call sites to import from @marketplaces/auth; delete migrated originals
  from core/auth, core/admin-auth, services/, models/
- Replace environment coupling with AUTH_API_URL/TELEGRAM_BOT_USERNAME injection
  tokens and isDevMode(); wired as file:packages/auth pending registry publish
- Add TRACK-S §8: bootstrap per-marketplace admin login + marketplace-scoped
  sub-admin invite/role endpoints
- Build, arch:check:boundaries, and full test suite (103/103) all green

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
sdarbinyan
2026-08-18 01:05:16 +04:00
parent 23060261c7
commit 14c72d1a6a
62 changed files with 420 additions and 127 deletions

View File

@@ -0,0 +1,50 @@
# @marketplaces/auth & @marketplaces/payment — build, version, publish, consume
See [ADR-0001](context/adrs/ADR-0001-extract-auth-and-payment-into-shared-marketplaces-packages.md) for why. This doc is the how.
## Current state
[packages/](../packages) here is now just a reference copy (`package.json`/`tsconfig.json`, no CI) — the real source, CI, and versioning live in the pushed [vitanovaPackages](https://sources.vitanova.network/sdarbinyan/vitanovaPackages.git) repo (§1). No app code has moved yet — `src/app/core/auth`, `src/app/core/admin-auth`, `src/app/core/finance`, `src/app/core/pricing` are still the live implementation in `marketplaces`.
## 1. Target repo
Pushed: [sources.vitanova.network/sdarbinyan/vitanovaPackages](https://sources.vitanova.network/sdarbinyan/vitanovaPackages.git) — single monorepo (npm workspaces) hosting both `packages/auth` and `packages/payment`, `main` branch, initial scaffold commit `5567154`. The `packages/*` scaffold here in `marketplaces` stays as the pre-push staging copy; the pushed repo is now the source of truth for the package source itself.
## 2. Versioning
Both packages live in one monorepo (vitanovaPackages, npm workspaces), so versioning uses [Changesets](https://github.com/changesets/changesets), not per-package semantic-release — Changesets is built for exactly this "many packages, one repo, independent versions" shape. A PR that changes `packages/auth` adds a changeset file (`npx changeset` from repo root, picks package + bump type + writes a short description) alongside the code change.
## 3. Publishing (CI)
`vitanovaPackages/.github/workflows/release.yml`: on push to `main`, installs, builds, tests, then `changesets/action`:
- if unreleased changesets exist, opens/updates a "Version Packages" PR that bumps `package.json` versions and writes changelogs,
- once that PR is merged, the next push to `main` publishes the bumped package(s) to the registry.
Requires two repo secrets: `NPM_TOKEN` (publish token) and `GITHUB_TOKEN` (auto-provided on GitHub Actions; use the Gitea/Forgejo equivalent if this host isn't GitHub-Actions-native — check with whoever administers `sources.vitanova.network`).
Registry choice — pick one before first publish:
- **npm private scope** (`@marketplaces` org on npmjs.com) — simplest, works with the workflow as-is.
- **GitHub Packages** — swap `registry-url` in the workflow to `https://npm.pkg.github.com`.
- **Self-hosted (Verdaccio) on the dev server** — point `registry-url` at the server's registry endpoint; requires the registry to be stood up on `213.21.246.138` first (not done yet).
## 4. Consuming from `marketplaces` (and other projects)
Once published:
```bash
npm install @marketplaces/auth @marketplaces/payment
```
```ts
import { ... } from '@marketplaces/auth';
```
Pin exact versions (no `^`/`~` ranges) per ADR-0001's consequence about registry-outage blast radius — bump deliberately, not automatically, on this side.
[renovate.json](../renovate.json) at repo root opens a grouped PR whenever either package publishes a new version — review and merge it manually (`automerge: false`), it does not land unattended.
## 5. Migration cutover
**Auth: done.** `@marketplaces/auth` now holds the real implementation — two independent modules, `telegram/` (live Telegram QR/session auth, customer + admin) and `ed25519/` (future challenge/response admin auth, backend not shipped). Environment coupling was replaced with `AUTH_API_URL`/`TELEGRAM_BOT_USERNAME` injection tokens, provided from `app.config.ts`; `environment.production` became Angular's `isDevMode()`. `AdminPermissionsService` and `requireAdminPermission` stayed in `marketplaces` (`core/admin-auth/`) since they read this app's mock Users domain, not a portable auth concern. All ~30 call sites now import `@marketplaces/auth`; the old `src/app/core/auth`, `src/app/core/admin-auth/admin-auth.service.ts` (+ interceptor, ed25519 files), `src/app/services/auth.service.ts`, `src/app/services/telegram-session-api.service.ts`, and `src/app/models/auth.model.ts`/`admin-auth.model.ts` are deleted. `npm run build`, `npm run arch:check:boundaries`, and `npm test` (103/103) all pass. Wired as a `file:packages/auth` dependency until the registry (§3) is live — swap to a real semver range once published.
**Payment: not started.** `core/finance`/`core/pricing` still live in `marketplaces`, same process as above once prioritized.

View File

@@ -75,7 +75,35 @@ Required before: bank/payment detail changes (Phase 5 §5), production launch (P
Customer/seller PII is exposed only to roles that need it for their scope (e.g. `FINANCE_VIEWER` sees payout totals, not raw bank account numbers unless `FINANCE_MANAGER`+). Export endpoints (`GET .../export`) are themselves audit-logged actions per §3.
## 8. What the frontend will start doing once this ships
## 8. Initial admin provisioning & self-service admin management
Each marketplace ships with one bootstrap `MARKETPLACE_ADMIN` account, seeded at provisioning time (Phase 9 launch step):
- `login` = marketplace slug (`projectName`)
- `password` = `{projectName}2026$`, flagged `mustChangePassword: true`
- Login succeeds but every non-auth request 403s with `PASSWORD_CHANGE_REQUIRED` until password is changed.
```
POST /api/identity/v1/session/change-password { currentPassword, newPassword }
```
A `MARKETPLACE_ADMIN` can then provision sub-admins scoped to their own marketplace only — mirrors the seller-team invite pattern in [Phase 5](PHASE-5-SELLER-PORTAL-CONTRACT.md) (`POST /api/seller/v1/team/invite`):
```
POST /api/admin/v2/team/invite { email, role: MarketplaceRole, marketplaceId }
GET /api/admin/v2/team?marketplaceId=
PATCH /api/admin/v2/team/{userId} { role }
DELETE /api/admin/v2/team/{userId}
```
Invariants:
- `role` must be one of the `MarketplaceRole` set (§1) — never `PlatformRole`. Backend rejects any attempt to grant a platform-scope role through this endpoint (`403 SCOPE_ESCALATION_DENIED`).
- `marketplaceId` is forced server-side to the caller's own tenant scope — request body value is ignored/validated, never trusted.
- Every invite/role-change/removal is an audit-logged action (§3, `action: 'admin_team.invited' | 'admin_team.role_changed' | 'admin_team.removed'`).
- Role grants at `MARKETPLACE_ADMIN` level require step-up auth (§6).
- Invited admins get their own credentials (email + set-password flow), not the shared bootstrap login — the bootstrap account is for first login only and should be rotated/retired once real admins exist.
## 9. What the frontend will start doing once this ships
- Route guards and action-level permission checks across the entire backoffice — currently none exist.
- Backoffice **Audit & Security** section (missing from admin nav today): role changes, sensitive actions, login/security events, exports.

View File

@@ -0,0 +1,38 @@
---
id: ADR-0001
title: Extract auth and payment into shared @marketplaces packages
status: active
date: 2026-08-17
supersedes: []
tags: [architecture, auth, payment, monorepo]
---
# ADR-0001: Extract auth and payment into shared @marketplaces packages
## Context
`marketplaces` currently owns auth end-to-end: customer auth (`core/auth` — VK ID, OTP, session, facade), admin auth (`core/admin-auth` — ed25519-verified admin sessions, permission guards, interceptor), and a legacy `services/auth.service.ts`. Payment/finance logic (`core/finance`, `core/pricing`) is server-owned per [Phase 1](../../backend/PHASE-1-MONEY-FX-PAYMENTS-CONTRACT.md) and [Phase 7](../../backend/PHASE-7-PAYMENTS-RECONCILIATION-CONTRACT.md) contracts — the frontend piece is thin (gateways/tokens, no business logic).
Multiple marketplace projects beyond this repo need the same auth and payment client logic. Duplicating it per-project drifts fast (auth bugs get fixed in one place, not others) and blocks a consistent security posture across projects — directly relevant to [TRACK-S-SECURITY-RBAC-CONTRACT.md](../../backend/TRACK-S-SECURITY-RBAC-CONTRACT.md), which already treats auth/RBAC as the single most serious cross-cutting concern.
## Decision
Extract auth and payment client logic into two standalone, independently versioned npm packages:
- `@marketplaces/auth` — customer auth (VK ID/OTP/session), admin auth (ed25519 verification, permission guards, interceptors), token/session management.
- `@marketplaces/payment` — payment/finance client gateways, FX/pricing models, checkout client contracts (thin — business logic stays backend per Phase 1/7).
Each package:
1. Lives in its own git repo (handed over separately; this repo does not host it long-term).
2. Is consumed by `marketplaces` (and other projects) as an installed node_modules dependency — imported, never copy-pasted.
3. Is versioned with semver; CI on the package repo auto-bumps and publishes on push to `main`, driven by conventional commit prefixes already used in this repo (`feat:`/`fix:`/etc — semantic-release reads these directly).
4. Ships with its own test suite; `marketplaces` treats it as a black-box dependency, not source to edit in place.
Rollout order: scaffold packages and CI in this repo first (reversible, local-only) → hand over target git repo → publish → migrate `marketplaces` call sites to import from the package → delete the in-repo originals only after the app builds and passes tests against the package.
## Consequences
- `marketplaces` loses direct edit access to auth/payment source — changes go through the package's own repo/PR/release cycle. Slower iteration, but consistent behavior across all consuming projects.
- ~30 call sites in `marketplaces` (see `core/auth`, `core/admin-auth`, `services/auth.service.ts`, interceptors) need import rewiring during migration — tracked as follow-up work, not done in this ADR.
- New failure mode: package registry/CI outage blocks `marketplaces` builds if a version bump lands mid-incident. Pin exact versions, do not use floating ranges, to keep this bounded.
- [TRACK-S-SECURITY-RBAC-CONTRACT.md](../../backend/TRACK-S-SECURITY-RBAC-CONTRACT.md) §8 (admin provisioning) becomes package-owned behavior once migrated — that doc's endpoint contracts stay backend-side and unaffected, only the frontend client implementation moves.

View File

@@ -4,3 +4,4 @@
{"id":"PV-20260713T000000Z-0004","subject":"translatable-fields","predicate":"must-be-modeled-as","object":"generic translations.{lang} map so adding/removing a language automatically exposes/removes translation fields across all translatable objects","src":["docs/context/adrs/ADR-0001-marketplace-platform-vision.md"],"status":"active","kind":"constraint","updated_at":"2026-07-13T00:00:00Z","confidence":"high","tags":["i18n","constraint"]}
{"id":"PV-20260713T000000Z-0005","subject":"admin-app","predicate":"is-isolated-from","object":"marketplace storefront bundle: admin code never ships to storefront and vice versa, though they may share a domain","src":["docs/context/adrs/ADR-0001-marketplace-platform-vision.md"],"status":"active","kind":"constraint","updated_at":"2026-07-13T00:00:00Z","confidence":"high","tags":["admin","security"]}
{"id":"PV-20260713T000000Z-0006","subject":"widgets","predicate":"must-not-own","object":"page spacing or page width; the renderer owns sections, spacing, and page width, widgets own only their internal layout","src":["docs/context/adrs/ADR-0001-marketplace-platform-vision.md"],"status":"active","kind":"constraint","updated_at":"2026-07-13T00:00:00Z","confidence":"high","tags":["widgets","layout"]}
{"id":"PV-20260818T001500Z-a1f3","subject":"auth-and-payment-client-logic","predicate":"is-decided-to-extract-into","object":"standalone versioned npm packages @marketplaces/auth and @marketplaces/payment, installed as dependencies rather than edited in-repo","src":["docs/context/adrs/ADR-0001-extract-auth-and-payment-into-shared-marketplaces-packages.md"],"status":"active","kind":"decision","updated_at":"2026-08-18T00:15:00Z","confidence":"high","tags":["architecture","auth","payment","decision"]}