Skip to content

WIP: WPA2 Enterprise support#152

Draft
shazow wants to merge 2 commits intomainfrom
feat-wpa2-enterprise-4884098636582878485
Draft

WIP: WPA2 Enterprise support#152
shazow wants to merge 2 commits intomainfrom
feat-wpa2-enterprise-4884098636582878485

Conversation

@shazow
Copy link
Copy Markdown
Owner

@shazow shazow commented Mar 10, 2026

This PR adds comprehensive support for WPA2 Enterprise networks, allowing users to connect to environments like eduroam. It refactors JoinNetwork to use a JoinOptions struct, providing room for advanced fields like identity, anonymous identity, EAP method, and Phase 2 authentication.

The CLI now has flags to ingest these fields, and the TUI has been updated with conditional form fields that dynamically mount into the view and focus management stack when the "WPA2 Enterprise" security choice is selected.

Currently, this is only robustly implemented in the NetworkManager backend due to limitations with D-Bus and CLI abstractions on iwd and darwin (which return explicit help errors directing users on how to configure these networks alternatively). Tests have been added to verify that state updates, scaling fields, and backends gracefully handle the new settings.


PR created automatically by Jules for task 4884098636582878485 started by @shazow

- Introduces a new `JoinOptions` struct to the `wifi.Backend.JoinNetwork` interface, allowing for scalable connection parameters.
- Adds `SecurityWPAEAP` to the `wifi.SecurityType` constants.
- Updates the NetworkManager backend to populate `802-1x` connection dictionaries with `eap`, `identity`, `password`, `phase2-auth`, and `anonymous-identity` for Enterprise networks.
- Updates the CLI `connect` command to accept `--identity`, `--anonymous-identity`, `--eap`, and `--phase2-auth` flags.
- Updates the TUI `EditModel` to conditionally display and manage focus for `Identity`, `Anonymous Identity`, `EAP Method`, and `Phase 2 Auth` inputs when the user selects "WPA2 Enterprise" as their security type.
- Ensures the `iwd` and `darwin` backends return helpful fallback errors when attempting to join a `WPAEAP` network via basic commands, as those platforms typically require more complex system-level profile configurations.
- Updates tests across the CLI, mock backend, and TUI to accommodate the new interface signatures and dynamic input states.

Co-authored-by: shazow <6292+shazow@users.noreply.github.com>
@google-labs-jules
Copy link
Copy Markdown
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@shazow shazow changed the title feat: add WPA2 Enterprise support with advanced options WIP: WPA2 Enterprise support Mar 10, 2026
@shazow shazow marked this pull request as draft March 10, 2026 20:17
isForgetting bool
focusManager *FocusManager
ssidAdapter *TextInput
identityAdapter *TextInput
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we move the components specific to WPA2 Enterprise into a standalone struct containing them which can be added/removed easily? Let's generally isolate new functionality changes when possible.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've moved the EAP-specific components into an EapComponents struct to better isolate the new WPA2 Enterprise functionality within the EditModel.

main.go Outdated
Comment on lines +96 to +98
Identity string `long:"identity" description:"identity/username for enterprise networks (WPA-EAP)"`
AnonymousIdentity string `long:"anonymous-identity" description:"anonymous identity for enterprise networks"`
EAP string `long:"eap" description:"EAP method for enterprise networks (e.g. peap, ttls)"`
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Group and prefix options related to EAP with eap-* , also provide choices and a default option when appropriate (eg for eap).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have updated the CLI options to use the eap-* prefix, specifically eap-identity, eap-anonymous-identity, eap-method, and eap-phase2-auth. I also added default options and bounded the string inputs with choice tags for the method and phase 2 auth flags.

- Groups EAP-related TUI components (`identity`, `anonymous-identity`, `eap`, `phase2-auth`) into a new `EapComponents` struct within `EditModel` for better encapsulation.
- Updates the CLI command flags for connecting to enterprise networks to be uniformly prefixed with `eap-*` (e.g., `--eap-identity`, `--eap-method`).
- Adds `default` and `choice` specifications to `--eap-method` (`peap`, `ttls`, `tls`) and `--eap-phase2-auth` (`mschapv2`, `pap`) in `main.go`.

Co-authored-by: shazow <6292+shazow@users.noreply.github.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds WPA2-Enterprise (WPA-EAP) connection support across backends and surfaces the required configuration fields in both the CLI and TUI, primarily implemented for the NetworkManager backend while other backends return explicit “not supported” guidance.

Changes:

  • Refactors Backend.JoinNetwork to accept a wifi.JoinOptions struct (enabling WPA-EAP parameters like identity, anonymous identity, EAP method, and phase2 auth).
  • Implements WPA-EAP join configuration for the NetworkManager backend; iwd/darwin return informative errors for WPA-EAP.
  • Extends CLI flags and TUI join flow to collect/pass WPA-EAP credentials, including dynamic TUI fields and focus-stack updates.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
wifi/backend.go Adds SecurityWPAEAP and introduces JoinOptions; updates Backend interface.
wifi/networkmanager/networkmanager.go Implements WPA-EAP connection settings via NetworkManager connection maps.
wifi/iwd/iwd.go Updates JoinNetwork signature; returns explicit error for WPA-EAP.
wifi/darwin/darwin.go Updates JoinNetwork signature; returns explicit error for WPA-EAP.
wifi/mock/mock.go Updates mock backend to accept JoinOptions.
wifi/mock/mock_test.go Updates tests to use JoinOptions.
cli.go Refactors connect flow to pass JoinOptions into the backend.
main.go Adds CLI flags for WPA-EAP fields; updates security parsing.
cli_test.go Updates tests to use JoinOptions and adjusts retry timing expectations.
internal/tui/component.go Expands joinNetworkMsg with WPA-EAP fields.
internal/tui/tui.go Builds JoinOptions from joinNetworkMsg and calls backend with it.
internal/tui/edit.go Adds WPA2 Enterprise option + mounts EAP fields dynamically and updates focus handling logic.
internal/tui/edit_test.go Adjusts focus navigation test to account for focus-manager order changes.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 51 to 83
ssidInput := textinput.New()
ssidInput.Focus()
ssidInput.CharLimit = 32
ssidInput.Width = 30
if !isNew {
ssidInput.SetValue(item.SSID)
}

identityInput := textinput.New()
identityInput.Focus()
identityInput.CharLimit = 64
identityInput.Width = 45

anonymousIdentInput := textinput.New()
anonymousIdentInput.Focus()
anonymousIdentInput.CharLimit = 64
anonymousIdentInput.Width = 45

eapInput := textinput.New()
eapInput.Focus()
eapInput.CharLimit = 64
eapInput.Width = 45
eapInput.SetValue("peap")

phase2AuthInput := textinput.New()
phase2AuthInput.Focus()
phase2AuthInput.CharLimit = 64
phase2AuthInput.Width = 45
phase2AuthInput.SetValue("mschapv2")

passwordInput := textinput.New()
passwordInput.Focus()
passwordInput.CharLimit = 64
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New EAP-related text inputs are initialized with Model.Focus() (and SSID/password inputs are also focused). This can leave multiple text inputs focused at the same time, including inputs that aren’t even mounted in the FocusManager items list, which breaks IsConsumingInput() (global keybindings get suppressed) and can cause inconsistent cursor/focus behavior. Prefer leaving these models blurred by default and let FocusManager/Focusable.Focus() be the only place that calls Model.Focus(); when a field is not part of the active items list, ensure it’s blurred.

Copilot uses AI. Check for mistakes.
Comment on lines +424 to +437
// Preserve focus if possible, otherwise reset
currentFocused := m.focusManager.Focused()
m.focusManager = NewFocusManager(items...)
found := false
for _, item := range items {
if item == currentFocused {
m.focusManager.SetFocus(item)
found = true
break
}
}
if !found {
m.focusManager.Focus()
}
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updateFocusManagerItems replaces m.focusManager with a new instance without blurring the previously-focused item first. If the previously focused item was a TextInput (or a field that is being removed), its underlying textinput.Model may remain focused, again causing IsConsumingInput() to stay true and leading to “stuck” focus/cursor state. Blur the current focused item (and/or call the old focusManager.Blur()) before swapping managers, and explicitly blur any items that are being removed from the focus stack.

Copilot uses AI. Check for mistakes.
Comment on lines +187 to +197
sec := wifi.SecurityOpen
switch m.securityGroup.Selected() {
case 0:
sec = wifi.SecurityOpen
case 1:
sec = wifi.SecurityWEP
case 2:
sec = wifi.SecurityWPA
case 3:
sec = wifi.SecurityWPAEAP
}
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Security selection index -> wifi.SecurityType mapping is duplicated in multiple places (NewEditModel, buttonAction, Update’s old/new security calculations). This repetition is easy to desync when options change. Consider factoring the mapping into a small helper (e.g., selectedSecurityType()) used everywhere.

Copilot uses AI. Check for mistakes.
Comment on lines +23 to +26
identityAdapter *TextInput
anonymousIdentAdapter *TextInput
eapAdapter *TextInput
phase2AuthAdapter *TextInput
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EapComponents field name anonymousIdentAdapter is inconsistent/abbreviated compared to the user-facing label and JoinOptions.AnonymousIdentity. Renaming to anonymousIdentityAdapter would make the code clearer and reduce the chance of mixing up identity vs anonymous identity fields.

Suggested change
identityAdapter *TextInput
anonymousIdentAdapter *TextInput
eapAdapter *TextInput
phase2AuthAdapter *TextInput
identityAdapter *TextInput
anonymousIdentityAdapter *TextInput
eapAdapter *TextInput
phase2AuthAdapter *TextInput

Copilot uses AI. Check for mistakes.
Comment on lines +158 to +162
if opts.Password != "" || opts.Identity != "" || opts.IsHidden {
return b.JoinNetwork(opts)
}

return b.ActivateConnection(ssid)
return b.ActivateConnection(opts.SSID)
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

attemptConnect decides between JoinNetwork vs ActivateConnection based on Password/Identity/IsHidden, but ignores other WPA-EAP inputs and even opts.Security itself. If the user specifies --security wpa-eap (or provides EAP method/phase2/anonymous identity) without Identity/Password, this will incorrectly call ActivateConnection (dropping the enterprise settings entirely) and likely fail with a not-found error. Consider switching on opts.Security (e.g., always JoinNetwork for WPA-EAP) or treating any non-empty enterprise field as requiring JoinNetwork.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants