@productcraft/platform-auth 0.0.4 → 0.0.5

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 (2) hide show
  1. package/README.md +69 -21
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -29,15 +29,25 @@ const platformAuth = new PlatformAuth({
29
29
  // Resolve the caller's effective policy + enabled services for a workspace
30
30
  const { data } = await platformAuth.client.GET(
31
31
  "/v1/introspect/workspaces/{workspace_id}",
32
- { params: { path: { workspace_id: "ws_..." } } },
32
+ { params: { path: { workspace_id: "<workspace-uuid>" } } },
33
33
  );
34
34
 
35
- // data.policy.statements[…] — what the caller is allowed to do
36
- // data.enabled_services[…] — envoi / rally / agora / heimdall / …
35
+ // data.policy — flat array of IAM-style { effect, actions, resources } statements
36
+ // data.services — string[] of enabled services (envoi / rally / agora / heimdall / …)
37
37
  ```
38
38
 
39
39
  The `client` is an [`openapi-fetch`](https://openapi-ts.dev/openapi-fetch/) instance bound to `https://api.auth.productcraft.co` and your auth credential.
40
40
 
41
+ ### Auth modes
42
+
43
+ | Mode | When to use | Mint/rotate/delete surfaces |
44
+ |---|---|---|
45
+ | `apiKey` (`pcft_live_*`) | Backend-to-backend reads + introspect calls | ❌ — mint requires a human session |
46
+ | `bearer` (PlatformUser JWT) | Anything administrative: workspace create/delete, role/policy authoring, PAK minting, invite mutations | ✅ |
47
+ | `cookie` (`auth_token`) | Server-side route handlers running behind the console session | ✅ |
48
+
49
+ PAKs are workspace-scoped credentials — they read + introspect freely against the workspace they're bound to but cannot mint another PAK or reshape the human-admin surface. Cross-workspace probes by a PAK return the uniform non-member shape (no slug → UUID oracle).
50
+
41
51
  ## Configuration
42
52
 
43
53
  ```ts
@@ -82,17 +92,21 @@ await platformAuth.client.POST(
82
92
  ### Introspect — used by every other ProductCraft backend
83
93
 
84
94
  ```ts
85
- // Resolve the bearer of a token: returns account + email_verified_at + workspaces
95
+ // Resolve the bearer of a token: returns account + email_verified_at + workspaces.
96
+ // Accepts a `pcft_live_*` PAK too — the response carries a synthetic verified
97
+ // account block so downstream `RequireEmailVerifiedGuard`-style gates pass.
86
98
  await platformAuth.client.GET("/v1/introspect");
87
99
 
88
100
  // Resolve effective policy + enabled services for one workspace
89
101
  await platformAuth.client.GET(
90
102
  "/v1/introspect/workspaces/{workspace_id}",
91
- { params: { path: { workspace_id: "ws_..." } } },
103
+ { params: { path: { workspace_id: "<workspace-uuid>" } } },
92
104
  );
93
105
 
94
- // Resolve a PAK — used internally by services that accept pcft_live_* on routes
95
- await platformAuth.client.GET("/v1/introspect/api-key");
106
+ // Resolve a PAK — POST, not GET. Used internally by services that accept
107
+ // `pcft_live_*` on routes. Caller's bearer is the PAK being introspected;
108
+ // the body is unused (sent as `{}`).
109
+ await platformAuth.client.POST("/v1/introspect/api-key");
96
110
  ```
97
111
 
98
112
  The latter two are cached by every consuming service for ~60 seconds. Reach for them from a backend gate; never from a hot request path.
@@ -100,17 +114,21 @@ The latter two are cached by every consuming service for ~60 seconds. Reach for
100
114
  ### Workspaces
101
115
 
102
116
  ```ts
103
- // List
117
+ // List — for a PAK caller, this returns exactly the workspace the PAK is
118
+ // bound to (member_role / member_role_id are null on that lane).
104
119
  await platformAuth.client.GET("/v1/workspaces");
105
120
 
106
- // Create
121
+ // Create — cookie/JWT only. PAKs cannot create workspaces.
107
122
  await platformAuth.client.POST(
108
123
  "/v1/workspaces",
109
- { body: { name: "Acme", slug: "acme" } },
124
+ { body: { display_name: "Acme", slug: "acme" } },
110
125
  );
111
126
 
112
- // Get / update / delete by slug
127
+ // Get / update by slug
113
128
  await platformAuth.client.GET("/v1/workspaces/{workspace_slug}", { ... });
129
+
130
+ // Delete — cookie/JWT only. PAKs cannot delete workspaces.
131
+ await platformAuth.client.DELETE("/v1/workspaces/{workspace_slug}", { ... });
114
132
  ```
115
133
 
116
134
  ### Members, invites, roles
@@ -146,14 +164,15 @@ await platformAuth.client.GET(
146
164
  { ... },
147
165
  );
148
166
 
149
- // Create a custom policy
167
+ // Create a managed policy. Cookie/JWT only.
150
168
  await platformAuth.client.POST(
151
169
  "/v1/workspaces/{workspace_slug}/policies",
152
170
  {
153
171
  params: { ... },
154
172
  body: {
155
- name: "Envoi-read-only",
156
- statements: [
173
+ name: "envoi-read-only",
174
+ description: "Read-only access to all Envoi resources",
175
+ policy: [
157
176
  { effect: "allow", actions: ["envoi.read", "envoi.list"], resources: ["*"] },
158
177
  ],
159
178
  },
@@ -163,18 +182,47 @@ await platformAuth.client.POST(
163
182
 
164
183
  ### Workspace API keys (PATs)
165
184
 
185
+ PATs bind to one or more managed `workspace_policy` rows. Author the policy first, then mint the key with its id. The inline `policy: [...]` shape is **not** accepted — `policy_ids: [<uuid>]` is required.
186
+
187
+ Mint / rotate / revoke require a cookie or JWT — a PAK cannot mint another PAK.
188
+
166
189
  ```ts
167
- // Mint a PAK for CI / IaC
168
- await platformAuth.client.POST(
190
+ // 1. Author (or reuse) a managed policy
191
+ const { data: policy } = await platformAuth.client.POST(
192
+ "/v1/workspaces/{workspace_slug}/policies",
193
+ {
194
+ params: { path: { workspace_slug: "acme" } },
195
+ body: {
196
+ name: "ci-staging-deploy",
197
+ description: "Heimdall + Envoi for the staging pipeline",
198
+ policy: [
199
+ { effect: "allow", actions: ["heimdall.create", "heimdall.read", "heimdall.update"], resources: ["pcft:heimdall:app/*"] },
200
+ { effect: "allow", actions: ["envoi.template.read", "envoi.template.send"], resources: ["*"] },
201
+ ],
202
+ },
203
+ },
204
+ );
205
+
206
+ // 2. Mint a PAT that binds to that policy
207
+ const { data: pak } = await platformAuth.client.POST(
169
208
  "/v1/workspaces/{workspace_slug}/api-keys",
170
- { params: { ... }, body: { name: "github-actions", policy_ids: ["pol_..."] } },
209
+ {
210
+ params: { path: { workspace_slug: "acme" } },
211
+ body: {
212
+ name: "github-actions-staging-deploy",
213
+ description: "Deploys acme-staging app from CI",
214
+ policy_ids: [policy!.id],
215
+ },
216
+ },
171
217
  );
218
+ // pak.token — the plaintext `pcft_live_*` value, returned exactly once
219
+ // pak.record — public metadata (id, prefix, policies, last_used_at, …)
172
220
 
173
- // Bind / unbind a PAK to additional workspaces (rare — most PAKs stay
174
- // scoped to the workspace they were minted under)
175
- await platformAuth.client.PUT(
221
+ // Replace the bound policies on an existing PAK (re-runs caller-narrowing
222
+ // against the merged statement set)
223
+ await platformAuth.client.PATCH(
176
224
  "/v1/workspaces/{workspace_slug}/api-keys/{id}/bindings",
177
- { ... },
225
+ { params: { ... }, body: { policy_ids: ["<new-policy-uuid>"] } },
178
226
  );
179
227
  ```
180
228
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@productcraft/platform-auth",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
4
4
  "description": "PlatformUser auth, workspaces, members, invites, roles + IAM policies, API keys, service activation, and the introspect endpoint every ProductCraft backend uses to resolve permissions. Generated from the production OpenAPI spec.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",