@productcraft/platform-auth 0.0.3 → 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 +222 -13
  2. package/package.json +2 -2
package/README.md CHANGED
@@ -1,14 +1,21 @@
1
1
  # @productcraft/platform-auth
2
2
 
3
- Typed Node.js SDK for [ProductCraft Platform-Auth](https://productcraft.co) — workspaces, PlatformUser sign-in, API keys, members, invites.
3
+ Typed Node.js SDK for [ProductCraft Platform-Auth](https://productcraft.co) — PlatformUser sign-in / sign-up / password reset, workspaces, members + invites, workspace roles + IAM policies, workspace API keys, service activation, and the `/v1/introspect` endpoint every other ProductCraft backend uses to resolve permissions.
4
4
 
5
5
  ```bash
6
6
  npm install @productcraft/platform-auth
7
7
  ```
8
8
 
9
- Server-side only. The SDK ships a Platform API Key; never embed it in a browser bundle.
9
+ Server-side only. The SDK ships a Platform API Key in the `Authorization` header — never embed it in a browser bundle.
10
10
 
11
- This package targets the **platform-side** identity model — the ProductCraft workspace owners who administer your apps. For end-user authentication on a product you build on top of Heimdall, you want [`@productcraft/heimdall`](https://www.npmjs.com/package/@productcraft/heimdall) instead.
11
+ ## Which SDK do I want?
12
+
13
+ | If your goal is to … | Reach for |
14
+ |---|---|
15
+ | Authenticate **your own end-users** in a product you're building (Heimdall apps) | [`@productcraft/heimdall`](https://www.npmjs.com/package/@productcraft/heimdall) |
16
+ | Authenticate **your team** into the ProductCraft console + manage workspaces | **this package** |
17
+
18
+ The two are easy to confuse because both have "auth" in the name. Platform-Auth owns *the ProductCraft account you use to log into productcraft.co*. Heimdall owns *the end-users of products you build on the platform*. Distinct databases, distinct signing keys.
12
19
 
13
20
  ## Quick start
14
21
 
@@ -21,22 +28,26 @@ const platformAuth = new PlatformAuth({
21
28
 
22
29
  // Resolve the caller's effective policy + enabled services for a workspace
23
30
  const { data } = await platformAuth.client.GET(
24
- "/v1/introspect/workspaces/{workspaceId}",
25
- { params: { path: { workspaceId: "ws_..." } } },
31
+ "/v1/introspect/workspaces/{workspace_id}",
32
+ { params: { path: { workspace_id: "<workspace-uuid>" } } },
26
33
  );
27
34
 
28
- // Create a workspace API key for a CI bot
29
- await platformAuth.client.POST(
30
- "/v1/workspaces/{slug}/api-keys",
31
- {
32
- params: { path: { slug: "my-workspace" } },
33
- body: { name: "ci-bot", permissions: ["envoi.read", "rally.read"] },
34
- },
35
- );
35
+ // data.policy — flat array of IAM-style { effect, actions, resources } statements
36
+ // data.services — string[] of enabled services (envoi / rally / agora / heimdall / …)
36
37
  ```
37
38
 
38
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.
39
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
+
40
51
  ## Configuration
41
52
 
42
53
  ```ts
@@ -49,6 +60,204 @@ new PlatformAuth({
49
60
  });
50
61
  ```
51
62
 
63
+ ## Common operations
64
+
65
+ ### Authentication (PlatformUser sign-in)
66
+
67
+ ```ts
68
+ // Sign in
69
+ await platformAuth.client.POST(
70
+ "/v1/auth/signin",
71
+ { body: { identifier: "you@example.com", password: "..." } },
72
+ );
73
+
74
+ // Refresh tokens
75
+ await platformAuth.client.POST("/v1/auth/refresh", { body: { refresh_token } });
76
+
77
+ // Logout (single session) — or /v1/auth/logout to revoke every refresh token
78
+ await platformAuth.client.POST("/v1/auth/logout-current", { body: { refresh_token } });
79
+
80
+ // Password reset
81
+ await platformAuth.client.POST(
82
+ "/v1/auth/password/request-reset",
83
+ { body: { email: "you@example.com" } },
84
+ );
85
+
86
+ await platformAuth.client.POST(
87
+ "/v1/auth/password/reset",
88
+ { body: { token, new_password: "..." } },
89
+ );
90
+ ```
91
+
92
+ ### Introspect — used by every other ProductCraft backend
93
+
94
+ ```ts
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.
98
+ await platformAuth.client.GET("/v1/introspect");
99
+
100
+ // Resolve effective policy + enabled services for one workspace
101
+ await platformAuth.client.GET(
102
+ "/v1/introspect/workspaces/{workspace_id}",
103
+ { params: { path: { workspace_id: "<workspace-uuid>" } } },
104
+ );
105
+
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");
110
+ ```
111
+
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.
113
+
114
+ ### Workspaces
115
+
116
+ ```ts
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).
119
+ await platformAuth.client.GET("/v1/workspaces");
120
+
121
+ // Create — cookie/JWT only. PAKs cannot create workspaces.
122
+ await platformAuth.client.POST(
123
+ "/v1/workspaces",
124
+ { body: { display_name: "Acme", slug: "acme" } },
125
+ );
126
+
127
+ // Get / update by slug
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}", { ... });
132
+ ```
133
+
134
+ ### Members, invites, roles
135
+
136
+ ```ts
137
+ // Members
138
+ await platformAuth.client.GET("/v1/workspaces/{workspace_slug}/members", { ... });
139
+ await platformAuth.client.PATCH(
140
+ "/v1/workspaces/{workspace_slug}/members/{account_id}/role",
141
+ { params: { ... }, body: { role_id: "role_..." } },
142
+ );
143
+
144
+ // Invites
145
+ await platformAuth.client.POST(
146
+ "/v1/workspaces/{workspace_slug}/invites",
147
+ { params: { ... }, body: { email: "...", role_id: "..." } },
148
+ );
149
+ await platformAuth.client.POST("/v1/workspaces/invites/accept", { body: { code } });
150
+
151
+ // Roles
152
+ await platformAuth.client.POST(
153
+ "/v1/workspaces/{workspace_slug}/roles",
154
+ { params: { ... }, body: { name: "billing-admin", policy_ids: ["pol_..."] } },
155
+ );
156
+ ```
157
+
158
+ ### IAM policies
159
+
160
+ ```ts
161
+ // List the canonical permission catalogue (what verbs exist for each service)
162
+ await platformAuth.client.GET(
163
+ "/v1/workspaces/{workspace_slug}/policies/actions/catalog",
164
+ { ... },
165
+ );
166
+
167
+ // Create a managed policy. Cookie/JWT only.
168
+ await platformAuth.client.POST(
169
+ "/v1/workspaces/{workspace_slug}/policies",
170
+ {
171
+ params: { ... },
172
+ body: {
173
+ name: "envoi-read-only",
174
+ description: "Read-only access to all Envoi resources",
175
+ policy: [
176
+ { effect: "allow", actions: ["envoi.read", "envoi.list"], resources: ["*"] },
177
+ ],
178
+ },
179
+ },
180
+ );
181
+ ```
182
+
183
+ ### Workspace API keys (PATs)
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
+
189
+ ```ts
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(
208
+ "/v1/workspaces/{workspace_slug}/api-keys",
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
+ },
217
+ );
218
+ // pak.token — the plaintext `pcft_live_*` value, returned exactly once
219
+ // pak.record — public metadata (id, prefix, policies, last_used_at, …)
220
+
221
+ // Replace the bound policies on an existing PAK (re-runs caller-narrowing
222
+ // against the merged statement set)
223
+ await platformAuth.client.PATCH(
224
+ "/v1/workspaces/{workspace_slug}/api-keys/{id}/bindings",
225
+ { params: { ... }, body: { policy_ids: ["<new-policy-uuid>"] } },
226
+ );
227
+ ```
228
+
229
+ ### Service activation
230
+
231
+ ```ts
232
+ // Enable / disable a service for the workspace (envoi, rally, agora, heimdall, …)
233
+ await platformAuth.client.PUT(
234
+ "/v1/workspaces/{workspace_slug}/services/{service}",
235
+ { params: { ... }, body: { enabled: true } },
236
+ );
237
+
238
+ // Per-service workspace settings
239
+ await platformAuth.client.PUT(
240
+ "/v1/workspaces/{workspace_slug}/services/{service}/settings",
241
+ { ... },
242
+ );
243
+ ```
244
+
245
+ ### Audit
246
+
247
+ ```ts
248
+ // Read the workspace audit log
249
+ await platformAuth.client.GET(
250
+ "/v1/workspaces/{workspace_slug}/audit-logs",
251
+ { params: { ... }, query: { limit: 100 } },
252
+ );
253
+
254
+ // Streaming-friendly "recent activity" feed (for the console UI)
255
+ await platformAuth.client.GET(
256
+ "/v1/workspaces/{workspace_slug}/audit-feed",
257
+ { ... },
258
+ );
259
+ ```
260
+
52
261
  ## How this SDK is built
53
262
 
54
263
  Generated from the live OpenAPI spec at `https://api.auth.productcraft.co/docs-json` via [`openapi-typescript`](https://openapi-ts.dev/) + [`openapi-fetch`](https://openapi-ts.dev/openapi-fetch/). The nightly `spec-refresh` workflow opens a PR whenever the spec changes.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@productcraft/platform-auth",
3
- "version": "0.0.3",
4
- "description": "Platform-side authentication — workspace login, API keys, members for the ProductCraft platform itself. Generated from the production OpenAPI spec.",
3
+ "version": "0.0.5",
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",
7
7
  "module": "./dist/index.js",