@holo-js/auth-clerk 0.1.9 → 0.2.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/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { AuthResult, AuthLogoutResult, AuthFieldErrors, AuthenticatedAuthUser, AuthEstablishedSession, authRuntimeInternals } from '@holo-js/auth';
1
+ import { AuthResult, AuthenticatedAuthUser, AuthEstablishedSession, AuthFieldErrors, AuthLogoutResult, authRuntimeInternals } from '@holo-js/auth';
2
2
  import { AuthClerkProviderConfig, NormalizedAuthClerkProviderConfig } from '@holo-js/config';
3
3
 
4
4
  interface ClerkEmailAddress {
@@ -150,7 +150,7 @@ interface ClerkAuthFacade {
150
150
  logoutWithClerk(request: ClerkRequestInput, options?: {
151
151
  readonly provider?: string;
152
152
  readonly returnTo?: string;
153
- }): Promise<ClerkLogoutResult>;
153
+ }): Promise<Response>;
154
154
  completeClerkAuth<TUserAttributes extends ClerkUserAttributes = ClerkDefaultUserAttributes$1>(request: ClerkRequestInput, options?: {
155
155
  readonly provider?: string;
156
156
  readonly user?: (clerkUser: ClerkUserProfile) => TUserAttributes;
@@ -218,7 +218,7 @@ declare function registerWithClerk(_input: ClerkRequestInput, options?: {
218
218
  declare function logoutWithClerk(input: ClerkRequestInput, options?: {
219
219
  readonly provider?: string;
220
220
  readonly returnTo?: string;
221
- }): Promise<ClerkLogoutResult>;
221
+ }): Promise<Response>;
222
222
  declare function completeClerkAuth<TUserAttributes extends ClerkUserAttributes = ClerkDefaultUserAttributes>(input: ClerkRequestInput, options?: CompleteClerkAuthOptions<TUserAttributes>): Promise<ClerkCompleteAuthResult<TUserAttributes>>;
223
223
  declare function configureClerkAuthRuntime(bindings?: ConfigureClerkAuthRuntimeOptions): void;
224
224
  declare function resetClerkAuthRuntime(): void;
@@ -245,4 +245,4 @@ declare const clerkAuthInternals: {
245
245
  resolvePrimaryEmail: typeof resolvePrimaryEmail;
246
246
  };
247
247
 
248
- 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 ClerkRequestHeaders, 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 };
248
+ 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 };
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/index.ts
2
- import { authRuntimeInternals, getAuthRuntime } from "@holo-js/auth";
2
+ import { authRuntimeInternals as authRuntimeInternals3, getAuthRuntime } from "@holo-js/auth";
3
3
  import { parseCookieHeader } from "@holo-js/session";
4
4
 
5
5
  // src/contracts.ts
@@ -18,41 +18,19 @@ var ClerkAuthConflictError = class extends Error {
18
18
  };
19
19
 
20
20
  // src/jwt.ts
21
- import { createPublicKey, verify as verifySignature } from "crypto";
21
+ import { authRuntimeInternals } from "@holo-js/auth";
22
22
  var CLERK_API_BASE_URL = "https://api.clerk.com";
23
23
  var clerkJwksCache = /* @__PURE__ */ new Map();
24
- function decodeJwtSegment(value, label) {
25
- try {
26
- return JSON.parse(Buffer.from(value, "base64url").toString("utf8"));
27
- } catch {
28
- throw new Error(`[@holo-js/auth-clerk] Clerk token ${label} was not valid JSON.`);
29
- }
30
- }
31
24
  function parseJwt(token) {
32
- const segments = token.split(".");
33
- if (segments.length !== 3 || !segments[0] || !segments[1] || !segments[2]) {
34
- throw new Error("[@holo-js/auth-clerk] Clerk token was not a valid JWT.");
35
- }
36
- return {
37
- header: decodeJwtSegment(segments[0], "header"),
38
- payload: decodeJwtSegment(segments[1], "payload"),
39
- signature: Buffer.from(segments[2], "base64url"),
40
- signingInput: Buffer.from(`${segments[0]}.${segments[1]}`, "utf8")
41
- };
25
+ return authRuntimeInternals.jwt.parseJwt(token, {
26
+ errorPrefix: "[@holo-js/auth-clerk] Clerk token",
27
+ malformedMessage: "[@holo-js/auth-clerk] Clerk token was not a valid JWT."
28
+ });
42
29
  }
43
30
  function verifyJwtSignatureWithJwk(token, jwk) {
44
- const algorithm = typeof token.header.alg === "string" ? token.header.alg : "";
45
- const key = createPublicKey({ key: jwk, format: "jwk" });
46
- switch (algorithm) {
47
- case "RS256":
48
- return verifySignature("RSA-SHA256", token.signingInput, key, token.signature);
49
- case "RS384":
50
- return verifySignature("RSA-SHA384", token.signingInput, key, token.signature);
51
- case "RS512":
52
- return verifySignature("RSA-SHA512", token.signingInput, key, token.signature);
53
- default:
54
- throw new Error(`[@holo-js/auth-clerk] Unsupported Clerk JWT algorithm "${algorithm || "unknown"}".`);
55
- }
31
+ return authRuntimeInternals.jwt.verifyJwtSignatureWithJwk(token, jwk, {
32
+ unsupportedAlgorithmMessage: (algorithm) => `[@holo-js/auth-clerk] Unsupported Clerk JWT algorithm "${algorithm}".`
33
+ });
56
34
  }
57
35
  function resolveClerkJwksUrl(config) {
58
36
  const frontendApi = config.frontendApi?.trim();
@@ -63,32 +41,18 @@ function resolveClerkJwksUrl(config) {
63
41
  return `${apiUrl.replace(/\/$/, "")}/v1/jwks`;
64
42
  }
65
43
  async function fetchClerkJwks(jwksUrl, options = {}) {
66
- if (options.refresh) {
67
- clerkJwksCache.delete(jwksUrl);
68
- }
69
- const existing = clerkJwksCache.get(jwksUrl);
70
- if (existing) {
71
- return existing;
72
- }
73
- const pending = (async () => {
74
- const response = await fetch(jwksUrl, {
75
- headers: {
76
- accept: "application/json"
77
- }
78
- });
79
- if (!response.ok) {
80
- throw new Error(`[@holo-js/auth-clerk] Failed to load Clerk JWKS from "${jwksUrl}".`);
81
- }
82
- const payload = await response.json();
83
- return payload.keys ?? [];
84
- })();
85
- clerkJwksCache.set(jwksUrl, pending);
86
- try {
87
- return await pending;
88
- } catch (error) {
89
- clerkJwksCache.delete(jwksUrl);
90
- throw error;
91
- }
44
+ return authRuntimeInternals.jwt.fetchCachedJwks(jwksUrl, {
45
+ cache: clerkJwksCache,
46
+ requestUrl: jwksUrl,
47
+ refresh: options.refresh,
48
+ errorMessage: `[@holo-js/auth-clerk] Failed to load Clerk JWKS from "${jwksUrl}".`
49
+ });
50
+ }
51
+
52
+ // src/request.ts
53
+ import { authRuntimeInternals as authRuntimeInternals2 } from "@holo-js/auth";
54
+ function normalizeClerkRequest(input) {
55
+ return authRuntimeInternals2.normalizeRequestInput(input);
92
56
  }
93
57
 
94
58
  // src/index.ts
@@ -112,99 +76,6 @@ function getRuntimeState() {
112
76
  function throwUnconfigured() {
113
77
  throw new Error("[@holo-js/auth-clerk] Clerk auth runtime is not configured yet.");
114
78
  }
115
- function isPlainHeaderRecord(value) {
116
- return Boolean(value) && typeof value === "object" && Object.getPrototypeOf(value) === Object.prototype;
117
- }
118
- function appendKnownHeaders(headers, input) {
119
- for (const name of ["authorization", "cookie", "host", "x-forwarded-host", "x-forwarded-proto"]) {
120
- const value = input.get?.(name);
121
- if (typeof value === "string" && value) {
122
- headers.set(name, value);
123
- }
124
- }
125
- }
126
- function hasHeaderForEach(input) {
127
- return !Array.isArray(input) && "forEach" in input && typeof input.forEach === "function";
128
- }
129
- function hasHeaderEntries(input) {
130
- return !Array.isArray(input) && "entries" in input && typeof input.entries === "function";
131
- }
132
- function hasHeaderGet(input) {
133
- return !Array.isArray(input) && "get" in input && typeof input.get === "function";
134
- }
135
- function normalizeRequestHeaders(input) {
136
- const headers = new Headers();
137
- if (!input) {
138
- return headers;
139
- }
140
- if (input instanceof Headers || Array.isArray(input)) {
141
- new Headers(input).forEach((value, name) => headers.append(name, value));
142
- return headers;
143
- }
144
- if (hasHeaderForEach(input)) {
145
- input.forEach((value, name) => headers.append(name, value));
146
- return headers;
147
- }
148
- if (hasHeaderEntries(input)) {
149
- for (const [name, value] of input.entries()) {
150
- headers.append(name, value);
151
- }
152
- return headers;
153
- }
154
- if (hasHeaderGet(input)) {
155
- appendKnownHeaders(headers, input);
156
- return headers;
157
- }
158
- if (isPlainHeaderRecord(input)) {
159
- for (const [name, value] of Object.entries(input)) {
160
- if (typeof value === "string") {
161
- headers.append(name, value);
162
- continue;
163
- }
164
- if (Array.isArray(value)) {
165
- const separator = name.toLowerCase() === "cookie" ? "; " : ",";
166
- const joined = value.filter((entry) => typeof entry === "string").join(separator);
167
- if (joined) {
168
- headers.append(name, joined);
169
- }
170
- }
171
- }
172
- }
173
- return headers;
174
- }
175
- function getRequestFromLikeInput(input) {
176
- return input.request ?? input.web?.request ?? (input.req instanceof Request ? input.req : void 0);
177
- }
178
- function getRequestLikeHeaders(input) {
179
- return input.headers ?? (typeof input.req === "object" && !(input.req instanceof Request) ? input.req.headers : void 0) ?? input.node?.req?.headers;
180
- }
181
- function getRequestLikeMethod(input) {
182
- return input.method ?? (typeof input.req === "object" && !(input.req instanceof Request) ? input.req.method : void 0) ?? input.node?.req?.method ?? "GET";
183
- }
184
- function getRequestLikeUrl(input, headers) {
185
- const url = (typeof input.url === "string" ? input.url : input.url?.toString()) ?? (typeof input.req === "object" && !(input.req instanceof Request) ? input.req.url : void 0) ?? input.node?.req?.url ?? input.path ?? "/";
186
- try {
187
- return new URL(url).toString();
188
- } catch {
189
- const protocol = headers.get("x-forwarded-proto") ?? "http";
190
- const host = headers.get("x-forwarded-host") ?? headers.get("host") ?? "localhost";
191
- return new URL(url, `${protocol}://${host}`).toString();
192
- }
193
- }
194
- function normalizeClerkRequest(input) {
195
- if (input instanceof Request) {
196
- return input;
197
- }
198
- const request = getRequestFromLikeInput(input);
199
- if (request) {
200
- return request;
201
- }
202
- const headers = normalizeRequestHeaders(getRequestLikeHeaders(input));
203
- return new Request(getRequestLikeUrl(input, headers), {
204
- method: getRequestLikeMethod(input),
205
- headers
206
- });
207
- }
208
79
  function getBindings() {
209
80
  const clerkBindings = getRuntimeState().bindings;
210
81
  if (!clerkBindings?.identityStore) {
@@ -340,7 +211,7 @@ function createDefaultProviderRuntime(providerName, config) {
340
211
  return runtime;
341
212
  }
342
213
  function resolveConfiguredProviderName(provider) {
343
- const bindings = authRuntimeInternals.getRuntimeBindings();
214
+ const bindings = authRuntimeInternals3.getRuntimeBindings();
344
215
  if (provider?.trim()) {
345
216
  return provider.trim();
346
217
  }
@@ -365,7 +236,7 @@ function isNormalizedClerkProviderConfig(value) {
365
236
  }
366
237
  function getConfiguredProviderConfig(provider) {
367
238
  const providerName = resolveConfiguredProviderName(provider);
368
- const authBindings = authRuntimeInternals.getRuntimeBindings();
239
+ const authBindings = authRuntimeInternals3.getRuntimeBindings();
369
240
  const configured = authBindings.config.clerk[providerName];
370
241
  if (!isNormalizedClerkProviderConfig(configured)) {
371
242
  throw new Error(`[@holo-js/auth-clerk] Clerk provider "${providerName}" is not configured in auth.clerk.`);
@@ -455,7 +326,7 @@ function createClerkErrorResponse(error, code) {
455
326
  });
456
327
  }
457
328
  function resolveGuardAndProvider(provider) {
458
- const authBindings = authRuntimeInternals.getRuntimeBindings();
329
+ const authBindings = authRuntimeInternals3.getRuntimeBindings();
459
330
  const providerConfig = getConfiguredProviderConfig(provider);
460
331
  const guardName = providerConfig.guard ?? authBindings.config.defaults.guard;
461
332
  const guard = authBindings.config.guards[guardName];
@@ -681,7 +552,7 @@ async function ensureNoUnexpectedEmailCollision(adapter, providerName, profile,
681
552
  }
682
553
  }
683
554
  function isEmailVerificationRequired() {
684
- return authRuntimeInternals.getRuntimeBindings().config.emailVerification.required === true;
555
+ return authRuntimeInternals3.getRuntimeBindings().config.emailVerification.required === true;
685
556
  }
686
557
  function createIdentityRecord(input) {
687
558
  const now = /* @__PURE__ */ new Date();
@@ -790,7 +661,7 @@ function getSetCookieHeaders(headers) {
790
661
  return combined ? splitSetCookieHeader(combined) : [];
791
662
  }
792
663
  async function appendClerkResponseCookies(headers) {
793
- const append = authRuntimeInternals.getRuntimeBindings().context.appendResponseCookie;
664
+ const append = authRuntimeInternals3.getRuntimeBindings().context.appendResponseCookie;
794
665
  if (!append) {
795
666
  return;
796
667
  }
@@ -799,7 +670,7 @@ async function appendClerkResponseCookies(headers) {
799
670
  }
800
671
  }
801
672
  function getHoloSessionIdFromRequest(request) {
802
- const cookieHeader = authRuntimeInternals.getRuntimeBindings().session.sessionCookie("");
673
+ const cookieHeader = authRuntimeInternals3.getRuntimeBindings().session.sessionCookie("");
803
674
  const separator = cookieHeader.indexOf("=");
804
675
  const cookieName = separator > 0 ? decodeURIComponent(cookieHeader.slice(0, separator)) : "";
805
676
  if (!cookieName) {
@@ -809,14 +680,14 @@ function getHoloSessionIdFromRequest(request) {
809
680
  return cookies[cookieName] ?? null;
810
681
  }
811
682
  async function reuseExistingHoloSession(request, authenticated) {
812
- const bindings = authRuntimeInternals.getRuntimeBindings();
683
+ const bindings = authRuntimeInternals3.getRuntimeBindings();
813
684
  const sessionId = getHoloSessionIdFromRequest(request);
814
685
  if (!sessionId) {
815
686
  return null;
816
687
  }
817
688
  bindings.context.setSessionId(authenticated.guard, sessionId);
818
689
  const record = await bindings.session.read(sessionId);
819
- const payload = authRuntimeInternals.readSessionPayload(record, authenticated.guard);
690
+ const payload = authRuntimeInternals3.readSessionPayload(record, authenticated.guard);
820
691
  if (!payload || payload.guard !== authenticated.guard || payload.provider !== authenticated.authProvider || String(payload.userId) !== String(authenticated.user.id)) {
821
692
  return null;
822
693
  }
@@ -893,8 +764,8 @@ async function verifyRequestWithClerkBackendSdk(request, providerConfig) {
893
764
  if (requestState.status === "handshake") {
894
765
  const redirectUrl = requestState.headers.get("location");
895
766
  if (redirectUrl && !hasClerkHandshakeAttempt(request)) {
896
- await authRuntimeInternals.redirectResponse(
897
- authRuntimeInternals.getRuntimeBindings(),
767
+ await authRuntimeInternals3.redirectResponse(
768
+ authRuntimeInternals3.getRuntimeBindings(),
898
769
  new URL(redirectUrl, request.url).toString(),
899
770
  307
900
771
  );
@@ -1083,7 +954,7 @@ async function authenticate(input, provider) {
1083
954
  return null;
1084
955
  }
1085
956
  const authenticated = await syncIdentity(session, provider);
1086
- const authSession = await reuseExistingHoloSession(request, authenticated) ?? await authRuntimeInternals.establishSessionForUser(authenticated.user, {
957
+ const authSession = await reuseExistingHoloSession(request, authenticated) ?? await authRuntimeInternals3.establishSessionForUser(authenticated.user, {
1087
958
  guard: authenticated.guard,
1088
959
  provider: authenticated.authProvider,
1089
960
  payload: createClerkSessionPayload(authenticated, session)
@@ -1132,13 +1003,13 @@ async function registerWithClerk(_input, options = {}) {
1132
1003
  }
1133
1004
  }
1134
1005
  async function readCurrentClerkLogoutSession(guard, providerName) {
1135
- const bindings = authRuntimeInternals.getRuntimeBindings();
1006
+ const bindings = authRuntimeInternals3.getRuntimeBindings();
1136
1007
  const sessionId = bindings.context.getSessionId(guard);
1137
1008
  if (!sessionId) {
1138
1009
  return null;
1139
1010
  }
1140
1011
  const record = await bindings.session.read(sessionId);
1141
- return getClerkLogoutSession(authRuntimeInternals.readSessionPayload(record, guard), providerName);
1012
+ return getClerkLogoutSession(authRuntimeInternals3.readSessionPayload(record, guard), providerName);
1142
1013
  }
1143
1014
  async function revokeClerkSession(config, sessionId) {
1144
1015
  const secretKey = config.secretKey?.trim();
@@ -1165,24 +1036,28 @@ async function logoutWithClerk(input, options = {}) {
1165
1036
  const { guard } = resolveGuardAndProvider(providerName);
1166
1037
  const requestSessionId = getHoloSessionIdFromRequest(request);
1167
1038
  if (requestSessionId) {
1168
- authRuntimeInternals.getRuntimeBindings().context.setSessionId(guard, requestSessionId);
1039
+ authRuntimeInternals3.getRuntimeBindings().context.setSessionId(guard, requestSessionId);
1169
1040
  }
1170
1041
  const clerkSession = await readCurrentClerkLogoutSession(guard, providerName);
1171
1042
  if (!clerkSession) {
1172
- return createHostedAuthFailure(
1173
- "clerk_session_missing",
1174
- "The current Holo session was not created by Clerk.",
1175
- 422
1176
- );
1043
+ return Response.redirect(new URL("/", request.url), 303);
1177
1044
  }
1178
1045
  await revokeClerkSession(providerConfig, clerkSession.sessionId);
1179
1046
  const local = await getAuthRuntime().guard(guard).logout();
1180
- return createHostedAuthSuccess(Object.freeze({
1181
- url: createClerkReturnUrl(request, options.returnTo),
1182
- local
1183
- }));
1047
+ const response = new Response(null, {
1048
+ status: 303,
1049
+ headers: {
1050
+ Location: createClerkReturnUrl(request, options.returnTo)
1051
+ }
1052
+ });
1053
+ for (const cookie of local.cookies) {
1054
+ response.headers.append("Set-Cookie", cookie);
1055
+ }
1056
+ return response;
1184
1057
  } catch {
1185
- return createHostedAuthFailure("clerk_logout_failed", "Unable to complete Clerk logout.", 500);
1058
+ return Response.json(createHostedAuthFailure("clerk_logout_failed", "Unable to complete Clerk logout.", 500), {
1059
+ status: 500
1060
+ });
1186
1061
  }
1187
1062
  }
1188
1063
  async function completeClerkAuth(input, options = {}) {
@@ -1209,7 +1084,7 @@ async function completeClerkAuth(input, options = {}) {
1209
1084
  const authenticated = await syncIdentity(session, providerConfig.name, {
1210
1085
  user: options.user
1211
1086
  });
1212
- const authSession = await authRuntimeInternals.establishSessionForUser(authenticated.user, {
1087
+ const authSession = await authRuntimeInternals3.establishSessionForUser(authenticated.user, {
1213
1088
  guard: authenticated.guard,
1214
1089
  provider: authenticated.authProvider,
1215
1090
  payload: createClerkSessionPayload(authenticated, session)
@@ -1225,7 +1100,7 @@ async function completeClerkAuth(input, options = {}) {
1225
1100
  authSession
1226
1101
  }));
1227
1102
  } catch (error) {
1228
- if (authRuntimeInternals.isResponseInterrupt(error)) {
1103
+ if (authRuntimeInternals3.isResponseInterrupt(error)) {
1229
1104
  throw error;
1230
1105
  }
1231
1106
  return createHostedAuthFailure(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@holo-js/auth-clerk",
3
- "version": "0.1.9",
3
+ "version": "0.2.0",
4
4
  "description": "Holo-JS Framework - Clerk auth integration contracts",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -24,9 +24,9 @@
24
24
  },
25
25
  "dependencies": {
26
26
  "@clerk/backend": "^3.4.7",
27
- "@holo-js/auth": "^0.1.9",
28
- "@holo-js/config": "^0.1.9",
29
- "@holo-js/session": "^0.1.9"
27
+ "@holo-js/auth": "^0.2.0",
28
+ "@holo-js/config": "^0.2.0",
29
+ "@holo-js/session": "^0.2.0"
30
30
  },
31
31
  "devDependencies": {
32
32
  "@types/node": "^22.10.2",