Revised Registration
All checks were successful
Registration / build-deploy (push) Successful in 9m8s
All checks were successful
Registration / build-deploy (push) Successful in 9m8s
This commit is contained in:
@@ -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 |
|
||||
|
||||
Reference in New Issue
Block a user