# Registration Service ASP.NET Core (.NET 8) service for managing prospect registration in AdPlatform. Self-hosted via docker-compose as `registration:8080` behind nginx / Gateway. ## Architecture ``` Client SPA (client.positivespend.com) └─► Gateway (portal.positivespend.com) └─► Registration API (regapi.positivespend.com → registration:8080) ├─► dbRegistration (10.10.99.212 — Registrations table, spRegistration) └─► CIAM (PositiveSpendClients.ciamlogin.com — token validation) Management API (mgmt.positivespend.com) └─► Registration API (admin endpoints, x-functions-key auth) └─► dbRegistration ``` The Registration service never touches `dbAdPlatform`. Management never touches `dbRegistration`. Approval provisioning into `dbAdPlatform` is the Management API's responsibility, called after the `complete` action marks a registration as Approved. ## Endpoints | Method | Route | Auth | Description | |--------|-------|------|-------------| | `POST` | `/api/registration/register` | Bearer (CIAM JWT) | New prospect signup | | `GET` | `/api/registration/pending` | x-functions-key | List pending applicants | | `GET` | `/api/registration/item/{id}` | x-functions-key | Get single applicant | | `POST` | `/api/registration/action/{id}/reject` | x-functions-key | Reject applicant | | `POST` | `/api/registration/action/{id}/complete` | x-functions-key | Mark approved | | `GET` | `/api/health` | Anonymous | Health check | `entraSubjectId` is **never** read from the request body on `/register` — it is extracted from the validated CIAM Bearer token (OID claim). The client cannot spoof it. ## Authentication **Client-facing endpoint** (`/register`): Bearer token issued by `PositiveSpendClients.ciamlogin.com` (tenant `cbf8b7d7`). Validated by `Microsoft.Identity.Web` against the CIAM tenant. The Client SPA acquires this token via MSAL after the user signs in with Google, Apple, or Microsoft. **Admin endpoints** (`pending` / `item` / `reject` / `complete`): `x-functions-key` header validated by `ApiKeyAuthFilter`. Key must match `Registration__FunctionKey` env var. These endpoints are called by the Management API, not the browser. ## CIAM Tenant Reference | Field | Value | |-------|-------| | Tenant | Positive Spend Clients | | Tenant ID | `cbf8b7d7-1e13-486d-b5b0-287ba79fdf0b` | | Client SPA App ID | `c426967f-bfcc-46af-b4e5-d69dc01cbf75` | | Authority | `https://PositiveSpendClients.ciamlogin.com/cbf8b7d7.../` | This is the **client-facing CIAM tenant** — separate from the internal `positivespend.com` org tenant (`f56a3c51`) used by Management and the Tech/Admin consoles. ## Mock Mode Switch in `Program.cs` to run without a database (seeds 4 test applicants in memory): ```csharp services.AddSingleton(); ``` State resets on container restart — by design. ## Database Setup Run `dbo.spRegistration.sql` against `dbRegistration` on `10.10.99.212` once. It is idempotent (`CREATE OR ALTER PROCEDURE`, `IF OBJECT_ID ... IS NULL` guard on the table). ## docker-compose Environment Variables ```bash # SQL Server ConnectionStrings__Sql=Server=10.10.99.212;Database=dbRegistration;User Id=appAdPlatformReg;Password=...;TrustServerCertificate=True; # CIAM — already correct in .env AzureAd__Instance=https://PositiveSpendClients.ciamlogin.com/ AzureAd__TenantId=cbf8b7d7-1e13-486d-b5b0-287ba79fdf0b AzureAd__ClientId=c426967f-bfcc-46af-b4e5-d69dc01cbf75 # CORS — already in .env CORS__AllowedOrigins=https://client.positivespend.com,https://portal.positivespend.com,... # Admin key — already in .env Registration__FunctionKey=mra0B2boC5m36E7CUn-Urhwp7k3t3QvPZKjJvtNVEdVgAzFuuaAyRA== ``` ## Health Check ```bash curl https://regapi.positivespend.com/api/health # {"ok":true,"service":"registration","mode":"database","timestamp":"..."} ``` ## SSL ```bash sudo certbot --nginx -d regapi.positivespend.com ``` --- ## Host Swap — Restore Azure Functions Three files change. Everything else (Data layer, Mock layer, models, SQL) is identical. | File | Change | |------|--------| | `Registration.csproj` | Comment ASP.NET Core `ItemGroup`, uncomment Functions `ItemGroup`, restore `v4`, change `Sdk` to `Microsoft.NET.Sdk` | | `Program.cs` | Comment ASP.NET Core block, uncomment Functions `HostBuilder` block | | `Functions/RegistrationFunctions.cs` | Swap class declaration, swap each method signature (all marked `◄ INACTIVE`) | Client changes when restoring Functions: - `authConfig.js`: set `API_BASE_URL` to the Azure Function App URL, set `API_FUNCTION_KEY` from Azure Portal → App Keys → default Run locally in Functions mode: ```bash func start curl http://localhost:7071/api/health ```