@lodashventure/medusa-login-provider 4.1.0 → 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.
@@ -7,10 +7,19 @@ const utils_1 = require("@medusajs/framework/utils");
7
7
  const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
8
8
  const crypto_1 = __importDefault(require("crypto"));
9
9
  class LineProviderService extends utils_1.AbstractAuthModuleProvider {
10
- constructor({ logger }, options) {
10
+ constructor({ logger, customerService }, options) {
11
11
  super();
12
+ this.LINE_TOKEN_ENDPOINT = "https://api.line.me/oauth2/v2.1/token";
13
+ this.LINE_PROFILE_ENDPOINT = "https://api.line.me/v2/profile";
14
+ this.LINE_VERIFY_ENDPOINT = "https://api.line.me/oauth2/v2.1/verify";
15
+ this.LINE_JWKS_ENDPOINT = "https://api.line.me/oauth2/v2.1/certs";
12
16
  this.logger_ = logger;
13
- this.options_ = options;
17
+ this.options_ = {
18
+ autoCreateCustomer: true,
19
+ syncProfileData: true,
20
+ ...options,
21
+ };
22
+ this.customerService_ = customerService;
14
23
  }
15
24
  static validateOptions(options) {
16
25
  if (!options.lineChannelId) {
@@ -19,9 +28,6 @@ class LineProviderService extends utils_1.AbstractAuthModuleProvider {
19
28
  if (!options.lineChannelSecret) {
20
29
  throw new Error("line channel secret is required");
21
30
  }
22
- if (!options.lineChannelId) {
23
- throw new Error("redirect url is required");
24
- }
25
31
  }
26
32
  async register(data, authIdentityService) {
27
33
  throw new utils_1.MedusaError(utils_1.MedusaError.Types.NOT_ALLOWED, "Line does not support registration. Use method `authenticate` instead.");
@@ -36,16 +42,24 @@ class LineProviderService extends utils_1.AbstractAuthModuleProvider {
36
42
  };
37
43
  }
38
44
  const stateKey = crypto_1.default.randomBytes(32).toString("hex");
45
+ // Use Medusa's native callback URL pattern
46
+ const baseUrl = body?.callback_url || this.options_.callbackUrl || this.options_.lineRedirectUrl;
47
+ const callbackUrl = baseUrl?.includes('/auth/customer/line/callback')
48
+ ? baseUrl
49
+ : `${baseUrl}/auth/customer/line/callback`;
39
50
  const state = {
40
- callback_url: body?.callback_url ?? this.options_.lineRedirectUrl,
51
+ callback_url: callbackUrl,
52
+ success_redirect: body?.success_redirect || this.options_.successRedirect || '/',
53
+ failure_redirect: body?.failure_redirect || this.options_.failureRedirect || '/login',
41
54
  };
42
55
  await authIdentityService.setState(stateKey, state);
43
- return this.getRedirect(this.options_.lineChannelId, state.callback_url, stateKey);
56
+ return this.getRedirect(this.options_.lineChannelId, callbackUrl, stateKey);
44
57
  }
45
58
  async validateCallback(req, authIdentityService) {
46
59
  const query = req.query ?? {};
47
60
  const body = req.body ?? {};
48
61
  if (query.error) {
62
+ this.logger_.error(`LINE OAuth error: ${query.error}`);
49
63
  return {
50
64
  success: false,
51
65
  error: `${query.error_description}, read more at: ${query.error_uri}`,
@@ -53,88 +67,224 @@ class LineProviderService extends utils_1.AbstractAuthModuleProvider {
53
67
  }
54
68
  const code = query?.code ?? body?.code;
55
69
  if (!code) {
56
- return { success: false, error: "No code provided" };
70
+ return { success: false, error: "No authorization code provided" };
57
71
  }
58
- const state = await authIdentityService.getState(query?.state);
72
+ const stateKey = query?.state ?? body?.state;
73
+ if (!stateKey) {
74
+ return { success: false, error: "No state parameter provided" };
75
+ }
76
+ const state = await authIdentityService.getState(stateKey);
59
77
  if (!state) {
60
- return { success: false, error: "No state provided, or session expired" };
78
+ return { success: false, error: "Invalid state or session expired" };
61
79
  }
62
80
  try {
63
- const response = await fetch("https://api.line.me/oauth2/v2.1/token", {
64
- method: "POST",
65
- headers: {
66
- "Content-Type": "application/x-www-form-urlencoded",
67
- },
68
- body: new URLSearchParams({
69
- grant_type: "authorization_code",
70
- code: code,
71
- client_id: this.options_.lineChannelId,
72
- client_secret: this.options_.lineChannelSecret,
73
- redirect_uri: state.callback_url,
74
- }),
75
- }).then((r) => {
76
- if (!r.ok) {
77
- throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, `Could not exchange token, ${r.status}, ${r.statusText}`);
78
- }
79
- return r.json();
80
- });
81
- const { authIdentity, success } = await this.verify_(response.id_token, authIdentityService);
81
+ const tokenResponse = await this.exchangeCodeForTokens(code, state.callback_url);
82
+ const idTokenPayload = await this.verifyIdToken(tokenResponse.id_token);
83
+ const profile = await this.fetchUserProfile(tokenResponse.access_token);
84
+ const { authIdentity, customer } = await this.findOrCreateAuthIdentity(idTokenPayload, profile, tokenResponse, authIdentityService);
82
85
  return {
83
- success,
86
+ success: true,
84
87
  authIdentity,
85
88
  };
86
89
  }
87
90
  catch (error) {
91
+ this.logger_.error("LINE callback validation failed:", error);
88
92
  return { success: false, error: error.message };
89
93
  }
90
94
  }
91
- async verify_(idToken, authIdentityService) {
92
- if (!idToken) {
93
- return { success: false, error: "No ID found" };
95
+ async exchangeCodeForTokens(code, redirectUri) {
96
+ const response = await fetch(this.LINE_TOKEN_ENDPOINT, {
97
+ method: "POST",
98
+ headers: {
99
+ "Content-Type": "application/x-www-form-urlencoded",
100
+ },
101
+ body: new URLSearchParams({
102
+ grant_type: "authorization_code",
103
+ code: code,
104
+ client_id: this.options_.lineChannelId,
105
+ client_secret: this.options_.lineChannelSecret,
106
+ redirect_uri: redirectUri,
107
+ }),
108
+ });
109
+ if (!response.ok) {
110
+ const errorData = await response.text();
111
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, `Failed to exchange authorization code: ${response.status} ${errorData}`);
112
+ }
113
+ return response.json();
114
+ }
115
+ async verifyIdToken(idToken) {
116
+ const decoded = jsonwebtoken_1.default.decode(idToken, { complete: true });
117
+ if (!decoded) {
118
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "Failed to decode ID token");
119
+ }
120
+ const payload = decoded.payload;
121
+ if (payload.aud !== this.options_.lineChannelId) {
122
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "ID token audience mismatch");
94
123
  }
95
- const jwtData = jsonwebtoken_1.default.decode(idToken, {
96
- complete: true,
124
+ if (payload.iss !== "https://access.line.me") {
125
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "ID token issuer mismatch");
126
+ }
127
+ const now = Math.floor(Date.now() / 1000);
128
+ if (payload.exp < now) {
129
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "ID token has expired");
130
+ }
131
+ return payload;
132
+ }
133
+ async fetchUserProfile(accessToken) {
134
+ const response = await fetch(this.LINE_PROFILE_ENDPOINT, {
135
+ headers: {
136
+ Authorization: `Bearer ${accessToken}`,
137
+ },
97
138
  });
98
- const payload = jwtData.payload;
99
- const entity_id = payload.sub;
139
+ if (!response.ok) {
140
+ const errorData = await response.text();
141
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, `Failed to fetch LINE profile: ${response.status} ${errorData}`);
142
+ }
143
+ return response.json();
144
+ }
145
+ async findOrCreateAuthIdentity(idTokenPayload, profile, tokenResponse, authIdentityService) {
146
+ const entity_id = profile.userId;
100
147
  const userMetadata = {
101
- name: payload.name,
102
- picture: payload.picture,
148
+ line_user_id: profile.userId,
149
+ display_name: profile.displayName,
150
+ picture_url: profile.pictureUrl,
151
+ status_message: profile.statusMessage,
152
+ email: idTokenPayload.email,
153
+ name: idTokenPayload.name || profile.displayName,
154
+ picture: idTokenPayload.picture || profile.pictureUrl,
103
155
  };
104
156
  let authIdentity;
157
+ let customer;
105
158
  try {
106
- authIdentity = await authIdentityService.retrieve({
107
- entity_id,
108
- });
159
+ authIdentity = await authIdentityService.retrieve({ entity_id });
160
+ if (this.options_.syncProfileData && authIdentity) {
161
+ authIdentity = await authIdentityService.update(entity_id, { user_metadata: userMetadata });
162
+ }
163
+ if (this.customerService_) {
164
+ customer = await this.findCustomerByLineId(profile.userId);
165
+ }
109
166
  }
110
167
  catch (error) {
111
168
  if (error.type === utils_1.MedusaError.Types.NOT_FOUND) {
112
- const createdAuthIdentity = await authIdentityService.create({
169
+ authIdentity = await authIdentityService.create({
113
170
  entity_id,
114
171
  user_metadata: userMetadata,
115
172
  });
116
- authIdentity = createdAuthIdentity;
173
+ if (this.options_.autoCreateCustomer && this.customerService_) {
174
+ customer = await this.createCustomerFromLineProfile(profile, idTokenPayload);
175
+ }
117
176
  }
118
177
  else {
119
- return { success: false, error: error.message };
178
+ throw error;
120
179
  }
121
180
  }
122
- return {
123
- success: true,
124
- authIdentity,
125
- };
181
+ return { authIdentity, customer };
182
+ }
183
+ async findCustomerByLineId(lineUserId) {
184
+ if (!this.customerService_) {
185
+ return null;
186
+ }
187
+ try {
188
+ const [customers] = await this.customerService_.listAndCount({
189
+ metadata: {
190
+ line_user_id: lineUserId,
191
+ },
192
+ }, {});
193
+ return customers.length > 0 ? customers[0] : null;
194
+ }
195
+ catch (error) {
196
+ this.logger_.error("Failed to find customer by LINE ID:", error);
197
+ return null;
198
+ }
199
+ }
200
+ async createCustomerFromLineProfile(profile, idTokenPayload) {
201
+ if (!this.customerService_) {
202
+ return null;
203
+ }
204
+ const email = idTokenPayload.email || `line-${profile.userId}@line.local`;
205
+ const [firstName, ...lastNameParts] = profile.displayName.split(" ");
206
+ const lastName = lastNameParts.join(" ") || "";
207
+ try {
208
+ const customer = await this.customerService_.create({
209
+ email,
210
+ first_name: firstName,
211
+ last_name: lastName,
212
+ metadata: {
213
+ channel: "line",
214
+ line_user_id: profile.userId,
215
+ line_display_name: profile.displayName,
216
+ line_picture_url: profile.pictureUrl,
217
+ created_via: "line_oauth",
218
+ },
219
+ });
220
+ this.logger_.info(`Created new customer from LINE profile: ${customer.id}`);
221
+ return customer;
222
+ }
223
+ catch (error) {
224
+ this.logger_.error("Failed to create customer from LINE profile:", error);
225
+ return null;
226
+ }
227
+ }
228
+ async refreshToken(refreshToken) {
229
+ try {
230
+ const response = await fetch(this.LINE_TOKEN_ENDPOINT, {
231
+ method: "POST",
232
+ headers: {
233
+ "Content-Type": "application/x-www-form-urlencoded",
234
+ },
235
+ body: new URLSearchParams({
236
+ grant_type: "refresh_token",
237
+ refresh_token: refreshToken,
238
+ client_id: this.options_.lineChannelId,
239
+ client_secret: this.options_.lineChannelSecret,
240
+ }),
241
+ });
242
+ if (!response.ok) {
243
+ this.logger_.error(`Failed to refresh LINE token: ${response.status}`);
244
+ return null;
245
+ }
246
+ return response.json();
247
+ }
248
+ catch (error) {
249
+ this.logger_.error("Error refreshing LINE token:", error);
250
+ return null;
251
+ }
252
+ }
253
+ async revokeToken(accessToken) {
254
+ try {
255
+ const response = await fetch("https://api.line.me/oauth2/v2.1/revoke", {
256
+ method: "POST",
257
+ headers: {
258
+ "Content-Type": "application/x-www-form-urlencoded",
259
+ },
260
+ body: new URLSearchParams({
261
+ access_token: accessToken,
262
+ client_id: this.options_.lineChannelId,
263
+ client_secret: this.options_.lineChannelSecret,
264
+ }),
265
+ });
266
+ return response.ok;
267
+ }
268
+ catch (error) {
269
+ this.logger_.error("Error revoking LINE token:", error);
270
+ return false;
271
+ }
126
272
  }
127
273
  getRedirect(clientId, callbackUrl, stateKey) {
274
+ const nonce = crypto_1.default.randomBytes(16).toString("hex");
128
275
  const authUrl = new URL(`https://access.line.me/oauth2/v2.1/authorize`);
129
276
  authUrl.searchParams.set("response_type", "code");
130
- authUrl.searchParams.set("scope", "profile openid");
277
+ authUrl.searchParams.set("scope", "profile openid email");
131
278
  authUrl.searchParams.set("client_id", clientId);
132
279
  authUrl.searchParams.set("redirect_uri", callbackUrl);
133
280
  authUrl.searchParams.set("state", stateKey);
281
+ authUrl.searchParams.set("nonce", nonce);
282
+ authUrl.searchParams.set("prompt", "consent");
283
+ authUrl.searchParams.set("max_age", "0");
134
284
  return { success: true, location: authUrl.toString() };
135
285
  }
136
286
  }
137
287
  LineProviderService.identifier = "line";
138
288
  LineProviderService.DISPLAY_NAME = "LINE";
139
289
  exports.default = LineProviderService;
140
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy9wcm92aWRlcnMvbGluZS9zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7O0FBQUEscURBR21DO0FBT25DLGdFQUErQztBQUMvQyxvREFBNEI7QUFZNUIsTUFBTSxtQkFBb0IsU0FBUSxrQ0FBMEI7SUFNMUQsWUFBWSxFQUFFLE1BQU0sRUFBd0IsRUFBRSxPQUFnQjtRQUM1RCxLQUFLLEVBQUUsQ0FBQztRQUVSLElBQUksQ0FBQyxPQUFPLEdBQUcsTUFBTSxDQUFDO1FBQ3RCLElBQUksQ0FBQyxRQUFRLEdBQUcsT0FBTyxDQUFDO0lBQzFCLENBQUM7SUFFRCxNQUFNLENBQUMsZUFBZSxDQUFDLE9BQXlCO1FBQzlDLElBQUksQ0FBQyxPQUFPLENBQUMsYUFBYSxFQUFFLENBQUM7WUFDM0IsTUFBTSxJQUFJLEtBQUssQ0FBQyw2QkFBNkIsQ0FBQyxDQUFDO1FBQ2pELENBQUM7UUFDRCxJQUFJLENBQUMsT0FBTyxDQUFDLGlCQUFpQixFQUFFLENBQUM7WUFDL0IsTUFBTSxJQUFJLEtBQUssQ0FBQyxpQ0FBaUMsQ0FBQyxDQUFDO1FBQ3JELENBQUM7UUFDRCxJQUFJLENBQUMsT0FBTyxDQUFDLGFBQWEsRUFBRSxDQUFDO1lBQzNCLE1BQU0sSUFBSSxLQUFLLENBQUMsMEJBQTBCLENBQUMsQ0FBQztRQUM5QyxDQUFDO0lBQ0gsQ0FBQztJQUVELEtBQUssQ0FBQyxRQUFRLENBQ1osSUFBeUIsRUFDekIsbUJBQWdEO1FBRWhELE1BQU0sSUFBSSxtQkFBVyxDQUNuQixtQkFBVyxDQUFDLEtBQUssQ0FBQyxXQUFXLEVBQzdCLHdFQUF3RSxDQUN6RSxDQUFDO0lBQ0osQ0FBQztJQUVELEtBQUssQ0FBQyxZQUFZLENBQ2hCLElBQXlCLEVBQ3pCLG1CQUFnRDtRQUVoRCxNQUFNLEtBQUssR0FBMkIsSUFBSSxDQUFDLEtBQUssSUFBSSxFQUFFLENBQUM7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
290
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy9wcm92aWRlcnMvbGluZS9zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7O0FBQUEscURBR21DO0FBT25DLGdFQUErQztBQUMvQyxvREFBNEI7QUErQzVCLE1BQU0sbUJBQW9CLFNBQVEsa0NBQTBCO0lBVzFELFlBQ0UsRUFBRSxNQUFNLEVBQUUsZUFBZSxFQUF3QixFQUNqRCxPQUFnQjtRQUVoQixLQUFLLEVBQUUsQ0FBQztRQVRPLHdCQUFtQixHQUFHLHVDQUF1QyxDQUFDO1FBQzlELDBCQUFxQixHQUFHLGdDQUFnQyxDQUFDO1FBQ3pELHlCQUFvQixHQUFHLHdDQUF3QyxDQUFDO1FBQ2hFLHVCQUFrQixHQUFHLHVDQUF1QyxDQUFDO1FBUTVFLElBQUksQ0FBQyxPQUFPLEdBQUcsTUFBTSxDQUFDO1FBQ3RCLElBQUksQ0FBQyxRQUFRLEdBQUc7WUFDZCxrQkFBa0IsRUFBRSxJQUFJO1lBQ3hCLGVBQWUsRUFBRSxJQUFJO1lBQ3JCLEdBQUcsT0FBTztTQUNYLENBQUM7UUFDRixJQUFJLENBQUMsZ0JBQWdCLEdBQUcsZUFBZSxDQUFDO0lBQzFDLENBQUM7SUFFRCxNQUFNLENBQUMsZUFBZSxDQUFDLE9BQXlCO1FBQzlDLElBQUksQ0FBQyxPQUFPLENBQUMsYUFBYSxFQUFFLENBQUM7WUFDM0IsTUFBTSxJQUFJLEtBQUssQ0FBQyw2QkFBNkIsQ0FBQyxDQUFDO1FBQ2pELENBQUM7UUFDRCxJQUFJLENBQUMsT0FBTyxDQUFDLGlCQUFpQixFQUFFLENBQUM7WUFDL0IsTUFBTSxJQUFJLEtBQUssQ0FBQyxpQ0FBaUMsQ0FBQyxDQUFDO1FBQ3JELENBQUM7SUFDSCxDQUFDO0lBRUQsS0FBSyxDQUFDLFFBQVEsQ0FDWixJQUF5QixFQUN6QixtQkFBZ0Q7UUFFaEQsTUFBTSxJQUFJLG1CQUFXLENBQ25CLG1CQUFXLENBQUMsS0FBSyxDQUFDLFdB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
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHlwZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9zcmMvcHJvdmlkZXJzL2xpbmUvdHlwZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiJ9
@@ -0,0 +1,119 @@
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
+ exports.RateLimiter = void 0;
7
+ exports.generateState = generateState;
8
+ exports.generateNonce = generateNonce;
9
+ exports.generatePlaceholderEmail = generatePlaceholderEmail;
10
+ exports.parseDisplayName = parseDisplayName;
11
+ exports.validateChannelId = validateChannelId;
12
+ exports.validateCallbackUrl = validateCallbackUrl;
13
+ exports.sanitizeInput = sanitizeInput;
14
+ exports.createAuditLog = createAuditLog;
15
+ exports.handleLineApiError = handleLineApiError;
16
+ const crypto_1 = __importDefault(require("crypto"));
17
+ const utils_1 = require("@medusajs/framework/utils");
18
+ /**
19
+ * Generates a secure state parameter for OAuth flow
20
+ */
21
+ function generateState() {
22
+ return crypto_1.default.randomBytes(32).toString("hex");
23
+ }
24
+ /**
25
+ * Generates a nonce for ID token validation
26
+ */
27
+ function generateNonce() {
28
+ return crypto_1.default.randomBytes(16).toString("hex");
29
+ }
30
+ /**
31
+ * Creates a placeholder email for LINE users without email
32
+ */
33
+ function generatePlaceholderEmail(lineUserId, domain = "line.local") {
34
+ return `line-${lineUserId}@${domain}`;
35
+ }
36
+ /**
37
+ * Extracts first and last name from display name
38
+ */
39
+ function parseDisplayName(displayName) {
40
+ const parts = displayName.trim().split(" ");
41
+ const firstName = parts[0] || "";
42
+ const lastName = parts.slice(1).join(" ") || "";
43
+ return { firstName, lastName };
44
+ }
45
+ /**
46
+ * Validates LINE Channel ID format
47
+ */
48
+ function validateChannelId(channelId) {
49
+ return /^\d{10}$/.test(channelId);
50
+ }
51
+ /**
52
+ * Validates callback URL
53
+ */
54
+ function validateCallbackUrl(url) {
55
+ try {
56
+ const parsed = new URL(url);
57
+ return parsed.protocol === "https:" || parsed.hostname === "localhost";
58
+ }
59
+ catch {
60
+ return false;
61
+ }
62
+ }
63
+ /**
64
+ * Rate limit tracker for API calls
65
+ */
66
+ class RateLimiter {
67
+ constructor(maxAttempts = 10, windowMs = 60000) {
68
+ this.attempts = new Map();
69
+ this.maxAttempts = maxAttempts;
70
+ this.windowMs = windowMs;
71
+ }
72
+ isAllowed(key) {
73
+ const now = Date.now();
74
+ const attempts = this.attempts.get(key) || [];
75
+ // Remove expired attempts
76
+ const validAttempts = attempts.filter((timestamp) => now - timestamp < this.windowMs);
77
+ if (validAttempts.length >= this.maxAttempts) {
78
+ return false;
79
+ }
80
+ validAttempts.push(now);
81
+ this.attempts.set(key, validAttempts);
82
+ return true;
83
+ }
84
+ reset(key) {
85
+ this.attempts.delete(key);
86
+ }
87
+ }
88
+ exports.RateLimiter = RateLimiter;
89
+ /**
90
+ * Sanitizes user input to prevent injection attacks
91
+ */
92
+ function sanitizeInput(input) {
93
+ return input
94
+ .replace(/[<>]/g, "")
95
+ .replace(/javascript:/gi, "")
96
+ .trim();
97
+ }
98
+ function createAuditLog(entry) {
99
+ return {
100
+ ...entry,
101
+ timestamp: new Date(),
102
+ };
103
+ }
104
+ /**
105
+ * Handles LINE API errors
106
+ */
107
+ function handleLineApiError(status, message) {
108
+ const errorMessages = {
109
+ 400: "Invalid request to LINE API",
110
+ 401: "LINE authentication failed",
111
+ 403: "Access forbidden by LINE",
112
+ 429: "Too many requests to LINE API",
113
+ 500: "LINE server error",
114
+ 503: "LINE service temporarily unavailable",
115
+ };
116
+ const errorMessage = errorMessages[status] || `LINE API error: ${status}`;
117
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, `${errorMessage}: ${message}`);
118
+ }
119
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXRpbHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9zcmMvcHJvdmlkZXJzL2xpbmUvdXRpbHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7O0FBTUEsc0NBRUM7QUFLRCxzQ0FFQztBQUtELDREQUVDO0FBS0QsNENBUUM7QUFLRCw4Q0FFQztBQUtELGtEQU9DO0FBMENELHNDQUtDO0FBZUQsd0NBS0M7QUFLRCxnREFnQkM7QUE5SUQsb0RBQTRCO0FBQzVCLHFEQUF3RDtBQUV4RDs7R0FFRztBQUNILFNBQWdCLGFBQWE7SUFDM0IsT0FBTyxnQkFBTSxDQUFDLFdBQVcsQ0FBQyxFQUFFLENBQUMsQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLENBQUM7QUFDaEQsQ0FBQztBQUVEOztHQUVHO0FBQ0gsU0FBZ0IsYUFBYTtJQUMzQixPQUFPLGdCQUFNLENBQUMsV0FBVyxDQUFDLEVBQUUsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsQ0FBQztBQUNoRCxDQUFDO0FBRUQ7O0dBRUc7QUFDSCxTQUFnQix3QkFBd0IsQ0FBQyxVQUFrQixFQUFFLE1BQU0sR0FBRyxZQUFZO0lBQ2hGLE9BQU8sUUFBUSxVQUFVLElBQUksTUFBTSxFQUFFLENBQUM7QUFDeEMsQ0FBQztBQUVEOztHQUVHO0FBQ0gsU0FBZ0IsZ0JBQWdCLENBQUMsV0FBbUI7SUFJbEQsTUFBTSxLQUFLLEdBQUcsV0FBVyxDQUFDLElBQUksRUFBRSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQztJQUM1QyxNQUFNLFNBQVMsR0FBRyxLQUFLLENBQUMsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDO0lBQ2pDLE1BQU0sUUFBUSxHQUFHLEtBQUssQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxJQUFJLEVBQUUsQ0FBQztJQUNoRCxPQUFPLEVBQUUsU0FBUyxFQUFFLFFBQVEsRUFBRSxDQUFDO0FBQ2pDLENB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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lodashventure/medusa-login-provider",
3
- "version": "4.1.0",
3
+ "version": "4.1.2",
4
4
  "description": "A starter for Medusa plugins.",
5
5
  "author": "Medusa (https://medusajs.com)",
6
6
  "license": "MIT",
@@ -24,7 +24,16 @@
24
24
  ],
25
25
  "scripts": {
26
26
  "build": "medusa plugin:build",
27
- "dev": "medusa plugin:develop"
27
+ "dev": "medusa plugin:develop",
28
+ "test": "jest",
29
+ "test:watch": "jest --watch",
30
+ "test:coverage": "jest --coverage",
31
+ "test:ci": "jest --ci --coverage --watchAll=false",
32
+ "test:unit": "jest src/**/*.test.ts --testPathIgnorePatterns=e2e",
33
+ "test:integration": "jest src/api/__tests__/**/*.test.ts",
34
+ "test:e2e": "jest src/__tests__/e2e/**/*.test.ts",
35
+ "lint": "eslint src --ext .ts --fix",
36
+ "type-check": "tsc --noEmit"
28
37
  },
29
38
  "devDependencies": {
30
39
  "@medusajs/admin-sdk": "2.10.0",
@@ -40,15 +49,25 @@
40
49
  "@mikro-orm/migrations": "6.4.3",
41
50
  "@mikro-orm/postgresql": "6.4.3",
42
51
  "@swc/core": "1.5.7",
52
+ "@types/jest": "^29.5.0",
43
53
  "@types/jsonwebtoken": "^9",
44
54
  "@types/node": "^20.0.0",
45
55
  "@types/react": "^18.3.2",
46
56
  "@types/react-dom": "^18.2.25",
57
+ "@typescript-eslint/eslint-plugin": "^6.0.0",
58
+ "@typescript-eslint/parser": "^6.0.0",
47
59
  "awilix": "^8.0.1",
60
+ "eslint": "^8.40.0",
61
+ "jest": "^29.5.0",
62
+ "jest-junit": "^16.0.0",
63
+ "jest-watch-typeahead": "^2.2.2",
64
+ "node-fetch": "^3.3.1",
48
65
  "pg": "^8.13.0",
49
66
  "prop-types": "^15.8.1",
50
67
  "react": "^18.2.0",
51
68
  "react-dom": "^18.2.0",
69
+ "reflect-metadata": "^0.1.13",
70
+ "ts-jest": "^29.1.0",
52
71
  "ts-node": "^10.9.2",
53
72
  "typescript": "^5.6.2",
54
73
  "vite": "^5.2.11",