Skip to content

Auth & Roles

SafeWatch uses its own cookie-based auth scheme, completely separate from the JWT Bearer scheme used by the REST API.


Auth Flow

  1. User navigates to /safewatchSafeWatchAuthMiddleware checks for sw_session cookie
  2. No valid cookie → redirect to /safewatch/login?returnUrl=...
  3. User submits credentials → SafeWatchLoginService.ValidateCredentials() → BCrypt verify
  4. Valid credentials → SignInAsync("SafeWatch", principal)sw_session cookie issued
  5. All subsequent requests: middleware validates cookie → Blazor circuit renders

Property Value
Name sw_session
HttpOnly true
SameSite Strict
Secure Always (HTTPS required in production)
Expiry 4 hours, sliding

Credentials Configuration

Accounts are stored in appsettings.json (never in the database):

{
  "SafeWatch": {
    "Accounts": [
      { "Username": "sysadmin", "PasswordHash": "<bcrypt>", "Role": "sys_admin" },
      { "Username": "product",  "PasswordHash": "<bcrypt>", "Role": "product" },
      { "Username": "support",  "PasswordHash": "<bcrypt>", "Role": "support" },
      { "Username": "ceo",      "PasswordHash": "<bcrypt>", "Role": "ceo" }
    ]
  }
}

Passwords are hashed with BCrypt (work factor 12 for production). See Adding an Account.


Roles

Role Landing page Description
sys_admin /safewatch/sysadmin Full access, system health focus
product /safewatch/product Usage metrics, feature adoption
support /safewatch/support User lookup, recent auth activity
ceo /safewatch/ceo Business metrics, MAU, retention

The sidebar nav renders only the modules the current role can access. Navigating directly to a forbidden route renders a 403 page within the Blazor shell.


Middleware Ordering

SafeWatchAuthMiddleware must be placed between UseAuthentication() and UseAuthorization() in Program.cs:

app.UseAuthentication();
app.UseMiddleware<SafeWatchAuthMiddleware>();   // ← here
app.UseAuthorization();

It also blocks unauthenticated /_blazor WebSocket upgrades to prevent circuit creation before cookie validation.