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:
50
docs/PACKAGE-EXTRACTION.md
Normal file
50
docs/PACKAGE-EXTRACTION.md
Normal 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.
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
@@ -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"]}
|
||||
|
||||
Reference in New Issue
Block a user