Identity in 2026
Frame the modern identity landscape: passwordless-default, federated-by-default, agents-everywhere. The world as it is now, not historical narrative.
Why Identity Is the New Perimeter
Think about the last time you accessed a work application. You probably weren't inside a corporate office, connected to a managed LAN, sitting behind a hardware firewall stack. You were somewhere else — a home network, a coffee shop, a hotel — and you authenticated with credentials tied to your identity, not to your network location. The application had no idea where you were sitting. It only knew who you claimed to be, verified that claim, and made an access decision based on it. That shift — from where you are to who you are — is the defining architectural change in security over the past decade, and it has moved identity from a supporting layer into the primary control plane for virtually every meaningful access decision in modern systems.
The Castle and the Cloud: How the Perimeter Collapsed
The classic security model was spatial. A corporate network was a perimeter — a defined boundary, enforced by firewalls and physical access controls, inside which assets lived and inside which traffic was implicitly trusted. The mental model was a castle with a moat: threats came from outside, defenders guarded the walls, and once you were inside, you were presumed to belong there. Network location served as a proxy for identity.
This model worked under a specific set of conditions: applications ran on-premises, employees worked from managed devices on managed networks, and the number of external integration points was small and tightly controlled.
Cloud infrastructure dismantled those conditions systematically. When applications move to cloud providers, they no longer live inside your network. When development teams adopt SaaS tooling, the data flows to services your firewall never touches. When remote and hybrid work becomes normalized, employees authenticate from networks you don't control. The moat doesn't surround anything meaningful anymore. The castle is distributed across a dozen cloud regions and a hundred SaaS vendors, and the "inside" no longer exists as a coherent concept.
OLD MODEL: Network Perimeter
[ Internet ] ──firewall──► [ Internal Network ]
│
(implicitly trusted)
│
┌─────────────┼─────────────┐
│ │ │
[App A] [App B] [DB C]
Trust signal: "You are inside the network"
Access control: Coarse, network-layer, binary
NEW MODEL: Identity as Perimeter
[ Internet ] ◄──────────────────────────────────────►
│ │ │
[Cloud SaaS] [Cloud Infra] [Remote Work]
│ │ │
└────────────────────┴────────────────────┘
│
Identity Provider (IdP)
┌─────────────────────┐
│ Who are you? │
│ What can you do? │
│ Is this token valid? │
└─────────────────────┘
│
Fine-grained, per-request decisions
Trust signal: "You hold a valid, scoped credential"
Access control: Fine-grained, identity-layer, contextual
The result is that every API endpoint, every data store, every service mesh boundary now needs to make an access decision without the luxury of trusting network location. The question every system must answer has changed from "is this request coming from inside?" to "does this principal have a verified identity with sufficient privilege to do this specific thing?"
💡 Mental Model: In the old model, the building lobby was the security checkpoint and the badge was optional inside. In the new model, there is no lobby. Every door has its own reader, and every door checks the badge independently. Identity is the badge, and it must be valid everywhere.
Identity as the Access Control Plane
When network location stops being a trust signal, identity becomes the control plane — the layer through which all access decisions are made and enforced. In practical terms, this means the answer to "can this entity do this thing?" is derived entirely from:
- Who or what the entity claims to be
- Whether that claim has been cryptographically verified
- What privileges are associated with that verified identity
APIs that previously lived safely "inside" a network boundary now sit exposed to the internet — or at minimum, exposed to other services that are themselves exposed to the internet. Access to those APIs is gated by tokens, certificates, and signed assertions rather than by firewall rules.
🎯 Key Principle: Identity is not just authentication. It is the continuous thread that connects who initiated an action, through what it was permitted to do, to what it actually did — and that thread must be verifiable, auditable, and revocable at every point in the chain.
Consider a concrete scenario: a microservice that writes records to a database. In a perimeter-based architecture, this might have worked through network trust alone — the service was on the internal network, so the database accepted its connections. In an identity-based architecture, the service holds a workload credential — a short-lived certificate or token issued by an identity authority — and the database validates that credential before accepting the connection. The network path may still be restricted, but the access decision is made on identity, not geography.
The Three Actors Modern Identity Systems Must Handle
One of the most consequential shifts in identity architecture is the expansion of who needs an identity. A system designed only around human users will systematically fail to secure the majority of its principals. Modern identity systems must handle three distinct categories of actor, each with different lifecycle patterns, credential types, and risk profiles.
Humans
Human identities are what most people picture first: a person logging into an application with some form of credential. The human identity lifecycle maps to an employment or customer relationship — onboarding, role changes, offboarding — and authentication typically involves something the human knows, possesses, or is. Human identity is well-understood compared to the other two categories, but it remains the most visible attack surface because humans are susceptible to phishing, credential reuse, and social engineering in ways that automated actors are not.
Services and Workloads
Service and workload identities represent the software systems that call other software systems: a backend API calling a payment processor, a microservice authenticating to a message queue, a deployment pipeline writing artifacts to a registry. These principals have no human in the loop at authentication time. They can't receive an SMS one-time code or tap a hardware key.
Workload identities are typically established through client certificates issued by an internal certificate authority, short-lived tokens issued by a workload identity platform, or API keys and client secrets — though the latter carry significant rotation and storage risks.
The critical difference from human identities is lifecycle velocity. A container might spin up, authenticate, perform work, and terminate in under a minute. The identity system must issue and validate credentials at that speed, and revocation must be near-instantaneous.
Automated Agents
Automated agents are a newer and rapidly expanding category: software that acts on behalf of a human or an organization with some degree of autonomy. LLM-based assistants that make API calls, robotic process automation systems, CI/CD pipelines that open pull requests and deploy code, and orchestration systems that chain multiple services together all fall here.
Agents introduce a layered identity problem: an agent may hold credentials issued by a human's delegation, but it acts independently. The question is not just "who is this?" but "who authorized this agent to act, under what constraints, and is this specific action within those constraints?"
Three Actor Categories in a Modern System
┌─────────────────┬──────────────────┬──────────────────────┐
│ HUMANS │ WORKLOADS │ AGENTS │
├─────────────────┼──────────────────┼──────────────────────┤
│ • Employees │ • Microservices │ • LLM assistants │
│ • Customers │ • CI/CD pipelines│ • Automation bots │
│ • Admins │ • Cloud functions│ • Orchestrators │
├─────────────────┼──────────────────┼──────────────────────┤
│ Auth: passkeys, │ Auth: mTLS, │ Auth: delegated │
│ FIDO2, SSO │ workload tokens │ tokens, OAuth scopes │
├─────────────────┼──────────────────┼──────────────────────┤
│ Session-oriented│ Stateless, fast │ Contextual, │
│ │ credential churn │ constrained scope │
└─────────────────┴──────────────────┴──────────────────────┘
🤔 Did you know? In many organizations with significant cloud and automation footprints, non-human identities — services, pipelines, and agents — outnumber human identities by a wide margin. The practical implication is that designing identity systems primarily around human login flows leaves the majority of principals under-governed.
Why Identity Failures Are the Leading Breach Vector
When identity is the control plane, attacking identity is the highest-leverage path into a system. Major incident post-mortems consistently identify credential-related failures as the initiating vector in a substantial proportion of significant breaches. The patterns repeat reliably enough to understand as categories.
Credential theft covers any scenario in which a valid credential is obtained by an unauthorized party — phishing attacks that harvest passwords or session cookies, secrets checked into version control, API keys in client-side JavaScript, or plaintext credentials in application logs. Once a credential is stolen, the attacker is indistinguishable from a legitimate principal at the identity layer. No network anomaly triggers. The traffic looks correct because the credential is correct. This is why the movement toward phishing-resistant, hardware-bound credentials is architecturally significant rather than just a usability improvement.
Misconfigured federation occurs when trust relationships between an application and an external identity provider are set up incorrectly, allowing tokens meant for one service to be accepted by another. Concrete examples include an application that validates a token's signature but doesn't check the aud (audience) claim, or an OAuth integration that accepts access tokens without verifying the issuer. Later sections cover the specific validation steps that prevent these failures.
Over-privileged credentials are perhaps the most pervasive failure mode because they don't require any attack to initiate — they exist as configuration debt. A service account with administrator-level permissions because that was the path of least resistance during setup. A long-lived API key with read-write access to an entire data store. The risk isn't only that these credentials exist, but that their blast radius is enormous when compromised. A short-lived, tightly scoped credential gives an attacker a narrow, time-limited window. A long-lived, broad credential gives an attacker persistent, wide access.
⚠️ Common Mistake: Treating credential scope and lifetime as performance optimizations rather than security controls. Broader scopes and longer lifetimes reduce the number of token exchanges required — which is why teams default to them. The security cost is invisible until a breach.
📋 Quick Reference: Identity Failure Patterns
| Failure Category | Root Cause | Concrete Consequence |
|---|---|---|
| Credential theft | Secret stored or transmitted insecurely | Attacker authenticates as legitimate principal |
| Misconfigured federation | Missing token claim validation | Cross-service token injection or replay |
| Over-privileged tokens | Scope set broader than required | Large blast radius on compromise |
| Unmanaged non-human identities | Agent/service accounts treated as afterthought | No rotation, no audit, persistent access after compromise |
The Mental Model That Unifies the Rest of This Lesson
Every section that follows builds on a single underlying premise: identity is infrastructure, not an add-on to infrastructure. It is as foundational to a modern system's security posture as TLS is to its transport security — not optional, not configurable after the fact.
The three actors — humans, workloads, and agents — each require verified identities. Those identities are established through authentication mechanisms, represented in tokens and credentials, and enforced through authorization policies. Each of those terms has a precise meaning that shapes how you read documentation, design systems, and debug incidents.
🧠 Mnemonic: When evaluating any access control decision, ask W-W-S: Who is the principal? What is their verified credential? Scope — what are they specifically permitted to do? A system that can answer all three questions clearly for every actor category has the foundation of a sound identity model.
The Three Default Postures Shaping Identity Today
If you look at how a modern organization's identity infrastructure actually behaves — rather than how legacy documentation describes it — three structural patterns dominate. Authentication has moved away from shared-secret passwords as the standard path. Applications delegate authentication to external providers rather than managing credentials themselves. And non-human actors now participate in identity flows at a scale that exceeds human users in many environments. These aren't aspirational trends; they are the defaults that shape how new systems are built and how existing systems are being migrated.
Posture 1: Passwordless-by-Default
For decades, the shared-secret password — a string of characters known to both the user and the system — was the canonical credential. That model has a structural weakness: the secret exists in two places simultaneously, which means it can be stolen from either. Phishing, credential-stuffing attacks, and server-side breaches all exploit the same root cause.
Passwordless authentication eliminates the shared secret by replacing it with a cryptographic keypair. The private key never leaves the device; the server stores only the public key. During authentication, the device signs a server-generated challenge with the private key, and the server verifies the signature against the stored public key. There is no password to phish and no credential database worth stealing in the traditional sense.
The ecosystem that makes this practical has three interlocking components:
Passkeys are the user-facing artifact — a discoverable credential stored in the operating system's credential manager (or a hardware security key) and synchronized across a user's devices via an encrypted keychain. When a user registers with a service, the platform generates a keypair and registers the public key with the service. Login is a local biometric verification followed by a cryptographic signature — the user never types a password.
Platform authenticators are the hardware and OS facilities that execute and protect the cryptographic operations. A device TPM (Trusted Platform Module) or a mobile device's Secure Enclave stores private key material in hardware-isolated memory. Even if the operating system is compromised, software cannot export the raw private key. Biometric verification serves as the local user-presence check before the authenticator performs a signing operation.
FIDO2 attestation is the protocol layer — part of the WebAuthn specification — that allows a server to verify not just the cryptographic signature but also what kind of device produced it. During registration, the authenticator can include a signed attestation statement proving it is a genuine, certified hardware device rather than a software emulator. Organizations with strict compliance requirements can use attestation to enforce that only managed-device credentials are accepted.
💡 Real-World Example: A developer authenticating to a code-hosting platform selects "Sign in with passkey." Their OS shows a Face ID prompt. The secure enclave signs the server's challenge with the stored private key. The server verifies the signature against the registered public key. The developer is authenticated — no password was typed, no password was transmitted, and no password exists to be phished.
⚠️ Common Mistake: Passwordless is sometimes equated with "no second factor," which inverts its security model. A passkey is inherently a two-factor artifact in a single gesture: possession (the device holding the private key) plus inherence (the biometric or PIN that unlocks it). The second factor isn't removed — it's embedded in the credential operation itself.
Passwords are not gone. Legacy systems, shared accounts, and recovery flows still use them. But they are now the legacy path — the fallback that exists because migration is incomplete, not the design target for new systems.
Posture 2: Federated-by-Default
The second structural shift is about who manages credentials. In a siloed model, every application maintains its own user store, its own password hashing logic, its own session management, and its own recovery flows — making every application an independent attack surface for credential theft.
Federated identity means an application delegates the act of authentication to an external Identity Provider (IdP) — a dedicated service whose job is to verify who a user is. The application only receives an assertion from the IdP: "this user authenticated successfully, here are their attributes." The application never handles the user's credential directly.
Two standards dominate how this delegation is expressed:
SAML (Security Assertion Markup Language) is the older XML-based standard, dominant in enterprise single sign-on scenarios. An IdP issues a signed XML assertion that the service provider validates. It is verbose and complex but deeply entrenched in enterprise identity infrastructure.
OIDC (OpenID Connect) is the modern JSON/JWT-based identity layer built on top of OAuth 2.0. Most new systems — web apps, mobile apps, APIs — use OIDC. The IdP issues signed JSON tokens; the application validates those tokens using published cryptographic keys. Identity in Practice walks through an OIDC flow step by step.
┌────────────────────────────────────────────────────────────┐
│ USER'S BROWSER │
└───────────────────┬──────────────────┬─────────────────────┘
│ 1. Login request │ 4. Token returned
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ Application │ │ Identity │
│ (Relying │◄──│ Provider (IdP) │
│ Party) │ │ e.g., Entra ID,│
│ │ │ Okta, Auth0 │
└────────┬────────┘ └─────────────────┘
│ 5. Access granted based on token claims
▼
┌─────────────────┐
│ Resource / │
│ Backend API │
└─────────────────┘
Steps:
1. User tries to access the application
2. Application redirects to IdP (no credentials handled by app)
3. User authenticates at IdP (passkey, MFA, etc.)
4. IdP issues signed token to application
5. Application grants access based on token claims
Federation provides a concrete benefit beyond convenience: the credential surface collapses to one place. If a user is de-provisioned, that change propagates immediately to every federated application — because none of those applications ever stored the credential in the first place.
🎯 Key Principle: In a federated model, the application's trust is placed in the IdP's signed assertion, not in any credential it has verified itself. This means the application must validate that assertion rigorously — checking signature, issuer, audience, and expiry. The implications of getting that validation wrong are covered in Common Misconceptions and Where They Lead.
💡 Mental Model: Think of the IdP as a passport authority. The application is a border checkpoint that doesn't independently verify who you are — it checks whether your passport is genuine, issued by a recognized authority, and currently valid.
Posture 3: Agents Everywhere
The third shift is less about how humans authenticate and more about who — or what — is authenticating at all. In a contemporary organization's identity graph, humans are typically outnumbered by non-human identities: CI/CD pipeline runners, microservices calling other microservices, scheduled jobs, data pipelines, and increasingly, LLM-based agents that take multi-step actions on behalf of users or autonomously.
These identities require credentials and lifecycle management just as human accounts do — but the patterns that work for humans break down quickly when applied to machines.
A human can respond to an MFA prompt, rotate their own password when notified, and recognize a suspicious login attempt. A service account cannot. When non-human identities are given human-style credentials — long-lived shared passwords, broad permission scopes, no rotation policy — the result is credentials that are months or years old, stored in environment variables or configuration files, with no audit trail.
The alternative is to design credential and lifecycle patterns that fit how machines operate:
Short-lived tokens, not long-lived secrets. Rather than issuing a permanent API key, modern systems issue tokens valid for minutes or hours. The OAuth 2.0 Client Credentials flow is the standard mechanism: a service authenticates with its client ID and secret to the IdP, receives a short-lived access token, uses it, and refreshes when needed.
Workload identity federation. Cloud platforms now offer mechanisms where a workload's platform identity (e.g., the identity of a Kubernetes pod or a CI/CD job) is the credential. The workload proves who it is by virtue of the environment it's running in — signed by the platform — and exchanges that proof for a short-lived token from the IdP without any stored secret. This eliminates the secret-storage problem entirely for workloads running on supported platforms.
Scoped, least-privilege permissions. A microservice that reads from one database table should hold a token scoped to exactly that operation. Broad service accounts that can read, write, and delete across an entire system are a legacy of treating non-human identities as administrative convenience rather than security principals.
LLM-based agents introduce a specific wrinkle: they act on behalf of a human, but the actions they take may not be directly supervised in real time. The identity question is not just "who is this agent?" but "whose authority is this agent operating under, and what are the boundaries of that delegation?" OAuth 2.0 has mechanisms for this — token delegation and impersonation scopes — but the patterns are still maturing in practice. The key design requirement is that an agent's token should carry explicit, narrow scopes that represent what the user delegated, not the user's full permission set.
HUMAN USER (authenticated via passkey + IdP)
│
│ delegates narrow scope
▼
┌───────────────┐
│ LLM Agent │ ← holds a scoped token on user's behalf
│ (non-human │
│ identity) │
└──────┬────────┘
│
┌──────┴────────────────────┐
│ │
▼ ▼
┌──────────────┐ ┌──────────────┐
│ Calendar API │ │ Email API │
│ (read only) │ │ (send only) │
└──────────────┘ └──────────────┘
Each downstream API validates the token's scope
before honoring the request.
How the Three Postures Interact
These three shifts are not independent. They combine in the same transaction, creating layered identity chains where multiple actors and credential types appear in sequence.
Consider a concrete scenario. A user opens a project management application. They authenticate with a passkey — the platform authenticator on their laptop signs the FIDO2 challenge (Posture 1: passwordless). The application redirected to the organization's IdP, which handled the passkey ceremony and issued a signed OIDC token asserting the user's identity (Posture 2: federated). The user then triggers a workflow that activates an LLM-based agent. The agent receives a scoped token derived from the user's session — scoped to only the APIs the user explicitly authorized (Posture 3: agents, operating under delegated authority).
┌──────────────────────────────────────────────────────────────────┐
│ LAYERED IDENTITY CHAIN │
│ │
│ [User] │
│ │ passkey auth (FIDO2) │
│ ▼ │
│ [IdP] ──── issues OIDC ID token + access token ────► │
│ │ │
│ [Application] ─── user triggers agent workflow ────► │
│ │ │
│ [Agent] ─── holds scoped delegated token ──────────► │
│ │ │
│ [API A] [API B] ← each validates token scope │
│ independently │
└──────────────────────────────────────────────────────────────────┘
Each arrow represents a distinct identity assertion.
Each receiver validates independently — no implicit trust flows
down the chain automatically.
The critical insight from this chain is that each hop requires its own validation. API A does not trust the request because the agent trusted the application, or because the application trusted the IdP. API A validates the token it receives against the IdP's published keys, checks the scope, checks the audience, and enforces its own authorization policy. Trust is not transitive by default — it must be explicitly asserted and explicitly verified at each step.
🎯 Key Principle: A federated, passwordless authentication that produces a token doesn't end the identity question — it starts a chain of identity assertions. Every service in that chain that consumes a token is making a trust decision, and that decision should be based on what the token cryptographically proves, not on the assumption that something upstream already handled it.
📋 Quick Reference: The Three Default Postures
| Posture | Core Mechanism | What It Replaces | Key Standard |
|---|---|---|---|
| Passwordless-by-default | Device-bound cryptographic keypair | Shared-secret passwords | FIDO2 / WebAuthn |
| Federated-by-default | Delegated authentication to external IdP | Per-app credential stores | OIDC / SAML |
| Agents-everywhere | Machine identity with scoped, short-lived tokens | Service accounts with static secrets | OAuth 2.0 Client Credentials, Workload Identity |
Core Vocabulary: Entities, Credentials, Tokens, and Sessions
Every field has a vocabulary problem: terms get borrowed, stretched, and conflated until a word like "authentication" means five different things depending on who is speaking. In identity systems, that ambiguity has a direct cost — engineers conflate authentication with authorization and skip access-control checks; operators treat tokens as session cookies and miss expiry logic; architects confuse a credential with the token it produces and build incorrect revocation systems. This section establishes precise definitions for the terms you will encounter in documentation, RFCs, and incident post-mortems throughout the rest of this roadmap.
The Three-Layer Distinction: Identity, Authentication, Authorization
These three words are used interchangeably in casual conversation and almost never in formal specifications. The distinction maps to three structurally different system components that fail in different ways.
Identity is a claim. When a system says "I am the service account pipeline-bot@ci.example.com" or a user presents a username, that is an identity assertion. It carries no verification weight on its own. Identity answers: who does this entity say it is?
Authentication is the process of verifying that claim. The system demands proof — a password, a cryptographic signature, a biometric measurement, or possession of a hardware token. Authentication answers: can this entity prove it is who it claims?
Authorization is the decision that follows successful authentication. A verified identity does not automatically mean permission to act. Authorization consults policies — RBAC rules, ABAC attributes, ACLs — and answers: what is this verified entity allowed to do?
Entity presents identity claim
│
▼
┌─────────────────────┐
│ AUTHENTICATION │ ← "Are you really who you say?"
│ Verify the claim │
└────────┬────────────┘
│ verified
▼
┌─────────────────────┐
│ AUTHORIZATION │ ← "What are you allowed to do?"
│ Apply access policy│
└────────┬────────────┘
│ permitted
▼
Access granted
Why does separating these matter? Consider an API endpoint that checks whether a valid JWT is present (authentication) but does not check whether the token's scope permits the specific operation being requested (authorization). The authentication layer is working correctly; the authorization layer is simply absent. Fixing it requires changes to a different part of the system. Common Misconceptions and Where They Lead examines specific failure modes that arise from conflating these two concepts.
Credentials vs. Tokens: What You Present vs. What You Receive
This distinction underpins how modern systems separate the act of proving identity from the artifacts used to act on its behalf afterward.
A credential is the secret or artifact an entity uses to prove its identity during authentication. Credentials are long-lived by design — their whole purpose is to be held by the entity and produced when challenged. Examples include:
- Passwords — shared secrets; the entity knows something
- Private keys — asymmetric cryptographic material; the entity possesses something only it can use to sign
- Client certificates (X.509) — a private key plus a signed certificate binding it to an identity, used in mTLS
- Hardware tokens / passkeys — credentials bound to a physical device or a platform authenticator (TPM, Secure Enclave)
Credentials are input to authentication. They should be treated as secrets, rotated on a schedule, and never logged.
A token is the short-lived, scoped artifact issued after successful authentication. Key examples:
- JWT (JSON Web Token) — a Base64URL-encoded structure with a header, payload (claims), and signature. Self-describing; the receiving system can validate it without calling the issuer, by checking the signature against a public key.
- Opaque token — a random string that carries no intrinsic meaning; the resource server must call the authorization server's introspection endpoint to determine what it represents.
- SAML assertion — an XML document issued by an IdP, signed with the IdP's private key, asserting facts about the authenticated subject; used in enterprise federation.
┌──────────┐ presents credential ┌──────────────┐
│ Entity │ ─────────────────────► │ Auth System │
└──────────┘ └──────┬───────┘
│ issues token (short-lived, scoped)
▼
┌────────────┐
│ Token │
│ • expiry │
│ • scope │
│ • audience │
└─────┬──────┘
│ used to access
▼
┌─────────────┐
│ Resource │
│ Server │
└─────────────┘
⚠️ Common Mistake: Treating the token as a credential. A token is not how you prove who you are — it is what you receive after proving who you are. Tokens have scope, expiry, and audience constraints that credentials do not. More critically, a stolen token grants access to a resource server without requiring any knowledge of the original credential. This is why token lifetime management, transmission security, and scope minimization matter so much.
💡 Mental Model: Think of a credential as your employee ID badge and the token as the temporary visitor pass issued at the front desk after your badge is verified. The visitor pass is time-limited, scoped to specific floors, and usable without showing your badge again — but if someone steals your visitor pass, they can use it until it expires.
🤔 Did you know? The reason JWTs can be validated without calling the issuer is that the issuer signs them with a private key and publishes the corresponding public keys at a well-known JWKS (JSON Web Key Set) endpoint. Any service that fetches the JWKS can verify the signature locally. This is also why a compromised signing key is catastrophic — it undermines every token that key ever signed.
Sessions: The Context That Persists
Authentication is an event; a session is the record that the event occurred and that subsequent requests can proceed without re-authenticating.
A session is a record of an authenticated context that spans more than one request. Sessions can be implemented in two broad ways:
Stateful sessions store the session record server-side. The server generates a session ID, stores the associated identity and authorization data in a session store, and issues the session ID to the client as a cookie. Revocation is immediate — delete the record from the store.
Stateless sessions embed the authenticated context inside the token itself. A signed JWT carries the subject, expiry, and scope in its payload. The resource server validates the signature and reads the claims directly, with no server-side lookup. Revocation becomes harder: because there is no central store to invalidate, a stolen token remains valid until it expires. This is why short expiry windows matter for JWTs used as stateless sessions.
STATEFUL SESSION STATELESS SESSION
───────────────────── ──────────────────────
Client holds: session_id Client holds: signed JWT
Server lookup required: No lookup required:
session_id → session store Validate signature + check expiry
│ │
▼ ▼
Revoke instantly (delete record) Revoke only by expiry
(or a token blocklist,
which reintroduces state)
Tokens and sessions interact in practice: an access token in OAuth 2.0 is essentially a short-lived stateless session for a resource server, while the user's browser session at the IdP is a separate, typically stateful session. These two sessions have independent lifetimes, which is why a user can revoke an application's access without logging out everywhere, and why logging out of an application does not necessarily invalidate the IdP session.
⚠️ Common Mistake: Conflating "logout" with "session revocation." Clearing a token from the client makes the token inaccessible to client-side code, but if the token was intercepted earlier, it remains valid until expiry. True session revocation for stateless tokens requires either a blocklist or waiting for expiry.
Principal, Subject, and Claim: Protocol-Level Vocabulary
When you read OAuth 2.0 RFCs, OIDC specifications, or SAML metadata, you encounter vocabulary that sits below the application level. These terms describe how protocols name and assert things about the entities involved.
A principal is the entity that is taking action or on whose behalf an action is taken. In a standard OAuth 2.0 flow, the principal is the user authorizing the client application. In a Client Credentials flow, the principal is the client application itself.
A subject is the protocol's specific identifier for the principal within a token or assertion. In a JWT, the sub claim names the subject — typically a stable, opaque identifier for the user ("sub": "u-8f3b1a2c") rather than a mutable attribute like an email address.
💡 Pro Tip: Use sub — not email — as the stable identifier for a user in your application's database. Email addresses change; sub is intended to be immutable for a given user at a given issuer.
Claims are key-value assertions about the subject, embedded in a token. A JWT payload is a JSON object whose keys are claim names:
{
"iss": "https://idp.example.com",
"sub": "u-8f3b1a2c",
"aud": "api.example.com",
"exp": 1780000000,
"iat": 1779996400,
"scope": "read:orders write:cart",
"email": "alice@example.com",
"email_verified": true
}
Three standard claims deserve particular attention because they are also common validation targets — and failing to validate them is a frequent vulnerability:
iss(issuer) — identifies who issued the token; your application should only accept tokens from issuers it explicitly trustsaud(audience) — identifies the intended recipient of the token; if your API is not in theaudfield, you should reject the tokenexp(expiration time) — a Unix timestamp; tokens must be rejected after this time
A token's claims are assertions made by the issuer, not facts automatically enforced by the token format itself — your application is responsible for checking them.
🧠 Mnemonic: P-S-C: Principal acts, Subject names, Claims describe.
Putting the Vocabulary Together
These terms form a chain that describes a single authentication event end-to-end:
ENTITY ("alice@example.com")
│
│ presents CREDENTIAL (passkey / private key)
▼
AUTHENTICATION (IdP verifies the credential)
│
│ issues TOKEN (JWT with claims)
│ ├─ iss: https://idp.example.com
│ ├─ sub: u-8f3b1a2c ← SUBJECT
│ ├─ aud: api.example.com
│ ├─ scope: read:orders
│ └─ exp: <15 minutes from now>
▼
SESSION established (stateless, lives in token expiry)
│
▼
AUTHORIZATION (API checks scope, enforces policy)
│
│ PRINCIPAL (alice) acts on Resource
▼
Access granted (or denied)
This vocabulary applies equally to non-human identities: a CI/CD pipeline is an entity; its client secret or federated workload identity credential is the credential; the access token it receives carries a sub that is a service account identifier rather than a human user. The chain is identical; only the actors change. This uniform vocabulary is precisely what allows a single policy and audit framework to span both human and non-human principals.
📋 Quick Reference: Core Vocabulary
| Term | Layer | One-line definition |
|---|---|---|
| Identity | Conceptual | A claim about who an entity is |
| Credential | Proof | The secret/artifact used to prove identity |
| Authentication | Process | Verification of the identity claim |
| Authorization | Decision | What a verified entity is permitted to do |
| Token | Artifact | Short-lived, scoped artifact issued post-authentication |
| Session | State | Record that authentication occurred; spans multiple requests |
| Principal | Protocol | The entity acting or on whose behalf action is taken |
| Subject | Protocol | How a protocol encodes the principal's identity in a token (sub) |
| Claim | Protocol | A key-value assertion about the subject embedded in a token |
Identity in Practice: Reading a Real Authentication Flow
Vocabulary without a concrete system to attach it to stays abstract longer than it needs to. This section traces a single, realistic authentication scenario from first browser click to backend API call — and then extends it to cover how an automated agent authenticates through the same infrastructure without a user in the loop. By the end, every term introduced in earlier sections should have a visible home in observable system behavior.
The scenario: a user opens a web application, signs in through a federated Identity Provider, and the application's backend microservice validates the resulting token to authorize an API call.
The OpenID Connect Authorization Code Flow, Step by Step
OpenID Connect (OIDC) is an identity layer built on top of OAuth 2.0. It standardizes how a client application asks an Identity Provider to authenticate a user and return verified identity information. The Authorization Code flow is the variant used by web applications with a server-side component, because it keeps tokens out of the browser's URL bar and allows the backend to authenticate directly with the IdP using a client secret.
Browser Web App (Client) IdP Backend API
| | | |
|-- clicks login --> | | |
| |-- redirect -----> | |
| | (auth request) | |
|<------- browser redirect to IdP login UI ------------------|
| | | |
|-- user authenticates (passkey / biometric / etc.) -------> |
| | | |
|<------- browser redirect back to web app ------------------|
| (authorization code in query string) | |
| | | |
| |-- code exchange -> | |
| | (code + secret) | |
| |<-- ID token ------| |
| | access token | |
| | refresh token | |
| | | |
| |-- API call (access token) -----------> |
| | | (validates JWT) |
| |<------------------ API response ------ |
Step 1: The Authorization Request. When the user clicks "Sign In," the web app constructs an authorization request and redirects the browser to the IdP's authorization endpoint. This request includes the client's client_id, a redirect_uri pointing back to the app, response_type=code (signaling the Authorization Code flow), a scope list (openid is required for OIDC, plus any additional scopes like email), and a state value the app generates to prevent cross-site request forgery.
Step 2: User Authentication at the IdP. The browser lands on the IdP's login UI. The IdP handles the actual credential verification — this is the federated-by-default posture in practice. The client application never sees the credential.
Step 3: The Authorization Code. After successful authentication, the IdP redirects the browser back to the app's redirect_uri with a short-lived authorization code appended as a query parameter. This code is a one-time-use artifact; it is not a token and grants nothing on its own.
Step 4: The Code Exchange. The web app's server makes a back-channel POST request to the IdP's token endpoint, presenting the authorization code, its client_id, its client_secret (or a signed assertion for more secure deployments), and the same redirect_uri used in step 1. This server-to-server call is not visible in the browser.
Step 5: Token Issuance. If everything checks out, the IdP returns three artifacts: an ID token, an access token, and optionally a refresh token.
What Each Token Carries and What It Is For
The two tokens returned from the code exchange serve distinct purposes.
The ID Token
The ID token is a JWT addressed to the client application. Its audience (aud claim) is the client's client_id. It asserts facts about the authenticated user: sub, email, name, and similar profile claims, along with iss and exp.
{
"iss": "https://idp.example.com",
"sub": "user_9f3a2c",
"aud": "app_client_id",
"exp": 1751400000,
"iat": 1751396400,
"email": "alex@example.com",
"name": "Alex Rivera"
}
The ID token answers: Who just authenticated? The application uses it to establish a local session. The ID token is not meant to be sent to backend APIs. It proves identity to the client, not authorization to a resource server.
The Access Token
The access token is the artifact the client forwards to backend services. Unlike the ID token, the access token's aud is the backend API's identifier, and the scope claim specifies what operations are permitted.
{
"iss": "https://idp.example.com",
"sub": "user_9f3a2c",
"aud": "https://api.example.com",
"exp": 1751400000,
"iat": 1751396400,
"scope": "read:documents write:documents"
}
🎯 Key Principle: The ID token answers who is this user (for the client). The access token answers what is this caller allowed to do (for the resource server). They are designed for different audiences and should be validated differently.
🧠 Mnemonic: ID token = Identity for the client. Access token = Authorization for the API.
How a Backend Microservice Validates an Access Token
JWT access tokens enable offline validation — the backend can verify the token's authenticity and claims without contacting the IdP on every request, using only the IdP's public keys.
1. Fetch the JWKS. The IdP publishes its public keys at a JSON Web Key Set (JWKS) endpoint, discoverable via https://idp.example.com/.well-known/openid-configuration. The backend fetches this key set at startup and caches it with periodic refresh.
2. Verify the JWT signature. A JWT has three base64url-encoded sections: header, payload, and signature. The header identifies which algorithm and key ID (kid) were used to sign the token. The backend finds the matching key in its cached JWKS and verifies that the signature over header.payload is valid.
3. Validate the claims. Signature validity alone is not sufficient. The backend must also check:
exp: the token has not expirediss: the issuer matches the expected IdPaud: the audience includes this service's identifiernbf(if present): the token's "not before" time has passed
Incoming Request
+ Bearer Token
|
v
[Extract JWT]
|
v
[Parse header: kid, alg]
|
v
[Look up kid in cached JWKS]
| ^
| | (refresh cache
| | if kid not found)
v |
[Verify signature] ------+
|
v
[Check exp, iss, aud]
|
v
[Enforce scope / authz]
|
v
[Handle Request]
⚠️ Common Mistake: Validating the signature but skipping the aud check. An access token issued for one API is technically well-signed — its signature will verify correctly against the IdP's public key — but it was never intended for your service. Without the audience check, a token obtained for a low-privilege API could be replayed against a higher-privilege one if both share the same IdP. This is called a confused deputy scenario.
The JWKS cache deserves special attention. Public keys rotate — IdPs issue new signing keys on a schedule and during incident response. The standard pattern: cache the JWKS with a reasonable TTL, but if an incoming token's kid is not found in the cache, trigger an immediate refresh before failing. This handles key rotation without requiring a service restart.
Where an Automated Agent Fits In
Automated agents — CI/CD pipelines, scheduled jobs, LLM-based orchestration services — have no user to redirect through an IdP login page. They use a different OAuth 2.0 flow: the Client Credentials flow.
Agent / Service IdP Backend API
| | |
|-- POST /token -----------> | |
| client_id | |
| client_secret | |
| grant_type= | |
| client_credentials | |
| scope=read:documents | |
| | |
|<-- access token ---------- | |
| | |
|-- API call (access token) ------------------> |
| | (same JWT |
|<-- API response --------------------------------|
| | validation) |
The agent presents its client_id and client_secret (or a signed client assertion) directly to the IdP's token endpoint. No browser, no user interaction, no authorization code. The IdP returns an access token scoped to the permissions configured for that service identity.
The backend API validates this access token using exactly the same JWKS-based signature verification described above. The only difference is what the token asserts: instead of a sub pointing to a human user, the subject is the service's own client identity. This architectural symmetry is deliberate — the backend API doesn't need separate validation logic for human versus service tokens. The distinction, if the application cares about it, is visible in the token's claims.
📋 Quick Reference: Authorization Code vs. Client Credentials
| Authorization Code Flow | Client Credentials Flow | |
|---|---|---|
| Who uses it | Human users via browser | Services, agents, pipelines |
| Credential type | User authenticates at IdP | Client ID + secret / assertion |
| Tokens issued | ID token + access token | Access token only |
| Browser involved | Yes (redirects) | No |
| Token subject | Human user sub |
Service client identity |
| Backend validation | JWKS signature + claims | JWKS signature + claims |
The consistency of backend validation across both flows is not accidental — it is what makes identity the uniform control plane described in the opening section of this lesson. Access decisions at the API layer depend on the token's claims, not on how the token was obtained.
Common Misconceptions and Where They Lead
Every mental model in this lesson so far has a shadow version — a close-but-wrong belief that produces systems which look correct until they fail in production. These misconceptions are not beginner errors that experience automatically corrects. They are plausible simplifications that compile cleanly, pass code review, and only surface when an attacker probes a gap or an audit surfaces a credential that has been sitting unchanged for three years.
Misconception 1: Authenticated Means Authorized
The wrong mental model runs like this: "The user had to log in to reach this endpoint, so they must be allowed to use it." The problem is that modern systems are not one building with one door. A single authenticated session can reach dozens of microservices, each of which must make its own authorization decision.
Here is what the failure looks like concretely:
GET /api/user/profile → returns caller's own profile ✓
GET /api/user/42/transactions → should require ownership or admin role
but the endpoint only checks "is the
token valid?" not "does this token
belong to user 42 or a role allowed
to read user 42's data?"
This pattern — a valid token being accepted as implicit permission for any action — is categorized in the OWASP API Security project as Broken Object Level Authorization. It is consistently among the most-reported API vulnerabilities, precisely because the authentication layer is doing its job correctly while the authorization layer is simply absent.
🎯 Key Principle: Authentication answers who. Authorization answers may this who perform this action on this resource. Neither answer implies the other.
⚠️ Common Mistake: Adding an authentication middleware to every route and considering the security problem solved. Authentication middleware confirms the token is valid and extracts the identity. A separate authorization check — on every handler — must confirm the identity is permitted to perform the specific operation.
Misconception 2: Tokens Are Just Session Cookies with a Different Name
Session cookies and tokens serve overlapping purposes — both persist an authenticated context across requests — but treating them as equivalent causes concrete failures when the behavioral differences matter.
| Session Cookie | Token (e.g., JWT) | |
|---|---|---|
| Revocation | Session table deletion | Expiry or token introspection |
| Expiry | Server-controlled | Encoded in the token itself |
| Scope | Typically all-or-nothing | Explicitly scoped to named resources |
| Stateless? | No (server stores state) | Often yes (self-contained) |
| Theft consequence | Session can be invalidated | Token is valid until expiry, regardless |
The misconception produces three distinct failure modes:
Ignoring token expiry. A service that does not check the exp claim will accept tokens that are past their valid window, because the token's cryptographic signature is still technically valid. The expiry check is a separate step that must be explicitly performed.
Ignoring scope constraints. Tokens issued by modern authorization servers carry explicit scopes. A token with scope: read:reports should not be accepted by an endpoint that writes data, even if the token is otherwise valid. Skipping scope checks treats the token as granting blanket access.
Underestimating the impact of theft. With a session cookie, a stolen cookie can often be invalidated server-side. With a self-contained, stateless token, the server holds no record to invalidate. A stolen token is valid until it expires, regardless of whether the legitimate owner has changed their password. This makes token lifetime a security control in its own right.
💡 Real-World Example: A service issues access tokens with a 24-hour lifetime. An attacker who extracts a token from a log file has a 24-hour window of unrestricted access — even if the user immediately changes their password. Shortening token lifetime to minutes, combined with refresh token rotation, closes this window.
🤔 Did you know? Token theft from logs is not a theoretical risk. Access tokens are short strings that look innocuous in log output, and developers often log request headers during debugging. Structured logging policies that explicitly redact authorization headers are a direct mitigation.
Misconception 3: Federation Means Unconditional Trust in the IdP
Federating with a reputable IdP is a substantial improvement over managing credentials directly. It is not, however, a blank check. An application that federates still has four validation responsibilities it cannot delegate:
IdP issues token
│
▼
┌─────────────────────────────────────────────┐
│ Application MUST still verify: │
│ │
│ 1. Signature → Was this token actually │
│ signed by the IdP's key? │
│ │
│ 2. Issuer → Does 'iss' match the IdP │
│ (iss) I federated with? │
│ │
│ 3. Audience → Does 'aud' include MY │
│ (aud) application's identifier? │
│ │
│ 4. Authz → Is this identity allowed │
│ to perform this action in │
│ MY system? │
└─────────────────────────────────────────────┘
Each check defends against a distinct attack:
Signature verification defends against forged tokens. Misconfigured algorithm: none acceptance has historically allowed signature bypass in real systems.
Issuer (iss) checking defends against token confusion. If your application federates with your organization's IdP and an attacker controls a different IdP that issues tokens with similar structure, iss validation is what rejects their tokens.
Audience (aud) checking defends against token replay across services. A service that skips aud validation will accept any valid token from the trusted IdP, including tokens meant for other services — allowing an attacker who extracts a token for a low-privilege service to replay it against a higher-privilege one.
Authorization policy enforcement is covered under Misconception 1. Federation handles authentication; the application owns authorization.
🧠 Mnemonic: SIAE — Signature, Issuer, Audience, Expiry. These are the four fields every token consumer must verify before acting on a token's claims.
⚠️ Common Mistake: Accepting any token that the IdP's JWKS endpoint can verify the signature of, without checking aud or iss. This is the configuration equivalent of accepting any badge printed on official-looking paper because the ink is authentic.
Misconception 4: Non-Human Identities Are a Secondary Concern
Identity systems designed exclusively around human users produce a recognizable failure pattern in production: service accounts with static credentials that have never rotated, CI/CD pipeline tokens with administrative permissions because scoping them was "too complicated", and agent credentials with no audit trail.
As established in The Three Default Postures, non-human identities now outnumber human identities in many organizations. Treating them as an afterthought means the majority of identities in a system receive the weakest controls.
Afterthought design → Production consequence
─────────────────────────────────────────────────────────
Static credentials → Credential in a leaked
created once, never rotated git repo is valid forever
Broad scopes assigned → Compromised service can
for convenience access unrelated resources
No audit trail → Incident responders cannot
on service account actions determine blast radius
Shared credentials across → Rotating one credential
multiple services breaks multiple services
simultaneously
🎯 Key Principle: Every non-human identity needs the same lifecycle treatment as a human identity: a creation event, scoped permissions that match actual need, a rotation or expiry policy, and an audit trail of actions taken under that identity.
The agent proliferation dimension adds urgency. An agent acting under an over-privileged service account can cause damage proportional to the scope of those credentials, not proportional to the narrow task it was designed to perform.
💡 Real-World Example: A deployment pipeline requires read access to a secrets manager to retrieve a database password at deploy time. The afterthought approach grants the pipeline full administrative access to the secrets manager because it was easier to configure. If the pipeline is compromised, the attacker gains access to every secret in the organization. The correct scope is read access to exactly the one secret path used during deployment.
Summary
This lesson has traced a single structural argument across five sections. The network perimeter is gone — cloud infrastructure, SaaS tooling, and distributed work have dissolved the boundary that once served as an implicit trust signal. Identity is now the control plane: every access decision, at every layer, depends on verifying who or what is making a request and what it is permitted to do.
Three structural postures define how modern identity systems are built: passwordless-by-default, where device-bound cryptographic credentials replace shared-secret passwords; federated-by-default, where applications delegate authentication to a dedicated Identity Provider via OIDC or SAML rather than managing credentials themselves; and agents-everywhere, where non-human identities — services, pipelines, and LLM-based agents — now outnumber human identities in many organizations and require distinct credential and lifecycle patterns.
The vocabulary that makes this landscape legible: identity is a claim, authentication verifies it, authorization decides what the verified entity may do. Credentials are what you present to prove identity; tokens are what you receive afterward, carrying claims about the subject and valid for a bounded scope and lifetime. Sessions record that authentication occurred; tokens can create stateless sessions, with revocation tradeoffs that make lifetime a security control rather than just a UX preference.
And four misconceptions consistently undermine systems built on this foundation:
📋 Quick Reference: Common Misconceptions
| Misconception | Correct Model | Production Failure | |
|---|---|---|---|
| 1 | Authenticated = authorized | Authn confirms identity; authz is a separate check per operation | Missing endpoint authorization checks |
| 2 | Tokens = session cookies | Tokens have expiry, scope, and irrevocable validity until expiry | Stolen tokens used beyond password resets; scope ignored |
| 3 | IdP trust = unconditional | Validate signature, iss, aud, exp; enforce your own authz |
Token replay, cross-audience acceptance, forged claims |
| 4 | Non-human identities = afterthought | Same lifecycle rigor as human identities: scope, rotation, audit | Static credentials, over-privileged agents, no blast radius visibility |
Three practical checks to carry forward:
Audit your authorization layer. For each API endpoint, verify there is an explicit check confirming the authenticated identity is permitted to perform the specific operation — not just that a valid token is present. Pay particular attention to endpoints that accept a resource ID as a parameter.
Review your non-human credential inventory. List every service account, pipeline credential, and agent identity you operate. For each: when was it last rotated? What is its scope? Is there an audit log of its actions? This exercise typically reveals credentials that are far older and broader than anyone realized.
Validate your token consumer configuration. Confirm that iss, aud, and exp are all explicitly checked in every service that consumes federated tokens. Write a test that presents a token with an incorrect aud and confirms rejection. This turns a configuration assumption into a verified property.