Add method-level authorization via [NexusAuthorize<TPermission>]#65
Open
Add method-level authorization via [NexusAuthorize<TPermission>]#65
Conversation
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds
[NexusAuthorize<TPermission>]attribute support for method-level authorization on server nexuses. The source generator emits auth guards inInvokeMethodCorethat call a virtualOnAuthorizemethod before any argument deserialization or method execution. Users define a permission enum, decorate server nexus methods with[NexusAuthorize<TPermission>(...)], and overrideOnAuthorizefor custom authorization logic.Example:
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
AuthorizeResultenum,NexusAuthorizeAttribute<TPermission>,ProxyUnauthorizedExceptionServerNexusBase<TProxy>gainsOnAuthorizevirtual andCheckAuthorizationprotected methodInvocationResultMessage.StateType.Unauthorized(value 3), newMessageType.DisconnectUnauthorized(value 34), newDisconnectReason.Unauthorized[NexusAuthorize<>]from class methods, emits static permission arrays and auth guards, adds 3 new diagnostics (NEXNET024-026)ProxyInvocationBasenow handlesUnauthorizedstate by throwingProxyUnauthorizedExceptionMigration Steps
None required. This is a purely additive feature. Existing code continues to work without modification. To adopt:
[NexusAuthorize<TPermission>(...)]to server nexus class methodsOnAuthorizeon the server nexus classProxyUnauthorizedExceptionon the client side where neededPerformance Considerations
[NexusAuthorize]OnAuthorizereturningValueTask<AuthorizeResult>with a synchronous result allocates nothingint[]arrays are emitted asstatic readonlyclass fields, allocated onceSecurity Considerations
OnAuthorizethrows, the session is disconnected (logged as error). This prevents accidental authorization bypass from buggy auth logic[NexusAuthorize]is used butOnAuthorizeis not overridden (default allows all)Breaking changes
Public consumer-facing changes
None. All additions are backward-compatible:
AuthorizeResult,NexusAuthorizeAttribute<T>,ProxyUnauthorizedExceptionare new typesOnAuthorizevirtual has a default implementation returningAllowedDisconnectReason.Unauthorizedis a new enum member (underlying byte value 34)Internal non-consumer changes
InvocationResultMessage.StateTypegainsUnauthorized = 3— older clients receiving this from a newer server will hit thedefaultswitch case and throwInvalidOperationException. This only occurs if the feature is actively used against an older client.ProxyRemoteInvocationExceptiongains aprotectedconstructor accepting astring messageparameter (forProxyUnauthorizedExceptioninheritance). 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