# VIT AutoConnect (MVP)

Windows tray app that detects the VIT Wi-Fi captive portal and logs you in
automatically, using credentials stored via Windows DPAPI. Implements the
PRD's MVP scope (sections 1–14).

## Using it

1. Run `VITAutoConnect.exe`. Nothing to install — it is self-contained.
2. Enter your VIT ID and password once in the setup screen, then **Save & Connect**.
3. It sits in the system tray. When you join VIT Wi-Fi and the portal gets in
   the way, it signs you in — no browser, no typing.

Right-click the tray icon for **Connect / Retry**, **Settings**, and
**View log**. "Start with Windows" is on by default and can be turned off in
Settings.

**You do not need to configure a portal address.** The app reads the login
page off the portal itself at the moment it intercepts you — that is how it
learns the endpoint, the field names, and the hidden session fields your
campus appliance requires.

## Requirements to build

- .NET 8 SDK (Windows desktop workload on Windows; on Linux the project sets
  `EnableWindowsTargeting` so `dotnet publish -r win-x64` works there too)

```powershell
dotnet build -c Release
dotnet run
```

There are **no external NuGet dependencies** — DPAPI is called directly via
P/Invoke instead of the `System.Security.Cryptography.ProtectedData` package,
so `dotnet restore` works fully offline.

## Tests

`tests/PortalTests` stands up a real HTTP server that behaves like a captive
portal (302-to-login, login page substituted inline, meta-refresh splash,
session cookies, hidden fields, wrong-password rejection) and drives the real
discovery and authentication code against it:

```bash
cd tests/PortalTests
dotnet run
```

40 checks, exit code 0 when they all pass. This runs on any OS — the portal
logic is deliberately free of Windows dependencies so it can be tested away
from campus.

## How it decides what to do

| Step | File |
|---|---|
| FR-1 Tray app | `App/TrayController.cs` |
| FR-2 Network detection | `Network/NetworkMonitor.cs` |
| FR-3 VIT network ID | `Network/VITNetworkDetector.cs`, `Network/WlanInterop.cs` |
| FR-4 Captive portal detection | `Network/ConnectivityChecker.cs` |
| FR-5 Portal discovery + auth | `Auth/PortalDiscovery.cs`, `Auth/VITAuthenticator.cs` |
| FR-6 Verification | `TrayController.AuthenticateAsync` (re-probes after login) |
| FR-7 Retry strategy | `Auth/AuthRetryPolicy.cs` |
| FR-8 Credential storage | `Security/CredentialStore.cs`, `Security/Dpapi.cs` |
| FR-9 Manual control | Tray menu "Connect / Retry" |
| FR-10 Logging | `Diagnostics/AppLogger.cs` |
| Setup / Settings UI | `UI/SetupWindow.cs`, `UI/SettingsWindow.cs` |
| Start with Windows | `App/StartupManager.cs` |

## If it doesn't sign you in

The tray tooltip says what it is doing, and **View log** has the detail. The
app is deliberately honest about *why* it is not acting, so a network it won't
touch is never a silent hang:

- *"Not on Wi-Fi - monitoring"* — no wireless connection is up (Wi-Fi off,
  not joined yet, or on ethernet). It re-checks every 30 seconds, so joining
  VIT Wi-Fi is picked up on its own.
- *"Can't identify the Wi-Fi network (…)"* — a Wi-Fi network is connected but
  its name couldn't be read. The app **refuses to guess** and send your
  password to an unidentified network. Open **View log** and send it; the SSID
  read is the first thing to check.
- *"Ignoring 'SOME-SSID' — not a VIT network"* — the Wi-Fi name isn't in the
  list. Add it in Settings (comma-separated). Matching ignores case, dashes
  and underscores, and matches prefixes *and* substrings, so `VIT-WiFi`
  already covers `VIT_WIFI`, `VIT-WiFi-5G`, `VITWIFI_MensHostel` and so on.
- *"The saved password is forgotten"* — credentials now round-trip-verify on
  save, and a failed save shows a dialog with the storage path
  (`%LOCALAPPDATA%\VITAutoConnect\credentials.dat`) instead of silently doing
  nothing. If you get that dialog, **View log** will say which DPAPI call
  failed (e.g. WLAN/profile issues) — send it over.
- *"Could not find the Wi-Fi login page"* — the portal didn't present a form
  we could read. Sign in through a browser once, then send the log; the three
  portal boxes in Settings are the manual escape hatch.
- *"The portal rejected your VIT ID or password"* — deliberate: the app stops
  instead of retrying, so a wrong password can't lock your account.

## What is *not* verified

The portal and authentication paths are covered by the tests above. The
Windows-only pieces — reading the SSID through `wlanapi.dll`
(`Network/WlanInterop.cs`), DPAPI credential encryption, the tray icon, and
"Start with Windows" — are compiled but cannot be executed on the Linux host
that builds this release. The WLAN struct offsets were re-checked by hand
against the Windows SDK layout (`WLAN_CONNECTION_ATTRIBUTES`: 4-byte state +
4-byte mode + 512-byte profile name, so the SSID sits at offset 520), and the
read now retries transient failures and logs each Win32 error code. But first
run on a real laptop is still the real test — if the tray says it can't
identify the network, the log now names the exact failing WLAN call.

## Security notes

- Password is never written to disk in plaintext; `CredentialStore` encrypts
  it with DPAPI scoped to the current Windows user (`CRYPTPROTECT_UI_FORBIDDEN`,
  no `CRYPTPROTECT_LOCAL_MACHINE`), so only this Windows account on this
  machine can decrypt it. Saves are read back and verified before the app
  reports success.
- If the portal's HTTPS certificate can't be validated, the app **refuses to
  send your password** rather than turning validation off, and tells you so.
- `AppLogger` only ever receives operational strings from this codebase and
  additionally redacts anything matching `password=`/`pwd=`/`secret=`.
- No functionality here attempts to bypass VIT's authentication or network
  controls — it automates the same login you would do by hand in a browser,
  using your own credentials, with your consent (per PRD section 10).