@holo-js/auth-clerk 0.3.27 → 0.4.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Holo-JS contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ 758123eab196506094be9bede1e3fcfd8ca970c0574dee155a030a4882ecc655
package/dist/index.d.ts CHANGED
@@ -244,4 +244,5 @@ declare const clerkAuthInternals: {
244
244
  resolvePrimaryEmail: typeof resolvePrimaryEmail;
245
245
  };
246
246
 
247
- export { type ClerkAuthBindings, ClerkAuthConflictError, type ClerkAuthFacade, type ClerkAuthenticatedUser, type ClerkAuthenticationResult, type ClerkCompleteAuthData, type ClerkCompleteAuthResult, type ClerkEmailAddress, type ClerkHostedAuthFailureFields, type ClerkLogoutData, type ClerkLogoutErrorCode, type ClerkLogoutResult, type ClerkLogoutSession, type ClerkProviderRuntime, type ClerkRequestInput, type ClerkRequestLike, type ClerkSyncStatus, type ClerkUserAttributeValue, type ClerkUserAttributes, type ClerkUserProfile, type ClerkVerifiedSession, type ClerkVerifyRequestContext, type ClerkVerifySessionContext, type ConfigureClerkAuthRuntimeOptions, type HostedIdentityRecord, type HostedIdentityStore, authenticate, clerkAuth, clerkAuthInternals, completeClerkAuth, configureClerkAuthRuntime, loginWithClerk, logoutWithClerk, registerWithClerk, resetClerkAuthRuntime, syncIdentity, verifyRequest, verifySession };
247
+ export { ClerkAuthConflictError, authenticate, clerkAuth, clerkAuthInternals, completeClerkAuth, configureClerkAuthRuntime, loginWithClerk, logoutWithClerk, registerWithClerk, resetClerkAuthRuntime, syncIdentity, verifyRequest, verifySession };
248
+ export type { ClerkAuthBindings, ClerkAuthFacade, ClerkAuthenticatedUser, ClerkAuthenticationResult, ClerkCompleteAuthData, ClerkCompleteAuthResult, ClerkEmailAddress, ClerkHostedAuthFailureFields, ClerkLogoutData, ClerkLogoutErrorCode, ClerkLogoutResult, ClerkLogoutSession, ClerkProviderRuntime, ClerkRequestInput, ClerkRequestLike, ClerkSyncStatus, ClerkUserAttributeValue, ClerkUserAttributes, ClerkUserProfile, ClerkVerifiedSession, ClerkVerifyRequestContext, ClerkVerifySessionContext, ConfigureClerkAuthRuntimeOptions, HostedIdentityRecord, HostedIdentityStore };
package/dist/index.mjs CHANGED
@@ -32,6 +32,9 @@ function verifyJwtSignatureWithJwk(token, jwk) {
32
32
  unsupportedAlgorithmMessage: (algorithm) => `[@holo-js/auth-clerk] Unsupported Clerk JWT algorithm "${algorithm}".`
33
33
  });
34
34
  }
35
+ function resetClerkJwksCache() {
36
+ clerkJwksCache.clear();
37
+ }
35
38
  function resolveClerkJwksUrl(config) {
36
39
  const frontendApi = config.frontendApi?.trim();
37
40
  if (frontendApi) {
@@ -40,11 +43,10 @@ function resolveClerkJwksUrl(config) {
40
43
  const apiUrl = config.apiUrl?.trim() || CLERK_API_BASE_URL;
41
44
  return `${apiUrl.replace(/\/$/, "")}/v1/jwks`;
42
45
  }
43
- async function fetchClerkJwks(jwksUrl, options = {}) {
44
- return authRuntimeInternals.jwt.fetchCachedJwks(jwksUrl, {
45
- cache: clerkJwksCache,
46
+ async function resolveClerkJwk(jwksUrl, kid) {
47
+ return authRuntimeInternals.jwt.resolveCachedJwk(clerkJwksCache, jwksUrl, {
46
48
  requestUrl: jwksUrl,
47
- refresh: options.refresh,
49
+ kid,
48
50
  errorMessage: `[@holo-js/auth-clerk] Failed to load Clerk JWKS from "${jwksUrl}".`
49
51
  });
50
52
  }
@@ -88,15 +90,7 @@ function getBindings() {
88
90
  async function verifyClerkSessionToken(token, config, authorizedParties = []) {
89
91
  const parsed = parseJwt(token);
90
92
  const headerKid = typeof parsed.header.kid === "string" ? parsed.header.kid : void 0;
91
- const jwksUrl = resolveClerkJwksUrl(config);
92
- const resolveKey = async (refresh = false) => {
93
- const keys = await fetchClerkJwks(jwksUrl, { refresh });
94
- return headerKid ? keys.find((candidate) => candidate.kid === headerKid) : keys[0];
95
- };
96
- let key = await resolveKey();
97
- if (!key || !verifyJwtSignatureWithJwk(parsed, key)) {
98
- key = await resolveKey(true);
99
- }
93
+ const key = await resolveClerkJwk(resolveClerkJwksUrl(config), headerKid);
100
94
  if (!key || !verifyJwtSignatureWithJwk(parsed, key)) {
101
95
  throw new Error("[@holo-js/auth-clerk] Clerk token signature verification failed.");
102
96
  }
@@ -120,10 +114,13 @@ async function verifyClerkSessionToken(token, config, authorizedParties = []) {
120
114
  }
121
115
  const azp = typeof parsed.payload.azp === "string" ? parsed.payload.azp.trim() : "";
122
116
  const allowedAuthorizedParties = authorizedParties.map((value) => value.trim()).filter(Boolean);
123
- if (allowedAuthorizedParties.length > 0 && !azp) {
117
+ if (allowedAuthorizedParties.length === 0) {
118
+ throw new Error("[@holo-js/auth-clerk] Clerk token authorized party cannot be verified because no authorized party is configured.");
119
+ }
120
+ if (!azp) {
124
121
  throw new Error("[@holo-js/auth-clerk] Clerk token did not include an authorized party.");
125
122
  }
126
- if (azp && allowedAuthorizedParties.length > 0 && !allowedAuthorizedParties.includes(azp)) {
123
+ if (!allowedAuthorizedParties.includes(azp)) {
127
124
  throw new Error(`[@holo-js/auth-clerk] Clerk token authorized party "${azp}" is not allowed.`);
128
125
  }
129
126
  return Object.freeze({
@@ -186,6 +183,7 @@ function createDefaultProviderRuntime(providerName, config) {
186
183
  config.apiUrl ?? "",
187
184
  config.frontendApi ?? "",
188
185
  config.secretKey ?? "",
186
+ config.redirectUri ?? "",
189
187
  [...config.authorizedParties].sort()
190
188
  ]);
191
189
  const existing = clerkDefaultProviderRuntimeCache.get(cacheKey);
@@ -194,7 +192,7 @@ function createDefaultProviderRuntime(providerName, config) {
194
192
  }
195
193
  const runtime = Object.freeze({
196
194
  async verifySession({ token }) {
197
- const claims = await verifyClerkSessionToken(token, config, config.authorizedParties);
195
+ const claims = await verifyClerkSessionToken(token, config, resolveAuthorizedParties(config));
198
196
  const profile = await fetchClerkUserProfile(claims.sub, config);
199
197
  return Object.freeze({
200
198
  sessionId: claims.sessionId,
@@ -421,12 +419,15 @@ function resolveDisplayName(profile) {
421
419
  const fullName = [profile.firstName?.trim(), profile.lastName?.trim()].filter(Boolean).join(" ").trim();
422
420
  return fullName || profile.email || profile.id;
423
421
  }
422
+ function resolveHostedPlaceholderEmail(profile) {
423
+ return `${profile.id}@clerk.hosted.local`;
424
+ }
424
425
  function resolveEmailForCreation(profile) {
425
- const primary = resolvePrimaryEmail(profile).email;
426
- if (primary) {
427
- return primary;
426
+ const primary = resolvePrimaryEmail(profile);
427
+ if (primary.email && primary.emailVerified) {
428
+ return primary.email;
428
429
  }
429
- return `${profile.id}@clerk.hosted.local`;
430
+ return resolveHostedPlaceholderEmail(profile);
430
431
  }
431
432
  function toAdapterInput(input) {
432
433
  return Object.freeze(Object.fromEntries(
@@ -449,12 +450,14 @@ function resolveCreateUserInput(profile, mapper) {
449
450
  }, profile));
450
451
  }
451
452
  function resolveUpdateUserInput(profile, mapper) {
452
- return toAdapterInput(withRequiredUserFields({
453
- email: profile.email.trim() || void 0,
453
+ const verifiedEmail = profile.emailVerified ? profile.email.trim() || void 0 : void 0;
454
+ return toAdapterInput({
455
+ name: resolveDisplayName(profile),
456
+ email: verifiedEmail,
454
457
  avatar: profile.imageUrl,
455
- email_verified_at: profile.emailVerified ? /* @__PURE__ */ new Date() : void 0,
458
+ email_verified_at: verifiedEmail ? /* @__PURE__ */ new Date() : void 0,
456
459
  ...mapper?.(profile) ?? {}
457
- }, profile));
460
+ });
458
461
  }
459
462
  function normalizeHostedProfile(profile) {
460
463
  const resolvedEmail = resolvePrimaryEmail(profile);
@@ -545,14 +548,15 @@ async function appendClerkResponseCookies(headers) {
545
548
  }
546
549
  }
547
550
  function getHoloSessionIdFromRequest(request) {
548
- const cookieHeader = authRuntimeInternals3.getRuntimeBindings().session.sessionCookie("");
551
+ const { session } = authRuntimeInternals3.getRuntimeBindings();
552
+ const cookieHeader = session.sessionCookie("");
549
553
  const separator = cookieHeader.indexOf("=");
550
554
  const cookieName = separator > 0 ? decodeURIComponent(cookieHeader.slice(0, separator)) : "";
551
555
  if (!cookieName) {
552
556
  return null;
553
557
  }
554
- const cookies = parseCookieHeader(request.headers.get("cookie"));
555
- return cookies[cookieName] ?? null;
558
+ const value = parseCookieHeader(request.headers.get("cookie"))[cookieName];
559
+ return value ? session.decryptCookie(cookieName, value) ?? null : null;
556
560
  }
557
561
  async function reuseExistingHoloSession(request, authenticated) {
558
562
  const bindings = authRuntimeInternals3.getRuntimeBindings();
@@ -593,11 +597,19 @@ async function verifySession(token, provider) {
593
597
  config: providerConfig
594
598
  });
595
599
  }
596
- function resolveRequestAuthorizedParties(config, request) {
600
+ function toOrigin(value) {
601
+ try {
602
+ return new URL(value).origin;
603
+ } catch {
604
+ return value;
605
+ }
606
+ }
607
+ function resolveAuthorizedParties(config) {
608
+ const redirectOrigin = config.redirectUri?.trim() ? new URL(config.redirectUri.trim()).origin : void 0;
597
609
  return Object.freeze([
598
610
  .../* @__PURE__ */ new Set([
599
- ...config.authorizedParties,
600
- new URL(request.url).origin
611
+ ...config.authorizedParties.map((value) => toOrigin(value.trim())),
612
+ ...redirectOrigin ? [redirectOrigin] : []
601
613
  ])
602
614
  ]);
603
615
  }
@@ -627,7 +639,7 @@ async function verifyRequestWithClerkBackendSdk(request, providerConfig) {
627
639
  });
628
640
  const authenticateOptions = {
629
641
  apiUrl: providerConfig.apiUrl,
630
- authorizedParties: [...resolveRequestAuthorizedParties(providerConfig, request)],
642
+ authorizedParties: [...resolveAuthorizedParties(providerConfig)],
631
643
  publishableKey,
632
644
  secretKey
633
645
  };
@@ -650,7 +662,7 @@ async function verifyRequestWithClerkBackendSdk(request, providerConfig) {
650
662
  return verifyDefaultSessionToken(
651
663
  requestState.token,
652
664
  providerConfig,
653
- resolveRequestAuthorizedParties(providerConfig, request)
665
+ resolveAuthorizedParties(providerConfig)
654
666
  );
655
667
  }
656
668
  async function verifyRequest(input, provider) {
@@ -685,7 +697,7 @@ async function verifyRequest(input, provider) {
685
697
  config: providerConfig
686
698
  });
687
699
  }
688
- return verifyDefaultSessionToken(token, providerConfig, resolveRequestAuthorizedParties(providerConfig, request));
700
+ return verifyDefaultSessionToken(token, providerConfig, resolveAuthorizedParties(providerConfig));
689
701
  }
690
702
  async function syncIdentity(session, provider, options = {}) {
691
703
  const providerConfig = getConfiguredProviderConfig(provider);
@@ -706,6 +718,7 @@ async function syncIdentity(session, provider, options = {}) {
706
718
  ownership: "serialized-claim",
707
719
  verifiedEmail,
708
720
  email: resolvedEmail.email,
721
+ placeholderEmail: resolveHostedPlaceholderEmail(profile),
709
722
  errorPrefix: "[@holo-js/auth-clerk]",
710
723
  identityLabel: "Clerk",
711
724
  createUserInput: () => resolveCreateUserInput(profile, options.user),
@@ -912,6 +925,7 @@ function configureClerkAuthRuntime(bindings) {
912
925
  function resetClerkAuthRuntime() {
913
926
  getRuntimeState().bindings = void 0;
914
927
  clerkDefaultProviderRuntimeCache.clear();
928
+ resetClerkJwksCache();
915
929
  }
916
930
  var clerkAuth = Object.freeze({
917
931
  authenticate,
package/package.json CHANGED
@@ -1,9 +1,14 @@
1
1
  {
2
2
  "name": "@holo-js/auth-clerk",
3
- "version": "0.3.27",
3
+ "version": "0.4.0",
4
4
  "description": "Holo-JS Framework - Clerk auth integration contracts",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/cobraprojects/holo-js.git",
10
+ "directory": "packages/auth-clerk"
11
+ },
7
12
  "exports": {
8
13
  ".": {
9
14
  "types": "./dist/index.d.ts",
@@ -14,7 +19,8 @@
14
19
  "main": "./dist/index.mjs",
15
20
  "types": "./dist/index.d.ts",
16
21
  "files": [
17
- "dist"
22
+ "dist",
23
+ "LICENSE"
18
24
  ],
19
25
  "scripts": {
20
26
  "build": "tsup",
@@ -23,17 +29,17 @@
23
29
  "test": "vitest --run"
24
30
  },
25
31
  "dependencies": {
26
- "@clerk/backend": "^3.4.7",
27
- "@holo-js/auth": "^0.3.27",
28
- "@holo-js/session": "^0.3.27"
32
+ "@clerk/backend": "^3.26.1",
33
+ "@holo-js/auth": "^0.4.0",
34
+ "@holo-js/session": "^0.4.0"
29
35
  },
30
36
  "devDependencies": {
31
- "@types/node": "^22.10.2",
32
- "tsup": "^8.3.5",
33
- "typescript": "^5.7.2",
34
- "vitest": "^4.1.5"
37
+ "@types/node": "^22.20.5",
38
+ "tsup": "^9.0.0",
39
+ "typescript": "^6.0.3",
40
+ "vitest": "^5.0.5"
35
41
  },
36
42
  "engines": {
37
- "node": "^22.12.0 || ^24.0.0 || ^26.0.0"
43
+ "node": "^22.18.0 || ^24.0.0 || ^26.0.0"
38
44
  }
39
45
  }