docs(swagger)+test(rest): document /auth/refresh + /auth/logout, add HTTP smoke tests #12

Merged
nsatoshi merged 1 commits from devin/1776541136-docs-auth-refresh-logout-followups into master 2026-04-18 19:41:50 +00:00
Owner

Summary

Follow-up to PR #8 (JWT revocation + refresh), addressing two of the three in-scope follow-ups called out on PR #11:

  1. swagger.yaml pre-dated /api/v1/auth/refresh and /api/v1/auth/logout — client generators couldn't pick them up.
  2. Those handlers were covered by unit tests on the WalletAuth layer and by the make e2e-full Playwright spec, but had no HTTP-level unit tests — regressions at the mux/handler seam (wrong method, missing walletAuth, unregistered route) were invisible to go test ./backend/api/rest.

Changes

backend/api/rest/swagger.yaml

  • New POST /api/v1/auth/refresh entry under the Auth tag. Uses bearerAuth, returns the existing WalletAuthResponse on 200, 401 via components/responses/Unauthorized, 503 when auth storage or jwt_revocations (migration 0016) is missing. Description calls out that legacy tokens without a jti cannot be refreshed.
  • New POST /api/v1/auth/logout entry. Same auth requirement; returns {status: ok} on 200; 401 via Unauthorized; 503 when migration 0016 hasn't run. Description names the jwt_revocations table so ops can correlate 503s with the migration.
  • Both slot in between /auth/wallet and /auth/register so the tag block stays ordered.

backend/api/rest/auth_refresh_internal_test.go (new — 7 tests)

  • TestHandleAuthRefreshRejectsGet — GET returns 405 method_not_allowed.
  • TestHandleAuthRefreshReturns503WhenWalletAuthUnconfiguredwalletAuth nil, POST with a Bearer header returns 503 rather than panicking.
  • TestHandleAuthLogoutRejectsGet — symmetric 405 on GET.
  • TestHandleAuthLogoutReturns503WhenWalletAuthUnconfigured — symmetric 503.
  • TestAuthRefreshRouteRegistered — exercises SetupRoutes and confirms POST /api/v1/auth/refresh and /api/v1/auth/logout are registered (not 404). Catches regressions where a future refactor drops the mux.HandleFunc entries.
  • TestAuthRefreshRequiresBearerToken + TestAuthLogoutRequiresBearerToken — a POST with no Authorization resolves to 401 or 503 (never 200 or 500).
  • decodeErrorBody helper extracts ErrorDetail from writeError's {"error":{"code":...,"message":...}} envelope so asserts match the actual wire format.
  • newServerNoWalletAuth builds a rest.Server with JWT_SECRET set to a 32-byte string so PR #3's fail-fast check is satisfied; nil db pool is fine because the tests don't touch any DB path.

Verification

  • cd backend && go vet ./... — clean.
  • cd backend && go test ./api/rest/ — pass.
  • cd backend && go test ./... — pass.

Out of scope

The live credential rotation follow-up (rotate the L@ker?s?$?2010 passwords in infra) needs database + SSH + deploy-pipeline access and belongs to the operator. docs/SECURITY.md (added in PR #3) lists every asset to rotate.

## Summary Follow-up to [PR #8](https://gitea.d-bis.org/d-bis/explorer-monorepo/pulls/8) (JWT revocation + refresh), addressing two of the three in-scope follow-ups called out on [PR #11](https://gitea.d-bis.org/d-bis/explorer-monorepo/pulls/11): 1. `swagger.yaml` pre-dated `/api/v1/auth/refresh` and `/api/v1/auth/logout` — client generators couldn't pick them up. 2. Those handlers were covered by unit tests on the `WalletAuth` layer and by the `make e2e-full` Playwright spec, but had **no HTTP-level unit tests** — regressions at the mux/handler seam (wrong method, missing `walletAuth`, unregistered route) were invisible to `go test ./backend/api/rest`. ## Changes ### `backend/api/rest/swagger.yaml` - New `POST /api/v1/auth/refresh` entry under the `Auth` tag. Uses `bearerAuth`, returns the existing `WalletAuthResponse` on 200, 401 via `components/responses/Unauthorized`, 503 when auth storage or `jwt_revocations` (migration 0016) is missing. Description calls out that legacy tokens without a `jti` cannot be refreshed. - New `POST /api/v1/auth/logout` entry. Same auth requirement; returns `{status: ok}` on 200; 401 via `Unauthorized`; 503 when migration 0016 hasn't run. Description names the `jwt_revocations` table so ops can correlate 503s with the migration. - Both slot in between `/auth/wallet` and `/auth/register` so the tag block stays ordered. ### `backend/api/rest/auth_refresh_internal_test.go` (new — 7 tests) - `TestHandleAuthRefreshRejectsGet` — GET returns 405 `method_not_allowed`. - `TestHandleAuthRefreshReturns503WhenWalletAuthUnconfigured` — `walletAuth` nil, POST with a Bearer header returns 503 rather than panicking. - `TestHandleAuthLogoutRejectsGet` — symmetric 405 on GET. - `TestHandleAuthLogoutReturns503WhenWalletAuthUnconfigured` — symmetric 503. - `TestAuthRefreshRouteRegistered` — exercises `SetupRoutes` and confirms `POST /api/v1/auth/refresh` and `/api/v1/auth/logout` are registered (not 404). Catches regressions where a future refactor drops the `mux.HandleFunc` entries. - `TestAuthRefreshRequiresBearerToken` + `TestAuthLogoutRequiresBearerToken` — a POST with no `Authorization` resolves to 401 or 503 (never 200 or 500). - `decodeErrorBody` helper extracts `ErrorDetail` from `writeError`'s `{"error":{"code":...,"message":...}}` envelope so asserts match the actual wire format. - `newServerNoWalletAuth` builds a `rest.Server` with `JWT_SECRET` set to a 32-byte string so PR #3's fail-fast check is satisfied; nil db pool is fine because the tests don't touch any DB path. ## Verification - `cd backend && go vet ./...` — clean. - `cd backend && go test ./api/rest/` — pass. - `cd backend && go test ./...` — pass. ## Out of scope The **live credential rotation** follow-up (rotate the `L@ker?s?$?2010` passwords in infra) needs database + SSH + deploy-pipeline access and belongs to the operator. `docs/SECURITY.md` (added in PR #3) lists every asset to rotate.
nsatoshi added 1 commit 2026-04-18 19:41:42 +00:00
docs(swagger)+test(rest): document /auth/refresh + /auth/logout, add HTTP smoke tests
Some checks failed
CI / Backend (go 1.23.x) (pull_request) Successful in 52s
CI / Backend security scanners (pull_request) Failing after 39s
CI / Frontend (node 20) (pull_request) Successful in 2m13s
CI / gitleaks (secret scan) (pull_request) Failing after 8s
e2e-full / e2e-full (pull_request) Has been skipped
7bd119228c
Follow-up to PR #8 (JWT revocation + refresh), addressing the two
in-scope follow-ups called out in the completion-sequence summary on
PR #11:

  1. swagger.yaml pre-dated /api/v1/auth/refresh and /api/v1/auth/logout
     - client generators could not pick them up.
  2. Those handlers were covered by unit tests on the WalletAuth layer
     and by the e2e-full Playwright spec, but had no HTTP-level unit
     tests - regressions at the mux/handler seam (wrong method,
     missing walletAuth, unregistered route) were invisible to
     go test ./backend/api/rest.

Changes:

backend/api/rest/swagger.yaml:
  - New POST /api/v1/auth/refresh entry under the Auth tag.
    Uses bearerAuth, returns the existing WalletAuthResponse on 200,
    401 via components/responses/Unauthorized, 503 when the auth
    storage or the jwt_revocations table from migration 0016 is
    missing. Description calls out that legacy tokens without a jti
    cannot be refreshed.
  - New POST /api/v1/auth/logout entry. Same auth requirement;
    returns {status: ok} on 200; 401 via Unauthorized; 503 when
    migration 0016 has not run. Description names the jwt_revocations
    table explicitly so ops can correlate 503s with the migration.
  - Both slot in alphabetically between /auth/wallet and /auth/register
    so the tag block stays ordered.

backend/api/rest/auth_refresh_internal_test.go (new, 8 tests):
  - TestHandleAuthRefreshRejectsGet - GET returns 405 method_not_allowed.
  - TestHandleAuthRefreshReturns503WhenWalletAuthUnconfigured -
    walletAuth nil, POST with a Bearer header returns 503 rather
    than panicking (guards against a regression where someone calls
    s.walletAuth.RefreshJWT without the nil-check).
  - TestHandleAuthLogoutRejectsGet   - symmetric 405 on GET.
  - TestHandleAuthLogoutReturns503WhenWalletAuthUnconfigured -
    symmetric 503 on nil walletAuth.
  - TestAuthRefreshRouteRegistered - exercises SetupRoutes and
    confirms POST /api/v1/auth/refresh and /api/v1/auth/logout are
    registered (i.e. not 404). Catches regressions where a future
    refactor drops the mux.HandleFunc entries for either endpoint.
  - TestAuthRefreshRequiresBearerToken +
    TestAuthLogoutRequiresBearerToken - sanity-check that a POST
    with no Authorization header resolves to 401 or 503 (never 200
    or 500).
  - decodeErrorBody helper extracts ErrorDetail from writeError's
    {"error":{"code":...,"message":...}} envelope, so asserts
    on body["code"] match the actual wire format (not the looser
    {"error":"..."} shape).
  - newServerNoWalletAuth builds a rest.Server with JWT_SECRET set
    to a 32-byte string of 'a' so NewServer's fail-fast check from
    PR #3 is happy; nil db pool is fine because the tests do not
    exercise any DB path.

Verification:
  cd backend && go vet ./...             clean
  cd backend && go test ./api/rest/      pass (17 tests; 7 new)
  cd backend && go test ./...            pass

Out of scope: the live credential rotation in the third follow-up
bullet requires infra access (database + SSH + deploy pipeline) and
belongs to the operator.
nsatoshi merged commit 7a684d010c into master 2026-04-18 19:41:50 +00:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: d-bis/explorer-monorepo#12