Agencio Predict — User flows from UI to backend with role gates and license validation
Generated: 2026-05-19
1. System Overview
High-level view of user authentication, authorization, and feature access in Agencio Predict.
flowchart TB
subgraph "User Entry Points"
LP["/login Page"]
RP["/register Page"]
TD["Terminal Subdomain"]
end
subgraph "Authentication Layer"
AS["Auth Store (Zustand)"]
JWT["JWT Token"]
MFA["MFA Challenge"]
SS["Session Service"]
end
subgraph "Auth Providers"
LOCAL["Local Provider (bcrypt + JWT)"]
COGNITO["AWS Cognito"]
AGENCIO["Agencio Auth Service"]
end
subgraph "Authorization Layer"
MW["Auth Middleware extractUser() / requireAuth()"]
RBAC["RBAC Engine hasRole() / hasPermission()"]
FG["Feature Gate checkFeatureAccess()"]
end
subgraph "License Layer"
DG["Deployment Guard"]
LS["License Server"]
SUB["Subscription Service"]
end
subgraph "Protected Resources"
DASH["/dashboard"]
ADMIN["/admin/*"]
TRADE["/trading"]
ALGO["/algorithms"]
end
LP --> AS
RP --> AS
TD --> AS
AS --> LOCAL
AS --> COGNITO
AS --> AGENCIO
LOCAL --> JWT
COGNITO --> JWT
AGENCIO --> JWT
JWT --> MFA
MFA --> SS
SS --> MW
MW --> RBAC
RBAC --> FG
FG --> DG
DG --> LS
FG --> SUB
MW --> DASH
RBAC --> ADMIN
FG --> TRADE
FG --> ALGO
Key Security Principles
Principle
Implementation
Role Authority
Local DB role takes precedence over external provider token role
Defense in Depth
Role checks at API route, handler, AND database levels
Dev Bypass Safety
Requires BOTH NODE_ENV !== 'production' AND ALLOW_DEV_AUTH_BYPASS=true
Session Tokens
SHA256 hashed in DB, never stored raw
License Validation
HMAC-SHA256 signed requests/responses with timestamp freshness check
2. Login Flow (UI → Backend)
Complete login sequence from user input to authenticated session.
sequenceDiagram
autonumber
participant U as User
participant LP as /login Page
participant AS as Auth Store
participant API as /api/auth/login
participant MW as Middleware
participant P as Auth Provider
participant DB as Database
participant SS as Session Service
U->>LP: Enter email + password
LP->>AS: login(email, password)
AS->>API: POST /auth/login
API->>MW: extractUser() [skip - login is public]
API->>P: provider.authenticate(credentials)
alt Provider: Local
P->>DB: SELECT from auth.users WHERE email
DB-->>P: User record + password_hash
P->>P: bcrypt.compare(password, hash)
P-->>API: AuthResult {tokens}
else Provider: Cognito
P->>P: InitiateAuthCommand
P-->>API: AuthResult {tokens OR challenge}
else Provider: Agencio
P->>P: HTTP to auth service
P-->>API: AuthResult {tokens OR challenge}
end
alt MFA Required
API-->>AS: {challenge: {name, session}}
AS->>AS: Store pendingMfa
AS-->>LP: {status: 'mfa_required'}
LP->>U: Redirect to /login/mfa
else No MFA
API->>DB: Sync external user to local
DB-->>API: localUser with DB role
API->>SS: createSession(userId, token, deviceInfo)
SS->>DB: INSERT into auth.user_sessions
SS-->>API: {sessionId, sessionsRevoked}
API-->>AS: {user, token, expiresIn, nextStep}
AS->>AS: Set user, token, isAuthenticated
AS-->>LP: {status: 'ok', nextStep}
LP->>U: Route based on nextStep
end
New user signup flow with CAPTCHA, email verification, and optional admin approval.
sequenceDiagram
autonumber
participant U as User
participant RP as /register Page
participant API as /api/auth/register
participant CF as Cloudflare Turnstile
participant P as Auth Provider
participant DB as Database
participant EM as Email Service
U->>RP: Fill registration form
RP->>CF: Complete CAPTCHA challenge
CF-->>RP: Turnstile token
RP->>API: POST /auth/register
API->>CF: Verify Turnstile token
CF-->>API: {success: true}
API->>DB: Check email blacklist
DB-->>API: Not blacklisted
API->>P: provider.register(credentials)
P->>DB: INSERT auth.users (status='pending_verification')
P-->>API: AuthResult
API->>EM: Send verification email
EM-->>U: Email with 6-digit code
API-->>RP: {nextStep: 'verify-email'}
RP->>U: Redirect to /verify-email
Note over U,EM: Email Verification
U->>API: POST /auth/verify-email {code}
API->>P: provider.confirmEmail(email, code)
P->>DB: UPDATE status='pending_approval' OR 'pending_mfa'
API-->>U: {nextStep: 'await-approval' OR 'enroll-mfa'}
flowchart TB
subgraph "Role Hierarchy"
SA["super_admin"]
AD["admin / administrator"]
ED["editor"]
US["user"]
CU["console_user"]
VW["viewer"]
SV["service"]
SA --> AD
AD --> ED
ED --> US
US --> VW
CU --> VW
end
subgraph "Access Levels"
L1["Platform Config (super_admin only)"]
L2["Admin Panel (admin+)"]
L3["Create/Edit (editor+)"]
L4["Read/Trade (user+)"]
L5["Console Only (console_user)"]
L6["Read Only (viewer)"]
end
SA --> L1
AD --> L2
ED --> L3
US --> L4
CU --> L5
VW --> L6
flowchart LR
subgraph "Request Flow"
REQ["API Request"]
MW["requireAuth()"]
ROLE["hasRole()"]
PERM["hasPermission()"]
FEAT["checkFeatureAccess()"]
HAND["Handler"]
end
subgraph "Permission Types"
ER["events:read"]
EC["events:create"]
EU["events:update"]
ED["events:delete"]
EV["events:resolve"]
SR["signals:read"]
SC["signals:create"]
OM["orgs:manage"]
AA["admin:access"]
SY["system:config"]
end
REQ --> MW --> ROLE --> PERM --> FEAT --> HAND
PERM --> ER
PERM --> EC
PERM --> EU
PERM --> ED
PERM --> EV
PERM --> SR
PERM --> SC
PERM --> OM
PERM --> AA
PERM --> SY
Role → Permission Matrix
Permission
super_admin
admin
editor
user
viewer
service
events:read
✓
✓
✓
✓
✓
✓
events:create
✓
✓
✓
✗
✗
✓
events:update
✓
✓
✓
✗
✗
✓
events:delete
✓
✓
✗
✗
✗
✗
events:resolve
✓
✓
✗
✗
✗
✗
signals:create
✓
✓
✓
✗
✗
✓
orgs:manage
✓
✓
✗
✗
✗
✗
admin:access
✓
✓
✗
✗
✗
✗
system:config
✓
✗
✗
✗
✗
✗
RBAC Functions
// Check if user has any of the specified roles (direct or inherited)
hasRole(user, ...roles: Role[]): boolean
// Check if user has all specified permissions
hasPermission(user, ...permissions: Permission[]): boolean
// Check if user has any of the specified permissions
hasAnyPermission(user, ...permissions: Permission[]): boolean
// Throw 403 if permission check fails
requirePermissions(user, ...permissions: Permission[]): void
// Throw 403 if role check fails
requireRoles(user, ...roles: Role[]): void
// Get all effective permissions for a user
getEffectivePermissions(user): Permission[]
Admin panel access control and super_admin-only features.
flowchart TB
subgraph "Admin Layout Check"
REQ["Request to /admin/*"]
AUTH["isAuthenticated?"]
ROLE["isAdminRole(user.role)?"]
SUPER["isSuperAdmin?"]
end
subgraph "Admin Routes (all admins)"
AU["/admin/users"]
AT["/admin/trades"]
AA["/admin/algorithms"]
AC["/admin/categories"]
AP["/admin/predictions"]
AS["/admin/signals"]
AN["/admin/news"]
ASF["/admin/social-follows"]
end
subgraph "Super-Admin Only"
AB["/admin/billing"]
AAI["/admin/ai-billing"]
APK["/admin/platform-ai-keys"]
ASH["/admin/stock-hunter-config"]
ABR["/admin/brokers"]
ASA["/admin/service-accounts"]
ACG["/admin/cognito"]
AAW["/admin/aws"]
AI["/admin/integrations"]
AF["/admin/feeds"]
AFL["/admin/flow"]
ASI["/admin/silo"]
ABE["/admin/bertha"]
ASY["/admin/system"]
ASS["/admin/assistant"]
ASD["/admin/support-docs"]
APM["/admin/pinn-models"]
end
REQ --> AUTH
AUTH -->|No| LOGIN["/login"]
AUTH -->|Yes| ROLE
ROLE -->|No| DENY["403 Forbidden"]
ROLE -->|Yes| AU & AT & AA & AC & AP & AS & AN & ASF
ROLE -->|Yes| SUPER
SUPER -->|Yes| AB & AAI & APK & ASH & ABR & ASA & ACG & AAW & AI & AF & AFL & ASI & ABE & ASY & ASS & ASD & APM
SUPER -->|No| HIDE["Items hidden from nav"]
Backend Admin Pattern
// In route.ts (thin wrapper)
export async function GET(request: NextRequest) {
const adminError = await requireAdmin(request);
if (adminError) return adminError;
return handleListUsers(request);
}
// In handlers.ts
async function requireAdmin(request: NextRequest) {
const user = await requireAuth(request);
if (!isAdminRole(user.role)) {
throw Errors.forbidden('Admin role required');
}
return user;
}
async function requireSuperAdmin(request: NextRequest) {
const user = await requireAuth(request);
if (!isSuperAdmin(user.role)) {
throw Errors.forbidden('Super admin role required');
}
return user;
}