Skip to content

feat(plugin): aircraft — optional OpenSky OAuth2 credentials#393

Merged
Kohei-Wada merged 2 commits into
mainfrom
feat/aircraft-opensky-oauth
May 30, 2026
Merged

feat(plugin): aircraft — optional OpenSky OAuth2 credentials#393
Kohei-Wada merged 2 commits into
mainfrom
feat/aircraft-opensky-oauth

Conversation

@Kohei-Wada
Copy link
Copy Markdown
Owner

Second half of OpenSky API-key support, built on #392 (ttymap.http POST/headers). Pure Lua — no aircraft-specific Rust.

What

OpenSky moved to OAuth2 client-credentials in 2025; anonymous access is capped at 400 credits/day (a ~10°×10° /states/all call costs ~2). Authenticate to lift the cap to 4000/day (8000 if you feed the network).

Set credentials in ~/.config/ttymap/init.lua:

local aircraft = require("ttymap.aircraft")
aircraft.client_id     = "your-client-id"
aircraft.client_secret = "your-client-secret"

Leave unset for anonymous access (unchanged default).

How

  • runtime/lua/ttymap/aircraft.lua — new config-holder lib (same nvim-style pattern as ttymap.here); the plugin reads the fields lazily at fetch time.
  • opensky.lua — token state machine (poll_auth, driven once per tick): drains an async token POST (via the new http:fetch(url, {method="POST", form=...})), caches the Bearer token, refreshes ~1 min before its expires_in (~30 min) lapses, and backs off ~60 s on failure so bad credentials don't hammer the token endpoint every frame. fetch_states adds Authorization: Bearer once a token is in hand, else anonymous GET.
  • init.luaon_tick calls opensky.poll_auth() and fetches via opensky.fetch_states(...).

Verification

  • luac -p on all three Lua files.
  • Booted the real subsystem via build_subsystem (throwaway test) to confirm plugin.aircraft (now requiring ttymap.aircraft) loads and registers — "Toggle aircraft" lands in the registry.
  • cargo test (full workspace), clippy --all-targets, fmt --check pass.
  • Not exercised live: an actual authenticated OpenSky round-trip (needs real credentials + network). The token-flow logic is Lua; the HTTP transport it rides was tested in feat(lua): ttymap.http:fetch opts — POST, headers, form body #392.

OpenSky moved to OAuth2 client-credentials in 2025; anonymous access is
capped at 400 credits/day. Add opt-in auth: set client_id/client_secret
via require("ttymap.aircraft") in init.lua to lift the cap (4000/day).

opensky.lua gains a token state machine (poll_auth): drains an async
token POST, caches the Bearer token, refreshes ~1 min before expiry,
and backs off ~60s on failure so bad credentials don't hammer the
endpoint. fetch_states adds the Authorization header once a token is in
hand and falls back to anonymous GET otherwise. Pure Lua — built on the
generic ttymap.http POST/headers surface (no aircraft-specific Rust).
Copy link
Copy Markdown

@amazon-q-developer amazon-q-developer Bot left a comment

Choose a reason for hiding this comment

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

Summary

This PR successfully implements OAuth2 client-credentials authentication for the OpenSky Network API in pure Lua, enabling users to lift rate limits from 400 to 4000+ credits/day. The implementation includes a config holder module, token state machine with automatic refresh, and graceful fallback to anonymous access.

Critical Changes Requested

Security (1 issue):

  • The configured() function must validate that credentials are non-empty strings, not just non-nil, to prevent authentication failures that trigger unnecessary token endpoint requests every 60 seconds

Architecture

The implementation follows sound patterns:

  • Lazy credential reading allows init.lua configuration
  • Token refresh logic with 60-second backoff on failure prevents endpoint hammering
  • Proper separation of concerns across three modules
  • Graceful degradation to anonymous access when unconfigured

Testing Notes

Per PR description, the OAuth flow itself hasn't been exercised with live credentials. Recommend manual testing with actual OpenSky OAuth2 credentials before merge.


You can now have the agent implement changes and create commits directly on your pull request's source branch. Simply comment with /q followed by your request in natural language to ask the agent to make changes.

Comment on lines +60 to +62
local function configured()
return cfg.client_id ~= nil and cfg.client_secret ~= nil
end
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛑 Security Vulnerability: Validate OAuth2 credentials before sending to prevent potential injection or malformed request issues. The configured() function only checks for nil but accepts empty strings, which will cause authentication failures that hammer the token endpoint every 60 seconds (backoff timer at line 83). Additionally, credentials aren't sanitized before being sent in the POST form body.1

Add validation to ensure credentials are non-empty strings and potentially sanitize them to prevent injection attacks.

Suggested change
local function configured()
return cfg.client_id ~= nil and cfg.client_secret ~= nil
end
local function configured()
return cfg.client_id ~= nil and cfg.client_secret ~= nil
and type(cfg.client_id) == "string" and type(cfg.client_secret) == "string"
and cfg.client_id ~= "" and cfg.client_secret ~= ""
end

Footnotes

  1. CWE-20: Improper Input Validation - https://cwe.mitre.org/data/definitions/20.html

…rce notify

- ttymap.aircraft.max_count: cap how many aircraft show (dense airspace
  returns hundreds). Keeps the nearest N to the map centre, ranked by
  equirectangular distance. nil = no cap.
- one-shot "reading via OpenSky API (authenticated)" notify when the
  token is first acquired, so you can see which source is live when
  credentials are configured.
@Kohei-Wada Kohei-Wada merged commit 9c69515 into main May 30, 2026
4 checks passed
@Kohei-Wada Kohei-Wada deleted the feat/aircraft-opensky-oauth branch May 30, 2026 05:49
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.

1 participant