Manual validation of GraphQL access control is tedious, error-prone, and unscalable.
When RBAC rules evolve, it’s easy to introduce permission drift — granting unintended access or revoking legitimate ones.
This article shows how to automate authorization difference testing to ensure only the right roles access the right resources.
1. Problem: RBAC Drift in GraphQL APIs
When field-level or object-level rules change, it’s difficult to answer:
- “Can a
USER
still reademail
?” - “Did we accidentally give
GUEST
access toadminStats
?” - “Are
ADMIN
andSUPERADMIN
aligned across environments?”
2. Solution Overview
We define:
- A set of test queries that represent core permissions
- A matrix of roles & tokens
- Expected
allow
/deny
outcomes - A CI test that verifies these expectations per role
3. Setup: Roles and Test Matrix
Example roles:
const roles = ['GUEST', 'USER', 'ADMIN'];
Test cases:
const tests = [
{
name: 'user can read their own profile',
query: `{ me { id email } }`,
role: 'USER',
expectAccess: true,
},
{
name: 'guest cannot access stats',
query: `{ adminStats { userCount } }`,
role: 'GUEST',
expectAccess: false,
},
];
4. Token Generator
Use pre-signed JWTs or mock tokens per role:
function getTokenForRole(role: string): string {
return jwt.sign({ role, userId: '123' }, SECRET);
}
5. Execution Logic
Use graphql-request
or apollo-client
to send queries:
import { request, gql } from 'graphql-request';
async function runTest({ query, role, expectAccess }) {
const token = getTokenForRole(role);
try {
const res = await request({
url: 'http://localhost:4000/graphql',
document: gql`${query}`,
requestHeaders: { authorization: `Bearer ${token}` }
});
if (!expectAccess) throw new Error('Expected denial but got data');
} catch (err) {
if (expectAccess) throw new Error('Expected data but got denied');
}
}
6. CI Integration
Run tests via jest
, vitest
, or plain node
script:
node test-permissions.js
Add to GitHub Actions, GitLab CI, or any CI/CD runner to verify RBAC on every push.
7. Bonus: Diff Snapshot Testing
Store expected output snapshots per role using jest-snapshot
:
expect(response).toMatchSnapshot(`${role}-${queryName}`);
Detect subtle API shape drifts, even across environments.
Final Thoughts
Authorization is dynamic. Regression is silent.
With RBAC diff testing in place, you get early warning before a misconfigured permission becomes an incident.
Next:
- Visual matrix reports (role × query coverage)
- Field-level permission map generators
- Integration with Hasura metadata or Apollo schema diff tools
Declare the rules. Prove the intent. Automate the boundary.