{
  "id": 7235772,
  "title": "Stop Returning “Access Denied”",
  "url": "https://urgent.news/2026/09/14/stop-returning-access-denied",
  "topic": "tech",
  "section": "Tech",
  "published": "2026-09-14T03:33:58.000Z",
  "source": {
    "name": "Dev.to",
    "slug": "dev-to",
    "url": "https://dev.to/anusha_mukka/stop-returning-access-denied-197b"
  },
  "original_language": "en",
  "account": "Security teams dedicate much of their time evaluating whether a request should be granted access. Users who make such requests encounter this decision through a concise interface, typically a status code and a brief message. This is the third installment in a series discussing the intersection of security design and production systems. When a user attempts to export a report, they might receive a \"403 Forbidden\" response. While the service is technically accurate, it is also poised to generate a support ticket. The phrase \"Access denied\" does not clarify whether the request violated policy, an approval expired, or an identity dependency failed. These scenarios may yield the same HTTP status code but require different remedies. Authorization errors are integral to the product interface, and overlooking them can lead to insecure systems that are difficult to use and maintain. It is essential to distinguish the enforcement effect from the reason behind it. The enforcement effect is generally straightforward: permit or prohibit. However, the reason necessitates more detailed structuring. The effect code remains constant for software, whereas the message can be altered without disrupting clients. A unique decision identifier links the user-facing error to diagnostic records in protected storage. It is imperative not to burden clients with parsing lengthy text. Someone may inadvertently design workflows based on punctuation. When a device posture service encounters a timeout, the authorization layer cannot affirm whether the device adheres to policy. Failing closed could be appropriate for the operation, but returning \"DEVICE_NOT_COMPLIANT\" would be incorrect since the system did not verify noncompliance. Instead, a distinct reason like \"DEVICE_STATUS_UNAVAILABLE\" should be used, informing the user to retry or contact the service owner. It alerts operations to investigate the posture dependency and prevents the creation of false security findings against the device. Different readers require varying levels of detail. The requester should not receive the entire policy trace, as this could expose group names, thresholds, or restricted resources. Responses should be differentiated based on the audience. The requester receives a safe reason and a next step: \"This export requires a current approval. Request a new approval and try again.\" The service owner sees policy identifiers and dependency status, including the decision identifier. Authorized investigators can retrieve attribute provenance and evaluation trace using the decision identifier. The explanation endpoint must have its own authorization to prevent attackers from using it as a convenient policy-discovery tool. The next step must be actionable rather than a generic link. The linked workflow must correspond to the reason. \"Contact your administrator\" is seldom helpful, as it does not specify which administrator to contact, what information is required, or whether the recipient can alter the outcome. Common denial reasons should have defined owners and tested remediation procedures. If no remediation exists because the action is prohibited, this should be clearly stated. The REASON_CATALOG includes various reasons, each with a message and a suggested next step. For instance, \"APPROVAL_EXPIRED\" informs the user that the approval for the export has expired and directs them to request a new approval. \"DEVICE_STATUS_UNAVAILABLE\" indicates that the device status could not be verified, suggesting a retry. \"EXPORT_NOT_PERMITTED\" notifies the user that the report cannot be exported. Testing the user-facing explanations is crucial. Authorization tests should not only verify the effect but also ensure the reason and absence of sensitive details are accurate. The following Python function exemplifies such testing: ```python def test_expired_approval_is_actionable(): response = export_report(approval=expired_approval()) assert response.status_code == 403 assert response.body[reason][code] == APPROVAL_EXPIRED assert response.body[reason][next_step] == request_export_approval assert \"required_clearance\" not in response.body ``` Ultimately, a denial can still provide a positive user experience when security controls incorrectly block legitimate users. The key is to design an effective path out of the denial. Returning a stable reason code and offering a single, safe explanation preserves the detailed evidence elsewhere, ensuring the user still receives a comprehensive understanding of the situation. Even though \"403 Forbidden\" remains the HTTP status code, it should not be the sole point of communication.",
  "summary": "Security teams spend a lot of time deciding whether a request should be allowed. The person making that request experiences the decision through a much smaller interface: usually a status code and a sentence. This is Part 3 of Security Infrastructure in Practice, a series about what happens when security design meets production systems. A user tries to export a report and receives: 403 Forbidden…",
  "key_points": [
    "403 Forbidden status code provides enforcement effect but lacks reason for denial",
    "Unique decision identifier links user-facing error to diagnostic records",
    "Reason catalog offers defined owners and tested remediation procedures for common denials"
  ],
  "editors_take": null,
  "illustration": null,
  "coverage": {
    "outlets": 1,
    "also_reported_by": []
  },
  "ai_generated": true,
  "disclaimer": "Summaries, key points and the editor’s take are written by software from other outlets’ reporting and may contain errors — always check the linked original."
}