Skip to content

feat(auth): auth(apikey) helper with refresh-on-401 + TokenStore#1

Merged
mrmx merged 3 commits into
mainfrom
feat/auth-helper-phase3
May 26, 2026
Merged

feat(auth): auth(apikey) helper with refresh-on-401 + TokenStore#1
mrmx merged 3 commits into
mainfrom
feat/auth-helper-phase3

Conversation

@mrmx
Copy link
Copy Markdown
Contributor

@mrmx mrmx commented May 25, 2026

Summary

Phase 3 of openapi-auth-rollout. Adds the QTSurfer.auth(apikey) helper that wraps the api-client with automatic JWT refresh, env-var pickup, and a pluggable TokenStore.

  • QTSurfer.auth(String apikey) / QTSurfer.auth() / QTSurfer.auth(String, AuthOptions)AuthenticatedClient.
  • Optional apikey; falls back to QTSURFER_APIKEY env.
  • AuthenticatedClient mirrors the QTSurfer surface (compile, backtest, exchanges, instruments, tickers, klines) and adds refresh(), ensureToken(), clear(), token().
  • interface TokenStore { AuthTokenResponse load(); void save(AuthTokenResponse); void clear(); } with InMemoryTokenStore default (AtomicReference-backed).
  • Refresh-on-401: refresh JWT once, retry once. Second 401 propagates.

Notes for review

  • Base URL defaults to api.qtsurfer.com (prod target).
  • README leads with the auth(apikey) quickstart per goal spec.
  • Version 0.4.10.5.0.
  • Depends on com.qtsurfer:api-client 0.3.1 from Phase 2.

Test plan

  • 11 new unit (AuthenticatedClientTest) pass
  • 3 new integration (AuthHelperIntegrationTest) pass
  • 34/34 of the clean-run subset pass
  • Pre-existing Mockito-on-JDK-25 failures in BacktestWorkflowTest / DomainObjectsTest (not from this PR — Mockito inline mock maker vs newer JDK finals; reproduces on main)

mrmx added 3 commits May 25, 2026 21:56
… TokenStore

One-call setup for SDK adopters: `QTSurfer.auth(apikey)` exchanges a
long-lived API key for a short-lived JWT and returns an
`AuthenticatedClient` mirroring the existing `QTSurfer` workflow
surface (compile / backtest / exchanges / instruments / tickers /
klines), with transparent refresh-on-401 (refresh once, retry once,
surface the error otherwise).

  * Reads `QTSURFER_APIKEY` from the environment when no argument is
    passed; an explicit argument always wins.
  * Pluggable `TokenStore` interface (`load` / `save` / `clear`) with
    an in-memory default (`InMemoryTokenStore`). Adopters can plug in a
    file, a secret manager, or a desktop keychain.
  * `AuthOptions` configures base URL, token store, HTTP client, and
    executor. Defaults to `https://api.qtsurfer.com/v1` + in-memory store.
  * New `QTSAuthError` for missing-apikey and JWT-exchange failures.

Bumps `com.qtsurfer:api-client-java` to 0.3.1 (adds AuthApi,
AuthTokenResponse, AuthTokenError). Bumps sdk version to 0.5.0.

`DownloadFormat#wire()` is now `public` so the auth-session can pass
the underlying `ExchangeBinaryDownloads.Format` through without
re-encoding.

Tests: 11 new unit tests for the auth surface (explicit-apikey,
env-var resolution via the package-private overload, blank-apikey,
mint-401, store-save, store-seed, refresh-happy, refresh-fail,
non-401-passthrough, clear) plus 3 offline integration tests against
a local HttpServer (no live network).

Pre-existing Mockito-based tests (`BacktestWorkflowTest`,
`DomainObjectsTest`) fail on JDK 25 because Mockito's inline mock
maker can't modify generated `final` classes — unrelated to this PR;
reproducible on `origin/main`.
JitPack with the registered com.qtsurfer domain uses the GitHub repo
name as the artifactId, not pom <artifactId>. Renaming sdk → sdk-java
in the pom makes the published name (com.qtsurfer:sdk-java) match the
local mvn install name match the README snippets. Companion change in
api-client-java is in PR #2 on that repo (com.qtsurfer:api-client-java).
Matches the api-client-java approach: the badge above the snippet is
the source of truth for the latest published version on JitPack;
stamping a literal 0.5.0 forced a sync we kept losing.
@mrmx mrmx merged commit a191b19 into main May 26, 2026
4 of 5 checks passed
@mrmx mrmx deleted the feat/auth-helper-phase3 branch May 26, 2026 10:21
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