Skip to content
All insights

Security & Accessibility

API security checklist: tenant isolation, permissions and abuse controls

Use a resource-authorization test matrix to review tenant boundaries, field permissions, retries and abuse controls in a growing API.

An API security review should begin with the resource a request can reach: authorize the user, action and organization on the server before returning data or changing state. A valid login, well-formed identifier or encrypted connection does not establish that permission.

This checklist uses an illustrative multi-tenant job platform. Members can read their organization's jobs; managers can assign them. The worked test matrix turns that policy into requests you can reproduce, including cross-tenant identifiers, field updates and exports. Follow it with input, session, integration and abuse controls appropriate to your API.

Key takeaways

  • Authorize each action against the specific resource and organization on the server.
  • Treat input schemas, output fields, file processing, and outbound requests as separate boundaries.
  • Design rate limits, retries, and privileged operations around the cost of abuse.
  • Test denied access and recovery behavior as carefully as successful requests.
Applying this to your product?Discuss API security

Map the data and trust boundaries first

Draw the path from a client request to the systems it touches. Include the application server, identity provider, database, object storage, queue workers, webhooks, and third-party APIs. Record where a user-controlled value becomes a database query, file path, external request, or background job.

Identify the assets that matter: customer records, credentials, payment actions, administrative permissions, and availability. Then describe plausible failures in business terms. An attacker might read a competitor’s report, submit expensive jobs repeatedly, replace a payout destination, or obtain a long-lived download link after losing access.

Use this map to prioritize work. A public catalog endpoint and an account-ownership transfer endpoint do not have the same risk. The latter may need stronger verification, detailed auditing, and a recovery process. Applying one generic middleware policy to both can leave important behavior unexamined.

Keep the map close to the code and update it when integrations change. A new export worker or AI connector creates a new boundary even if the public API routes remain unchanged.

Test tenant isolation in a Supabase SaaS application

Separate authentication from authorization

Authentication establishes an identity or session. Authorization decides whether that identity may perform an action in the current context. Keep those questions distinct in the code so a successful token check does not accidentally become permission to access every resource.

OWASP’s authorization guidance recommends least privilege, denying access by default, and checking permissions on every request. Translate those principles into concrete policies for your application. For example, a technician may update a report only when they belong to the organization, are assigned to the job, and the report remains editable.

Avoid trusting role names or organization identifiers supplied by the client. Resolve membership and permissions from authoritative server-side information. If permission data is cached, define how revocations become effective and which operations require a fresh check.

Make denied behavior predictable. Return an appropriate response without revealing unnecessary details about resources the user cannot access. Log enough context for investigation, but avoid collecting sensitive payloads merely because a request failed. A clear authorization layer should make the permitted and denied cases easy to review together.

OWASP: authorization guidance

Enforce ownership at the resource boundary

Write the policy in terms of the requested action, authenticated principal and actual record. For GET /jobs/:id in the illustrative platform, the job must belong to an organization in which the caller has active read permission. For PATCH /jobs/:id/assignee, the caller also needs assignment permission, and the proposed assignee must be an eligible member of that organization.

Resolve membership from authoritative server data and scope the lookup accordingly. Do not trust a tenant ID from the request body, assume a random identifier cannot be guessed, or accept a broad role without checking the resource. OWASP's object-level authorization guidance calls for checking the user's permission whenever an endpoint acts on a supplied object identifier.

Use at least two synthetic organizations and several roles in tests. The matrix below defines an example product policy, not a library implementation. Denials should follow your documented 403/404 policy without leaking the other tenant's record or changing its state. Repeat the invariant for list, export, attachment and worker paths.

Synthetic job-platform authorization test matrix
Request caseExpected behaviorEvidence to assert
Active member reads a job in tenant AReturn only the permitted fields of tenant A's job.Response matches the fixture; administrative fields are absent.
Member in A supplies a job ID from BDeny access under the API's disclosure policy.No B data, download link or state change is produced.
Member tries to change an assigneeDeny assignment when the role lacks that capability.The database record remains unchanged.
Manager in A selects an assignee from BReject the cross-tenant relationship.Assignment remains valid for A; B membership is not exposed.
Revoked member uses an existing sessionDeny the request after membership is revoked.A still-valid credential does not restore revoked permissions.
Export includes a job from BApply tenant authorization to export selection and download.Generated files and links contain only authorized records.

OWASP: broken object-level authorization

A valid identifier can still be the wrong resource

A request for invoice 42 has a signed-in caller from tenant A, but the invoice belongs to tenant B. The server denies access despite a valid request format and an existing record.
Synthetic invoice example: both the record ID and request format can be valid while tenant authorization fails. Scope the server’s lookup and permission check to the caller and resource.

Validate input without confusing validation with safety

Define accepted types, lengths, ranges, formats, and allowed fields for each operation. Reject or deliberately ignore unknown fields according to a documented policy. Avoid passing an entire request body directly into an update operation, where a caller might set fields intended only for administrators.

Validation should express business rules as well as syntax. A quantity can be a valid integer and still exceed an allowed limit. A date can be correctly formatted and still fall outside an editable period. Apply these rules in the authoritative service so another client cannot bypass interface validation.

Use parameterized database operations and appropriate encoding at output boundaries. A schema validator does not make string-built queries safe, and an escaped HTML value does not make an arbitrary URL safe to fetch. Different destinations require different controls.

Set limits before expensive processing. Cap request size, collection length, recursion depth where relevant, and the amount of work an endpoint can schedule. A technically valid request should not be able to consume unbounded memory or create an unlimited queue backlog.

Return only the fields the caller needs

Build explicit response models rather than serializing complete database records. Internal notes, reset tokens, provider identifiers, and administrative flags can leak when a new database column automatically appears in an API response. An allowlist makes exposure a deliberate decision.

Review list, search, export, and error responses separately. A detail endpoint may be carefully restricted while a CSV export exposes additional fields. Search suggestions can reveal the existence of records, and verbose errors can reveal internal configuration or query structure.

For downloadable files, define who can issue a link, how long it lasts, and whether access must be rechecked when the file is retrieved. A signed URL is a capability held by whoever receives it; its lifetime should fit the sensitivity and intended use of the file.

Treat logs and analytics as additional outputs. Redact credentials and sensitive fields, control access, and define retention. Debugging convenience is not a reason to send complete customer requests to every monitoring vendor connected to the application.

FROM READING TO DOING

Strengthening the boundaries of your API?

Bring your sensitive request paths and role policy. We can help define the boundary tests and engineering work.

Protect sessions and privileged operations

Choose a session model that fits the clients and deployment environment. Review cookie settings, token storage, expiration, refresh behavior, and logout. Browser applications need deliberate protection against cross-site request forgery when credentials are sent automatically, and every client needs a plan for compromised or revoked credentials.

Avoid placing long-lived privileged tokens in mobile or browser code. Client applications are inspectable. A secret embedded in a build is not protected merely because the code is bundled or compiled. Route privileged provider operations through a server that enforces the user’s permissions.

Require additional safeguards for changes such as transferring account ownership, altering payout details, or granting administrator access. Depending on the product, those safeguards may include recent authentication, an approval step, notifications, or a delay with a cancellation path.

Design recovery alongside prevention. If an administrator loses access or a credential is compromised, support needs a documented verification and restoration process. Improvised account recovery can undermine otherwise careful authentication controls.

Handle files and outbound connections as independent risks

A file upload can affect storage, malware exposure, document parsing, and later rendering. Restrict size and supported formats, use server-generated storage names, and avoid executing or directly trusting uploaded content. Where the workflow warrants it, quarantine files until scanning or validation completes.

Keep uploaded files separate from executable application assets. Decide whether downloads should be served as attachments or displayed inline, and set appropriate response headers. A document preview service also needs limits because malformed or unusually large files can exhaust processing resources.

For endpoints that fetch a user-provided URL, constrain destinations and redirects. Internal services and metadata endpoints should not become reachable through a public fetch feature. Validate the destination at the network boundary as well as the initial string, accounting for resolution and redirect behavior.

Apply similar care to webhook destinations and integration callbacks. A legitimate business feature can become a network access tool if callers can choose arbitrary targets. Prefer explicit connector configurations with scoped permissions and an auditable owner.

Design for retries, abuse, and expensive actions

Rate limits should reflect what an operation costs and who is using it. A cheap cached read and a report that scans millions of records need different controls. Combine user, organization, and infrastructure limits where appropriate so one account cannot consume a shared service’s entire capacity.

Use idempotency for actions that may be retried, such as creating an order or scheduling a paid job. Bind an idempotency key to the correct caller and operation, and define what happens when the same key is reused with different input. A duplicate request should not quietly create duplicate side effects.

Set concurrency and spending limits around third-party services. AI generation, document conversion, and messaging can create substantial costs even when each request is authorized. A bounded queue and per-customer allowance help keep a compromised account from becoming an unrestricted spending channel.

Expose useful retry information without encouraging infinite client retries. Distinguish temporary failures from rejected actions, use backoff, and make the final state discoverable. Security and reliability meet here: predictable recovery reduces the pressure to bypass controls during incidents.

Test the negative cases and production assumptions

Build tests around access matrices. For each important action, cover an authorized user, a user in the wrong organization, an insufficient role, a revoked membership, and an unauthenticated request. Test the same policy through bulk routes, background jobs, and file downloads where those paths exist.

Include malformed input, oversized requests, repeated submissions, expired credentials, and unavailable dependencies. Test whether failures leave partial records or unintended side effects. A request that returns an error after completing a payment needs a different recovery path from one that never reached the provider.

Review production configuration separately from application code. Network exposure, storage permissions, secret rotation, and logging settings can differ from development. Confirm that administrative endpoints and debug interfaces are not accessible simply because the main application’s routes are protected.

Use a security review appropriate to the product’s risk before a significant launch. Automated scanning can identify certain classes of problems, but it does not replace understanding the business authorization model or testing how a determined user can combine otherwise valid operations.

Apply the same boundaries to AI-agent tools

Make security maintainable after launch

Assign owners for access policies, dependencies, incident response, and data retention. Keep a record of privileged changes and review unusual activity with enough context to distinguish abuse from legitimate customer behavior. Alerts should lead to a documented action rather than simply accumulate.

Add a security acceptance section to feature work. A new endpoint should explain its caller, resources, allowed actions, data exposure, and limits. This makes review a normal part of delivery rather than a separate exercise performed only before launch.

After an incident or near miss, update the underlying controls and tests. If support discovers that an export bypasses a permission check, fix the shared policy boundary where possible rather than adding one isolated condition. The objective is a system whose security properties remain understandable as the product gains users, integrations, and additional teams.

Design audit evidence for sensitive SaaS account changes

Frequently asked questions

Is a valid JWT enough to secure an API?

No. A valid token can establish claims about a caller, but the server must still validate its intended use and authorize the requested resource and action. Membership can change, resources can belong to another organization, and different actions can require different permissions. Token verification is one layer in that decision.

Should permissions be checked in the frontend?

Frontend checks help users understand available actions and avoid unnecessary errors. They do not enforce security because callers can bypass the interface. Keep authoritative checks on the server, and make the interface reflect those policies without assuming its hidden buttons protect the underlying endpoints.

How much rate limiting is enough?

There is no universal request count. Consider the cost of each operation, expected customer behavior, shared capacity, and the impact of a compromised account. Test limits against realistic bursts and provide a clear response when a limit is reached. Expensive operations often need concurrency and budget controls as well as request counts.

When should an API receive a dedicated security review?

Before exposing sensitive data or privileged actions, after major authentication or tenancy changes, and when adding high-impact integrations. The depth should match the risk. A review should include business logic and operational configuration, not only dependency scanning or a generic endpoint checklist.

YOUR NEXT STEP

Let’s work through your next step.

Tell us what you’re building, what you’ve tried, and where you need a hand. We’ll work with you to define a practical way forward.

Keep reading.

Choose which optional tools can run. You can reopen these settings from the footer. These choices do not subscribe you to marketing emails.

Cookie Policy