Revised Registration
All checks were successful
Registration / build-deploy (push) Successful in 9m8s

This commit is contained in:
Grae Jones
2026-03-22 09:37:28 -07:00
parent 8de463cd17
commit fae2226581
6 changed files with 389 additions and 215 deletions

View File

@@ -1,91 +1,126 @@
# Registration Function
# Registration Service
Azure Function (isolated worker, .NET 8) for managing prospect registration in AdPlatform.
ASP.NET Core (.NET 8) service for managing prospect registration in AdPlatform.
Self-hosted via docker-compose as `registration:8080` behind nginx / Gateway.
## Architecture
```
Prospect → Registration Function → dbRegistration (future)
Admin Panel → Management API → Registration Function (proxy)
→ spClientManagement (approve → dbAdPlatform)
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
```
Management validates admin sessions, then proxies registration calls to this Function.
The Function never touches `dbAdPlatform`. Management never touches `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 |
|--------|-------|------|-------------|
| GET | `/api/registration/pending` | Function Key | List pending applicants |
| GET | `/api/registration/{id}` | Function Key | Get single applicant |
| POST | `/api/registration/register` | Function Key | New prospect signup |
| POST | `/api/registration/{id}/reject` | Function Key | Reject applicant |
| POST | `/api/registration/{id}/complete` | Function Key | Mark approved (called after platform client created) |
| GET | `/api/registration/health` | Anonymous | Health check |
| `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 |
## Mock Mode (Current)
`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.
Starts with 4 realistic test applicants in memory. State persists within a Function host
lifecycle and resets on cold start. No database required.
## 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):
To switch to mock mode, in `Program.cs`:
```csharp
services.AddSingleton<IRegistrationDataService, MockDataService>();
```
## Database Mode (Future)
State resets on container restart — by design.
When `dbRegistration` is ready:
## Database Setup
1. Create the database and run the `spRegistration` stored proc migration
2. Set `ConnectionStrings:Sql` to the registration database connection string
3. In `Program.cs`, swap DI registration:
```csharp
services.AddSingleton<SqlService>();
services.AddSingleton<IRegistrationDataService, SqlDataService>();
```
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).
The `SqlDataService` calls `dbo.spRegistration` with the standard `@action/@rqst/@resp OUTPUT`
pattern used across all AdPlatform services.
## Local Development
## docker-compose Environment Variables
```bash
# Requires Azure Functions Core Tools
# 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 `<AzureFunctionsVersion>v4</AzureFunctionsVersion>`, 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
```
Test with:
```bash
curl http://localhost:7071/api/registration/health
curl http://localhost:7071/api/registration/pending
```
## Deployment
Deploy as an Azure Function App (Consumption or Flex Consumption plan).
After deployment:
1. Copy the Function Key from Azure Portal → Function App → App Keys
2. Set in Management API config:
- `Registration:BaseUrl` = `https://your-function-app.azurewebsites.net/api`
- `Registration:FunctionKey` = `<key from portal>`
These can be set as Azure Container App environment variables:
```
Registration__BaseUrl=https://your-function-app.azurewebsites.net/api
Registration__FunctionKey=<key>
```
## Mock Applicants
The mock data includes 4 test applicants representing the target market
(small businesses with low ad spend thresholds):
| Business | Category | Payment Verified | Days Waiting |
|----------|----------|-----------------|-------------|
| Bella's Boutique | Retail | Yes | 3 |
| Pacific Coast Plumbing | Home Services | Yes | 1 |
| Sunrise Dental Group | Healthcare | No | ~0.25 |
| FreshBite Meal Prep | Food & Beverage | Yes | ~0.08 |