---
title: SAIF.Web.API security and authorization
summary: Authorization requirements, AD resource groups, security enforcement points, and data-level access control.
status: draft
owner: andmil
last_reviewed: 2026-09-15
library: SAIF.Web.API
reference_type: package
---

# SAIF.Web.API security and authorization

This page documents authorization mechanisms implemented or consumed by `SAIF.Web.API`. Authentication is handled entirely by infrastructure (Windows/AD integrated authentication, inferred from the `WindowsPrincipal` cast in `ApiResponseHandler`)* and is not application-level — the library only reads an already-established identity. `SAIF.Web.API` is a **consumer-side, fully delegated** authorization library: it supplies the enforcement mechanism (a Web API `AuthorizeAttribute` subclass and an HTTPS-enforcing action filter), while the enforcement policy (which resource names guard which endpoints) and the enforcement decision (whether a user holds a resource) are both assumed to live outside this repository — no such policy or decision logic exists inside this library.*

## Authorization architecture

Authorization is centralized and delegated: `SaifAuthorizeAttribute` resolves `ISecurityApiClient` and `IEnvironmentService` per request from the Web API dependency scope and delegates the actual per-role decision to `ISecurityApiClient.IsAuthorized(user, role)`. This library defines the *enforcement point*, not the decision logic — the concrete authorization mechanism (WCF `SecurityService` facade, HTTP API, or another mechanism) is implemented in the `SAIF.Framework.Core` package and could not be determined from this repository.**

```mermaid
flowchart TD
    Caller["Calling code / decorated<br>action"] --> Attr["SaifAuthorizeAttribute.<br>IsAuthorized"]
    Attr --> Scope["Web API IDependencyScope"]
    Scope --> Sec["ISecurityApiClient<br>(SAIF.Framework.Core)"]
    Scope --> Env["IEnvironmentService<br>(SAIF.Framework.Core)"]
    Sec --> Decision{"IsAuthorized(user, role)<br>for any declared role"}
    Decision -->|true| Allow[Request proceeds]
    Decision -->|false| Deny["HandleUnauthorized<br>Request<br>401 + SecurityException<br>log"]
```

## AD resource catalog

**No AD group or resource names are defined, referenced, or hard-coded anywhere in this repository** — resource/role names are supplied entirely by the consuming application as `params string[] roles` passed to the `SaifAuthorizeAttribute` constructor, e.g. `[SaifAuthorizeAttribute(roles)]`; no such application exists inside this library.*

| Resource name | Purpose | Enforcement point | Source |
| --- | --- | --- | --- |
| *(none — supplied by consumer)* | Consumer-supplied resource/role names passed to the attribute constructor, e.g. `[SaifAuthorize("SecureClaim")]` | `SaifAuthorizeAttribute` constructor → `AuthorizeAttribute.Roles` → `ISecurityApiClient.IsAuthorized(user, role)` | `src/SAIF.Web.API/SaifAuthorizeAttribute.cs` |

## Authorization enforcement points

### Attribute-based authorization

| Controller / Action | Attribute | Resource name(s) | Source |
| --- | --- | --- | --- |
| Consumer-decorated controller/action (none exist inside this library) | `[SaifAuthorize]` (`SaifAuthorizeAttribute`, sealed, derives `System.Web.Http.AuthorizeAttribute`) | Consumer-supplied via `params string[] roles` | `src/SAIF.Web.API/SaifAuthorizeAttribute.cs` |
| Consumer-decorated controller/action (none exist inside this library) | `[RequireSSL]` (`RequireSSLAttribute`, derives `System.Web.Http.Filters.ActionFilterAttribute`) | N/A (transport security, not resource-based) | `src/SAIF.Web.API/RequireSSLAttribute.cs` |

Neither attribute is applied anywhere inside this repository — there are no controllers or actions here. Both are library-provided primitives intended for consuming Web API applications to decorate their own controllers/actions with, though no such usage is observed in this repository.*

### Programmatic authorization checks

| Class | Method | Resource / Role | Check type | Source |
| --- | --- | --- | --- | --- |
| `SaifAuthorizeAttribute` | `IsAuthorized(HttpActionContext)` | Caller-supplied roles (OR semantics) | `ISecurityApiClient.IsAuthorized(user, role)` — see [BR-WEBAPI-017](business-rules.md#br-webapi-017-role-evaluation-uses-or-semantics) | `src/SAIF.Web.API/SaifAuthorizeAttribute.cs` |

No other programmatic authorization checks exist — no `IsInRole`, `HasAccess`, `CheckAccess`, or `CheckClaim` calls occur outside `SaifAuthorizeAttribute`, and no `ISecurityManager`/`clsSecurityPrincipal`-style types are defined or referenced anywhere in this repository.

## Data-level authorization

**None detected.** No entity-ownership or data-scope validation exists — no `CheckValidContext`, `IsValidPolicyForCurrentUser`, `ValidateOwnership`, or equivalent methods. This library performs no database access of any kind, so no SQL-based ownership checks are possible. The entire authorization surface is coarse-grained, role/resource-name based, and evaluated per action; data-level scoping is the responsibility of consuming applications.*

## Segregation of duties

**None detected.** No `*SegregationOfDuties*`/`*SOD*` types and no mutually-exclusive-privilege rules exist anywhere in the repository.

## Security interfaces and contracts

| Interface / Class | Role | Consumers | Source |
| --- | --- | --- | --- |
| `ISecurityApiClient` (`SAIF.Core.Services.UserProfile`) | Authorization decision provider: `IsAuthorized(string user, string role)` | `SaifAuthorizeAttribute` | `SAIF.Framework.Core` NuGet package (consumed by `src/SAIF.Web.API/SaifAuthorizeAttribute.cs`) |
| `IEnvironmentService` (`SAIF.Core.Services`) | Current-user identity (`User.Identity.Name`, `UserId()`) and configuration access | `ApiResponseHandler`, `SaifAuthorizeAttribute` | `SAIF.Framework.Core` NuGet package |
| `ILogService` / `ILogServiceForApi` (`SAIF.Core.Services`) | Logging of authorization denials and API exceptions | `ApiResponseHandler`, `SaifAuthorizeAttribute` | `SAIF.Framework.Core` NuGet package |

All three are resolved through ASP.NET Web API's per-request `IDependencyScope` service locator (`request.GetDependencyScope().GetService(typeof(T))`), never by constructor injection. If the hosting application does not register `ISecurityApiClient` and `IEnvironmentService` with its `IDependencyResolver`, `SaifAuthorizeAttribute.IsAuthorized` throws `MissingDependencyException` rather than denying access — the failure surfaces as a server error, not a 401 (see [BR-WEBAPI-014](business-rules.md#br-webapi-014-authorization-fails-closed-on-missing-dependencies)); no hosting application or registration exists in this repository to confirm this end-to-end.*

## Transport security

`RequireSSLAttribute` is a transport-security control, not an authorization control, but is cataloged here as a security enforcement point. It redirects a non-HTTPS request to HTTPS on port 443 unless the request is local and the host's `RequireSSLForLocal` app setting is absent or parses to `false` (see [BR-WEBAPI-012](business-rules.md#br-webapi-012-https-required-unless-local-exemption-applies) and [BR-WEBAPI-013](business-rules.md#br-webapi-013-redirect-non-https-requests-to-https)). The redirect hard-codes port 443 and uses `HttpContext.Current.Response.Redirect`, coupling this filter to `System.Web` (IIS/ASP.NET) hosting.

## Security observations

The following observations are not business rules but are notable for downstream review:

1. **401 vs. 403 semantics disagree across paths.** An authenticated-but-unauthorized user denied by `SaifAuthorizeAttribute` receives `401 Unauthorized` ([BR-WEBAPI-018](business-rules.md#br-webapi-018-unauthorized-requests-are-logged-and-answered-with-401)), while `ExceptionHandler.ToHttpError` maps `SAIF.API.Exceptions.UnauthorizedAccessException` to `403 Forbidden` ([BR-WEBAPI-011](business-rules.md#br-webapi-011-saif-exception-types-map-to-fixed-http-status-codes)) — the conventional code for an authorization denial. The two paths disagree.
2. **User name echoed in the 401 body.** The denial response message `The user '{user}' is not authorized.` discloses the resolved account name to the caller.
3. **Redaction scope is limited.** The password-redaction regex in `BuildTraceMessage` ([BR-WEBAPI-007](business-rules.md#br-webapi-007-redact-password-material-from-debug-traces)) covers only the input-stream copy; request/response bodies and headers (including any `Authorization` header) are written to the debug trace un-redacted.
4. **Error-detail exposure.** `GetErrorResponse` prefers `error.ExceptionMessage`, so with `IncludeErrorDetailPolicy.Always` (which [BR-WEBAPI-010](business-rules.md#br-webapi-010-developer-guidance-text-when-stack-trace-is-unavailable)'s guidance text actively encourages) raw exception messages reach the client.
5. **Base identity check bypassed.** `SaifAuthorizeAttribute` never calls `base.IsAuthorized(actionContext)`, so `AuthorizeAttribute`'s built-in `Identity.IsAuthenticated` check and `Users` property are bypassed entirely; an attribute applied with no roles authorizes every request, including unauthenticated ones (see [BR-WEBAPI-015](business-rules.md#br-webapi-015-no-roles-specified-authorizes-unconditionally)).*
6. **`forceProductionRole` is dead code.** The `SaifAuthorizeAttribute(bool forceProductionRole, params string[] roles)` constructor stores the flag in `_ForceProductionRole`, which is never read anywhere in the repository; the two-argument overload behaves identically to the one-argument overload.
7. **Identity source diverges from the request principal.** Authorization is evaluated against `IEnvironmentService.User.Identity.Name`, not `actionContext.RequestContext.Principal` — whether the host guarantees these are the same principal is unknown from this repository.**

## Notes
- Mark inferred statements with `*` and unknown statements with `**`. Do not use bold inline confidence labels.

> **Note:** Statements marked with * were inferred from code structure, naming conventions, or
> contextual evidence where direct confirmation was not available in source artifacts. Statements
> marked with ** could not be confirmed; treat as unknown until verified.

## Related documentation
- [Index](index.md)
- [Overview](overview.md)
- [Architecture](architecture.md)
- [Dependencies](dependencies.md)
- [Configuration](configuration.md)
- [Operations and processes](operations-processes.md)
- [Business rules](business-rules.md)
- [API reference](api/index.md)

## Sources
- `src/SAIF.Web.API/SaifAuthorizeAttribute.cs`
- `src/SAIF.Web.API/RequireSSLAttribute.cs`
- `src/SAIF.Web.API/ExceptionHandler.cs`
- `src/SAIF.Web.API/ApiResponseHandler.cs`
- `src/SAIF.Web.API/HttpRequestExtensions.cs`
- `src/SAIF.Web.API/app.config`
- `src/SAIF.Web.API/SAIF.Web.API.csproj`
- `src/SAIF.Web.API.Core/SAIFException.cs`
