API Access Control
REST endpoints, GraphQL queries, and the missing auth check that leaks data through every field resolver. Why API access control is harder than page-level auth.
REST endpoints, GraphQL queries, and the missing auth check that leaks data through every field resolver. Why API access control is harder than page-level auth.
API endpoints — REST, GraphQL, RPC — are the backbone of modern web applications. Unlike page-level routes that return HTML and redirect unauthorised users, APIs silently return structured data. A missing access control check on an API endpoint is invisible to the user but devastating to data security.
Page-level access control is straightforward: if the user is not logged in, redirect to the login page. If they lack the required role, show a 403 page. APIs, however, expose individual fields, nested objects, and batch operations — each of which may have different access requirements. A single GraphQL query can request a user profile, their private notes, and billing history in one round trip. If any resolver omits an access check, the entire response leaks sensitive data.
REST APIs face the same problem at the route level. An endpoint like GET /api/invoices/:id must verify that the authenticated user owns the requested invoice. The temptation is to apply a blanket auth check — "is user logged in?" — without adding per-resource ownership verification. This single omission makes every resource endpoint an IDOR surface.
The sandbox below simulates a GraphQL-like API playground. You can send two queries — one public (list projects), one private (view billing details). Toggle the "auth check" switch to see how a missing resolver check in the private query returns sensitive billing data that should be restricted to the account owner.
query {
projects {
id
name
status
visibility
}
}In 2021, a critical vulnerability was discovered in GitLab's GraphQL API. The platform used GraphQL for many of its modern API features, including project lookup. The endpoint accepted a project ID and returned full project details — including the project's visibility level, issues, merge requests, and confidential notes — but the GraphQL resolver did not verify that the requesting user had access to the specific project. Any authenticated user could query any project by its numeric ID.
The root cause was that GitLab's GraphQL resolvers performed an authentication check at the query root but assumed that individual field resolvers would inherit that check. In practice, the resolver for project lookup performed a database fetch by ID without scoping the query to projects the user was a member of. GitLab patched it by adding explicit access checks inside each resolver that could access user-scoped resources.
The fix is never to assume that a single auth gate at the edge protects every downstream resource. Each resolver (GraphQL) or route handler (REST) that accesses a user-scoped resource must independently verify that the authenticated user has permission to access that specific resource.
// VULNERABLE — GraphQL resolver trusts the root auth
const resolvers = {
Query: {
project: (_root, args, ctx) => {
return db.projects.findUnique({ where: { id: args.id } });
},
},
};
// SAFE — resolver checks membership
const resolvers = {
Query: {
project: async (_root, args, ctx) => {
const project = await db.projects.findUnique({
where: { id: args.id },
});
if (!project) return null;
const member = await db.members.findFirst({
where: { projectId: args.id, userId: ctx.user.id },
});
if (!member && project.ownerId !== ctx.user.id) {
throw new ForbiddenError('Access denied');
}
return project;
},
},
};Additional mitigations: avoid exposing internal IDs in API responses; use opaque cursor-based pagination instead of offset-based; implement DataLoader patterns that batch access checks; and audit each resolver for missing ownership verification during code review.
1.Why is API access control harder to implement correctly than page-level auth?
2.In the GitLab 2021 GraphQL IDOR vulnerability, what was the root cause?
3.A developer writes a REST endpoint that fetches an invoice by ID. They add an auth middleware that checks "is user logged in?" Is the endpoint secure?