name: apex-custom-permissions-check description: "Custom Permissions in Apex: FeatureManagement.checkPermission, $Permission global variable, permission-set gating of feature code, Custom Permission metadata. NOT for CRUD/FLS enforcement (use security-apex-crud-fls). NOT for standard Salesforce permissions (use permission-set-architecture)." category: apex salesforce-version: "Spring '25+" well-architected-pillars:
- Security
- Operational Excellence tags:
- apex
- custom-permissions
- featuremanagement
- permission-sets
- authorization triggers:
- "featuremanagement.checkpermission apex custom permission"
- "gate apex feature behind custom permission"
- "custom permission vs profile permission best practice"
- "how to check custom permission in lwc and apex"
- "custom permission in validation rule and formula"
- "feature flag pattern salesforce custom permission" inputs:
- Feature or code path to gate
- Target user populations (profiles / permission sets)
- Test scenarios needing permission toggling outputs:
- Custom Permission metadata
- Apex check pattern
- Permission Set assignment plan
- Test class using System.runAs with permission set dependencies: [] version: 1.0.0 author: Pranav Nagrecha updated: 2026-04-21
Apex Custom Permissions Check
Activate when gating code paths, feature availability, or field-level behavior behind a Custom Permission. Custom Permissions are the platform's feature-flag primitive — declarative on/off tokens assigned via Permission Sets or Profiles that Apex, LWC, formulas, and validation rules can check consistently.
Before Starting
- Create the Custom Permission metadata first. Setup → Custom Permissions or via
-meta.xmldeployment. - Assign via Permission Set, not Profile for maintainability.
- Use
FeatureManagement.checkPermissionfor runtime checks — not the deprecated Schema describe path.
Core Concepts
Custom Permission metadata
<CustomPermission xmlns="http://soap.sforce.com/2006/04/metadata">
<label>Approve Big Deals</label>
<isLicensed>false</isLicensed>
</CustomPermission>
API Name is the handle used in Apex / formulas.
Apex check
Boolean canApprove = FeatureManagement.checkPermission('Approve_Big_Deals');
if (!canApprove) throw new AuraHandledException('Not authorized');
Per-user, transaction-cached. Safe to call repeatedly.
Formula / validation rule
$Permission.Approve_Big_Deals returns Boolean for declarative gating.
LWC
import hasApprove from '@salesforce/customPermission/Approve_Big_Deals';
Returns Boolean at module load — evaluate in getters.
Testing
@IsTest
static void canApprove() {
User u = TestUserFactory.makeUser();
insert new PermissionSetAssignment(
AssigneeId = u.Id,
PermissionSetId = [SELECT Id FROM PermissionSet WHERE Name = 'Big_Deal_Approvers'].Id
);
System.runAs(u) { ... }
}
Common Patterns
Pattern: Gate Apex service entry point
public with sharing class ApprovalService {
public void approve(Id oppId) {
if (!FeatureManagement.checkPermission('Approve_Big_Deals')) {
throw new AuraHandledException('Not authorized');
}
}
}
Pattern: LWC conditional render
import hasApprove from '@salesforce/customPermission/Approve_Big_Deals';
export default class Approve extends LightningElement {
get showButton() { return hasApprove; }
}
Pattern: Validation rule gate
AND(ISCHANGED(Status), Status='Approved', NOT($Permission.Approve_Big_Deals))
Decision Guidance
| Need | Mechanism |
|---|---|
| Feature flag for code path | Custom Permission |
| Object CRUD | Object permission on Permission Set |
| Field-level gate | FLS on Permission Set |
| Temporary admin toggle | Custom Setting + check |
| A/B test | Custom Permission + assignment script |
Recommended Workflow
- Define the feature boundary — what code runs for privileged users only.
- Create the Custom Permission metadata.
- Create a Permission Set granting it; assign to users.
- Add
FeatureManagement.checkPermissionat service entry points. - Mirror the check in LWC and formulas where needed.
- Write Apex tests using
System.runAs+ PermissionSetAssignment. - Document the permission in the feature runbook.
Review Checklist
- Custom Permission metadata exists with clear label
- Assigned via Permission Set (not Profile)
- Apex uses
FeatureManagement.checkPermission - LWC uses
@salesforce/customPermission/import - Formulas use
$Permission.<API_Name> - Tests cover both granted and denied
- No hardcoded user Ids in permission checks
- Permission documented in feature doc
Salesforce-Specific Gotchas
isLicensed=truerequires the user's managed-package license; non-licensed users cannot receive it even if assigned.Schema.describefor Custom Permissions is deprecated — useFeatureManagement.checkPermission.- Custom Permission grants propagate through Permission Set Groups — audit muting flags on groups.
Output Artifacts
| Artifact | Description |
|---|---|
| Custom Permission metadata | XML file |
| Permission Set granting the CP | Metadata + assignment plan |
| Apex test class | Positive + negative cases with runAs |
Related Skills
security/permission-set-architecture— permission-set designsecurity/security-apex-crud-fls— CRUD/FLS enforcementlwc/lwc-user-permission-aware-components— LWC permission checks