Vetora logo
๐Ÿ”Security

Authentication vs Authorization

Authentication (authn) verifies identity -- who are you? Authorization (authz) verifies permissions -- what can you do? Conflating the two is one of the most common security architecture mistakes in distributed systems.

Overview

Authentication and authorization are the two fundamental pillars of access control, yet they are routinely conflated in system design discussions. Authentication (authn) is the process of verifying that an entity is who it claims to be. When a user provides a username and password, or when a service presents a TLS client certificate, the system is performing authentication. The output of authentication is a verified identity: 'this request comes from user alice@example.com' or 'this request comes from the inventory-service.'

Authorization (authz) is the process of determining whether an authenticated identity is permitted to perform a specific action on a specific resource. After authentication establishes that the caller is alice@example.com, authorization checks whether Alice can read document 12345, delete user bob, or invoke the admin API. Authorization is inherently contextual: it depends on the action, the resource, the caller's roles or attributes, and potentially environmental factors like time of day or IP address.

The distinction matters architecturally because authn and authz scale, evolve, and fail differently. Authentication is typically centralized: all services delegate identity verification to a single identity provider (IdP) like Okta, Auth0, or an internal OAuth server. Authorization is typically distributed: each service (or a sidecar policy agent) makes its own permission decisions because it understands its own resource model. A document service knows what 'editor' means for a document; an inventory service knows what 'read-only' means for a warehouse.

In microservice architectures, authentication usually happens once at the API gateway (verify the JWT, extract the identity) and the verified identity is propagated to downstream services via headers or token forwarding. Authorization happens at each service boundary, because each service has different resources and permission models. This separation of concerns is essential: centralizing authorization in the gateway creates a single point of failure and forces all services to share a single permission model.

The most dangerous antipattern is encoding authorization decisions in the authentication token. Putting 'role: admin' in a JWT means the role is frozen for the token's lifetime (often 15-60 minutes). If you revoke the admin role, the user retains admin access until the token expires. Proper architecture keeps identity in the token and checks permissions in real-time against a policy store.

Key Points
  • 1Authentication verifies identity (who). Authorization verifies permissions (what). They are sequential: authn must complete before authz can begin, because you need to know who the caller is before checking what they can do.
  • 2Authentication is typically centralized (one IdP, one JWT issuer). Authorization is typically distributed (each service evaluates permissions against its own resource model).
  • 3In microservices, the API gateway handles authentication (verify JWT, extract claims). Individual services handle authorization (check if this user can access this resource).
  • 4JWTs carry identity claims (sub, email, tenant_id). Roles and permissions should be looked up at authorization time, not baked into the JWT. This enables real-time revocation.
  • 5OAuth 2.0 is an authorization framework (it issues access tokens with scopes), but it also involves authentication (the user proves identity to the authorization server). OIDC adds a standardized identity layer on top of OAuth.
  • 6Multi-tenancy adds a third dimension: after authn (who) and authz (what), the system must check tenant isolation (which data). Tenant ID should be part of every authorization check.
Simple Example

Hotel Key Card Analogy

When you check into a hotel, the front desk verifies your identity (authentication) by checking your ID and reservation. They then issue a key card that opens specific rooms and facilities (authorization). The key card does not prove who you are -- anyone holding it can open the door. Similarly, a JWT proves identity was verified at issuance, but the token itself can be stolen. The hotel can also re-key your card if you extend your stay (dynamic authorization), but if permissions were written permanently onto the card at check-in, changing them would require issuing a new card (the JWT revocation problem).

Real-World Examples

Google (BeyondCorp)

Google's BeyondCorp model eliminates the concept of a trusted network perimeter. Every request, even from inside Google's network, must be authenticated (who is the user and device?) and authorized (does this user with this device trust level have access to this application?). Authentication uses device certificates and user SSO. Authorization uses a real-time access policy engine that considers user role, device health, location, and time.

AWS (IAM)

AWS IAM separates authentication (proving you are an AWS principal via access keys, roles, or SAML federation) from authorization (IAM policies that grant or deny specific actions on specific resources). Every API call is authenticated, then evaluated against all applicable policies. The policy language supports conditions (ip-based, time-based, MFA-required) for fine-grained authorization.

Spotify

Spotify uses an internal identity platform for authentication across 2,000+ microservices. JWTs carry user identity claims but not permissions. Each service queries a centralized authorization service (backed by Zanzibar-style relationship tuples) to check whether the authenticated user can access the requested resource. This enables instant permission revocation without token invalidation.

Trade-Offs
AspectDescription
Centralized vs. Distributed AuthorizationCentralized authorization (single policy service) ensures consistency and simplifies auditing but creates a latency and availability bottleneck. Distributed authorization (each service checks locally) is faster and more resilient but risks policy drift and inconsistent enforcement.
Token-Based vs. Session-Based AuthenticationJWTs are stateless and scalable (no session store) but difficult to revoke before expiry. Sessions are server-side and instantly revocable but require shared state (Redis/DB) across service instances, adding latency and a single point of failure.
Coarse-Grained vs. Fine-Grained AuthorizationRole-based (RBAC) authorization is simple (admin, editor, viewer) but cannot express resource-level permissions. Attribute-based (ABAC) and relationship-based (ReBAC) authorization supports 'Alice can edit document 123' but adds complexity and policy management overhead.
Security vs. User ExperienceStrict re-authentication (MFA on every action) maximizes security but frustrates users. Long-lived tokens reduce friction but increase the blast radius of token theft. Step-up authentication (re-verify for sensitive actions) balances both.
Case Study

Slack's Shift from RBAC to Relationship-Based Authorization

Scenario

Slack's original authorization model used simple RBAC: workspace owner, admin, member, guest. As Slack added Enterprise Grid (multi-workspace organizations), shared channels, and granular admin roles, the flat RBAC model could not express permissions like 'this admin can manage users in workspace A but not workspace B' or 'this guest can see channel X in workspace Y but not channel Z.'

Solution

Slack migrated to a relationship-based authorization model inspired by Google Zanzibar. Permissions are expressed as relationships: 'user:alice is admin of workspace:acme', 'user:bob is member of channel:general in workspace:acme'. Authorization checks traverse these relationships at query time. The authorization service is centralized but heavily cached, with sub-millisecond latency for common checks.

Outcome

The migration enabled Slack to ship Enterprise Grid features (cross-workspace admin roles, granular channel permissions, compliance controls) without one-off authorization hacks. Permission checks are auditable (every check is logged with the relationship chain), and new permission models can be expressed by adding relationship types without code changes. Latency overhead was <1ms per authorization check due to aggressive caching of relationship tuples.

Common Mistakes
  • โš Encoding authorization decisions in JWTs. Putting 'role: admin' or 'permissions: [delete_users]' in a JWT freezes the authorization decision for the token's lifetime. If the role is revoked, the user retains the permission until the token expires. Instead, keep identity claims in the JWT (sub, email, tenant_id) and look up permissions at authorization time.
  • โš Using authentication as authorization. Verifying that a request has a valid JWT (authentication) does not mean the caller is authorized to perform the requested action. Every service must check both: 'Is this token valid?' AND 'Is this user allowed to do this?'
  • โš Single shared authorization model across microservices. Forcing all services to use the same role definitions (admin, editor, viewer) creates a lowest-common-denominator model that cannot express service-specific permissions. Let each service define its own resource model and permission checks.
  • โš No authorization for service-to-service calls. Internal services often skip authorization ('it is behind the firewall'). A compromised service can then access any other service without restriction. Use mTLS for authn and service-level policies for authz, even for internal traffic.
Related Concepts

See Authentication vs Authorization in action

Explore system design templates that use authentication vs authorization and run traffic simulations to see how these concepts perform under real load.

Browse Templates

Simulate authentication and authorization flows at scale

Metrics to watch
auth_latency_mstoken_validation_rateunauthorized_request_pctthroughput_rps
Run Simulation
Test Your Understanding

1In a microservice architecture, where should authorization checks typically happen?

2Why is encoding roles directly in JWTs considered an antipattern?

Deeper Reading