Login
ChallengesLearn
Scoreboard
Teams
Profile

Preferences

Truesapiens

LearnAccess ControlAPI Access Control
Access Control·Lesson 6 of 12

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.

Intermediate12 min
Access ControlAPIGraphQL
Loading lesson…
PreviousAccess Control HardeningNextJWT Attacks

© 2026 Truesapiens.

Terms of ServicePrivacy PolicyCookie Policy

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.

What you'll be able to do
  • Explain why API access control is harder to get right than page-level auth.
  • Identify common API auth gaps: missing resolver checks, unprotected mutations, and blind object queries.
  • Recognise the 2021 GitLab GraphQL IDOR vulnerability and its root cause.
  • Apply per-resource access checks to both REST and GraphQL endpoints.
Key terms
Resolver
A function that populates a field in a GraphQL response. If the resolver lacks an access check, the field leaks data even when the parent query is authorised (CWE-862).
GraphQL IDOR
An Insecure Direct Object Reference vulnerability exposed through a GraphQL endpoint, where nested queries or batch requests bypass per-resource access controls.
Auth gate
A middleware or wrapper function that checks authentication and authorisation before a request reaches the business logic.
What is it?

When the API trusts the client

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.

API access control flow
Mini Map
Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.
Try it

Explore the API Explorer

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.

staging/graphql
API Explorer
query {
  projects {
    id
    name
    status
    visibility
  }
}
Hint: This query fetches public project data. It works regardless of the auth check setting.
Real-world relevance

GitLab GraphQL IDOR (2021)

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.

Mitigation

Per-resolver and per-route access checks

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.

javascriptvulnerable
// 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.

Further reading
  • CWE-862: Missing Authorization(MITRE)
  • GitLab GraphQL IDOR Disclosure (2021)(GitLab)
  • OWASP API Security Top 10(OWASP)
Key takeaways

What to remember

  • API access control is harder than page-level auth because every field and resolver may have different access requirements.
  • A blanket auth check at the route level does not protect individual resource resolvers — each one must verify ownership independently.
  • GraphQL magnifies the problem: one query can request multiple sensitive objects, and if any resolver is missing a check, all data is leaked.
  • Internal IDs in URLs or query parameters turn API endpoints into IDOR surfaces. Always scope queries by the authenticated user.
  • The 2021 GitLab incident is a textbook case: root auth passed, but per-resource membership check was absent in the project resolver.

Knowledge check

0/3 answered · 0 correct
  1. 1.Why is API access control harder to implement correctly than page-level auth?

  2. 2.In the GitLab 2021 GraphQL IDOR vulnerability, what was the root cause?

  3. 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?