Anik Sikder
Blueprints/accesscore-iam-enterprise-identity-platform

AccessCore IAM (Enterprise Identity & Access Management Platform)

Enterprise-grade identity and access management platform designed to centralize authentication, authorization, user lifecycle management, RBAC governance, and organizational security controls.

IAMIdentity ManagementRBACAuthenticationAuthorizationSecuritySaaSMulti-TenantDjango
27 min readMarch 1, 2026
  • Read Time
    27 min read
  • Topics
    9
  • Patterns
    5
  • Level
    Advanced
Architecture Highlights

Multi-tenant identity core

Hierarchical RBAC engine

SSO & token architecture

Audit-ready security logging

Delegated administration model

blueprint.md

$ open blueprint

The high-level architecture explains what AccessCore contains.

The deeper question is:

How do those components interact when a real request enters the system?

An enterprise identity platform cannot simply authenticate a user and return a JWT.

It must establish:

  1. Who the caller is
  2. Which organization they are operating within
  3. Which session or client initiated the request
  4. Which roles and permissions they possess
  5. Which resource they are trying to access
  6. Whether contextual policies permit the action
  7. Whether the action must be audited
  8. How the decision can be revoked or invalidated
  9. How the system behaves when one dependency fails

That makes AccessCore less like a login service and more like a central security control plane.


Architecture Principles

The entire system is built around a small number of architectural invariants.

1. Identity Is Centralized

Applications should not maintain their own independent identity model.

code
Application A ─┐
Application B ─┤
Application C ─┼──► AccessCore Identity
Application D ─┤
Mobile App ────┘

AccessCore becomes the authoritative source for:

  • User identity
  • Organization membership
  • Authentication state
  • Roles
  • Permissions
  • Sessions
  • Identity federation
  • Access policies
  • Security events

Applications consume identity capabilities rather than rebuilding them.


2. Tenant Context Is Established Before Authorization

Authorization without tenant context is incomplete.

The system must establish:

code
Request
   │
   ▼
Authenticate Caller
   │
   ▼
Resolve Organization Context
   │
   ▼
Validate Membership
   │
   ▼
Evaluate Authorization
   │
   ▼
Access Resource

A client-supplied organization ID must never be treated as proof of membership.

The organization context must be derived from trusted authentication state and validated against the user's actual memberships.

This is particularly important because multi-tenant vulnerabilities can occur not only in database queries but also in caches, queues, object storage, logging, and session handling. OWASP recommends establishing tenant context early, binding it to authenticated identity, and enforcing tenant ownership at the data-access layer.


3. Authentication and Authorization Are Separate

Authentication answers:

code
"Who are you?"

Authorization answers:

code
"What are you allowed to do?"

AccessCore deliberately separates them.

code
                 ┌─────────────────────┐
                 │ Authentication      │
                 │                     │
                 │ Password            │
                 │ OIDC                │
                 │ SAML                │
                 │ MFA                 │
                 │ Session             │
                 └──────────┬──────────┘
                            │
                            ▼
                     Authenticated
                        Identity
                            │
                            ▼
                 ┌─────────────────────┐
                 │ Authorization       │
                 │                     │
                 │ Roles               │
                 │ Permissions         │
                 │ Policies            │
                 │ Resource Scope      │
                 │ Context             │
                 └──────────┬──────────┘
                            │
                            ▼
                         Decision

This separation prevents authentication logic from becoming entangled with business permissions.


Request Processing Architecture

A request entering AccessCore passes through multiple security boundaries.

code
                    Client
                      │
                      ▼
              ┌───────────────┐
              │ Load Balancer │
              └───────┬───────┘
                      │
                      ▼
              ┌───────────────┐
              │ API Gateway   │
              │               │
              │ TLS           │
              │ Rate Limit    │
              │ Routing       │
              └───────┬───────┘
                      │
                      ▼
              ┌───────────────┐
              │ Auth Middleware│
              └───────┬───────┘
                      │
             ┌────────┴────────┐
             ▼                 ▼
      Token Validation    Session Lookup
             │                 │
             └────────┬────────┘
                      ▼
              ┌───────────────┐
              │ Tenant Context│
              └───────┬───────┘
                      │
                      ▼
              ┌───────────────┐
              │ Authorization │
              │ Engine        │
              └───────┬───────┘
                      │
                ┌─────┴─────┐
                ▼           ▼
             Allow        Deny
                │           │
                ▼           ▼
           Application    Audit
             Logic        Event

The important architectural principle is that authorization is enforced server-side.

Hiding a button in a frontend is not authorization.

The backend must make the final decision. OWASP explicitly recommends enforcing authorization at a trusted service layer rather than relying on client-side controls.


Trust Boundaries

AccessCore contains several distinct trust zones.

code
┌────────────────────────────────────────────────────────────┐
│                       UNTRUSTED ZONE                       │
│                                                            │
│ Browser / Mobile / External API Client                     │
└──────────────────────────┬─────────────────────────────────┘
                           │
                           │ TLS
                           ▼
┌─────────────────────────────────────────────────────────────┐
│                     EDGE SECURITY ZONE                      │
│                                                             │
│ CDN / WAF / Load Balancer / API Gateway                     │
└──────────────────────────┬──────────────────────────────────┘
                           │
                           ▼
┌─────────────────────────────────────────────────────────────┐
│                    APPLICATION TRUST ZONE                   │
│                                                             │
│ Authentication │ Authorization │ Tenant │ Session           │
└──────────────────────────┬──────────────────────────────────┘
                           │
                           ▼
┌─────────────────────────────────────────────────────────────┐
│                       DATA ZONE                             │
│                                                             │
│ PostgreSQL │ Redis │ Audit Store │ Object Storage           │
└─────────────────────────────────────────────────────────────┘

Every boundary should answer:

What does this component trust, and what does it refuse to trust?

For example:

code
Browser
  └── trusted for nothing except presenting credentials

JWT
  └── trusted for cryptographic integrity
  └── NOT trusted as proof that current permissions are unchanged

Tenant ID
  └── trusted only after membership validation

Role
  └── trusted only when resolved from authoritative identity state

Authorization decision
  └── trusted only when produced by the authorization layer

This distinction becomes extremely important once AccessCore operates at enterprise scale.


Identity Domain Architecture

The Identity Core is the system of record.

code
Organization
     │
     ├── Organization Settings
     │
     ├── Users
     │      │
     │      ├── Credentials
     │      ├── MFA Factors
     │      ├── Sessions
     │      └── External Identities
     │
     ├── Memberships
     │      │
     │      └── Role Assignments
     │
     ├── Roles
     │      │
     │      └── Permissions
     │
     └── Policies
            │
            └── Resource Scopes

A critical design decision is to avoid putting organization identity directly on the global user record.

Instead:

code
User
 │
 ├── identity
 │
 └── memberships
        │
        ├── Organization A
        ├── Organization B
        └── Organization C

This allows one human identity to belong to multiple organizations without duplicating the user.


Core Identity Data Model

A simplified relational model looks like this:

code
users
-----
id
email
status
created_at
updated_at


organizations
-------------
id
name
status
created_at


organization_memberships
------------------------
id
organization_id
user_id
status
joined_at


roles
-----
id
organization_id
name
code
parent_role_id


permissions
-----------
id
resource
action
description


role_permissions
----------------
role_id
permission_id


membership_roles
----------------
membership_id
role_id
scope


sessions
--------
id
user_id
organization_id
client_id
refresh_token_hash
status
expires_at
last_seen_at


external_identities
-------------------
id
user_id
provider
provider_subject
metadata

The relationship is deliberately normalized.

code
User
  │
  ▼
Membership
  │
  ├── Organization
  │
  └── Roles
        │
        └── Permissions

This prevents authorization logic from being embedded directly inside the user record.


Authentication Architecture

Authentication is implemented as a state machine rather than a single login endpoint.

code
                   Login Request
                        │
                        ▼
                Identify Client
                        │
                        ▼
               Resolve Organization
                        │
                        ▼
                Find User Identity
                        │
                        ▼
               Verify Credential
                        │
                        ▼
                 MFA Required?
                   /       \
                 Yes        No
                 │           │
                 ▼           │
            Verify MFA       │
                 │           │
                 └─────┬─────┘
                       ▼
                 Create Session
                       │
                       ▼
                Issue Credentials
                       │
                       ▼
                  Audit Event
                       │
                       ▼
                Authenticated

Each stage has a specific responsibility.

This prevents authentication code from becoming a large conditional block containing passwords, MFA, SSO, sessions, permissions, and business logic.


Credential Verification

Passwords should never be stored directly.

code
Password
   │
   ▼
Password Hashing Function
   │
   ▼
Stored Password Hash

At login:

code
Submitted Password
        │
        ▼
Password Verification
        │
    ┌───┴───┐
    │       │
 Invalid   Valid
    │       │
    ▼       ▼
 Reject    Continue

Credential verification should also be protected by:

  • Rate limiting
  • Brute-force detection
  • Account lockout or progressive throttling
  • Credential stuffing detection
  • Security event logging
  • MFA enforcement

MFA Architecture

MFA should be evaluated as a policy decision.

code
Authenticated Identity
        │
        ▼
Evaluate MFA Policy
        │
   ┌────┴────┐
   │         │
Required   Not Required
   │         │
   ▼         ▼
Challenge   Continue
   │
   ▼
Verify Factor
   │
   ▼
Continue

MFA policy can eventually become contextual:

code
IF
    user.role == privileged
AND
    resource.sensitivity == critical
THEN
    require_step_up_authentication

This allows AccessCore to evolve beyond static login-time MFA.


Session Architecture

The session is the bridge between authentication and ongoing access.

code
User
 │
 ▼
Authentication
 │
 ▼
Session
 │
 ├── User
 ├── Organization
 ├── Client
 ├── Device
 ├── Authentication Method
 ├── Created At
 ├── Last Seen
 ├── Expiration
 └── Revocation State

A session should be independently revocable.

For example:

code
User has 4 active sessions

Laptop      → Active
Mobile      → Active
Office PC   → Active
Old Laptop  → Revoked

Revoking one session must not require changing the user's password or terminating every other session.


Token Architecture

AccessCore uses two fundamentally different credentials.

code
                 Authentication
                       │
                       ▼
                 ┌────────────┐
                 │  Session   │
                 └─────┬──────┘
                       │
             ┌─────────┴─────────┐
             ▼                   ▼
       Access Token         Refresh Token
       short-lived          long-lived
       API access           session renewal

Access Token

The access token should be:

  • Short-lived
  • Audience restricted
  • Scope restricted
  • Cryptographically signed
  • Bound to an issuer
  • Bound to a subject
  • Time limited

Example:

code
{
  "iss": "https://identity.example.com",
  "sub": "usr_1029",
  "aud": "billing-api",
  "sid": "ses_83921",
  "org": "org_44",
  "scope": "invoice:read invoice:approve",
  "iat": 1755508442,
  "exp": 1755509342,
  "jti": "tok_9a71"
}

The token carries enough information for efficient verification, but it should not become the authoritative database for mutable authorization state.


Why the Session Still Matters

Suppose a user's role changes:

code
09:00
User = Department Manager

09:15
Manager privileges revoked

09:16
Old JWT still has manager-related claims

A purely stateless architecture can continue trusting the old token until expiration.

The session layer gives AccessCore a revocation mechanism.

code
JWT
 │
 ▼
Validate Signature
 │
 ▼
Validate Expiration
 │
 ▼
Validate Session
 │
 ▼
Evaluate Current Authorization

This gives the system both:

  • JWT verification efficiency
  • Server-side revocation capability

Refresh Token Rotation

Refresh tokens should be treated as high-value credentials.

A simplified lifecycle:

code
Refresh Token A
      │
      ▼
Token Refresh Request
      │
      ▼
Validate Token A
      │
      ▼
Invalidate Token A
      │
      ▼
Issue Token B
      │
      ▼
Store Hash(Token B)

If Token A is presented again:

code
Token A
  │
  ▼
Already Used
  │
  ▼
Possible Replay
  │
  ▼
Revoke Token Family
  │
  ▼
Security Event

Refresh-token rotation is specifically recommended as a mechanism for detecting replay of compromised refresh tokens.


Browser Security Model

For browser applications, the architecture should avoid exposing long-lived credentials to JavaScript whenever possible.

A stronger model is:

code
Browser
   │
   │ Secure + HttpOnly Cookie
   ▼
AccessCore Session

rather than:

code
Browser
   │
   ▼
localStorage
   │
   ▼
JavaScript-readable token

The exact browser architecture can vary depending on whether the application uses a backend-for-frontend (BFF), SPA, or traditional server-rendered application, but the security goal remains the same:

Minimize the amount of credential material directly exposed to browser JavaScript.


Authorization Engine

Authentication establishes identity.

The Authorization Engine establishes authority.

The engine receives a structured authorization request:

code
Subject
   │
   ├── user
   ├── organization
   ├── roles
   └── attributes

Action
   │
   └── invoice.approve

Resource
   │
   ├── type = invoice
   ├── id = inv_1029
   └── organization = org_44

Context
   │
   ├── IP
   ├── device
   ├── time
   └── authentication strength

Then:

code
Authorization Request
        │
        ▼
Policy Resolution
        │
        ▼
Role Resolution
        │
        ▼
Permission Resolution
        │
        ▼
Resource Scope Check
        │
        ▼
Contextual Policy Check
        │
        ▼
Allow / Deny

RBAC Is the Foundation, Not the Entire Model

AccessCore starts with RBAC because it is understandable and operationally manageable.

code
Role
  │
  ├── Permission A
  ├── Permission B
  └── Permission C

But enterprise authorization eventually needs more context.

For example:

code
Manager
can approve invoices

BUT

Manager
can only approve invoices

WHERE

invoice.organization == manager.organization
AND
invoice.amount < manager.approval_limit

That is no longer just a role check.

It is a combination of:

code
RBAC
+
Resource Scope
+
Attributes
+
Policy

OWASP notes that RBAC alone can become cumbersome in complex multi-tenant and cross-organizational environments, where ABAC or relationship-based approaches can express more contextual authorization rules.

Therefore, the long-term architecture should be:

code
                    Authorization
                         │
          ┌──────────────┼──────────────┐
          ▼              ▼              ▼
         RBAC           ABAC           ReBAC
          │              │              │
       Roles         Attributes     Relationships
          │              │              │
          └──────────────┼──────────────┘
                         ▼
                  Policy Decision

RBAC remains the primary operational model.

ABAC/ReBAC becomes the extension mechanism.


Permission Model

Permissions should describe capabilities rather than organizational titles.

Bad:

code
admin
manager
employee

Better:

code
user.read
user.invite
user.suspend

invoice.read
invoice.create
invoice.approve
invoice.refund

organization.settings.read
organization.settings.update

Then:

code
Role: Department Manager

Permissions:
    user.read
    user.invite
    invoice.read
    invoice.approve

This means introducing a new role does not require changing application business logic.


Hierarchical Role Resolution

A role hierarchy can be represented as:

code
Organization Owner
       │
       ▼
Administrator
       │
       ▼
Department Manager
       │
       ▼
Team Member
       │
       ▼
Viewer

Effective permissions are inherited:

code
Administrator
    │
    ├── Own permissions
    │
    └── Inherited permissions
            │
            └── Department Manager
                    │
                    └── Team Member

However, hierarchy must not automatically imply unrestricted resource access.

A role can inherit permissions while still being constrained by scope.

code
Administrator
    │
    └── user.invite
          │
          └── Scope = Department A

Delegated Administration

Delegation is one of the most important enterprise capabilities.

Example:

code
Organization Owner
        │
        ▼
Delegates:
user.invite
        │
        ▼
Department Manager
        │
        ▼
Can invite:
Department A users
        │
        X
Cannot:
Grant Organization Owner
Grant Administrator
Modify billing

Delegation should therefore have:

code
Delegator
Delegatee
Permission
Resource Scope
Expiration
Constraints
Audit Record

A delegation itself becomes an auditable authorization object.


Authorization Decision Pipeline

A complete authorization request might look like:

code
User
 │
 ▼
Authenticate
 │
 ▼
Resolve Membership
 │
 ▼
Load Effective Roles
 │
 ▼
Resolve Permission
 │
 ▼
Resolve Resource
 │
 ▼
Validate Tenant
 │
 ▼
Evaluate Scope
 │
 ▼
Evaluate Contextual Policy
 │
 ▼
Check Delegation
 │
 ▼
Decision

The result should be structured.

code
{
  "decision": "allow",
  "subject": "usr_1029",
  "organization": "org_44",
  "resource": "invoice:inv_778",
  "action": "invoice.approve",
  "policy": "invoice_approval_policy",
  "scope": "department:finance",
  "reason": "role_permission_and_scope_match"
}

This becomes extremely useful for debugging and security investigations.


Policy Decision Point vs Policy Enforcement Point

AccessCore should distinguish between:

Policy Enforcement Point

The application endpoint where access is actually blocked or allowed.

code
API
 │
 ▼
PEP
 │
 ▼
Authorization Decision

Policy Decision Point

The component that evaluates the policy.

code
Authorization Request
        │
        ▼
       PDP
        │
        ▼
Allow / Deny

The architecture becomes:

code
Application
    │
    ▼
Policy Enforcement Point
    │
    ▼
Authorization Engine
    │
    ▼
Policy Decision

This separation allows multiple applications to use the same authorization model.


Tenant Isolation Architecture

Tenant isolation should exist at multiple layers.

code
                Tenant Isolation
                      │
       ┌──────────────┼──────────────┐
       ▼              ▼              ▼
    API Layer      Data Layer      Cache Layer
       │              │              │
       ▼              ▼              ▼
    Tenant ID      tenant_id       tenant:key

And additionally:

code
Queue
Storage
Audit
Search
Metrics
Logs
Background Jobs

must also understand tenant boundaries.

A common mistake is:

code
Database = isolated
Redis = shared incorrectly

or:

code
API = tenant-aware
Celery = tenant-unaware

That still creates a tenant-isolation vulnerability.


Database Isolation

The baseline model uses a shared PostgreSQL database with explicit tenant ownership.

code
organizations
      │
      ├──── users
      ├──── memberships
      ├──── roles
      ├──── policies
      └──── resources

Every tenant-owned resource carries:

code
organization_id

Queries become:

code
SELECT *
FROM invoices
WHERE organization_id = :current_organization
AND id = :invoice_id;

Never:

code
SELECT *
FROM invoices
WHERE id = :invoice_id;

and then assume the result belongs to the current tenant.

The tenant boundary should be enforced as close to the data as practical, with database-level controls such as PostgreSQL Row-Level Security considered as defense in depth. OWASP similarly recommends tenant-aware queries and data-access-layer enforcement rather than relying only on API-level checks.


Cache Isolation

Redis keys must contain tenant context where the data is tenant-specific.

Bad:

code
session:user_1029

Better:

code
session:org_44:user_1029

For permissions:

code
permissions:org_44:user_1029

For rate limits:

code
ratelimit:org_44:user_1029

A shared cache key without tenant boundaries can turn a harmless caching bug into a cross-tenant data leak.


Background Job Isolation

Every asynchronous job must carry its tenant context.

Bad:

code
{
  "job": "send_invitation",
  "user_id": "usr_1029"
}

Better:

code
{
  "job": "send_invitation",
  "organization_id": "org_44",
  "user_id": "usr_1029"
}

The worker must validate:

code
organization_id
       │
       ▼
User belongs to organization?
       │
   ┌───┴───┐
  Yes      No
   │        │
 Process   Reject

Never assume a background worker is trusted simply because the request originated inside the platform.


Audit Architecture

Audit logging should be separated from ordinary application logging.

code
Application Event
       │
       ├──────────────► Operational Log
       │
       └──────────────► Security Audit Event
                              │
                              ▼
                        Audit Pipeline
                              │
                              ▼
                       Append-Only Store

An audit record should answer:

code
Who?
What?
When?
Where?
Which organization?
Which resource?
Which authorization decision?
What changed?
Why?

Example:

code
{
  "event_id": "evt_82921",
  "event": "role.assigned",
  "actor_id": "usr_1029",
  "target_id": "usr_5521",
  "organization_id": "org_44",
  "resource": "membership",
  "action": "role.assign",
  "before": {
    "roles": ["team_member"]
  },
  "after": {
    "roles": ["department_manager"]
  },
  "request_id": "req_78291",
  "ip": "redacted",
  "timestamp": "2026-08-18T09:14:02Z"
}

The audit system should ideally be append-only and protected from ordinary administrative modification.

"Immutable" should mean more than simply hiding an update endpoint. A stronger design uses append-only storage, restricted write paths, retention controls, and potentially WORM/object-lock mechanisms depending on compliance requirements.


Event-Driven Architecture

Not every operation should happen synchronously.

Identity changes often trigger multiple secondary workflows.

For example:

code
User Disabled
     │
     ▼
Identity Service
     │
     ├────────► Revoke Sessions
     │
     ├────────► Publish Event
     │
     ├────────► Audit Event
     │
     ├────────► Notify Applications
     │
     └────────► Provisioning Workflow

The core transaction should remain small.

code
BEGIN TRANSACTION

Update User
Create Security Event
Create Outbox Event

COMMIT

Then:

code
Outbox
   │
   ▼
Message Broker / Celery
   │
   ├── Revoke application sessions
   ├── Send notification
   ├── SCIM deprovisioning
   └── External synchronization

This prevents external systems from determining whether the identity transaction succeeds.


Transactional Outbox

For important security events, AccessCore should avoid this pattern:

code
Database Commit
      │
      ▼
Publish Event
      │
      X
   Broker Down

Now the database says the user was disabled, but downstream systems never received the event.

Instead:

code
Database Transaction
       │
       ├── User Updated
       │
       └── Outbox Event Created
              │
              ▼
           COMMIT
              │
              ▼
        Outbox Worker
              │
              ▼
        Message Broker

The database transaction guarantees that the state change and event intent are committed together.


Provisioning Architecture

Enterprise identity becomes significantly more valuable when it manages application lifecycle.

code
Organization
      │
      ▼
User Created
      │
      ▼
Role Assigned
      │
      ▼
Provisioning Event
      │
      ├────────► CRM
      ├────────► Billing
      ├────────► Support
      └────────► Internal Tools

For deprovisioning:

code
User Suspended
      │
      ▼
AccessCore
      │
      ├── Revoke Sessions
      ├── Disable Login
      ├── Publish Event
      ├── SCIM Deprovision
      └── Audit

This turns AccessCore from an authentication service into an actual identity lifecycle platform.


SSO Architecture

AccessCore acts as the central identity broker.

code
                Enterprise IdP
              SAML / OIDC
                    │
                    ▼
            ┌───────────────┐
            │  AccessCore   │
            │ Authentication│
            └───────┬───────┘
                    │
                    ▼
             Internal Identity
                    │
                    ▼
             AccessCore Session
                    │
        ┌───────────┼───────────┐
        ▼           ▼           ▼
       CRM       Billing      Support

External providers establish identity.

AccessCore establishes the organization's internal authorization model.

For OIDC integrations, issuer, audience, signature, expiration, and provider keys should be validated rather than trusting identity claims blindly.


Identity Federation Mapping

External identities must map to internal identities.

code
External Provider
       │
       ▼
provider_subject
       │
       ▼
External Identity Record
       │
       ▼
Internal User
       │
       ▼
Organization Membership

Never use email alone as the permanent identity key.

Prefer:

code
provider
+
provider_subject

because the external provider's stable subject identifier is the actual identity binding.


API Architecture

AccessCore APIs should be divided by domain.

code
/api/v1/auth/*
/api/v1/users/*
/api/v1/organizations/*
/api/v1/memberships/*
/api/v1/roles/*
/api/v1/permissions/*
/api/v1/policies/*
/api/v1/sessions/*
/api/v1/audit/*
/api/v1/sso/*
/api/v1/scim/*

The API layer should not contain the authorization model itself.

Instead:

code
View / Controller
      │
      ▼
Application Service
      │
      ▼
Authorization Service
      │
      ▼
Domain Logic
      │
      ▼
Repository

This prevents authorization logic from spreading across controllers.


Service Layer Architecture

Inside a Django implementation, I would structure the system around domain services rather than putting everything into views or models.

code
accesscore/
│
├── identity/
│   ├── models.py
│   ├── services.py
│   ├── selectors.py
│   └── repositories.py
│
├── authentication/
│   ├── services.py
│   ├── providers/
│   ├── tokens.py
│   └── sessions.py
│
├── authorization/
│   ├── engine.py
│   ├── policies.py
│   ├── permissions.py
│   ├── roles.py
│   └── scopes.py
│
├── organizations/
│   ├── models.py
│   └── services.py
│
├── audit/
│   ├── events.py
│   ├── writer.py
│   └── storage.py
│
├── federation/
│   ├── oidc.py
│   ├── saml.py
│   └── mapping.py
│
├── provisioning/
│   ├── jobs.py
│   ├── scim.py
│   └── workflows.py
│
└── sessions/
    ├── models.py
    └── service.py

The exact Django app boundaries can change, but the domain boundaries should remain explicit.


Authorization Service Contract

Applications should be able to ask AccessCore a simple question.

code
decision = authorization.check(
    subject=user,
    organization=organization,
    action="invoice.approve",
    resource=invoice,
)

The application should not need to know:

code
How roles are inherited
How permissions are cached
How delegation works
How policies are evaluated
How scopes are resolved

Those are AccessCore's responsibilities.


Authorization Caching

Authorization decisions are attractive caching candidates because permission checks can occur on almost every request.

code
Request
   │
   ▼
Authorization Check
   │
   ▼
Redis
   │
 ┌─┴──────────────┐
 │                │
Hit              Miss
 │                │
 ▼                ▼
Decision       Authorization
                  Engine
                     │
                     ▼
                   Cache

But authorization caching introduces a dangerous problem:

How quickly does a permission revocation become effective?

Therefore cache keys should include a version.

code
authz:
org_44:
user_1029:
version_17

When permissions change:

code
Permission Change
      │
      ▼
Increment Authorization Version
      │
      ▼
Old Cache Becomes Invalid

This is often safer than attempting to delete every possible authorization cache entry individually.


Failure Architecture

Enterprise identity systems must define failure behavior explicitly.

Redis unavailable

Do not automatically fail open.

code
Redis Down
   │
   ▼
Can authorization be safely evaluated?
   │
 ┌─┴────────┐
Yes        No
 │          │
Continue   Fail Closed

For sensitive authorization paths, fail-closed behavior is generally safer.


PostgreSQL unavailable

Authentication and authorization should not invent state.

code
Database unavailable
        │
        ▼
Cannot verify authoritative state
        │
        ▼
Reject sensitive operation

Availability matters, but an IAM system must not trade authorization correctness for availability without an explicit risk decision.


Rate Limiting Architecture

Rate limiting should operate at multiple levels.

code
Global
  │
  ├── IP
  ├── Client
  ├── Organization
  ├── User
  └── Endpoint

Authentication endpoints require particularly aggressive protection.

Example:

code
/login
   │
   ├── IP limit
   ├── Account limit
   ├── Device signal
   └── Progressive delay

This prevents a single compromised or malicious client from exhausting shared authentication infrastructure.


Security Event Detection

Audit logging records what happened.

Security monitoring determines whether it is suspicious.

code
Audit Events
     │
     ▼
Security Event Pipeline
     │
     ▼
Detection Rules
     │
     ├── Impossible login pattern
     ├── Mass permission changes
     ├── Repeated MFA failures
     ├── Privilege escalation
     ├── Cross-tenant access attempt
     └── Refresh-token replay
     │
     ▼
Alert / Automated Response

For example:

code
10 role assignments
       │
       ▼
Same administrator
       │
       ▼
Within 30 seconds
       │
       ▼
Security Rule Triggered
       │
       ▼
Alert

This transforms audit data into an operational security capability.


Observability Architecture

AccessCore needs three observability layers.

Logs

code
What happened?

Metrics

code
How often?
How fast?
How many failures?

Traces

code
Where did the request spend time?

Example:

code
Request
 │
 ├── Gateway: 5ms
 │
 ├── JWT Validation: 1ms
 │
 ├── Session: 2ms
 │
 ├── Authorization: 7ms
 │
 ├── PostgreSQL: 4ms
 │
 └── Response: 20ms

Every request should have a correlation identifier:

code
request_id

and security events should reference it.


Security Invariants

The architecture should define rules that must never be violated.

code
Invariant 1
A user cannot access another organization's resources.

Invariant 2
An unauthenticated request cannot reach protected resources.

Invariant 3
A revoked session cannot authenticate a new request.

Invariant 4
A role cannot grant permissions outside its defined scope.

Invariant 5
A delegated administrator cannot grant authority beyond its delegation.

Invariant 6
Security-sensitive state changes generate audit events.

Invariant 7
Authorization cannot depend on frontend behavior.

Invariant 8
Tenant context cannot be trusted solely from client input.

These invariants should become automated tests, not merely documentation.

OWASP specifically recommends automated authorization regression testing for horizontal escalation, vertical escalation, and tenant-isolation failures.


Authorization Test Architecture

Testing should use an explicit authorization matrix.

code
                 Resource
                 │
         ┌───────┼────────┐
         ▼       ▼        ▼
       User    Invoice   Settings

Role
 │
 ├── Owner
 ├── Admin
 ├── Manager
 ├── Member
 └── Viewer

Then test:

code
Owner + invoice.approve = ALLOW
Admin + invoice.approve = ALLOW
Manager + invoice.approve = CONDITIONAL
Member + invoice.approve = DENY
Viewer + invoice.approve = DENY

And critically:

code
Organization A User
        │
        ▼
Organization B Resource
        │
        ▼
DENY

This should be part of CI/CD rather than a manual security exercise performed only before release.


Deployment Architecture

A production deployment can start relatively simply.

code
                         Internet
                            │
                            ▼
                     CDN / WAF
                            │
                            ▼
                     Load Balancer
                            │
              ┌─────────────┼─────────────┐
              ▼             ▼             ▼
           API-1         API-2         API-3
              │             │             │
              └─────────────┼─────────────┘
                            │
              ┌─────────────┼──────────────┐
              ▼             ▼              ▼
         PostgreSQL       Redis         Celery
              │                            │
              │                            ▼
              │                     Worker Pool
              │
              ▼
         Audit Storage

API instances remain stateless where possible.

State that must survive process restarts belongs in durable infrastructure.


High Availability

The identity layer becomes a dependency for every application.

Therefore:

code
AccessCore Down
      │
      ▼
CRM Login      X
Billing        X
Support        X
Admin          X

Identity availability becomes organizational availability.

The architecture should therefore consider:

code
Multiple API Instances
        │
        ▼
Load Balancer
        │
        ▼
Highly Available PostgreSQL
        │
        ▼
Redis HA
        │
        ▼
Multiple Workers

The goal is not merely to make AccessCore fast.

The goal is to prevent identity from becoming a single point of organizational failure.


Disaster Recovery

Identity data requires stronger recovery planning than ordinary application data.

Critical recovery assets include:

code
User identities
Organizations
Memberships
Roles
Permissions
Policies
Sessions
Audit events
Federation configuration
Signing keys
Encryption keys

Backups should therefore be:

code
Encrypted
Versioned
Access-controlled
Regularly tested
Geographically redundant

Most importantly:

A backup that has never been restored is not a proven recovery strategy.


Key Management Architecture

Signing keys should not live permanently inside application source code or environment variables without a proper key-management strategy.

Conceptually:

code
AccessCore
    │
    ▼
Key Management System
    │
    ├── Active Signing Key
    ├── Previous Signing Key
    └── Future Signing Key

This enables key rotation.

During rotation:

code
Old Key ──────────────► Verify
New Key ──────────────► Sign

Eventually:

code
Old Key
   │
   ▼
Retired

Consumers should be able to obtain public keys through a controlled JWKS endpoint.


Authorization as a Control Plane

At this point, the architecture becomes clearer.

AccessCore is not simply:

code
Login Server

It is:

code
                 AccessCore Control Plane

        ┌──────────────────────────────────┐
        │                                  │
        │          Identity                │
        │             │                    │
        │             ▼                    │
        │       Authentication             │
        │             │                    │
        │             ▼                    │
        │       Session Management         │
        │             │                    │
        │             ▼                    │
        │       Authorization              │
        │             │                    │
        │             ▼                    │
        │       Policy Enforcement         │
        │             │                    │
        │             ▼                    │
        │       Audit / Security           │
        │             │                    │
        │             ▼                    │
        │       Provisioning               │
        │                                  │
        └──────────────────────────────────┘
                         │
        ┌────────────────┼────────────────┐
        ▼                ▼                ▼
       CRM            Billing          Support

Applications become consumers of the control plane.


End-to-End Authorization Example

Consider:

A department manager attempts to approve a $5,000 invoice.

The request travels through the architecture.

code
1. Request
   │
   ▼
2. API Gateway
   │
   ▼
3. Access Token Validation
   │
   ▼
4. Session Validation
   │
   ▼
5. Resolve Organization
   │
   ▼
6. Resolve Membership
   │
   ▼
7. Resolve Department Manager Role
   │
   ▼
8. Resolve invoice.approve Permission
   │
   ▼
9. Load Invoice
   │
   ▼
10. Verify Invoice Organization
   │
   ▼
11. Evaluate Department Scope
   │
   ▼
12. Evaluate Approval Limit
   │
   ▼
13. Authorization Decision
   │
   ├── DENY ──► Audit ──► 403
   │
   └── ALLOW
          │
          ▼
      Approve Invoice
          │
          ▼
      Audit Event

This is the level at which an IAM architecture becomes useful to engineers.

The architecture doesn't simply say:

"Managers can approve invoices."

It defines how the system proves that a particular manager is allowed to approve a particular invoice at a particular moment.


Complete End-to-End System Flow

Putting everything together:

code
                         CLIENT
                            │
                            ▼
                     CDN / WAF / LB
                            │
                            ▼
                       API Gateway
                            │
                            ▼
                  Authentication Layer
                            │
                ┌───────────┴───────────┐
                ▼                       ▼
          Token Validation        Session Validation
                │                       │
                └───────────┬───────────┘
                            ▼
                     Tenant Resolver
                            │
                            ▼
                   Membership Resolver
                            │
                            ▼
                   Authorization Engine
                            │
            ┌───────────────┼────────────────┐
            ▼               ▼                ▼
           RBAC            ABAC             ReBAC
            │               │                │
            └───────────────┼────────────────┘
                            ▼
                      Policy Decision
                            │
                     ┌──────┴──────┐
                     ▼             ▼
                   DENY           ALLOW
                     │             │
                     ▼             ▼
                   Audit       Application
                                 Logic
                                   │
                                   ▼
                              Data Layer
                                   │
                 ┌─────────────────┼────────────────┐
                 ▼                 ▼                ▼
             PostgreSQL          Redis           Services
                 │                                  │
                 └────────────────┬─────────────────┘
                                  ▼
                           Event / Outbox
                                  │
                                  ▼
                           Celery / Workers
                                  │
                 ┌────────────────┼────────────────┐
                 ▼                ▼                ▼
             Provisioning      Notification      Audit

This is the actual architectural backbone of AccessCore.


Architecture Evolution

The platform can evolve without replacing the foundation.

code
Phase 1
Authentication
     │
     ▼
Phase 2
Sessions + Basic RBAC
     │
     ▼
Phase 3
Multi-Tenant Identity
     │
     ▼
Phase 4
Hierarchical RBAC
     │
     ▼
Phase 5
Delegated Administration
     │
     ▼
Phase 6
OIDC / SAML Federation
     │
     ▼
Phase 7
SCIM Provisioning
     │
     ▼
Phase 8
ABAC / ReBAC Policies
     │
     ▼
Phase 9
Security Intelligence
     │
     ▼
Phase 10
Enterprise Identity Control Plane

The important architectural decision is to establish the right boundaries early.

The implementation can become more sophisticated later without changing the fundamental model.


What I Would Build Today

For the initial production architecture:

code
Application
    │
    ▼
Django + Django REST Framework
    │
    ├── Authentication
    ├── Authorization
    ├── Tenant Context
    ├── Session Management
    ├── Organization Management
    └── Audit Events
    │
    ▼
PostgreSQL
    │
    ├── Identity
    ├── Organizations
    ├── Memberships
    ├── Roles
    ├── Permissions
    ├── Policies
    └── Sessions

Redis
    │
    ├── Rate Limiting
    ├── Authorization Cache
    └── Short-lived State

Celery
    │
    ├── Provisioning
    ├── Notifications
    ├── Synchronization
    └── Background Security Jobs

Object / Immutable Audit Storage
    │
    └── Security Audit Trail

I would not start by splitting every box into a microservice.

The architectural boundaries should exist first.

The deployment boundaries can evolve later.

A modular Django monolith can provide:

code
Clear domain boundaries
+
Strong transaction semantics
+
Simple deployment
+
Lower operational complexity

while still allowing individual domains to become independent services when scale or organizational boundaries justify it.


What I Would Avoid

Avoid Microservices for the sake of looking enterprise

code
10 services
10 deployments
10 failure modes
10 authentication paths

does not automatically produce better architecture.

The goal is clear boundaries, not maximum service count.


Avoid Stateless Authorization as the Only Revocation Mechanism

JWTs are useful for distributed verification, but authorization state changes.

Sessions, token families, policy versions, or another revocation mechanism should exist for security-sensitive systems.


Avoid Tenant Context as a Request Parameter

code
GET /users?organization_id=org_44

should never mean:

"The caller is authorized for org_44."

Tenant context must be derived and validated from trusted identity state.


Avoid Role Names in Business Logic

code
if user.role == "admin":

creates architectural coupling.

Prefer:

code
authorization.check(
    subject=user,
    action="invoice.approve",
    resource=invoice,
)

Avoid Putting Authorization Entirely Inside JWT Claims

JWT claims are useful for carrying identity and coarse-grained context.

They should not become the permanent source of truth for mutable permissions.


Avoid Treating Audit Logging as Debug Logging

Application logs answer:

code
"What happened inside the software?"

Audit logs answer:

code
"Who performed this security-sensitive action, against what resource, under which organization and authorization context?"

Those are different systems.


The Architectural Contract

The most important output of AccessCore is not a JWT.

It is a trustworthy decision:

code
SUBJECT
   │
   ▼
IDENTITY
   │
   ▼
ORGANIZATION
   │
   ▼
MEMBERSHIP
   │
   ▼
ROLE
   │
   ▼
PERMISSION
   │
   ▼
RESOURCE
   │
   ▼
POLICY
   │
   ▼
DECISION
   │
   ├── ALLOW
   └── DENY

Everything else exists to make that decision:

  • Correct
  • Fast
  • Revocable
  • Auditable
  • Tenant-safe
  • Explainable
  • Consistent

That is the real architecture of an enterprise IAM platform.


##Final Architecture Principle

The deepest architectural decision in AccessCore is not Django, PostgreSQL, Redis, JWT, Celery, SAML, or OIDC.

It is this:

No application should have to independently answer the question of who a user is, which organization they belong to, or whether they are allowed to perform an action.

AccessCore centralizes that responsibility.

Applications consume the decision.

code
                    ┌───────────────────────┐
                    │      AccessCore       │
                    │                       │
                    │ Identity              │
                    │ Authentication        │
                    │ Authorization         │
                    │ Tenant Isolation      │
                    │ Sessions              │
                    │ Federation            │
                    │ Provisioning          │
                    │ Audit                 │
                    └───────────┬───────────┘
                                │
              ┌─────────────────┼─────────────────┐
              ▼                 ▼                 ▼
             CRM              Billing           Support
              │                 │                 │
              └─────────────────┼─────────────────┘
                                ▼
                     One trusted identity
                         control plane
status: blueprint_loaded

$ ls related_articles

status: end_of_file

Related Blueprints

Continue exploring related architecture patterns.

AccessCore IAM (Enterprise Identity & Access Management Platform)

Enterprise-grade identity and access management platform designed to centralize authentication, authorization, user lifecycle management, RBAC governance, and organizational security controls.

01Multi-tenant identity core
02Hierarchical RBAC engine
03SSO & token architecture
04Audit-ready security logging
05Delegated administration model

Identity & Access Management

Implementing secure authentication, authorization, and access control across complex business systems.

01JWT authentication
02Refresh token rotation
03Session security
04Permission enforcement
05Token revocation

Authorization & RBAC

Designing role-based access systems with hierarchical permissions and organization-aware security models.

01Hierarchical roles
02Permission inheritance
03Object-level authorization
04Multi-role support
05Fine-grained policies