@holo-js/auth-workos 0.1.9 → 0.2.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/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 { NormalizedAuthWorkosProviderConfig } from '@holo-js/config';
3
3
 
4
4
  type WorkosJsonValue = string | number | boolean | null | readonly WorkosJsonValue[] | {
@@ -99,16 +99,7 @@ interface HostedIdentityStore {
99
99
  save(record: HostedIdentityRecord): Promise<void>;
100
100
  }
101
101
  type WorkosSyncStatus = 'created' | 'updated' | 'linked' | 'relinked';
102
- interface WorkosAuthenticationResult<TUserAttributes extends WorkosUserAttributes = WorkosDefaultUserAttributes> {
103
- readonly provider: string;
104
- readonly guard: string;
105
- readonly authProvider: string;
106
- readonly status: WorkosSyncStatus;
107
- readonly user: WorkosAuthenticatedUser<TUserAttributes>;
108
- readonly identity: HostedIdentityRecord;
109
- readonly session: WorkosVerifiedSession;
110
- readonly authSession?: AuthEstablishedSession;
111
- }
102
+ type WorkosAuthenticationResult<TUserAttributes extends WorkosUserAttributes = WorkosDefaultUserAttributes> = WorkosCompleteAuthData<TUserAttributes>;
112
103
  interface WorkosAuthBindings {
113
104
  readonly providers: Readonly<Record<string, WorkosProviderRuntime>>;
114
105
  readonly identityStore: HostedIdentityStore;
@@ -127,7 +118,7 @@ interface WorkosAuthFacade {
127
118
  logoutWithWorkos(request: Request, options?: {
128
119
  readonly provider?: string;
129
120
  readonly returnTo?: string;
130
- }): Promise<WorkosLogoutResult>;
121
+ }): Promise<Response>;
131
122
  completeWorkosAuth<TUserAttributes extends WorkosUserAttributes = WorkosDefaultUserAttributes>(request: Request, options?: WorkosCompleteAuthOptions<TUserAttributes>): Promise<WorkosCompleteAuthResult<TUserAttributes>>;
132
123
  verifyRequest(request: Request, provider?: string): Promise<WorkosVerifiedSession | null>;
133
124
  verifySession(token: string, provider?: string): Promise<WorkosVerifiedSession | null>;
@@ -203,7 +194,7 @@ declare function registerWithWorkos(input: WorkosRequestInput, options?: {
203
194
  declare function logoutWithWorkos(input: WorkosRequestInput, options?: {
204
195
  readonly provider?: string;
205
196
  readonly returnTo?: string;
206
- }): Promise<WorkosLogoutResult>;
197
+ }): Promise<Response>;
207
198
  declare function completeWorkosAuth<TUserAttributes extends WorkosUserAttributes = WorkosDefaultUserAttributes>(input: WorkosRequestInput, options?: WorkosCompleteAuthOptions<TUserAttributes>): Promise<WorkosCompleteAuthResult<TUserAttributes>>;
208
199
  declare function configureWorkosAuthRuntime(bindings?: ConfigureWorkosAuthRuntimeOptions): void;
209
200
  declare function resetWorkosAuthRuntime(): void;
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/index.ts
2
- import { createPublicKey, randomBytes, verify as verifySignature } from "crypto";
2
+ import { randomBytes } from "crypto";
3
3
  import { authRuntimeInternals, getAuthRuntime } from "@holo-js/auth";
4
4
  import { cookie, parseCookieHeader } from "@holo-js/session";
5
5
 
@@ -43,98 +43,8 @@ function getRuntimeState() {
43
43
  function throwUnconfigured() {
44
44
  throw new Error("[@holo-js/auth-workos] WorkOS auth runtime is not configured yet.");
45
45
  }
46
- function isPlainHeaderRecord(value) {
47
- return Boolean(value) && typeof value === "object" && Object.getPrototypeOf(value) === Object.prototype;
48
- }
49
- function appendKnownHeaders(headers, input) {
50
- for (const name of ["authorization", "cookie", "host", "x-forwarded-host", "x-forwarded-proto"]) {
51
- const value = input.get?.(name);
52
- if (typeof value === "string" && value) {
53
- headers.set(name, value);
54
- }
55
- }
56
- }
57
- function hasHeaderForEach(input) {
58
- return !Array.isArray(input) && "forEach" in input && typeof input.forEach === "function";
59
- }
60
- function hasHeaderEntries(input) {
61
- return !Array.isArray(input) && "entries" in input && typeof input.entries === "function";
62
- }
63
- function hasHeaderGet(input) {
64
- return !Array.isArray(input) && "get" in input && typeof input.get === "function";
65
- }
66
- function normalizeRequestHeaders(input) {
67
- const headers = new Headers();
68
- if (!input) {
69
- return headers;
70
- }
71
- if (input instanceof Headers || Array.isArray(input)) {
72
- new Headers(input).forEach((value, name) => headers.append(name, value));
73
- return headers;
74
- }
75
- if (hasHeaderForEach(input)) {
76
- input.forEach((value, name) => headers.append(name, value));
77
- return headers;
78
- }
79
- if (hasHeaderEntries(input)) {
80
- for (const [name, value] of input.entries()) {
81
- headers.append(name, value);
82
- }
83
- return headers;
84
- }
85
- if (hasHeaderGet(input)) {
86
- appendKnownHeaders(headers, input);
87
- return headers;
88
- }
89
- if (isPlainHeaderRecord(input)) {
90
- for (const [name, value] of Object.entries(input)) {
91
- if (typeof value === "string") {
92
- headers.append(name, value);
93
- continue;
94
- }
95
- if (Array.isArray(value)) {
96
- const separator = name.toLowerCase() === "cookie" ? "; " : ",";
97
- const joined = value.filter((entry) => typeof entry === "string").join(separator);
98
- if (joined) {
99
- headers.append(name, joined);
100
- }
101
- }
102
- }
103
- }
104
- return headers;
105
- }
106
- function getRequestFromLikeInput(input) {
107
- return input.request ?? input.web?.request ?? (input.req instanceof Request ? input.req : void 0);
108
- }
109
- function getRequestLikeHeaders(input) {
110
- return input.headers ?? (typeof input.req === "object" && !(input.req instanceof Request) ? input.req.headers : void 0) ?? input.node?.req?.headers;
111
- }
112
- function getRequestLikeMethod(input) {
113
- return input.method ?? (typeof input.req === "object" && !(input.req instanceof Request) ? input.req.method : void 0) ?? input.node?.req?.method ?? "GET";
114
- }
115
- function getRequestLikeUrl(input, headers) {
116
- 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 ?? "/";
117
- try {
118
- return new URL(url).toString();
119
- } catch {
120
- const protocol = headers.get("x-forwarded-proto") ?? "http";
121
- const host = headers.get("x-forwarded-host") ?? headers.get("host") ?? "localhost";
122
- return new URL(url, `${protocol}://${host}`).toString();
123
- }
124
- }
125
46
  function normalizeWorkosRequest(input) {
126
- if (input instanceof Request) {
127
- return input;
128
- }
129
- const request = getRequestFromLikeInput(input);
130
- if (request) {
131
- return request;
132
- }
133
- const headers = normalizeRequestHeaders(getRequestLikeHeaders(input));
134
- return new Request(getRequestLikeUrl(input, headers), {
135
- method: getRequestLikeMethod(input),
136
- headers
137
- });
47
+ return authRuntimeInternals.normalizeRequestInput(input);
138
48
  }
139
49
  function getBindings() {
140
50
  const workosBindings = getRuntimeState().bindings;
@@ -146,75 +56,31 @@ function getBindings() {
146
56
  identityStore: workosBindings.identityStore
147
57
  };
148
58
  }
149
- function decodeJwtSegment(value, label) {
150
- try {
151
- return JSON.parse(Buffer.from(value, "base64url").toString("utf8"));
152
- } catch {
153
- throw new Error(`[@holo-js/auth-workos] WorkOS token ${label} was not valid JSON.`);
154
- }
155
- }
156
59
  function parseJwt(token) {
157
- const segments = token.split(".");
158
- if (segments.length !== 3 || !segments[0] || !segments[1] || !segments[2]) {
159
- throw new Error("[@holo-js/auth-workos] WorkOS token was not a valid JWT.");
160
- }
161
- return {
162
- header: decodeJwtSegment(segments[0], "header"),
163
- payload: decodeJwtSegment(segments[1], "payload"),
164
- signature: Buffer.from(segments[2], "base64url"),
165
- signingInput: Buffer.from(`${segments[0]}.${segments[1]}`, "utf8")
166
- };
60
+ return authRuntimeInternals.jwt.parseJwt(token, {
61
+ errorPrefix: "[@holo-js/auth-workos] WorkOS token",
62
+ malformedMessage: "[@holo-js/auth-workos] WorkOS token was not a valid JWT."
63
+ });
167
64
  }
168
65
  function getJwtStringClaim(token, claim) {
169
- try {
170
- const value = parseJwt(token).payload[claim];
171
- return typeof value === "string" && value.trim() ? value : void 0;
172
- } catch {
173
- return void 0;
174
- }
66
+ return authRuntimeInternals.jwt.getJwtStringClaim(token, claim, {
67
+ errorPrefix: "[@holo-js/auth-workos] WorkOS token",
68
+ malformedMessage: "[@holo-js/auth-workos] WorkOS token was not a valid JWT."
69
+ });
175
70
  }
176
71
  function verifyJwtSignatureWithJwk(token, jwk) {
177
- const algorithm = typeof token.header.alg === "string" ? token.header.alg : "";
178
- const key = createPublicKey({ key: jwk, format: "jwk" });
179
- switch (algorithm) {
180
- case "RS256":
181
- return verifySignature("RSA-SHA256", token.signingInput, key, token.signature);
182
- case "RS384":
183
- return verifySignature("RSA-SHA384", token.signingInput, key, token.signature);
184
- case "RS512":
185
- return verifySignature("RSA-SHA512", token.signingInput, key, token.signature);
186
- default:
187
- throw new Error(`[@holo-js/auth-workos] Unsupported WorkOS JWT algorithm "${algorithm || "unknown"}".`);
188
- }
72
+ return authRuntimeInternals.jwt.verifyJwtSignatureWithJwk(token, jwk, {
73
+ unsupportedAlgorithmMessage: (algorithm) => `[@holo-js/auth-workos] Unsupported WorkOS JWT algorithm "${algorithm}".`
74
+ });
189
75
  }
190
76
  async function fetchWorkosJwks(clientId, options = {}) {
191
77
  const normalizedClientId = clientId.trim();
192
- if (options.refresh) {
193
- workosJwksCache.delete(normalizedClientId);
194
- }
195
- const existing = workosJwksCache.get(normalizedClientId);
196
- if (existing) {
197
- return existing;
198
- }
199
- const pending = (async () => {
200
- const response = await fetch(`${WORKOS_API_BASE_URL}/sso/jwks/${encodeURIComponent(normalizedClientId)}`, {
201
- headers: {
202
- accept: "application/json"
203
- }
204
- });
205
- if (!response.ok) {
206
- throw new Error(`[@holo-js/auth-workos] Failed to load WorkOS JWKS for "${normalizedClientId}".`);
207
- }
208
- const payload = await response.json();
209
- return payload.keys ?? [];
210
- })();
211
- workosJwksCache.set(normalizedClientId, pending);
212
- try {
213
- return await pending;
214
- } catch (error) {
215
- workosJwksCache.delete(normalizedClientId);
216
- throw error;
217
- }
78
+ return authRuntimeInternals.jwt.fetchCachedJwks(normalizedClientId, {
79
+ cache: workosJwksCache,
80
+ requestUrl: `${WORKOS_API_BASE_URL}/sso/jwks/${encodeURIComponent(normalizedClientId)}`,
81
+ refresh: options.refresh,
82
+ errorMessage: `[@holo-js/auth-workos] Failed to load WorkOS JWKS for "${normalizedClientId}".`
83
+ });
218
84
  }
219
85
  async function verifyWorkosSessionToken(token, config) {
220
86
  const clientId = config.clientId?.trim();
@@ -1014,21 +880,18 @@ function createHostedAuthFailure(code, message, status) {
1014
880
  });
1015
881
  }
1016
882
  async function loginWithWorkos(input, options = {}) {
1017
- try {
1018
- const request = normalizeWorkosRequest(input);
1019
- const providerConfig = getConfiguredProviderConfig(options.provider);
1020
- return createWorkosRedirect(request, providerConfig, "sign-in");
1021
- } catch (error) {
1022
- return createWorkosErrorResponse(error, "workos_login_failed");
1023
- }
883
+ return createWorkosHostedAuthRedirect(input, options.provider, "sign-in", "workos_login_failed");
1024
884
  }
1025
885
  async function registerWithWorkos(input, options = {}) {
886
+ return createWorkosHostedAuthRedirect(input, options.provider, "sign-up", "workos_register_failed");
887
+ }
888
+ function createWorkosHostedAuthRedirect(input, provider, screenHint, failureCode) {
1026
889
  try {
1027
890
  const request = normalizeWorkosRequest(input);
1028
- const providerConfig = getConfiguredProviderConfig(options.provider);
1029
- return createWorkosRedirect(request, providerConfig, "sign-up");
891
+ const providerConfig = getConfiguredProviderConfig(provider);
892
+ return createWorkosRedirect(request, providerConfig, screenHint);
1030
893
  } catch (error) {
1031
- return createWorkosErrorResponse(error, "workos_register_failed");
894
+ return createWorkosErrorResponse(error, failureCode);
1032
895
  }
1033
896
  }
1034
897
  async function readCurrentWorkosLogoutSession(guard, providerName) {
@@ -1052,19 +915,23 @@ async function logoutWithWorkos(input, options = {}) {
1052
915
  }
1053
916
  const workosSession = await readCurrentWorkosLogoutSession(guard, providerName);
1054
917
  if (!workosSession) {
1055
- return createHostedAuthFailure(
1056
- "workos_session_missing",
1057
- "The current Holo session was not created by WorkOS.",
1058
- 422
1059
- );
918
+ return Response.redirect(new URL("/", request.url), 303);
1060
919
  }
1061
920
  const local = await getAuthRuntime().guard(guard).logout();
1062
- return createHostedAuthSuccess(Object.freeze({
1063
- url: createLogoutUrl(workosSession.sessionId, request, options.returnTo),
1064
- local
1065
- }));
921
+ const response = new Response(null, {
922
+ status: 303,
923
+ headers: {
924
+ Location: createLogoutUrl(workosSession.sessionId, request, options.returnTo)
925
+ }
926
+ });
927
+ for (const cookie2 of local.cookies) {
928
+ response.headers.append("Set-Cookie", cookie2);
929
+ }
930
+ return response;
1066
931
  } catch {
1067
- return createHostedAuthFailure("workos_logout_failed", "Unable to complete WorkOS logout.", 500);
932
+ return Response.json(createHostedAuthFailure("workos_logout_failed", "Unable to complete WorkOS logout.", 500), {
933
+ status: 500
934
+ });
1068
935
  }
1069
936
  }
1070
937
  async function completeWorkosAuth(input, options = {}) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@holo-js/auth-workos",
3
- "version": "0.1.9",
3
+ "version": "0.2.1",
4
4
  "description": "Holo-JS Framework - WorkOS auth integration contracts",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -23,9 +23,9 @@
23
23
  "test": "vitest --run"
24
24
  },
25
25
  "dependencies": {
26
- "@holo-js/auth": "^0.1.9",
27
- "@holo-js/config": "^0.1.9",
28
- "@holo-js/session": "^0.1.9"
26
+ "@holo-js/auth": "^0.2.1",
27
+ "@holo-js/config": "^0.2.1",
28
+ "@holo-js/session": "^0.2.1"
29
29
  },
30
30
  "devDependencies": {
31
31
  "@types/node": "^22.10.2",