Skip to content

Add scoped API key auth for Gateway gRPC#166

Merged
PurpleTheBest merged 4 commits into
devfrom
claude/nervous-chandrasekhar-41745d
May 6, 2026
Merged

Add scoped API key auth for Gateway gRPC#166
PurpleTheBest merged 4 commits into
devfrom
claude/nervous-chandrasekhar-41745d

Conversation

@PurpleTheBest
Copy link
Copy Markdown
Contributor

Summary

  • Bearer-token auth on SolverService + ExecutionService via opaque tsk_<env>_<random> tokens (~52 chars).
  • Scopes: read, reveal, wallet.execute:{address}, wallet.execute:*.
  • Token prefix is a hard environment scope (not a label) — tsk_test_* keys can only operate on testnet networks, tsk_live_* only on mainnet. GetRoutes/GetQuote filter or reject on env mismatch; SubmitTransaction/RevealSecret/GetOrder/GetTransactionStatus reject.
  • DB stores SHA-256(token); raw value is shown once at creation, never retrievable afterwards.
  • In-memory cache with absolute 60s TTL — revocation propagates within one window.
  • AdminAPI CRUD at /api/api-keys, AdminPanel page at /api-keys with one-time token display.
  • Full design notes in .claude/rules/api-key-auth.md.

Components

  • Data: ApiKey entity, IApiKeyRepository/EFApiKeyRepository, DbSet<ApiKey> with unique index on KeyHash.
  • Service: IApiKeyService/ApiKeyService (transient), singleton ApiKeyCache. Token gen via RandomNumberGenerator.GetBytes(32) + base64url. SHA-256 digest stored.
  • gRPC: ApiKeyAuthInterceptor registered globally on AddGrpc. ApiKeyContextExtensions (RequireScope, RequireWalletExecute, RequireMatchingEnv) called per method.
  • AdminPanel: ApiKeys.razor with create/view/revoke/delete and a one-time copy modal for the raw token.

DB migration

Schema change adds ApiKeys table with unique indexes on KeyHash and Name. EF migration left for the user to generate per project convention.

Test plan

  • dotnet ef migrations add AddApiKeys and apply.
  • Create a tsk_test_* key with read scope via AdminPanel → call GetRoutes → verify only testnet routes returned.
  • Same key call GetQuote with mainnet networks → expect PermissionDenied.
  • Create a tsk_live_* key with wallet.execute:0xABC... → call SubmitTransaction for that wallet on a mainnet network → succeeds.
  • Same key call SubmitTransaction for a testnet network → expect PermissionDenied.
  • Revoke a key, confirm rejection within 60s (cache window).

🤖 Generated with Claude Code

PurpleTheBest and others added 4 commits May 6, 2026 17:48
Bearer-token auth on SolverService and ExecutionService via tsk_<env>_<random>
opaque tokens. Scopes: read, reveal, wallet.execute:{address}, wallet.execute:*.
Token prefix is a hard environment scope (test vs live) — test keys can only
operate on testnet networks; GetRoutes/GetQuote filter or reject on env mismatch.

Stores SHA-256 of the token; raw value is shown once at creation. In-memory
60s absolute TTL cache so revocation propagates within one window.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
EF migration creates the ApiKeys table with unique indexes on KeyHash and Name.

Auth header swapped from `Authorization: Bearer` to `X-API-Key` (token sent
as the raw header value, no prefix) — matches Stripe/Twilio/AWS convention
and avoids confusion with OAuth/JWT auth flows.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Header constant updated in interceptor + docs. StationAPI's SolverConfig
gains an optional ApiKey field (read from station-config.json) and a
client-side gRPC interceptor (ApiKeyClientInterceptor) that injects
X-Train-API-Key on every outgoing solver call. Optional for backward
compat with solvers that haven't enabled auth yet.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…sekhar-41745d

# Conflicts:
#	csharp/src/StationAPI/Configuration/StationConfig.cs
#	csharp/src/StationAPI/station-config.json
@PurpleTheBest PurpleTheBest merged commit d55f132 into dev May 6, 2026
9 checks 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