docs: backend handoff, package usage guide, finalized CI/CD
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
- 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>
This commit is contained in:
136
docs/PACKAGES-USAGE.md
Normal file
136
docs/PACKAGES-USAGE.md
Normal file
@@ -0,0 +1,136 @@
|
||||
# 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](context/adrs/ADR-0001-extract-auth-and-payment-into-shared-marketplaces-packages.md); for how they are built and released see [PACKAGE-EXTRACTION.md](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:
|
||||
|
||||
```bash
|
||||
ssh -L 4873:127.0.0.1:4873 seto@213.21.246.138
|
||||
```
|
||||
|
||||
```bash
|
||||
npm login --registry=http://127.0.0.1:4873/ --scope=@marketplaces
|
||||
```
|
||||
|
||||
Then install normally:
|
||||
|
||||
```bash
|
||||
npm install @marketplaces/auth
|
||||
```
|
||||
|
||||
Versions are pinned exactly (`"@marketplaces/auth": "0.1.0"`, no `^`/`~`) — see [ADR-0001](context/adrs/ADR-0001-extract-auth-and-payment-into-shared-marketplaces-packages.md) 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`:
|
||||
|
||||
```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:
|
||||
|
||||
```ts
|
||||
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:
|
||||
|
||||
```ts
|
||||
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](backend/TRACK-S-SECURITY-RBAC-CONTRACT.md).
|
||||
|
||||
### `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`:
|
||||
|
||||
```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](backend/PHASE-1-MONEY-FX-PAYMENTS-CONTRACT.md) and [Phase 7](backend/PHASE-7-PAYMENTS-RECONCILIATION-CONTRACT.md)); the eventual package is a thin client for FX/pricing/checkout gateways.
|
||||
|
||||
## 6. Making a change to a package
|
||||
|
||||
1. Clone [vitanovaPackages](https://sources.vitanova.network/sdarbinyan/vitanovaPackages.git).
|
||||
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](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.
|
||||
Reference in New Issue
Block a user