Core Concepts
You've configured [Authorize(Roles = 'Admin')] and policy-based authorization in dozens of .NET apps.
The question
You've configured [Authorize(Roles = 'Admin')] and policy-based authorization in dozens of .NET apps. ServiceNow uses 'ACLs' — Access Control Lists — to protect data at the table, row, and field level. Walk me through how ACLs work, the evaluation order, and what surprises a developer used to role-based auth in .NET.
Why This Comes Up in Interviews
ServiceNow ACLs are a declarative security layer that evaluates automatically on every data access — UI, API, scripts, everywhere. Unlike .NET where you explicitly decorate endpoints with [Authorize], ACLs are always on. This is closer to SQL Server's row-level security or Azure AD's conditional access than ASP.NET's attribute-based auth.
Evaluation Order (Most to Least Specific)
// ACL Evaluation Hierarchy // Think of it like CSS specificity — most specific wins // // 1. table.field (e.g., incident.caller_id) → Most specific // 2. table.* (e.g., incident.*) → Table-level // 3. *.field (e.g., *.caller_id) → Field across all tables // 4. *.* (e.g., *.*) → Global default // // Operations: read, write, create, delete // Must pass BOTH the matching field rule AND the matching table rule. // More-specific rules stop processing of less-specific ones, and // several rules matching at the SAME point are OR'd (pass any one). // .NET Equivalent Mental Model: // [Authorize(Roles = "itil")] → Role check in ACL // [Authorize(Policy = "SameTeam")] → Script/Condition in ACL // Both must pass = AND behavior
The Three ACL Components
- Role requirement: Like
[Authorize(Roles = "itil")]. User must have the specified role. - Condition: A filter query on the record — like a LINQ
.Where()clause. Example:active=true^assigned_to=javascript:gs.getUserID()means 'only if the record is active AND assigned to the current user.' - Script: Advanced logic in JavaScript. Like writing a custom
IAuthorizationHandlerin .NET policy-based auth. Returns true/false.
// ACL Script example — like a custom IAuthorizationHandler
// This runs on every read of the incident table
answer = (function() {
// Allow if user is in the same department as the caller
var caller = current.caller_id.department;
var userDept = gs.getUser().getDepartmentID();
return caller == userDept;
})();What Surprises .NET Developers
- ACLs are always enforced: Unlike .NET where forgetting
[Authorize]means open access, ServiceNow denies access by default if no ACL grants it. It's a whitelist model, not a blacklist. - Admin bypasses everything: The
adminrole skips ACL evaluation entirely — unless the ACL script explicitly checks for admin. This is like having a global[AllowAnonymous]override for admins, which can be a security concern. - API calls respect ACLs: If you call the Table API as a service account, ACLs on that user's roles determine what's returned. There's no separate API auth layer — unlike .NET where you might have different auth for UI vs API.
- Row + field AND catches people: If the table-level ACL requires role 'itil' AND the field-level ACL requires role 'incident_manager', the user needs BOTH roles — like stacking
[Authorize]attributes. But note the flip side: multiple ACLs matching at the same point are OR'd, so passing any one of them is enough.
💡 .NET Developer Pro Tip
Enable Debug Security Rules (System Security > Debugging) in your dev instance — it's like adding app.UseDeveloperExceptionPage() for auth. You'll see exactly which ACLs evaluated, which passed, and which blocked access. Essential for debugging 'why can't this user see this record?' issues.