Skip to main content

SaaS API function-level authorization

Secure SaaS APIs from broken function-level authorization with deny-by-default policies, role and tenant checks, and direct tests for privileged actions.

In this guide

What is function-level authorization, and how should a SaaS API enforce it?

Function-level authorization decides whether a caller may perform an operation such as inviting users, exporting records, changing billing or administering a tenant. It is separate from authentication, which identifies the caller, and object-level authorization, which checks access to a particular record. Deny functions by default and make an explicit server-side permission check for every operation, role and relevant tenant context. A hidden button or `/admin` URL convention is not an access-control boundary.

Define permissions around actions, not only role names

List sensitive operations such as changing roles, issuing API keys, exporting data, deleting a workspace or modifying payment settings. Map each action to the roles, tenant relationships and workflow conditions that permit it. Keep permissions narrow and understandable; broad labels like `manager` should not silently grant unrelated administration features.

Check permission in the server-side business operation

Invoke a consistent policy or authorization service from every controller, resolver, job and service path that performs the action. Do not rely on the front end, URL prefix, HTTP method, API gateway rule or client-supplied role claim alone. For actions involving a specific record, apply function permission and object-level permission together.

Default unknown and newly added functions to denied

Require an explicit grant before an endpoint or operation becomes available to a role. Review new routes, GraphQL mutations, alternate API versions, exports and administrative functions during code review. If the identity, tenant or policy decision is missing, fail closed and return a safe denial without performing a partial change.

API function authorization matrix
Function / operationMemberWorkspace ownerSupport / platform roleNegative test and owner
Invite or remove member
Export tenant data
Change role, billing or security settings

Which authorization mistakes commonly expose privileged API functions?

Do not infer privilege from the route name or client interface

A privileged function can sit under a general route such as `/users`, a GraphQL mutation or a mobile-only endpoint. Attackers can call APIs directly, change an HTTP method or replay a request without using the interface. Apply server-side policy at the action that reads or changes protected state.

Protect background and support operations too

Exports, scheduled jobs, support tools and internal service endpoints can expose the same privileged functions as a web controller. Carry an authenticated actor or service identity into queued work, record why the action is allowed and audit sensitive changes. Do not treat an internal network or hidden route as authorization.

How do you test function-level authorization?

Build a role-by-function matrix and test every denied cell

For each operation, test anonymous callers and representative member, owner, support and administrator accounts. Attempt prohibited actions directly through the API, including alternate methods, routes, GraphQL mutations and API versions. Verify the denial happens before data disclosure or state change.

Test cross-tenant and changed-membership scenarios

Use separate test tenants and try privileged functions with the wrong tenant context, after a role is removed, after an invitation is revoked and from a suspended account. Confirm queued work rechecks authority or carries a deliberate, auditable authorization decision rather than trusting stale client state.

Function-level authorization questions