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!
2 Answer(s)
-
0
On the client, the ABP-conventional way is to detect authorization/permission failures via the ABP error payload’s
error.code, not by parsingmessagetext.- For ABP authorization failures (including missing permissions that result in
AbpAuthorizationException), ABP serializes the error as aRemoteServiceErrorResponsewhoseerror.codeis 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.”).
- For ABP authorization failures (including missing permissions that result in
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.codeto be reliable.
Is there a documented list of ABP authorization error codes?
- I can verify
Volo.Authorization:010001from 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:010001as the canonical permission/authorization failure code; no verified complete list is available from the tools for this specific query.
- I can verify
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.codevalues.
- Authorization:
- So: don’t branch on 403 alone; branch on
error.code.
- Yes, you can get the same HTTP status for different categories depending on middleware/filters, but the ABP way to distinguish is again
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):
- ABP Angular already has the “global HTTP error handling with opt-out (
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) -
0
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.codeto 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_AbpErrorFormatresponse 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 — anerror.errorobject (a message, usually acode) plus the_AbpErrorFormat: trueheader. ABusinessExceptionalso uses 403, which is exactly why the status alone can't separate it from a permission failure.
The reason for the difference: ABP handles
AbpAuthorizationExceptiondifferently from other exceptions. Instead of serializing a JSON error body, it hands off to the authentication scheme'sForbid/Challenge(403 when authenticated, 401 when not). So theVolo.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.403without an ABP error body → permission/authorization denied → show your "You don't have permission" message.- Any status with an ABP error body (
error.errorpresent) → business/validation/other error → showerror.error.message.
For Angular, the built-in extension point is a custom error handler (
CUSTOM_ERROR_HANDLERS), which runs before the default handling based onpriority: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: trueand handle the error yourself, use the same check (this assumes the default JSON response — for atext/blobresponseTypethe 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 customIAbpAuthorizationExceptionHandler) 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
_AbpErrorFormatheader directly needs it exposed via CORS (the ABP template already does this). You don't need the header though — checking whethererror.errorexists is enough.
Thanks
Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post) - Missing permission (authenticated): 403 with an empty body — no ABP payload, no