@endora-commerce/mod-api-keys 0.100.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +57 -0
  3. package/dist/admin/index.d.ts +37 -0
  4. package/dist/admin/index.d.ts.map +1 -0
  5. package/dist/admin/index.js +43 -0
  6. package/dist/admin/index.js.map +1 -0
  7. package/dist/admin/pages/ApiKeysPage.d.ts +3 -0
  8. package/dist/admin/pages/ApiKeysPage.d.ts.map +1 -0
  9. package/dist/admin/pages/ApiKeysPage.js +182 -0
  10. package/dist/admin/pages/ApiKeysPage.js.map +1 -0
  11. package/dist/backend/entities/api-key.entity.d.ts +33 -0
  12. package/dist/backend/entities/api-key.entity.d.ts.map +1 -0
  13. package/dist/backend/entities/api-key.entity.js +118 -0
  14. package/dist/backend/entities/api-key.entity.js.map +1 -0
  15. package/dist/backend/index.d.ts +71 -0
  16. package/dist/backend/index.d.ts.map +1 -0
  17. package/dist/backend/index.js +57 -0
  18. package/dist/backend/index.js.map +1 -0
  19. package/dist/backend/plugin.d.ts +65 -0
  20. package/dist/backend/plugin.d.ts.map +1 -0
  21. package/dist/backend/plugin.js +75 -0
  22. package/dist/backend/plugin.js.map +1 -0
  23. package/dist/backend/routes.d.ts +26 -0
  24. package/dist/backend/routes.d.ts.map +1 -0
  25. package/dist/backend/routes.js +51 -0
  26. package/dist/backend/routes.js.map +1 -0
  27. package/dist/backend/services/api-key-service.d.ts +72 -0
  28. package/dist/backend/services/api-key-service.d.ts.map +1 -0
  29. package/dist/backend/services/api-key-service.js +186 -0
  30. package/dist/backend/services/api-key-service.js.map +1 -0
  31. package/dist/manifest.d.ts +169 -0
  32. package/dist/manifest.d.ts.map +1 -0
  33. package/dist/manifest.js +155 -0
  34. package/dist/manifest.js.map +1 -0
  35. package/dist/migrations/20260724T173916_api_keys_distributor_binding.d.ts +50 -0
  36. package/dist/migrations/20260724T173916_api_keys_distributor_binding.d.ts.map +1 -0
  37. package/dist/migrations/20260724T173916_api_keys_distributor_binding.js +117 -0
  38. package/dist/migrations/20260724T173916_api_keys_distributor_binding.js.map +1 -0
  39. package/dist/migrations/index.d.ts +27 -0
  40. package/dist/migrations/index.d.ts.map +1 -0
  41. package/dist/migrations/index.js +29 -0
  42. package/dist/migrations/index.js.map +1 -0
  43. package/docs/api_keys.md +89 -0
  44. package/i18n/en.json +5 -0
  45. package/i18n/pl.json +5 -0
  46. package/package.json +93 -0
  47. package/tailwind.css +14 -0
@@ -0,0 +1,89 @@
1
+ ---
2
+ title: api_keys
3
+ description: Bearer-token integration credentials
4
+ ---
5
+
6
+ # `api_keys`
7
+
8
+ Scoped bearer-token credentials for machine-to-machine integrations. The
9
+ plaintext token is shown once at creation; only its SHA-256 hash is stored.
10
+ A key may additionally carry a **distributor binding**
11
+ (Organization + Sales Channel + service Customer Account) and an optional
12
+ expiry, turning it into a partner credential for the `/api/v1/external/*`
13
+ namespace, which the *Partner API access* integration guide documents in full.
14
+
15
+ ## Public surface
16
+
17
+ | Verb + Path | Audience | Purpose |
18
+ | --- | --- | --- |
19
+ | `GET /api/v1/admin/api-keys` | admin | List keys + last-used timestamps, binding, expiry |
20
+ | `POST /api/v1/admin/api-keys` | admin | Create; returns the raw bearer **once** |
21
+ | `DELETE /api/v1/admin/api-keys/:id` | admin | Revoke |
22
+
23
+ External calls authenticate by sending `Authorization: Bearer sk_live_…`.
24
+ `requireApiKey(scope)` and `requireBoundApiKey(scope)` are Fastify
25
+ pre-handlers exposed by the `api_keys` plugin; route surfaces gate themselves
26
+ with them (`requireApiKey('catalog:write')`,
27
+ `requireBoundApiKey('orders:write')`, etc.).
28
+
29
+ ## Scope enum
30
+
31
+ Creation validates scopes against the typed catalog in
32
+ `packages/contracts/src/api-keys.ts` (`apiKeyScopeSchema`):
33
+
34
+ | Scope | Meaning |
35
+ | --- | --- |
36
+ | `catalog:read` | PIM reads (unbound) and the external catalog surface (bound) |
37
+ | `catalog:write` | PIM by-SKU upsert — **unbound keys only** |
38
+ | `orders:read` | External order reads — **bound keys only** |
39
+ | `orders:write` | External order intake — **bound keys only** |
40
+
41
+ Enforcement stays membership-based, so legacy free-text scopes on existing
42
+ keys remain readable and enforceable — only creation is validated.
43
+
44
+ ## Binding model
45
+
46
+ The binding is all-or-none and **immutable post-create** (token-shown-once
47
+ lifecycle; rebinding means revoking and issuing a new key). Creation rules,
48
+ validated server-side and mirrored inline in the admin form:
49
+
50
+ | Rule | Detail |
51
+ | --- | --- |
52
+ | B1 | Any `orders:*` scope ⇒ binding required |
53
+ | B2 | Binding present ⇒ `catalog:write` forbidden (PIM writes stay unbound-only) |
54
+ | B3 | The service Customer Account must be active and belong to the bound Organization |
55
+ | B4 | Organization and Sales Channel must exist (the channel need not be active at creation — the key simply fails closed while it is inactive) |
56
+ | B5 | `expiresAt`, when present, must be a future instant |
57
+
58
+ At request time a bound key derives a single-org tenant context and a pinned
59
+ sales channel (an explicit `X-Sales-Channel` header naming a different channel
60
+ is refused with `403 API_KEY_CHANNEL_MISMATCH`). An unbound key keeps the
61
+ legacy system context and header/host/default channel resolution.
62
+
63
+ ## Expiry
64
+
65
+ `expiresAt` is optional and applies to both key modes. Once the instant
66
+ passes, `authenticate` refuses the key with `401 UNAUTHORIZED` — the same
67
+ refusal as a revoked key.
68
+
69
+ ## Entities
70
+
71
+ `ApiKey` (name, keyHash, lastFour, scopes, status, lastUsedAt, and the
72
+ nullable binding/expiry columns `organizationId`, `salesChannelId`,
73
+ `customerAccountId`, `expiresAt`).
74
+
75
+ ## Out-of-scope / gate behaviour
76
+
77
+ - Wrong scope ⇒ `403 API_KEY_OUT_OF_SCOPE` + audit row `api_key.out_of_scope`.
78
+ - Unbound key on a bound-only endpoint ⇒ `403 API_KEY_NOT_BOUND` + audit row
79
+ `api_key.not_bound`.
80
+ - Channel mismatch ⇒ `403 API_KEY_CHANNEL_MISMATCH` + audit row
81
+ `api_key.channel_mismatch`.
82
+
83
+ ## Extension points
84
+
85
+ - **New scopes** — extend `apiKeyScopeSchema` in `@endora-commerce/contracts` and gate the
86
+ new surface at its call site; the service is scope-name-agnostic at
87
+ enforcement time.
88
+ - **Per-key rate limit** — `api-key-service.authenticate` returns the key
89
+ id; layer a per-key counter in the pre-handler or a downstream middleware.
package/i18n/en.json ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "nav.apiKeys.label": "API keys",
3
+ "actions.openApiKeys.label": "API keys",
4
+ "actions.openApiKeys.description": "Machine-to-machine bearer tokens"
5
+ }
package/i18n/pl.json ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "nav.apiKeys.label": "Klucze API",
3
+ "actions.openApiKeys.label": "Klucze API",
4
+ "actions.openApiKeys.description": "Tokeny bearer dla integracji maszynowych"
5
+ }
package/package.json ADDED
@@ -0,0 +1,93 @@
1
+ {
2
+ "name": "@endora-commerce/mod-api-keys",
3
+ "version": "0.100.0",
4
+ "type": "module",
5
+ "sideEffects": false,
6
+ "description": "Programmatic API keys (Bearer tokens) used by integrations and webhooks.",
7
+ "license": "MIT",
8
+ "endora": {
9
+ "type": "module",
10
+ "id": "api_keys"
11
+ },
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/endora-commerce/endora-commerce.git",
15
+ "directory": "packages/modules/api_keys"
16
+ },
17
+ "publishConfig": {
18
+ "access": "public"
19
+ },
20
+ "exports": {
21
+ ".": {
22
+ "types": "./dist/manifest.d.ts",
23
+ "default": "./dist/manifest.js"
24
+ },
25
+ "./backend": {
26
+ "types": "./dist/backend/index.d.ts",
27
+ "default": "./dist/backend/index.js"
28
+ },
29
+ "./migrations": {
30
+ "types": "./dist/migrations/index.d.ts",
31
+ "default": "./dist/migrations/index.js"
32
+ },
33
+ "./admin": {
34
+ "types": "./dist/admin/index.d.ts",
35
+ "default": "./dist/admin/index.js"
36
+ },
37
+ "./tailwind.css": "./tailwind.css",
38
+ "./package.json": "./package.json"
39
+ },
40
+ "files": [
41
+ "dist",
42
+ "i18n",
43
+ "docs",
44
+ "tailwind.css"
45
+ ],
46
+ "engines": {
47
+ "node": ">=22.18.0"
48
+ },
49
+ "peerDependencies": {
50
+ "@mikro-orm/core": "^6",
51
+ "@mikro-orm/migrations": "^6",
52
+ "@mikro-orm/postgresql": "^6",
53
+ "fastify": "^5",
54
+ "lucide-react": "^1",
55
+ "react": "^19",
56
+ "@endora-commerce/admin-kit": "0.100.0",
57
+ "@endora-commerce/contracts": "0.100.0",
58
+ "@endora-commerce/platform": "0.100.0"
59
+ },
60
+ "peerDependenciesMeta": {
61
+ "@endora-commerce/admin-kit": {
62
+ "optional": true
63
+ },
64
+ "lucide-react": {
65
+ "optional": true
66
+ },
67
+ "react": {
68
+ "optional": true
69
+ }
70
+ },
71
+ "devDependencies": {
72
+ "@fastify/type-provider-zod": "^1.0.0",
73
+ "@mikro-orm/core": "^6.6.13",
74
+ "@mikro-orm/migrations": "^6.6.13",
75
+ "@mikro-orm/postgresql": "^6.6.13",
76
+ "@types/node": "^22.9.0",
77
+ "@types/react": "^19.2.14",
78
+ "fastify": "^5.12.5",
79
+ "lucide-react": "^1.11.0",
80
+ "react": "^19.2.5",
81
+ "typescript": "^5.9.3",
82
+ "vitest": "^4.1.11",
83
+ "@endora-commerce/admin-kit": "0.100.0",
84
+ "@endora-commerce/contracts": "0.100.0",
85
+ "@endora-commerce/platform": "0.100.0"
86
+ },
87
+ "scripts": {
88
+ "build": "tsc -p tsconfig.build.json && tsc -p tsconfig.ui.json",
89
+ "typecheck": "tsc -p tsconfig.json && tsc -p tsconfig.ui.json --noEmit",
90
+ "lint": "eslint src",
91
+ "test": "vitest run"
92
+ }
93
+ }
package/tailwind.css ADDED
@@ -0,0 +1,14 @@
1
+ /* @endora-commerce/mod-api-keys — AUTO-GENERATED by `pnpm --filter backend run manifests:generate`.
2
+ *
3
+ * The `@source` directives this package asks its host to scan
4
+ * (`specs/110-instance-repository/contracts/admin-stylesheet-composition.md` R1).
5
+ * They resolve relative to **this file**, so they hold wherever the package is
6
+ * installed — a workspace link here, `node_modules` in a client's instance.
7
+ *
8
+ * The `dist` line is what a published tarball ships and is what an instance
9
+ * scans; the `src` line is inert there and is what keeps `pnpm --filter admin
10
+ * run dev` reading source in this repository. Do not edit: run
11
+ * `pnpm --filter backend run manifests:generate`.
12
+ */
13
+ @source "./dist/admin";
14
+ @source "./src/admin";