ScreenshotNeo

BlogGuides

Authentication vs. Authorization: Core Concepts for Securing Your Apps

Authentication proves identity; authorization decides what that identity can do. Learn how OIDC, OAuth, tokens, scopes, and policies fit together.

By the ScreenshotNeo team1 October 20267 min read

Authentication answers “Who are you?” Authorization answers “What may you do?” A successful login establishes an identity, but it does not grant access to every resource or operation. Your application must authenticate the caller, then evaluate permissions for each requested action and resource.

Microsoft describes authentication as proving who you are and authorization as granting an authenticated party permission to do something. OWASP, citing NIST, defines authorization as verifying that a requested action or service is approved for a specific entity.

Authentication and authorization in one request

  1. The client presents credentials or a token.
  2. The identity system verifies the credential and establishes an identity.
  3. The API validates the token’s issuer, signature, audience, expiration, and relevant claims.
  4. An authorization policy checks the requested action, resource, tenant, role, scope, and other conditions.
  5. The API allows the operation or returns an appropriate error.

These checks are separate. An unauthenticated visitor can be authorized to read a public login page. An authenticated employee can still be denied access to another team’s payroll record.

Authentication vs. authorization

Question Authentication (AuthN) Authorization (AuthZ)
Purpose Establishes who or what is calling Decides whether that caller may perform an action
Typical inputs Password, passkey, certificate, identity token, service credential Scopes, roles, resource ownership, policy rules
Result An identity and verified claims Allow or deny for a specific operation
When it runs At sign-in or when a credential is presented On every protected operation
Common protocol OpenID Connect (OIDC) OAuth 2.0 for delegated API access

OIDC and OAuth: which one does what?

OpenID Connect is the identity layer

OpenID Connect (OIDC) adds an identity layer to OAuth 2.0. An identity provider issues an ID token containing claims about the authenticated user. The application validates those claims and uses them to establish a session or account identity. OIDC is the usual choice for user authentication and single sign-on.

OAuth 2.0 delegates API access

OAuth 2.0 is an authorization framework. After a resource owner grants access, an authorization server issues an access token. The client presents that token to a resource server, usually with a scope such as invoices.read or calendar.write. The API evaluates the token and its scopes before serving the request.

An access token is not proof of the user’s identity. If your application needs to know who signed in, use OIDC and validate the ID token. OWASP summarizes the division as: use OIDC for authentication and SSO; use OAuth for authorization to APIs.

ID tokens and access tokens

Property ID token Access token
Audience The client application The API or resource server
Meaning Identity claims about the sign-in Permission to call protected resources
Consumer Your web or mobile client The API named by the token’s audience
Use Create an application session and display account data Authorize a particular API request
Validation Issuer, signature, audience, expiration, nonce where applicable Issuer, signature, audience, expiration, scopes and other policy claims

Never send an ID token to an API as a substitute for an access token, and never treat possession of an access token as a complete identity record.

Implementing authorization at the API boundary

Apply authorization where the protected resource and action are known. A gateway can reject obviously invalid tokens, but the service that owns the data must still enforce resource-level rules.

Authorization checklist

  • Authenticate the user or calling application with an appropriate mechanism.
  • Validate issuer, signature, audience, expiration, and relevant claims.
  • Check scopes and roles for every protected operation.
  • Check ownership, tenant, project, or record-level permissions before returning data.
  • Use TLS for credentials and tokens in transit.
  • Use authorization code flow with PKCE for user-facing OAuth integrations where applicable.
  • Keep access tokens short-lived and narrowly scoped when the threat model supports it.
  • Return consistent 401 responses for missing or invalid authentication and 403 responses when an authenticated caller lacks permission.

Illustrative Node.js policy middleware

function requireScope(requiredScope) {
  return (req, res, next) => {
    // req.auth must be populated by a token-validation middleware that
    // verifies issuer, signature, audience, and expiration first.
    if (!req.auth) return res.status(401).json({ error: 'unauthenticated' });

    const scopes = new Set((req.auth.scope || '').split(' ').filter(Boolean));
    if (!scopes.has(requiredScope)) {
      return res.status(403).json({ error: 'insufficient_scope' });
    }

    next();
  };
}

app.get('/reports/:id', requireScope('reports.read'), async (req, res) => {
  const report = await loadReport(req.params.id);
  if (!report) return res.sendStatus(404);

  // Resource-level authorization: do not rely on the scope alone.
  if (report.tenantId !== req.auth.tenant_id) {
    return res.sendStatus(403);
  }
  res.json(report);
});

The snippet assumes a preceding, standards-compliant token validator. Do not decode a JWT and trust its claims without verifying its signature and validation parameters.

Calling a protected API

Replace the placeholder with an access token issued for the API. The API, rather than the client, makes the final authorization decision.

cURL

curl https://api.example.com/v1/reports \
  -H "Authorization: Bearer ACCESS_TOKEN"

Python

import os
import requests

token = os.environ["ACCESS_TOKEN"]
r = requests.get(
    "https://api.example.com/v1/reports",
    headers={"Authorization": f"Bearer {token}"},
    timeout=30,
)
r.raise_for_status()
print(r.json())

Node.js

const token = process.env.ACCESS_TOKEN;
const res = await fetch('https://api.example.com/v1/reports', {
  headers: { Authorization: `Bearer ${token}` }
});
if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
console.log(await res.json());

Common mistakes and fixes

Symptom Likely cause Fix
Every request returns 401 Missing, expired, malformed, or incorrectly signed token Send the access token in the Authorization header and validate issuer, signature, audience, and expiration against the provider configuration.
Valid login but 403 from the API The token lacks the required scope or role, or resource policy denies access Request the minimum required scope and enforce the resource owner or tenant check.
API rejects an ID token An ID token was sent to a resource server Request an access token for the API and keep the ID token for the client’s identity session.
Token works for one API but not another Audience is bound to a different resource server Request a token whose audience is the API being called.
Users see stale permissions Long-lived access tokens retain old claims Use short-lived tokens and enforce current permissions at the resource service.
Public endpoint unexpectedly requires login Authentication was applied globally Mark public routes explicitly while keeping authorization on protected operations.

Security, reliability, performance, and cost considerations

Security

  • Protect tokens as secrets. Do not place them in URLs, logs, screenshots, or client-visible error messages.
  • Use TLS everywhere credentials or tokens travel.
  • Use least-privilege scopes and roles; avoid a single all-powerful scope.
  • Validate claims on every request instead of trusting a prior successful login.
  • For browser and mobile authorization-code integrations, use PKCE.

Reliability

  • Cache provider signing keys according to the provider’s rotation guidance, while refreshing them when an unknown key identifier appears.
  • Handle clock skew consistently when checking expiration.
  • Fail closed when token validation or policy evaluation cannot be completed.
  • Log authorization outcomes without logging raw credentials or tokens.

Performance

Local signature and claim checks avoid a network round trip on every API call. Keep policy evaluation close to the resource service, and avoid loading more identity data than the decision requires. Token introspection can provide fresher revocation information but adds latency and dependency on the authorization server.

Cost

Authentication and authorization costs depend on your identity provider, token volume, directory size, and operational requirements. Compare providers using the same axes: supported OIDC and OAuth flows, token and key management, policy granularity, revocation behavior, SSO support, delegated access, auditability, and administrative complexity.

Or skip the browser setup

If your application needs a rendered capture of an authenticated or public page, ScreenshotNeo provides a single screenshot API request instead of maintaining browser automation. Cookie and consent banners, newsletter popups, and chat widgets are removed before the shot. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing result. Its MCP server lets Claude, Cursor, and other MCP clients use take_screenshot, get_page_info, and capture_pdf.

See the ScreenshotNeo API documentation for options such as custom headers, cookies, authorization, user agents, waits, selectors, device presets, PDFs, caching, signed links, asynchronous jobs, and bulk capture.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

There is a free plan with 1,000 screenshots each month and no card required; paid plans start at $5 for 3,000 screenshots. Create a free ScreenshotNeo account.

FAQ

Does authentication automatically authorize a user?

No. Authentication establishes identity. Authorization must still approve each protected action and resource.

Is OAuth an authentication protocol?

OAuth is primarily for delegated authorization to APIs. Use OIDC when your application needs user authentication and identity claims.

Can an unauthenticated request be authorized?

Yes. Public resources, such as a login page, can authorize anonymous access.

Which token should an API receive?

An access token issued for that API. An ID token is intended for the client application’s identity session.

Where should authorization be enforced?

At every service that owns or returns the protected resource, with checks for both the requested action and the specific record, tenant, or owner.