Skip to content
Parser BlogSecurity & Auth19 min readAll posts

JWT, IAM, and Auth0: Authentication and Authorization Explained (Without the Confusion)

Modern software rarely lives in one place — a single product spans web and mobile clients, APIs, microservices, third-party integrations, and several cloud environments. In that world the question is never just “Who are you?” It is also “What are you allowed to do?” and “How do we prove it securely across systems?”

Authentication, authorization, JWT, IAM, and platforms like Auth0 all answer pieces of those questions — and they are constantly used interchangeably, which is exactly where brittle security designs come from. Confusing the terms leads to over-permissive access, duplicated logic, and painful rework. This guide separates the concepts cleanly, shows how they connect, and gives you patterns you can drop into a real architecture.

Authentication vs. authorization: the distinction that fixes everything

Authentication verifies identity — it answers “Are you really Alice?”through passwords, MFA, passkeys, biometrics, social login, or SSO. Its output is a verified principal: a user or service the system now trusts.

Authorization decides permissions — “Can Alice view invoices? Can she delete users? Can this service call that endpoint?” Its output is a yes/no decision, usually qualified by scopes, roles, or policies. The one-line version worth memorizing:

Authentication is who you are. Authorization is what you are allowed to do.

— The rule of thumb for every access decision

What IAM actually covers

IAM stands for Identity and Access Management — occasionally written IdAM, and said as the three letters rather than as a word. The abbreviation names a discipline, not a product. When a vendor says “our IAM,” they mean their slice of it. When a security team says IAM, they mean all of it: every mechanism that establishes who or what an identity is, and every mechanism that decides what that identity may reach.

So it is broader than any single login screen — the systems and processes for managing identities and controlling access across users, devices, services, and workloads. A mature IAM practice typically includes:

  • Identity lifecycle — provisioning and deprovisioning users automatically
  • Authentication mechanisms — SSO, MFA, passwordless
  • Authorization models — RBAC, ABAC, and policy-based access
  • Directory services and user stores
  • Audit logs and compliance reporting
  • Secrets, keys, and credential policies

In an enterprise, IAM is what lets employees sign in to every internal tool through one SSO, revokes a contractor's access the moment their contract ends, lets APIs authenticate one another, and enforces least privilege across thousands of individual permissions.

How IAM works, step by step

Described as a list of capabilities, IAM sounds abstract. Followed through one request, it is concrete and short. Six stages run every time someone or something touches a protected resource, and each one belongs to a different part of your architecture:

Stage, What happens, Who owns it
StageWhat happensWho owns it
1. Provision
An identity is created and attached to attributes: department, tenant, employment status, or for a machine identity, the service it represents
HR system or directory, synced to the IdP
2. Authenticate
The identity proves itself — password plus MFA, passkey, SSO assertion, or a client credentials exchange for a service
The identity provider
3. Issue
A session or token is minted carrying the claims the rest of the chain will read
The identity provider
4. Decide
A policy is evaluated: these claims, this action, this specific resource — allow or deny
Your API, or a policy engine beside it
5. Enforce
The decision is applied at the point the data is actually served, not at the point the button is drawn
Your API
6. Audit and deprovision
The decision is logged with enough context to reconstruct it later, and access ends when the identity does
Logging pipeline plus the directory

Two of those stages are where designs usually go wrong. Stage 4 gets collapsed into stage 3 — the team treats the presence of a claim as the decision, so whoever holds a token holds the permission. And stage 6 gets skipped, which is invisible until an auditor asks who could read a given record last March and nobody can answer.

Notice also that the boundary between stages 3 and 4 is the boundary between two vendors-worth of responsibility. Your identity platform owns everything up to issuing the token. Everything after it is code you write. That split is the single most useful thing to hold in your head when reading any IAM documentation, including Auth0's.

What a JWT is (and what it is not)

A JWT (JSON Web Token) is a compact, URL-safe token format used to carry claims about a user or client — most often for API access. Standardized as RFC 7519, it has three base64url-encoded parts: a header (token type and signing algorithm), a payload (the claims), and a signature that proves integrity and authenticity. Decode a real access token's payload and it looks like this:

Examplejwt-payload.json — the decoded middle segmentjson
{  "sub": "auth0|a1b2c3d4e5",  "iss": "https://login.example.com/",  "aud": "https://api.example.com",  "iat": 1740411449,  "exp": 1740415049,  "scope": "read:invoices write:invoices",  "roles": ["finance_admin"],  "tenant": "tenant_042"}

JWTs are popular because they are stateless (an API can validate one without a database lookup), portable (they travel in an HTTP header), and verifiable (signed, and encrypted with JWE when needed). But a JWT is not “authentication” by itself — it is evidence that authentication already happened, issued by an identity provider after a successful login. Authentication happens at the IdP; authorization decisions happen at your API, using the token's claims plus your own policy.

Encoded is not encrypted

A standard JWT payload is base64url-encoded, not encrypted. Anyone holding the token can read every claim. Never put secrets, passwords, or sensitive PII in a JWT unless you are explicitly using JWE and the design demands it.

Where Auth0 fits

Auth0 is an identity platform — an IdP and CIAM solution — that helps teams implement authentication flows and issue tokens (usually JWTs) for apps and APIs. It handles user authentication (database users, social login, enterprise SSO), MFA, ID- and access-token issuance, sessions and refresh-token rotation, and customizable identity workflows through rules and actions.

What it does not do is own all of your authorization. Auth0 can store roles and permissions and stamp them into token claims, but fine-grained, contextual, resource-level authorization is still enforced at your API layer or in a dedicated policy engine. Treat the claims Auth0 issues as trusted inputs to your decision — not the decision itself.

IAM basics in Auth0's vocabulary

Much of the confusion around “IAM in Auth0” is vocabulary. The general IAM concepts above all exist in Auth0, but under product names that do not always match. Mapping one onto the other makes the dashboard, the docs and the token claims read as the same system:

IAM concept, Auth0 term, Where it shows up in a token
IAM conceptAuth0 termWhere it shows up in a token
Identity provider / authorization server
Tenant (your Auth0 domain)
iss — the tenant's URL
User store or upstream identity source
Connection (database, social, enterprise)
Prefix of sub, such as auth0| or a social provider name
Client — the app asking for access
Application
azp — the application's client ID
Protected resource
API, identified by its Identifier
aud
Permission
Permission defined on an API
scope and, with RBAC on, permissions
Role
Role (a bundle of API permissions)
Not by default — added through an Action as a namespaced claim
Business customer / B2B tenant
Organization
org_id
Policy hook at sign-in
Action on the post-login trigger
Any custom claim it sets, or a denied login

Two consequences follow. First, “IAM” in Auth0 is not one menu: it is the combination of connections, applications, APIs, roles and Actions. Second, roles are a dashboard concept until you choose to put them in the token — your API sees permissions and claims, never the role object itself, which is one more reason to authorize on permissions rather than role names.

Auth0 authentication vs. authorization, concretely

The comparison people search for is really a question about where a boundary sits. Auth0 has features on both sides of it, which is exactly why the two get conflated. Splitting them by what the platform can know makes the line obvious:

Auth0 handles (authentication side), Your API handles (authorization side)
Auth0 handles (authentication side)Your API handles (authorization side)
Verifying credentials, MFA, passkeys, social and enterprise SSO
Whether this request may touch this record
Sessions, refresh-token rotation, logout across apps
Whether the record belongs to the caller's tenant
Issuing ID tokens and access tokens, signing them, publishing the keys
Validating that signature and every registered claim
Storing roles and permissions, stamping them into claims
Deciding what those roles mean for a given operation
Coarse gating: which applications may request which API's scopes
Contextual gating: amount thresholds, time windows, data class, device trust

Mechanically, the Auth0 side works by registering your API with an identifier that becomes the token's aud claim, defining permissions on it, assigning those permissions to roles, and enabling RBAC so the granted permissions ride along in the access token. Anything you add beyond the registered claims has to be namespaced with a URI-shaped key, because unnamespaced custom claims are dropped:

Exampleaccess-token-claims.json — registered claims plus namespaced custom claimsjson
{  "iss": "https://login.example.com/",  "sub": "auth0|a1b2c3d4e5",  "aud": ["https://api.example.com", "https://login.example.com/userinfo"],  "exp": 1740415049,  "azp": "3kL9xQ2mNpR7vT",  "scope": "read:invoices write:invoices",  "permissions": ["read:invoices", "write:invoices"],  "https://api.example.com/tenant": "tenant_042",  "https://api.example.com/cost_centers": ["CC-110", "CC-240"]}

Your API fetches the signing keys from the tenant's JWKS endpoint, caches them by key ID, and pins the expected algorithm rather than reading it out of the token header. Then it treats permissions and the namespaced tenant claim as two of several inputs — never as the answer. The token says this caller may write invoices somewhere. Only your database knows whether invoice 4471 is one of them.

Validating an Auth0 JWT in your API, step by step

An Auth0 access token for a registered API is a JWT signed with RS256 by default: the tenant signs with a private key, and anyone can verify with the public key it publishes. That asymmetry is the point — your API never holds a secret that could mint tokens. Decode the first segment of a token and you see which key to use:

Examplejwt-header.json — the decoded first segmentjson
{  "alg": "RS256",  "typ": "JWT",  "kid": "nYx2b8qL0f3Kp"}

The kid (key ID) names one key in the tenant's JSON Web Key Set, published at https://YOUR_DOMAIN/.well-known/jwks.json and also discoverable from the tenant's /.well-known/openid-configuration document. A correct validator does the same eight things on every request, in this order, and rejects at the first failure:

  1. Read the bearer token from the Authorization header; reject anything missing or malformed with 401.
  2. Pin the algorithm. Accept RS256 only. Never let the header's alg choose — that is how “alg: none” and key-confusion attacks work.
  3. Find the key by kid in a cached copy of the JWKS. On an unknown kid, refetch once (keys rotate), with a rate limit so a flood of forged kids cannot hammer the endpoint.
  4. Verify the signature with that public key.
  5. Check iss equals your tenant URL exactly, including the trailing slash.
  6. Check aud contains your API's Identifier. A token minted for another API on the same tenant is validly signed and still not yours.
  7. Check exp and nbf with a small clock-skew allowance, typically under a minute.
  8. Check the permission the endpoint needs is in scope or permissions — then hand over to the resource-level check against your own data.

Steps 1–7 are authentication of the token; only step 8 begins authorization, and it is the coarse half. Use a maintained JOSE/JWT library for steps 2–7 rather than hand-rolled parsing, and configure it with the issuer, audience and algorithm explicitly instead of relying on defaults. Access tokens for a new Auth0 API default to a 24-hour lifetime; for anything that touches money or personal data, shorten it and lean on refresh-token rotation instead.

The checks people skip

Skipping aud is the most common gap: every API on the tenant then accepts every other API's tokens. The second is caching the JWKS forever, which turns a routine key rotation into an outage.

Auth0 authorization: the four levers

When people search for “Auth0 authorization,” they usually mean one of four distinct features. Each moves a different amount of the decision into the identity platform:

Lever, What it does, Use it for
LeverWhat it doesUse it for
RBAC on an API
With “Enable RBAC” and “Add Permissions in the Access Token” switched on, the permissions granted through a user's roles ride in the permissions claim
Coarse, operation-level gates: may this caller write invoices at all
Post-login Actions
Code that runs at sign-in, can deny the login or add namespaced claims such as tenant or role names
Context the API needs on every call; blocking sign-ins that break policy
Organizations
Models B2B customers as organizations with their own members and roles, stamped as org_id
Multi-tenant SaaS where each customer is a tenant of yours
Fine-grained authorization (FGA)
A separate relationship-based service, built on the open-source OpenFGA, that answers “can user X do Y on object Z” at request time
Per-record sharing and hierarchies that would bloat a token

The first three all put data into the token at sign-in, so they are fixed for the token's lifetime and blind to which record is requested. The fourth is a runtime check your API calls. That is the line to design around: anything that can change during a session, or depends on the specific object, belongs in a runtime check — whether that is an FGA call or a tenant-scoped query in your own database.

ID token vs. access token

The ID token describes the user and is for your application to consume. The access token is for your API and is the only one it should accept. Sending an ID token to an API — or accepting one there — is a common and avoidable mistake.

How they work together: the typical flow

For a web or mobile app calling an API, the pieces line up in a predictable sequence:

  1. The user signs in via Auth0 — that is authentication.
  2. Auth0 issues an access token, usually a JWT.
  3. The app calls your API with an Authorization: Bearer <token> header.
  4. Your API validates the signature (trust), verifies iss, aud, and exp (correctness), then checks scopes, roles, and claims (authorization).
  5. The API returns data only if the request is permitted.

Drawn as a diagram, the identity and access management flow with Auth0 has one boundary that matters — the line between what the identity platform proves and what your API decides:

Exampleiam-flow.txt — identity and access management with Auth0, as a diagramtext
  User / service       │  1. sign in (password + MFA, SSO, passkey,       │     or client credentials for a machine)       ▼  ┌─────────────────────────────┐  │  Auth0 tenant  (the IdP)    │   AUTHENTICATION  │  connections · Actions      │   who is this?  │  roles → permissions        │  └─────────────────────────────┘       │  2. access token (JWT): iss, aud, exp,       │     scope, permissions, namespaced claims       ▼  Client app ── 3. Authorization: Bearer <token> ──┐                                                   ▼  ┌──────────────────────────────────────────────────┐  │  Your API                                        │  AUTHORIZATION  │  4. verify signature against JWKS (kid)          │  may this caller  │  5. check iss · aud · exp                        │  do this, to this  │  6. check permission for the endpoint            │  record, now?  │  7. resource policy: tenant, owner, amount       │  │  8. log the decision                             │  └──────────────────────────────────────────────────┘       │  allowed → data      denied → 403

Illustrative
Illustrative validation checklist an API runs on every request before returning data.

Never trust the frontend to hide power

Even with a valid JWT, the API must enforce authorization at the endpoint and resource level. Hiding an admin button in the UI is not access control — a determined caller sends the request anyway.

Authorization models you will meet

JWT-based systems typically lean on three models, often combined:

Model, How it decides, Trade-off
ModelHow it decidesTrade-off
RBAC (role-based)
Users get roles — admin, manager, viewer — that map to permissions
Simple and common, but rigid when access depends on context
ABAC (attribute-based)
Decisions weigh attributes: department, location, risk, device trust, data class
Flexible and powerful, but harder to implement and audit
Scopes (OAuth 2.0)
Tokens carry scopes like read:orders or write:orders
Ideal for API operations; needs consistent scope design across services

Mature architectures blend them: roles for coarse access, scopes for API operations, and ABAC policies for the sensitive decisions that depend on which record is being touched.

What an IAM role actually is

A role is a named bundle of permissions you attach to an identity so you can reason about people instead of about individual grants. “Finance admin” is a role; write:invoices is a permission; the role exists so that onboarding an analyst is one assignment rather than forty. Three distinctions are worth keeping straight, because the words get used loosely:

  • Role vs. group. A group is a collection of identities; a role is a collection of permissions. Groups often map onto roles, but the mapping is a choice, not a given.
  • Role vs. scope. A role describes what the person may do. An OAuth scope describes what the person has authorized this application to do on their behalf. A user with a finance role using a read-only integration should still be limited by the scope.
  • Role vs. policy. A role is static — it does not know which record you asked for. A policy can weigh the request itself. Roles answer “what kind of user is this”; policies answer “is this particular action allowed right now.”

Roles fail in two predictable directions. They multiply — one role per client, per region, per exception, until nobody can say what a given role grants and reviews become theatre. Or they stay small and get stretched, so a single broad role covers a job it was never scoped for. Both failures produce the same symptom: a valid token that reaches far more than its holder needs.

Illustrative
Illustrative: reach of one over-broad role claim, before and after resource-level policy checks are added at the API.

The fix is not to abandon roles. It is to keep them coarse and few, put operation-level detail in scopes, and leave every question that depends on which record to a policy check that runs against your own data. Roles get a caller into the right building. They are not supposed to open every door inside it.

Where auth actually goes wrong

The failures rarely come from a broken token format. They come from the checks teams skip around it — accepting a token without fully validating it, handing out scopes far broader than a client needs, or checking that a user is a “finance_admin” but never checking whether this invoice belongs to their tenant.

Illustrative
Illustrative breakdown of where token-based access failures tend to originate.

Security essentials for JWT and IAM

  • Validate tokens fully. Check the signature with the correct algorithm and key, then iss, aud, exp (with clock skew), token type, and the required scopes or roles.
  • Keep tokens short-lived. A short expiration shrinks the blast radius of a stolen token. Use refresh tokens carefully, especially on SPAs and mobile.
  • Never authorize on a claim alone. Claims are inputs. Still ask: can this user access this specific invoice ID, in this tenant?
  • Design for revocation. Stateless tokens are efficient but hard to revoke — pair short lifetimes, rotating refresh tokens, and introspection or deny-lists for high-risk flows.
  • Keep secrets out of the payload. Assume every claim is readable by whoever holds the token.

IAM security beyond the request

That checklist secures a single request. IAM security is also about the state of the system between requests — the accounts nobody uses, the keys nobody rotates, the permissions that accumulate as people change teams. These are the controls that tend to matter in a real review:

  • Least privilege as the default, not the cleanup. New roles start with nothing and earn permissions. The opposite order never converges, because nobody volunteers to have access removed.
  • Privilege drift. Someone who moves from AP to controllership keeps both sets of permissions unless something takes the first away. Re-certify access on a schedule and on role change, and make the reviewer someone who would notice.
  • Machine identities outnumber people. Service accounts, CI runners and integrations are identities too, usually with broader scopes and longer-lived credentials. They deserve the same expiry, rotation and inventory as staff.
  • Key rotation and algorithm pinning. Fetch signing keys from JWKS and cache by key ID so rotation is a non-event. Pin the expected algorithm server-side; never let a token's own header choose how it is verified.
  • Tenant isolation as a hard boundary. In a multi-tenant system, the tenant check belongs in the query, not in a conditional that a future refactor can drop.
  • Audit trails you can actually query. Log the decision, the claims it rested on and the resource, not just the fact that a request arrived. The test is whether you can answer “who could have read this record, and did they” without a migration.
  • A rehearsed revocation path. Know in advance how you kill a session, a refresh token and an API key, and how long each takes to propagate. Finding out during an incident is expensive.

Not “who has access?” but “who would still have access if we were wrong about someone?”

— The question behind every access review

Why this matters for document-heavy workflows

Many teams first hit serious IAM and authorization complexity when they digitize high-volume, sensitive workflows — invoices, receipts, contracts, and identity documents. These processes move PII, financial data, and contractual terms, so access has to guarantee that only authorized roles can upload or view certain document types, that extracted fields land only in permitted systems, and that a full audit trail exists for compliance.

That is the setting for a document extraction service such as Parser, which returns the fields you define from a document through a REST API. The service reads the document; who may send it, which roles may see the result, where the fields are allowed to land and what is logged are decisions of the system around it. Design that part with the same care as the extraction itself.

The practical shape of that is covered elsewhere on this blog. Our guide to integrating document extraction via API walks through bearer tokens, key rotation and least-privilege credentials in the context of an extraction pipeline. And because extracted fields flow onward into payment runs and ERP writes, it is worth reading how a malicious document can try to steer that pipeline — an authorization boundary is what stops an attacker-chosen field from becoming an attacker-chosen action.

3

JWT parts: header, payload, signature

RFC 7519

the standard behind JWT

2

layers: authn at the IdP, authz at the API

Common questions

What does IAM stand for?

Identity and Access Management, sometimes written IdAM. It refers to the whole discipline of managing identities and controlling what they can reach — not to one product or one login box.

How does IAM work?

In six stages: an identity is provisioned, authenticated, and issued a token or session; then a policy decides whether a specific action on a specific resource is allowed, that decision is enforced where the data is served, and it is logged. The identity provider owns the first three; your own code owns the rest.

What is an IAM role?

A named bundle of permissions assigned to an identity, so access is granted by job rather than one permission at a time. A role is static and does not know which record was requested — keep roles coarse and let a policy check handle resource-level decisions.

Is Auth0 authentication or authorization?

Primarily authentication, plus the storage and issuance of authorization data. It verifies identity and stamps roles, permissions and scopes into tokens; deciding what those mean for a given record stays with your API.

What is the difference between an ID token and an access token?

The ID token tells your application who signed in. The access token authorizes calls to an API and is the only one an API should accept. They are both JWTs, which is why they get mixed up.

How do I validate an Auth0 JWT?

Fetch the public keys from your tenant's /.well-known/jwks.json, pick the one matching the token's kid, verify the RS256 signature with the algorithm pinned server-side, then check iss, aud and exp, and finally the permission the endpoint requires.

What is the difference between JWT and OAuth?

JWT is a token format. OAuth 2.0 is an authorization framework that defines how clients obtain access to APIs. OAuth access tokens can be JWTs, but they do not have to be.

Is Auth0 an IAM solution?

Auth0 is an identity platform widely used for authentication, SSO, and token issuance. In many organizations it functions as a key part of an IAM strategy — especially for customer identity and application authentication — rather than the entire IAM stack.

Does a JWT prove a user is authenticated?

It can indicate authentication occurred, if it was issued by a trusted identity provider. Your API must still validate the token and enforce authorization rules before granting access.

What should be stored in a JWT?

Only what the API needs to make decisions — user ID, issuer, audience, expiration, and the scopes or roles that matter. Avoid sensitive data unless the token is encrypted and the design explicitly requires it.

Bringing it together

IAM is the overarching approach to identity and access. Authentication confirms who someone is; authorization controls what they can do. A JWT is a verifiable, portable way to carry the claims that inform those decisions — often as an OAuth access token — while Auth0 helps you implement secure authentication and token issuance at scale. Do all of that well, add resource-level checks the token can't make on its own, and the buzzwords become what they were meant to be: the foundation for scalable, secure digital operations.

More
The newest other posts

More from the blog.

Solutions
Where Parser fits

Related solutions.

  • Invoice processing

    Vendor, tax ID, line items, taxes and due date from invoices and bills.

    Fields vendor tax_id line_items total

    Invoice processing, see use case
  • Contract extraction

    Parties, dates, amounts and the clauses you name, read at intake.

    Fields parties effective_date term contract_value

    Contract extraction, see use case
  • Receipts & expenses

    Merchant, tax ID, total, taxes and expense category from receipts and proofs of payment.

    Fields merchant tax_id total taxes

    Receipts & expenses, see use case
  • Bank statements

    Account, period, opening and closing balances and one row per transaction from bank statements.

    Fields account_holder account_number period_end closing_balance

    Bank statements, see use case
  • Bills of lading

    Shipper, consignee, ports, vessel and cargo lines from each bill of lading, to JSON or Excel.

    Fields bl_number shipper shipped_on_board_date cargo_lines

    Bills of lading, see use case

See it on your own documents.

In the demo we process your documents live — no slides, no fluff.

30 minutes · no commitment