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.
| Function / operation | Member | Workspace owner | Support / platform role | Negative 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.
Check roles, groups and tenant relationships consistently
A person may have different roles across workspaces, belong to nested groups or receive a temporary delegated permission. Resolve the correct tenant and current membership from trusted server data for every request. Avoid a single global role flag when authority is scoped to one workspace or action.
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.
Keep regression tests beside the function policy
When adding an operation or changing role rules, add positive and negative tests for its callers. Review authorization outcomes in logs without recording sensitive payloads, and investigate unexpected denials or access grants. Re-run the matrix after API gateway, identity-provider or role-model changes.
Function-level authorization questions
Is an authenticated user allowed to call every API endpoint?
No. Authentication identifies the caller; function-level authorization decides which operations that caller may perform. Check permission on every sensitive action.
Does an `/admin` URL prove the endpoint is protected?
No. Route names are not security controls. Verify authorization in server-side code for the operation, even when the interface or path looks administrative.
Is function authorization the same as object-level authorization?
No. Function-level checks whether a caller may perform an operation at all; object-level checks whether they may perform it on a particular record. Many requests need both.
Should an API allow new operations to every authenticated role by default?
No. Deny by default and explicitly grant each function to the roles and tenant contexts that need it. This avoids a new route silently widening access.
Related practical guides
Related issue guides
Sources and publication record
Draft prepared 27 September 2026; engineering, security and editorial review pending · Sources checked .
- OWASP API Security Top 10: API5:2023 Broken Function Level AuthorizationOWASP Foundation
- OWASP Cheat Sheet: AuthorizationOWASP Foundation
- OWASP API Security Top 10: API1:2023 Broken Object Level AuthorizationOWASP Foundation