Identity and Access Management Architecture

Design IAM systems that balance security with usability. Covers authentication protocols, authorization models, identity federation, session management, API key patterns, machine-to-machine auth, and the IAM architecture decisions that protect without paralyzing.

Identity and Access Management Architecture defines the structure, protocols, data flows, and operational practices that govern how users, services, and systems authenticate and authorize access across a distributed environment. It matters when a system must securely and consistently manage identities across multiple applications, cloud environments, and hybrid infrastructures—especially when users, devices, and APIs must dynamically gain or lose access based on policies, roles, and context.

Core Identity Lifecycle

User identity begins with provisioning: creating a user record with attributes such as email, given_name, family_name, preferred_username, locale, and timezone. This record is stored in an identity store—typically a relational database or directory service like LDAP. The identity is uniquely identified by a subject_id (e.g., sub in JWT tokens), and each identity may have one or more identities (e.g., email, phone, saml:entity_id).

A user signs up via a web form or API endpoint, triggering a POST /api/v1/users with:

{
  "email": "[email protected]",
  "given_name": "Alice",
  "family_name": "Smith",
  "preferred_username": "alice-smith",
  "locale": "en-US",
  "timezone": "America/New_York"
}

The system validates the email, assigns a unique subject_id (e.g., sub=usr_9a2b3c), and persists the record. The identity is now *provisioned*. Upon login, the user receives a session cookie with Set-Cookie: auth_session=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...; HttpOnly; Secure; SameSite=Lax.

Identity Verification and Multi-Factor Authentication

After sign-up, the system sends a verification email with a link containing a token parameter. The token is a JWT with iss="verify-email", aud="identity-service", and exp set to 1 hour. The endpoint GET /api/v1/verify-email?token=... validates the token and updates the user’s email_verified flag to true.

Multi-factor authentication (MFA) is implemented using the TOTP (Time-based One-Time Password) standard. The user enrolls by scanning a QR code generated via GET /api/v1/mfa/qrcode?user_id=usr_9a2b3c. The QR code contains a otpauth://totp/Alice%20Smith:[email protected]?secret=JBSWY3DPEHPK3PXP&issuer=ExampleCorp&algorithm=SHA1&digits=6&period=30.

The client app (e.g., Google Authenticator) reads the secret and generates 6-digit codes every 30 seconds. On login, the user enters the current code. The service validates it via POST /api/v1/mfa/verify:

{
  "user_id": "usr_9a2b3c",
  "code": "723194",
  "timestamp": "2024-04-10T12:00:00Z"
}

The response includes status: "success" and mfa_verified: true, setting user.mfa_enabled = true. If the code is invalid, the service returns status: "failed", error: "invalid_code", and retry_after: 30.

Session Management and Token Lifetime

Sessions are maintained via JWTs issued at login. The JWT header specifies alg: "HS256" and typ: "Bearer". The payload includes standard claims:

{
  "sub": "usr_9a2b3c",
  "name": "Alice Smith",
  "email": "[email protected]",
  "roles": ["user", "editor"],
  "permissions": ["posts:read", "posts:write"],
  "exp": 1712793600,
  "iat": 1712787600,
  "auth_time": 1712787600,
  "session_id": "sess_abc123"
}

The exp (expiration) claim determines session lifetime. The service validates the token on each request by verifying the signature using a shared secret and checking exp and iat. A request to GET /api/v1/posts with Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... returns 401 Unauthorized if the token is expired or invalid.

Authorization and Access Control

Access to resources is governed by policies that define who can do what. Policies are expressed using a declarative language—typically JSON or YAML—and are evaluated at runtime.

Role-Based Access Control (RBAC)

Users are assigned roles, and roles grant permissions. A role is defined in a policy file:

name: editor
description: "Can read and write posts"
permissions:
  - "posts:read"
  - "posts:write"
  - "posts:delete"
  - "users:read"
  - "users:write"

The system loads policies at startup from ./policies/*.yaml. During authorization, it collects all roles assigned to a user and combines their permissions into a flat set.

When a service checks access, it calls POST /api/v1/authorize with:

{
  "user_id": "usr_9a2b3c",
  "resource": "posts:123",
  "action": "read"
}

The service evaluates the policy set and returns:

{
  "allowed": true,
  "reason": "User has 'editor' role with 'posts:read' permission",
  "policy": "editor"
}

If allowed: false, the response includes reason: "missing_permission" and missing_permission: "posts:read".

Attribute-Based Access Control (ABAC)

For more complex access needs, ABAC uses attributes of the user, resource, and environment. A policy evaluates Boolean expressions over these attributes.

Example: A policy allows editing a post only if the user is in the same department as the post author and the post is not published.

{
  "name": "edit_unpublished_post",
  "description": "Allow editing a post if user's department matches post.author.department and post.status != 'published'",
  "conditions": [
    {
      "match": "user.department",
      "equals": "post.author.department"
    },
    {
      "match": "post.status",
      "not_equals": "published"
    }
  ]
}

During authorization, the service evaluates the conditions using a set of available attributes:

{
  "user": {
    "id": "usr_9a2b3c",
    "name": "Alice Smith",
    "department": "engineering",
    "role": "editor"
  },
  "resource": {
    "type": "post",
    "id": "123",
    "title": "Building Identity Systems",
    "author": {
      "id": "usr_7d8e9f",
      "name": "Bob Johnson",
      "department": "engineering"
    },
    "status": "draft"
  },
  "context": {
    "time": "2024-04-10T12:30:00Z",
    "ip": "192.168.1.100",
    "device_type": "mobile"
  }
}

The evaluation returns allowed: true, reason: "user.department matches post.author.department and post.status is not published".

Federation and Single Sign-On

Federated identity enables users to sign in using external identity providers (IdPs), such as Google, GitHub, or Azure AD. The system supports the OpenID Connect (OIDC) protocol.

OIDC Flow: Authorization Code with PKCE

A web application initiates login by redirecting to the IdP with:

GET /auth/authorize?
  response_type=code
  &client_id=app_xyz
  &redirect_uri=https://app.example.com/callback
  &scope=openid profile email
  &state=abc123
  &code_challenge=xyz789
  &code_challenge_method=S256

The IdP returns a 302 Found redirect to https://app.example.com/callback?code=auth_code_123&state=abc123.

The client exchanges the code for tokens via POST /auth/token:

{
  "grant_type": "authorization_code",
  "code": "auth_code_123",
  "redirect_uri": "https://app.example.com/callback",
  "code_verifier": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6a7b8c9d0e1f2",
  "client_id": "app_xyz"
}

The response includes:

{
  "access_token": "at_abc123",
  "id_token": "idt_123abc",
  "refresh_token": "rt_456def",
  "expires_in": 3600,
  "scope": "openid profile email"
}

The id_token is a JWT with iss, aud, exp, sub, and email claims. The system validates the JWT, extracts the user’s sub, and creates or updates the local user record.

SAML 2.0 for Enterprise Integration

For legacy enterprise systems, SAML 2.0 is used. The IdP generates a SAML Response as XML:

<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
                xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
                ID="_abc123"
                Version="2.0"
                IssueInstant="2024-04-10T12:00:00Z">
  <saml:Issuer>https://idp.example.com</saml:Issuer>
  <samlp:Status>
    <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
  </samlp:Status>
  <saml:Assertion ID="_xyz789"
                  IssueInstant="2024-04-10T12:00:00Z"
                  Version="2.0">
    <saml:Subject>
      <saml:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">[email protected]</saml:NameID>
      <saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
        <saml:SubjectConfirmationData NotOnOrAfter="2024-04-10T12:15:00Z"/>
      </saml:SubjectConfirmation>
    </saml:Subject>
    <saml:Conditions NotBefore="2024-04-10T12:00:00Z" NotOnOrAfter="2024-04-10T12:15:00Z">
      <saml:AudienceRestriction>
        <saml:Audience>https://app.example.com</saml:Audience>
      </saml:AudienceRestriction>
    </saml:Conditions>
    <saml:AttributeStatement>
      <saml:Attribute Name="email" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
        <saml:AttributeValue>[email protected]</saml:AttributeValue>
      </saml:Attribute>
      <saml:Attribute Name="roles" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
        <saml:AttributeValue>editor</saml:AttributeValue>
        <saml:AttributeValue>admin</saml:AttributeValue>
      </saml:Attribute>
    </saml:AttributeStatement>
  </saml:Assertion>
</samlp:Response>

The service parses the SAML response, validates the signature, checks Audience, NotOnOrAfter, and NameID, and maps attributes to local user fields.

Identity Lifecycle Automation and Events

User lifecycle events trigger actions across systems. Events are published to a message broker (e.g., Kafka, RabbitMQ) as JSON messages.

User Provisioning Event

On user creation, a user.created event is published:

{
  "event": "user.created",
  "timestamp": "2024-04-10T12:00:00Z",
  "data": {
    "user_id": "usr_9a2b3c",
    "email": "[email protected]",
    "given_name": "Alice",
    "family_name": "Smith",
    "preferred_username": "alice-smith",
    "created_at": "2024-04-10T12:00:00Z",
    "roles": ["user"],
    "attributes": {
      "department": "engineering",
      "location": "San Francisco"
    }
  }
}

Other services consume this event to provision accounts in downstream systems:

User Deactivation and SSO Session Management

When a user is deactivated (e.g., via PATCH /api/v1/users/usr_9a2b3c with status: "inactive"), the system emits user.deactivated:

{
  "event": "user.deactivated",
  "timestamp": "2024-04-10T13:00:00Z",
  "data": {
    "user_id": "usr_9a2b3c",
    "reason": "left_company",
    "deactivated_at": "2024-04-10T13:00:00Z"
  }
}

The system invalidates all active sessions for the user by updating the sessions table:

UPDATE sessions SET active = false, invalidated_at = NOW()
WHERE user_id = 'usr_9a2b3c' AND active = true;

Additionally, it broadcasts a user.logout event to invalidate SSO sessions across all IdPs:

{
  "event": "user.logout",
  "timestamp": "2024-04-10T13:00:00Z",
  "data": {
    "user_id": "usr_9a2b3c",
    "logout_url": "https://idp.example.com/logout?user_id=usr_9a2b3c"
  }
}

The IdP handles the logout by clearing the user’s session and redirecting to a logout page.

Failure Modes and Operational Nuances

Common Identity Artifacts and Data Formats

The architecture is not about a tool or a platform—it is about the consistent flow of identity, the rules of access, and the protocols that bind systems together across time and trust boundaries.

This page was rewritten on 16 October 2026. It replaced a templated version that was not specific to its own title. How these pages are written and checked. If anything here is wrong, tell us at [email protected] and we will correct it.