Authentication & Authorization Architecture - ThriveSend B2B2G
Authentication & Authorization Architecture - ThriveSend B2B2G
Section titled “Authentication & Authorization Architecture - ThriveSend B2B2G”Version: 0.1.4 Last Updated: November 5, 2025 Status: Production Auth Provider: Clerk Security Standard: POPIA + Government Grade
Overview
Section titled “Overview”ThriveSend B2B2G implements a comprehensive authentication and authorization system using Clerk for identity management, combined with custom role-based access control (RBAC) and security clearance levels for government compliance.
Authentication Flow
Section titled “Authentication Flow”1. Initial Authentication with Clerk
Section titled “1. Initial Authentication with Clerk”sequenceDiagram participant User as User Browser participant App as Next.js App participant ClerkJS as Clerk Frontend SDK participant ClerkAPI as Clerk API participant MW as Next.js Middleware participant DB as PostgreSQL
User->>App: Navigate to Protected Route App->>MW: Request Intercepted
MW->>MW: Check Clerk Session Cookie
alt No Valid Session MW-->>App: Redirect to /sign-in App->>ClerkJS: Mount SignIn Component User->>ClerkJS: Enter Credentials ClerkJS->>ClerkAPI: Authenticate ClerkAPI->>ClerkAPI: Verify Credentials ClerkAPI-->>ClerkJS: JWT Token + Session ClerkJS->>ClerkJS: Store Session Cookie ClerkJS-->>App: Redirect to Original Route else Valid Session Exists MW->>MW: Verify JWT Signature MW->>ClerkAPI: Validate Token (if needed) ClerkAPI-->>MW: Token Valid MW->>MW: Extract Organization ID MW->>DB: Load Organization Data DB-->>MW: Organization Data MW-->>App: Allow Request end
Note over User,DB: User Authenticated<br/>Organization Context LoadedOrganization-Based Multi-Tenancy
Section titled “Organization-Based Multi-Tenancy”2. Organization Context Loading
Section titled “2. Organization Context Loading”flowchart TB Start([User Authenticated<br/>with Clerk])
GetClerkOrg[Get Active Organization<br/>from Clerk Session]
CheckOrg{Organization<br/>Exists?}
RedirectOnboard[Redirect to<br/>/onboarding/organization]
LoadOrgDB[Load Organization<br/>from Database]
CheckOrgDB{Org in<br/>Database?}
SyncOrg[Sync Organization<br/>from Clerk to DB]
LoadUser[Load OrganizationUser<br/>Record]
CheckUser{User<br/>Record Exists?}
CreateUser[Create OrganizationUser<br/>Record with Default Role]
LoadPermissions[Load User Permissions<br/>Role + Security Clearance]
StoreContext[Store in<br/>ServiceProviderContext]
Complete([User Ready<br/>with Full Context])
Start --> GetClerkOrg GetClerkOrg --> CheckOrg
CheckOrg -->|No| RedirectOnboard CheckOrg -->|Yes| LoadOrgDB
LoadOrgDB --> CheckOrgDB
CheckOrgDB -->|No| SyncOrg CheckOrgDB -->|Yes| LoadUser
SyncOrg --> LoadUser
LoadUser --> CheckUser
CheckUser -->|No| CreateUser CheckUser -->|Yes| LoadPermissions
CreateUser --> LoadPermissions
LoadPermissions --> StoreContext StoreContext --> Complete
style RedirectOnboard fill:#fff3e0 style LoadPermissions fill:#e1f5fe style Complete fill:#e8f5e9Authorization Layers
Section titled “Authorization Layers”3. Multi-Layer Authorization
Section titled “3. Multi-Layer Authorization”graph TB Request([API Request])
Layer1[Layer 1: Clerk JWT<br/>Signature Verification] Pass1{Valid JWT?} Fail1[401 Unauthorized]
Layer2[Layer 2: Organization<br/>Membership Check] Pass2{Member of Org?} Fail2[403 Forbidden]
Layer3[Layer 3: Role-Based<br/>Access Control] Pass3{Has Required<br/>Role?} Fail3[403 Forbidden]
Layer4[Layer 4: Security<br/>Clearance Check] Pass4{Sufficient<br/>Clearance?} Fail4[403 Insufficient Clearance]
Layer5[Layer 5: Resource-Level<br/>Permissions] Pass5{Can Access<br/>Resource?} Fail5[403 Forbidden]
Audit[Log Access<br/>POPIA Audit Trail]
Grant[Grant Access<br/>to Resource]
Request --> Layer1 Layer1 --> Pass1
Pass1 -->|No| Fail1 Pass1 -->|Yes| Layer2
Layer2 --> Pass2 Pass2 -->|No| Fail2 Pass2 -->|Yes| Layer3
Layer3 --> Pass3 Pass3 -->|No| Fail3 Pass3 -->|Yes| Layer4
Layer4 --> Pass4 Pass4 -->|No| Fail4 Pass4 -->|Yes| Layer5
Layer5 --> Pass5 Pass5 -->|No| Fail5 Pass5 -->|Yes| Audit
Audit --> Grant
style Fail1 fill:#ffebee style Fail2 fill:#ffebee style Fail3 fill:#ffebee style Fail4 fill:#ffebee style Fail5 fill:#ffebee style Audit fill:#e1f5fe style Grant fill:#e8f5e9Role-Based Access Control (RBAC)
Section titled “Role-Based Access Control (RBAC)”4. B2B2G Role Hierarchy
Section titled “4. B2B2G Role Hierarchy”graph TB subgraph Roles["User Roles"] ADMIN[SERVICE_PROVIDER_ADMIN<br/>Full access to organization] MANAGER[ACCOUNT_MANAGER<br/>Client & campaign management] CREATOR[CONTENT_CREATOR<br/>Content creation & editing] COMPLIANCE[COMPLIANCE_OFFICER<br/>Compliance & approval workflows] LIAISON[GOVERNMENT_LIAISON<br/>Government client specialist] ANALYST[ANALYST<br/>Read-only analytics access] end
subgraph Permissions["Permission Matrix"] direction TB P1[Organization Management] P2[Client Management] P3[Campaign Management] P4[Content Creation] P5[Content Approval] P6[Analytics Access] P7[Team Management] P8[Billing Management] P9[Compliance Configuration] P10[API Key Management] end
ADMIN -.Full Access.-> P1 ADMIN -.Full Access.-> P2 ADMIN -.Full Access.-> P3 ADMIN -.Full Access.-> P4 ADMIN -.Full Access.-> P5 ADMIN -.Full Access.-> P6 ADMIN -.Full Access.-> P7 ADMIN -.Full Access.-> P8 ADMIN -.Full Access.-> P9 ADMIN -.Full Access.-> P10
MANAGER --> P2 MANAGER --> P3 MANAGER --> P6
CREATOR --> P4 CREATOR -.Read Only.-> P3
COMPLIANCE --> P5 COMPLIANCE --> P9 COMPLIANCE -.Read Only.-> P6
LIAISON --> P2 LIAISON --> P3 LIAISON --> P4 LIAISON -.Government Only.-> P5
ANALYST -.Read Only.-> P6
style ADMIN fill:#e3f2fd style COMPLIANCE fill:#f3e5f5 style LIAISON fill:#fff3e0Role Capabilities:
| Role | Organization | Clients | Campaigns | Content | Approval | Analytics | Team | Billing |
|---|---|---|---|---|---|---|---|---|
| SERVICE_PROVIDER_ADMIN | Full | Full | Full | Full | Full | Full | Full | Full |
| ACCOUNT_MANAGER | Read | Full | Full | Create | - | Read | - | - |
| CONTENT_CREATOR | Read | Read | Read | Full | - | - | - | - |
| COMPLIANCE_OFFICER | Read | Read | Read | Read | Full | Read | - | - |
| GOVERNMENT_LIAISON | Read | Gov Only | Gov Only | Gov Only | Gov Only | Gov Only | - | - |
| ANALYST | Read | Read | Read | - | - | Full | - | - |
Security Clearance System
Section titled “Security Clearance System”5. Government Security Clearance Levels
Section titled “5. Government Security Clearance Levels”graph TB subgraph Clearance["Security Clearance Levels"] BASIC[BASIC Clearance<br/>Municipal/Local Government<br/>PUBLIC data only] ENHANCED[ENHANCED Clearance<br/>Provincial Government<br/>CONFIDENTIAL data allowed] CONFIDENTIAL[CONFIDENTIAL Clearance<br/>National Government<br/>RESTRICTED data allowed] end
subgraph DataClass["Data Classification"] PUBLIC[PUBLIC<br/>Publicly available data] COMMERCIAL[COMMERCIAL<br/>Commercial sensitive data] CONF[CONFIDENTIAL<br/>Government confidential] RESTRICTED[RESTRICTED<br/>National security data] end
subgraph Access["Access Control"] Check[Security Clearance<br/>Validation] Allow[Access Granted] Deny[Access Denied] Audit[Audit Log Entry] end
BASIC -.Can Access.-> PUBLIC BASIC -.Can Access.-> COMMERCIAL
ENHANCED -.Can Access.-> PUBLIC ENHANCED -.Can Access.-> COMMERCIAL ENHANCED -.Can Access.-> CONF
CONFIDENTIAL -.Can Access.-> PUBLIC CONFIDENTIAL -.Can Access.-> COMMERCIAL CONFIDENTIAL -.Can Access.-> CONF CONFIDENTIAL -.Can Access.-> RESTRICTED
PUBLIC --> Check COMMERCIAL --> Check CONF --> Check RESTRICTED --> Check
Check --> Allow Check --> Deny
Allow --> Audit Deny --> Audit
style BASIC fill:#e8f5e9 style ENHANCED fill:#fff3e0 style CONFIDENTIAL fill:#ffebee style RESTRICTED fill:#ffcdd2 style Audit fill:#e1f5feClearance Requirements:
| Client Type | Required Clearance | Data Classification |
|---|---|---|
| Municipal Government | BASIC | PUBLIC |
| Provincial Government | ENHANCED | PUBLIC, CONFIDENTIAL |
| National Government | CONFIDENTIAL | PUBLIC, CONFIDENTIAL, RESTRICTED |
| Business (SME) | BASIC | PUBLIC, COMMERCIAL |
| Business (Large Corp) | BASIC | PUBLIC, COMMERCIAL |
Middleware Implementation
Section titled “Middleware Implementation”6. Next.js Middleware Flow
Section titled “6. Next.js Middleware Flow”flowchart TB Start([HTTP Request])
MW[Next.js Middleware<br/>middleware.ts]
CheckPath{Protected<br/>Path?}
AllowPublic[Allow Request<br/>No Auth Required]
ClerkMW[Clerk Middleware<br/>clerkMiddleware]
ValidateJWT[Validate JWT Token]
ValidJWT{Valid Token?}
Redirect401[Redirect to /sign-in]
ExtractClaims[Extract JWT Claims<br/>- User ID<br/>- Organization ID<br/>- Session ID]
CheckOrg{Has Active<br/>Organization?}
RedirectOnboard[Redirect to<br/>/onboarding/organization]
AddHeaders[Add Auth Headers<br/>- X-User-Id<br/>- X-Organization-Id<br/>- X-Session-Id]
Continue[Continue to<br/>Route Handler]
Start --> MW MW --> CheckPath
CheckPath -->|Public| AllowPublic CheckPath -->|Protected| ClerkMW
ClerkMW --> ValidateJWT ValidateJWT --> ValidJWT
ValidJWT -->|No| Redirect401 ValidJWT -->|Yes| ExtractClaims
ExtractClaims --> CheckOrg
CheckOrg -->|No| RedirectOnboard CheckOrg -->|Yes| AddHeaders
AddHeaders --> Continue
style Redirect401 fill:#ffebee style RedirectOnboard fill:#fff3e0 style Continue fill:#e8f5e9Middleware Code Pattern:
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'
const isProtectedRoute = createRouteMatcher([ '/dashboard(.*)', '/api/(?!webhooks)(.*)',])
export default clerkMiddleware((auth, req) => { if (isProtectedRoute(req)) { auth().protect()
const { userId, orgId } = auth()
if (!orgId) { return NextResponse.redirect(new URL('/onboarding/organization', req.url)) }
// Add auth context to headers const requestHeaders = new Headers(req.headers) requestHeaders.set('x-user-id', userId) requestHeaders.set('x-organization-id', orgId)
return NextResponse.next({ request: { headers: requestHeaders, }, }) }})Session Management
Section titled “Session Management”7. Session Lifecycle
Section titled “7. Session Lifecycle”stateDiagram-v2 [*] --> Unauthenticated
Unauthenticated --> SignIn: User Signs In SignIn --> Authenticated: Clerk Creates Session
Authenticated --> Active: User Activity Active --> Authenticated: Refresh Token
Authenticated --> Idle: No Activity Idle --> Authenticated: User Returns Idle --> Expired: Timeout (30min default)
Authenticated --> SignOut: User Signs Out SignOut --> Unauthenticated: Clerk Destroys Session
Expired --> Unauthenticated: Session Expired
Unauthenticated --> [*]
note right of Authenticated Clerk JWT Token: - Valid for 60 seconds - Auto-refreshed - Contains user + org claims end note
note right of Idle Configurable Timeout: - Default: 30 minutes - Government: 15 minutes - Can be customized per org end noteSession Configuration:
// Security Configuration Modelinterface SecurityConfiguration { sessionTimeoutMinutes: number // Default: 30, Government: 15 mfaRequired: boolean // Required for CONFIDENTIAL clearance ipWhitelistEnabled: boolean // Optional per organization sessionRefreshEnabled: boolean // Auto-refresh JWT}API Authentication
Section titled “API Authentication”8. API Key Authentication Flow
Section titled “8. API Key Authentication Flow”sequenceDiagram participant Client as API Client participant API as API Gateway participant KeyValidator as API Key Validator participant DB as Database participant RateLimit as Rate Limiter participant Handler as API Handler participant Audit as Audit Logger
Client->>API: Request with API Key<br/>Header: X-API-Key
API->>KeyValidator: Validate Key
KeyValidator->>DB: Query ApiKey Table DB-->>KeyValidator: Key Data
alt Key Invalid KeyValidator-->>API: Invalid Key API-->>Client: 401 Unauthorized else Key Valid KeyValidator->>KeyValidator: Check Expiry KeyValidator->>KeyValidator: Verify Permissions KeyValidator-->>API: Key Valid
API->>RateLimit: Check Rate Limit
alt Rate Limit Exceeded RateLimit-->>API: Limit Exceeded API-->>Client: 429 Too Many Requests else Within Limit RateLimit-->>API: Allowed
API->>Handler: Forward Request Handler->>Handler: Process Request Handler-->>API: Response
API->>Audit: Log API Usage Audit->>DB: Store Audit Log
API-->>Client: 200 Success end end
Note over Client,Audit: API Key Protected<br/>Rate Limited & AuditedAPI Key Model:
interface ApiKey { id: string organizationId: string key: string // Encrypted name: string permissions: { endpoints: string[] // Allowed endpoints methods: string[] // Allowed HTTP methods rateLimit: number // Requests per minute } expiresAt?: Date lastUsedAt?: Date createdAt: Date}Webhook Authentication
Section titled “Webhook Authentication”9. Webhook Security
Section titled “9. Webhook Security”flowchart TB Start([Webhook Event])
ExtractSig[Extract Signature<br/>from Headers]
LoadSecret[Load Webhook Secret<br/>from Database]
ComputeHMAC[Compute HMAC-SHA256<br/>of Request Body]
CompareSig{Signatures<br/>Match?}
CheckTimestamp[Check Timestamp<br/>< 5 minutes old]
ValidTime{Valid<br/>Timestamp?}
CheckIP[Check IP Whitelist<br/>if Enabled]
ValidIP{IP<br/>Allowed?}
RejectReplay[Reject: Replay Attack]
RejectUnauth[Reject: Unauthorized]
RejectIP[Reject: IP Not Whitelisted]
LogWebhook[Log Webhook Delivery<br/>to Database]
ProcessEvent[Process Webhook Event]
AuditLog[Create Audit Log<br/>POPIA Compliance]
Success([200 OK])
Start --> ExtractSig ExtractSig --> LoadSecret LoadSecret --> ComputeHMAC ComputeHMAC --> CompareSig
CompareSig -->|No| RejectUnauth CompareSig -->|Yes| CheckTimestamp
CheckTimestamp --> ValidTime ValidTime -->|No| RejectReplay ValidTime -->|Yes| CheckIP
CheckIP --> ValidIP ValidIP -->|No| RejectIP ValidIP -->|Yes| LogWebhook
LogWebhook --> ProcessEvent ProcessEvent --> AuditLog AuditLog --> Success
style RejectReplay fill:#ffebee style RejectUnauth fill:#ffebee style RejectIP fill:#ffebee style Success fill:#e8f5e9 style AuditLog fill:#e1f5feWebhook Security Implementation:
// Webhook signature verificationasync function verifyWebhookSignature( payload: string, signature: string, secret: string, timestamp: string): Promise<boolean> { // Check timestamp (prevent replay attacks) const eventTime = new Date(timestamp) const now = new Date() const diff = Math.abs(now.getTime() - eventTime.getTime())
if (diff > 5 * 60 * 1000) { // 5 minutes return false }
// Compute HMAC const expectedSignature = crypto .createHmac('sha256', secret) .update(`${timestamp}.${payload}`) .digest('hex')
// Constant-time comparison return crypto.timingSafeEqual( Buffer.from(signature), Buffer.from(expectedSignature) )}POPIA Compliance in Authentication
Section titled “POPIA Compliance in Authentication”10. Authentication Audit Logging
Section titled “10. Authentication Audit Logging”flowchart TB AuthEvent([Authentication Event])
Classify{Event Type?}
SignIn[Sign In Event] SignOut[Sign Out Event] Failed[Failed Auth] OrgSwitch[Organization Switch] PermChange[Permission Change]
GatherData[Gather Event Data<br/>- User ID<br/>- IP Address<br/>- User Agent<br/>- Organization<br/>- Timestamp<br/>- Result]
SanitizePII[Sanitize PII<br/>Remove sensitive data<br/>from logs]
CreateAudit[Create AuditLog<br/>Record]
StoreDB[(Store in<br/>PostgreSQL)]
CheckAlert{Critical<br/>Event?}
SendAlert[Send Alert to<br/>Compliance Officer]
Complete([Audit Complete])
AuthEvent --> Classify
Classify --> SignIn Classify --> SignOut Classify --> Failed Classify --> OrgSwitch Classify --> PermChange
SignIn --> GatherData SignOut --> GatherData Failed --> GatherData OrgSwitch --> GatherData PermChange --> GatherData
GatherData --> SanitizePII SanitizePII --> CreateAudit CreateAudit --> StoreDB StoreDB --> CheckAlert
CheckAlert -->|Yes| SendAlert CheckAlert -->|No| Complete SendAlert --> Complete
style Failed fill:#ffebee style SanitizePII fill:#fff3e0 style StoreDB fill:#e1f5fe style Complete fill:#e8f5e9Audited Authentication Events:
| Event | Description | POPIA Requirement |
|---|---|---|
| USER_SIGN_IN | User successfully authenticated | Yes - Access logging |
| USER_SIGN_OUT | User logged out | Yes - Session termination |
| AUTH_FAILED | Failed authentication attempt | Yes - Security monitoring |
| ORG_SWITCH | User switched organizations | Yes - Context change |
| PERMISSION_GRANTED | New permissions assigned | Yes - Access change |
| PERMISSION_REVOKED | Permissions removed | Yes - Access change |
| SESSION_EXPIRED | Session timed out | Yes - Session lifecycle |
| MFA_ENABLED | MFA activated | Yes - Security enhancement |
| PASSWORD_CHANGED | Password updated | Yes - Security event |
| API_KEY_CREATED | New API key generated | Yes - Access credential |
Security Best Practices
Section titled “Security Best Practices”Authentication
Section titled “Authentication”- Strong Passwords: Enforce minimum requirements via Clerk settings
- MFA Required: Mandatory for CONFIDENTIAL security clearance
- Session Timeout: 15 minutes for government, 30 minutes for business
- JWT Rotation: Automatic token refresh every 60 seconds
- IP Whitelisting: Optional per organization for sensitive operations
Authorization
Section titled “Authorization”- Principle of Least Privilege: Users get minimum required permissions
- Defense in Depth: Multiple authorization layers
- Security Clearance: Mandatory for government client access
- Resource-Level Checks: Verify access to specific resources
- Audit Everything: Complete audit trail for compliance
POPIA Compliance
Section titled “POPIA Compliance”- Consent Tracking: Record consent for authentication
- Data Minimization: Store only necessary auth data
- Right to Access: Users can view their auth history
- Right to Deletion: Support for account deletion
- Data Residency: All auth data in South African servers
Environment Configuration
Section titled “Environment Configuration”Clerk Configuration
Section titled “Clerk Configuration”NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_live_...CLERK_SECRET_KEY=sk_live_...
# Optional: Custom domainsNEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-inNEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-upNEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL=/dashboardNEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL=/onboarding/organizationSecurity Settings
Section titled “Security Settings”// SecurityConfiguration defaultsconst DEFAULT_SECURITY = { sessionTimeoutMinutes: 30, mfaRequired: false, ipWhitelistEnabled: false, allowedOrigins: ['https://thrivesend.isutech.co.za'], corsEnabled: true, rateLimitPerMinute: 60,}
// Government overridesconst GOVERNMENT_SECURITY = { ...DEFAULT_SECURITY, sessionTimeoutMinutes: 15, mfaRequired: true, // For CONFIDENTIAL clearance ipWhitelistEnabled: true,}Related Documentation
Section titled “Related Documentation”Last Updated: November 5, 2025 Verified: Production v0.1.4