mirror of
https://github.com/DeBrosOfficial/network.git
synced 2025-10-06 12:29:07 +00:00
136 lines
7.9 KiB
Markdown
136 lines
7.9 KiB
Markdown
# Task: Enforce API Key/JWT and Namespace in Go Client (Auto-Resolve Namespace) and Guard All Operations
|
|
|
|
Owner: To be assigned
|
|
Status: Ready to implement
|
|
|
|
## Objective
|
|
Implement strict client-side access enforcement in the Go client (`pkg/client`) so that:
|
|
- An API key or JWT is required by default to use the client.
|
|
- The client auto-resolves the namespace from the provided API key or JWT without requiring callers to pass the namespace per call.
|
|
- Per-call namespace overrides via context are still allowed for compatibility, but must match the resolved namespace; otherwise, deny the call.
|
|
- All operations (Storage, PubSub, Database/RQLite, and NetworkInfo) are guarded and return access errors when unauthenticated or namespace-mismatched.
|
|
- No backward compatibility guarantees required.
|
|
|
|
Note: This is client-side enforcement for now. Protocol-level auth/ACL for libp2p can be added later.
|
|
|
|
## High-level behavior
|
|
- `ClientConfig.RequireAPIKey` defaults to true. If true and neither `APIKey` nor `JWT` is present, `Connect()` fails.
|
|
- Namespace is automatically derived:
|
|
- From JWT: parse claims and read `Namespace` claim (no network roundtrip). Verification of signature is not required for this task; parsing is enough to derive namespace. Optionally, add a TODO hook for future verification against JWKS if provided.
|
|
- From API key: the namespace must be embedded in the key using a documented format (below). The client parses it locally and derives the namespace without any remote calls.
|
|
- All calls check that any provided per-call namespace override matches the derived namespace, else return an “access denied: namespace mismatch” error.
|
|
- All modules are guarded: Database (RQLite), Storage, PubSub, and NetworkInfo.
|
|
|
|
## API key and JWT formats
|
|
- JWT: RS256 token with claim `Namespace` (string). We will parse claims (unverified) to obtain `Namespace`.
|
|
- API key: change to an encoded format that includes the namespace so the client can parse locally. Options (pick one and implement consistently):
|
|
- Option A (dotted): `ak_<random>.<namespace>`
|
|
- Option B (colon): `ak_<random>:<namespace>`
|
|
- Option C (base64 JSON): base64url of `{ "kid": "...", "ns": "<namespace>" }` prefixed by `ak_`
|
|
|
|
For simplicity and readability, choose Option B: `ak_<random>:<namespace>`.
|
|
- Parsing rules:
|
|
- If `APIKey` contains a single colon, split and use the right side as `namespace` (trim spaces). If empty -> error.
|
|
- If more than one colon or invalid format -> error.
|
|
|
|
## Changes to implement
|
|
|
|
### 1) Client configuration and types
|
|
- File: `pkg/client/interface.go`
|
|
- Extend `ClientConfig`:
|
|
- `Namespace string` // optional; if empty, auto-derived from API key or JWT; if still empty, fallback to `AppName`.
|
|
- `RequireAPIKey bool` // default true; when true, require either `APIKey` or `JWT`.
|
|
- `JWT string` // optional bearer token; used for namespace derivation and future protocol auth.
|
|
- Update `DefaultClientConfig(appName string)` to set:
|
|
- `RequireAPIKey: true`
|
|
- `Namespace: ""` (meaning auto)
|
|
|
|
### 2) Namespace resolution and access gating
|
|
- File: `pkg/client/client.go`
|
|
- At construction or `Connect()` time:
|
|
- Implement `deriveNamespace()`:
|
|
- If `config.Namespace != ""`, use it.
|
|
- Else if `config.JWT != ""`, parse JWT claims (unverified) and read `Namespace` claim.
|
|
- Else if `config.APIKey != ""`, parse `ak_<random>:<namespace>` and extract namespace.
|
|
- Else use `config.AppName`.
|
|
- Store the resolved namespace back into `config.Namespace`.
|
|
- Enforce presence of credentials:
|
|
- If `config.RequireAPIKey` is true AND both `config.APIKey` and `config.JWT` are empty -> return error `access denied: API key or JWT required`.
|
|
- Add `func (c *Client) requireAccess(ctx context.Context) error` that:
|
|
- If `RequireAPIKey` and both `APIKey` and `JWT` are empty -> error `access denied: credentials required`.
|
|
- Resolve per-call namespace override from context (via storage/pubsub helpers below). If present and `override != c.config.Namespace` -> error `access denied: namespace mismatch`.
|
|
|
|
### 3) Guard all operations
|
|
- File: `pkg/client/implementations.go`
|
|
- At the start of each public method, call `client.requireAccess(ctx)` and return the error if any.
|
|
- DatabaseClientImpl: `Query`, `Transaction`, `CreateTable`, `DropTable`, `GetSchema`.
|
|
- StorageClientImpl: `Get`, `Put`, `Delete`, `List`, `Exists`.
|
|
- NetworkInfoImpl: `GetPeers`, `GetStatus`, `ConnectToPeer`, `DisconnectFromPeer`.
|
|
- For Storage operations, ensure we propagate the effective namespace:
|
|
- If override present and equals `config.Namespace`, pass that context through; else use `storage.WithNamespace(ctx, config.Namespace)`.
|
|
|
|
### 4) PubSub context-based namespace override (parity with Storage)
|
|
- Files: `pkg/pubsub/*`
|
|
- Add:
|
|
- `type ctxKey string`
|
|
- `const CtxKeyNamespaceOverride ctxKey = "pubsub_ns_override"`
|
|
- `func WithNamespace(ctx context.Context, ns string) context.Context`
|
|
- Update topic naming in `manager.go` and `subscriptions.go`/`publish.go`:
|
|
- Before computing `namespacedTopic`, check for ctx override; if present and non-empty, use it; else fall back to `m.namespace`.
|
|
|
|
### 5) Client context helper
|
|
- New file: `pkg/client/context.go`
|
|
- Add `func WithNamespace(ctx context.Context, ns string) context.Context` that applies both storage and pubsub overrides by chaining:
|
|
- `ctx = storage.WithNamespace(ctx, ns)`
|
|
- `ctx = pubsub.WithNamespace(ctx, ns)`
|
|
- return `ctx`
|
|
|
|
### 6) Documentation updates
|
|
- Files: `README.md`, `AI_CONTEXT.md`
|
|
- Document the new client auth behavior:
|
|
- An API key or JWT is required by default (`RequireAPIKey=true`).
|
|
- Namespace auto-derived from token:
|
|
- JWT claim `Namespace`.
|
|
- API key format `ak_<random>:<namespace>`.
|
|
- Per-call override via `client.WithNamespace(ctx, ns)` allowed but must match derived namespace.
|
|
- All modules (Storage, PubSub, Database, NetworkInfo) are guarded.
|
|
- Provide usage examples for constructing `ClientConfig` with API key or JWT and making calls.
|
|
|
|
## Helper details
|
|
- JWT parsing: implement a minimal helper to split the token and base64url-decode the payload; read `Namespace` field from JSON. Do not verify signature for this task. If parsing fails, return a clear error.
|
|
- API key parsing: simple split on `:`; trim spaces; validate non-empty.
|
|
|
|
## Error messages (standardize)
|
|
- Missing credentials: `access denied: API key or JWT required`
|
|
- Namespace mismatch: `access denied: namespace mismatch`
|
|
- Client not connected: keep existing `client not connected` error.
|
|
|
|
## Acceptance criteria
|
|
- Without credentials and `RequireAPIKey=true`, `Connect()` returns error and no operations are allowed.
|
|
- With API key `ak_abc123:myapp`, the client auto-resolves namespace `myapp`; operations succeed.
|
|
- With JWT containing `{ "Namespace": "myapp" }`, the client auto-resolves `myapp`; operations succeed.
|
|
- If a caller sets `client.WithNamespace(ctx, "otherNS")` while resolved namespace is `myapp`, any operation returns `access denied: namespace mismatch`.
|
|
- PubSub topic names use the override when present (and allowed) else the resolved namespace.
|
|
- NetworkInfo methods are also guarded and require credentials.
|
|
|
|
## Out of scope (for this task)
|
|
- Protocol-level auth or verification of JWT signatures against JWKS.
|
|
- ETH payments/subscriptions and tier enforcement. (Separate design/implementation.)
|
|
|
|
## Files to modify/add
|
|
- Modify:
|
|
- `pkg/client/interface.go`
|
|
- `pkg/client/client.go`
|
|
- `pkg/client/implementations.go`
|
|
- `pkg/pubsub/manager.go`
|
|
- `pkg/pubsub/subscriptions.go`
|
|
- `pkg/pubsub/publish.go` (if exists; add override resolution there too)
|
|
- `README.md`, `AI_CONTEXT.md`
|
|
- Add:
|
|
- `pkg/pubsub/context.go` (if not present)
|
|
- `pkg/client/context.go`
|
|
|
|
## Notes
|
|
- Keep logs concise and avoid leaking tokens in logs. You may log the resolved namespace at `INFO` level on connect.
|
|
- Ensure thread-safety when accessing `Client.config` fields (use existing locks if needed).
|