@codefox-inc/oauth-provider 0.2.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 +201 -0
- package/README.md +572 -0
- package/dist/client/_generated/_ignore.d.ts +1 -0
- package/dist/client/_generated/_ignore.d.ts.map +1 -0
- package/dist/client/_generated/_ignore.js +3 -0
- package/dist/client/_generated/_ignore.js.map +1 -0
- package/dist/client/auth-config.d.ts +85 -0
- package/dist/client/auth-config.d.ts.map +1 -0
- package/dist/client/auth-config.js +81 -0
- package/dist/client/auth-config.js.map +1 -0
- package/dist/client/auth-helper.d.ts +81 -0
- package/dist/client/auth-helper.d.ts.map +1 -0
- package/dist/client/auth-helper.js +97 -0
- package/dist/client/auth-helper.js.map +1 -0
- package/dist/client/index.d.ts +189 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +230 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/routes.d.ts +94 -0
- package/dist/client/routes.d.ts.map +1 -0
- package/dist/client/routes.js +113 -0
- package/dist/client/routes.js.map +1 -0
- package/dist/component/_generated/api.d.ts +44 -0
- package/dist/component/_generated/api.d.ts.map +1 -0
- package/dist/component/_generated/api.js +31 -0
- package/dist/component/_generated/api.js.map +1 -0
- package/dist/component/_generated/component.d.ts +123 -0
- package/dist/component/_generated/component.d.ts.map +1 -0
- package/dist/component/_generated/component.js +11 -0
- package/dist/component/_generated/component.js.map +1 -0
- package/dist/component/_generated/dataModel.d.ts +46 -0
- package/dist/component/_generated/dataModel.d.ts.map +1 -0
- package/dist/component/_generated/dataModel.js +11 -0
- package/dist/component/_generated/dataModel.js.map +1 -0
- package/dist/component/_generated/server.d.ts +121 -0
- package/dist/component/_generated/server.d.ts.map +1 -0
- package/dist/component/_generated/server.js +78 -0
- package/dist/component/_generated/server.js.map +1 -0
- package/dist/component/clientManagement.d.ts +39 -0
- package/dist/component/clientManagement.d.ts.map +1 -0
- package/dist/component/clientManagement.js +169 -0
- package/dist/component/clientManagement.js.map +1 -0
- package/dist/component/constants.d.ts +31 -0
- package/dist/component/constants.d.ts.map +1 -0
- package/dist/component/constants.js +36 -0
- package/dist/component/constants.js.map +1 -0
- package/dist/component/convex.config.d.ts +3 -0
- package/dist/component/convex.config.d.ts.map +1 -0
- package/dist/component/convex.config.js +3 -0
- package/dist/component/convex.config.js.map +1 -0
- package/dist/component/handlers.d.ts +143 -0
- package/dist/component/handlers.d.ts.map +1 -0
- package/dist/component/handlers.js +624 -0
- package/dist/component/handlers.js.map +1 -0
- package/dist/component/mutations.d.ts +111 -0
- package/dist/component/mutations.d.ts.map +1 -0
- package/dist/component/mutations.js +459 -0
- package/dist/component/mutations.js.map +1 -0
- package/dist/component/queries.d.ts +127 -0
- package/dist/component/queries.d.ts.map +1 -0
- package/dist/component/queries.js +145 -0
- package/dist/component/queries.js.map +1 -0
- package/dist/component/schema.d.ts +116 -0
- package/dist/component/schema.d.ts.map +1 -0
- package/dist/component/schema.js +77 -0
- package/dist/component/schema.js.map +1 -0
- package/dist/component/token_security.d.ts +53 -0
- package/dist/component/token_security.d.ts.map +1 -0
- package/dist/component/token_security.js +91 -0
- package/dist/component/token_security.js.map +1 -0
- package/dist/lib/convex-types.d.ts +21 -0
- package/dist/lib/convex-types.d.ts.map +1 -0
- package/dist/lib/convex-types.js +2 -0
- package/dist/lib/convex-types.js.map +1 -0
- package/dist/lib/oauth.d.ts +123 -0
- package/dist/lib/oauth.d.ts.map +1 -0
- package/dist/lib/oauth.js +295 -0
- package/dist/lib/oauth.js.map +1 -0
- package/dist/react/index.d.ts +2 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +6 -0
- package/dist/react/index.js.map +1 -0
- package/package.json +121 -0
- package/src/client/__tests__/auth-config.test.ts +244 -0
- package/src/client/__tests__/auth-helper.test.ts +273 -0
- package/src/client/__tests__/oauth-provider.test.ts +418 -0
- package/src/client/__tests__/routes.test.ts +428 -0
- package/src/client/_generated/_ignore.ts +1 -0
- package/src/client/auth-config.ts +157 -0
- package/src/client/auth-helper.ts +201 -0
- package/src/client/index.ts +326 -0
- package/src/client/routes.ts +251 -0
- package/src/component/__tests__/oauth.test.ts +3310 -0
- package/src/component/__tests__/rfc-compliance.test.ts +788 -0
- package/src/component/__tests__/token-security.test.ts +133 -0
- package/src/component/_generated/api.ts +60 -0
- package/src/component/_generated/component.ts +201 -0
- package/src/component/_generated/dataModel.ts +60 -0
- package/src/component/_generated/server.ts +156 -0
- package/src/component/clientManagement.ts +189 -0
- package/src/component/constants.ts +40 -0
- package/src/component/convex.config.ts +3 -0
- package/src/component/handlers.ts +964 -0
- package/src/component/mutations.ts +531 -0
- package/src/component/queries.ts +165 -0
- package/src/component/schema.ts +92 -0
- package/src/component/token_security.ts +102 -0
- package/src/lib/__tests__/oauth-helpers.test.ts +143 -0
- package/src/lib/__tests__/oauth-jwt.test.ts +405 -0
- package/src/lib/convex-types.ts +37 -0
- package/src/lib/oauth.ts +412 -0
- package/src/react/index.ts +7 -0
- package/src/test.ts +21 -0
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import { describe, test, expect, vi, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { generateAuthConfig, createAuthConfig } from "../auth-config";
|
|
3
|
+
|
|
4
|
+
describe("Auth Config Generator", () => {
|
|
5
|
+
describe("generateAuthConfig", () => {
|
|
6
|
+
test("should generate config with localhost only (default options)", () => {
|
|
7
|
+
const config = generateAuthConfig();
|
|
8
|
+
|
|
9
|
+
expect(config.providers).toHaveLength(1);
|
|
10
|
+
expect(config.providers[0]).toEqual({
|
|
11
|
+
domain: "http://localhost:5173/oauth",
|
|
12
|
+
applicationID: "convex",
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test("should include CONVEX_SITE_URL provider when provided", () => {
|
|
17
|
+
const config = generateAuthConfig({
|
|
18
|
+
convexSiteUrl: "https://example.convex.site",
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
expect(config.providers).toHaveLength(3);
|
|
22
|
+
expect(config.providers[0]).toEqual({
|
|
23
|
+
domain: "https://example.convex.site",
|
|
24
|
+
applicationID: "convex",
|
|
25
|
+
});
|
|
26
|
+
expect(config.providers[1]).toEqual({
|
|
27
|
+
domain: "http://localhost:5173/oauth",
|
|
28
|
+
applicationID: "convex",
|
|
29
|
+
});
|
|
30
|
+
expect(config.providers[2]).toEqual({
|
|
31
|
+
domain: "https://example.convex.site/oauth",
|
|
32
|
+
applicationID: "convex",
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test("should exclude CONVEX_SITE_URL provider when includeConvexSiteUrl is false", () => {
|
|
37
|
+
const config = generateAuthConfig({
|
|
38
|
+
convexSiteUrl: "https://example.convex.site",
|
|
39
|
+
includeConvexSiteUrl: false,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
expect(config.providers).toHaveLength(2);
|
|
43
|
+
expect(config.providers[0]).toEqual({
|
|
44
|
+
domain: "http://localhost:5173/oauth",
|
|
45
|
+
applicationID: "convex",
|
|
46
|
+
});
|
|
47
|
+
expect(config.providers[1]).toEqual({
|
|
48
|
+
domain: "https://example.convex.site/oauth",
|
|
49
|
+
applicationID: "convex",
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("should apply custom prefix", () => {
|
|
54
|
+
const config = generateAuthConfig({
|
|
55
|
+
convexSiteUrl: "https://example.convex.site",
|
|
56
|
+
prefix: "/api/auth",
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
expect(config.providers).toHaveLength(3);
|
|
60
|
+
expect(config.providers[0]).toEqual({
|
|
61
|
+
domain: "https://example.convex.site",
|
|
62
|
+
applicationID: "convex",
|
|
63
|
+
});
|
|
64
|
+
expect(config.providers[1]).toEqual({
|
|
65
|
+
domain: "http://localhost:5173/api/auth",
|
|
66
|
+
applicationID: "convex",
|
|
67
|
+
});
|
|
68
|
+
expect(config.providers[2]).toEqual({
|
|
69
|
+
domain: "https://example.convex.site/api/auth",
|
|
70
|
+
applicationID: "convex",
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("should normalize root prefix", () => {
|
|
75
|
+
const config = generateAuthConfig({
|
|
76
|
+
convexSiteUrl: "https://example.convex.site",
|
|
77
|
+
prefix: "/",
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
expect(config.providers).toHaveLength(3);
|
|
81
|
+
expect(config.providers[0]).toEqual({
|
|
82
|
+
domain: "https://example.convex.site",
|
|
83
|
+
applicationID: "convex",
|
|
84
|
+
});
|
|
85
|
+
expect(config.providers[1]).toEqual({
|
|
86
|
+
domain: "http://localhost:5173",
|
|
87
|
+
applicationID: "convex",
|
|
88
|
+
});
|
|
89
|
+
expect(config.providers[2]).toEqual({
|
|
90
|
+
domain: "https://example.convex.site",
|
|
91
|
+
applicationID: "convex",
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test("should apply custom localPort", () => {
|
|
96
|
+
const config = generateAuthConfig({
|
|
97
|
+
localPort: 3000,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
expect(config.providers).toHaveLength(1);
|
|
101
|
+
expect(config.providers[0]).toEqual({
|
|
102
|
+
domain: "http://localhost:3000/oauth",
|
|
103
|
+
applicationID: "convex",
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test("should apply custom applicationID", () => {
|
|
108
|
+
const config = generateAuthConfig({
|
|
109
|
+
applicationID: "my-app",
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
expect(config.providers).toHaveLength(1);
|
|
113
|
+
expect(config.providers[0]).toEqual({
|
|
114
|
+
domain: "http://localhost:5173/oauth",
|
|
115
|
+
applicationID: "my-app",
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test("should include additional providers", () => {
|
|
120
|
+
const config = generateAuthConfig({
|
|
121
|
+
additionalProviders: [
|
|
122
|
+
{ domain: "https://auth.example.com", applicationID: "external" },
|
|
123
|
+
{ domain: "https://oauth.example.com", applicationID: "oauth" },
|
|
124
|
+
],
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
expect(config.providers).toHaveLength(3);
|
|
128
|
+
expect(config.providers[0]).toEqual({
|
|
129
|
+
domain: "http://localhost:5173/oauth",
|
|
130
|
+
applicationID: "convex",
|
|
131
|
+
});
|
|
132
|
+
expect(config.providers[1]).toEqual({
|
|
133
|
+
domain: "https://auth.example.com",
|
|
134
|
+
applicationID: "external",
|
|
135
|
+
});
|
|
136
|
+
expect(config.providers[2]).toEqual({
|
|
137
|
+
domain: "https://oauth.example.com",
|
|
138
|
+
applicationID: "oauth",
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test("should handle all options combined", () => {
|
|
143
|
+
const config = generateAuthConfig({
|
|
144
|
+
convexSiteUrl: "https://example.convex.site",
|
|
145
|
+
localPort: 8080,
|
|
146
|
+
prefix: "/api/oauth",
|
|
147
|
+
applicationID: "custom-app",
|
|
148
|
+
includeConvexSiteUrl: true,
|
|
149
|
+
additionalProviders: [
|
|
150
|
+
{ domain: "https://auth.example.com", applicationID: "external" },
|
|
151
|
+
],
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
expect(config.providers).toHaveLength(4);
|
|
155
|
+
expect(config.providers[0]).toEqual({
|
|
156
|
+
domain: "https://example.convex.site",
|
|
157
|
+
applicationID: "custom-app",
|
|
158
|
+
});
|
|
159
|
+
expect(config.providers[1]).toEqual({
|
|
160
|
+
domain: "http://localhost:8080/api/oauth",
|
|
161
|
+
applicationID: "custom-app",
|
|
162
|
+
});
|
|
163
|
+
expect(config.providers[2]).toEqual({
|
|
164
|
+
domain: "https://example.convex.site/api/oauth",
|
|
165
|
+
applicationID: "custom-app",
|
|
166
|
+
});
|
|
167
|
+
expect(config.providers[3]).toEqual({
|
|
168
|
+
domain: "https://auth.example.com",
|
|
169
|
+
applicationID: "external",
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
describe("createAuthConfig", () => {
|
|
175
|
+
let consoleWarnSpy: ReturnType<typeof vi.spyOn>;
|
|
176
|
+
|
|
177
|
+
beforeEach(() => {
|
|
178
|
+
consoleWarnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
afterEach(() => {
|
|
182
|
+
consoleWarnSpy.mockRestore();
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
test("should not warn when OAuth issuer is present (with prefix)", () => {
|
|
186
|
+
const config = createAuthConfig({
|
|
187
|
+
convexSiteUrl: "https://example.convex.site",
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
expect(config.providers).toHaveLength(3);
|
|
191
|
+
expect(consoleWarnSpy).not.toHaveBeenCalled();
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
test("should not warn when localhost OAuth issuer is present", () => {
|
|
195
|
+
const config = createAuthConfig();
|
|
196
|
+
|
|
197
|
+
expect(config.providers).toHaveLength(1);
|
|
198
|
+
expect(consoleWarnSpy).not.toHaveBeenCalled();
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
test("should not warn when custom port OAuth issuer is present", () => {
|
|
202
|
+
const config = createAuthConfig({
|
|
203
|
+
localPort: 3000,
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
expect(config.providers).toHaveLength(1);
|
|
207
|
+
expect(config.providers[0].domain).toContain(":3000");
|
|
208
|
+
expect(consoleWarnSpy).not.toHaveBeenCalled();
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
test("should not warn when custom prefix OAuth issuer is present", () => {
|
|
212
|
+
const config = createAuthConfig({
|
|
213
|
+
convexSiteUrl: "https://example.convex.site",
|
|
214
|
+
prefix: "/api/auth",
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
expect(config.providers.some(p => p.domain.includes("/api/auth"))).toBe(true);
|
|
218
|
+
expect(consoleWarnSpy).not.toHaveBeenCalled();
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
test("should warn when no OAuth issuer is found", () => {
|
|
222
|
+
// This scenario is difficult to trigger because localhost is always added
|
|
223
|
+
// But we can test the warning logic by providing only convexSiteUrl without prefix
|
|
224
|
+
const config = createAuthConfig({
|
|
225
|
+
convexSiteUrl: "https://example.convex.site",
|
|
226
|
+
includeConvexSiteUrl: true,
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
// Should have OAuth issuers (localhost + production)
|
|
230
|
+
expect(config.providers).toHaveLength(3);
|
|
231
|
+
expect(consoleWarnSpy).not.toHaveBeenCalled();
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
test("should handle root prefix validation", () => {
|
|
235
|
+
const config = createAuthConfig({
|
|
236
|
+
convexSiteUrl: "https://example.convex.site",
|
|
237
|
+
prefix: "/",
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
expect(config.providers).toHaveLength(3);
|
|
241
|
+
expect(consoleWarnSpy).not.toHaveBeenCalled();
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
});
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import { describe, test, expect, vi } from "vitest";
|
|
2
|
+
import { createAuthHelper } from "../auth-helper";
|
|
3
|
+
|
|
4
|
+
describe("Auth Helper", () => {
|
|
5
|
+
describe("OAuth tokens", () => {
|
|
6
|
+
test("default issuer pattern rejects non-OAuth tokens", async () => {
|
|
7
|
+
const helper = createAuthHelper();
|
|
8
|
+
const ctx = {
|
|
9
|
+
auth: {
|
|
10
|
+
getUserIdentity: async () => ({
|
|
11
|
+
issuer: "https://example.com",
|
|
12
|
+
subject: "user-1",
|
|
13
|
+
}),
|
|
14
|
+
},
|
|
15
|
+
db: {
|
|
16
|
+
normalizeId: (_table: string, id: string) => id,
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const userId = await helper.getCurrentUserId(ctx as any);
|
|
21
|
+
expect(userId).toBeNull();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test("issuer match enforces authorization check", async () => {
|
|
25
|
+
const checkAuthorization = vi.fn(async () => false);
|
|
26
|
+
const helper = createAuthHelper({ checkAuthorization });
|
|
27
|
+
const ctx = {
|
|
28
|
+
auth: {
|
|
29
|
+
getUserIdentity: async () => ({
|
|
30
|
+
issuer: "https://example.com/oauth",
|
|
31
|
+
subject: "user-1",
|
|
32
|
+
cid: "client-1",
|
|
33
|
+
}),
|
|
34
|
+
},
|
|
35
|
+
db: {
|
|
36
|
+
normalizeId: (_table: string, id: string) => id,
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const userId = await helper.getCurrentUserId(ctx as any);
|
|
41
|
+
expect(userId).toBeNull();
|
|
42
|
+
expect(checkAuthorization).toHaveBeenCalledWith(ctx, "user-1", "client-1");
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test("issuer match returns user id when authorized", async () => {
|
|
46
|
+
const checkAuthorization = vi.fn(async () => true);
|
|
47
|
+
const helper = createAuthHelper({ checkAuthorization });
|
|
48
|
+
const ctx = {
|
|
49
|
+
auth: {
|
|
50
|
+
getUserIdentity: async () => ({
|
|
51
|
+
issuer: "https://example.com/oauth",
|
|
52
|
+
subject: "user-1",
|
|
53
|
+
cid: "client-1",
|
|
54
|
+
}),
|
|
55
|
+
},
|
|
56
|
+
db: {
|
|
57
|
+
normalizeId: (_table: string, id: string) => id,
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const userId = await helper.getCurrentUserId(ctx as any);
|
|
62
|
+
expect(userId).toBe("user-1");
|
|
63
|
+
expect(checkAuthorization).toHaveBeenCalledWith(ctx, "user-1", "client-1");
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test("OAuth token with invalid user ID returns null", async () => {
|
|
67
|
+
const helper = createAuthHelper();
|
|
68
|
+
const ctx = {
|
|
69
|
+
auth: {
|
|
70
|
+
getUserIdentity: async () => ({
|
|
71
|
+
issuer: "https://example.com/oauth",
|
|
72
|
+
subject: "invalid-user",
|
|
73
|
+
}),
|
|
74
|
+
},
|
|
75
|
+
db: {
|
|
76
|
+
normalizeId: (_table: string, _id: string) => null,
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const userId = await helper.getCurrentUserId(ctx as any);
|
|
81
|
+
expect(userId).toBeNull();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test("OAuth token without checkAuthorization still validates", async () => {
|
|
85
|
+
const helper = createAuthHelper();
|
|
86
|
+
const ctx = {
|
|
87
|
+
auth: {
|
|
88
|
+
getUserIdentity: async () => ({
|
|
89
|
+
issuer: "https://example.com/oauth",
|
|
90
|
+
subject: "user-1",
|
|
91
|
+
}),
|
|
92
|
+
},
|
|
93
|
+
db: {
|
|
94
|
+
normalizeId: (_table: string, id: string) => id,
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const userId = await helper.getCurrentUserId(ctx as any);
|
|
99
|
+
expect(userId).toBe("user-1");
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test("custom oauthIssuerPattern", async () => {
|
|
103
|
+
const helper = createAuthHelper({ oauthIssuerPattern: "/api/auth" });
|
|
104
|
+
const ctx = {
|
|
105
|
+
auth: {
|
|
106
|
+
getUserIdentity: async () => ({
|
|
107
|
+
issuer: "https://example.com/api/auth",
|
|
108
|
+
subject: "user-1",
|
|
109
|
+
}),
|
|
110
|
+
},
|
|
111
|
+
db: {
|
|
112
|
+
normalizeId: (_table: string, id: string) => id,
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const userId = await helper.getCurrentUserId(ctx as any);
|
|
117
|
+
expect(userId).toBe("user-1");
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
describe("Convex Auth", () => {
|
|
122
|
+
test("getAuthUserId from config", async () => {
|
|
123
|
+
const getAuthUserId = vi.fn(async () => "user-1");
|
|
124
|
+
const helper = createAuthHelper({ getAuthUserId });
|
|
125
|
+
const ctx = {
|
|
126
|
+
auth: {
|
|
127
|
+
getUserIdentity: async () => null,
|
|
128
|
+
},
|
|
129
|
+
db: {
|
|
130
|
+
normalizeId: (_table: string, id: string) => id,
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const userId = await helper.getCurrentUserId(ctx as any);
|
|
135
|
+
expect(userId).toBe("user-1");
|
|
136
|
+
expect(getAuthUserId).toHaveBeenCalledWith(ctx);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
test("getAuthUserId from parameter", async () => {
|
|
140
|
+
const helper = createAuthHelper();
|
|
141
|
+
const getAuthUserId = vi.fn(async () => "user-2");
|
|
142
|
+
const ctx = {
|
|
143
|
+
auth: {
|
|
144
|
+
getUserIdentity: async () => null,
|
|
145
|
+
},
|
|
146
|
+
db: {
|
|
147
|
+
normalizeId: (_table: string, id: string) => id,
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const userId = await helper.getCurrentUserId(ctx as any, getAuthUserId);
|
|
152
|
+
expect(userId).toBe("user-2");
|
|
153
|
+
expect(getAuthUserId).toHaveBeenCalledWith(ctx);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
test("handles userId|sessionId format", async () => {
|
|
157
|
+
const getAuthUserId = vi.fn(async () => "user-1|session-123");
|
|
158
|
+
const helper = createAuthHelper({ getAuthUserId });
|
|
159
|
+
const ctx = {
|
|
160
|
+
auth: {
|
|
161
|
+
getUserIdentity: async () => null,
|
|
162
|
+
},
|
|
163
|
+
db: {
|
|
164
|
+
normalizeId: (_table: string, id: string) => id === "user-1" ? id : null,
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const userId = await helper.getCurrentUserId(ctx as any);
|
|
169
|
+
expect(userId).toBe("user-1");
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
test("returns null when getAuthUserId returns null", async () => {
|
|
173
|
+
const getAuthUserId = vi.fn(async () => null);
|
|
174
|
+
const helper = createAuthHelper({ getAuthUserId });
|
|
175
|
+
const ctx = {
|
|
176
|
+
auth: {
|
|
177
|
+
getUserIdentity: async () => null,
|
|
178
|
+
},
|
|
179
|
+
db: {
|
|
180
|
+
normalizeId: (_table: string, id: string) => id,
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
const userId = await helper.getCurrentUserId(ctx as any);
|
|
185
|
+
expect(userId).toBeNull();
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
test("returns null when no getAuthUserId provided", async () => {
|
|
189
|
+
const helper = createAuthHelper();
|
|
190
|
+
const ctx = {
|
|
191
|
+
auth: {
|
|
192
|
+
getUserIdentity: async () => null,
|
|
193
|
+
},
|
|
194
|
+
db: {
|
|
195
|
+
normalizeId: (_table: string, id: string) => id,
|
|
196
|
+
},
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
const userId = await helper.getCurrentUserId(ctx as any);
|
|
200
|
+
expect(userId).toBeNull();
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
describe("getCurrentUser", () => {
|
|
205
|
+
test("returns user document when authenticated", async () => {
|
|
206
|
+
const getAuthUserId = vi.fn(async () => "user-1");
|
|
207
|
+
const helper = createAuthHelper({ getAuthUserId });
|
|
208
|
+
const mockUser = { _id: "user-1", name: "Test User" };
|
|
209
|
+
const ctx = {
|
|
210
|
+
auth: {
|
|
211
|
+
getUserIdentity: async () => null,
|
|
212
|
+
},
|
|
213
|
+
db: {
|
|
214
|
+
normalizeId: (_table: string, id: string) => id,
|
|
215
|
+
get: vi.fn(async () => mockUser),
|
|
216
|
+
},
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
const user = await helper.getCurrentUser(ctx as any);
|
|
220
|
+
expect(user).toEqual(mockUser);
|
|
221
|
+
expect(ctx.db.get).toHaveBeenCalledWith("user-1");
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
test("returns null when not authenticated", async () => {
|
|
225
|
+
const helper = createAuthHelper();
|
|
226
|
+
const ctx = {
|
|
227
|
+
auth: {
|
|
228
|
+
getUserIdentity: async () => null,
|
|
229
|
+
},
|
|
230
|
+
db: {
|
|
231
|
+
normalizeId: (_table: string, id: string) => id,
|
|
232
|
+
get: vi.fn(),
|
|
233
|
+
},
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
const user = await helper.getCurrentUser(ctx as any);
|
|
237
|
+
expect(user).toBeNull();
|
|
238
|
+
expect(ctx.db.get).not.toHaveBeenCalled();
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
describe("requireAuth", () => {
|
|
243
|
+
test("returns userId when authenticated", async () => {
|
|
244
|
+
const getAuthUserId = vi.fn(async () => "user-1");
|
|
245
|
+
const helper = createAuthHelper({ getAuthUserId });
|
|
246
|
+
const ctx = {
|
|
247
|
+
auth: {
|
|
248
|
+
getUserIdentity: async () => null,
|
|
249
|
+
},
|
|
250
|
+
db: {
|
|
251
|
+
normalizeId: (_table: string, id: string) => id,
|
|
252
|
+
},
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
const userId = await helper.requireAuth(ctx as any);
|
|
256
|
+
expect(userId).toBe("user-1");
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
test("throws error when not authenticated", async () => {
|
|
260
|
+
const helper = createAuthHelper();
|
|
261
|
+
const ctx = {
|
|
262
|
+
auth: {
|
|
263
|
+
getUserIdentity: async () => null,
|
|
264
|
+
},
|
|
265
|
+
db: {
|
|
266
|
+
normalizeId: (_table: string, id: string) => id,
|
|
267
|
+
},
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
await expect(helper.requireAuth(ctx as any)).rejects.toThrow("Not authenticated");
|
|
271
|
+
});
|
|
272
|
+
});
|
|
273
|
+
});
|