Skip to content

Serve a landing page and a Slack-sign-in-gated configuration page - #13

Merged
tytremblay merged 1 commit into
mainfrom
web-landing-and-config
Aug 22, 2026
Merged

Serve a landing page and a Slack-sign-in-gated configuration page#13
tytremblay merged 1 commit into
mainfrom
web-landing-and-config

Conversation

@tytremblay

Copy link
Copy Markdown
Contributor

What

mod.redhawkrobotics.org (handled by hawk_suite's proxy; PUBLIC_URL points at it) now serves:

  • / — a landing page: what Hawk Mod is, the enrollment link for mentors, and a link to the configuration page for admins. Same visual language as the existing enrollment pages.
  • /config — the web equivalent of /hawkmod config: every setting with its value and where it came from (Slack / env seed / unset), and a form to change each one. Changes are validated against Slack before storing, recorded in setting_changes under the signed-in admin's name, and a role-group change re-syncs the roster immediately.

Access control

  • Sign in with Slack (OpenID Connect) against the same Slack app — identity only: no scopes granted, no token stored.
  • Authority is then the same question every other entry point asks, answered by the same administrator() — workspace Owners and Admins, re-checked on every /config request. The cookie only says who; losing Slack admin locks the page within a minute.
  • Sessions and OAuth state are stateless HMAC tokens signed with the existing SLACK_STATE_SECRET, purpose-bound so a captured state parameter can never replay as a session cookie. Cookie is HttpOnly; SameSite=Lax; Secure, 8-hour lifetime, with an Origin check on POST.
  • Everything interpolated into HTML goes through esc() — setting values and display names are whatever their owner typed.

How

  • Setting validation/rendering extracted from commands.ts into src/slack/settingsAdmin.ts, shared by the slash command and the web page — one implementation, so a value one door refuses cannot be slipped in through the other.
  • src/web/{session,pages,routes}.ts; routes ride Bolt's customRoutes, no new dependencies.
  • New test/webSession.test.ts covers token round-trip, tampering, expiry, wrong secret, and purpose confusion.

Deployment note

The Slack app config needs the second redirect URL registered before sign-in works: https://mod.redhawkrobotics.org/auth/slack/callback (manifest updated). docs/deploy.md documents the pages and this requirement.

npm run typecheck && npm test && npm run format:check && npm run build all pass (112 tests).

🤖 Generated with Claude Code

mod.redhawkrobotics.org now shows a person something: a landing page at /
with the enrollment link, and /config, the web equivalent of
/hawkmod config. Sign-in is OpenID Connect against the same Slack app —
identity only, no scopes, no stored token — and authority is the same
question every entry point asks, answered by administrator() on every
request, so the cookie only says who is asking.

Setting validation moves to slack/settingsAdmin.ts, shared by the slash
command and the web page: one implementation, so a value one door
refuses cannot be slipped in through the other. Sessions and OAuth state
are stateless HMAC tokens signed with SLACK_STATE_SECRET, purpose-bound
so a captured state parameter can never pass as a session cookie.

The manifest gains a second redirect URL (/auth/slack/callback), which
has to be registered in the Slack app config before sign-in works.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@tytremblay
tytremblay merged commit d95b137 into main Aug 22, 2026
1 check passed
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