Skip to content

feat: partner mode — one partner API key for every managed tenant - #65

Open
asachs01 wants to merge 5 commits into
mainfrom
feat/partner-mode
Open

asachs01 wants to merge 5 commits into
mainfrom
feat/partner-mode

Conversation

@asachs01

@asachs01 asachs01 commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Summary

MSPs currently need one KnowBe4 Reporting API key per customer tenant. This adds partner mode: one partner Product API key drives every managed tenant through KnowBe4's partner GraphQL API and per-tenant Just-In-Time (JIT) tokens.

  • KNOWBE4_PARTNER_API_KEY (env) / X-KnowBe4-Partner-API-Key (gateway). KNOWBE4_API_KEY becomes optional; gateway mode accepts either key or both.
  • New partner domain: knowbe4_partner_accounts_list (search/status/pagination, returns each tenant's risk score, phish-prone %, % trained, seats, subscription end) and knowbe4_partner_account_get.
  • Every tenant tool gains an optional account_id. When set, the call runs over the tenant GraphQL API with a JIT token from apiTokensGenerateJit(accountId), cached 14 min (documented lifetime 15 min), keyed by sha256(partnerKey)[:16]:accountId so partners sharing a gateway process never share tokens. Concurrent calls share one mint; a 401 evicts the cached token. JWTs are never logged.
  • Without account_id the REST path is byte-for-byte unchanged. account_id has no REST fallback: unconfigured partner mode returns an error instead of answering from the wrong tenant.
  • GraphQL equivalents of all 27 tenant tools in src/graphql/<domain>.ts, same tool names / args / validation messages. Selections kept small for KnowBe4's 150-line complexity cap; page sizes clamped to 25..1000.

Also fixes a latent dispatch bug: knowbe4_store_* and knowbe4_policies_* were unreachable in non-lazy mode (prefix routing only knew six prefixes). Routing now uses the category map.

Design notes: docs/superpowers/specs/2026-09-09-partner-mode-design.md.

Verification

  • All GraphQL field/arg/enum names were validated against KnowBe4's published schema, fetched by introspection from https://training.knowbe4.com/graphql?scope=public_kmsat and ?scope=partner_public_kmsat on 2026-09-09.
  • Not verified against a live partner tenant — no partner key is available. Unit tests mock fetch / tenantQuery; an end-to-end stdio smoke test against a local mock GraphQL endpoint confirmed tool listing, JIT minting/caching, and the no-partner-key error path.
  • Partner mode has feature parity with REST for the MCP Apps user card (card builder now accepts camelCase GraphQL users) and the users-list filter prompt. Write mutations remain out of scope.
  • The GraphQL-only 2.0 question is tracked separately in Decide: 2.0 GraphQL-only path for both tenant and partner credentials #66 (breaking: requires Product API keys and the Diamond / SAT Advanced tier).

Test plan

  • npm test
  • npm run lint
  • npm run build
  • Live smoke test with a partner key once one is available: knowbe4_partner_accounts_list, then knowbe4_users_list with account_id

https://claude.ai/code/session_017fK8GjLU5PbuRc4UR62PFc

- Partner GraphQL client (src/utils/graphql.ts) and JIT tenant token cache (src/utils/jit.ts):
  apiTokensGenerateJit per account, cached 14 min, keyed by partner-key hash + account id,
  in-flight de-dup, evicted on auth failure, JWT never logged
- New `partner` domain: knowbe4_partner_accounts_list, knowbe4_partner_account_get
- Optional `account_id` on every tenant tool; when set the call is served over the tenant
  GraphQL API via JIT token (src/graphql/<domain>.ts), otherwise the REST path is unchanged
- Env mode: KNOWBE4_PARTNER_API_KEY (+ KNOWBE4_GRAPHQL_URL); gateway mode: X-KnowBe4-Partner-API-Key,
  401 only when neither key is present; knowbe4_status reports both credentials
- Fix: knowbe4_store_* and knowbe4_policies_* were unreachable in non-lazy mode because
  dispatch matched six hard-coded prefixes; route via the tool category map instead

Claude-Session: https://claude.ai/code/session_017fK8GjLU5PbuRc4UR62PFc
@coderabbitai

coderabbitai Bot commented Sep 10, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 76b4f1f5-df16-447f-b021-5e1b21d32574


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

- card.builder normalizes both REST snake_case and GraphQL camelCase users
  (status from `archived`, name from displayName, trend from riskScoreHistories)
- partner-mode knowbe4_users_get attaches the MCP Apps _card, fetching the
  risk history over GraphQL best-effort
- partner-mode knowbe4_users_list offers the same filter prompt as REST when
  called without filters

Claude-Session: https://claude.ai/code/session_017fK8GjLU5PbuRc4UR62PFc
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