@holo-js/auth-workos 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
+ 2a7a3172b7daf8be938a98765d10c89bc37b172775f9c93e82ba03ca38aa73a2
package/dist/index.d.ts CHANGED
@@ -218,4 +218,5 @@ declare const workosAuthInternals: {
218
218
  resolveGuardAndProvider: typeof resolveGuardAndProvider;
219
219
  };
220
220
 
221
- export { type ConfigureWorkosAuthRuntimeOptions, type HostedIdentityRecord, type HostedIdentityStore, type WorkosAuthBindings, WorkosAuthConflictError, type WorkosAuthFacade, type WorkosAuthenticatedUser, type WorkosAuthenticationResult, type WorkosCompleteAuthData, type WorkosCompleteAuthOptions, type WorkosCompleteAuthResult, type WorkosDefaultUserAttributes, type WorkosHostedAuthFailureFields, type WorkosIdentityProfile, type WorkosJsonValue, type WorkosLogoutData, type WorkosLogoutErrorCode, type WorkosLogoutResult, type WorkosLogoutSession, type WorkosProviderRuntime, type WorkosSyncIdentityOptions, type WorkosSyncStatus, type WorkosUserAttributeValue, type WorkosUserAttributes, type WorkosVerifiedSession, type WorkosVerifyRequestContext, type WorkosVerifySessionContext, authenticate, completeWorkosAuth, configureWorkosAuthRuntime, loginWithWorkos, logoutWithWorkos, registerWithWorkos, resetWorkosAuthRuntime, syncIdentity, verifyRequest, verifySession, workosAuth, workosAuthInternals };
221
+ export { WorkosAuthConflictError, authenticate, completeWorkosAuth, configureWorkosAuthRuntime, loginWithWorkos, logoutWithWorkos, registerWithWorkos, resetWorkosAuthRuntime, syncIdentity, verifyRequest, verifySession, workosAuth, workosAuthInternals };
222
+ export type { ConfigureWorkosAuthRuntimeOptions, HostedIdentityRecord, HostedIdentityStore, WorkosAuthBindings, WorkosAuthFacade, WorkosAuthenticatedUser, WorkosAuthenticationResult, WorkosCompleteAuthData, WorkosCompleteAuthOptions, WorkosCompleteAuthResult, WorkosDefaultUserAttributes, WorkosHostedAuthFailureFields, WorkosIdentityProfile, WorkosJsonValue, WorkosLogoutData, WorkosLogoutErrorCode, WorkosLogoutResult, WorkosLogoutSession, WorkosProviderRuntime, WorkosSyncIdentityOptions, WorkosSyncStatus, WorkosUserAttributeValue, WorkosUserAttributes, WorkosVerifiedSession, WorkosVerifyRequestContext, WorkosVerifySessionContext };
package/dist/index.mjs CHANGED
@@ -1,6 +1,7 @@
1
1
  // src/index.ts
2
2
  import { randomBytes } from "crypto";
3
3
  import { authRuntimeInternals, getAuthRuntime } from "@holo-js/auth";
4
+ import { resolveRequestUrl } from "@holo-js/kernel";
4
5
  import { cookie, parseCookieHeader } from "@holo-js/session";
5
6
 
6
7
  // src/contracts.ts
@@ -25,6 +26,7 @@ var WORKOS_AUTHORIZE_URL = `${WORKOS_API_BASE_URL}/user_management/authorize`;
25
26
  var WORKOS_AUTHENTICATE_URL = `${WORKOS_API_BASE_URL}/user_management/authenticate`;
26
27
  var WORKOS_LOGOUT_URL = `${WORKOS_API_BASE_URL}/user_management/sessions/logout`;
27
28
  var WORKOS_STATE_COOKIE_MAX_AGE_SECONDS = 300;
29
+ var WORKOS_SESSION_ID_PATTERN = /^session_[A-Za-z0-9_-]+$/;
28
30
  var workosDefaultProviderRuntimeCache = /* @__PURE__ */ new Map();
29
31
  var workosJwksCache = /* @__PURE__ */ new Map();
30
32
  var AUTH_PROVIDER_MARKER = /* @__PURE__ */ Symbol.for("holo-js.auth.provider");
@@ -73,12 +75,11 @@ function verifyJwtSignatureWithJwk(token, jwk) {
73
75
  unsupportedAlgorithmMessage: (algorithm) => `[@holo-js/auth-workos] Unsupported WorkOS JWT algorithm "${algorithm}".`
74
76
  });
75
77
  }
76
- async function fetchWorkosJwks(clientId, options = {}) {
78
+ async function resolveWorkosJwk(clientId, kid) {
77
79
  const normalizedClientId = clientId.trim();
78
- return authRuntimeInternals.jwt.fetchCachedJwks(normalizedClientId, {
79
- cache: workosJwksCache,
80
+ return authRuntimeInternals.jwt.resolveCachedJwk(workosJwksCache, normalizedClientId, {
80
81
  requestUrl: `${WORKOS_API_BASE_URL}/sso/jwks/${encodeURIComponent(normalizedClientId)}`,
81
- refresh: options.refresh,
82
+ kid,
82
83
  errorMessage: `[@holo-js/auth-workos] Failed to load WorkOS JWKS for "${normalizedClientId}".`
83
84
  });
84
85
  }
@@ -89,14 +90,7 @@ async function verifyWorkosSessionToken(token, config) {
89
90
  }
90
91
  const parsed = parseJwt(token);
91
92
  const headerKid = typeof parsed.header.kid === "string" ? parsed.header.kid : void 0;
92
- const resolveKey = async (refresh = false) => {
93
- const keys = await fetchWorkosJwks(clientId, { 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 resolveWorkosJwk(clientId, headerKid);
100
94
  if (!key || !verifyJwtSignatureWithJwk(parsed, key)) {
101
95
  throw new Error("[@holo-js/auth-workos] WorkOS token signature verification failed.");
102
96
  }
@@ -280,12 +274,15 @@ function getWorkosStateCookieName(providerName) {
280
274
  function createWorkosState() {
281
275
  return randomBytes(32).toString("base64url");
282
276
  }
277
+ function isHttpsRequest(request) {
278
+ return resolveRequestUrl(request).protocol === "https:";
279
+ }
283
280
  function createStateCookie(providerName, state, request) {
284
281
  return cookie(getWorkosStateCookieName(providerName), state, {
285
282
  httpOnly: true,
286
283
  maxAge: WORKOS_STATE_COOKIE_MAX_AGE_SECONDS,
287
284
  sameSite: "lax",
288
- secure: new URL(request.url).protocol === "https:"
285
+ secure: isHttpsRequest(request) || void 0
289
286
  });
290
287
  }
291
288
  function clearStateCookie(providerName, request) {
@@ -293,7 +290,7 @@ function clearStateCookie(providerName, request) {
293
290
  expires: /* @__PURE__ */ new Date(0),
294
291
  httpOnly: true,
295
292
  sameSite: "lax",
296
- secure: new URL(request.url).protocol === "https:"
293
+ secure: isHttpsRequest(request) || void 0
297
294
  });
298
295
  }
299
296
  function getWorkosStateFromRequest(request, providerName) {
@@ -384,6 +381,9 @@ function createWorkosSessionPayload(authenticated, session) {
384
381
  })
385
382
  });
386
383
  }
384
+ function isWorkosSessionId(value) {
385
+ return typeof value === "string" && WORKOS_SESSION_ID_PATTERN.test(value);
386
+ }
387
387
  function getWorkosLogoutSession(payload, providerName) {
388
388
  if (!payload || typeof payload !== "object" || !("workos" in payload)) {
389
389
  return null;
@@ -394,12 +394,12 @@ function getWorkosLogoutSession(payload, providerName) {
394
394
  }
395
395
  const provider = workos.provider;
396
396
  const sessionId = workos.sessionId;
397
- if (provider !== providerName || typeof sessionId !== "string" || !sessionId.trim()) {
397
+ if (provider !== providerName) {
398
398
  return null;
399
399
  }
400
400
  return Object.freeze({
401
401
  provider,
402
- sessionId
402
+ ...isWorkosSessionId(sessionId) ? { sessionId } : {}
403
403
  });
404
404
  }
405
405
  function serializeLocalUser(adapter, user, providerName) {
@@ -435,12 +435,15 @@ function resolveDisplayNameForUpdate(profile) {
435
435
  const fullName = [profile.firstName?.trim(), profile.lastName?.trim()].filter(Boolean).join(" ").trim();
436
436
  return fullName || void 0;
437
437
  }
438
+ function resolveHostedPlaceholderEmail(profile) {
439
+ return `${profile.id}@workos.hosted.local`;
440
+ }
438
441
  function resolveEmailForCreation(profile) {
439
442
  const normalized = profile.email.trim();
440
- if (normalized) {
443
+ if (normalized && profile.emailVerified === true) {
441
444
  return normalized;
442
445
  }
443
- return `${profile.id}@workos.hosted.local`;
446
+ return resolveHostedPlaceholderEmail(profile);
444
447
  }
445
448
  function toAdapterInput(input) {
446
449
  return Object.freeze(Object.fromEntries(
@@ -463,11 +466,12 @@ function resolveCreateUserInput(profile, mapper) {
463
466
  }, profile));
464
467
  }
465
468
  function resolveUpdateUserInput(profile, mapper) {
469
+ const verifiedEmail = profile.emailVerified ? profile.email.trim() || void 0 : void 0;
466
470
  return toAdapterInput({
467
- email: profile.email.trim() || void 0,
471
+ email: verifiedEmail,
468
472
  name: resolveDisplayNameForUpdate(profile),
469
473
  avatar: profile.profilePictureUrl,
470
- email_verified_at: profile.emailVerified ? /* @__PURE__ */ new Date() : void 0,
474
+ email_verified_at: verifiedEmail ? /* @__PURE__ */ new Date() : void 0,
471
475
  ...mapper?.(profile) ?? {}
472
476
  });
473
477
  }
@@ -514,14 +518,15 @@ function getSessionTokenFromRequest(request, sessionCookie) {
514
518
  return cookies[sessionCookie] ?? null;
515
519
  }
516
520
  function getHoloSessionIdFromRequest(request) {
517
- const cookieHeader = authRuntimeInternals.getRuntimeBindings().session.sessionCookie("");
521
+ const { session } = authRuntimeInternals.getRuntimeBindings();
522
+ const cookieHeader = session.sessionCookie("");
518
523
  const separator = cookieHeader.indexOf("=");
519
524
  const cookieName = separator > 0 ? decodeURIComponent(cookieHeader.slice(0, separator)) : "";
520
525
  if (!cookieName) {
521
526
  return null;
522
527
  }
523
- const cookies = parseCookieHeader(request.headers.get("cookie"));
524
- return cookies[cookieName] ?? null;
528
+ const value = parseCookieHeader(request.headers.get("cookie"))[cookieName];
529
+ return value ? session.decryptCookie(cookieName, value) ?? null : null;
525
530
  }
526
531
  async function reuseExistingHoloSession(request, authenticated) {
527
532
  const bindings = authRuntimeInternals.getRuntimeBindings();
@@ -593,9 +598,6 @@ function getRecordField(input, name) {
593
598
  const value = input[name];
594
599
  return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
595
600
  }
596
- function getRequestIpAddress(request) {
597
- return request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() || request.headers.get("x-real-ip")?.trim() || void 0;
598
- }
599
601
  async function authenticateWorkosCode(request, code, config) {
600
602
  const { clientId, apiKey } = requireWorkosClientConfig(config);
601
603
  const body = {
@@ -603,7 +605,7 @@ async function authenticateWorkosCode(request, code, config) {
603
605
  client_id: clientId,
604
606
  client_secret: apiKey,
605
607
  code,
606
- ip_address: getRequestIpAddress(request),
608
+ ip_address: authRuntimeInternals.resolveClientAddress(request),
607
609
  user_agent: request.headers.get("user-agent")?.trim() || void 0
608
610
  };
609
611
  const response = await fetch(WORKOS_AUTHENTICATE_URL, {
@@ -626,7 +628,10 @@ async function authenticateWorkosCode(request, code, config) {
626
628
  }
627
629
  const accessToken = getStringField(payload, "accessToken", "access_token");
628
630
  const session = getRecordField(payload, "session");
629
- const sessionId = getStringField(session ?? {}, "id") ?? getStringField(payload, "sessionId", "session_id") ?? (accessToken ? getJwtStringClaim(accessToken, "sid") : void 0) ?? accessToken ?? code;
631
+ const sessionId = getStringField(session ?? {}, "id") ?? getStringField(payload, "sessionId", "session_id") ?? (accessToken ? getJwtStringClaim(accessToken, "sid") : void 0) ?? (accessToken ? getJwtStringClaim(accessToken, "session_id") : void 0);
632
+ if (!isWorkosSessionId(sessionId)) {
633
+ throw new Error("[@holo-js/auth-workos] WorkOS callback did not include a valid session id.");
634
+ }
630
635
  return Object.freeze({
631
636
  sessionId,
632
637
  identity: normalizeWorkosUserProfile({
@@ -656,6 +661,7 @@ async function syncIdentity(session, provider, options = {}) {
656
661
  ownership: "save",
657
662
  verifiedEmail,
658
663
  email: profileEmail,
664
+ placeholderEmail: resolveHostedPlaceholderEmail(profile),
659
665
  errorPrefix: "[@holo-js/auth-workos]",
660
666
  identityLabel: "WorkOS",
661
667
  createUserInput: () => resolveCreateUserInput(profile, options.user),
@@ -766,7 +772,7 @@ async function logoutWithWorkos(input, options = {}) {
766
772
  const response = new Response(null, {
767
773
  status: 303,
768
774
  headers: {
769
- Location: createLogoutUrl(workosSession.sessionId, request, options.returnTo)
775
+ Location: workosSession.sessionId ? createLogoutUrl(workosSession.sessionId, request, options.returnTo) : createWorkosReturnUrl(options.returnTo?.trim() || "/", request)
770
776
  }
771
777
  });
772
778
  for (const cookie2 of local.cookies) {
@@ -809,6 +815,7 @@ async function completeWorkosAuth(input, options = {}) {
809
815
  422
810
816
  );
811
817
  }
818
+ const stateCookieClear = clearStateCookie(providerConfig.name, request);
812
819
  const session = await authenticateWorkosCode(request, code, providerConfig);
813
820
  const authenticated = await syncIdentity(session, providerConfig.name, {
814
821
  user: options.user
@@ -830,7 +837,7 @@ async function completeWorkosAuth(input, options = {}) {
830
837
  ...authSession,
831
838
  cookies: Object.freeze([
832
839
  ...authSession.cookies,
833
- clearStateCookie(providerConfig.name, request)
840
+ stateCookieClear
834
841
  ])
835
842
  })
836
843
  }));
package/package.json CHANGED
@@ -1,9 +1,14 @@
1
1
  {
2
2
  "name": "@holo-js/auth-workos",
3
- "version": "0.3.27",
3
+ "version": "0.4.0",
4
4
  "description": "Holo-JS Framework - WorkOS 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-workos"
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,16 +29,17 @@
23
29
  "test": "vitest --run"
24
30
  },
25
31
  "dependencies": {
26
- "@holo-js/auth": "^0.3.27",
27
- "@holo-js/session": "^0.3.27"
32
+ "@holo-js/auth": "^0.4.0",
33
+ "@holo-js/kernel": "^0.4.0",
34
+ "@holo-js/session": "^0.4.0"
28
35
  },
29
36
  "devDependencies": {
30
- "@types/node": "^22.10.2",
31
- "tsup": "^8.3.5",
32
- "typescript": "^5.7.2",
33
- "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"
34
41
  },
35
42
  "engines": {
36
- "node": "^22.12.0 || ^24.0.0 || ^26.0.0"
43
+ "node": "^22.18.0 || ^24.0.0 || ^26.0.0"
37
44
  }
38
45
  }