@holo-js/auth-clerk 0.3.28 → 0.4.1
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 +21 -0
- package/dist/.holo-build-fingerprint +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.mjs +47 -33
- package/package.json +16 -10
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
|
+
2bf3cb6ca0d9f39f67da29cd8d3727ac276a980bb14c5adc9e472065710bee29
|
package/dist/index.d.ts
CHANGED
|
@@ -244,4 +244,5 @@ declare const clerkAuthInternals: {
|
|
|
244
244
|
resolvePrimaryEmail: typeof resolvePrimaryEmail;
|
|
245
245
|
};
|
|
246
246
|
|
|
247
|
-
export {
|
|
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
|
|
44
|
-
return authRuntimeInternals.jwt.
|
|
45
|
-
cache: clerkJwksCache,
|
|
46
|
+
async function resolveClerkJwk(jwksUrl, kid) {
|
|
47
|
+
return authRuntimeInternals.jwt.resolveCachedJwk(clerkJwksCache, jwksUrl, {
|
|
46
48
|
requestUrl: jwksUrl,
|
|
47
|
-
|
|
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
|
|
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
|
|
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 (
|
|
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
|
|
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)
|
|
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
|
|
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
|
-
|
|
453
|
-
|
|
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:
|
|
458
|
+
email_verified_at: verifiedEmail ? /* @__PURE__ */ new Date() : void 0,
|
|
456
459
|
...mapper?.(profile) ?? {}
|
|
457
|
-
}
|
|
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
|
|
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
|
|
555
|
-
return
|
|
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
|
|
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
|
-
|
|
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: [...
|
|
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
|
-
|
|
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,
|
|
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
|
+
"version": "0.4.1",
|
|
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.
|
|
27
|
-
"@holo-js/auth": "^0.
|
|
28
|
-
"@holo-js/session": "^0.
|
|
32
|
+
"@clerk/backend": "^3.26.1",
|
|
33
|
+
"@holo-js/auth": "^0.4.1",
|
|
34
|
+
"@holo-js/session": "^0.4.1"
|
|
29
35
|
},
|
|
30
36
|
"devDependencies": {
|
|
31
|
-
"@types/node": "^22.
|
|
32
|
-
"tsup": "^
|
|
33
|
-
"typescript": "^
|
|
34
|
-
"vitest": "^
|
|
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.
|
|
43
|
+
"node": "^22.18.0 || ^24.0.0 || ^26.0.0"
|
|
38
44
|
}
|
|
39
45
|
}
|