@@ -25,14 +25,31 @@ public override bool ToProperties()
2525 // is now the single source of truth for the agent-facing URL. Overriding it
2626 // server-side would defeat the whole point of validating that the agent reached
2727 // the gateway through one of `AgentTunnel.AdvertisedNames`.
28- Runtime . Session [ AgentProperties . AgentTunnelEnrollmentString ] = enrollmentString . Text . Trim ( ) ;
28+ //
29+ // Important: the enrollment string is persisted with ALL internal whitespace
30+ // removed — not just edge `.Trim()`. The validation in `DoValidate` already
31+ // works on the whitespace-stripped form (browsers and password managers
32+ // routinely wrap long JWTs across lines on paste), so the value handed off
33+ // to `agent.exe up --enrollment-string` must match what was validated.
34+ // Otherwise a multiline pasted JWT passes validation here and then fails at
35+ // enrollment with an opaque error from the agent's JWT parser.
36+ Runtime . Session [ AgentProperties . AgentTunnelEnrollmentString ] = StripAllWhitespace ( enrollmentString . Text ) ;
2937 Runtime . Session [ AgentProperties . AgentTunnelAgentName ] = agentName . Text . Trim ( ) ;
3038 Runtime . Session [ AgentProperties . AgentTunnelAdvertiseSubnets ] = advertiseSubnets . Text . Trim ( ) ;
3139 Runtime . Session [ AgentProperties . AgentTunnelAdvertiseDomains ] = advertiseDomains . Text . Trim ( ) ;
3240
3341 return true ;
3442 }
3543
44+ /// <summary>
45+ /// Strip every whitespace character from <paramref name="value"/>. Used to
46+ /// canonicalize pasted JWTs (which often arrive wrapped onto multiple lines)
47+ /// so that validation and the value persisted into the MSI session refer to
48+ /// the exact same string.
49+ /// </summary>
50+ private static string StripAllWhitespace ( string value ) =>
51+ value is null ? string . Empty : Regex . Replace ( value , @"\s+" , "" ) ;
52+
3653 public override void OnLoad ( object sender , EventArgs e )
3754 {
3855 banner . Image = Runtime . Session . GetResourceBitmap ( "WixUI_Bmp_Banner" ) ;
@@ -58,7 +75,10 @@ public override bool DoValidate()
5875 // JWT shape: three base64url segments separated by dots. The agent's `up --enrollment-string`
5976 // parses the JWT claims for jet_gw_url / jet_agent_name, so the dialog only sanity-checks
6077 // shape and base64url decodability here; signature verification happens at the gateway.
61- string text = Regex . Replace ( enrollmentString . Text , @"\s+" , "" ) ;
78+ //
79+ // Use the same whitespace-stripping helper as `ToProperties` so validation and
80+ // persistence operate on byte-identical input.
81+ string text = StripAllWhitespace ( enrollmentString . Text ) ;
6282 string [ ] parts = text . Split ( '.' ) ;
6383 if ( parts . Length != 3 || parts . Any ( string . IsNullOrEmpty ) )
6484 {
0 commit comments