---
title: Evaluate a role requirement
summary: The self-handling authorization requirement that decides whether a single role succeeds for the current request.
status: draft
owner: SAIF Platform Team
last_reviewed: 2026-10-02
library: SAIF.NetCore.NtlmApiAuth
reference_type: package
operation_id: OP-06
trigger: ASP.NET Core authorization evaluation for a policy built by OP-02
---

# Evaluate a role requirement

Evaluates one `RoleRequirement` by extracting both candidate identities from the current request and delegating the actual authorization decision to `NtlmApiAuthorizationService`.

| Field | Value |
| --- | --- |
| **Operation ID** | OP-06 |
| **Trigger** | ASP.NET Core authorization for an endpoint whose policy came from [Build named role policies](build-named-role-policies.md), invoked via the framework `PassThroughAuthorizationHandler` |
| **Inputs** | `AuthorizationHandlerContext`, `RoleRequirement` (self) |
| **Outputs** | `context.Succeed(requirement)` or `context.Fail()` |
| **Primary components** | `RoleRequirement.HandleRequirementAsync` |
| **Entry point** | `src/SAIF.NetCore.NtlmApiAuth/src/SAIF.NetCore.NtlmApiAuth/Authorization/RoleRequirement.cs` |

## Steps

1. Compute `requestUser = context.User.Identity.Name` with the domain removed via `DomainlessUser` (null-safe). ([BR-NTLM-004](../business-rules.md#br-ntlm-004-blank-request-identity-is-never-authorized), [BR-NTLM-011](../business-rules.md#br-ntlm-011-user-name-normalization))
2. Compute `onBehalfOfUser = context.OnBehalfOfUser()` — the first `OnBehalfOfUser` claim value, or `string.Empty` if absent. ([BR-NTLM-001](../business-rules.md#br-ntlm-001-onbehalfofuser-header-and-claim))
3. Delegate to [Dual-identity authorization](dual-identity-authorization.md) — `INtlmApiAuthorizationService.IsAuthorized(requestUser, onBehalfOfUser, _role)` — using **this instance's own `_role`**, which is not necessarily the role carried by the `requirement` argument (because `RoleRequirement` is a self-handling requirement whose handler cross-evaluates every requirement in the context). The delegated logic, embedded here for reference:
   - If `requestUser` is blank, the call returns `false` immediately with no remote call.
   - Otherwise it checks `requestUser` against `_role` via the embedded AuthService call; if that check fails, the overall result is `false` and `onBehalfOfUser` is never evaluated.
   - If the caller check succeeds and `onBehalfOfUser` is blank, the result is the caller check alone (`true`).
   - If the caller check succeeds and `onBehalfOfUser` is present, a second check runs `onBehalfOfUser` against `_role`, and **both** identities must pass for the overall result to be `true`.
   - See [Dual-identity authorization](dual-identity-authorization.md) for the full embedded AuthService rewrite/error/wire-format behavior behind each check.

   > **Origin**: Step 3 is implemented in [Dual-identity authorization](dual-identity-authorization.md) (`SAIF.NetCore.NtlmApiAuth`) and executed on behalf of this operation.
4. If the result is `true`, call `context.Succeed(requirement)`; otherwise call `context.Fail()`, which fails the entire authorization regardless of any other requirement's success. ([BR-NTLM-007](../business-rules.md#br-ntlm-007-multi-role-policies-are-and))
   - On failure (exception): a `SecurityClientException` raised deep inside the delegated AuthService call (see step 3) propagates out of this handler uncaught — the resulting HTTP response is a function of the host's own error handling and is inferred to be a server error rather than an independently-verified 403.* ([BR-NTLM-008](../business-rules.md#br-ntlm-008-securityapi-failures-fail-closed-via-exception))

## Process flow

```mermaid
flowchart TD
	Start([Authorization evaluation]) --> S1[Compute domainless request user]
	S1 --> S2[Read OnBehalfOfUser claim]
	S2 --> S3[Call NtlmApiAuthorizationService.IsAuthorized]
	S3 --> D{Result}
	D -->|true| Succeed([context.Succeed])
	D -->|false| Fail([context.Fail])
	D -->|exception| Err(["Exception propagates to the caller,<br>uncaught by this handler"])
```

## Failure and edge-case behavior
- With N roles in a single policy, each `RoleRequirement`'s self-handling behavior evaluates its own role against every requirement in the context, so SecurityAPI can be called up to 2·N² times per request*.

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

> **Note:** Statements marked with `*` were inferred from code structure, naming
> conventions, or contextual evidence where direct confirmation was not available
> in source artifacts.

## Sources
- `src/SAIF.NetCore.NtlmApiAuth/src/SAIF.NetCore.NtlmApiAuth/Authorization/RoleRequirement.cs`
