Register eagle0:// URL scheme on Windows for OAuth deep links (#5609)

* Register eagle0:// URL scheme on Windows for OAuth deep links

The Windows installer now registers the eagle0:// URL scheme in the
registry (HKEY_CURRENT_USER\Software\Classes\eagle0) pointing to the
game executable. This allows OAuth callbacks to redirect back to the
app automatically.

Also updated OAuthManager to check command line arguments for deep
links since Windows passes URL scheme launches as command line args.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add golang.org/x/sys dependency for Windows registry access

The Windows installer uses golang.org/x/sys/windows/registry to register
the eagle0:// URL scheme. This adds the required dependency to go.mod,
go.sum, and MODULE.bazel.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-26 18:32:39 -08:00
committed by GitHub
co-authored by Claude Opus 4.5
parent 132abfa7a4
commit 04928f0e2c
8 changed files with 103 additions and 1 deletions
+1
View File
@@ -122,6 +122,7 @@ use_repo(
"com_github_webview_webview_go",
"org_golang_google_grpc",
"org_golang_google_protobuf",
"org_golang_x_sys",
)
#
+1
View File
@@ -12,6 +12,7 @@ require (
github.com/golang-jwt/jwt/v5 v5.2.1
github.com/google/uuid v1.6.0
github.com/webview/webview_go v0.0.0-20240831120633-6173450d4dd6
golang.org/x/sys v0.28.0
google.golang.org/grpc v1.68.0
google.golang.org/protobuf v1.36.3
)
+2
View File
@@ -43,6 +43,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/webview/webview_go v0.0.0-20240831120633-6173450d4dd6 h1:VQpB2SpK88C6B5lPHTuSZKb2Qee1QWwiFlC5CKY4AW0=
github.com/webview/webview_go v0.0.0-20240831120633-6173450d4dd6/go.mod h1:yE65LFCeWf4kyWD5re+h4XNvOHJEXOCOuJZ4v8l5sgk=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -53,6 +53,21 @@ namespace Auth {
if (!string.IsNullOrEmpty(Application.absoluteURL)) {
OnDeepLinkActivated(Application.absoluteURL);
}
// On Windows, URL scheme launches pass the URL as a command line argument
CheckCommandLineForDeepLink();
}
private void CheckCommandLineForDeepLink() {
var args = System.Environment.GetCommandLineArgs();
// Args[0] is the executable path, check remaining args for eagle0:// URLs
for (int i = 1; i < args.Length; i++) {
if (args[i].StartsWith("eagle0://")) {
Debug.Log($"[OAuthManager] Found deep link in command line: {args[i]}");
OnDeepLinkActivated(args[i]);
break;
}
}
}
private void OnDeepLinkActivated(string url) {
@@ -11,12 +11,15 @@ go_library(
"ui_webview_stub.go",
"ui_webview_windows.go",
"updater.go",
"urlscheme_stub.go",
"urlscheme_windows.go",
],
importpath = "github.com/nolen777/eagle0/src/main/go/net/eagle0/clients/win/installer",
visibility = ["//visibility:private"],
deps = select({
"@io_bazel_rules_go//go/platform:windows": [
"@com_github_webview_webview_go//:webview_go",
"@org_golang_x_sys//windows/registry",
],
"//conditions:default": [],
}),
@@ -50,6 +53,7 @@ genrule(
"ui_interface.go",
"ui_webview_windows.go",
"updater.go",
"urlscheme_windows.go",
"resource_windows_amd64.syso",
"@llvm_mingw//:all_files",
"@go_default_sdk//:files",
@@ -130,12 +134,17 @@ module github.com/nolen777/eagle0/src/main/go/net/eagle0/clients/win/installer
go 1.23
require github.com/webview/webview_go v0.0.0-20240831120633-6173450d4dd6
require (
github.com/webview/webview_go v0.0.0-20240831120633-6173450d4dd6
golang.org/x/sys v0.28.0
)
GOMOD
cat > go.sum << 'GOSUM'
github.com/webview/webview_go v0.0.0-20240831120633-6173450d4dd6 h1:VQpB2SpK88C6B5lPHTuSZKb2Qee1QWwiFlC5CKY4AW0=
github.com/webview/webview_go v0.0.0-20240831120633-6173450d4dd6/go.mod h1:yE65LFCeWf4kyWD5re+h4XNvOHJEXOCOuJZ4v8l5sgk=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
GOSUM
# Copy source files from Bazel sandbox (exclude Go SDK and llvm files)
@@ -154,6 +154,12 @@ func runInstallerLogic(invitationCode string) error {
saveInvitationCode(invitationCode)
}
// Register eagle0:// URL scheme for OAuth deep links
if err := RegisterURLScheme(exeLocation); err != nil {
ui.ShowWarning(fmt.Sprintf("Failed to register URL scheme: %v", err))
// Continue anyway - URL scheme is nice to have but not critical
}
ui.UpdateStatus("Launching Eagle0...")
cmd := exec.Command(exeLocation)
if err := cmd.Start(); err != nil {
@@ -0,0 +1,8 @@
//go:build !windows
package main
// RegisterURLScheme is a no-op on non-Windows platforms
func RegisterURLScheme(exePath string) error {
return nil
}
@@ -0,0 +1,60 @@
//go:build windows
package main
import (
"fmt"
"golang.org/x/sys/windows/registry"
)
const urlScheme = "eagle0"
// RegisterURLScheme registers the eagle0:// URL scheme in the Windows registry.
// This allows the browser to open the Eagle0 app when navigating to eagle0:// URLs.
// The URL scheme points to the game executable, which will handle the deep link.
func RegisterURLScheme(exePath string) error {
// Create the URL scheme key under HKEY_CURRENT_USER (doesn't require admin)
// Structure:
// HKEY_CURRENT_USER\Software\Classes\eagle0
// (Default) = "URL:Eagle0 Protocol"
// URL Protocol = ""
// shell\open\command
// (Default) = "\"C:\path\to\eagle0.exe\" \"%1\""
keyPath := `Software\Classes\` + urlScheme
// Create or open the main key
key, _, err := registry.CreateKey(registry.CURRENT_USER, keyPath, registry.ALL_ACCESS)
if err != nil {
return fmt.Errorf("failed to create registry key %s: %w", keyPath, err)
}
defer key.Close()
// Set the default value (description)
if err := key.SetStringValue("", "URL:Eagle0 Protocol"); err != nil {
return fmt.Errorf("failed to set default value: %w", err)
}
// Set URL Protocol (empty string indicates this is a URL protocol handler)
if err := key.SetStringValue("URL Protocol", ""); err != nil {
return fmt.Errorf("failed to set URL Protocol: %w", err)
}
// Create shell\open\command subkey
cmdKeyPath := keyPath + `\shell\open\command`
cmdKey, _, err := registry.CreateKey(registry.CURRENT_USER, cmdKeyPath, registry.ALL_ACCESS)
if err != nil {
return fmt.Errorf("failed to create registry key %s: %w", cmdKeyPath, err)
}
defer cmdKey.Close()
// Set the command to launch the game with the URL as argument
// The %1 will be replaced with the full URL (e.g., eagle0://auth/complete)
command := fmt.Sprintf(`"%s" "%%1"`, exePath)
if err := cmdKey.SetStringValue("", command); err != nil {
return fmt.Errorf("failed to set command: %w", err)
}
return nil
}