Skip to content

feat: site-wide access control with 5 auth modes#334

Open
fank wants to merge 16 commits intomainfrom
feat/access-control
Open

feat: site-wide access control with 5 auth modes#334
fank wants to merge 16 commits intomainfrom
feat/access-control

Conversation

@fank
Copy link
Member

@fank fank commented Mar 8, 2026

Summary

Site-wide access control for OCAP2-Web. Allows community operators to restrict who can view recordings via a single auth.mode config setting.

Auth Modes

  • public (default) — no restrictions, current behavior
  • password — shared viewer password, timing-safe comparison
  • steam — any Steam account can view
  • steamAllowlist — Steam login + admin-managed allowlist of Steam IDs stored in SQLite

What's included

  • requireViewer middleware gates recording list, metadata, data, and world endpoints
  • requireAdmin middleware gates management and allowlist API endpoints
  • Password login endpoint (POST /api/v1/auth/password) with mode guard
  • Auth config endpoint (GET /api/v1/auth/config) exposes mode to frontend
  • Steam allowlist CRUD: GET/PUT/DELETE /api/v1/auth/allowlist/{steamId} (admin only)
  • SQLite migration v11 for steam_allowlist table
  • Admin bypass: users in adminSteamIds always pass regardless of mode
  • Frontend: auth-gated UI with password form, Steam login, error messages, 401 redirect with return-to
  • Startup validation ensures required config values per mode

Config example

"auth": {
  "mode": "public",
  "sessionTTL": "24h",
  "adminSteamIds": ["76561198000074241"],
  "steamApiKey": "",
  "password": ""
}

Test plan

  • All Go tests pass (go test ./...)
  • All frontend tests pass (1516 tests, npx vitest run)
  • public mode: no auth required, existing behavior preserved
  • password mode: correct password → viewer JWT, wrong → 401, wrong mode → 404
  • steam mode: any Steam login → viewer JWT, admin IDs → admin JWT
  • steamAllowlist mode: allowed user → viewer JWT, denied → not_allowed error, admin bypasses
  • Allowlist CRUD: add (idempotent), list, remove via admin API
  • requireViewer middleware: passes in public, blocks unauthenticated in other modes
  • Manual test: end-to-end Steam OpenID flow with live instance

fank added 11 commits March 8, 2026 14:22
Covers five modes: public, password, steam, steamGroup, squadXml.
Builds on existing role-based auth foundation (PR #311).
9 tasks covering config, middleware, password login, Steam group check,
squad XML check, API exposure, and frontend auth-gated UI.
Add Mode, Password, SteamGroupID, SquadXmlURL, SquadXmlCacheTTL to the
Auth struct with viper defaults, env var bindings, and a
validateAuthConfig function that checks required fields per mode
(public, password, steam, steamGroup, squadXml).
Add requireViewer middleware that enforces site-wide access control:
in "public" mode all requests pass through, in other modes a valid
JWT is required. Apply it to recording list/detail, marker-blacklist,
worlds, and data endpoints via a viewer-gated route group.
Accepts {"password":"..."}, validates against auth.password config,
and issues a viewer JWT with "password" subject on success.
After Steam OpenID login succeeds in steamGroup mode, verify the user
belongs to the configured Steam group via ISteamUser/GetUserGroupList.
Admins bypass the check. Non-members are redirected with
auth_error=not_a_member; API failures redirect with
auth_error=membership_check_failed.
Implement squadXmlChecker that fetches a remote Arma 3 squad.xml,
parses member Steam IDs, and caches the result with configurable TTL
(0 = always refetch). Admins bypass the check. Integrated into the
SteamCallback flow alongside the existing steamGroup check.
The frontend needs to know the current auth mode to show appropriate
login controls (password field vs Steam button vs nothing). This adds
a public endpoint that returns the configured mode as JSON.
Add getAuthConfig() and passwordLogin() methods to ApiClient. Include
JWT auth headers in fetchJson/fetchBuffer so viewer-gated endpoints
work in non-public modes. On 401 responses, save the current path and
redirect to root for login.
Add authMode signal and loginWithPassword action to useAuth provider.
AuthBadge shows password form in password mode alongside Steam button.
Auth errors (not_a_member, membership_check_failed) are mapped to
user-friendly messages. Fix RecordingSelector test to scope text query
within auth-toast to avoid duplicate match.
- Use crypto/subtle.ConstantTimeCompare for password validation
- Reject password login requests when auth.mode is not "password"
- Add test for wrong-mode rejection
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a comprehensive site-wide access control system, empowering community operators to define precisely who can view recordings. By offering multiple authentication modes, from simple password protection to advanced Steam group and squad XML membership verification, it significantly enhances the security and flexibility of content access. The changes span both backend logic, including new middleware and API endpoints, and frontend UI, ensuring a seamless and intuitive user experience for authentication.

Highlights

  • Site-Wide Access Control: Implemented a configurable site-wide access control system with five distinct authentication modes: public, password, steam, steamGroup, and squadXml. This allows community operators to restrict who can view recordings.
  • Backend Middleware and API: Introduced a requireViewer middleware to gate access to recording and data endpoints. New backend endpoints were added for password-based login, and Steam callback logic was extended to include Steam group and Arma 3 squad XML membership checks.
  • Frontend Integration: The frontend AuthBadge component was updated to dynamically display appropriate login controls based on the configured authentication mode. A global 401 handler was added to redirect unauthenticated users to the login page, preserving their intended destination.
  • Configuration and Validation: Extended the auth configuration with new fields for mode-specific settings (e.g., password, steamGroupId, squadXmlUrl, squadXmlCacheTTL). Robust startup validation ensures that all required configuration fields are present for the active authentication mode.
  • Security and Admin Bypass: Password comparison uses crypto/subtle for timing-safe operations. An admin bypass mechanism ensures that users listed in adminSteamIds can always access the site, preventing accidental lockouts.
Changelog
  • docs/plans/2026-03-08-access-control-design.md
    • Added a new design document detailing the architecture and flows for the access control system.
  • docs/plans/2026-03-08-access-control-impl.md
    • Added a new implementation plan outlining the step-by-step tasks for developing the access control feature.
  • internal/server/handler.go
    • Added a squadXmlChecker field to the Handler struct.
    • Initialized the squadXmlChecker in NewHandler if squadXml authentication mode is active.
    • Modified route registration to apply the new requireViewer middleware to recording and data endpoints.
    • Registered new API routes for /api/v1/auth/config and /api/v1/auth/password.
  • internal/server/handler_auth.go
    • Added crypto/subtle, encoding/xml, and sync to imports.
    • Implemented the requireViewer middleware to enforce site-wide access control.
    • Implemented the PasswordLogin handler for password-based authentication, using timing-safe comparison.
    • Integrated Steam group membership checks into the SteamCallback handler.
    • Integrated Arma 3 squad XML membership checks with caching into the SteamCallback handler.
    • Added GetAuthConfig handler to expose the current authentication mode to the frontend.
    • Defined steamGroupListResponse and squadXmlChecker types and their associated logic for membership verification.
  • internal/server/handler_auth_test.go
    • Added new tests for the requireViewer middleware, covering public, unauthenticated, viewer, and admin access scenarios.
    • Added comprehensive tests for the PasswordLogin handler, including correct/wrong password, empty password, invalid JSON, and incorrect mode scenarios.
    • Added unit tests for checkSteamGroupMembership covering member/non-member status, API errors, invalid JSON, and connection errors.
    • Added integration tests for SteamCallback in steamGroup mode, verifying token issuance for members, error for non-members, admin bypass, and API failures.
    • Added unit tests for squadXmlChecker, covering member/non-member status, caching behavior, zero TTL, HTTP errors, invalid XML, and connection errors.
    • Added integration tests for SteamCallback in squadXml mode, verifying token issuance for members, error for non-members, admin bypass, and fetch failures.
    • Added tests for GetAuthConfig to ensure it returns the correct authentication mode.
  • internal/server/handler_test.go
    • Updated TestNewHandler to include Auth settings in the default configuration.
  • internal/server/setting.go
    • Extended the Auth struct with Mode, Password, SteamGroupID, SquadXmlURL, and SquadXmlCacheTTL fields.
    • Set default values for the new authentication configuration fields.
    • Updated environment variable bindings to include the new auth-related settings.
    • Implemented validateAuthConfig function to perform startup validation of authentication settings based on the active mode.
    • Called validateAuthConfig during NewSetting initialization to ensure configuration integrity.
  • internal/server/setting_test.go
    • Added tests for validateAuthConfig to verify correct validation of all authentication modes and their required fields.
    • Added tests for NewSetting to confirm default auth mode and cache TTL, handle invalid auth modes, and correctly load password mode settings.
  • setting.json.example
    • Updated the example configuration file to include the new auth.mode, auth.password, auth.steamGroupId, auth.squadXmlUrl, and auth.squadXmlCacheTTL fields.
  • ui/src/components/AuthBadge.module.css
    • Added new CSS styles for authentication controls, including password input forms, submit buttons, and error message displays.
  • ui/src/components/AuthBadge.tsx
    • Modified the AuthBadge component to conditionally render password input fields or Steam login buttons based on the authMode.
    • Integrated password state and loading indicators for the password login form.
    • Connected to loginWithPassword and authError from the useAuth hook to handle password authentication and display errors.
  • ui/src/components/tests/AuthBadge.test.tsx
    • Updated mock useAuth to include authMode and loginWithPassword.
    • Added tests to verify AuthBadge displays correctly in password, steam, and public modes.
    • Added tests for password form submission and error message display and dismissal.
  • ui/src/data/tests/apiClient.test.ts
    • Added tests for getAuthConfig to verify fetching the authentication mode and defaulting to public on error.
    • Added tests for passwordLogin to verify token storage on success and error handling for invalid passwords or other failures.
    • Added tests to confirm auth headers are included in fetchJson and fetchBuffer calls for viewer-gated endpoints when a token is present.
    • Added tests for global 401 handling in fetchJson and fetchBuffer, ensuring return path is saved and redirect occurs.
  • ui/src/data/apiClient.ts
    • Defined the AuthConfig interface for the authentication mode response.
    • Implemented getAuthConfig to retrieve the current authentication mode from the backend.
    • Implemented passwordLogin to send password credentials and store the received JWT.
    • Modified fetchJson and fetchBuffer methods to automatically include authentication headers and handle 401 responses by redirecting to the login page with a saved return path.
  • ui/src/hooks/tests/useAuth.test.tsx
    • Updated mocks for apiClient to include getAuthConfig and passwordLogin.
    • Added tests to verify authMode is correctly initialized and updated from server configuration.
    • Added tests for loginWithPassword success and failure scenarios.
    • Added tests to confirm correct mapping of specific auth_error query parameters to user-friendly messages.
  • ui/src/hooks/useAuth.tsx
    • Extended the Auth interface to include authMode and loginWithPassword.
    • Added authMode state and initialized it by fetching the configuration on mount.
    • Implemented the loginWithPassword function to handle password authentication.
    • Expanded AUTH_ERROR_MESSAGES to include new error types for Steam group and squad XML membership checks.
  • ui/src/pages/recording-selector/tests/RecordingSelector.test.tsx
    • Updated an assertion in TestRecordingSelector_AuthErrorFromURL to correctly target the auth toast content.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@github-actions
Copy link

github-actions bot commented Mar 8, 2026

Coverage Report for ui

Status Category Percentage Covered / Total
🔵 Lines 98.88%
⬇️ -0.15%
4896 / 4951
🔵 Statements 98.33%
⬇️ -0.11%
6802 / 6917
🔵 Functions 97.69%
⬇️ -0.14%
1911 / 1956
🔵 Branches 89.05%
⬇️ -0.11%
2254 / 2531
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
ui/src/components/AuthBadge.tsx 98.85%
⬇️ -1.15%
91.3%
⬇️ -1.00%
100%
🟰 ±0%
100%
🟰 ±0%
22
ui/src/data/apiClient.ts 93.47%
⬇️ -3.96%
89.36%
⬇️ -2.74%
91.83%
⬇️ -5.89%
94.28%
⬇️ -4.35%
508-545, 594-595
ui/src/hooks/useAuth.tsx 100%
🟰 ±0%
85%
⬇️ -1.66%
100%
🟰 ±0%
100%
🟰 ±0%
Generated in workflow #428 for commit 88746b4 by the Vitest Coverage Report Action

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a comprehensive site-wide access control system with five distinct authentication modes. The changes are extensive, touching backend configuration, middleware, API handlers, and frontend components to support the new authentication flows. The implementation is well-structured and includes a significant number of new tests, which is excellent. My review includes suggestions to improve the robustness of the testing setup, clarify some parts of the implementation plan documents, and address a potential issue with how test overrides for external APIs are handled to enhance future maintainability.

Note: Security Review did not run due to the size of the PR.

fank added 4 commits March 8, 2026 16:25
Add a new auth mode "steamAllowlist" where admins manage a list of
allowed Steam IDs via API. Only allowed Steam IDs (plus admins) can
log in when this mode is active.

- Migration v11: creates steam_allowlist table
- New allowlist.go with CRUD methods (Get, Add, Remove, IsOn)
- Admin API endpoints: GET/PUT/DELETE /api/v1/auth/allowlist
- SteamCallback checks allowlist before issuing JWT (admins bypass)
- Tests for CRUD, handler endpoints, and callback behavior
- Add not_allowed error message for denied allowlist users
- Add allowlist CRUD methods to ApiClient (admin use)
- Add AuthBadge test for steamAllowlist mode
- Remove obsolete steamGroup/squadXml error messages
@fank
Copy link
Member Author

fank commented Mar 8, 2026

/gemini review

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a comprehensive site-wide access control feature with multiple authentication modes. While the implementation of the requireViewer middleware and various authentication flows appears solid, critical vulnerabilities were identified in the administrative access control logic. Specifically, the requireAdmin middleware and the admin check in StoreOperation fail to validate JWT signatures before trusting any claims, which could allow an attacker to forge administrative tokens. It is strongly recommended to ensure all JWT-protected endpoints call Validate() before trusting any claims. Additionally, a resource leak in the test suite was identified, with a suggestion provided to ensure proper database connection closure and error checking, aligning with best practices for test reliability. Finally, the implementation plan documentation should be updated to reflect the actual implemented authentication modes.

@github-actions
Copy link

github-actions bot commented Mar 8, 2026

Merging this branch will decrease overall coverage

Impacted Packages Coverage Δ 🤖
github.com/OCAP2/web/internal/server 93.88% (-0.64%) 👎

Coverage by file

Changed files (no unit tests)

Changed File Coverage Δ Total Covered Missed 🤖
github.com/OCAP2/web/internal/server/allowlist.go 88.89% (+88.89%) 18 (+18) 16 (+16) 2 (+2) 🌟
github.com/OCAP2/web/internal/server/handler.go 96.40% (+0.09%) 278 (+7) 268 (+7) 10 👍
github.com/OCAP2/web/internal/server/handler_auth.go 88.89% (-3.77%) 162 (+53) 144 (+43) 18 (+10) 👎
github.com/OCAP2/web/internal/server/operation.go 88.94% (-0.34%) 199 (+3) 177 (+2) 22 (+1) 👎
github.com/OCAP2/web/internal/server/setting.go 94.44% (+1.00%) 72 (+11) 68 (+11) 4 👍

Please note that the "Total", "Covered", and "Missed" counts above refer to code statements instead of lines of code. The value in brackets refers to the test coverage of that file in the old version of the code.

Changed unit test files

  • github.com/OCAP2/web/internal/server/handler_auth_test.go
  • github.com/OCAP2/web/internal/server/handler_test.go
  • github.com/OCAP2/web/internal/server/operation_test.go
  • github.com/OCAP2/web/internal/server/setting_test.go

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