# TokenLimitHandler

- File ID: file:38gozs:tokenlimithandler-cs
- Path: NexusReader.Infrastructure/Identity/TokenLimitHandler.cs
- Role: middleware
- Layer: infrastructure

## Purpose

Authorization handler that enforces per-user AI token limits by loading the user and their subscription from the DB and succeeding the requirement when tokens remain or the plan is unlimited.

## Key Details

Handler uses an IDbContextFactory to create a DbContext, loads the user including SubscriptionPlan, and succeeds the requirement when tokens remain or plan is unlimited; it returns early when no user id or no user record is found. UserManager is injected but not used here.

## Symbols

- TokenLimitHandler: Authorization handler that enforces per-user AI token limits by loading the user and their subscription from the DB and succeeding the requirement when tokens remain or the plan is unlimited.
- TokenLimitHandler(): Ctor: receives and stores dependencies (DB context factory and UserManager).
- HandleRequirementAsync(): Authorization handler entrypoint: load current user (including subscription) and succeed the requirement if the user has remaining AI tokens or an unlimited plan.

## Dependencies

- imports NexusReader.Domain/Entities/NexusUser.cs
- imports NexusReader.Domain/Entities/SubscriptionPlan.cs

## Calls

- No resolved calls.

## Unresolved References

- dependency-target: NexusReader.Data.Persistence/AppDbContext.cs
- interaction-target: var userId = context.User.FindFirstValue(ClaimTypes.NameIdentifier);
- interaction-target: context.Succeed(requirement);

## Coverage

- Symbols: 3
- Executable symbols: 2
- Executable symbols with control flow: 2
- Grounded edges: 5
- Unresolved references: 3
- Control-flow fragments: 2
- Unresolved jumps: 0
- Unsupported constructs: 0
- Control-flow coverage: 100%
- Required diagrams: 2
- Generated diagrams: 3

## Control Flow Fragments

- TokenLimitHandler: 4 nodes, 3 edges, 0 jumps, 0 unsupported constructs
- HandleRequirementAsync: 35 nodes, 40 edges, 0 jumps, 0 unsupported constructs

## Diagrams

### TokenLimitHandler Neighborhood

```mermaid
flowchart LR
    class_dceqku_tokenlimithandler("TokenLimitHandler")
    file_38gozs_tokenlimithandler_cs("TokenLimitHandler")
    method_13vwa6r_handlerequirementasync[["HandleRequirementAsync()"]]
    method_1xc3hml_tokenlimithandler[["TokenLimitHandler()"]]
    file_1b03oou_subscriptionplan_cs[/"SubscriptionPlan"/]
    file_xcxh5w_nexususer_cs[/"NexusUser"/]
    file_38gozs_tokenlimithandler_cs -->|SubscriptionPlan| file_1b03oou_subscriptionplan_cs
    file_38gozs_tokenlimithandler_cs -->|NexusUser| file_xcxh5w_nexususer_cs
```

### TokenLimitHandler Execution Overview

```mermaid
flowchart TD
    class_dceqku_tokenlimithandler("TokenLimitHandler")
    method_13vwa6r_handlerequirementasync[["HandleRequirementAsync()"]]
    method_1xc3hml_tokenlimithandler[["TokenLimitHandler()"]]
    unresolved_unresolved_1g088fj_interaction_target{"Unresolved: var userId = context.User.FindFirstValue(ClaimTypes.NameIdentifier);"}
    unresolved_unresolved_vltt1w_interaction_target{"Unresolved: context.Succeed(requirement);"}
    class_dceqku_tokenlimithandler -->|contains| method_13vwa6r_handlerequirementasync
    class_dceqku_tokenlimithandler -->|contains| method_1xc3hml_tokenlimithandler
    class_dceqku_tokenlimithandler -->|unresolved interaction| unresolved_unresolved_1g088fj_interaction_target
    class_dceqku_tokenlimithandler -->|unresolved interaction| unresolved_unresolved_vltt1w_interaction_target
```

### HandleRequirementAsync() Control Flow

_Warnings: diagram method_13vwa6r_handlerequirementasync__fragment_diagram exceeds preferred complexity thresholds (35 nodes, 40 edges)._

```mermaid
---
config:
  flowchart:
    defaultRenderer: "elk"
---
flowchart TD
    method_13vwa6r_handlerequirementasync__action_28_external_resource_28__linear("Read user id from ClaimsPrincipal")
    method_13vwa6r_handlerequirementasync__action_29_guard_clause_29__condition{"userId == null"}
    method_13vwa6r_handlerequirementasync__action_29_guard_clause_29__false("otherwise")
    method_13vwa6r_handlerequirementasync__action_29_guard_clause_29__merge("merge")
    method_13vwa6r_handlerequirementasync__action_29_guard_clause_29__true("return (deny/stop)")
    method_13vwa6r_handlerequirementasync__action_29_handlerequirementasync_branch_29_1__condition{"userId == null"}
    method_13vwa6r_handlerequirementasync__action_29_handlerequirementasync_branch_29_1__false("false")
    method_13vwa6r_handlerequirementasync__action_29_handlerequirementasync_branch_29_1__merge("merge")
    method_13vwa6r_handlerequirementasync__action_29_handlerequirementasync_branch_29_1__true("true")
    method_13vwa6r_handlerequirementasync__action_34_resource_acquisition_34__linear("Create DB context via factory (scoped, disposed via using)")
    method_13vwa6r_handlerequirementasync__action_35_db_read_35__linear("Load user including SubscriptionPlan")
    method_13vwa6r_handlerequirementasync__action_35_handlerequirementasync_await_35_3__await[["Waits for async work"]]
    method_13vwa6r_handlerequirementasync__action_39_guard_clause_39__condition{"user == null"}
    method_13vwa6r_handlerequirementasync__action_39_guard_clause_39__false("otherwise")
    method_13vwa6r_handlerequirementasync__action_39_guard_clause_39__merge("merge")
    method_13vwa6r_handlerequirementasync__action_39_guard_clause_39__true("return (deny/stop)")
    method_13vwa6r_handlerequirementasync__action_39_handlerequirementasync_branch_39_4__condition{"user == null"}
    method_13vwa6r_handlerequirementasync__action_39_handlerequirementasync_branch_39_4__false("false")
    method_13vwa6r_handlerequirementasync__action_39_handlerequirementasync_branch_39_4__merge("merge")
    method_13vwa6r_handlerequirementasync__action_39_handlerequirementasync_branch_39_4__true("true")
    method_13vwa6r_handlerequirementasync__action_44_branch_44__condition{"user.SubscriptionPlan?.IsUnlimitedTokens == true || user.AITokensUsed < user.AITokenLimit"}
    method_13vwa6r_handlerequirementasync__action_44_branch_44__false("no action (authorization not granted here)")
    method_13vwa6r_handlerequirementasync__action_44_branch_44__merge("merge")
    method_13vwa6r_handlerequirementasync__action_44_branch_44__true("succeed requirement")
    method_13vwa6r_handlerequirementasync__action_45_handlerequirementasync_branch_45_6__condition{"user.SubscriptionPlan?.IsUnlimitedTokens == true || user.AITokensUsed < user.AITokenLimit"}
    method_13vwa6r_handlerequirementasync__action_45_handlerequirementasync_branch_45_6__false("false")
    method_13vwa6r_handlerequirementasync__action_45_handlerequirementasync_branch_45_6__merge("merge")
    method_13vwa6r_handlerequirementasync__action_45_handlerequirementasync_branch_45_6__true("true")
    method_13vwa6r_handlerequirementasync__action_47_state_change_47__linear("Succeed authorization requirement")
    method_13vwa6r_handlerequirementasync__entry(["HandleRequirementAsync entry"])
    method_13vwa6r_handlerequirementasync__action_28_handlerequirementasync_repository_read_28_0__linear[("Reads repository or persistence state")]
    method_13vwa6r_handlerequirementasync__action_47_handlerequirementasync_repository_read_47_7__linear[("Reads repository or persistence state")]
    method_13vwa6r_handlerequirementasync__action_31_handlerequirementasync_return_31_2__return(["Returns result"])
    method_13vwa6r_handlerequirementasync__action_41_handlerequirementasync_return_41_5__return(["Returns result"])
    method_13vwa6r_handlerequirementasync__implicit_return(["return"])
    method_13vwa6r_handlerequirementasync__action_28_external_resource_28__linear -->|next| method_13vwa6r_handlerequirementasync__action_29_guard_clause_29__condition
    method_13vwa6r_handlerequirementasync__action_28_handlerequirementasync_repository_read_28_0__linear -->|next| method_13vwa6r_handlerequirementasync__action_28_external_resource_28__linear
    method_13vwa6r_handlerequirementasync__action_29_guard_clause_29__condition -->|otherwise| method_13vwa6r_handlerequirementasync__action_29_guard_clause_29__false
    method_13vwa6r_handlerequirementasync__action_29_guard_clause_29__condition -->|return deny / stop| method_13vwa6r_handlerequirementasync__action_29_guard_clause_29__true
    method_13vwa6r_handlerequirementasync__action_29_guard_clause_29__false -->|next| method_13vwa6r_handlerequirementasync__action_29_guard_clause_29__merge
    method_13vwa6r_handlerequirementasync__action_29_guard_clause_29__merge -->|next| method_13vwa6r_handlerequirementasync__action_29_handlerequirementasync_branch_29_1__condition
    method_13vwa6r_handlerequirementasync__action_29_guard_clause_29__true -->|next| method_13vwa6r_handlerequirementasync__action_29_guard_clause_29__merge
    method_13vwa6r_handlerequirementasync__action_29_handlerequirementasync_branch_29_1__condition -->|false| method_13vwa6r_handlerequirementasync__action_29_handlerequirementasync_branch_29_1__false
    method_13vwa6r_handlerequirementasync__action_29_handlerequirementasync_branch_29_1__condition -->|true| method_13vwa6r_handlerequirementasync__action_29_handlerequirementasync_branch_29_1__true
    method_13vwa6r_handlerequirementasync__action_29_handlerequirementasync_branch_29_1__false -->|next| method_13vwa6r_handlerequirementasync__action_29_handlerequirementasync_branch_29_1__merge
    method_13vwa6r_handlerequirementasync__action_29_handlerequirementasync_branch_29_1__merge -->|return| method_13vwa6r_handlerequirementasync__action_31_handlerequirementasync_return_31_2__return
    method_13vwa6r_handlerequirementasync__action_29_handlerequirementasync_branch_29_1__true -->|next| method_13vwa6r_handlerequirementasync__action_29_handlerequirementasync_branch_29_1__merge
    method_13vwa6r_handlerequirementasync__action_31_handlerequirementasync_return_31_2__return -->|next| method_13vwa6r_handlerequirementasync__action_34_resource_acquisition_34__linear
    method_13vwa6r_handlerequirementasync__action_34_resource_acquisition_34__linear -->|next| method_13vwa6r_handlerequirementasync__action_35_handlerequirementasync_await_35_3__await
    method_13vwa6r_handlerequirementasync__action_35_db_read_35__linear -->|next| method_13vwa6r_handlerequirementasync__action_39_guard_clause_39__condition
    method_13vwa6r_handlerequirementasync__action_35_handlerequirementasync_await_35_3__await -->|next| method_13vwa6r_handlerequirementasync__action_35_db_read_35__linear
    method_13vwa6r_handlerequirementasync__action_39_guard_clause_39__condition -->|otherwise| method_13vwa6r_handlerequirementasync__action_39_guard_clause_39__false
    method_13vwa6r_handlerequirementasync__action_39_guard_clause_39__condition -->|return deny / stop| method_13vwa6r_handlerequirementasync__action_39_guard_clause_39__true
    method_13vwa6r_handlerequirementasync__action_39_guard_clause_39__false -->|next| method_13vwa6r_handlerequirementasync__action_39_guard_clause_39__merge
    method_13vwa6r_handlerequirementasync__action_39_guard_clause_39__merge -->|next| method_13vwa6r_handlerequirementasync__action_39_handlerequirementasync_branch_39_4__condition
    method_13vwa6r_handlerequirementasync__action_39_guard_clause_39__true -->|next| method_13vwa6r_handlerequirementasync__action_39_guard_clause_39__merge
    method_13vwa6r_handlerequirementasync__action_39_handlerequirementasync_branch_39_4__condition -->|false| method_13vwa6r_handlerequirementasync__action_39_handlerequirementasync_branch_39_4__false
    method_13vwa6r_handlerequirementasync__action_39_handlerequirementasync_branch_39_4__condition -->|true| method_13vwa6r_handlerequirementasync__action_39_handlerequirementasync_branch_39_4__true
    method_13vwa6r_handlerequirementasync__action_39_handlerequirementasync_branch_39_4__false -->|next| method_13vwa6r_handlerequirementasync__action_39_handlerequirementasync_branch_39_4__merge
    method_13vwa6r_handlerequirementasync__action_39_handlerequirementasync_branch_39_4__merge -->|return| method_13vwa6r_handlerequirementasync__action_41_handlerequirementasync_return_41_5__return
    method_13vwa6r_handlerequirementasync__action_39_handlerequirementasync_branch_39_4__true -->|next| method_13vwa6r_handlerequirementasync__action_39_handlerequirementasync_branch_39_4__merge
    method_13vwa6r_handlerequirementasync__action_41_handlerequirementasync_return_41_5__return -->|next| method_13vwa6r_handlerequirementasync__action_44_branch_44__condition
    method_13vwa6r_handlerequirementasync__action_44_branch_44__condition -->|no action authorization not granted here| method_13vwa6r_handlerequirementasync__action_44_branch_44__false
    method_13vwa6r_handlerequirementasync__action_44_branch_44__condition -->|succeed requirement| method_13vwa6r_handlerequirementasync__action_44_branch_44__true
    method_13vwa6r_handlerequirementasync__action_44_branch_44__false -->|next| method_13vwa6r_handlerequirementasync__action_44_branch_44__merge
    method_13vwa6r_handlerequirementasync__action_44_branch_44__merge -->|next| method_13vwa6r_handlerequirementasync__action_45_handlerequirementasync_branch_45_6__condition
    method_13vwa6r_handlerequirementasync__action_44_branch_44__true -->|next| method_13vwa6r_handlerequirementasync__action_44_branch_44__merge
    method_13vwa6r_handlerequirementasync__action_45_handlerequirementasync_branch_45_6__condition -->|false| method_13vwa6r_handlerequirementasync__action_45_handlerequirementasync_branch_45_6__false
    method_13vwa6r_handlerequirementasync__action_45_handlerequirementasync_branch_45_6__condition -->|true| method_13vwa6r_handlerequirementasync__action_45_handlerequirementasync_branch_45_6__true
    method_13vwa6r_handlerequirementasync__action_45_handlerequirementasync_branch_45_6__false -->|next| method_13vwa6r_handlerequirementasync__action_45_handlerequirementasync_branch_45_6__merge
    method_13vwa6r_handlerequirementasync__action_45_handlerequirementasync_branch_45_6__merge -->|next| method_13vwa6r_handlerequirementasync__action_47_state_change_47__linear
    method_13vwa6r_handlerequirementasync__action_45_handlerequirementasync_branch_45_6__true -->|next| method_13vwa6r_handlerequirementasync__action_45_handlerequirementasync_branch_45_6__merge
    method_13vwa6r_handlerequirementasync__action_47_handlerequirementasync_repository_read_47_7__linear -->|return| method_13vwa6r_handlerequirementasync__implicit_return
    method_13vwa6r_handlerequirementasync__action_47_state_change_47__linear -->|next| method_13vwa6r_handlerequirementasync__action_47_handlerequirementasync_repository_read_47_7__linear
    method_13vwa6r_handlerequirementasync__entry -->|next| method_13vwa6r_handlerequirementasync__action_28_handlerequirementasync_repository_read_28_0__linear
```

## Diagram Validation

- Status: warn
- Warning: control-flow: diagram method_13vwa6r_handlerequirementasync__fragment_diagram exceeds preferred complexity thresholds (35 nodes, 40 edges).
