@lodashventure/medusa-login-provider 4.1.1 → 4.1.2

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.
@@ -0,0 +1,438 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const service_1 = __importDefault(require("../service"));
7
+ const utils_1 = require("@medusajs/framework/utils");
8
+ const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
9
+ const crypto_1 = __importDefault(require("crypto"));
10
+ // Mock dependencies
11
+ jest.mock("jsonwebtoken");
12
+ jest.mock("crypto");
13
+ // Mock fetch globally
14
+ global.fetch = jest.fn();
15
+ describe("LineProviderService", () => {
16
+ let service;
17
+ let mockLogger;
18
+ let mockCustomerService;
19
+ let mockAuthIdentityService;
20
+ let mockFetch;
21
+ let mockCrypto;
22
+ let mockJwt;
23
+ const defaultOptions = {
24
+ lineChannelId: "1234567890",
25
+ lineChannelSecret: "test-secret",
26
+ lineRedirectUrl: "https://example.com/callback",
27
+ autoCreateCustomer: true,
28
+ syncProfileData: true,
29
+ };
30
+ beforeEach(() => {
31
+ jest.clearAllMocks();
32
+ mockFetch = global.fetch;
33
+ mockCrypto = crypto_1.default;
34
+ mockJwt = jsonwebtoken_1.default;
35
+ mockLogger = {
36
+ info: jest.fn(),
37
+ error: jest.fn(),
38
+ warn: jest.fn(),
39
+ debug: jest.fn(),
40
+ };
41
+ mockCustomerService = {
42
+ create: jest.fn(),
43
+ update: jest.fn(),
44
+ retrieve: jest.fn(),
45
+ listAndCount: jest.fn(),
46
+ };
47
+ mockAuthIdentityService = {
48
+ create: jest.fn(),
49
+ update: jest.fn(),
50
+ retrieve: jest.fn(),
51
+ setState: jest.fn(),
52
+ getState: jest.fn(),
53
+ };
54
+ service = new service_1.default({
55
+ logger: mockLogger,
56
+ customerService: mockCustomerService,
57
+ }, defaultOptions);
58
+ // Mock crypto.randomBytes
59
+ const mockBuffer = Buffer.from("a".repeat(32));
60
+ mockCrypto.randomBytes.mockReturnValue(mockBuffer);
61
+ });
62
+ describe("validateOptions", () => {
63
+ it("should validate required options", () => {
64
+ expect(() => {
65
+ service_1.default.validateOptions({});
66
+ }).toThrow("line channel id is required");
67
+ expect(() => {
68
+ service_1.default.validateOptions({
69
+ lineChannelId: "123",
70
+ });
71
+ }).toThrow("line channel secret is required");
72
+ expect(() => {
73
+ service_1.default.validateOptions({
74
+ lineChannelId: "123",
75
+ lineChannelSecret: "secret",
76
+ });
77
+ }).not.toThrow(); // Redirect URL is now optional
78
+ // Validation should pass with just channel ID and secret
79
+ });
80
+ });
81
+ describe("register", () => {
82
+ it("should throw NOT_ALLOWED error", async () => {
83
+ await expect(service.register({}, mockAuthIdentityService)).rejects.toThrow(utils_1.MedusaError);
84
+ });
85
+ });
86
+ describe("authenticate", () => {
87
+ it("should return redirect URL for LINE OAuth", async () => {
88
+ mockAuthIdentityService.setState.mockResolvedValue(undefined);
89
+ const result = await service.authenticate({ body: { callback_url: "https://example.com/callback" } }, mockAuthIdentityService);
90
+ expect(result.success).toBe(true);
91
+ expect(result.location).toContain("https://access.line.me/oauth2/v2.1/authorize");
92
+ expect(result.location).toContain("client_id=1234567890");
93
+ expect(result.location).toContain("scope=profile%20openid%20email");
94
+ expect(mockAuthIdentityService.setState).toHaveBeenCalled();
95
+ });
96
+ it("should handle authentication errors", async () => {
97
+ const result = await service.authenticate({
98
+ query: {
99
+ error: "access_denied",
100
+ error_description: "User denied access",
101
+ error_uri: "https://example.com/error",
102
+ },
103
+ }, mockAuthIdentityService);
104
+ expect(result.success).toBe(false);
105
+ expect(result.error).toContain("User denied access");
106
+ });
107
+ });
108
+ describe("validateCallback", () => {
109
+ const mockTokenResponse = {
110
+ access_token: "test-access-token",
111
+ id_token: "test-id-token",
112
+ refresh_token: "test-refresh-token",
113
+ expires_in: 2592000,
114
+ scope: "profile openid",
115
+ token_type: "Bearer",
116
+ };
117
+ const mockProfile = {
118
+ userId: "U1234567890",
119
+ displayName: "Test User",
120
+ pictureUrl: "https://example.com/picture.jpg",
121
+ };
122
+ const mockIdTokenPayload = {
123
+ iss: "https://access.line.me",
124
+ sub: "U1234567890",
125
+ aud: "1234567890",
126
+ exp: Math.floor(Date.now() / 1000) + 3600,
127
+ iat: Math.floor(Date.now() / 1000),
128
+ name: "Test User",
129
+ picture: "https://example.com/picture.jpg",
130
+ };
131
+ it("should handle missing authorization code", async () => {
132
+ const result = await service.validateCallback({ query: {}, body: {} }, mockAuthIdentityService);
133
+ expect(result.success).toBe(false);
134
+ expect(result.error).toBe("No authorization code provided");
135
+ });
136
+ it("should handle missing state", async () => {
137
+ const result = await service.validateCallback({ query: { code: "test-code" }, body: {} }, mockAuthIdentityService);
138
+ expect(result.success).toBe(false);
139
+ expect(result.error).toBe("No state parameter provided");
140
+ });
141
+ it("should handle invalid state", async () => {
142
+ mockAuthIdentityService.getState.mockResolvedValue(null);
143
+ const result = await service.validateCallback({ query: { code: "test-code", state: "invalid-state" }, body: {} }, mockAuthIdentityService);
144
+ expect(result.success).toBe(false);
145
+ expect(result.error).toBe("Invalid state or session expired");
146
+ });
147
+ it("should handle OAuth errors", async () => {
148
+ const result = await service.validateCallback({
149
+ query: {
150
+ error: "invalid_request",
151
+ error_description: "Invalid request",
152
+ },
153
+ body: {},
154
+ }, mockAuthIdentityService);
155
+ expect(result.success).toBe(false);
156
+ expect(result.error).toContain("Invalid request");
157
+ });
158
+ it("should successfully validate callback with complete flow", async () => {
159
+ // Setup mocks
160
+ mockAuthIdentityService.getState.mockResolvedValue({
161
+ callback_url: "https://example.com/callback",
162
+ });
163
+ // Mock token exchange
164
+ mockFetch.mockResolvedValueOnce({
165
+ ok: true,
166
+ json: () => Promise.resolve(mockTokenResponse),
167
+ });
168
+ // Mock ID token verification
169
+ mockJwt.decode.mockReturnValue({
170
+ payload: mockIdTokenPayload,
171
+ });
172
+ // Mock profile fetch
173
+ mockFetch.mockResolvedValueOnce({
174
+ ok: true,
175
+ json: () => Promise.resolve(mockProfile),
176
+ });
177
+ // Mock auth identity creation (new user)
178
+ mockAuthIdentityService.retrieve.mockRejectedValue(new utils_1.MedusaError(utils_1.MedusaError.Types.NOT_FOUND, "Not found"));
179
+ const mockAuthIdentity = {
180
+ entity_id: "U1234567890",
181
+ user_metadata: {
182
+ line_user_id: "U1234567890",
183
+ display_name: "Test User",
184
+ picture_url: "https://example.com/picture.jpg",
185
+ name: "Test User",
186
+ picture: "https://example.com/picture.jpg",
187
+ },
188
+ };
189
+ mockAuthIdentityService.create.mockResolvedValue(mockAuthIdentity);
190
+ // Mock customer creation
191
+ const mockCustomer = {
192
+ id: "cust_123",
193
+ email: "line-U1234567890@line.local",
194
+ first_name: "Test",
195
+ last_name: "User",
196
+ };
197
+ mockCustomerService.create.mockResolvedValue(mockCustomer);
198
+ const result = await service.validateCallback({ query: { code: "test-code", state: "valid-state" }, body: {} }, mockAuthIdentityService);
199
+ expect(result.success).toBe(true);
200
+ expect(result.authIdentity).toBe(mockAuthIdentity);
201
+ });
202
+ it("should handle existing user with profile sync", async () => {
203
+ mockAuthIdentityService.getState.mockResolvedValue({
204
+ callback_url: "https://example.com/callback",
205
+ });
206
+ mockFetch.mockResolvedValueOnce({
207
+ ok: true,
208
+ json: () => Promise.resolve(mockTokenResponse),
209
+ });
210
+ mockJwt.decode.mockReturnValue({
211
+ payload: mockIdTokenPayload,
212
+ });
213
+ mockFetch.mockResolvedValueOnce({
214
+ ok: true,
215
+ json: () => Promise.resolve(mockProfile),
216
+ });
217
+ // Mock existing auth identity
218
+ const existingAuthIdentity = {
219
+ entity_id: "U1234567890",
220
+ user_metadata: {
221
+ line_user_id: "U1234567890",
222
+ display_name: "Old Name",
223
+ },
224
+ };
225
+ mockAuthIdentityService.retrieve.mockResolvedValue(existingAuthIdentity);
226
+ const updatedAuthIdentity = {
227
+ ...existingAuthIdentity,
228
+ user_metadata: {
229
+ line_user_id: "U1234567890",
230
+ display_name: "Test User",
231
+ picture_url: "https://example.com/picture.jpg",
232
+ },
233
+ };
234
+ mockAuthIdentityService.update.mockResolvedValue(updatedAuthIdentity);
235
+ // Mock existing customer
236
+ mockCustomerService.listAndCount.mockResolvedValue([
237
+ [{
238
+ id: "cust_existing",
239
+ metadata: { line_user_id: "U1234567890" }
240
+ }],
241
+ 1
242
+ ]);
243
+ const result = await service.validateCallback({ query: { code: "test-code", state: "valid-state" }, body: {} }, mockAuthIdentityService);
244
+ expect(result.success).toBe(true);
245
+ expect(mockAuthIdentityService.update).toHaveBeenCalled();
246
+ });
247
+ });
248
+ describe("verifyIdToken", () => {
249
+ beforeEach(() => {
250
+ jest.spyOn(Date, "now").mockReturnValue(1000000);
251
+ });
252
+ afterEach(() => {
253
+ Date.now.mockRestore();
254
+ });
255
+ it("should verify valid ID token", async () => {
256
+ const payload = {
257
+ iss: "https://access.line.me",
258
+ sub: "U123456",
259
+ aud: "1234567890",
260
+ exp: 1001, // Future timestamp
261
+ iat: 900,
262
+ };
263
+ mockJwt.decode.mockReturnValue({
264
+ payload,
265
+ });
266
+ const result = await service.verifyIdToken("valid-token");
267
+ expect(result).toBe(payload);
268
+ });
269
+ it("should reject token with wrong audience", async () => {
270
+ const payload = {
271
+ iss: "https://access.line.me",
272
+ sub: "U123456",
273
+ aud: "wrong-channel-id",
274
+ exp: 1001,
275
+ iat: 900,
276
+ };
277
+ mockJwt.decode.mockReturnValue({
278
+ payload,
279
+ });
280
+ await expect(service.verifyIdToken("invalid-token")).rejects.toThrow(new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "ID token audience mismatch"));
281
+ });
282
+ it("should reject token with wrong issuer", async () => {
283
+ const payload = {
284
+ iss: "https://wrong-issuer.com",
285
+ sub: "U123456",
286
+ aud: "1234567890",
287
+ exp: 1001,
288
+ iat: 900,
289
+ };
290
+ mockJwt.decode.mockReturnValue({
291
+ payload,
292
+ });
293
+ await expect(service.verifyIdToken("invalid-token")).rejects.toThrow(new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "ID token issuer mismatch"));
294
+ });
295
+ it("should reject expired token", async () => {
296
+ const payload = {
297
+ iss: "https://access.line.me",
298
+ sub: "U123456",
299
+ aud: "1234567890",
300
+ exp: 999, // Past timestamp
301
+ iat: 900,
302
+ };
303
+ mockJwt.decode.mockReturnValue({
304
+ payload,
305
+ });
306
+ await expect(service.verifyIdToken("expired-token")).rejects.toThrow(new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "ID token has expired"));
307
+ });
308
+ it("should handle malformed ID token", async () => {
309
+ mockJwt.decode.mockReturnValue(null);
310
+ await expect(service.verifyIdToken("malformed-token")).rejects.toThrow(new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "Failed to decode ID token"));
311
+ });
312
+ });
313
+ describe("Token Management", () => {
314
+ beforeEach(() => {
315
+ global.fetch = jest.fn();
316
+ });
317
+ describe("refreshToken", () => {
318
+ it("should refresh LINE access token", async () => {
319
+ const mockResponse = {
320
+ access_token: "new-access-token",
321
+ expires_in: 2592000,
322
+ refresh_token: "new-refresh-token",
323
+ };
324
+ global.fetch.mockResolvedValue({
325
+ ok: true,
326
+ json: async () => mockResponse,
327
+ });
328
+ const result = await service.refreshToken("old-refresh-token");
329
+ expect(result).toEqual(mockResponse);
330
+ expect(global.fetch).toHaveBeenCalledWith("https://api.line.me/oauth2/v2.1/token", expect.objectContaining({
331
+ method: "POST",
332
+ headers: {
333
+ "Content-Type": "application/x-www-form-urlencoded",
334
+ },
335
+ }));
336
+ });
337
+ it("should handle refresh token errors", async () => {
338
+ global.fetch.mockResolvedValue({
339
+ ok: false,
340
+ status: 401,
341
+ });
342
+ const result = await service.refreshToken("invalid-token");
343
+ expect(result).toBeNull();
344
+ expect(mockLogger.error).toHaveBeenCalled();
345
+ });
346
+ });
347
+ describe("revokeToken", () => {
348
+ it("should revoke LINE access token", async () => {
349
+ global.fetch.mockResolvedValue({
350
+ ok: true,
351
+ });
352
+ const result = await service.revokeToken("access-token");
353
+ expect(result).toBe(true);
354
+ expect(global.fetch).toHaveBeenCalledWith("https://api.line.me/oauth2/v2.1/revoke", expect.objectContaining({
355
+ method: "POST",
356
+ }));
357
+ });
358
+ it("should handle revoke token errors", async () => {
359
+ global.fetch.mockResolvedValue({
360
+ ok: false,
361
+ });
362
+ const result = await service.revokeToken("invalid-token");
363
+ expect(result).toBe(false);
364
+ });
365
+ });
366
+ });
367
+ describe("Customer Management", () => {
368
+ it("should find existing customer by LINE ID", async () => {
369
+ const mockCustomer = {
370
+ id: "cus_123",
371
+ email: "test@example.com",
372
+ metadata: { line_user_id: "U1234567890" },
373
+ };
374
+ mockCustomerService.listAndCount.mockResolvedValue([
375
+ [mockCustomer],
376
+ 1,
377
+ ]);
378
+ const result = await service.findCustomerByLineId("U1234567890");
379
+ expect(result).toEqual(mockCustomer);
380
+ expect(mockCustomerService.listAndCount).toHaveBeenCalledWith({
381
+ metadata: {
382
+ line_user_id: "U1234567890",
383
+ },
384
+ }, {});
385
+ });
386
+ it("should return null when customer not found", async () => {
387
+ mockCustomerService.listAndCount.mockResolvedValue([[], 0]);
388
+ const result = await service.findCustomerByLineId("U1234567890");
389
+ expect(result).toBeNull();
390
+ });
391
+ it("should create new customer from LINE profile", async () => {
392
+ const mockProfile = {
393
+ userId: "U1234567890",
394
+ displayName: "John Doe",
395
+ pictureUrl: "https://example.com/pic.jpg",
396
+ };
397
+ const mockIdToken = {
398
+ email: "john@example.com",
399
+ };
400
+ const mockCustomer = {
401
+ id: "cus_new",
402
+ email: "john@example.com",
403
+ first_name: "John",
404
+ last_name: "Doe",
405
+ };
406
+ mockCustomerService.create.mockResolvedValue(mockCustomer);
407
+ const result = await service.createCustomerFromLineProfile(mockProfile, mockIdToken);
408
+ expect(result).toEqual(mockCustomer);
409
+ expect(mockCustomerService.create).toHaveBeenCalledWith({
410
+ email: "john@example.com",
411
+ first_name: "John",
412
+ last_name: "Doe",
413
+ metadata: expect.objectContaining({
414
+ line_user_id: "U1234567890",
415
+ line_display_name: "John Doe",
416
+ line_picture_url: "https://example.com/pic.jpg",
417
+ created_via: "line_oauth",
418
+ }),
419
+ });
420
+ });
421
+ it("should use placeholder email when not provided", async () => {
422
+ const mockProfile = {
423
+ userId: "U1234567890",
424
+ displayName: "Test User",
425
+ };
426
+ const mockIdToken = {};
427
+ mockCustomerService.create.mockResolvedValue({
428
+ id: "cus_new",
429
+ email: "line-U1234567890@line.local",
430
+ });
431
+ await service.createCustomerFromLineProfile(mockProfile, mockIdToken);
432
+ expect(mockCustomerService.create).toHaveBeenCalledWith(expect.objectContaining({
433
+ email: "line-U1234567890@line.local",
434
+ }));
435
+ });
436
+ });
437
+ });
438
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VydmljZS50ZXN0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vc3JjL3Byb3ZpZGVycy9saW5lL19fdGVzdHNfXy9zZXJ2aWNlLnRlc3QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFBQSx5REFBNkM7QUFDN0MscURBQXdEO0FBQ3hELGdFQUErQjtBQUMvQixvREFBNEI7QUFFNUIsb0JBQW9CO0FBQ3BCLElBQUksQ0FBQyxJQUFJLENBQUMsY0FBYyxDQUFDLENBQUM7QUFDMUIsSUFBSSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQztBQUVwQixzQkFBc0I7QUFDdEIsTUFBTSxDQUFDLEtBQUssR0FBRyxJQUFJLENBQUMsRUFBRSxFQUFFLENBQUM7QUFFekIsUUFBUSxDQUFDLHFCQUFxQixFQUFFLEdBQUcsRUFBRTtJQUNuQyxJQUFJLE9BQTRCLENBQUM7SUFDakMsSUFBSSxVQUFlLENBQUM7SUFDcEIsSUFBSSxtQkFBd0IsQ0FBQztJQUM3QixJQUFJLHVCQUE0QixDQUFDO0lBQ2pDLElBQUksU0FBNEMsQ0FBQztJQUNqRCxJQUFJLFVBQXNDLENBQUM7SUFDM0MsSUFBSSxPQUFnQyxDQUFDO0lBRXJDLE1BQU0sY0FBYyxHQUFHO1FBQ3JCLGFBQWEsRUFBRSxZQUFZO1FBQzNCLGlCQUFpQixFQUFFLGFBQWE7UUFDaEMsZUFBZSxFQUFFLDhCQUE4QjtRQUMvQyxrQkFBa0IsRUFBRSxJQUFJO1FBQ3hCLGVBQWUsRUFBRSxJQUFJO0tBQ3RCLENBQUM7SUFFRixVQUFVLENBQUMsR0FBRyxFQUFFO1FBQ2QsSUFBSSxDQUFDLGFBQWEsRUFBRSxDQUFDO1FBQ3JCLFNBQVMsR0FBRyxNQUFNLENBQUMsS0FBMEMsQ0FBQztRQUM5RCxVQUFVLEdBQUcsZ0JBQW9DLENBQUM7UUFDbEQsT0FBTyxHQUFHLHNCQUE4QixDQUFDO1FBRXpDLFVBQVUsR0FBRztZQUNYLElBQUksRUFBRSxJQUFJLENBQUMsRUFBRSxF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