...

/

Authentication, Authorization, and Least Privilege

Authentication, Authorization, and Least Privilege

Secure your systems end-to-end by combining strong authentication, precise authorization, and audit-ready logging—so safety becomes effortless and built-in.

Authentication, authorization, and least privilege aren’t “security team concerns.” They’re foundational architecture principles.

They determine whether:

  • A junior dev can accidentally delete prod data,

  • A leaked token causes a minor annoyance or a breach,

  • A feature is safe to ship fast—or stuck waiting for a security sign-off.

This lesson gives you a practical playbook for securing a feature end-to-end so that only the right people can run exports, tokens can’t be abused for long, and every decision is logged and reviewable.

Let’s get started.

AuthN vs. AuthZ
In case you don’t know the difference:

  • AuthN (Authentication): Proving you are who you say you are.

    • For example, logging in with a password and Multi-Factor Authentication (MFA).

  • AuthZ (Authorization): Proving you can do what you’re trying to do.

    • For example, an intern shouldn’t be able to delete production databases.

Authentication: The login basics

Use OIDC on OAuth 2.0 for sign-in. It’s an open standard, so you plug into trusted providers instead of writing custom auth code.

  • Web apps: Use secure cookies (HttpOnly, SameSite): the browser handles them, and those flags protect against XSS/CSRF.

  • APIs/service-to-service: Use JWTs. They’re stateless and scale cleanly without a central session store.

Best practices

  • Keep access tokens short-lived (10–15 min) → if one leaks, the replay window is tiny.

  • Use refresh-token rotation → if a refresh token is stolen, it’s invalid after first use, giving you a detection signal.

  • Offer safe recovery: backup codes, rebind device, change alerts.

Customer data export flow: A user logs in via OIDC, gets a 10-minute token, and when they click “Export all,” we ask for step-up MFA. Day-to-day is smooth; sensitive actions get extra checks.

Add step-up only where it counts

Use MFA or Web Authentication (WebAuthn) for the few actions that can really hurt you, like:

  • Exporting all customers.

  • Exporting >10k records.

  • Changing admin settings (roles, payouts).

Customer data export flow: Browsing reports is frictionless. Clicking “Export all” triggers MFA. That 5-second check blocks account takeovers without slowing real users.

Enforce permissions on the server (and log it)

Never trust the UI—attackers hit your APIs directly. Add one server-side policy check for each sensitive endpoint and log the decision.

Customer data export flow: Set the policy so owners can export their customers. Only admins can export all customers.

For our customer data exports, write a one-line policy for each sensitive action:

POST /exports/all
requireAuth()
decision = policy.can(user, "export.all")
audit({event:"AuthorizationDecision", user, action:"export.all",
resource:"customers", result: decision?"allow":"deny", traceId})
if (!decision) return 403
if (needsStepUp(user)) return 401 "step-up-required"
startExportAll()
Server permissions for the Customer Data exports

A non-admin gets “403 Forbidden” on /exports/all; the log shows the deny with a reason like not_admin.

Back it up in the database

Add row-level security (RLS) and column-level security (CLS) in the DB:

  • RLS: Non-admins only see their own tenant’s rows.

  • CLS/Encryption: Sensitive fields like emails are always protected.

Even if an API route is misconfigured, the DB blocks cross-tenant reads.

Customer data export flow: A stray query in the export feature can’t pull another tenant’s customers because RLS filters by tenant_id, and encrypted columns stay protected at rest.

Lock down cloud roles with least privilege

Give each service only what it needs:

  • Use short-lived Identity and Access Management (IAM) credentials with deny-by-default.

  • Write narrow policies (read customers, write exports bucket, nothing else).

Customer data export flow: For our export job, the role can read customer records and write a CSV to a specific bucket; it cannot touch billing, logs, or other storage.

Add small, targeted tests

Write small, targeted tests:

  • Unit: allow(admin) and deny(non-admin) on export.all.

  • Integration: Owner can export their scope; non-owner gets 403; admin can export all.

  • DB test: RLS blocks cross-tenant reads.

  • Step-up test: “Export all” requires MFA.

These quick checks catch regressions before prod.

Shift-left

Every feature should include a short Security block in the design document like the one given below:

  • Identity and tokens: “OIDC on OAuth 2.0; access tokens 15m; refresh rotation on.”

  • Step-up points: “MFA/WebAuthN on: export all, export >10k, admin settings.”

  • Permission model: “RBAC + ABAC; server check at endpoints; RLS in DB.”

  • Abuse cases:

    • Token replay → short token life + refresh rotation + timestamp/nonce.

    • Non-owner export → server policy + RLS + tests.

Pitfalls to avoid:

  • Long-lived bearer tokens

  • Authorization checks only in the UI

  • Hand-rolled cryptography

That’s it. With these simple moves, the customer data export feature is safe by default, easy to audit, and hard to misuse, and you didn’t slow anyone down.

Ask