Authentication vs Authorization
Authentication establishes a trusted identity; authorization decides whether that identity may perform a particular action on a particular resource. A successful sign-in does not grant access to every record, and a valid token does not automatically authorize every operation. Keep the two responsibilities connected but explicit in the application architecture.
For developers, the most useful question is not just “Is this caller signed in?” It is “What trusted identity do we have, and why is this exact operation permitted now?” Answer that question where the protected data or action is controlled. A hidden button can guide a user; it cannot make the decision for the server.
TL;DR
- Authentication verifies identity using an appropriate credential or established session.
- Authorization evaluates permissions for the subject, action, resource, and relevant context.
- Deny access unless a policy explicitly allows the operation, and check every protected request.
- OAuth concerns delegated authorization; OpenID Connect adds an identity layer. Token formats are not permission policies.
- Enforce tenant and object boundaries using trusted data, including for background jobs and alternate entry points.
- Test denied operations and permission changes, not only successful sign-in.
Quick Example
This complete Python example illustrates a small authorization policy after authentication has already produced a trusted principal. Save it as policy.py and run python policy.py. It performs no network calls or database operations.
This is a policy illustration, not an authentication implementation or a complete production access-control system. In a real service, the principal must come from validated identity/session handling, and ownership and tenant information must come from authoritative application data. Do not construct them from caller-controlled request fields. Permission revocation, data lookup, update atomicity, audit logging, and failure handling require integration-specific design and review.
Core Concepts
Authentication establishes a principal
A principal represents a user, service, or other identity the system recognizes. Authentication may involve passwords, passkeys, certificates, or a trusted identity provider, depending on the application. A session can carry the result forward without repeating the full sign-in interaction on every request. OWASP's authentication guidance covers implementation considerations; the appropriate method depends on the system's risks and users.
Distinguish identifying a claimed account from proving control of its credential. A username or user ID in a request is a claim, not an authenticated principal. Likewise, decoding the readable part of a token is not validation. Use the documented validation behavior of the chosen identity integration rather than inventing a token parser and calling it sign-in.
Authorization evaluates an operation
An authorization decision connects a subject to an action and resource. It can depend on roles, explicit permissions, attributes, relationships, tenant membership, or other policy context. In the example, a same-tenant owner or a specifically permitted reviewer can read the document. The broad permission still does not cross the tenant boundary.
OWASP recommends least privilege, denial by default, and permission validation on every request. Those are implementation obligations, not labels a system earns by having an “admin” role. Make the allow rule explicit, and define what happens when required policy data is missing or unavailable.
Authentication and authorization have different lifecycles
A user can remain signed in after losing a role or membership. A service credential can remain valid while access to a particular resource is removed. Decide how permission changes propagate, what caches retain, and how stale sessions or claims are handled. OWASP's session-management guidance discusses the connection between authentication, sessions, and access control.
Do not assume that the permissions embedded at sign-in remain appropriate indefinitely. The application's freshness requirements determine whether it must consult current policy, invalidate sessions, use short-lived assertions, or combine mechanisms. Test the actual revocation path rather than describing a token expiry time as a complete answer.
Head-to-Head
Some frameworks integrate these stages, which can be useful. Keep the reasoning separate even when the implementation uses shared middleware. Otherwise a successful authentication check can accidentally become the only access check an endpoint performs.
OAuth, OpenID Connect, and Tokens
OAuth 2.0 is a delegated authorization framework. OpenID Connect builds an identity layer on OAuth 2.0 so a client can obtain and validate identity information from an authentication flow. The OpenID Connect Core specification defines that distinction. Do not treat every OAuth access token as proof of an end-user sign-in for any arbitrary client.
Use each token for its intended audience and purpose. An ID token intended for a client and an access token intended for a resource server serve different roles. The resource server must apply the validation and permission rules appropriate to its API. Scope can contribute to authorization without proving that a caller owns a specific document.
JWT describes a token representation, not an authorization strategy. A service can use opaque sessions or structured tokens and still have correct or incorrect access control. Focus on trust, validation, policy, and revocation rather than assuming a token format provides them automatically.
For implementation, use maintained libraries and the identity provider's current integration guidance. Configuration details such as issuer, audience, redirect handling, keys, and token lifetime are specific to that integration. A conceptual comparison is not a substitute for reviewing those settings against the application.
Enforce Permissions Where Data Is Controlled
Apply object and tenant scope during lookup
If a caller supplies /documents/123, do not assume the ability to name the record implies permission to read it. Scope the lookup to the authorized dataset or enforce an equivalent resource check before returning data. OWASP's IDOR prevention guidance explains why hard-to-guess identifiers do not replace access control.
Apply the same reasoning to list endpoints, search results, exports, attachments, and derived views. A perfectly protected detail endpoint does not help if an export returns the same records without equivalent checks. Test the entry points that expose the data, not only the route that was easiest to inspect.
Include workers and internal callers
A queue message may be processed long after the initiating request. Determine whether the job runs with the user's current permissions, an explicitly delegated capability, or a narrowly scoped service identity. Record that policy and check it at the appropriate boundary. Do not give every worker global rights simply because the message came from an internal queue.
For writes, consider the gap between a permission check and a state change. If ownership or membership can change concurrently, a separate check followed by an unscoped update may not enforce the intended rule. Use the application's transaction and data-access mechanisms to keep the authorization condition connected to the operation.
Preserve enough audit context
Record the principal, action, resource identifier, outcome, and useful request correlation according to the system's logging policy. Avoid logging passwords or raw bearer tokens. A denied operation should be diagnosable without creating another store of reusable credentials or unnecessary personal data.
HTTP Status Codes Without the Name Trap
HTTP's 401 Unauthorized name is easy to misread. RFC 9110, section 15.5.2 defines it for requests lacking valid authentication credentials and requires an applicable WWW-Authenticate challenge. 403 Forbidden means the server understood the request and refuses to fulfill it; inadequate permissions are a common reason, but not its only possible cause.
The RFC also permits returning 404 Not Found when the server is unwilling to disclose that a resource exists. Choose a consistent API policy that avoids unintended information disclosure while preserving useful diagnostics through protected logs. A browser login redirect is a separate interaction pattern, not evidence that every API should return an HTML sign-in page.
Best Practices
Name the rule in application terms
Write “members of this tenant may read their own documents” rather than “logged-in users may call this route.” A resource-oriented statement exposes the scope that implementation and tests must preserve.
Test denial as an expected outcome
Include a valid user from another tenant, a same-tenant non-owner, a removed role, and an unsupported action. For writes, verify that denied attempts leave protected data unchanged. A successful login test does not cover these cases.
Keep policy enforcement consistent
Use shared, reviewed mechanisms where possible and verify that every protected entry point uses them. Keep resource-specific conditions explicit. An abstraction that hides which tenant or record is being checked makes review harder rather than safer.
Common Mistakes
Trusting identity fields supplied by the caller
Bad: accept a request's user_id, tenant_id, or role as the trusted principal.
Correct: obtain identity from validated authentication/session handling and resolve permissions and ownership from authoritative data.
Authorizing a resource type instead of the resource
Bad: let any signed-in user fetch any document because all users have “document access.”
Correct: enforce the applicable tenant, ownership, relationship, and action conditions for the requested record and operation.
Using the UI as the permission boundary
Bad: hide a delete button and leave the underlying endpoint unrestricted.
Correct: enforce the permission at the trusted operation boundary, then use the UI to communicate the available action.
FAQ
Can a user be authenticated but not authorized?
Yes. A recognized user can lack permission to access a particular resource or action. That is normal access-control behavior, not an authentication failure.
Is OAuth authentication or authorization?
OAuth is concerned with delegated authorization. OpenID Connect adds an identity layer on top of OAuth 2.0. Use the correct protocol and token validation for the application's purpose.
Does a valid JWT authorize every API call?
No. The API must validate the token appropriately and evaluate the requested action and resource against policy. A valid identity or broad scope does not establish ownership of every record.
Should permission failures return 401 or 403?
Use 401 according to the authentication requirements in HTTP semantics, including its challenge requirement. Use 403 for a refusal to fulfill an understood request when that matches the API policy. Consider resource-existence disclosure and the RFC's permitted 404 behavior where relevant.
Related Topics
- Keycloak — Realms, clients, identity federation, authentication flows, deployment and upgrades.
- Authentication — identity verification and session choices.
- OAuth — delegated access and protocol roles.
- OpenID Connect — identity assertions and sign-in.
- API Security — enforce controls at API boundaries.
- JWT — token structure and validation responsibilities.