.claude/skills/api-bypass-layers/SKILL.md
Multi-layer security architecture for API bypass (superadmin/developer). Covers authentication layers, authorization, team context, RLS policies, and three-layer bypass validation. Use this skill when implementing admin bypass features or validating security architecture.
npx skillsauth add NextSpark-js/nextspark api-bypass-layersInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Security architecture for superadmin and developer bypass access in the API layer.
| File | Purpose |
|------|---------|
| packages/core/src/lib/api/auth/dual-auth.ts | App-level bypass (canBypassTeamContext) |
| packages/core/src/lib/api/entity/generic-handler.ts | CRUD handler (validateTeamContextWithBypass) |
| packages/core/migrations/001_better_auth_and_functions.sql | get_auth_user_id() function |
| packages/core/migrations/010_teams_functions_triggers.sql | can_bypass_rls(), get_user_team_ids(), RLS policies |
| packages/core/migrations/090_sample_data.sql | System Admin Team (team-nextspark-001) |
┌─────────────────────────────────────────────────────────────────┐
│ LAYER 1: AUTHENTICATION │
├─────────────────────────────────────────────────────────────────┤
│ API Key Auth │ Session Auth │
│ ├─ x-api-key header │ ├─ Browser cookies │
│ ├─ Constant-time delay │ ├─ Better Auth framework │
│ ├─ Key expiration check │ └─ Full access (scopes: ['all']) │
│ ├─ Failed attempt lockout │ │
│ └─ Scoped permissions │ │
└─────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ LAYER 2: AUTHORIZATION │
├─────────────────────────────────────────────────────────────────┤
│ Admin Bypass Check (Three-Layer Validation) │
│ ├─ LAYER 1: Role check (superadmin OR developer) │
│ ├─ LAYER 2: Header confirmation (x-admin-bypass) │
│ └─ LAYER 3: System Admin Team membership (team-nextspark-001) │
│ │
│ Permission Check │
│ ├─ Entity-level permissions (entity.action format) │
│ ├─ Team role hierarchy (owner > admin > member > viewer) │
│ └─ Scope validation for API keys │
└─────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ LAYER 3: TEAM CONTEXT │
├─────────────────────────────────────────────────────────────────┤
│ Normal Mode (isBypass = false) │
│ ├─ x-team-id header REQUIRED │
│ ├─ Validate user is team member │
│ ├─ Filter by teamId AND userId │
│ └─ Reject if not member (403 TEAM_ACCESS_DENIED) │
│ │
│ Bypass Mode (isBypass = true) │
│ ├─ x-team-id header OPTIONAL │
│ ├─ Skip membership validation │
│ ├─ Skip userId filter │
│ └─ Cross-team or specific team access │
└─────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ LAYER 4: DATABASE RLS │
├─────────────────────────────────────────────────────────────────┤
│ PostgreSQL Row Level Security (Final Defense) │
│ ├─ can_bypass_rls() - superadmin ALWAYS, developer IF member │
│ ├─ get_auth_user_id() - reads from app.user_id GUC │
│ ├─ get_user_team_ids() - array of user's teams │
│ └─ Policies: SELECT/INSERT/UPDATE/DELETE per table │
│ │
│ Example Policy: │
│ can_bypass_rls() OR teamId = ANY(get_user_team_ids()) │
└─────────────────────────────────────────────────────────────────┘
File: packages/core/src/lib/api/auth/dual-auth.ts:13-31
// System Admin Team - Members can bypass team context validation
export const SYSTEM_ADMIN_TEAM_ID = 'team-nextspark-001'
// Header required to confirm cross-team access intention
export const ADMIN_BYPASS_HEADER = 'x-admin-bypass'
export const ADMIN_BYPASS_VALUE = 'confirm-cross-team-access'
// Roles that can potentially bypass team context
const ELEVATED_ROLES = ['superadmin', 'developer'] as const
File: packages/core/src/lib/api/auth/dual-auth.ts:205-226
Three-layer validation for admin bypass at the application level:
export async function canBypassTeamContext(
authResult: DualAuthResult,
request: NextRequest
): Promise<boolean> {
// LAYER 1: Must have elevated role
if (!authResult.success || !authResult.user) return false
const hasElevatedRole = ELEVATED_ROLES.includes(
authResult.user.role as typeof ELEVATED_ROLES[number]
)
if (!hasElevatedRole) return false
// LAYER 2: Must include confirmation header
const bypassHeader = request.headers.get(ADMIN_BYPASS_HEADER)
if (bypassHeader !== ADMIN_BYPASS_VALUE) return false
// LAYER 3: Must be member of System Admin Team
const isMember = await checkSystemAdminMembership(authResult.user.id)
if (!isMember) {
console.log('[dual-auth] User has elevated role but is not member of System Admin Team')
}
return isMember
}
| Layer | Check | Requirement |
|-------|-------|-------------|
| 1 | Role | user.role is superadmin OR developer |
| 2 | Header | x-admin-bypass: confirm-cross-team-access |
| 3 | Membership | User belongs to team-nextspark-001 |
File: packages/core/src/lib/api/entity/generic-handler.ts:267-309
async function validateTeamContextWithBypass(
request: NextRequest,
authResult: DualAuthResult,
userId: string
): Promise<{ valid: true; teamId: string | null; isBypass: boolean } | { valid: false; error: NextResponse }> {
const teamId = getTeamIdFromRequest(request)
// Check if user can bypass team validation
const canBypass = await canBypassTeamContext(authResult, request)
if (canBypass) {
// Admin bypass: teamId is optional
// - If provided: filter by that team (no membership check)
// - If not provided: cross-team access (all teams)
// isBypass = true means skip userId filter too (see all records)
console.log('[GenericHandler] Admin bypass active:', { userId, teamId: teamId || 'cross-team' })
return { valid: true, teamId, isBypass: true }
}
// Normal flow: require teamId and membership
if (!teamId) {
const response = createApiError(
'Team context required. Include x-team-id header.',
400,
undefined,
'TEAM_CONTEXT_REQUIRED'
)
return { valid: false, error: await addCorsHeaders(response) }
}
const isMember = await validateTeamMembership(userId, teamId)
if (!isMember) {
const response = createApiError(
'Access denied: You are not a member of this team',
403,
undefined,
'TEAM_ACCESS_DENIED'
)
return { valid: false, error: await addCorsHeaders(response) }
}
return { valid: true, teamId, isBypass: false }
}
File: packages/core/src/lib/api/entity/generic-handler.ts
The isBypass flag controls userId filtering in queries:
// Line ~509, 538, 563, 1138
if (userId && !entityConfig.access?.shared && !skipUserFilter && !isBypass) {
// Apply userId filter - only see own records
}
// When isBypass = true, this filter is skipped, allowing cross-user access
File: packages/core/migrations/010_teams_functions_triggers.sql:115-147
CRITICAL DIFFERENCE from App-Level:
CREATE OR REPLACE FUNCTION public.can_bypass_rls()
RETURNS BOOLEAN AS $$
DECLARE
current_user_id TEXT;
user_role TEXT;
is_system_admin_member BOOLEAN;
BEGIN
current_user_id := public.get_auth_user_id();
-- Get user role
SELECT role INTO user_role
FROM public."users"
WHERE id = current_user_id;
-- Superadmin ALWAYS bypasses (no team membership check)
IF user_role = 'superadmin' THEN
RETURN TRUE;
END IF;
-- Developer can bypass ONLY if member of System Admin Team
IF user_role = 'developer' THEN
SELECT EXISTS(
SELECT 1 FROM public."team_members"
WHERE "userId" = current_user_id
AND "teamId" = 'team-nextspark-001'
) INTO is_system_admin_member;
RETURN is_system_admin_member;
END IF;
RETURN FALSE;
END;
$$ LANGUAGE plpgsql STABLE SECURITY DEFINER;
| Role | App-Level (canBypassTeamContext) | DB-Level (can_bypass_rls) | |------|----------------------------------|---------------------------| | superadmin | Needs header + team membership | ALWAYS bypasses | | developer | Needs header + team membership | Only with team membership | | member | Cannot bypass | Cannot bypass |
Why the difference?
File: packages/core/migrations/001_better_auth_and_functions.sql:10-24
CREATE OR REPLACE FUNCTION public.get_auth_user_id()
RETURNS TEXT
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = public
AS $$
DECLARE v TEXT;
BEGIN
v := current_setting('app.user_id', true);
IF v IS NULL OR v = '' THEN
RETURN NULL;
END IF;
RETURN v;
END;
$$;
File: packages/core/migrations/010_teams_functions_triggers.sql:98-109
CREATE OR REPLACE FUNCTION public.get_user_team_ids()
RETURNS TEXT[] AS $$
DECLARE
user_teams TEXT[];
BEGIN
SELECT ARRAY_AGG(tm."teamId") INTO user_teams
FROM public."team_members" tm
WHERE tm."userId" = public.get_auth_user_id();
RETURN COALESCE(user_teams, ARRAY[]::TEXT[]);
END;
$$ LANGUAGE plpgsql STABLE SECURITY DEFINER;
File: packages/core/migrations/010_teams_functions_triggers.sql:164-208
-- SELECT policy
CREATE POLICY "teams_select_policy" ON public."teams"
FOR SELECT TO authenticated
USING (
public.is_superadmin() -- Alias for can_bypass_rls()
OR
id IN (
SELECT "teamId" FROM public."team_members"
WHERE "userId" = public.get_auth_user_id()
)
);
-- UPDATE policy
CREATE POLICY "teams_update_policy" ON public."teams"
FOR UPDATE TO authenticated
USING (
public.is_superadmin()
OR
"ownerId" = public.get_auth_user_id()
)
WITH CHECK (
public.is_superadmin()
OR
"ownerId" = public.get_auth_user_id()
);
-- DELETE policy
CREATE POLICY "teams_delete_policy" ON public."teams"
FOR DELETE TO authenticated
USING (
public.is_superadmin()
OR
"ownerId" = public.get_auth_user_id()
);
| Code | HTTP Status | Trigger |
|------|-------------|---------|
| TEAM_CONTEXT_REQUIRED | 400 | Missing x-team-id header (non-bypass mode) |
| TEAM_ACCESS_DENIED | 403 | User not member of specified team |
| AUTHENTICATION_FAILED | 401 | Invalid credentials |
// Headers required
const headers = {
'x-team-id': 'team-xxx' // REQUIRED
}
// Behavior
// - teamId is REQUIRED
// - Validate user is team member
// - Filter results by teamId AND userId
// - Return 403 TEAM_ACCESS_DENIED if not member
// Headers required
const headers = {
'x-admin-bypass': 'confirm-cross-team-access', // REQUIRED
'x-team-id': 'team-xxx' // OPTIONAL (for filtering)
}
// Behavior
// - teamId is OPTIONAL
// - Skip membership validation
// - Skip userId filter (isBypass = true)
// - Access all teams or specific team
┌──────────────────────────────────────────────────────────────────────────┐
│ API REQUEST │
├──────────────────────────────────────────────────────────────────────────┤
│ Headers: │
│ ├─ Authorization: Bearer <api-key> OR Cookie: session=... │
│ ├─ x-team-id: team-xxx (required in normal mode) │
│ └─ x-admin-bypass: confirm-... (enables bypass mode) │
└──────────────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────────┐
│ BACKEND SECURITY LAYERS │
├──────────────────────────────────────────────────────────────────────────┤
│ │
│ LAYER 1: Authentication (dual-auth.ts:71-89) │
│ └─ authenticateRequest() → DualAuthResult │
│ │
│ LAYER 2: Authorization (dual-auth.ts:205-226) │
│ └─ canBypassTeamContext() → role + header + team check │
│ │
│ LAYER 3: Team Context (generic-handler.ts:267-309) │
│ └─ validateTeamContextWithBypass() → { teamId, isBypass } │
│ │
│ LAYER 4: Query Building (generic-handler.ts:509+) │
│ └─ Add teamId/userId filters based on isBypass flag │
│ │
│ LAYER 5: Database RLS (010_teams_functions_triggers.sql:115-147) │
│ └─ can_bypass_rls() OR teamId = ANY(get_user_team_ids()) │
│ │
└──────────────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────────┐
│ FILTERED RESPONSE │
├──────────────────────────────────────────────────────────────────────────┤
│ { success: true, data: [...filtered results...], info: { total: N } } │
└──────────────────────────────────────────────────────────────────────────┘
describe('Admin Bypass', () => {
const SUPERADMIN_KEY = Cypress.env('SUPERADMIN_API_KEY')
it('should allow cross-team access with bypass', () => {
cy.request({
method: 'GET',
url: '/api/v1/products',
headers: {
'Authorization': `Bearer ${SUPERADMIN_KEY}`,
'x-admin-bypass': 'confirm-cross-team-access'
// No x-team-id = cross-team mode
}
}).then((response) => {
expect(response.status).to.eq(200)
// Returns data from ALL teams
})
})
it('should filter to specific team with bypass', () => {
cy.request({
method: 'GET',
url: '/api/v1/products',
headers: {
'Authorization': `Bearer ${SUPERADMIN_KEY}`,
'x-admin-bypass': 'confirm-cross-team-access',
'x-team-id': 'team-tmt-002' // Filter to specific team
}
}).then((response) => {
expect(response.status).to.eq(200)
// Returns data only from team-tmt-002
})
})
it('should reject bypass without header', () => {
cy.request({
method: 'GET',
url: '/api/v1/products',
headers: {
'Authorization': `Bearer ${SUPERADMIN_KEY}`
// Missing x-admin-bypass and x-team-id
},
failOnStatusCode: false
}).then((response) => {
expect(response.status).to.eq(400)
expect(response.body.code).to.eq('TEAM_CONTEXT_REQUIRED')
})
})
})
| Feature | Implementation | Benefit | |---------|----------------|---------| | Dual Authentication | API Key + Session | Flexibility for different use cases | | Three-Layer App Bypass | Role + Header + Team | Defense in depth, prevents accidents | | RLS Final Defense | PostgreSQL policies | Data isolation even if app bypassed | | Team Isolation | App + DB checks | Multi-tenant security | | System Admin Team | Hardcoded ID | Prevents privilege escalation | | isBypass Flag | Skip userId filter | Cross-user visibility for admins |
// NEVER: Allow bypass based on role alone at app level
if (user.role === 'superadmin') {
return true // WRONG! Missing header and team checks at app level
}
// CORRECT: Use the three-layer function
const isBypass = await canBypassTeamContext(authResult, request)
// NEVER: Trust client-provided bypass status
const isBypass = request.body.isBypass // User can fake this!
// CORRECT: Validate bypass on server
const isBypass = await canBypassTeamContext(authResult, request)
// NEVER: Skip userId filter without checking bypass
if (user.role === 'superadmin') {
skipUserFilter = true // WRONG! Should use isBypass from validation
}
// CORRECT: Use the isBypass flag from validateTeamContextWithBypass
const { teamId, isBypass } = await validateTeamContextWithBypass(request, authResult, userId)
if (!isBypass) {
// Apply userId filter
}
Before finalizing bypass implementation:
x-admin-bypass header value is exactly confirm-cross-team-accessteam-nextspark-001 (hardcoded)can_bypass_rls() functionisBypass flag controls userId filter in queriesx-team-id headerx-team-id optionalTEAM_CONTEXT_REQUIRED (400), TEAM_ACCESS_DENIED (403)better-auth - Authentication patterns and session managementpermissions-system - RBAC + Features + Quotas permission modeldatabase-migrations - PostgreSQL RLS policiesnextjs-api-development - API route patterns with dual authcypress-api - API testing with bypass scenariosdevelopment
Zod validation patterns for this Next.js application. Covers schema definition, API validation, form integration, error formatting, and type inference. Use this skill when implementing validation for APIs, forms, or entity schemas.
development
Review UI code for Web Interface Guidelines compliance. Use when asked to "review my UI", "check accessibility", "audit design", "review UX", or "check my site against best practices".
testing
Test coverage metrics and registry system for this Next.js application. Covers FEATURE_REGISTRY, FLOW_REGISTRY, TAGS_REGISTRY, and coverage metrics interpretation. Use this skill when evaluating test coverage, identifying gaps, or planning testing priorities.
development
TanStack Query (React Query) patterns for data fetching in this Next.js application. Covers useQuery, useMutation, optimistic updates, cache invalidation, and anti-patterns. Use this skill when implementing data fetching or state management with server data.