Skip to content

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.**

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 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); 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 and BR-WEBAPI-013). 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), while ExceptionHandler.ToHttpError maps SAIF.API.Exceptions.UnauthorizedAccessException to 403 Forbidden (BR-WEBAPI-011) — 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) 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'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).*
  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.

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