@productcraft/platform-auth 0.1.3 → 1.0.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.
- package/README.md +118 -212
- package/api.md +1716 -0
- package/dist/index.cjs +1809 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4082 -260
- package/dist/index.d.ts +4082 -260
- package/dist/index.js +1744 -8
- package/dist/index.js.map +1 -1
- package/dist/testing.cjs +691 -0
- package/dist/testing.cjs.map +1 -0
- package/dist/testing.d.cts +32 -0
- package/dist/testing.d.ts +32 -0
- package/dist/testing.js +689 -0
- package/dist/testing.js.map +1 -0
- package/manifest.json +1115 -0
- package/package.json +15 -5
package/README.md
CHANGED
|
@@ -1,283 +1,189 @@
|
|
|
1
1
|
# @productcraft/platform-auth
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Node.js / TypeScript client for the **ProductCraft Platform API**: workspaces, members and invites, roles and IAM policies, workspace API keys, service activation, audit logs, and the introspection endpoints every ProductCraft backend uses to resolve permissions.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Every method, type, error class and mock is generated from the production OpenAPI spec. Upgrading from 0.x? See the [1.0 migration guide](https://github.com/clauderanelagh/productcraft-node/blob/main/docs/migrations/platform-auth-1.0.0.md).
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```sh
|
|
6
10
|
npm install @productcraft/platform-auth
|
|
7
11
|
```
|
|
8
12
|
|
|
9
|
-
Server-side
|
|
13
|
+
Node 20.3 or later, Bun, Deno and edge runtimes. Server-side: it sends a workspace key or a platform session. Or install the umbrella package for every product: `npm install productcraft`.
|
|
10
14
|
|
|
11
15
|
## Which SDK do I want?
|
|
12
16
|
|
|
13
17
|
| If your goal is to … | Reach for |
|
|
14
18
|
|---|---|
|
|
15
|
-
|
|
|
16
|
-
|
|
|
19
|
+
| Sign in **your own end-users** in a product you build on ProductCraft | [`@productcraft/auth`](https://www.npmjs.com/package/@productcraft/auth) |
|
|
20
|
+
| Manage **your ProductCraft account and workspaces**: keys, members, roles, policies, services | **this package** |
|
|
17
21
|
|
|
18
|
-
|
|
22
|
+
Both have "auth" in the name. Platform owns the ProductCraft account you use to sign in to the console; Auth owns the end-users of the products you build. They are separate databases with separate signing keys.
|
|
19
23
|
|
|
20
24
|
## Quick start
|
|
21
25
|
|
|
22
26
|
```ts
|
|
23
27
|
import { PlatformAuth } from "@productcraft/platform-auth";
|
|
24
28
|
|
|
25
|
-
// Reads PRODUCTCRAFT_API_KEY from the environment
|
|
26
|
-
const
|
|
29
|
+
// Reads PRODUCTCRAFT_API_KEY from the environment.
|
|
30
|
+
const platform = new PlatformAuth();
|
|
27
31
|
|
|
28
|
-
//
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
{ params: { path: { workspace_id: "<workspace-uuid>" } } },
|
|
32
|
-
);
|
|
32
|
+
// Which workspace and permissions does this key carry?
|
|
33
|
+
const me = await platform.apiKeys.self();
|
|
34
|
+
console.log(me.workspace_id, me.name, me.allowed);
|
|
33
35
|
|
|
34
|
-
//
|
|
35
|
-
|
|
36
|
+
// Workspace routes take the workspace slug as the first argument.
|
|
37
|
+
const ws = await platform.workspaces.retrieve("acme");
|
|
38
|
+
|
|
39
|
+
// Pages are fetched as you iterate.
|
|
40
|
+
for await (const key of platform.workspaces.apiKeys.list(ws.slug, { include_revoked: "false" })) {
|
|
41
|
+
console.log(key.id, key.name, key.status);
|
|
42
|
+
}
|
|
36
43
|
```
|
|
37
44
|
|
|
38
|
-
|
|
45
|
+
Platform routes name the workspace by **slug**, positionally (`platform.workspaces.members.list("acme")`). The one exception is `platform.stats.retrieve()`, which uses the workspace id: pass `workspaceId`, or let a workspace key resolve it once per process.
|
|
39
46
|
|
|
40
|
-
|
|
47
|
+
## Credentials
|
|
41
48
|
|
|
42
|
-
|
|
|
49
|
+
| Credential | Pass | Use it for |
|
|
43
50
|
|---|---|---|
|
|
44
|
-
|
|
|
45
|
-
|
|
|
46
|
-
| `cookie` (`auth_token`) | Server-side route handlers running behind the console session | ✅ |
|
|
47
|
-
|
|
48
|
-
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).
|
|
51
|
+
| Workspace API key (`pcft_live_…`) | `apiKey`, or `PRODUCTCRAFT_API_KEY` in the environment | Backend reads and introspection for the workspace the key belongs to |
|
|
52
|
+
| Platform session (a signed-in person) | `auth: { type: "bearer", token }` | Administration: creating workspaces, minting and revoking keys, roles, policies, invites |
|
|
49
53
|
|
|
50
|
-
|
|
54
|
+
```ts
|
|
55
|
+
import { PlatformAuth } from "@productcraft/platform-auth";
|
|
51
56
|
|
|
52
|
-
|
|
57
|
+
const anon = new PlatformAuth();
|
|
58
|
+
const { access_token } = await anon.auth.signin({ identifier: "ada@example.com", password: process.env.PASSWORD!, session_duration: 3600 });
|
|
59
|
+
const admin = new PlatformAuth({ auth: { type: "bearer", token: access_token } });
|
|
53
60
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
61
|
+
const minted = await admin.workspaces.apiKeys.create("acme", { name: "Production mail", preset: "mail-send", expires_in_days: 90 });
|
|
62
|
+
console.log(minted.token); // shown once: store it in your secret manager
|
|
63
|
+
await admin.auth.logoutCurrent();
|
|
57
64
|
```
|
|
58
65
|
|
|
59
|
-
|
|
60
|
-
- Hosts are never read from the environment. Override them in code with `baseUrl`.
|
|
61
|
-
- This is a server-only fallback. Never put a key in `NEXT_PUBLIC_*` or any other variable a bundler inlines into browser code.
|
|
66
|
+
A workspace key cannot mint another key or reshape roles and policies; those calls need a person's session. An explicit `auth` always wins over the environment. The environment fallback is server-only: never put a key in `NEXT_PUBLIC_*` or any variable a bundler inlines into browser code. A keyed client constructed in a browser throws unless you set `dangerouslyAllowBrowser: true`.
|
|
62
67
|
|
|
63
|
-
##
|
|
68
|
+
## Introspection (for backends that accept workspace keys)
|
|
64
69
|
|
|
65
70
|
```ts
|
|
66
|
-
|
|
67
|
-
// Omit to use PRODUCTCRAFT_API_KEY from the environment
|
|
68
|
-
auth: { type: "apiKey", key: "pcft_live_..." }
|
|
69
|
-
| { type: "bearer", token: "eyJ..." }
|
|
70
|
-
| { type: "cookie", value: "auth_token=..." },
|
|
71
|
-
baseUrl: "https://api.auth.example.test", // optional override
|
|
72
|
-
fetch: customFetch, // optional
|
|
73
|
-
});
|
|
74
|
-
```
|
|
71
|
+
import { PlatformAuth } from "@productcraft/platform-auth";
|
|
75
72
|
|
|
76
|
-
|
|
73
|
+
// Resolve the key a request carried to its workspace and policy.
|
|
74
|
+
declare const incomingKey: string;
|
|
75
|
+
const caller = new PlatformAuth({ apiKey: incomingKey });
|
|
76
|
+
const { is_valid, workspace_id, policy } = await caller.introspect.apiKey();
|
|
77
|
+
```
|
|
77
78
|
|
|
78
|
-
|
|
79
|
+
<!-- generated:start (scripts/gen-sdk.mjs; edits between the markers are overwritten) -->
|
|
79
80
|
|
|
80
|
-
|
|
81
|
-
// Sign in
|
|
82
|
-
await platformAuth.client.POST(
|
|
83
|
-
"/v1/auth/signin",
|
|
84
|
-
{ body: { identifier: "you@example.com", password: "..." } },
|
|
85
|
-
);
|
|
86
|
-
|
|
87
|
-
// Refresh tokens
|
|
88
|
-
await platformAuth.client.POST("/v1/auth/refresh", { body: { refresh_token } });
|
|
89
|
-
|
|
90
|
-
// Logout (single session) — or /v1/auth/logout to revoke every refresh token
|
|
91
|
-
await platformAuth.client.POST("/v1/auth/logout-current", { body: { refresh_token } });
|
|
92
|
-
|
|
93
|
-
// Password reset
|
|
94
|
-
await platformAuth.client.POST(
|
|
95
|
-
"/v1/auth/password/request-reset",
|
|
96
|
-
{ body: { email: "you@example.com" } },
|
|
97
|
-
);
|
|
98
|
-
|
|
99
|
-
await platformAuth.client.POST(
|
|
100
|
-
"/v1/auth/password/reset",
|
|
101
|
-
{ body: { token, new_password: "..." } },
|
|
102
|
-
);
|
|
103
|
-
```
|
|
81
|
+
## Resources
|
|
104
82
|
|
|
105
|
-
|
|
83
|
+
Full reference: [api.md](./api.md).
|
|
106
84
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
// Resolve a PAK — POST, not GET. Used internally by services that accept
|
|
120
|
-
// `pcft_live_*` on routes. Caller's bearer is the PAK being introspected;
|
|
121
|
-
// the body is unused (sent as `{}`).
|
|
122
|
-
await platformAuth.client.POST("/v1/introspect/api-key");
|
|
123
|
-
```
|
|
85
|
+
- `platform.account`: `erase`
|
|
86
|
+
- `platform.apiKeys`: `self`
|
|
87
|
+
- `platform.auth`: `logout`, `logoutCurrent`, `password.requestReset`, `password.reset`, `refresh`, `signin`, `signup`
|
|
88
|
+
- `platform.introspect`: `apiKey`, `retrieve`, `workspaces.retrieve`
|
|
89
|
+
- `platform.me`: `avatar.clear`, `avatar.upload`, `hiddenGuides.hide`, `hiddenGuides.list`, `hiddenGuides.reset`, `hiddenGuides.unhide`, `profile.update`, `retrieve`
|
|
90
|
+
- `platform.oauth`: `connections.delete`, `connections.list`, `workspaces.connections.delete`, `workspaces.connections.list`, `workspaces.connections.restore`
|
|
91
|
+
- `platform.sessions`: `delete`, `list`, `revokeOthers`
|
|
92
|
+
- `platform.stats`: `retrieve`
|
|
93
|
+
- `platform.verification`: `resend`, `verify`
|
|
94
|
+
- `platform.wellKnown`: `jwks`, `oauthAuthorizationServer`
|
|
95
|
+
- `platform.workspaces`: `apiKeys.create`, `apiKeys.delete`, `apiKeys.list`, `apiKeys.presets.list`, `apiKeys.preview`, `apiKeys.retrieve`, `apiKeys.rotate`, `apiKeys.update`, `apiKeys.updateBindings`, `auditFeed.list`, `auditLogs.export`, `auditLogs.list`, `avatar.clear`, `avatar.upload`, `create`, `delete`, `invites.accept`, `invites.create`, `invites.delete`, `invites.list`, `list`, `members.delete`, `members.list`, `members.updateRole`, `policies.actions.catalog`, `policies.create`, `policies.delete`, `policies.list`, `policies.retrieve`, `policies.update`, `retrieve`, `roles.create`, `roles.delete`, `roles.list`, `roles.retrieve`, `roles.update`, `services.disable`, `services.enable`, `services.list`, `services.updateSettings`, `update`
|
|
124
96
|
|
|
125
|
-
|
|
97
|
+
## Pagination
|
|
126
98
|
|
|
127
|
-
|
|
99
|
+
List methods return a `PagePromise`: `for await` walks every item across pages, `await` gives one page.
|
|
128
100
|
|
|
129
101
|
```ts
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
await
|
|
102
|
+
import { PlatformAuth } from "@productcraft/platform-auth";
|
|
103
|
+
const platform = new PlatformAuth({ apiKey: process.env.PRODUCTCRAFT_API_KEY });
|
|
104
|
+
for await (const item of platform.oauth.connections.list({ limit: 50 })) console.log(item);
|
|
105
|
+
const page = await platform.oauth.connections.list();
|
|
106
|
+
if (page.hasNextPage()) await page.getNextPage();
|
|
107
|
+
const all = await platform.oauth.connections.list().toArray({ maxItems: 500 });
|
|
108
|
+
```
|
|
133
109
|
|
|
134
|
-
|
|
135
|
-
await platformAuth.client.POST(
|
|
136
|
-
"/v1/workspaces",
|
|
137
|
-
{ body: { display_name: "Acme", slug: "acme" } },
|
|
138
|
-
);
|
|
110
|
+
## Errors
|
|
139
111
|
|
|
140
|
-
|
|
141
|
-
await platformAuth.client.GET("/v1/workspaces/{workspace_slug}", { ... });
|
|
112
|
+
Every failure is a class you can catch. `APIError` carries `status`, `code`, `message`, `errors[]`, `requestId` and `docUrl`.
|
|
142
113
|
|
|
143
|
-
|
|
144
|
-
|
|
114
|
+
```ts
|
|
115
|
+
import { PlatformAuth, NotFoundError, RateLimitError, APIError } from "@productcraft/platform-auth";
|
|
116
|
+
const platform = new PlatformAuth();
|
|
117
|
+
try {
|
|
118
|
+
await platform.introspect.workspaces.retrieve("00000000-0000-0000-0000-000000000000");
|
|
119
|
+
} catch (err) {
|
|
120
|
+
if (err instanceof NotFoundError) console.log(err.code, err.requestId, err.docUrl);
|
|
121
|
+
else if (err instanceof RateLimitError) console.log(err.retryAfter);
|
|
122
|
+
else if (err instanceof APIError) console.log(err.status, err.errors);
|
|
123
|
+
else throw err;
|
|
124
|
+
}
|
|
145
125
|
```
|
|
146
126
|
|
|
147
|
-
|
|
127
|
+
## Retries and timeouts
|
|
128
|
+
|
|
129
|
+
Network errors, 408, 429, 5xx and `409 IDEMPOTENCY_IN_PROGRESS` are retried twice by default with exponential backoff and jitter, honouring `Retry-After` up to 60 s. A POST or PATCH without an idempotency key is never retried. Each attempt times out after 60 s, so the worst case with defaults is about 3 minutes; pass `signal: AbortSignal.timeout(ms)` for an overall deadline.
|
|
148
130
|
|
|
149
131
|
```ts
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
await
|
|
153
|
-
"/v1/workspaces/{workspace_slug}/members/{account_id}/role",
|
|
154
|
-
{ params: { ... }, body: { role_id: "role_..." } },
|
|
155
|
-
);
|
|
156
|
-
|
|
157
|
-
// Invites
|
|
158
|
-
await platformAuth.client.POST(
|
|
159
|
-
"/v1/workspaces/{workspace_slug}/invites",
|
|
160
|
-
{ params: { ... }, body: { email: "...", role_id: "..." } },
|
|
161
|
-
);
|
|
162
|
-
await platformAuth.client.POST("/v1/workspaces/invites/accept", { body: { code } });
|
|
163
|
-
|
|
164
|
-
// Roles
|
|
165
|
-
await platformAuth.client.POST(
|
|
166
|
-
"/v1/workspaces/{workspace_slug}/roles",
|
|
167
|
-
{ params: { ... }, body: { name: "billing-admin", policy_ids: ["pol_..."] } },
|
|
168
|
-
);
|
|
132
|
+
import { PlatformAuth } from "@productcraft/platform-auth";
|
|
133
|
+
const platform = new PlatformAuth({ maxRetries: 3, timeout: 20_000 });
|
|
134
|
+
await platform.oauth.connections.list({}, { maxRetries: 0, signal: AbortSignal.timeout(5_000) });
|
|
169
135
|
```
|
|
170
136
|
|
|
171
|
-
|
|
137
|
+
## Idempotency
|
|
138
|
+
|
|
139
|
+
Writes that support it send an `Idempotency-Key` automatically and reuse it on every retry. Pass your own to make a retry across processes safe. If the API's idempotency store is unavailable it runs the request without it (fail-open), so a retry during that window can execute twice.
|
|
172
140
|
|
|
173
141
|
```ts
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
{ ... },
|
|
178
|
-
);
|
|
179
|
-
|
|
180
|
-
// Create a managed policy. Cookie/JWT only.
|
|
181
|
-
await platformAuth.client.POST(
|
|
182
|
-
"/v1/workspaces/{workspace_slug}/policies",
|
|
183
|
-
{
|
|
184
|
-
params: { ... },
|
|
185
|
-
body: {
|
|
186
|
-
name: "mail-read-only",
|
|
187
|
-
description: "Read-only access to all Mail resources",
|
|
188
|
-
policy: [
|
|
189
|
-
{ effect: "allow", actions: ["mail.read", "mail.list"], resources: ["*"] },
|
|
190
|
-
],
|
|
191
|
-
},
|
|
192
|
-
},
|
|
193
|
-
);
|
|
142
|
+
import { PlatformAuth } from "@productcraft/platform-auth";
|
|
143
|
+
const platform = new PlatformAuth({ apiKey: process.env.PRODUCTCRAFT_API_KEY });
|
|
144
|
+
const workspace = await platform.workspaces.create({ display_name: "Acme Corp", slug: "acme-corp" }, { idempotencyKey: "order-1234" });
|
|
194
145
|
```
|
|
195
146
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
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.
|
|
147
|
+
## Raw responses
|
|
199
148
|
|
|
200
|
-
|
|
149
|
+
Every returned object has a non-enumerable `lastResponse` (`requestId`, `status`, `headers`). `.withResponse()` adds the `Response` and parsed rate-limit headers.
|
|
201
150
|
|
|
202
151
|
```ts
|
|
203
|
-
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
description: "Auth + Mail for the staging pipeline",
|
|
211
|
-
policy: [
|
|
212
|
-
// Permission action prefixes rebranded (heimdall.* → auth.*, envoi.* → mail.*),
|
|
213
|
-
// but resource-URN namespaces intentionally stay pcft:heimdall: / pcft:envoi: —
|
|
214
|
-
// the services still emit the old resource prefixes.
|
|
215
|
-
{ effect: "allow", actions: ["auth.create", "auth.read", "auth.update"], resources: ["pcft:heimdall:app/*"] },
|
|
216
|
-
{ effect: "allow", actions: ["mail.read", "mail.send"], resources: ["*"] },
|
|
217
|
-
],
|
|
218
|
-
},
|
|
219
|
-
},
|
|
220
|
-
);
|
|
221
|
-
|
|
222
|
-
// 2. Mint a PAT that binds to that policy
|
|
223
|
-
const { data: pak } = await platformAuth.client.POST(
|
|
224
|
-
"/v1/workspaces/{workspace_slug}/api-keys",
|
|
225
|
-
{
|
|
226
|
-
params: { path: { workspace_slug: "acme" } },
|
|
227
|
-
body: {
|
|
228
|
-
name: "github-actions-staging-deploy",
|
|
229
|
-
description: "Deploys acme-staging app from CI",
|
|
230
|
-
policy_ids: [policy!.id],
|
|
231
|
-
},
|
|
232
|
-
},
|
|
233
|
-
);
|
|
234
|
-
// pak.token — the plaintext `pcft_live_*` value, returned exactly once
|
|
235
|
-
// pak.record — public metadata (id, prefix, policies, last_used_at, …)
|
|
236
|
-
|
|
237
|
-
// Replace the bound policies on an existing PAK (re-runs caller-narrowing
|
|
238
|
-
// against the merged statement set)
|
|
239
|
-
await platformAuth.client.PATCH(
|
|
240
|
-
"/v1/workspaces/{workspace_slug}/api-keys/{id}/bindings",
|
|
241
|
-
{ params: { ... }, body: { policy_ids: ["<new-policy-uuid>"] } },
|
|
242
|
-
);
|
|
152
|
+
import { PlatformAuth } from "@productcraft/platform-auth";
|
|
153
|
+
const platform = new PlatformAuth({ apiKey: process.env.PRODUCTCRAFT_API_KEY });
|
|
154
|
+
const id = "9b2f6c1e-0d6a-4a63-9a43-2f1c3e4d5a6b";
|
|
155
|
+
const obj = await platform.introspect.workspaces.retrieve(id);
|
|
156
|
+
console.log(obj.lastResponse.requestId);
|
|
157
|
+
const { data, response, requestId, rateLimit } = await platform.introspect.workspaces.retrieve(id).withResponse();
|
|
158
|
+
console.log(data, response.status, requestId, rateLimit?.remaining);
|
|
243
159
|
```
|
|
244
160
|
|
|
245
|
-
|
|
161
|
+
## Testing
|
|
162
|
+
|
|
163
|
+
`@productcraft/platform-auth/testing` exports `createMockFetch()`, a `fetch` that answers every operation with its spec example. Override one operation by name.
|
|
246
164
|
|
|
247
165
|
```ts
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
);
|
|
253
|
-
|
|
254
|
-
// Per-service workspace settings
|
|
255
|
-
await platformAuth.client.PUT(
|
|
256
|
-
"/v1/workspaces/{workspace_slug}/services/{service}/settings",
|
|
257
|
-
{ ... },
|
|
258
|
-
);
|
|
166
|
+
import { PlatformAuth } from "@productcraft/platform-auth";
|
|
167
|
+
import { createMockFetch } from "@productcraft/platform-auth/testing";
|
|
168
|
+
const fetch = createMockFetch();
|
|
169
|
+
const platform = new PlatformAuth({ apiKey: "test", workspaceId: "9b2f6c1e-0d6a-4a63-9a43-2f1c3e4d5a6b", fetch });
|
|
170
|
+
await platform.oauth.connections.list();
|
|
171
|
+
console.log(fetch.calls.length);
|
|
259
172
|
```
|
|
260
173
|
|
|
261
|
-
|
|
174
|
+
## Escape hatch
|
|
175
|
+
|
|
176
|
+
`.client` is the typed `openapi-fetch` client over the same credential and base URL (no retries). Use it for anything the resource methods do not cover.
|
|
262
177
|
|
|
263
178
|
```ts
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
);
|
|
269
|
-
|
|
270
|
-
// Streaming-friendly "recent activity" feed (for the console UI)
|
|
271
|
-
await platformAuth.client.GET(
|
|
272
|
-
"/v1/workspaces/{workspace_slug}/audit-feed",
|
|
273
|
-
{ ... },
|
|
274
|
-
);
|
|
179
|
+
import { PlatformAuth } from "@productcraft/platform-auth";
|
|
180
|
+
const platform = new PlatformAuth({ apiKey: process.env.PRODUCTCRAFT_API_KEY });
|
|
181
|
+
const { data, error } = await platform.client.GET("/.well-known/jwks.json");
|
|
182
|
+
console.log(data, error);
|
|
275
183
|
```
|
|
276
184
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
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.
|
|
185
|
+
<!-- generated:end -->
|
|
280
186
|
|
|
281
187
|
## License
|
|
282
188
|
|
|
283
|
-
|
|
189
|
+
MIT
|