Introduction
Role-Based Access Control (RBAC) systems validate that the authenticated user's token includes the required scope or permission for the requested resource and action. When the token does not include the necessary scope -- because the role was not assigned, the scope was not requested during authentication, or the resource has more restrictive policies -- the API returns a 403 Forbidden error.
Symptoms
- API returns
403 Forbiddenwithinsufficient_scopeerror - Error response includes the missing scope in the
WWW-Authenticateheader - User can access some endpoints but not others within the same API
- Token introspection shows the user's roles do not include the required permission
- Error message:
{"error":"insufficient_scope","scope":"resource:write"}
Common Causes
- User role not assigned the required permission for the specific resource
- OAuth token requested without the necessary scope in the
scopeparameter - Resource-level RBAC policy added after the user's role was defined
- Token cached with old scopes before the role was updated
- Cross-tenant or cross-organization resource access not configured
Step-by-Step Fix
- 1.Check the token's current scopes: Inspect what permissions the token has.
- 2.```bash
- 3.# Decode the JWT token
- 4.echo "eyJhbGci..." | cut -d. -f2 | base64 -d | jq '.scope, .roles'
- 5.# Or introspect the token
- 6.curl -X POST https://auth.example.com/oauth/introspect \
- 7.-d "token=ACCESS_TOKEN" \
- 8.-H "Authorization: Basic BASE64(client:secret)"
- 9.
` - 10.Identify the required scope for the endpoint: Check the API documentation.
- 11.
` - 12.# API documentation should specify required scopes per endpoint:
- 13.# POST /api/resources - requires "resource:write" scope
- 14.# GET /api/resources - requires "resource:read" scope
- 15.
` - 16.Request a new token with the required scope: Include the missing scope.
- 17.```bash
- 18.curl -X POST https://auth.example.com/oauth/token \
- 19.-d "grant_type=client_credentials" \
- 20.-d "scope=resource:read resource:write" \
- 21.-d "client_id=CLIENT_ID" \
- 22.-d "client_secret=CLIENT_SECRET"
- 23.
` - 24.Assign the required role to the user if scope is role-based: Update RBAC.
- 25.```bash
- 26.# Via admin API
- 27.curl -X POST https://admin.example.com/api/users/$USER_ID/roles \
- 28.-H "Authorization: Bearer $ADMIN_TOKEN" \
- 29.-H "Content-Type: application/json" \
- 30.-d '{"role": "resource-admin"}'
- 31.
` - 32.Test the API request with the updated token: Verify access is granted.
- 33.```bash
- 34.curl -X POST https://api.example.com/api/resources \
- 35.-H "Authorization: Bearer $NEW_TOKEN" \
- 36.-H "Content-Type: application/json" \
- 37.-d '{"name": "test-resource"}'
- 38.# Should return 201 Created instead of 403
- 39.
`
Prevention
- Document required scopes for each API endpoint in the API documentation
- Use token introspection to validate scopes before making API requests
- Implement progressive scope requests -- start with read-only and request write scopes as needed
- Monitor 403 error rates and correlate with missing scope types
- Implement RBAC change management processes that include scope impact analysis
- Use automated testing to verify that service accounts have the required scopes for their operations