Skip to content

Commit 14ecbb6

Browse files
committed
Compute auth token in Node
1 parent cf59384 commit 14ecbb6

File tree

8 files changed

+47
-66
lines changed

8 files changed

+47
-66
lines changed

src/ElectronNET.API/ElectronNetRuntime.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ static ElectronNetRuntime()
2727

2828
public static string ElectronExtraArguments { get; set; }
2929

30+
public static string ElectronAuthToken { get; internal set; }
31+
3032
public static int? ElectronSocketPort { get; internal set; }
3133

3234
public static int? AspNetWebPort { get; internal set; }

src/ElectronNET.API/Runtime/Controllers/RuntimeControllerDotNetFirst.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,9 @@ protected override Task StartCore()
5858
private void ElectronProcess_Ready(object sender, EventArgs e)
5959
{
6060
var port = ElectronNetRuntime.ElectronSocketPort.Value;
61+
var token = ElectronNetRuntime.ElectronAuthToken;
6162
this.TransitionState(LifetimeState.Started);
62-
this.socketBridge = new SocketBridgeService(port, "");
63+
this.socketBridge = new SocketBridgeService(port, token);
6364
this.socketBridge.Ready += this.SocketBridge_Ready;
6465
this.socketBridge.Stopped += this.SocketBridge_Stopped;
6566
this.socketBridge.Start();

src/ElectronNET.API/Runtime/Controllers/RuntimeControllerElectronFirst.cs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ internal class RuntimeControllerElectronFirst : RuntimeControllerBase
1111
{
1212
private ElectronProcessBase electronProcess;
1313
private SocketBridgeService socketBridge;
14-
private int? port;
1514

1615
public RuntimeControllerElectronFirst()
1716
{
@@ -36,20 +35,16 @@ internal override SocketIOConnection Socket
3635

3736
protected override Task StartCore()
3837
{
39-
this.port = ElectronNetRuntime.ElectronSocketPort;
40-
41-
if (!this.port.HasValue)
42-
{
43-
throw new Exception("No port has been specified by Electron!");
44-
}
38+
var port = ElectronNetRuntime.ElectronSocketPort.Value;
39+
var token = ElectronNetRuntime.ElectronAuthToken;
4540

4641
if (!ElectronNetRuntime.ElectronProcessId.HasValue)
4742
{
4843
throw new Exception("No electronPID has been specified by Electron!");
4944
}
5045

5146
this.TransitionState(LifetimeState.Starting);
52-
this.socketBridge = new SocketBridgeService(this.port!.Value, "");
47+
this.socketBridge = new SocketBridgeService(port, token);
5348
this.socketBridge.Ready += this.SocketBridge_Ready;
5449
this.socketBridge.Stopped += this.SocketBridge_Stopped;
5550
this.socketBridge.Start();

src/ElectronNET.API/Runtime/Services/ElectronProcess/ElectronProcessActive.cs

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22
{
33
using System;
44
using System.ComponentModel;
5+
using System.Diagnostics;
56
using System.IO;
67
using System.Linq;
78
using System.Runtime.InteropServices;
9+
using System.Text.RegularExpressions;
810
using System.Threading.Tasks;
911
using ElectronNET.Common;
1012
using ElectronNET.Runtime.Data;
@@ -15,6 +17,7 @@
1517
[Localizable(false)]
1618
internal class ElectronProcessActive : ElectronProcessBase
1719
{
20+
private readonly Regex extractor = new Regex("^Electron Socket: listening on port (\\d+) at .* using ([a-f0-9]+)$");
1821
private readonly bool isUnpackaged;
1922
private readonly string electronBinaryName;
2023
private readonly string extraArguments;
@@ -159,37 +162,31 @@ private async Task StartInternal(string startCmd, string args, string directoriy
159162
{
160163
var tcs = new TaskCompletionSource();
161164

162-
void Read_SocketIO_Port(object sender, string line)
165+
void Read_SocketIO_Parameters(object sender, string line)
163166
{
164-
// Look for "Electron Socket: listening on port %s at"
165-
var prefix = "Electron Socket: listening on port ";
167+
// Look for "Electron Socket: listening on port %s at ..."
168+
var match = extractor.Match(line);
166169

167-
if (line.StartsWith(prefix))
170+
if (match?.Success ?? false)
168171
{
169-
var start = prefix.Length;
170-
var end = line.IndexOf(' ', start + 1);
171-
var port = line[start..end];
172+
var port = int.Parse(match.Groups[1].Value);
173+
var token = match.Groups[2].Value;
172174

173-
if (int.TryParse(port, out var p))
174-
{
175-
// We got the port, so no more need for reading this
176-
this.process.LineReceived -= Read_SocketIO_Port;
177-
ElectronNetRuntime.ElectronSocketPort = p;
178-
tcs.SetResult();
179-
}
175+
this.process.LineReceived -= Read_SocketIO_Parameters;
176+
ElectronNetRuntime.ElectronAuthToken = token;
177+
ElectronNetRuntime.ElectronSocketPort = port;
178+
tcs.SetResult();
180179
}
181180
}
182181

183182
try
184183
{
185-
await Task.Delay(10.ms()).ConfigureAwait(false);
186-
187184
Console.Error.WriteLine("[StartInternal]: startCmd: {0}", startCmd);
188185
Console.Error.WriteLine("[StartInternal]: args: {0}", args);
189186

190187
this.process = new ProcessRunner("ElectronRunner");
191188
this.process.ProcessExited += this.Process_Exited;
192-
this.process.LineReceived += Read_SocketIO_Port;
189+
this.process.LineReceived += Read_SocketIO_Parameters;
193190
this.process.Run(startCmd, args, directoriy);
194191

195192
await tcs.Task.ConfigureAwait(false);

src/ElectronNET.AspNet/Runtime/Controllers/RuntimeControllerAspNetBase.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using Microsoft.Extensions.DependencyInjection;
99
using ElectronNET.API;
1010
using ElectronNET.API.Bridge;
11+
using ElectronNET.AspNet.Services;
1112
using ElectronNET.Common;
1213
using ElectronNET.Runtime.Controllers;
1314
using ElectronNET.Runtime.Data;
@@ -17,12 +18,14 @@ internal abstract class RuntimeControllerAspNetBase : RuntimeControllerBase
1718
{
1819
private readonly IServer server;
1920
private readonly AspNetLifetimeAdapter aspNetLifetimeAdapter;
21+
private readonly IElectronAuthenticationService authenticationService;
2022
private SocketBridgeService socketBridge;
2123

22-
protected RuntimeControllerAspNetBase(IServer server, AspNetLifetimeAdapter aspNetLifetimeAdapter)
24+
protected RuntimeControllerAspNetBase(IServer server, AspNetLifetimeAdapter aspNetLifetimeAdapter, IElectronAuthenticationService authenticationService = null)
2325
{
2426
this.server = server;
2527
this.aspNetLifetimeAdapter = aspNetLifetimeAdapter;
28+
this.authenticationService = authenticationService;
2629
this.aspNetLifetimeAdapter.Ready += this.AspNetLifetimeAdapter_Ready;
2730
this.aspNetLifetimeAdapter.Stopping += this.AspNetLifetimeAdapter_Stopping;
2831
this.aspNetLifetimeAdapter.Stopped += this.AspNetLifetimeAdapter_Stopped;
@@ -59,10 +62,13 @@ protected void HandleReady()
5962
this.ElectronProcess.IsReady() &&
6063
this.aspNetLifetimeAdapter.IsReady())
6164
{
65+
var token = ElectronNetRuntime.ElectronAuthToken;
6266
var serverAddressesFeature = this.server.Features.Get<IServerAddressesFeature>();
6367
var address = serverAddressesFeature.Addresses.First();
6468
var uri = new Uri(address);
6569

70+
// Only if somebody registered an IElectronAuthenticationService service - otherwise we do not care
71+
this.authenticationService?.SetExpectedToken(token);
6672
ElectronNetRuntime.AspNetWebPort = uri.Port;
6773

6874
this.TransitionState(LifetimeState.Ready);

src/ElectronNET.AspNet/Runtime/Controllers/RuntimeControllerAspNetDotnetFirst.cs

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,14 @@
88
using ElectronNET.Runtime.Data;
99
using ElectronNET.Runtime.Helpers;
1010
using ElectronNET.Runtime.Services.ElectronProcess;
11+
using System.Security.Principal;
1112

1213
internal class RuntimeControllerAspNetDotnetFirst : RuntimeControllerAspNetBase
1314
{
1415
private ElectronProcessBase electronProcess;
15-
private readonly string authorization;
1616

17-
public RuntimeControllerAspNetDotnetFirst(IServer server, AspNetLifetimeAdapter aspNetLifetimeAdapter, IElectronAuthenticationService authenticationService = null) : base(server, aspNetLifetimeAdapter)
17+
public RuntimeControllerAspNetDotnetFirst(IServer server, AspNetLifetimeAdapter aspNetLifetimeAdapter, IElectronAuthenticationService authenticationService = null) : base(server, aspNetLifetimeAdapter, authenticationService)
1818
{
19-
this.authorization = Guid.NewGuid().ToString("N"); // 32 hex chars, no hyphens
20-
21-
// Only if somebody registered an IElectronAuthenticationService service - otherwise we do not care
22-
authenticationService?.SetExpectedToken(this.authorization);
2319
}
2420

2521
internal override ElectronProcessBase ElectronProcess => this.electronProcess;
@@ -28,9 +24,8 @@ protected override Task StartCore()
2824
{
2925
var isUnPacked = ElectronNetRuntime.StartupMethod.IsUnpackaged();
3026
var electronBinaryName = ElectronNetRuntime.ElectronExecutable;
31-
var authToken = this.authorization;
3227
var port = ElectronNetRuntime.ElectronSocketPort ?? 0;
33-
var args = $"{Environment.CommandLine} --authtoken={authToken}";
28+
var args = Environment.CommandLine;
3429

3530
this.electronProcess = new ElectronProcessActive(isUnPacked, electronBinaryName, args, port);
3631
this.electronProcess.Ready += this.ElectronProcess_Ready;
@@ -48,8 +43,9 @@ protected override Task StopCore()
4843
private void ElectronProcess_Ready(object sender, EventArgs e)
4944
{
5045
var port = ElectronNetRuntime.ElectronSocketPort.Value;
46+
var token = ElectronNetRuntime.ElectronAuthToken;
5147
this.TransitionState(LifetimeState.Started);
52-
this.CreateSocketBridge(port, this.authorization);
48+
this.CreateSocketBridge(port, token);
5349
}
5450

5551
private void ElectronProcess_Stopped(object sender, EventArgs e)

src/ElectronNET.AspNet/Runtime/Controllers/RuntimeControllerAspNetElectronFirst.cs

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,35 +3,31 @@
33
using System;
44
using System.Threading.Tasks;
55
using Microsoft.AspNetCore.Hosting.Server;
6+
using ElectronNET.AspNet.Services;
67
using ElectronNET.Runtime.Data;
78
using ElectronNET.Runtime.Services.ElectronProcess;
89

910
internal class RuntimeControllerAspNetElectronFirst : RuntimeControllerAspNetBase
1011
{
1112
private ElectronProcessBase electronProcess;
12-
private int? port;
1313

14-
public RuntimeControllerAspNetElectronFirst(IServer server, AspNetLifetimeAdapter aspNetLifetimeAdapter) : base(server, aspNetLifetimeAdapter)
14+
public RuntimeControllerAspNetElectronFirst(IServer server, AspNetLifetimeAdapter aspNetLifetimeAdapter, IElectronAuthenticationService authenticationService = null) : base(server, aspNetLifetimeAdapter, authenticationService)
1515
{
1616
}
1717

1818
internal override ElectronProcessBase ElectronProcess => this.electronProcess;
1919

2020
protected override Task StartCore()
2121
{
22-
this.port = ElectronNetRuntime.ElectronSocketPort;
23-
24-
if (!this.port.HasValue)
25-
{
26-
throw new Exception("No port has been specified by Electron!");
27-
}
22+
var port = ElectronNetRuntime.ElectronSocketPort.Value;
23+
var token = ElectronNetRuntime.ElectronAuthToken;
2824

2925
if (!ElectronNetRuntime.ElectronProcessId.HasValue)
3026
{
3127
throw new Exception("No electronPID has been specified by Electron!");
3228
}
3329

34-
this.CreateSocketBridge(this.port!.Value, "");
30+
this.CreateSocketBridge(port, token);
3531

3632
this.electronProcess = new ElectronProcessPassive(ElectronNetRuntime.ElectronProcessId.Value);
3733
this.electronProcess.Stopped += this.ElectronProcess_Stopped;

src/ElectronNET.Host/main.js

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
const { app } = require('electron');
22
const { BrowserWindow } = require('electron');
33
const { createServer } = require('http');
4+
const { randomUUID } = require('crypto');
45
const { Server } = require('socket.io');
56
const path = require('path');
67
const fs = require('fs');
@@ -25,6 +26,7 @@ let unpackeddotnet = false;
2526
let dotnetpacked = false;
2627
let electronforcedport;
2728
let electronUrl;
29+
let authToken = randomUUID().split('-').join('');
2830

2931
if (app.commandLine.hasSwitch('manifest')) {
3032
manifestJsonFileName = app.commandLine.getSwitchValue('manifest');
@@ -44,15 +46,8 @@ if (app.commandLine.hasSwitch('electronforcedport')) {
4446
electronforcedport = +app.commandLine.getSwitchValue('electronforcedport');
4547
}
4648

47-
let authToken;
48-
49-
if (app.commandLine.hasSwitch('authtoken')) {
50-
authToken = app.commandLine.getSwitchValue('authtoken');
51-
// Store in global for access by browser windows
52-
global.authToken = authToken;
53-
}
54-
55-
console.log('Started with token', authToken);
49+
// Store in global for access by browser windows
50+
global.authToken = authToken;
5651

5752
if (app.commandLine.hasSwitch('electronurl')) {
5853
electronUrl = app.commandLine.getSwitchValue('electronurl');
@@ -194,23 +189,14 @@ app.on('quit', async (event, exitCode) => {
194189
}
195190
}
196191

197-
// Clean up Socket.IO connection (legacy mode only)
192+
// Clean up Socket.IO connection
198193
if (typeof io !== 'undefined' && io && typeof io.close === 'function') {
199194
try {
200195
io.close();
201196
} catch (e) {
202197
console.error('Error closing Socket.IO connection:', e);
203198
}
204199
}
205-
206-
// Clean up SignalR connection (SignalR mode only)
207-
if (global['electronsignalr'] && typeof global['electronsignalr'].connection !== 'undefined') {
208-
try {
209-
await global['electronsignalr'].connection.stop();
210-
} catch (e) {
211-
console.error('Error closing SignalR connection:', e);
212-
}
213-
}
214200
});
215201

216202
function isSplashScreenEnabled() {
@@ -278,17 +264,19 @@ function startSocketApiBridge(port) {
278264
// otherwise the Windows Firewall will be triggered
279265
console.debug('Electron Socket: starting...');
280266
server = createServer();
267+
const host = !port ? '127.0.0.1' : 'localhost';
281268
let hostHook;
282269
io = new Server({
283270
pingTimeout: 60000, // in ms, default is 5000
284271
pingInterval: 10000, // in ms, default is 25000
285272
});
286273
io.attach(server);
287274

288-
server.listen(port, 'localhost');
275+
server.listen(port, host);
289276
server.on('listening', function () {
290277
const addr = server.address();
291-
console.info('Electron Socket: listening on port %s at %s', addr.port, addr.address);
278+
console.info(`Electron Socket: listening on port ${addr.port} at ${addr.address} using ${authToken}`);
279+
292280
// Now that socket connection is established, we can guarantee port will not be open for portscanner
293281
if (unpackedelectron) {
294282
startAspCoreBackendUnpackaged(port);

0 commit comments

Comments
 (0)