---
title: Handle an unauthorized request
summary: Log the denial (when the user is known) and answer the caller with a structured 401.
status: draft
owner: andmil
last_reviewed: 2026-09-15
library: SAIF.Web.API
reference_type: package
operation_id: OP-09
trigger: Invoked by AuthorizeAttribute when OP-08 (Authorize a request against SAIF roles) returns false.
---

# Handle an unauthorized request

`SaifAuthorizeAttribute.HandleUnauthorizedRequest(HttpActionContext)` logs the denial (when the user is known) and answers the caller with a structured 401.

| Field | Value |
| --- | --- |
| **Operation ID** | OP-09 |
| **Trigger** | Invoked by `AuthorizeAttribute` when [Authorize a request against SAIF roles](op-08-authorize-a-request-against-saif-roles.md) returns `false`. |
| **Inputs** | `HttpActionContext` |
| **Outputs** | sets `actionContext.Response` to 401 |
| **Primary components** | `SaifAuthorizeAttribute.HandleUnauthorizedRequest(HttpActionContext)` |
| **Entry point** | `src/SAIF.Web.API/SaifAuthorizeAttribute.cs` |

## Steps

1. `Enforce.ArgumentNotNull(actionContext, "actionContext")` ([BR-WEBAPI-014](../business-rules.md#br-webapi-014-authorization-fails-closed-on-missing-dependencies)).
   - `SAIF.Exceptions.Enforce` is implemented in `SAIF.Framework.Core`; its internal steps could not be embedded — the package is not indexed for this run.**
2. Resolve `ILogService` and `IEnvironmentService` from the request dependency scope; `Enforce.ArgumentNotNull` each ([BR-WEBAPI-014](../business-rules.md#br-webapi-014-authorization-fails-closed-on-missing-dependencies)).
   - Unlike [Post-process an API response](op-01-post-process-an-api-response.md), logging is **mandatory** here — a missing `ILogService` throws.
3. `user = environmentService.UserId()`.
   - `SAIF.Common.Extensions.UserId()` is implemented in `SAIF.Framework.Core`; its internal steps could not be embedded — the package is not indexed for this run.**
4. If `!user.IsNullOrEmpty()`, call `logService.LogException(applicationName, controllerAndActionName, state: GetContext(actionContext, environmentService), exception: new SecurityException($"The user '{user}' is not authorized for this view."))` ([BR-WEBAPI-018](../business-rules.md#br-webapi-018-unauthorized-requests-are-logged-and-answered-with-401)).
   - Deliberately skipped when the user is unknown — the source comment states "user may be null before credentials are demanded", i.e. the challenge round-trip is not logged as a security event.
5. `GetContext` formats the log state as `User: {userId}\nPage: {requestUri}` ([BR-WEBAPI-018](../business-rules.md#br-webapi-018-unauthorized-requests-are-logged-and-answered-with-401)).
6. Set `actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.Unauthorized, $"The user '{user}' is not authorized.")` ([BR-WEBAPI-018](../business-rules.md#br-webapi-018-unauthorized-requests-are-logged-and-answered-with-401)).
   - Returns **401 Unauthorized** (not 403), and echoes the resolved user name in the response body.

## Process flow

```mermaid
flowchart TD
	Start([OP-08 returned false]) --> NullCheck["Enforce.ArgumentNotNull<br>checks"]
	NullCheck --> UserId["user =<br>environmentService.<br>UserId("]
	UserId --> Known{user known?}
	Known -->|Yes| LogSec["LogException(<br>SecurityException)"]
	Known -->|No| Skip[Skip logging]
	LogSec --> Response["Set 401 response with<br>message"]
	Skip --> Response
```

## Failure and edge-case behavior
- A missing `ILogService` throws here (fail-closed), in contrast to the graceful degradation in [Post-process an API response](op-01-post-process-an-api-response.md).

> **Note:** Statements marked with ** could not be confirmed; treat as unknown until verified.

## Related documentation
- [Operations and processes](../operations-processes.md)
- [Business rules](../business-rules.md)

## Sources
- `src/SAIF.Web.API/SaifAuthorizeAttribute.cs`
