Live Demo API Blueprint (.NET)
Packages to install
dotnet add package PrimusSaaS.Identity.Validator
dotnet add package PrimusSaaS.Logging
dotnet add package PrimusSaaS.Notifications
dotnet add package PrimusSaaS.Documents
Baseline configuration (appsettings.*)
{
"PrimusLogging": {
"ApplicationId": "LiveDemoApi",
"Environment": "Development",
"MinLevel": 1,
"Targets": [
{ "Type": "console", "Pretty": true },
{
"Type": "file",
"Path": "logs/livedemo-api.log",
"Async": true,
"MaxFileSize": 10485760,
"MaxRetainedFiles": 5,
"CompressRotatedFiles": true
}
],
"Pii": {
"MaskEmails": true,
"MaskCreditCards": true,
"MaskSSN": true,
"MaskPasswords": true,
"MaskTokens": true,
"MaskSecrets": true
}
},
"PrimusIdentity": {
"RequireHttpsMetadata": true,
"ValidateLifetime": true,
"ClockSkew": "00:05:00",
"Issuers": [
{
"Name": "Auth0",
"Type": "Auth0",
"Authority": "https://YOUR-TENANT.auth0.com/",
"Issuer": "https://YOUR-TENANT.auth0.com/",
"Audiences": [ "https://your-api-identifier/" ]
},
{
"Name": "LocalDev",
"Type": "Jwt",
"Issuer": "https://primus.local",
"Audiences": [ "api://primus-livedemo" ],
"Secret": "your-32-character-minimum-secret-key-here"
}
],
"Diagnostics": {
"EnableDetailedErrors": true,
"IncludeTokenHintsInChallenges": true,
"IncludeDebugHeaders": true,
"LogTokenRejectionReasons": true,
"MaxRecentFailures": 50,
"AutoDetectDevelopment": true
}
},
"Notifications": {
"Smtp": {
"Host": "",
"Port": 587,
"Username": "",
"Password": "",
"EnableSsl": true,
"FromAddress": "",
"FromName": "Primus Live Demo"
},
"Twilio": {
"AccountSid": "",
"AuthToken": "",
"FromNumber": "",
"MessagingServiceSid": "",
"ValidateOnStartup": false
}
},
"PrimusDocuments": {
"Provider": "Default",
"TempStoragePath": "temp/documents",
"MaxContentLength": 20000,
"LinkTtl": "00:10:00",
"SelfTestEnabled": true,
"TitleFontSize": 24,
"BodyFontSize": 12,
"BrandName": "Primus Live Demo",
"IncludeTimestampInFooter": true,
"IncludeTenantInFooter": true
},
"PrimusFeatureFlags": {
"Provider": "InMemory",
"DefaultValue": false,
"CacheDurationSeconds": 30,
"Logging": {
"LogEvaluations": true,
"IncludeUserContext": false
},
"Flags": {
"NewDashboard": {
"Enabled": true,
"Description": "Enable the new dashboard UI experience",
"RolloutPercentage": 100
}
}
},
"DemoLocalAuth": {
"Email": "demo@primus.local",
"Password": "",
"Name": "Primus Demo User",
"Subject": "local-demo-user"
}
}
Store secrets in User Secrets/Key Vault/App Service settings; dont commit them.
Service registration (Program.cs)
using PrimusSaaS.Identity.Validator;
using PrimusSaaS.Notifications;
using PrimusSaaS.Logging.Extensions;
using Primus.Documents;
var builder = WebApplication.CreateBuilder(args);
// Logging + Application Insights
builder.Logging.ClearProviders();
builder.Logging.AddPrimus(opts => builder.Configuration.GetSection("PrimusLogging").Bind(opts));
var ai = builder.Configuration["PrimusLogging:ApplicationInsights:ConnectionString"];
if (!string.IsNullOrWhiteSpace(ai) && ai != "your-application-insights-connection-string")
builder.Services.AddApplicationInsightsTelemetry(o => o.ConnectionString = ai);
// Primus modules
builder.Services.AddPrimusIdentity(o => builder.Configuration.GetSection("PrimusIdentity").Bind(o));
builder.Services.AddPrimusNotifications(notifications =>
{
var templatesPath = Path.Combine(builder.Environment.ContentRootPath, "NotificationTemplates");
notifications.UseFileTemplates(templatesPath, validateOnStartup: true, watchForChanges: builder.Environment.IsDevelopment());
notifications.UseLogger();
notifications.UseInMemoryQueue(o =>
{
o.BoundedCapacity = 500;
o.MaxParallelHandlers = 2;
o.BaseRetryDelayMs = 250;
});
notifications.UseSmtp(builder.Configuration.GetSection("Notifications:Smtp"));
notifications.UseTwilio(builder.Configuration, "Notifications:Twilio", validateOnStartup: false);
notifications.ConfigureDispatch(o =>
{
o.ThrowOnFailure = true;
o.FallbackToLogger = false;
o.QueueOnFailure = false;
});
});
builder.Services.AddPrimusDocumentRenderer(o => builder.Configuration.GetSection("PrimusDocuments").Bind(o));
builder.Services.AddAuthorization();
builder.Services.AddCors(o => o.AddPolicy("AllowFrontend", policy =>
policy.WithOrigins("http://localhost:5173", "https://localhost:5173")
.AllowAnyHeader()
.AllowAnyMethod()));
Middleware order
app.UseCors("AllowFrontend");
app.UseHttpsRedirection();
app.UsePrimusLogging(); // request logging + correlation IDs
app.UseAuthentication(); // from AddPrimusIdentity
app.UseAuthorization();
Keep authentication/authorization between HTTPS redirection and your endpoints. The sample exposes /public (no auth) and /secure (auth) instead of a root endpoint; hit those when testing.
Golden-path endpoints to keep or adapt
These come from the Live Demo; remove them for production or put them behind strong auth (and role checks) if you must keep them.
| Endpoint | Purpose |
|---|---|
GET /primus/diagnostics | Issuer/config diagnostics (optional) |
GET /whoami | Authenticated user context echo |
POST /log/test | Logging demo with PII redaction |
POST /notifications/test | Render PasswordReset template without sending |
GET /notifications/health | Channel health snapshot |
POST /notifications/welcome | Send welcome email (Email + Logger) |
POST /notifications/sms | Send SMS via Twilio or logger |
POST /notifications/templates/preview | Render template content safely |
GET/PUT /notifications/templates/{type}/{channel} | Retrieve/update Liquid templates |
GET /notifications/templates | List available templates |
POST /documents/render | Render text/HTML/Markdown (plain-text output) to PDF (direct bytes) |
POST /documents/render/link | Render to PDF and return a one-time download token |
GET /documents/download/{token} | Consume a token and download PDF |
POST /documents/self-test | Renderer self-diagnostics (Basic/Extended) |
GET /telemetry/summary | App Insights + process stats (dev-only) |
GET /logs/recent | Tail the demo log file (dev-only; remove or lock down) |
POST /auth/auth0 | Auth0 client-credentials proxy (dev-only; remove) |
POST /auth/azure | Azure CLI token proxy (dev-only; remove) |
POST /auth/local | Local shared-secret JWT for demos |
Production hardening checklist
- Remove
/auth/auth0,/auth/azure,/logs/recent, and any demo secrets; if temporarily kept, require strict auth and audit. - Disable
IdentityModelEventSource.ShowPIIoutside local dev. - Lock down CORS origins to real frontends; require HTTPS everywhere.
- Swap the in-memory notification queue for a durable provider in production.
- Store SMTP/Twilio secrets in Key Vault/User Secrets; never in
appsettings.json. - Protect or remove template management endpoints; require auth/roles on any that remain.
Quick verification steps
GET /whoamireturns authenticated user context; 401 when unauthenticated.POST /notifications/testrenders templates without error.GET /feature-flags/testreturns definitions with user context.POST /documents/self-testreturnssuccess: true.- Application Insights receives request/dependency telemetry when configured.