@convex-dev/workos-authkit 0.1.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.
Files changed (60) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +288 -0
  3. package/dist/client/_generated/_ignore.d.ts +1 -0
  4. package/dist/client/_generated/_ignore.d.ts.map +1 -0
  5. package/dist/client/_generated/_ignore.js +3 -0
  6. package/dist/client/_generated/_ignore.js.map +1 -0
  7. package/dist/client/index.d.ts +83 -0
  8. package/dist/client/index.d.ts.map +1 -0
  9. package/dist/client/index.js +177 -0
  10. package/dist/client/index.js.map +1 -0
  11. package/dist/client/types.d.ts +27 -0
  12. package/dist/client/types.d.ts.map +1 -0
  13. package/dist/client/types.js +2 -0
  14. package/dist/client/types.js.map +1 -0
  15. package/dist/component/_generated/api.d.ts +112 -0
  16. package/dist/component/_generated/api.d.ts.map +1 -0
  17. package/dist/component/_generated/api.js +31 -0
  18. package/dist/component/_generated/api.js.map +1 -0
  19. package/dist/component/_generated/component.d.ts +50 -0
  20. package/dist/component/_generated/component.d.ts.map +1 -0
  21. package/dist/component/_generated/component.js +11 -0
  22. package/dist/component/_generated/component.js.map +1 -0
  23. package/dist/component/_generated/dataModel.d.ts +46 -0
  24. package/dist/component/_generated/dataModel.d.ts.map +1 -0
  25. package/dist/component/_generated/dataModel.js +11 -0
  26. package/dist/component/_generated/dataModel.js.map +1 -0
  27. package/dist/component/_generated/server.d.ts +121 -0
  28. package/dist/component/_generated/server.d.ts.map +1 -0
  29. package/dist/component/_generated/server.js +78 -0
  30. package/dist/component/_generated/server.js.map +1 -0
  31. package/dist/component/convex.config.d.ts +3 -0
  32. package/dist/component/convex.config.d.ts.map +1 -0
  33. package/dist/component/convex.config.js +6 -0
  34. package/dist/component/convex.config.js.map +1 -0
  35. package/dist/component/lib.d.ts +43 -0
  36. package/dist/component/lib.d.ts.map +1 -0
  37. package/dist/component/lib.js +177 -0
  38. package/dist/component/lib.js.map +1 -0
  39. package/dist/component/schema.d.ts +44 -0
  40. package/dist/component/schema.d.ts.map +1 -0
  41. package/dist/component/schema.js +24 -0
  42. package/dist/component/schema.js.map +1 -0
  43. package/dist/react/index.d.ts +2 -0
  44. package/dist/react/index.d.ts.map +1 -0
  45. package/dist/react/index.js +6 -0
  46. package/dist/react/index.js.map +1 -0
  47. package/package.json +108 -0
  48. package/src/client/_generated/_ignore.ts +1 -0
  49. package/src/client/index.ts +278 -0
  50. package/src/client/types.ts +71 -0
  51. package/src/component/_generated/api.ts +138 -0
  52. package/src/component/_generated/component.ts +63 -0
  53. package/src/component/_generated/dataModel.ts +60 -0
  54. package/src/component/_generated/server.ts +161 -0
  55. package/src/component/convex.config.ts +8 -0
  56. package/src/component/lib.ts +189 -0
  57. package/src/component/schema.ts +24 -0
  58. package/src/component/setup.test.ts +5 -0
  59. package/src/react/index.ts +7 -0
  60. package/src/test.ts +17 -0
@@ -0,0 +1,278 @@
1
+ import {
2
+ type AuthConfig,
3
+ type FunctionReference,
4
+ type GenericDataModel,
5
+ type GenericMutationCtx,
6
+ type HttpRouter,
7
+ createFunctionHandle,
8
+ httpActionGeneric,
9
+ internalMutationGeneric,
10
+ } from "convex/server";
11
+ import type { RunQueryCtx } from "./types.js";
12
+ import {
13
+ WorkOS,
14
+ type Event as WorkOSEvent,
15
+ type ActionContext as WorkOSActionContext,
16
+ type UserRegistrationActionResponseData,
17
+ type AuthenticationActionResponseData,
18
+ } from "@workos-inc/node";
19
+ import type { SetRequired } from "type-fest";
20
+ import { v } from "convex/values";
21
+ import type { ComponentApi } from "../component/_generated/component.js";
22
+
23
+ type WorkOSResponsePayload =
24
+ | AuthenticationActionResponseData
25
+ | UserRegistrationActionResponseData;
26
+
27
+ type Options = {
28
+ authFunctions: AuthFunctions;
29
+ clientId?: string;
30
+ apiKey?: string;
31
+ webhookSecret?: string;
32
+ webhookPath?: string;
33
+ additionalEventTypes?: WorkOSEvent["event"][];
34
+ actionSecret?: string;
35
+ logLevel?: "DEBUG";
36
+ };
37
+ type Config = SetRequired<
38
+ Options,
39
+ "clientId" | "apiKey" | "webhookSecret" | "actionSecret"
40
+ >;
41
+
42
+ export type AuthFunctions = {
43
+ authKitAction: FunctionReference<
44
+ "mutation",
45
+ "internal",
46
+ { action: unknown },
47
+ WorkOSResponsePayload
48
+ >;
49
+ authKitEvent?: FunctionReference<
50
+ "mutation",
51
+ "internal",
52
+ { event: string; data: unknown },
53
+ null
54
+ >;
55
+ };
56
+
57
+ export class AuthKit<DataModel extends GenericDataModel> {
58
+ public workos: WorkOS;
59
+ private config: Config;
60
+ constructor(
61
+ public component: ComponentApi,
62
+ public options: Options
63
+ ) {
64
+ const clientId = options?.clientId ?? process.env.WORKOS_CLIENT_ID;
65
+ const apiKey = options?.apiKey ?? process.env.WORKOS_API_KEY;
66
+ const webhookSecret =
67
+ options?.webhookSecret ?? process.env.WORKOS_WEBHOOK_SECRET;
68
+ const actionSecret =
69
+ options?.actionSecret ?? process.env.WORKOS_ACTION_SECRET;
70
+ const missingEnvVars: string[] = [];
71
+ if (!clientId) {
72
+ missingEnvVars.push("WORKOS_CLIENT_ID");
73
+ }
74
+ if (!apiKey) {
75
+ missingEnvVars.push("WORKOS_API_KEY");
76
+ }
77
+ if (!webhookSecret) {
78
+ missingEnvVars.push("WORKOS_WEBHOOK_SECRET");
79
+ }
80
+ if (!actionSecret) {
81
+ missingEnvVars.push("WORKOS_ACTION_SECRET");
82
+ }
83
+ if (!clientId || !apiKey || !webhookSecret || !actionSecret) {
84
+ throw new Error(
85
+ `Missing environment variables: ${missingEnvVars.join(", ")}`
86
+ );
87
+ }
88
+ this.config = {
89
+ ...options,
90
+ clientId,
91
+ apiKey,
92
+ webhookSecret,
93
+ actionSecret,
94
+ webhookPath: options?.webhookPath ?? "/workos/webhook",
95
+ };
96
+ this.workos = new WorkOS(this.config.apiKey);
97
+ }
98
+
99
+ getAuthConfigProviders = () =>
100
+ [
101
+ {
102
+ type: "customJwt",
103
+ issuer: `https://api.workos.com/`,
104
+ algorithm: "RS256",
105
+ jwks: `https://api.workos.com/sso/jwks/${this.config.clientId}`,
106
+ applicationID: this.config.clientId,
107
+ },
108
+ {
109
+ type: "customJwt",
110
+ issuer: `https://api.workos.com/user_management/${this.config.clientId}`,
111
+ algorithm: "RS256",
112
+ jwks: `https://api.workos.com/sso/jwks/${this.config.clientId}`,
113
+ },
114
+ ] satisfies AuthConfig["providers"];
115
+
116
+ async getAuthUser(ctx: RunQueryCtx) {
117
+ const identity = await ctx.auth.getUserIdentity();
118
+ if (!identity) {
119
+ return null;
120
+ }
121
+ return ctx.runQuery(this.component.lib.getAuthUser, {
122
+ id: identity.subject,
123
+ });
124
+ }
125
+ events<K extends WorkOSEvent["event"]>(opts: {
126
+ [Key in K]: <
127
+ E extends Extract<
128
+ WorkOSEvent,
129
+ {
130
+ event: Key;
131
+ }
132
+ >,
133
+ >(
134
+ ctx: GenericMutationCtx<DataModel>,
135
+ event: E
136
+ ) => Promise<void>;
137
+ }) {
138
+ return {
139
+ authKitEvent: internalMutationGeneric({
140
+ args: {
141
+ event: v.string(),
142
+ data: v.record(v.string(), v.any()),
143
+ },
144
+ returns: v.null(),
145
+ handler: async (ctx, args) => {
146
+ await opts[args.event as K](ctx, args as never);
147
+ },
148
+ }),
149
+ };
150
+ }
151
+ actions<K extends "authentication" | "userRegistration">(opts: {
152
+ [Key in K]: <
153
+ A extends Extract<
154
+ WorkOSActionContext,
155
+ {
156
+ object: Key extends "authentication"
157
+ ? "authentication_action_context"
158
+ : "user_registration_action_context";
159
+ }
160
+ >,
161
+ >(
162
+ ctx: GenericMutationCtx<DataModel>,
163
+ action: A,
164
+ {
165
+ allow,
166
+ deny,
167
+ }: {
168
+ allow: () => WorkOSResponsePayload;
169
+ deny: (errorMessage: string) => WorkOSResponsePayload;
170
+ }
171
+ ) => Promise<WorkOSResponsePayload>;
172
+ }) {
173
+ return {
174
+ authKitAction: internalMutationGeneric({
175
+ args: {
176
+ action: v.record(v.string(), v.any()),
177
+ },
178
+ returns: v.record(v.string(), v.any()),
179
+ handler: async (ctx, args) => {
180
+ const resp = {
181
+ type:
182
+ args.action.object === "authentication_action_context"
183
+ ? ("authentication" as const)
184
+ : ("user_registration" as const),
185
+ timestamp: new Date().getTime(),
186
+ };
187
+ const allow = () => ({ ...resp, verdict: "Allow" as const });
188
+ const deny = (errorMessage: string) => ({
189
+ ...resp,
190
+ verdict: "Deny" as const,
191
+ errorMessage,
192
+ });
193
+ const responsePayload = await opts[
194
+ (args.action.object === "authentication_action_context"
195
+ ? "authentication"
196
+ : "userRegistration") as K
197
+ ](ctx, args.action as never, {
198
+ allow,
199
+ deny,
200
+ });
201
+ return responsePayload;
202
+ },
203
+ }),
204
+ };
205
+ }
206
+ registerRoutes(http: HttpRouter) {
207
+ http.route({
208
+ path: "/workos/webhook",
209
+ method: "POST",
210
+ handler: httpActionGeneric(async (ctx, request) => {
211
+ const payload = await request.text();
212
+ const sigHeader = request.headers.get("workos-signature");
213
+ if (!sigHeader) {
214
+ throw new Error("No signature header");
215
+ }
216
+ const secret = this.config.webhookSecret;
217
+ if (!secret) {
218
+ throw new Error("webhook secret is not set");
219
+ }
220
+ const event = await this.workos.webhooks.constructEvent({
221
+ payload: JSON.parse(payload),
222
+ sigHeader: sigHeader,
223
+ secret,
224
+ });
225
+ if (this.config.logLevel === "DEBUG") {
226
+ console.log("received event", event);
227
+ }
228
+ await ctx.runMutation(this.component.lib.enqueueWebhookEvent, {
229
+ apiKey: this.config.apiKey,
230
+ eventId: event.id,
231
+ event: event.event,
232
+ onEventHandle: this.config.authFunctions?.authKitEvent
233
+ ? await createFunctionHandle(this.config.authFunctions.authKitEvent)
234
+ : undefined,
235
+ updatedAt:
236
+ "updated_at" in event ? (event.updated_at as string) : undefined,
237
+ eventTypes: this.config.additionalEventTypes,
238
+ logLevel: this.config.logLevel,
239
+ });
240
+ return new Response("OK", { status: 200 });
241
+ }),
242
+ });
243
+ http.route({
244
+ path: "/workos/action",
245
+ method: "POST",
246
+ handler: httpActionGeneric(async (ctx, request) => {
247
+ const payload = await request.text();
248
+ const sigHeader = request.headers.get("workos-signature");
249
+ if (!sigHeader) {
250
+ throw new Error("No signature header");
251
+ }
252
+ const secret = this.config.actionSecret;
253
+ if (!secret) {
254
+ throw new Error("webhook secret is not set");
255
+ }
256
+ const action = await this.workos.actions.constructAction({
257
+ payload: JSON.parse(payload),
258
+ sigHeader: sigHeader,
259
+ secret,
260
+ });
261
+ if (this.config.logLevel === "DEBUG") {
262
+ console.log("received action", action);
263
+ }
264
+ const responsePayload: WorkOSResponsePayload = await ctx.runMutation(
265
+ this.config.authFunctions.authKitAction,
266
+ {
267
+ action,
268
+ }
269
+ );
270
+ const response = await this.workos.actions.signResponse(
271
+ responsePayload,
272
+ this.config.actionSecret
273
+ );
274
+ return new Response(JSON.stringify(response), { status: 200 });
275
+ }),
276
+ });
277
+ }
278
+ }
@@ -0,0 +1,71 @@
1
+ import type {
2
+ Auth,
3
+ Expand,
4
+ FunctionArgs,
5
+ FunctionReference,
6
+ FunctionReturnType,
7
+ StorageActionWriter,
8
+ StorageReader,
9
+ } from "convex/server";
10
+ import type { GenericId } from "convex/values";
11
+
12
+ // Type utils follow
13
+
14
+ export type RunQueryCtx = {
15
+ auth: Auth;
16
+ runQuery: <Query extends FunctionReference<"query", "internal">>(
17
+ query: Query,
18
+ args: FunctionArgs<Query>
19
+ ) => Promise<FunctionReturnType<Query>>;
20
+ };
21
+ export type RunMutationCtx = RunQueryCtx & {
22
+ auth: Auth;
23
+ runMutation: <Mutation extends FunctionReference<"mutation", "internal">>(
24
+ mutation: Mutation,
25
+ args: FunctionArgs<Mutation>
26
+ ) => Promise<FunctionReturnType<Mutation>>;
27
+ };
28
+ export type RunActionCtx = RunMutationCtx & {
29
+ auth: Auth;
30
+ runAction<Action extends FunctionReference<"action", "internal">>(
31
+ action: Action,
32
+ args: FunctionArgs<Action>
33
+ ): Promise<FunctionReturnType<Action>>;
34
+ };
35
+ export type ActionCtx = RunActionCtx & {
36
+ storage: StorageActionWriter;
37
+ };
38
+ export type QueryCtx = RunQueryCtx & {
39
+ storage: StorageReader;
40
+ };
41
+
42
+ export type OpaqueIds<T> =
43
+ T extends GenericId<infer _T>
44
+ ? string
45
+ : T extends (infer U)[]
46
+ ? OpaqueIds<U>[]
47
+ : T extends ArrayBuffer
48
+ ? ArrayBuffer
49
+ : T extends object
50
+ ? {
51
+ [K in keyof T]: OpaqueIds<T[K]>;
52
+ }
53
+ : T;
54
+
55
+ export type UseApi<API> = Expand<{
56
+ [mod in keyof API]: API[mod] extends FunctionReference<
57
+ infer FType,
58
+ "public",
59
+ infer FArgs,
60
+ infer FReturnType,
61
+ infer FComponentPath
62
+ >
63
+ ? FunctionReference<
64
+ FType,
65
+ "internal",
66
+ OpaqueIds<FArgs>,
67
+ OpaqueIds<FReturnType>,
68
+ FComponentPath
69
+ >
70
+ : UseApi<API[mod]>;
71
+ }>;
@@ -0,0 +1,138 @@
1
+ /* eslint-disable */
2
+ /**
3
+ * Generated `api` utility.
4
+ *
5
+ * THIS CODE IS AUTOMATICALLY GENERATED.
6
+ *
7
+ * To regenerate, run `npx convex dev`.
8
+ * @module
9
+ */
10
+
11
+ import type * as lib from "../lib.js";
12
+
13
+ import type {
14
+ ApiFromModules,
15
+ FilterApi,
16
+ FunctionReference,
17
+ } from "convex/server";
18
+ import { anyApi, componentsGeneric } from "convex/server";
19
+
20
+ const fullApi: ApiFromModules<{
21
+ lib: typeof lib;
22
+ }> = anyApi as any;
23
+
24
+ /**
25
+ * A utility for referencing Convex functions in your app's public API.
26
+ *
27
+ * Usage:
28
+ * ```js
29
+ * const myFunctionReference = api.myModule.myFunction;
30
+ * ```
31
+ */
32
+ export const api: FilterApi<
33
+ typeof fullApi,
34
+ FunctionReference<any, "public">
35
+ > = anyApi as any;
36
+
37
+ /**
38
+ * A utility for referencing Convex functions in your app's internal API.
39
+ *
40
+ * Usage:
41
+ * ```js
42
+ * const myFunctionReference = internal.myModule.myFunction;
43
+ * ```
44
+ */
45
+ export const internal: FilterApi<
46
+ typeof fullApi,
47
+ FunctionReference<any, "internal">
48
+ > = anyApi as any;
49
+
50
+ export const components = componentsGeneric() as unknown as {
51
+ eventWorkpool: {
52
+ lib: {
53
+ cancel: FunctionReference<
54
+ "mutation",
55
+ "internal",
56
+ {
57
+ id: string;
58
+ logLevel: "DEBUG" | "TRACE" | "INFO" | "REPORT" | "WARN" | "ERROR";
59
+ },
60
+ any
61
+ >;
62
+ cancelAll: FunctionReference<
63
+ "mutation",
64
+ "internal",
65
+ {
66
+ before?: number;
67
+ limit?: number;
68
+ logLevel: "DEBUG" | "TRACE" | "INFO" | "REPORT" | "WARN" | "ERROR";
69
+ },
70
+ any
71
+ >;
72
+ enqueue: FunctionReference<
73
+ "mutation",
74
+ "internal",
75
+ {
76
+ config: {
77
+ logLevel: "DEBUG" | "TRACE" | "INFO" | "REPORT" | "WARN" | "ERROR";
78
+ maxParallelism: number;
79
+ };
80
+ fnArgs: any;
81
+ fnHandle: string;
82
+ fnName: string;
83
+ fnType: "action" | "mutation" | "query";
84
+ onComplete?: { context?: any; fnHandle: string };
85
+ retryBehavior?: {
86
+ base: number;
87
+ initialBackoffMs: number;
88
+ maxAttempts: number;
89
+ };
90
+ runAt: number;
91
+ },
92
+ string
93
+ >;
94
+ enqueueBatch: FunctionReference<
95
+ "mutation",
96
+ "internal",
97
+ {
98
+ config: {
99
+ logLevel: "DEBUG" | "TRACE" | "INFO" | "REPORT" | "WARN" | "ERROR";
100
+ maxParallelism: number;
101
+ };
102
+ items: Array<{
103
+ fnArgs: any;
104
+ fnHandle: string;
105
+ fnName: string;
106
+ fnType: "action" | "mutation" | "query";
107
+ onComplete?: { context?: any; fnHandle: string };
108
+ retryBehavior?: {
109
+ base: number;
110
+ initialBackoffMs: number;
111
+ maxAttempts: number;
112
+ };
113
+ runAt: number;
114
+ }>;
115
+ },
116
+ Array<string>
117
+ >;
118
+ status: FunctionReference<
119
+ "query",
120
+ "internal",
121
+ { id: string },
122
+ | { previousAttempts: number; state: "pending" }
123
+ | { previousAttempts: number; state: "running" }
124
+ | { state: "finished" }
125
+ >;
126
+ statusBatch: FunctionReference<
127
+ "query",
128
+ "internal",
129
+ { ids: Array<string> },
130
+ Array<
131
+ | { previousAttempts: number; state: "pending" }
132
+ | { previousAttempts: number; state: "running" }
133
+ | { state: "finished" }
134
+ >
135
+ >;
136
+ };
137
+ };
138
+ };
@@ -0,0 +1,63 @@
1
+ /* eslint-disable */
2
+ /**
3
+ * Generated `ComponentApi` utility.
4
+ *
5
+ * THIS CODE IS AUTOMATICALLY GENERATED.
6
+ *
7
+ * To regenerate, run `npx convex dev`.
8
+ * @module
9
+ */
10
+
11
+ import type { FunctionReference } from "convex/server";
12
+
13
+ /**
14
+ * A utility for referencing a Convex component's exposed API.
15
+ *
16
+ * Useful when expecting a parameter like `components.myComponent`.
17
+ * Usage:
18
+ * ```ts
19
+ * async function myFunction(ctx: QueryCtx, component: ComponentApi) {
20
+ * return ctx.runQuery(component.someFile.someQuery, { ...args });
21
+ * }
22
+ * ```
23
+ */
24
+ export type ComponentApi<Name extends string | undefined = string | undefined> =
25
+ {
26
+ lib: {
27
+ enqueueWebhookEvent: FunctionReference<
28
+ "mutation",
29
+ "internal",
30
+ {
31
+ apiKey: string;
32
+ event: string;
33
+ eventId: string;
34
+ eventTypes?: Array<string>;
35
+ logLevel?: "DEBUG";
36
+ onEventHandle?: string;
37
+ updatedAt?: string;
38
+ },
39
+ any,
40
+ Name
41
+ >;
42
+ getAuthUser: FunctionReference<
43
+ "query",
44
+ "internal",
45
+ { id: string },
46
+ {
47
+ createdAt: string;
48
+ email: string;
49
+ emailVerified: boolean;
50
+ externalId?: null | string;
51
+ firstName?: null | string;
52
+ id: string;
53
+ lastName?: null | string;
54
+ lastSignInAt?: null | string;
55
+ locale?: null | string;
56
+ metadata: Record<string, any>;
57
+ profilePictureUrl?: null | string;
58
+ updatedAt: string;
59
+ } | null,
60
+ Name
61
+ >;
62
+ };
63
+ };
@@ -0,0 +1,60 @@
1
+ /* eslint-disable */
2
+ /**
3
+ * Generated data model types.
4
+ *
5
+ * THIS CODE IS AUTOMATICALLY GENERATED.
6
+ *
7
+ * To regenerate, run `npx convex dev`.
8
+ * @module
9
+ */
10
+
11
+ import type {
12
+ DataModelFromSchemaDefinition,
13
+ DocumentByName,
14
+ TableNamesInDataModel,
15
+ SystemTableNames,
16
+ } from "convex/server";
17
+ import type { GenericId } from "convex/values";
18
+ import schema from "../schema.js";
19
+
20
+ /**
21
+ * The names of all of your Convex tables.
22
+ */
23
+ export type TableNames = TableNamesInDataModel<DataModel>;
24
+
25
+ /**
26
+ * The type of a document stored in Convex.
27
+ *
28
+ * @typeParam TableName - A string literal type of the table name (like "users").
29
+ */
30
+ export type Doc<TableName extends TableNames> = DocumentByName<
31
+ DataModel,
32
+ TableName
33
+ >;
34
+
35
+ /**
36
+ * An identifier for a document in Convex.
37
+ *
38
+ * Convex documents are uniquely identified by their `Id`, which is accessible
39
+ * on the `_id` field. To learn more, see [Document IDs](https://docs.convex.dev/using/document-ids).
40
+ *
41
+ * Documents can be loaded using `db.get(id)` in query and mutation functions.
42
+ *
43
+ * IDs are just strings at runtime, but this type can be used to distinguish them from other
44
+ * strings when type checking.
45
+ *
46
+ * @typeParam TableName - A string literal type of the table name (like "users").
47
+ */
48
+ export type Id<TableName extends TableNames | SystemTableNames> =
49
+ GenericId<TableName>;
50
+
51
+ /**
52
+ * A type describing your Convex data model.
53
+ *
54
+ * This type includes information about what tables you have, the type of
55
+ * documents stored in those tables, and the indexes defined on them.
56
+ *
57
+ * This type is used to parameterize methods like `queryGeneric` and
58
+ * `mutationGeneric` to make them type-safe.
59
+ */
60
+ export type DataModel = DataModelFromSchemaDefinition<typeof schema>;