Open Closed

Detection of permission specific errors. #10780


User avatar
0
douwm created

Hi ABP Support Team,

Environment: ABP Framework & Commercial 9.2.0, .NET 9, Angular 19.2.14, OpenIddict (Pro), LeptonX 4.2.0. Multi-module solution with separate API hosts; Angular uses @abp/ng.core / @abp/ng.theme.shared (~9.2.0).

Problem: We can't reliably tell when a failure is a permission/authorization error versus another error. The same status code (usually 403, sometimes 401) is returned for missing permissions, AbpAuthorizationException, and unrelated business/infrastructure errors. This makes it hard for our Angular error interceptor (and component handlers using skipHandleError: true) to show a proper "You don't have permission" message instead of a generic one. We'd rather follow ABP conventions than parse message text.

Questions: What is the recommended way to detect a permission/authorization failure on the client — HTTP status code, a specific ABP error code, or a property in RemoteServiceErrorResponse/RemoteServiceErrorInfo? Is there a documented list of ABP authorization error codes to key off? What status code and response shape should we expect for AbpAuthorizationException / permission failures (host API vs module APIs vs via gateway)? Does ABP ever return 403 for non-permission errors (e.g. BusinessException, feature/license checks)? If so, how do we distinguish them? Is there a built-in Angular helper for this, or is custom parsing expected?

Thanks!

Markdown supported.
Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)

2 Answer(s)
  • User Avatar
    0
    AI-Bot created
    Support Team ABP Agent
    • On the client, the ABP-conventional way is to detect authorization/permission failures via the ABP error payload’s error.code, not by parsing message text.

      • For ABP authorization failures (including missing permissions that result in AbpAuthorizationException), ABP serializes the error as a RemoteServiceErrorResponse whose error.code is typically:
        • Volo.Authorization:010001
      • This is visible in server logs as well as in the serialized RemoteServiceErrorInfo (example in an ABP support ticket shows exactly this code with message “Authorization failed! Given policy has not granted.”).
    • Status code expectation

      • In practice you can see either 401 or 403 at the HTTP layer depending on the authentication/authorization pipeline behavior (e.g., whether the request is treated as unauthenticated vs authenticated but forbidden, whether a challenge happens, etc.).
      • Therefore, for Angular (and especially with gateways / separate API hosts), treat HTTP status as a hint, but use error.code to be reliable.
    • Is there a documented list of ABP authorization error codes?

      • I can verify Volo.Authorization:010001 from ABP support content, but I don’t have a verified official “complete list of authorization error codes” document in the available context.
      • So: use Volo.Authorization:010001 as the canonical permission/authorization failure code; no verified complete list is available from the tools for this specific query.
    • Does ABP ever return 403 for non-permission errors? How to distinguish?

      • Yes, you can get the same HTTP status for different categories depending on middleware/filters, but the ABP way to distinguish is again RemoteServiceErrorInfo.code:
        • Authorization: Volo.Authorization:010001
        • Other exceptions (business/validation/etc.) will have different error.code values.
      • So: don’t branch on 403 alone; branch on error.code.
    • Angular side / built-in helper

      • ABP Angular already has the “global HTTP error handling with opt-out (skipHandleError)” infrastructure (RestService + HttpErrorReporterService + error handler).
      • For your specific need (“show a specific ‘no permission’ message when it’s authorization”), implement a small check in your interceptor/handler that inspects the ABP error payload and matches error.code === 'Volo.Authorization:010001'.

      Minimal example logic (pseudo-TypeScript):

    const abpError = (err?.error as any)?.error; // RemoteServiceErrorResponse.error
      if (abpError?.code === 'Volo.Authorization:010001') {
        // show "You don't have permission"
      } else {
        // generic handling
      }
    

    Links (verified):

    • https://abp.io/support/questions/10507

    This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Hi,

    On a normal Bearer/OpenIddict API host (your setup), the HTTP status alone isn't enough, and an authorization/permission failure doesn't carry an ABP error body at all — so there's no error.code to match on for that case. Here's what actually comes back:

    • Missing permission (authenticated): 403 with an empty body — no ABP payload, no error.code, no _AbpErrorFormat response header.
    • Not authenticated (missing/expired token): 401 with an empty body (WWW-Authenticate: Bearer).
    • A BusinessException (and validation, not-found, etc.): comes back with the ABP error body — an error.error object (a message, usually a code) plus the _AbpErrorFormat: true header. A BusinessException also uses 403, which is exactly why the status alone can't separate it from a permission failure.

    The reason for the difference: ABP handles AbpAuthorizationException differently from other exceptions. Instead of serializing a JSON error body, it hands off to the authentication scheme's Forbid/Challenge (403 when authenticated, 401 when not). So the Volo.Authorization:... codes you may have seen only show up in the server log, not in the response the client gets.

    So the reliable signal on the client is "is there an ABP error body?", not the status code or a specific error code:

    • 401 → not authenticated → send the user to login.
    • 403 without an ABP error body → permission/authorization denied → show your "You don't have permission" message.
    • Any status with an ABP error body (error.error present) → business/validation/other error → show error.error.message.

    For Angular, the built-in extension point is a custom error handler (CUSTOM_ERROR_HANDLERS), which runs before the default handling based on priority:

    import { Injectable } from '@angular/core';
    import { HttpErrorResponse } from '@angular/common/http';
    import {
      CUSTOM_ERROR_HANDLERS,
      CustomHttpErrorHandlerService,
      CUSTOM_HTTP_ERROR_HANDLER_PRIORITY,
    } from '@abp/ng.theme.shared';
    
    @Injectable({ providedIn: 'root' })
    export class PermissionErrorHandlerService implements CustomHttpErrorHandlerService {
      // Higher than the built-in handlers so it wins for the bare 403 case.
      readonly priority = CUSTOM_HTTP_ERROR_HANDLER_PRIORITY.veryHigh;
    
      canHandle(error: unknown): boolean {
        // 403 with no ABP error body => authorization/permission denied.
        return error instanceof HttpErrorResponse && error.status === 403 && !error.error?.error;
      }
    
      execute(): void {
        // show your "You don't have permission" message here
      }
    }
    

    Register it as a multi-provider:

    { provide: CUSTOM_ERROR_HANDLERS, useExisting: PermissionErrorHandlerService, multi: true }
    

    For the calls where you pass skipHandleError: true and handle the error yourself, use the same check (this assumes the default JSON response — for a text/blob responseType the ABP envelope isn't parsed, so you'd parse it first):

    if (err.status === 401) {
      // not authenticated -> login
    } else if (err.status === 403 && !err.error?.error) {
      // no permission
    } else {
      // business/other error -> err.error?.error?.message
    }
    

    A few notes for your topology:

    • Host API vs module APIs behave identically — same ABP exception pipeline on both.
    • This holds for an API host whose forbid/challenge scheme is Bearer/JWT. If a host is set up with a cookie scheme instead, an authorization failure redirects to the login/access-denied page rather than returning 401/403, and custom JwtBearerEvents (or a custom IAbpAuthorizationExceptionHandler) can change the shape too.
    • Through the gateway it's the same as long as the gateway just forwards the response. Note the gateway/proxy can also produce its own 401/403 that isn't from an ABP permission check — those land in the "403 without ABP body" bucket as well, so treat that bucket as "not authorized" generically rather than "definitely a missing permission".
    • Since your Angular app is on a different origin, reading the _AbpErrorFormat header directly needs it exposed via CORS (the ABP template already does this). You don't need the header though — checking whether error.error exists is enough.

    Thanks

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
Boost Your Development
ABP Live Training
Packages
See Trainings
Mastering ABP Framework Book
The Official Guide
Mastering
ABP Framework
Learn More
Mastering ABP Framework Book
Made with ❤️ on ABP v10.8.0-preview. Updated on September 24, 2026, 12:09
1
ABP Assistant
🔐 You need to be logged in to use the chatbot. Please log in first.