Secure AppHost RPC with session token handshake#15344
Conversation
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/dotnet/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 15344Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 15344" |
|
The transient CI rerun workflow requested reruns for the following jobs after analyzing the failed attempt.
|
|
The transient CI rerun workflow requested reruns for the following jobs after analyzing the failed attempt.
|
tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/Snapshots/transport.verified.ts
Show resolved
Hide resolved
There was a problem hiding this comment.
Pull request overview
Adds an authentication handshake to the RemoteHost JSON-RPC channel (using ASPIRE_REMOTE_APPHOST_TOKEN) and updates the CLI + generated SDK transports/tests to participate in that handshake, preventing unauthenticated RPC access to AppHost operations.
Changes:
- Introduce per-connection JSON-RPC authentication state on the RemoteHost server and gate protected RPC methods until authenticated.
- Update Aspire CLI flows to generate and pass an authentication token when launching/connecting to the AppHost server.
- Update generated SDK transports (TS/Go/Python/Java/Rust) plus snapshots/tests to authenticate during connect (or shortly after).
Reviewed changes
Copilot reviewed 36 out of 36 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/Aspire.Hosting.RemoteHost.Tests/JsonRpcAuthenticationTests.cs | Adds server/client tests validating auth gating and disconnect behavior on failed auth. |
| tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/Snapshots/transport.verified.ts | Updates TS transport snapshot to include authentication during connect. |
| tests/Aspire.Hosting.CodeGeneration.TypeScript.JsTests/tests/transport.test.ts | Updates JS/TS transport unit tests to expect authenticate during connect and to provide a test handler. |
| tests/Aspire.Hosting.CodeGeneration.Rust.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.rs | Updates Rust snapshot to authenticate when token env var is present. |
| tests/Aspire.Hosting.CodeGeneration.Rust.Tests/Snapshots/AtsGeneratedAspire.verified.rs | Updates Rust snapshot to authenticate when token env var is present. |
| tests/Aspire.Hosting.CodeGeneration.Python.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.py | Updates Python snapshot to authenticate when token env var is present. |
| tests/Aspire.Hosting.CodeGeneration.Python.Tests/Snapshots/AtsGeneratedAspire.verified.py | Updates Python snapshot to authenticate when token env var is present. |
| tests/Aspire.Hosting.CodeGeneration.Java.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.java | Updates Java snapshot to authenticate when token env var is present. |
| tests/Aspire.Hosting.CodeGeneration.Java.Tests/Snapshots/AtsGeneratedAspire.verified.java | Updates Java snapshot to authenticate when token env var is present. |
| tests/Aspire.Hosting.CodeGeneration.Go.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.go | Updates Go snapshot to authenticate when token env var is present. |
| tests/Aspire.Hosting.CodeGeneration.Go.Tests/Snapshots/AtsGeneratedAspire.verified.go | Updates Go snapshot to authenticate when token env var is present. |
| src/Shared/KnownConfigNames.cs | Adds KnownConfigNames.RemoteAppHostToken constant for ASPIRE_REMOTE_APPHOST_TOKEN. |
| src/Aspire.Hosting.RemoteHost/RemoteHostServer.cs | Adjusts DI lifetimes (scoped) and registers per-client auth state. |
| src/Aspire.Hosting.RemoteHost/RemoteAppHostService.cs | Adds authenticate RPC method and enforces auth for capability/cancellation calls. |
| src/Aspire.Hosting.RemoteHost/Language/LanguageService.cs | Requires authentication before language/scaffolding RPC methods. |
| src/Aspire.Hosting.RemoteHost/JsonRpcServer.cs | Resolves per-client scoped services per connection and registers multiple RPC targets. |
| src/Aspire.Hosting.RemoteHost/JsonRpcAuthenticationState.cs | New per-connection auth state + fixed-time token comparison and gatekeeper helper. |
| src/Aspire.Hosting.RemoteHost/CodeGeneration/CodeGenerationService.cs | Requires authentication for capabilities/codegen RPC methods. |
| src/Aspire.Hosting.RemoteHost/Aspire.Hosting.RemoteHost.csproj | Links KnownConfigNames.cs into RemoteHost project. |
| src/Aspire.Hosting.CodeGeneration.TypeScript/Resources/transport.ts | TS transport now authenticates during connect using ASPIRE_REMOTE_APPHOST_TOKEN. |
| src/Aspire.Hosting.CodeGeneration.Rust/Resources/transport.rs | Adds Rust authenticate client method. |
| src/Aspire.Hosting.CodeGeneration.Rust/AtsRustCodeGenerator.cs | Emits Rust connect helper that authenticates when token env var is present. |
| src/Aspire.Hosting.CodeGeneration.Python/Resources/transport.py | Adds Python authenticate client method. |
| src/Aspire.Hosting.CodeGeneration.Python/AtsPythonCodeGenerator.cs | Emits Python connect helper that authenticates when token env var is present. |
| src/Aspire.Hosting.CodeGeneration.Java/Resources/Transport.java | Adds Java authenticate client method. |
| src/Aspire.Hosting.CodeGeneration.Java/AtsJavaCodeGenerator.cs | Emits Java connect helper that authenticates when token env var is present. |
| src/Aspire.Hosting.CodeGeneration.Go/Resources/transport.go | Adds Go Authenticate client method. |
| src/Aspire.Hosting.CodeGeneration.Go/AtsGoCodeGenerator.cs | Emits Go connect helper that authenticates when token env var is present. |
| src/Aspire.Cli/Scaffolding/ScaffoldingService.cs | Generates an auth token and passes it to the AppHost server + RPC client during scaffolding. |
| src/Aspire.Cli/Projects/IAppHostRpcClient.cs | Updates RPC client factory API to require authentication token for ConnectAsync. |
| src/Aspire.Cli/Projects/GuestAppHostProject.cs | Generates/passes token for codegen/run/publish AppHost server sessions and guest env vars. |
| src/Aspire.Cli/Projects/AppHostServerSession.cs | Persists per-session auth token and uses it when creating RPC client. |
| src/Aspire.Cli/Projects/AppHostRpcClient.cs | Updates ConnectAsync to authenticate after connect and ensure cleanup on failure. |
| src/Aspire.Cli/Commands/Sdk/SdkGenerateCommand.cs | Generates/passes token when running server for SDK generation. |
| src/Aspire.Cli/Commands/Sdk/SdkDumpCommand.cs | Generates/passes token when running server for capability dump. |
| src/Aspire.Cli/Aspire.Cli.csproj | Links shared TokenGenerator.cs into CLI project. |
Comments suppressed due to low confidence (1)
src/Aspire.Hosting.RemoteHost/JsonRpcServer.cs:212
- JsonRpcServer calls jsonRpc.StartListening() before wiring the JsonRpc instance into RemoteAppHostService via SetClientConnection(). A client can potentially send an
authenticaterequest immediately after StartListening, and the handler may run before _clientRpc is set, meaning failed authentication won’t actually close the connection as intended. Set the client connection before StartListening (or otherwise ensure the service has the JsonRpc reference before any requests are dispatched).
using var jsonRpc = new JsonRpc(handler, clientService);
// Add the shared CodeGenerationService as an additional target for generateCode method
jsonRpc.AddLocalRpcTarget(codeGenerationService);
// Add the shared LanguageService as an additional target for language support methods
jsonRpc.AddLocalRpcTarget(languageService);
jsonRpc.StartListening();
// Enable bidirectional communication - allow .NET to call back to TypeScript
clientService.SetClientConnection(jsonRpc);
_logger.LogDebug("JsonRpc connection established for client {ClientId} (bidirectional)", clientId);
|
Re-running the failed jobs in the CI workflow for this pull request because 3 jobs were identified as retry-safe transient failures in the CI run attempt.
|
JamesNK
left a comment
There was a problem hiding this comment.
Security design looks solid overall — FixedTimeEquals for comparison, scoped auth state, connection teardown on failed auth, and token generation with sufficient entropy. The main concerns are around code quality (indentation), cross-language consistency (Java named vs positional params), and API design (dictionary mutation side effect).
| Assert.Equal(session.AuthenticationToken, project.ReceivedEnvironmentVariables[KnownConfigNames.RemoteAppHostToken]); | ||
| } | ||
|
|
||
| private sealed class RecordingAppHostServerProject : IAppHostServerProject |
There was a problem hiding this comment.
nit: I hate private test implementations. Can it be a shared TestAppHostServiceProject in the TestServices directory?
There was a problem hiding this comment.
I need to update AGENTS.md to tell it to stop doing this.
|
@sebastienros do we understand why the failures are happening here? |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sync the AppHost server project contract with current release/13.2 and make the RemoteHost authentication test tolerate the expected disconnect race after failed auth. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
fa99c8e to
10ae547
Compare
|
🎬 CLI E2E Test Recordings — 52 recordings uploaded (commit View recordings
📹 Recordings uploaded automatically from CI run #23277104122 |
* Secure AppHost RPC with session token handshake Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix TypeScript RPC authentication request Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address AppHost RPC auth review feedback Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Update SDKs with token checks * Move scoped registrations to correct location * Consolidate temporary AppHost sessions Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address PR review feedback Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix CI regressions after branch drift Sync the AppHost server project contract with current release/13.2 and make the RemoteHost authentication test tolerate the expected disconnect race after failed auth. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Description
Secure the local JSON-RPC channel between the Aspire CLI, the managed AppHost server, and guest runtimes by introducing a session token handshake.
The CLI now generates a cryptographically random session token for each AppHost-server session and passes it to the managed AppHost server and guest runtime through environment variables. Clients must authenticate immediately after connecting, before they can invoke AppHost RPC methods. This makes the trust boundary explicit in the implementation instead of relying only on named pipe / Unix domain socket discovery and OS ACLs.
Token flow:
ASPIRE_REMOTE_APPHOST_TOKEN.authenticate(token)before using privileged RPC methods.Why this design was chosen:
Checklist
<remarks />and<code />elements on your triple slash comments?aspire.devissue: