@holo-js/auth-workos 0.1.4 → 0.1.5

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,17 +1,40 @@
1
- import { AuthUserLike, AuthEstablishedSession, authRuntimeInternals } from '@holo-js/auth';
2
- import { AuthWorkosProviderConfig } from '@holo-js/config';
1
+ import { AuthResult, AuthLogoutResult, AuthFieldErrors, AuthenticatedAuthUser, AuthEstablishedSession, authRuntimeInternals } from '@holo-js/auth';
2
+ import { NormalizedAuthWorkosProviderConfig } from '@holo-js/config';
3
3
 
4
+ type WorkosJsonValue = string | number | boolean | null | readonly WorkosJsonValue[] | {
5
+ readonly [key: string]: WorkosJsonValue;
6
+ };
4
7
  interface WorkosIdentityProfile {
5
8
  readonly id: string;
6
- readonly email?: string;
7
- readonly emailVerified?: boolean;
9
+ readonly email: string;
10
+ readonly name: string;
11
+ readonly emailVerified: boolean;
8
12
  readonly firstName?: string;
9
13
  readonly lastName?: string;
10
- readonly name?: string;
11
- readonly avatar?: string;
14
+ readonly profilePictureUrl?: string;
15
+ readonly externalId?: string;
12
16
  readonly organizationId?: string;
13
- readonly raw?: unknown;
17
+ readonly metadata: Readonly<Record<string, WorkosJsonValue>>;
18
+ readonly createdAt?: string;
19
+ readonly updatedAt?: string;
20
+ readonly raw: Readonly<Record<string, WorkosJsonValue>>;
14
21
  }
22
+ type WorkosUserAttributeValue = string | number | boolean | Date | null | undefined | readonly WorkosUserAttributeValue[] | {
23
+ readonly [key: string]: WorkosUserAttributeValue;
24
+ };
25
+ type WorkosUserAttributes = Readonly<Record<string, WorkosUserAttributeValue>>;
26
+ type WorkosDefaultUserAttributes = {
27
+ readonly email: string;
28
+ readonly name: string;
29
+ };
30
+ type WorkosUserMapper<TUserAttributes extends WorkosUserAttributes = WorkosDefaultUserAttributes> = (workosUser: WorkosIdentityProfile) => TUserAttributes;
31
+ type WorkosCompleteAuthOptions<TUserAttributes extends WorkosUserAttributes = WorkosDefaultUserAttributes> = {
32
+ readonly provider?: string;
33
+ readonly user?: WorkosUserMapper<TUserAttributes>;
34
+ };
35
+ type WorkosSyncIdentityOptions<TUserAttributes extends WorkosUserAttributes = WorkosDefaultUserAttributes> = {
36
+ readonly user?: WorkosUserMapper<TUserAttributes>;
37
+ };
15
38
  interface WorkosVerifiedSession {
16
39
  readonly sessionId: string;
17
40
  readonly identity: WorkosIdentityProfile;
@@ -19,21 +42,45 @@ interface WorkosVerifiedSession {
19
42
  readonly expiresAt?: Date;
20
43
  readonly raw?: unknown;
21
44
  }
45
+ interface WorkosLogoutSession {
46
+ readonly provider: string;
47
+ readonly sessionId: string;
48
+ }
49
+ type WorkosAuthenticatedUser<TUserAttributes extends WorkosUserAttributes = WorkosDefaultUserAttributes> = AuthenticatedAuthUser & Omit<TUserAttributes, 'can' | 'id'> & {
50
+ readonly id: string | number;
51
+ };
52
+ type WorkosHostedAuthFailureFields = AuthFieldErrors<'_root'>;
53
+ interface WorkosCompleteAuthData<TUserAttributes extends WorkosUserAttributes = WorkosDefaultUserAttributes> {
54
+ readonly provider: string;
55
+ readonly guard: string;
56
+ readonly authProvider: string;
57
+ readonly status: WorkosSyncStatus;
58
+ readonly user: WorkosAuthenticatedUser<TUserAttributes>;
59
+ readonly identity: HostedIdentityRecord;
60
+ readonly session: WorkosVerifiedSession;
61
+ readonly authSession?: AuthEstablishedSession;
62
+ }
63
+ type WorkosCompleteAuthResult<TUserAttributes extends WorkosUserAttributes = WorkosDefaultUserAttributes> = AuthResult<WorkosCompleteAuthData<TUserAttributes>, string, WorkosHostedAuthFailureFields>;
64
+ type WorkosLogoutErrorCode = 'workos_logout_failed' | 'workos_session_missing';
65
+ interface WorkosLogoutData {
66
+ readonly url: string;
67
+ readonly local: AuthLogoutResult;
68
+ }
69
+ type WorkosLogoutResult = AuthResult<WorkosLogoutData, WorkosLogoutErrorCode, WorkosHostedAuthFailureFields>;
22
70
  interface WorkosVerifyRequestContext {
23
71
  readonly provider: string;
24
72
  readonly request: Request;
25
- readonly config: AuthWorkosProviderConfig;
73
+ readonly config: NormalizedAuthWorkosProviderConfig;
26
74
  }
27
75
  interface WorkosVerifySessionContext {
28
76
  readonly provider: string;
29
77
  readonly token: string;
30
- readonly config: AuthWorkosProviderConfig;
78
+ readonly config: NormalizedAuthWorkosProviderConfig;
31
79
  }
32
80
  interface WorkosProviderRuntime {
33
81
  verifyRequest?(context: WorkosVerifyRequestContext): Promise<WorkosVerifiedSession | null>;
34
82
  verifySession?(context: WorkosVerifySessionContext): Promise<WorkosVerifiedSession | null>;
35
83
  }
36
- type RuntimeAuthProviderAdapter = ReturnType<typeof authRuntimeInternals.getRuntimeBindings>['providers'][string];
37
84
  interface HostedIdentityRecord {
38
85
  readonly provider: string;
39
86
  readonly providerUserId: string;
@@ -52,12 +99,12 @@ interface HostedIdentityStore {
52
99
  save(record: HostedIdentityRecord): Promise<void>;
53
100
  }
54
101
  type WorkosSyncStatus = 'created' | 'updated' | 'linked' | 'relinked';
55
- interface WorkosAuthenticationResult {
102
+ interface WorkosAuthenticationResult<TUserAttributes extends WorkosUserAttributes = WorkosDefaultUserAttributes> {
56
103
  readonly provider: string;
57
104
  readonly guard: string;
58
105
  readonly authProvider: string;
59
106
  readonly status: WorkosSyncStatus;
60
- readonly user: AuthUserLike;
107
+ readonly user: WorkosAuthenticatedUser<TUserAttributes>;
61
108
  readonly identity: HostedIdentityRecord;
62
109
  readonly session: WorkosVerifiedSession;
63
110
  readonly authSession?: AuthEstablishedSession;
@@ -71,9 +118,20 @@ interface ConfigureWorkosAuthRuntimeOptions {
71
118
  readonly identityStore?: HostedIdentityStore;
72
119
  }
73
120
  interface WorkosAuthFacade {
121
+ loginWithWorkos(request: Request, options?: {
122
+ readonly provider?: string;
123
+ }): Promise<Response>;
124
+ registerWithWorkos(request: Request, options?: {
125
+ readonly provider?: string;
126
+ }): Promise<Response>;
127
+ logoutWithWorkos(request: Request, options?: {
128
+ readonly provider?: string;
129
+ readonly returnTo?: string;
130
+ }): Promise<WorkosLogoutResult>;
131
+ completeWorkosAuth<TUserAttributes extends WorkosUserAttributes = WorkosDefaultUserAttributes>(request: Request, options?: WorkosCompleteAuthOptions<TUserAttributes>): Promise<WorkosCompleteAuthResult<TUserAttributes>>;
74
132
  verifyRequest(request: Request, provider?: string): Promise<WorkosVerifiedSession | null>;
75
133
  verifySession(token: string, provider?: string): Promise<WorkosVerifiedSession | null>;
76
- syncIdentity(session: WorkosVerifiedSession, provider?: string): Promise<WorkosAuthenticationResult>;
134
+ syncIdentity<TUserAttributes extends WorkosUserAttributes = WorkosDefaultUserAttributes>(session: WorkosVerifiedSession, provider?: string, options?: WorkosSyncIdentityOptions<TUserAttributes>): Promise<WorkosAuthenticationResult<TUserAttributes>>;
77
135
  authenticate(request: Request, provider?: string): Promise<WorkosAuthenticationResult | null>;
78
136
  }
79
137
  declare class WorkosAuthConflictError extends Error {
@@ -88,35 +146,73 @@ declare class WorkosAuthConflictError extends Error {
88
146
  readonly message: string;
89
147
  });
90
148
  }
149
+
150
+ type RuntimeAuthProviderAdapter = ReturnType<typeof authRuntimeInternals.getRuntimeBindings>['providers'][string];
151
+ type WorkosRequestHeaders = Headers | ReadonlyArray<readonly [string, string]> | Record<string, string | readonly string[] | undefined> | {
152
+ readonly get?: (name: string) => string | null | undefined;
153
+ readonly forEach?: (callback: (value: string, key: string) => void) => void;
154
+ readonly entries?: () => Iterable<readonly [string, string]>;
155
+ };
156
+ type WorkosRequestLike = {
157
+ readonly method?: string;
158
+ readonly path?: string;
159
+ readonly url?: string | URL;
160
+ readonly headers?: WorkosRequestHeaders;
161
+ readonly request?: Request;
162
+ readonly req?: Request | {
163
+ readonly method?: string;
164
+ readonly url?: string;
165
+ readonly headers?: WorkosRequestHeaders;
166
+ };
167
+ readonly node?: {
168
+ readonly req?: {
169
+ readonly method?: string;
170
+ readonly url?: string;
171
+ readonly headers?: WorkosRequestHeaders;
172
+ };
173
+ };
174
+ readonly web?: {
175
+ readonly request?: Request;
176
+ };
177
+ };
178
+ type WorkosRequestInput = Request | WorkosRequestLike;
91
179
  declare function getBindings(): WorkosAuthBindings;
92
180
  declare function resolveConfiguredProviderName(provider?: string): string;
93
181
  declare function getConfiguredProviderConfig(provider?: string): {
94
182
  readonly name: string;
95
- readonly clientId?: string;
96
- readonly apiKey?: string;
97
- readonly cookiePassword?: string;
98
- readonly redirectUri?: string;
99
- readonly sessionCookie: string;
100
- readonly guard?: string;
101
- readonly mapToProvider?: string;
102
- };
183
+ } & NormalizedAuthWorkosProviderConfig;
103
184
  declare function resolveGuardAndProvider(provider?: string): {
104
185
  readonly guard: string;
105
186
  readonly authProvider: string;
106
187
  readonly adapter: RuntimeAuthProviderAdapter;
107
188
  };
108
- declare function resolveDisplayName(profile: WorkosIdentityProfile): string | undefined;
189
+ declare function resolveDisplayName(profile: WorkosIdentityProfile): string;
109
190
  declare function resolveEmailForCreation(profile: WorkosIdentityProfile): string;
110
191
  declare function normalizeHostedProfile(profile: WorkosIdentityProfile): Readonly<Record<string, unknown>>;
111
192
  declare function getSessionTokenFromRequest(request: Request, sessionCookie: string): string | null;
112
193
  declare function verifySession(token: string, provider?: string): Promise<WorkosVerifiedSession | null>;
113
- declare function verifyRequest(request: Request, provider?: string): Promise<WorkosVerifiedSession | null>;
114
- declare function syncIdentity(session: WorkosVerifiedSession, provider?: string): Promise<WorkosAuthenticationResult>;
115
- declare function authenticate(request: Request, provider?: string): Promise<WorkosAuthenticationResult | null>;
194
+ declare function verifyRequest(input: WorkosRequestInput, provider?: string): Promise<WorkosVerifiedSession | null>;
195
+ declare function syncIdentity<TUserAttributes extends WorkosUserAttributes = WorkosDefaultUserAttributes>(session: WorkosVerifiedSession, provider?: string, options?: WorkosSyncIdentityOptions<TUserAttributes>): Promise<WorkosAuthenticationResult<TUserAttributes>>;
196
+ declare function authenticate(input: WorkosRequestInput, provider?: string): Promise<WorkosAuthenticationResult | null>;
197
+ declare function loginWithWorkos(input: WorkosRequestInput, options?: {
198
+ readonly provider?: string;
199
+ }): Promise<Response>;
200
+ declare function registerWithWorkos(input: WorkosRequestInput, options?: {
201
+ readonly provider?: string;
202
+ }): Promise<Response>;
203
+ declare function logoutWithWorkos(input: WorkosRequestInput, options?: {
204
+ readonly provider?: string;
205
+ readonly returnTo?: string;
206
+ }): Promise<WorkosLogoutResult>;
207
+ declare function completeWorkosAuth<TUserAttributes extends WorkosUserAttributes = WorkosDefaultUserAttributes>(input: WorkosRequestInput, options?: WorkosCompleteAuthOptions<TUserAttributes>): Promise<WorkosCompleteAuthResult<TUserAttributes>>;
116
208
  declare function configureWorkosAuthRuntime(bindings?: ConfigureWorkosAuthRuntimeOptions): void;
117
209
  declare function resetWorkosAuthRuntime(): void;
118
210
  declare const workosAuth: Readonly<{
119
211
  authenticate: typeof authenticate;
212
+ completeWorkosAuth: typeof completeWorkosAuth;
213
+ loginWithWorkos: typeof loginWithWorkos;
214
+ logoutWithWorkos: typeof logoutWithWorkos;
215
+ registerWithWorkos: typeof registerWithWorkos;
120
216
  syncIdentity: typeof syncIdentity;
121
217
  verifyRequest: typeof verifyRequest;
122
218
  verifySession: typeof verifySession;
@@ -132,4 +228,4 @@ declare const workosAuthInternals: {
132
228
  resolveGuardAndProvider: typeof resolveGuardAndProvider;
133
229
  };
134
230
 
135
- export { type ConfigureWorkosAuthRuntimeOptions, type HostedIdentityRecord, type HostedIdentityStore, type WorkosAuthBindings, WorkosAuthConflictError, type WorkosAuthFacade, type WorkosAuthenticationResult, type WorkosIdentityProfile, type WorkosProviderRuntime, type WorkosSyncStatus, type WorkosVerifiedSession, type WorkosVerifyRequestContext, type WorkosVerifySessionContext, authenticate, configureWorkosAuthRuntime, resetWorkosAuthRuntime, syncIdentity, verifyRequest, verifySession, workosAuth, workosAuthInternals };
231
+ 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 };
package/dist/index.mjs CHANGED
@@ -1,7 +1,9 @@
1
1
  // src/index.ts
2
- import { createPublicKey, verify as verifySignature } from "crypto";
3
- import { authRuntimeInternals } from "@holo-js/auth";
4
- import { parseCookieHeader } from "@holo-js/session";
2
+ import { createPublicKey, randomBytes, verify as verifySignature } from "crypto";
3
+ import { authRuntimeInternals, getAuthRuntime } from "@holo-js/auth";
4
+ import { cookie, parseCookieHeader } from "@holo-js/session";
5
+
6
+ // src/contracts.ts
5
7
  var WorkosAuthConflictError = class extends Error {
6
8
  code = "workos_identity_conflict";
7
9
  provider;
@@ -15,15 +17,127 @@ var WorkosAuthConflictError = class extends Error {
15
17
  this.email = options.email;
16
18
  }
17
19
  };
18
- var workosBindings;
20
+
21
+ // src/index.ts
22
+ var WORKOS_RUNTIME_STATE_KEY = "__holoJsAuthWorkosRuntime";
19
23
  var WORKOS_API_BASE_URL = "https://api.workos.com";
24
+ var WORKOS_AUTHORIZE_URL = `${WORKOS_API_BASE_URL}/user_management/authorize`;
25
+ var WORKOS_AUTHENTICATE_URL = `${WORKOS_API_BASE_URL}/user_management/authenticate`;
26
+ var WORKOS_LOGOUT_URL = `${WORKOS_API_BASE_URL}/user_management/sessions/logout`;
27
+ var WORKOS_STATE_COOKIE_MAX_AGE_SECONDS = 300;
20
28
  var workosDefaultProviderRuntimeCache = /* @__PURE__ */ new Map();
21
29
  var workosJwksCache = /* @__PURE__ */ new Map();
22
30
  var AUTH_PROVIDER_MARKER = /* @__PURE__ */ Symbol.for("holo-js.auth.provider");
31
+ function getRuntimeState() {
32
+ const runtimeGlobal = globalThis;
33
+ if (!runtimeGlobal[WORKOS_RUNTIME_STATE_KEY]) {
34
+ Object.defineProperty(runtimeGlobal, WORKOS_RUNTIME_STATE_KEY, {
35
+ value: {},
36
+ enumerable: false,
37
+ configurable: true,
38
+ writable: true
39
+ });
40
+ }
41
+ return runtimeGlobal[WORKOS_RUNTIME_STATE_KEY];
42
+ }
23
43
  function throwUnconfigured() {
24
44
  throw new Error("[@holo-js/auth-workos] WorkOS auth runtime is not configured yet.");
25
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
+ 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
+ });
138
+ }
26
139
  function getBindings() {
140
+ const workosBindings = getRuntimeState().bindings;
27
141
  if (!workosBindings?.identityStore) {
28
142
  throwUnconfigured();
29
143
  }
@@ -51,6 +165,14 @@ function parseJwt(token) {
51
165
  signingInput: Buffer.from(`${segments[0]}.${segments[1]}`, "utf8")
52
166
  };
53
167
  }
168
+ 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
+ }
175
+ }
54
176
  function verifyJwtSignatureWithJwk(token, jwk) {
55
177
  const algorithm = typeof token.header.alg === "string" ? token.header.alg : "";
56
178
  const key = createPublicKey({ key: jwk, format: "jwk" });
@@ -122,20 +244,43 @@ async function verifyWorkosSessionToken(token, config) {
122
244
  }
123
245
  return parsed.payload;
124
246
  }
247
+ function toWorkosJsonValue(value) {
248
+ if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
249
+ return value;
250
+ }
251
+ if (Array.isArray(value)) {
252
+ return value.map((item) => toWorkosJsonValue(item)).filter((item) => typeof item !== "undefined");
253
+ }
254
+ if (value && typeof value === "object" && Object.getPrototypeOf(value) === Object.prototype) {
255
+ return Object.freeze(Object.fromEntries(
256
+ Object.entries(value).map(([key, item]) => [key, toWorkosJsonValue(item)]).filter((entry) => typeof entry[1] !== "undefined")
257
+ ));
258
+ }
259
+ return void 0;
260
+ }
261
+ function toWorkosJsonObject(value) {
262
+ const normalized = toWorkosJsonValue(value);
263
+ return normalized && typeof normalized === "object" && !Array.isArray(normalized) ? normalized : Object.freeze({});
264
+ }
125
265
  function normalizeWorkosUserProfile(user) {
126
266
  const firstName = typeof user.firstName === "string" ? user.firstName : typeof user.first_name === "string" ? user.first_name : void 0;
127
267
  const lastName = typeof user.lastName === "string" ? user.lastName : typeof user.last_name === "string" ? user.last_name : void 0;
128
- const name = typeof user.name === "string" ? user.name : [firstName, lastName].filter(Boolean).join(" ").trim() || void 0;
268
+ const name = typeof user.name === "string" ? user.name : [firstName, lastName].filter(Boolean).join(" ").trim();
269
+ const email = typeof user.email === "string" ? user.email.trim() : "";
129
270
  return Object.freeze({
130
271
  id: String(user.id ?? ""),
131
- email: typeof user.email === "string" ? user.email : void 0,
272
+ email,
132
273
  emailVerified: user.emailVerified === true || user.email_verified === true,
133
274
  firstName,
134
275
  lastName,
135
- name,
136
- avatar: typeof user.profilePictureUrl === "string" ? user.profilePictureUrl : typeof user.profile_picture_url === "string" ? user.profile_picture_url : void 0,
276
+ name: name || email || String(user.id ?? ""),
277
+ profilePictureUrl: typeof user.profilePictureUrl === "string" ? user.profilePictureUrl : typeof user.profile_picture_url === "string" ? user.profile_picture_url : void 0,
278
+ externalId: typeof user.externalId === "string" ? user.externalId : typeof user.external_id === "string" ? user.external_id : void 0,
137
279
  organizationId: typeof user.organizationId === "string" ? user.organizationId : typeof user.organization_id === "string" ? user.organization_id : void 0,
138
- raw: user
280
+ metadata: toWorkosJsonObject(user.metadata),
281
+ createdAt: typeof user.createdAt === "string" ? user.createdAt : typeof user.created_at === "string" ? user.created_at : void 0,
282
+ updatedAt: typeof user.updatedAt === "string" ? user.updatedAt : typeof user.updated_at === "string" ? user.updated_at : void 0,
283
+ raw: toWorkosJsonObject(user)
139
284
  });
140
285
  }
141
286
  async function fetchWorkosUserProfile(userId, config) {
@@ -193,7 +338,11 @@ function resolveConfiguredProviderName(provider) {
193
338
  if (provider?.trim()) {
194
339
  return provider.trim();
195
340
  }
196
- const configuredProviders = Object.keys(bindings.config.workos);
341
+ const configuredDefaultProvider = typeof bindings.config.workos.provider === "string" ? bindings.config.workos.provider.trim() : "";
342
+ if (configuredDefaultProvider) {
343
+ return configuredDefaultProvider;
344
+ }
345
+ const configuredProviders = Object.entries(bindings.config.workos).flatMap(([name, value]) => name !== "provider" && name !== "identityStore" && isNormalizedWorkosProviderConfig(value) ? [name] : []);
197
346
  if (configuredProviders.length === 0) {
198
347
  return "default";
199
348
  }
@@ -205,18 +354,20 @@ function resolveConfiguredProviderName(provider) {
205
354
  }
206
355
  throw new Error("[@holo-js/auth-workos] WorkOS provider name is required when multiple auth.workos entries exist.");
207
356
  }
357
+ function isNormalizedWorkosProviderConfig(value) {
358
+ return value !== null && typeof value === "object" && "sessionCookie" in value && typeof value.sessionCookie === "string";
359
+ }
208
360
  function getConfiguredProviderConfig(provider) {
209
361
  const providerName = resolveConfiguredProviderName(provider);
210
362
  const authBindings = authRuntimeInternals.getRuntimeBindings();
211
363
  const configured = authBindings.config.workos[providerName];
212
- if (!configured) {
364
+ if (!isNormalizedWorkosProviderConfig(configured)) {
213
365
  throw new Error(`[@holo-js/auth-workos] WorkOS provider "${providerName}" is not configured in auth.workos.`);
214
366
  }
215
367
  return {
216
368
  name: providerName,
217
369
  clientId: configured.clientId,
218
370
  apiKey: configured.apiKey,
219
- cookiePassword: configured.cookiePassword,
220
371
  redirectUri: configured.redirectUri,
221
372
  sessionCookie: configured.sessionCookie,
222
373
  guard: configured.guard,
@@ -227,6 +378,85 @@ function getProviderRuntime(provider) {
227
378
  const providerName = resolveConfiguredProviderName(provider);
228
379
  return getBindings().providers[providerName] ?? createDefaultProviderRuntime(providerName, getConfiguredProviderConfig(providerName));
229
380
  }
381
+ function requireWorkosClientConfig(config) {
382
+ const clientId = config.clientId?.trim();
383
+ const apiKey = config.apiKey?.trim();
384
+ const redirectUri = config.redirectUri?.trim();
385
+ if (!clientId) {
386
+ throw new Error("[@holo-js/auth-workos] WorkOS AuthKit requires clientId to be configured.");
387
+ }
388
+ if (!apiKey) {
389
+ throw new Error("[@holo-js/auth-workos] WorkOS AuthKit requires apiKey to be configured.");
390
+ }
391
+ if (!redirectUri) {
392
+ throw new Error("[@holo-js/auth-workos] WorkOS AuthKit requires redirectUri to be configured.");
393
+ }
394
+ return { clientId, apiKey, redirectUri };
395
+ }
396
+ function getWorkosStateCookieName(providerName) {
397
+ return `holo_workos_${providerName}_state`;
398
+ }
399
+ function createWorkosState() {
400
+ return randomBytes(32).toString("base64url");
401
+ }
402
+ function createStateCookie(providerName, state, request) {
403
+ return cookie(getWorkosStateCookieName(providerName), state, {
404
+ httpOnly: true,
405
+ maxAge: WORKOS_STATE_COOKIE_MAX_AGE_SECONDS,
406
+ sameSite: "lax",
407
+ secure: new URL(request.url).protocol === "https:"
408
+ });
409
+ }
410
+ function clearStateCookie(providerName, request) {
411
+ return cookie(getWorkosStateCookieName(providerName), "", {
412
+ expires: /* @__PURE__ */ new Date(0),
413
+ httpOnly: true,
414
+ sameSite: "lax",
415
+ secure: new URL(request.url).protocol === "https:"
416
+ });
417
+ }
418
+ function getWorkosStateFromRequest(request, providerName) {
419
+ const cookies = parseCookieHeader(request.headers.get("cookie"));
420
+ return cookies[getWorkosStateCookieName(providerName)]?.trim() || void 0;
421
+ }
422
+ function createAuthorizationUrl(config, screenHint, state) {
423
+ const { clientId, redirectUri } = requireWorkosClientConfig(config);
424
+ const url = new URL(WORKOS_AUTHORIZE_URL);
425
+ url.searchParams.set("provider", "authkit");
426
+ url.searchParams.set("response_type", "code");
427
+ url.searchParams.set("client_id", clientId);
428
+ url.searchParams.set("redirect_uri", redirectUri);
429
+ url.searchParams.set("screen_hint", screenHint);
430
+ url.searchParams.set("state", state);
431
+ return url.toString();
432
+ }
433
+ function createWorkosRedirect(request, config, screenHint) {
434
+ const state = createWorkosState();
435
+ return new Response(null, {
436
+ status: 302,
437
+ headers: {
438
+ location: createAuthorizationUrl(config, screenHint, state),
439
+ "set-cookie": createStateCookie(config.name, state, request)
440
+ }
441
+ });
442
+ }
443
+ function createLogoutUrl(sessionId, request, returnTo) {
444
+ const url = new URL(WORKOS_LOGOUT_URL);
445
+ url.searchParams.set("session_id", sessionId);
446
+ if (returnTo?.trim()) {
447
+ url.searchParams.set("return_to", new URL(returnTo, request.url).toString());
448
+ }
449
+ return url.toString();
450
+ }
451
+ function createWorkosErrorResponse(error, code) {
452
+ return Response.json({
453
+ ok: false,
454
+ code,
455
+ message: getErrorMessage(error)
456
+ }, {
457
+ status: error instanceof WorkosAuthConflictError ? 409 : 422
458
+ });
459
+ }
230
460
  function resolveGuardAndProvider(provider) {
231
461
  const authBindings = authRuntimeInternals.getRuntimeBindings();
232
462
  const providerConfig = getConfiguredProviderConfig(provider);
@@ -265,6 +495,46 @@ function requireUserRecord(user, message) {
265
495
  }
266
496
  return user;
267
497
  }
498
+ function requireSerializedUser(user, message) {
499
+ if (typeof user.id === "string" || typeof user.id === "number") {
500
+ return user;
501
+ }
502
+ throw new Error(message);
503
+ }
504
+ function createWorkosSessionPayload(authenticated, session) {
505
+ const user = requireSerializedUser(
506
+ authenticated.user,
507
+ "[@holo-js/auth-workos] WorkOS-authenticated local users must expose a serializable id."
508
+ );
509
+ return Object.freeze({
510
+ guard: authenticated.guard,
511
+ provider: authenticated.authProvider,
512
+ userId: user.id,
513
+ user,
514
+ workos: Object.freeze({
515
+ provider: authenticated.provider,
516
+ sessionId: session.sessionId
517
+ })
518
+ });
519
+ }
520
+ function getWorkosLogoutSession(payload, providerName) {
521
+ if (!payload || typeof payload !== "object" || !("workos" in payload)) {
522
+ return null;
523
+ }
524
+ const workos = payload.workos;
525
+ if (!workos || typeof workos !== "object") {
526
+ return null;
527
+ }
528
+ const provider = workos.provider;
529
+ const sessionId = workos.sessionId;
530
+ if (provider !== providerName || typeof sessionId !== "string" || !sessionId.trim()) {
531
+ return null;
532
+ }
533
+ return Object.freeze({
534
+ provider,
535
+ sessionId
536
+ });
537
+ }
268
538
  function serializeLocalUser(adapter, user, providerName) {
269
539
  const id = adapter.getId(user);
270
540
  const serialized = adapter.serialize ? adapter.serialize(user) : { ...user };
@@ -277,22 +547,63 @@ function serializeLocalUser(adapter, user, providerName) {
277
547
  enumerable: false,
278
548
  configurable: true
279
549
  });
550
+ Object.defineProperty(result, "can", {
551
+ value: () => false,
552
+ enumerable: false,
553
+ configurable: true
554
+ });
280
555
  return Object.freeze(result);
281
556
  }
282
557
  function resolveDisplayName(profile) {
283
- if (profile.name?.trim()) {
558
+ if (typeof profile.name === "string" && profile.name.trim()) {
559
+ return profile.name.trim();
560
+ }
561
+ const fullName = [profile.firstName?.trim(), profile.lastName?.trim()].filter(Boolean).join(" ").trim();
562
+ return fullName || profile.email || profile.id;
563
+ }
564
+ function resolveDisplayNameForUpdate(profile) {
565
+ if (typeof profile.name === "string" && profile.name.trim()) {
284
566
  return profile.name.trim();
285
567
  }
286
568
  const fullName = [profile.firstName?.trim(), profile.lastName?.trim()].filter(Boolean).join(" ").trim();
287
569
  return fullName || void 0;
288
570
  }
289
571
  function resolveEmailForCreation(profile) {
290
- const normalized = profile.email?.trim();
572
+ const normalized = typeof profile.email === "string" ? profile.email.trim() : "";
291
573
  if (normalized) {
292
574
  return normalized;
293
575
  }
294
576
  return `${profile.id}@workos.hosted.local`;
295
577
  }
578
+ function toAdapterInput(input) {
579
+ return Object.freeze(Object.fromEntries(
580
+ Object.entries(input).filter((entry) => typeof entry[1] !== "undefined")
581
+ ));
582
+ }
583
+ function withRequiredUserFields(input, profile) {
584
+ return Object.freeze({
585
+ ...input,
586
+ email: typeof input.email === "undefined" ? resolveEmailForCreation(profile) : input.email,
587
+ name: typeof input.name === "undefined" ? resolveDisplayName(profile) : input.name
588
+ });
589
+ }
590
+ function resolveCreateUserInput(profile, mapper) {
591
+ return toAdapterInput(withRequiredUserFields({
592
+ password: null,
593
+ avatar: profile.profilePictureUrl ?? null,
594
+ email_verified_at: profile.emailVerified ? /* @__PURE__ */ new Date() : null,
595
+ ...mapper?.(profile) ?? {}
596
+ }, profile));
597
+ }
598
+ function resolveUpdateUserInput(profile, mapper) {
599
+ return toAdapterInput({
600
+ email: typeof profile.email === "string" ? profile.email.trim() || void 0 : void 0,
601
+ name: resolveDisplayNameForUpdate(profile),
602
+ avatar: profile.profilePictureUrl,
603
+ email_verified_at: profile.emailVerified ? /* @__PURE__ */ new Date() : void 0,
604
+ ...mapper?.(profile) ?? {}
605
+ });
606
+ }
296
607
  function normalizeHostedProfile(profile) {
297
608
  return Object.freeze({
298
609
  id: profile.id,
@@ -301,7 +612,7 @@ function normalizeHostedProfile(profile) {
301
612
  firstName: profile.firstName,
302
613
  lastName: profile.lastName,
303
614
  name: resolveDisplayName(profile),
304
- avatar: profile.avatar,
615
+ profilePictureUrl: profile.profilePictureUrl,
305
616
  organizationId: profile.organizationId,
306
617
  raw: profile.raw
307
618
  });
@@ -314,21 +625,20 @@ async function findUserByEmail(adapter, email) {
314
625
  return user ? requireUserRecord(user, "[@holo-js/auth-workos] Auth provider lookups must return object users.") : null;
315
626
  }
316
627
  async function updateLocalUser(adapter, user, input) {
317
- const nextInput = {
318
- name: input.name,
319
- email: input.email,
320
- avatar: typeof input.avatar === "undefined" ? void 0 : input.avatar,
321
- email_verified_at: input.emailVerified === true ? /* @__PURE__ */ new Date() : void 0
322
- };
323
628
  const current = user;
324
- const changed = typeof nextInput.name !== "undefined" && nextInput.name !== current.name || typeof nextInput.email !== "undefined" && nextInput.email !== current.email || typeof nextInput.avatar !== "undefined" && nextInput.avatar !== (current.avatar ?? void 0) || input.emailVerified === true && !current.email_verified_at;
629
+ const changed = Object.entries(input).some(([key, value]) => {
630
+ if (key === "email_verified_at") {
631
+ return value instanceof Date && !current.email_verified_at;
632
+ }
633
+ return !Object.is(value, user[key]);
634
+ });
325
635
  if (!changed) {
326
636
  return { user, changed: false };
327
637
  }
328
638
  if (adapter.update) {
329
639
  return {
330
640
  user: requireUserRecord(
331
- await adapter.update(user, nextInput),
641
+ await adapter.update(user, input),
332
642
  "[@holo-js/auth-workos] Auth provider updates must return object users."
333
643
  ),
334
644
  changed: true
@@ -338,8 +648,8 @@ async function updateLocalUser(adapter, user, input) {
338
648
  "[@holo-js/auth-workos] Auth provider adapters must implement update() to persist profile changes."
339
649
  );
340
650
  }
341
- async function ensureNoUnexpectedEmailCollision(adapter, authProvider, profile, currentUserId) {
342
- const email = profile.email?.trim();
651
+ async function ensureNoUnexpectedEmailCollision(adapter, providerName, profile, currentUserId) {
652
+ const email = typeof profile.email === "string" ? profile.email.trim() : "";
343
653
  if (!email) {
344
654
  return;
345
655
  }
@@ -353,7 +663,7 @@ async function ensureNoUnexpectedEmailCollision(adapter, authProvider, profile,
353
663
  "[@holo-js/auth-workos] Matched local users must expose a serializable id."
354
664
  ) !== currentUserId) {
355
665
  throw new WorkosAuthConflictError({
356
- provider: "workos",
666
+ provider: providerName,
357
667
  workosUserId: profile.id,
358
668
  email,
359
669
  message: `[@holo-js/auth-workos] WorkOS email "${email}" collides with a different local user.`
@@ -426,9 +736,16 @@ async function reuseExistingHoloSession(request, authenticated) {
426
736
  if (!payload || payload.guard !== authenticated.guard || payload.provider !== authenticated.authProvider || String(payload.userId) !== String(authenticated.user.id)) {
427
737
  return null;
428
738
  }
739
+ const source = payload;
740
+ if (source.workos && typeof source.workos === "object") {
741
+ if (typeof source.workos.provider !== "string" || source.workos.provider !== authenticated.provider) {
742
+ return null;
743
+ }
744
+ }
429
745
  bindings.context.setCachedUser(authenticated.guard, authenticated.user);
430
746
  return Object.freeze({
431
747
  guard: authenticated.guard,
748
+ provider: source.workos && typeof source.workos === "object" ? "workos" : payload.provider,
432
749
  user: authenticated.user,
433
750
  sessionId,
434
751
  cookies: Object.freeze([])
@@ -446,7 +763,8 @@ async function verifySession(token, provider) {
446
763
  config: providerConfig
447
764
  });
448
765
  }
449
- async function verifyRequest(request, provider) {
766
+ async function verifyRequest(input, provider) {
767
+ const request = normalizeWorkosRequest(input);
450
768
  const providerConfig = getConfiguredProviderConfig(provider);
451
769
  const runtime = getProviderRuntime(providerConfig.name);
452
770
  if (runtime.verifyRequest) {
@@ -462,13 +780,72 @@ async function verifyRequest(request, provider) {
462
780
  }
463
781
  return verifySession(token, providerConfig.name);
464
782
  }
465
- async function syncIdentity(session, provider) {
783
+ function getStringField(input, ...names) {
784
+ for (const name of names) {
785
+ const value = input[name];
786
+ const normalized = typeof value === "string" ? value.trim() : "";
787
+ if (normalized) {
788
+ return normalized;
789
+ }
790
+ }
791
+ return void 0;
792
+ }
793
+ function getRecordField(input, name) {
794
+ const value = input[name];
795
+ return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
796
+ }
797
+ function getRequestIpAddress(request) {
798
+ return request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() || request.headers.get("x-real-ip")?.trim() || void 0;
799
+ }
800
+ async function authenticateWorkosCode(request, code, config) {
801
+ const { clientId, apiKey } = requireWorkosClientConfig(config);
802
+ const body = {
803
+ grant_type: "authorization_code",
804
+ client_id: clientId,
805
+ client_secret: apiKey,
806
+ code,
807
+ ip_address: getRequestIpAddress(request),
808
+ user_agent: request.headers.get("user-agent")?.trim() || void 0
809
+ };
810
+ const response = await fetch(WORKOS_AUTHENTICATE_URL, {
811
+ method: "POST",
812
+ headers: {
813
+ accept: "application/json",
814
+ authorization: `Bearer ${apiKey}`,
815
+ "content-type": "application/json"
816
+ },
817
+ body: JSON.stringify(body)
818
+ });
819
+ const payload = await response.json();
820
+ if (!response.ok) {
821
+ const message = getStringField(payload, "message", "error_description", "error") ?? `WorkOS callback authentication failed with status ${response.status}.`;
822
+ throw new Error(message);
823
+ }
824
+ const user = getRecordField(payload, "user");
825
+ if (!user) {
826
+ throw new Error("[@holo-js/auth-workos] WorkOS callback did not return a user.");
827
+ }
828
+ const accessToken = getStringField(payload, "accessToken", "access_token");
829
+ const session = getRecordField(payload, "session");
830
+ const sessionId = getStringField(session ?? {}, "id") ?? getStringField(payload, "sessionId", "session_id") ?? (accessToken ? getJwtStringClaim(accessToken, "sid") : void 0) ?? accessToken ?? code;
831
+ return Object.freeze({
832
+ sessionId,
833
+ identity: normalizeWorkosUserProfile({
834
+ ...user,
835
+ organizationId: getStringField(payload, "organizationId", "organization_id") ?? user.organizationId ?? user.organization_id
836
+ }),
837
+ accessToken,
838
+ raw: toWorkosJsonObject(payload)
839
+ });
840
+ }
841
+ async function syncIdentity(session, provider, options = {}) {
466
842
  const providerConfig = getConfiguredProviderConfig(provider);
467
843
  const providerName = providerConfig.name;
468
844
  const profile = session.identity;
469
845
  const { guard, authProvider, adapter } = resolveGuardAndProvider(providerName);
470
846
  const verificationRequired = isEmailVerificationRequired();
471
- const verifiedEmail = profile.emailVerified === true && profile.email?.trim() ? profile.email.trim() : void 0;
847
+ const profileEmail = typeof profile.email === "string" ? profile.email.trim() : "";
848
+ const verifiedEmail = profile.emailVerified === true && profileEmail ? profileEmail : void 0;
472
849
  if (verificationRequired && !verifiedEmail) {
473
850
  throw new Error(`[@holo-js/auth-workos] WorkOS identity "${profile.id}" must provide a verified email address.`);
474
851
  }
@@ -483,20 +860,12 @@ async function syncIdentity(session, provider) {
483
860
  await assertUserLinkAvailable(providerName, authProvider, adapter, linkedUser, profile.id);
484
861
  }
485
862
  if (!linkedUser) {
486
- linkedUser = requireUserRecord(await adapter.create({
487
- name: resolveDisplayName(profile),
488
- email: resolveEmailForCreation(profile),
489
- password: null,
490
- avatar: profile.avatar ?? null,
491
- email_verified_at: profile.emailVerified === true ? /* @__PURE__ */ new Date() : null
492
- }), "[@holo-js/auth-workos] Auth provider create() must return an object user.");
863
+ linkedUser = requireUserRecord(
864
+ await adapter.create(resolveCreateUserInput(profile, options.user)),
865
+ "[@holo-js/auth-workos] Auth provider create() must return an object user."
866
+ );
493
867
  }
494
- const relinked = await updateLocalUser(adapter, linkedUser, {
495
- name: resolveDisplayName(profile),
496
- email: profile.email?.trim(),
497
- avatar: profile.avatar,
498
- emailVerified: profile.emailVerified === true
499
- });
868
+ const relinked = await updateLocalUser(adapter, linkedUser, resolveUpdateUserInput(profile, options.user));
500
869
  const relinkedUser = relinked.user;
501
870
  const identity3 = createIdentityRecord({
502
871
  provider: providerName,
@@ -523,7 +892,7 @@ async function syncIdentity(session, provider) {
523
892
  }
524
893
  await ensureNoUnexpectedEmailCollision(
525
894
  adapter,
526
- authProvider,
895
+ providerName,
527
896
  profile,
528
897
  requireUserId(
529
898
  adapter,
@@ -531,12 +900,7 @@ async function syncIdentity(session, provider) {
531
900
  "[@holo-js/auth-workos] Linked local users must expose a serializable id."
532
901
  )
533
902
  );
534
- const updated = await updateLocalUser(adapter, linkedUser, {
535
- name: resolveDisplayName(profile),
536
- email: profile.email?.trim(),
537
- avatar: profile.avatar,
538
- emailVerified: profile.emailVerified === true
539
- });
903
+ const updated = await updateLocalUser(adapter, linkedUser, resolveUpdateUserInput(profile, options.user));
540
904
  const identity2 = createIdentityRecord({
541
905
  provider: providerName,
542
906
  guard,
@@ -563,12 +927,7 @@ async function syncIdentity(session, provider) {
563
927
  let localUser = verifiedEmail ? await findUserByEmail(adapter, verifiedEmail) : null;
564
928
  if (localUser) {
565
929
  await assertUserLinkAvailable(providerName, authProvider, adapter, localUser, profile.id);
566
- const linked = await updateLocalUser(adapter, localUser, {
567
- name: resolveDisplayName(profile),
568
- email: profile.email?.trim(),
569
- avatar: profile.avatar,
570
- emailVerified: profile.emailVerified === true
571
- });
930
+ const linked = await updateLocalUser(adapter, localUser, resolveUpdateUserInput(profile, options.user));
572
931
  const identity2 = createIdentityRecord({
573
932
  provider: providerName,
574
933
  guard,
@@ -591,13 +950,10 @@ async function syncIdentity(session, provider) {
591
950
  session
592
951
  });
593
952
  }
594
- localUser = requireUserRecord(await adapter.create({
595
- name: resolveDisplayName(profile),
596
- email: resolveEmailForCreation(profile),
597
- password: null,
598
- avatar: profile.avatar ?? null,
599
- email_verified_at: profile.emailVerified === true ? /* @__PURE__ */ new Date() : null
600
- }), "[@holo-js/auth-workos] Auth provider create() must return an object user.");
953
+ localUser = requireUserRecord(
954
+ await adapter.create(resolveCreateUserInput(profile, options.user)),
955
+ "[@holo-js/auth-workos] Auth provider create() must return an object user."
956
+ );
601
957
  const identity = createIdentityRecord({
602
958
  provider: providerName,
603
959
  guard,
@@ -620,7 +976,8 @@ async function syncIdentity(session, provider) {
620
976
  session
621
977
  });
622
978
  }
623
- async function authenticate(request, provider) {
979
+ async function authenticate(input, provider) {
980
+ const request = normalizeWorkosRequest(input);
624
981
  const session = await verifyRequest(request, provider);
625
982
  if (!session) {
626
983
  return null;
@@ -628,28 +985,175 @@ async function authenticate(request, provider) {
628
985
  const authenticated = await syncIdentity(session, provider);
629
986
  const authSession = await reuseExistingHoloSession(request, authenticated) ?? await authRuntimeInternals.establishSessionForUser(authenticated.user, {
630
987
  guard: authenticated.guard,
631
- provider: authenticated.authProvider
988
+ provider: authenticated.authProvider,
989
+ payload: createWorkosSessionPayload(authenticated, session)
632
990
  });
633
991
  return Object.freeze({
634
992
  ...authenticated,
635
993
  authSession
636
994
  });
637
995
  }
996
+ function getErrorMessage(error) {
997
+ return error instanceof Error ? error.message : "WorkOS authentication failed.";
998
+ }
999
+ function createHostedAuthSuccess(data) {
1000
+ return Object.freeze({
1001
+ data,
1002
+ error: null
1003
+ });
1004
+ }
1005
+ function createHostedAuthFailure(code, message, status) {
1006
+ return Object.freeze({
1007
+ data: null,
1008
+ error: Object.freeze({
1009
+ code,
1010
+ message,
1011
+ status,
1012
+ fields: Object.freeze({
1013
+ _root: Object.freeze([message])
1014
+ })
1015
+ })
1016
+ });
1017
+ }
1018
+ async function loginWithWorkos(input, options = {}) {
1019
+ try {
1020
+ const request = normalizeWorkosRequest(input);
1021
+ const providerConfig = getConfiguredProviderConfig(options.provider);
1022
+ return createWorkosRedirect(request, providerConfig, "sign-in");
1023
+ } catch (error) {
1024
+ return createWorkosErrorResponse(error, "workos_login_failed");
1025
+ }
1026
+ }
1027
+ async function registerWithWorkos(input, options = {}) {
1028
+ try {
1029
+ const request = normalizeWorkosRequest(input);
1030
+ const providerConfig = getConfiguredProviderConfig(options.provider);
1031
+ return createWorkosRedirect(request, providerConfig, "sign-up");
1032
+ } catch (error) {
1033
+ return createWorkosErrorResponse(error, "workos_register_failed");
1034
+ }
1035
+ }
1036
+ async function readCurrentWorkosLogoutSession(guard, providerName) {
1037
+ const bindings = authRuntimeInternals.getRuntimeBindings();
1038
+ const sessionId = bindings.context.getSessionId(guard);
1039
+ if (!sessionId) {
1040
+ return null;
1041
+ }
1042
+ const record = await bindings.session.read(sessionId);
1043
+ return getWorkosLogoutSession(authRuntimeInternals.readSessionPayload(record, guard), providerName);
1044
+ }
1045
+ async function logoutWithWorkos(input, options = {}) {
1046
+ const request = normalizeWorkosRequest(input);
1047
+ try {
1048
+ const providerConfig = getConfiguredProviderConfig(options.provider);
1049
+ const providerName = providerConfig.name;
1050
+ const { guard } = resolveGuardAndProvider(providerName);
1051
+ const requestSessionId = getHoloSessionIdFromRequest(request);
1052
+ if (requestSessionId) {
1053
+ authRuntimeInternals.getRuntimeBindings().context.setSessionId(guard, requestSessionId);
1054
+ }
1055
+ const workosSession = await readCurrentWorkosLogoutSession(guard, providerName);
1056
+ if (!workosSession) {
1057
+ return createHostedAuthFailure(
1058
+ "workos_session_missing",
1059
+ "The current Holo session was not created by WorkOS.",
1060
+ 422
1061
+ );
1062
+ }
1063
+ const local = await getAuthRuntime().guard(guard).logout();
1064
+ return createHostedAuthSuccess(Object.freeze({
1065
+ url: createLogoutUrl(workosSession.sessionId, request, options.returnTo),
1066
+ local
1067
+ }));
1068
+ } catch {
1069
+ return createHostedAuthFailure("workos_logout_failed", "Unable to complete WorkOS logout.", 500);
1070
+ }
1071
+ }
1072
+ async function completeWorkosAuth(input, options = {}) {
1073
+ try {
1074
+ const request = normalizeWorkosRequest(input);
1075
+ const url = new URL(request.url);
1076
+ const callbackError = url.searchParams.get("error")?.trim();
1077
+ if (callbackError) {
1078
+ return createHostedAuthFailure(
1079
+ callbackError,
1080
+ url.searchParams.get("error_description")?.trim() || "WorkOS authentication failed.",
1081
+ 422
1082
+ );
1083
+ }
1084
+ const code = url.searchParams.get("code")?.trim();
1085
+ if (!code) {
1086
+ return createHostedAuthFailure(
1087
+ "workos_code_required",
1088
+ "WorkOS callback did not include an authorization code.",
1089
+ 422
1090
+ );
1091
+ }
1092
+ const providerConfig = getConfiguredProviderConfig(options.provider);
1093
+ const callbackState = url.searchParams.get("state")?.trim();
1094
+ const expectedState = getWorkosStateFromRequest(request, providerConfig.name);
1095
+ if (!callbackState || !expectedState || !Object.is(callbackState, expectedState)) {
1096
+ return createHostedAuthFailure(
1097
+ "workos_state_mismatch",
1098
+ "WorkOS callback state did not match the login request.",
1099
+ 422
1100
+ );
1101
+ }
1102
+ const session = await authenticateWorkosCode(request, code, providerConfig);
1103
+ const authenticated = await syncIdentity(session, providerConfig.name, {
1104
+ user: options.user
1105
+ });
1106
+ const authSession = await authRuntimeInternals.establishSessionForUser(authenticated.user, {
1107
+ guard: authenticated.guard,
1108
+ provider: authenticated.authProvider,
1109
+ payload: createWorkosSessionPayload(authenticated, session)
1110
+ });
1111
+ return createHostedAuthSuccess(Object.freeze({
1112
+ provider: authenticated.provider,
1113
+ guard: authenticated.guard,
1114
+ authProvider: authenticated.authProvider,
1115
+ status: authenticated.status,
1116
+ user: authenticated.user,
1117
+ identity: authenticated.identity,
1118
+ session,
1119
+ authSession: Object.freeze({
1120
+ ...authSession,
1121
+ cookies: Object.freeze([
1122
+ ...authSession.cookies,
1123
+ clearStateCookie(providerConfig.name, request)
1124
+ ])
1125
+ })
1126
+ }));
1127
+ } catch (error) {
1128
+ return createHostedAuthFailure(
1129
+ error instanceof WorkosAuthConflictError ? error.code : "workos_auth_failed",
1130
+ getErrorMessage(error),
1131
+ error instanceof WorkosAuthConflictError ? 409 : 422
1132
+ );
1133
+ }
1134
+ }
638
1135
  function configureWorkosAuthRuntime(bindings) {
1136
+ const runtimeState = getRuntimeState();
639
1137
  if (!bindings) {
640
- workosBindings = void 0;
1138
+ runtimeState.bindings = void 0;
641
1139
  return;
642
1140
  }
643
- workosBindings = Object.freeze({
644
- providers: bindings.providers ?? workosBindings?.providers,
645
- identityStore: bindings.identityStore ?? workosBindings?.identityStore
1141
+ runtimeState.bindings = Object.freeze({
1142
+ providers: bindings.providers ?? runtimeState.bindings?.providers,
1143
+ identityStore: bindings.identityStore ?? runtimeState.bindings?.identityStore
646
1144
  });
647
1145
  }
648
1146
  function resetWorkosAuthRuntime() {
649
- workosBindings = void 0;
1147
+ getRuntimeState().bindings = void 0;
1148
+ workosDefaultProviderRuntimeCache.clear();
1149
+ workosJwksCache.clear();
650
1150
  }
651
1151
  var workosAuth = Object.freeze({
652
1152
  authenticate,
1153
+ completeWorkosAuth,
1154
+ loginWithWorkos,
1155
+ logoutWithWorkos,
1156
+ registerWithWorkos,
653
1157
  syncIdentity,
654
1158
  verifyRequest,
655
1159
  verifySession
@@ -667,7 +1171,11 @@ var workosAuthInternals = {
667
1171
  export {
668
1172
  WorkosAuthConflictError,
669
1173
  authenticate,
1174
+ completeWorkosAuth,
670
1175
  configureWorkosAuthRuntime,
1176
+ loginWithWorkos,
1177
+ logoutWithWorkos,
1178
+ registerWithWorkos,
671
1179
  resetWorkosAuthRuntime,
672
1180
  syncIdentity,
673
1181
  verifyRequest,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@holo-js/auth-workos",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "Holo-JS Framework - WorkOS auth integration contracts",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -23,14 +23,14 @@
23
23
  "test": "vitest --run"
24
24
  },
25
25
  "dependencies": {
26
- "@holo-js/auth": "workspace:*",
27
- "@holo-js/config": "workspace:*",
28
- "@holo-js/session": "workspace:*"
26
+ "@holo-js/auth": "^0.1.5",
27
+ "@holo-js/config": "^0.1.5",
28
+ "@holo-js/session": "^0.1.5"
29
29
  },
30
30
  "devDependencies": {
31
31
  "@types/node": "^22.10.2",
32
32
  "tsup": "^8.3.5",
33
33
  "typescript": "^5.7.2",
34
- "vitest": "^2.1.8"
34
+ "vitest": "^4.1.5"
35
35
  }
36
36
  }