Skip to content

feat(network): captive portal detection and sign-in popup#2678

Open
Rocho-EL-Locho wants to merge 2 commits into
AvengeMedia:masterfrom
Rocho-EL-Locho:feat/captive-portal
Open

feat(network): captive portal detection and sign-in popup#2678
Rocho-EL-Locho wants to merge 2 commits into
AvengeMedia:masterfrom
Rocho-EL-Locho:feat/captive-portal

Conversation

@Rocho-EL-Locho

Copy link
Copy Markdown
Contributor

Description

Adds captive portal detection and a sign-in popup, similar to mainstream desktop and mobile OSes: when you join a network that requires sign-in before reaching the internet, DMS surfaces a popup and opens the login page in your default browser.

How it works:

  • A backend-agnostic connectivity probe in the network manager requests a known endpoint with redirects disabled. A redirect (or an unexpected response body) means traffic is being intercepted by a portal; a 204/expected body means full connectivity. The result is exposed as two new NetworkState fields, connectivity (unknown/none/portal/limited/full) and portalURL, which flow over the socket to the QML services.
  • CaptivePortalModal auto-opens when a portal is detected (gated by a setting) and closes automatically once the user signs in (the periodic re-probe flips the state to full).
  • A settings toggle under Network Status → Captive Portal enables/disables the popup, and a manual "Open sign-in page" entry is shown there while a portal is active.

Notes / design decisions (open to feedback):

  • The probe lives in the Manager rather than the NetworkManager backend, so it works across all backends (NetworkManager, iwd, networkd) and does not depend on NM's connectivity checking being enabled.
  • The probe endpoint defaults to http://nmcheck.gnome.org/check_network_status.txt (the same default NetworkManager uses) and is overridable via DMS_CAPTIVE_PROBE_URL. Once a connection is healthy, it is re-checked only every ~5 minutes to keep background traffic low.
  • The modal reports the SSID and warns when a VPN is active (it may need to be disconnected to reach the portal).

Type of change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that changes existing behavior)
  • Refactor / internal cleanup
  • Documentation
  • Other

Related issues

Fixes #2632

Screenshots / video

The popup ("Sign in to network") shows the network name with Open login page and Dismiss actions. I can attach a screenshot/recording with demo data on request.

Checklist

  • My code follows the conventions in CONTRIBUTING.md
  • I have tested my changes locally (against a real captive portal and a local simulation)
  • New user-facing strings are wrapped in I18n.tr() and registered in en.json, reusing existing terms where possible
  • Go changes: ran make fmt, added/updated tests, make test passes, and go mod tidy is clean
  • QML changes: make lint-qml was not run locally — generating the tooling VFS requires a newer Quickshell than the packaged 0.3.0 here. QML was verified with qmllint syntax checks and a live runtime load; happy to address anything CI flags.
  • I have opened a corresponding pull request in DankLinux-Docs — glad to do so if you'd like docs for this.

Detect captive portals and prompt the user to sign in, similar to
mainstream desktop and mobile OSes.

A backend-agnostic connectivity probe in the network manager requests a
known endpoint (configurable via DMS_CAPTIVE_PROBE_URL); a redirect or an
unexpected body means traffic is being intercepted. The result is exposed
as new connectivity and portalURL fields over the socket. A modal opens
when a portal is detected and closes once the user signs in, with the
login page opening in the default browser. The popup can be disabled, and
a manual entry point is available while a portal is active.

Fixes AvengeMedia#2632
Adds the captivePortal section and captivePortalAutoOpen toggle to the
generated settings search index (extract_settings_index.py).
DankModal {
id: root

readonly property string fallbackURL: "http://nmcheck.gnome.org/check_network_status.txt"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Probably better if we don't hardcode this in 2 places on th e QML side, can be a constant in NetworkService

ConnectivityUnknown Connectivity = "unknown"
ConnectivityNone Connectivity = "none"
ConnectivityPortal Connectivity = "portal"
ConnectivityLimited Connectivity = "limited"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Doesnt seem to do anything with the limited case, should we? I'd rather remove it or add a // TODO than leave the unused value

}

Rectangle {
width: Math.max(120, openText.contentWidth + Theme.spacingM * 2)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Better to use standard Widgets/ than hand-rolling custom buttons, e.g. Dankbutton


func (p *portalProbe) run() {
defer p.wg.Done()
ticker := time.NewTicker(portalProbeInterval)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Not a fan of using a ticker here, better to rewrite polling to be event-driven. I'd re-work that as a timer thats conditionalled armed, cancelled when sign in is "full" . We can probably subscribe to NM PropertiesChanged, or something like that to catch portal changes.

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.

Captive Portal Integration

2 participants