Skip to content

Comments

Add method-level authorization via [NexusAuthorize<TPermission>]#65

Open
DJGosnell wants to merge 6 commits intomasterfrom
feature/method-authorization
Open

Add method-level authorization via [NexusAuthorize<TPermission>]#65
DJGosnell wants to merge 6 commits intomasterfrom
feature/method-authorization

Conversation

@DJGosnell
Copy link
Member

Summary

Adds [NexusAuthorize<TPermission>] attribute support for method-level authorization on server nexuses. The source generator emits auth guards in InvokeMethodCore that call a virtual OnAuthorize method before any argument deserialization or method execution. Users define a permission enum, decorate server nexus methods with [NexusAuthorize<TPermission>(...)], and override OnAuthorize for custom authorization logic.

Example:

[NexusAuthorize<Permission>(Permission.Admin)]
public override ValueTask AdminMethod() { ... }

protected override ValueTask<AuthorizeResult> OnAuthorize(
    ServerSessionContext<TProxy> context, int methodId,
    string methodName, ReadOnlyMemory<int> requiredPermissions)
{
    // Custom auth logic
}

Reason for the change

NexNet had no built-in authorization mechanism. Users needing per-method access control had to implement manual checks inside every method body, which is error-prone, repetitive, and happens after argument deserialization (wasted work for unauthorized calls). This feature provides a declarative, generator-driven approach with compile-time type safety.

Impacts of changes

  • New public types: AuthorizeResult enum, NexusAuthorizeAttribute<TPermission>, ProxyUnauthorizedException
  • Modified base class: ServerNexusBase<TProxy> gains OnAuthorize virtual and CheckAuthorization protected method
  • Wire protocol: New InvocationResultMessage.StateType.Unauthorized (value 3), new MessageType.DisconnectUnauthorized (value 34), new DisconnectReason.Unauthorized
  • Generator: Extracts [NexusAuthorize<>] from class methods, emits static permission arrays and auth guards, adds 3 new diagnostics (NEXNET024-026)
  • Proxy layer: ProxyInvocationBase now handles Unauthorized state by throwing ProxyUnauthorizedException

Migration Steps

None required. This is a purely additive feature. Existing code continues to work without modification. To adopt:

  1. Define a permission enum
  2. Add [NexusAuthorize<TPermission>(...)] to server nexus class methods
  3. Override OnAuthorize on the server nexus class
  4. Catch ProxyUnauthorizedException on the client side where needed

Performance Considerations

  • Zero cost for unannotated methods — no auth guard is emitted for methods without [NexusAuthorize]
  • Auth check before deserialization — unauthorized calls skip argument deserialization entirely
  • Synchronous fast-pathOnAuthorize returning ValueTask<AuthorizeResult> with a synchronous result allocates nothing
  • Static permission arraysint[] arrays are emitted as static readonly class fields, allocated once

Security Considerations

  • Fail-safe on exception: If OnAuthorize throws, the session is disconnected (logged as error). This prevents accidental authorization bypass from buggy auth logic
  • Server-side only: Authorization is enforced server-side; clients cannot bypass it. The attribute is a compile-time error on client nexuses (NEXNET024)
  • Per-invocation: Auth is checked on every invocation, not cached. After reconnection, auth is re-evaluated naturally
  • Compile-time warnings: NEXNET025 warns if [NexusAuthorize] is used but OnAuthorize is not overridden (default allows all)

Breaking changes

Public consumer-facing changes

None. All additions are backward-compatible:

  • AuthorizeResult, NexusAuthorizeAttribute<T>, ProxyUnauthorizedException are new types
  • OnAuthorize virtual has a default implementation returning Allowed
  • DisconnectReason.Unauthorized is a new enum member (underlying byte value 34)

Internal non-consumer changes

  • InvocationResultMessage.StateType gains Unauthorized = 3 — older clients receiving this from a newer server will hit the default switch case and throw InvalidOperationException. This only occurs if the feature is actively used against an older client.
  • ProxyRemoteInvocationException gains a protected constructor accepting a string message parameter (for ProxyUnauthorizedException inheritance). This is binary-compatible but a minor API surface addition.
  • MessageType.DisconnectUnauthorized = 34 — older peers receiving this will not recognize the disconnect reason, but will still disconnect cleanly as the transport closes.

🤖 Generated with Claude Code

DJGosnell and others added 6 commits February 20, 2026 20:57
Introduces AuthorizeResult enum, NexusAuthorizeAttribute<TPermission>,
ProxyUnauthorizedException, OnAuthorize virtual on ServerNexusBase,
Unauthorized state on InvocationResultMessage, and Unauthorized
disconnect reason. This is the runtime foundation for generator-emitted
auth guards.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Generator extracts [NexusAuthorize<TPermission>] from class methods,
correlates with interface methods, emits static permission arrays and
auth guards in InvokeMethodCore switch cases. Adds NEXNET024-026
diagnostics for client usage, missing OnAuthorize override, and mixed
permission enum types.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Generator tests verify diagnostics (NEXNET024-026) and successful code
generation for authorized, marker-only, and multi-permission methods.
Integration tests cover Allowed/Unauthorized/Disconnect flows, parameter
verification, return values, method body non-execution, and exception
handling across all 6 transport types (66 tests).

Refactored ServerNexusBase to expose a single protected
CheckAuthorization method that handles the full auth lifecycle
(authorize, send unauthorized result, disconnect) internally,
keeping generated code minimal.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The pinned libmsquic 2.4.8 was removed from Microsoft's Ubuntu 24.04
apt repository. Install latest available version instead and run
apt-get update first to ensure fresh package index.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Using [NexusAuthorize] without overriding OnAuthorize now fails
compilation, ensuring fail-closed authorization by default.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extend [NexusAuthorize<TPermission>] to work on collection properties
alongside methods. The auth guard is emitted before StartCollection in
InvokeMethodCore, so every collection connect/reconnect is authorized.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant