@theholocron/holocron-plugin-clerk 2.0.0-alpha.0 → 2.0.0-alpha.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.
- package/README.md +24 -15
- package/dist/index.d.mts +41 -3
- package/dist/index.mjs +49 -6
- package/package.json +6 -4
package/README.md
CHANGED
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
Clerk plugin for [Holocron](../cli). Implements the `auth` capability
|
|
4
4
|
against [Clerk's Backend REST API](https://clerk.com/docs/reference/backend-api).
|
|
5
5
|
|
|
6
|
+
## Install
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
pnpm add -D @theholocron/holocron-plugin-clerk@alpha
|
|
10
|
+
```
|
|
11
|
+
|
|
6
12
|
## Auth
|
|
7
13
|
|
|
8
14
|
Token resolution order:
|
|
@@ -12,7 +18,7 @@ Token resolution order:
|
|
|
12
18
|
3. `CLERK_SECRET_KEY` env var (Clerk's own default; what their docs reference)
|
|
13
19
|
|
|
14
20
|
> **Why not the `clerk` CLI?** Rando's adapter shells out to `npx clerk@latest
|
|
15
|
-
|
|
21
|
+
api …`, but the `clerk` CLI just wraps the same REST API holocron talks to.
|
|
16
22
|
> Direct REST drops a system-binary dependency and matches the uniform auth/REST
|
|
17
23
|
> pattern across the other holocron plugins.
|
|
18
24
|
|
|
@@ -20,9 +26,9 @@ Token resolution order:
|
|
|
20
26
|
|
|
21
27
|
```jsonc
|
|
22
28
|
{
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
29
|
+
"providers": {
|
|
30
|
+
"auth": "clerk",
|
|
31
|
+
},
|
|
26
32
|
}
|
|
27
33
|
```
|
|
28
34
|
|
|
@@ -32,17 +38,20 @@ for Development, `sk_live_*` for Production.
|
|
|
32
38
|
|
|
33
39
|
## What's implemented
|
|
34
40
|
|
|
35
|
-
| Method
|
|
36
|
-
|
|
|
37
|
-
| `describe`
|
|
38
|
-
| `whoami`
|
|
39
|
-
| `ensureWebhookApp`
|
|
40
|
-
| `getWebhookDashboardUrl`
|
|
41
|
-
| `createUser`
|
|
41
|
+
| Method | What it does |
|
|
42
|
+
| ------------------------ | --------------------------------------------------------------------------- |
|
|
43
|
+
| `describe` | Declares `CLERK_PUBLISHABLE_KEY` + `CLERK_SECRET_KEY` as runtime envs |
|
|
44
|
+
| `whoami` | `GET /users/count` reachability probe (returns user count) |
|
|
45
|
+
| `ensureWebhookApp` | `POST /webhooks/svix` — idempotent; already-exists → `{alreadyExists:true}` |
|
|
46
|
+
| `getWebhookDashboardUrl` | `POST /webhooks/svix_url` — deep-link to Svix dashboard |
|
|
47
|
+
| `createUser` | `POST /users` — seeds users (test fixtures, admin bootstrap) |
|
|
42
48
|
|
|
43
49
|
## Status
|
|
44
50
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
51
|
+
**`v2.0.0-alpha.0`** — published on npm under the `alpha` dist-tag.
|
|
52
|
+
[Release notes](https://github.com/theholocron/holocron/releases/tag/v2.0.0-alpha.0).
|
|
53
|
+
APIs may still shift before stable v2.0.0.
|
|
54
|
+
|
|
55
|
+
Real Svix HMAC verification in `parseWebhook` is deferred to
|
|
56
|
+
[#80](https://github.com/theholocron/holocron/issues/80) — current
|
|
57
|
+
implementation validates shape only.
|
package/dist/index.d.mts
CHANGED
|
@@ -4,10 +4,13 @@ import { Auth, AuthDescription, AuthEvent, AuthIdentity, AuthUser, CreateAuthUse
|
|
|
4
4
|
/**
|
|
5
5
|
* Token resolution for the Clerk plugin.
|
|
6
6
|
*
|
|
7
|
-
* Resolution order
|
|
7
|
+
* Resolution order (matches the standard 4-step precedence set by
|
|
8
|
+
* `.notes/tech-auth-bootstrap.spec.md`):
|
|
8
9
|
* 1. explicit `cliToken` argument (from `--token` flag)
|
|
9
10
|
* 2. HOLOCRON_CLERK_SECRET_KEY env var (preferred — explicit intent)
|
|
10
11
|
* 3. CLERK_SECRET_KEY env var (the default Clerk's docs reference)
|
|
12
|
+
* 4. keyring (com.theholocron.cli / "clerk")
|
|
13
|
+
* 5. AuthError naming all four options + the bootstrap hint
|
|
11
14
|
*
|
|
12
15
|
* The key (sk_test_* / sk_live_*) determines which Clerk instance —
|
|
13
16
|
* Development or Production — every call hits.
|
|
@@ -20,6 +23,8 @@ interface ResolveTokenInput {
|
|
|
20
23
|
cliToken?: string;
|
|
21
24
|
/** Env vars; passed in for testability. Defaults to `process.env`. */
|
|
22
25
|
env?: NodeJS.ProcessEnv;
|
|
26
|
+
/** Keyring lookup fn; passed in for testability. Defaults to `getToken(provider)`. */
|
|
27
|
+
keyring?: (provider: string) => string | null;
|
|
23
28
|
}
|
|
24
29
|
declare function resolveToken(input?: ResolveTokenInput): string;
|
|
25
30
|
//#endregion
|
|
@@ -38,7 +43,7 @@ interface RestClientOptions {
|
|
|
38
43
|
baseUrl?: string;
|
|
39
44
|
}
|
|
40
45
|
interface RequestOptions {
|
|
41
|
-
method?:
|
|
46
|
+
method?: "GET" | "POST" | "PATCH" | "PUT" | "DELETE";
|
|
42
47
|
body?: unknown;
|
|
43
48
|
query?: Record<string, string>;
|
|
44
49
|
}
|
|
@@ -69,6 +74,32 @@ declare class ClerkAuth implements Auth {
|
|
|
69
74
|
//#region src/parse-webhook.d.ts
|
|
70
75
|
declare function parseWebhook(input: ParseWebhookInput): Promise<AuthEvent>;
|
|
71
76
|
//#endregion
|
|
77
|
+
//#region src/verify-token.d.ts
|
|
78
|
+
/**
|
|
79
|
+
* `verifyToken` — plugin-level export used by `holocron auth set` +
|
|
80
|
+
* `holocron auth check`. Hits Clerk's `/v1/instance` endpoint, which
|
|
81
|
+
* requires a valid secret key and returns instance metadata (id,
|
|
82
|
+
* environment_type, etc.). Invalid keys → 401.
|
|
83
|
+
*
|
|
84
|
+
* Clerk doesn't have a traditional user-oriented whoami — the "authed
|
|
85
|
+
* entity" is your Clerk INSTANCE, not a user. `/v1/instance` is the
|
|
86
|
+
* canonical "is this secret key valid?" endpoint.
|
|
87
|
+
*/
|
|
88
|
+
interface VerifyTokenSuccess {
|
|
89
|
+
ok: true;
|
|
90
|
+
subject: string;
|
|
91
|
+
}
|
|
92
|
+
interface VerifyTokenFailure {
|
|
93
|
+
ok: false;
|
|
94
|
+
message: string;
|
|
95
|
+
}
|
|
96
|
+
type VerifyTokenResult = VerifyTokenSuccess | VerifyTokenFailure;
|
|
97
|
+
interface VerifyTokenOptions {
|
|
98
|
+
baseUrl?: string;
|
|
99
|
+
fetch?: typeof fetch;
|
|
100
|
+
}
|
|
101
|
+
declare function verifyToken(token: string, opts?: VerifyTokenOptions): Promise<VerifyTokenResult>;
|
|
102
|
+
//#endregion
|
|
72
103
|
//#region src/index.d.ts
|
|
73
104
|
interface ClerkPluginOptions extends ResolveTokenInput {
|
|
74
105
|
/** Override base URL for tests. */
|
|
@@ -88,5 +119,12 @@ declare function createPlugin(options?: ClerkPluginOptions): {
|
|
|
88
119
|
auth: () => Auth;
|
|
89
120
|
};
|
|
90
121
|
};
|
|
122
|
+
/**
|
|
123
|
+
* One-line hint printed by `holocron auth set clerk` when no token
|
|
124
|
+
* is supplied or the supplied token is rejected. Points operators at
|
|
125
|
+
* the Clerk dashboard's API Keys section — MUST be the SECRET key
|
|
126
|
+
* (sk_test_* / sk_live_*), not the publishable key.
|
|
127
|
+
*/
|
|
128
|
+
declare const AUTH_HINT: string;
|
|
91
129
|
//#endregion
|
|
92
|
-
export { AuthError, ClerkAuth, ClerkPluginOptions, ClerkRestClient, PluginContext, ResolveTokenInput, auth, createContext, createPlugin, parseWebhook, resolveToken };
|
|
130
|
+
export { AUTH_HINT, AuthError, ClerkAuth, ClerkPluginOptions, ClerkRestClient, PluginContext, ResolveTokenInput, type VerifyTokenFailure, type VerifyTokenResult, type VerifyTokenSuccess, auth, createContext, createPlugin, parseWebhook, resolveToken, verifyToken };
|
package/dist/index.mjs
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
import { ProviderApiError, WebhookVerificationError } from "@theholocron/cli";
|
|
1
|
+
import { ProviderApiError, WebhookVerificationError, getToken } from "@theholocron/cli";
|
|
2
2
|
//#region src/auth.ts
|
|
3
3
|
/**
|
|
4
4
|
* Token resolution for the Clerk plugin.
|
|
5
5
|
*
|
|
6
|
-
* Resolution order
|
|
6
|
+
* Resolution order (matches the standard 4-step precedence set by
|
|
7
|
+
* `.notes/tech-auth-bootstrap.spec.md`):
|
|
7
8
|
* 1. explicit `cliToken` argument (from `--token` flag)
|
|
8
9
|
* 2. HOLOCRON_CLERK_SECRET_KEY env var (preferred — explicit intent)
|
|
9
10
|
* 3. CLERK_SECRET_KEY env var (the default Clerk's docs reference)
|
|
11
|
+
* 4. keyring (com.theholocron.cli / "clerk")
|
|
12
|
+
* 5. AuthError naming all four options + the bootstrap hint
|
|
10
13
|
*
|
|
11
14
|
* The key (sk_test_* / sk_live_*) determines which Clerk instance —
|
|
12
15
|
* Development or Production — every call hits.
|
|
@@ -16,8 +19,9 @@ var AuthError = class extends Error {
|
|
|
16
19
|
};
|
|
17
20
|
function resolveToken(input = {}) {
|
|
18
21
|
const env = input.env ?? process.env;
|
|
19
|
-
const
|
|
20
|
-
|
|
22
|
+
const keyring = input.keyring ?? getToken;
|
|
23
|
+
const token = input.cliToken || env["HOLOCRON_CLERK_SECRET_KEY"] || env["CLERK_SECRET_KEY"] || keyring("clerk");
|
|
24
|
+
if (!token) throw new AuthError("no Clerk secret key found. Pass --token <KEY>, set HOLOCRON_CLERK_SECRET_KEY / CLERK_SECRET_KEY, or run: holocron auth set clerk <KEY>");
|
|
21
25
|
return token;
|
|
22
26
|
}
|
|
23
27
|
//#endregion
|
|
@@ -127,7 +131,9 @@ var ClerkRestClient = class {
|
|
|
127
131
|
constructor(opts) {
|
|
128
132
|
this.token = opts.token;
|
|
129
133
|
this.fetchImpl = opts.fetch ?? globalThis.fetch;
|
|
130
|
-
|
|
134
|
+
let url = opts.baseUrl ?? "https://api.clerk.com/v1";
|
|
135
|
+
while (url.endsWith("/")) url = url.slice(0, -1);
|
|
136
|
+
this.baseUrl = url;
|
|
131
137
|
}
|
|
132
138
|
async request(path, opts = {}) {
|
|
133
139
|
const url = new URL(`${this.baseUrl}${path.startsWith("/") ? path : "/" + path}`);
|
|
@@ -230,6 +236,36 @@ async function verifySignature(input) {
|
|
|
230
236
|
return Promise.resolve();
|
|
231
237
|
}
|
|
232
238
|
//#endregion
|
|
239
|
+
//#region src/verify-token.ts
|
|
240
|
+
/**
|
|
241
|
+
* `verifyToken` — plugin-level export used by `holocron auth set` +
|
|
242
|
+
* `holocron auth check`. Hits Clerk's `/v1/instance` endpoint, which
|
|
243
|
+
* requires a valid secret key and returns instance metadata (id,
|
|
244
|
+
* environment_type, etc.). Invalid keys → 401.
|
|
245
|
+
*
|
|
246
|
+
* Clerk doesn't have a traditional user-oriented whoami — the "authed
|
|
247
|
+
* entity" is your Clerk INSTANCE, not a user. `/v1/instance` is the
|
|
248
|
+
* canonical "is this secret key valid?" endpoint.
|
|
249
|
+
*/
|
|
250
|
+
async function verifyToken(token, opts = {}) {
|
|
251
|
+
const restOpts = { token };
|
|
252
|
+
if (opts.baseUrl !== void 0) restOpts.baseUrl = opts.baseUrl;
|
|
253
|
+
if (opts.fetch !== void 0) restOpts.fetch = opts.fetch;
|
|
254
|
+
const rest = new ClerkRestClient(restOpts);
|
|
255
|
+
try {
|
|
256
|
+
const instance = await rest.request("/instance");
|
|
257
|
+
return {
|
|
258
|
+
ok: true,
|
|
259
|
+
subject: `${instance?.environment_type ?? "unknown"} instance ${instance?.id ?? "unknown"}`
|
|
260
|
+
};
|
|
261
|
+
} catch (err) {
|
|
262
|
+
return {
|
|
263
|
+
ok: false,
|
|
264
|
+
message: err instanceof Error ? err.message : String(err)
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
//#endregion
|
|
233
269
|
//#region src/index.ts
|
|
234
270
|
function createContext(options = {}) {
|
|
235
271
|
const restOpts = { token: resolveToken(options) };
|
|
@@ -250,5 +286,12 @@ function createPlugin(options = {}) {
|
|
|
250
286
|
capabilities: { auth: () => auth(ctx) }
|
|
251
287
|
};
|
|
252
288
|
}
|
|
289
|
+
/**
|
|
290
|
+
* One-line hint printed by `holocron auth set clerk` when no token
|
|
291
|
+
* is supplied or the supplied token is rejected. Points operators at
|
|
292
|
+
* the Clerk dashboard's API Keys section — MUST be the SECRET key
|
|
293
|
+
* (sk_test_* / sk_live_*), not the publishable key.
|
|
294
|
+
*/
|
|
295
|
+
const AUTH_HINT = "grab your SECRET key (sk_test_* / sk_live_*) at https://dashboard.clerk.com → API Keys, then run: holocron auth set clerk <KEY>. Do NOT use the publishable key.";
|
|
253
296
|
//#endregion
|
|
254
|
-
export { AuthError, ClerkAuth, ClerkRestClient, auth, createContext, createPlugin, parseWebhook, resolveToken };
|
|
297
|
+
export { AUTH_HINT, AuthError, ClerkAuth, ClerkRestClient, auth, createContext, createPlugin, parseWebhook, resolveToken, verifyToken };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@theholocron/holocron-plugin-clerk",
|
|
3
|
-
"version": "2.0.0-alpha.
|
|
3
|
+
"version": "2.0.0-alpha.5",
|
|
4
4
|
"description": "Holocron plugin for Clerk. Implements the auth capability against Clerk's Backend REST API.",
|
|
5
5
|
"homepage": "https://github.com/theholocron/holocron/tree/main/packages/holocron-plugin-clerk#readme",
|
|
6
6
|
"bugs": "https://github.com/theholocron/holocron/issues",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
}
|
|
22
22
|
},
|
|
23
23
|
"peerDependencies": {
|
|
24
|
-
"@theholocron/cli": "2.0.0-alpha.
|
|
24
|
+
"@theholocron/cli": "2.0.0-alpha.5"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"@theholocron/tsconfig": "^4.1.0",
|
|
@@ -32,7 +32,8 @@
|
|
|
32
32
|
"typescript": "^5.9.3",
|
|
33
33
|
"vitest": "^3.2.6",
|
|
34
34
|
"tsdown": "^0.22.3",
|
|
35
|
-
"
|
|
35
|
+
"tsx": "^4.22.4",
|
|
36
|
+
"@theholocron/cli": "2.0.0-alpha.5"
|
|
36
37
|
},
|
|
37
38
|
"publishConfig": {
|
|
38
39
|
"access": "public"
|
|
@@ -47,7 +48,8 @@
|
|
|
47
48
|
"typecheck": "tsc --noEmit",
|
|
48
49
|
"test": "vitest run",
|
|
49
50
|
"test:watch": "vitest",
|
|
50
|
-
"test:coverage": "vitest run --coverage"
|
|
51
|
+
"test:coverage": "vitest run --coverage",
|
|
52
|
+
"validate": "tsx scripts/validate.mjs"
|
|
51
53
|
},
|
|
52
54
|
"types": "./dist/index.d.mts"
|
|
53
55
|
}
|