@maka/maka-cli 5.1.46 → 5.1.47

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maka/maka-cli",
3
- "version": "5.1.46",
3
+ "version": "5.1.47",
4
4
  "type": "module",
5
5
  "summary": "A command line tool for scaffolding Meteor 2.x applications using either React.",
6
6
  "description": "A command line tool for scaffolding Meteor 2.x applications using React.",
@@ -1,4 +1,5 @@
1
1
  import path from 'path';
2
+ import fs from 'fs';
2
3
  import { Generator } from '../../../generator.js';
3
4
  import { Log } from '../../../tools/log/log.class.js';
4
5
  import AuthGen from './auth.generator.js';
@@ -148,6 +149,7 @@ Generator.create({
148
149
  handleAuth0Callback,
149
150
  logoutAuth0,
150
151
  getAuth0IdToken,
152
+ refreshAuth0Token,
151
153
  } from './auth0-client';
152
154
  `
153
155
  });
@@ -272,6 +274,26 @@ Generator.create({
272
274
  throw error;
273
275
  }
274
276
  }
277
+
278
+ /**
279
+ * Silently refresh the Auth0 token and update Meteor session
280
+ * This is called automatically when the token is about to expire
281
+ * Returns the updated expiry time
282
+ */
283
+ static async refreshAuth0Token() {
284
+ try {
285
+ // Get fresh token from Auth0 silently (using refresh token)
286
+ const idToken = await refreshAuth0Token();
287
+
288
+ // Call Meteor method to validate and update token
289
+ const result = await Meteor.callAsync('auth0.refreshToken', idToken);
290
+
291
+ return result;
292
+ } catch (error) {
293
+ console.error('Auth0 token refresh failed:', error);
294
+ throw error;
295
+ }
296
+ }
275
297
  `;
276
298
  await scaffold.injectIntoFile({
277
299
  filePath: `${pathToAuthService}.${jsEngine}`,
@@ -310,6 +332,15 @@ Generator.create({
310
332
  end: '\\}\\), \\[',
311
333
  replace: false
312
334
  });
335
+ // Comment out the placeholder token validation in useEffect
336
+ // This prevents "Login failed: Invalid token" errors when using Meteor authentication
337
+ const authProviderFilePath = `${pathToAuthProvider}.${jsEngine}`;
338
+ const authProviderContent = fs.readFileSync(authProviderFilePath, 'utf8');
339
+ // Replace the loginUserWithToken call with a comment explaining Meteor handles this
340
+ const updatedContent = authProviderContent
341
+ .replace(/const renewedUser = await loginUserWithToken\(\);/g, '// Auth0/Meteor handles token validation automatically\n // const renewedUser = await loginUserWithToken();')
342
+ .replace(/if \(renewedUser\) \{[\s\S]*?setUserAuthorizations\(\[\]\);[\s\S]*?\}/, '// Meteor.user() is managed by Meteor\'s accounts system\n // Token validation is handled by Meteor');
343
+ fs.writeFileSync(authProviderFilePath, updatedContent, 'utf8');
313
344
  // ===================================================================================
314
345
  // 7. Import auth0-methods on server startup
315
346
  // ===================================================================================
@@ -599,11 +630,21 @@ When a user logs in with Auth0, their Meteor user document includes:
599
630
 
600
631
  ## Troubleshooting
601
632
 
602
- ### "Invalid token" Error
633
+ ### "Login failed: Invalid token" Error After Successful Login
634
+
635
+ If you see this error message even though you're logged in, it's because the placeholder token validation code is still running. The Auth0 kit automatically comments out this code, but if you see this error:
636
+
637
+ 1. Check your \`authentication-provider.${jsEngine}\` file
638
+ 2. Look for the \`loginUserWithToken\` call in the \`useEffect\` hook
639
+ 3. Ensure it's commented out (the generator does this automatically)
640
+ 4. Meteor's authentication system handles token validation automatically
641
+
642
+ ### "Invalid token" Error During Login
603
643
 
604
644
  - Check that your Auth0 domain and clientId are correct
605
645
  - Verify the token hasn't expired
606
646
  - Ensure your Auth0 application settings are configured correctly
647
+ - Make sure the domain in settings.json doesn't include \`https://\` (the kit handles this automatically)
607
648
 
608
649
  ### "Auth0 configuration missing" Error
609
650
 
@@ -1 +1 @@
1
- {"version":3,"file":"auth0-meteor.auth.gen.js","sourceRoot":"","sources":["../../../../../../src/generators/kits/auth/auth0-meteor.auth.gen.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"auth0-meteor.auth.gen.js","sourceRoot":"","sources":["../../../../../../src/generators/kits/auth/auth0-meteor.auth.gen.ts"],"names":[],"mappings":"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"}
@@ -139,3 +139,25 @@ export const getAuth0IdToken = async () => {
139
139
 
140
140
  return token.__raw;
141
141
  };
142
+
143
+ /**
144
+ * Silently refresh the Auth0 token and update Meteor session
145
+ * This is called automatically when the token is about to expire
146
+ */
147
+ export const refreshAuth0Token = async () => {
148
+ const client = await getAuth0Client();
149
+
150
+ // Get a fresh token silently (using refresh token)
151
+ await client.getTokenSilently({
152
+ cacheMode: 'off', // Force refresh
153
+ });
154
+
155
+ // Get the new ID token
156
+ const token = await client.getIdTokenClaims();
157
+
158
+ if (!token?.__raw) {
159
+ throw new Error('Failed to refresh Auth0 token');
160
+ }
161
+
162
+ return token.__raw;
163
+ };
@@ -139,3 +139,25 @@ export const getAuth0IdToken = async (): Promise<string> => {
139
139
 
140
140
  return token.__raw;
141
141
  };
142
+
143
+ /**
144
+ * Silently refresh the Auth0 token and update Meteor session
145
+ * This is called automatically when the token is about to expire
146
+ */
147
+ export const refreshAuth0Token = async (): Promise<string> => {
148
+ const client = await getAuth0Client();
149
+
150
+ // Get a fresh token silently (using refresh token)
151
+ await client.getTokenSilently({
152
+ cacheMode: 'off', // Force refresh
153
+ });
154
+
155
+ // Get the new ID token
156
+ const token = await client.getIdTokenClaims();
157
+
158
+ if (!token?.__raw) {
159
+ throw new Error('Failed to refresh Auth0 token');
160
+ }
161
+
162
+ return token.__raw;
163
+ };
@@ -5,6 +5,15 @@
5
5
  * Set your Auth0 credentials in Meteor settings or environment variables.
6
6
  */
7
7
 
8
+ /**
9
+ * Normalize Auth0 domain by removing protocol and trailing slashes
10
+ */
11
+ const normalizeDomain = (domain) => {
12
+ return domain
13
+ .replace(/^https?:\/\//, '') // Remove http:// or https://
14
+ .replace(/\/$/, ''); // Remove trailing slash
15
+ };
16
+
8
17
  /**
9
18
  * Get Auth0 configuration from Meteor settings or environment variables
10
19
  */
@@ -18,10 +27,12 @@ export const getAuth0Config = () => {
18
27
  );
19
28
  }
20
29
 
30
+ const normalizedDomain = normalizeDomain(settings.domain);
31
+
21
32
  return {
22
- domain: settings.domain,
33
+ domain: normalizedDomain,
23
34
  clientId: settings.clientId,
24
- audience: settings.audience || `https://${settings.domain}/api/v2/`,
35
+ audience: settings.audience || `https://${normalizedDomain}/api/v2/`,
25
36
  redirectUri: settings.redirectUri || window.location.origin + '/auth/callback',
26
37
  scope: settings.scope || 'openid profile email',
27
38
  };
@@ -36,10 +47,12 @@ export const getAuth0Config = () => {
36
47
  );
37
48
  }
38
49
 
50
+ const normalizedDomain = normalizeDomain(settings.domain);
51
+
39
52
  return {
40
- domain: settings.domain,
53
+ domain: normalizedDomain,
41
54
  clientId: settings.clientId,
42
- audience: settings.audience || `https://${settings.domain}/api/v2/`,
55
+ audience: settings.audience || `https://${normalizedDomain}/api/v2/`,
43
56
  redirectUri: settings.redirectUri || Meteor.absoluteUrl('auth/callback'),
44
57
  scope: settings.scope || 'openid profile email',
45
58
  };
@@ -13,6 +13,15 @@ export interface Auth0Config {
13
13
  scope: string;
14
14
  }
15
15
 
16
+ /**
17
+ * Normalize Auth0 domain by removing protocol and trailing slashes
18
+ */
19
+ const normalizeDomain = (domain: string): string => {
20
+ return domain
21
+ .replace(/^https?:\/\//, '') // Remove http:// or https://
22
+ .replace(/\/$/, ''); // Remove trailing slash
23
+ };
24
+
16
25
  /**
17
26
  * Get Auth0 configuration from Meteor settings or environment variables
18
27
  */
@@ -26,10 +35,12 @@ export const getAuth0Config = (): Auth0Config => {
26
35
  );
27
36
  }
28
37
 
38
+ const normalizedDomain = normalizeDomain(settings.domain);
39
+
29
40
  return {
30
- domain: settings.domain,
41
+ domain: normalizedDomain,
31
42
  clientId: settings.clientId,
32
- audience: settings.audience || `https://${settings.domain}/api/v2/`,
43
+ audience: settings.audience || `https://${normalizedDomain}/api/v2/`,
33
44
  redirectUri: settings.redirectUri || window.location.origin + '/auth/callback',
34
45
  scope: settings.scope || 'openid profile email',
35
46
  };
@@ -44,10 +55,12 @@ export const getAuth0Config = (): Auth0Config => {
44
55
  );
45
56
  }
46
57
 
58
+ const normalizedDomain = normalizeDomain(settings.domain);
59
+
47
60
  return {
48
- domain: settings.domain,
61
+ domain: normalizedDomain,
49
62
  clientId: settings.clientId,
50
- audience: settings.audience || `https://${settings.domain}/api/v2/`,
63
+ audience: settings.audience || `https://${normalizedDomain}/api/v2/`,
51
64
  redirectUri: settings.redirectUri || Meteor.absoluteUrl('auth/callback'),
52
65
  scope: settings.scope || 'openid profile email',
53
66
  };
@@ -2,6 +2,17 @@ import { Meteor } from 'meteor/meteor';
2
2
  import { Accounts } from 'meteor/accounts-base';
3
3
  import { verifyAuth0Token, findOrCreateUserFromAuth0 } from '../auth0-validator';
4
4
 
5
+ // Hook into Meteor logout to clean up Auth0 session
6
+ Accounts.onLogout((options) => {
7
+ const user = options.user;
8
+
9
+ // If user has Auth0 account linked, we log the logout
10
+ // Note: Client-side should handle Auth0 logout via auth0Client.logout()
11
+ if (user?.services?.auth0) {
12
+ console.log(`User ${user._id} logged out (Auth0 ID: ${user.services.auth0.id})`);
13
+ }
14
+ });
15
+
5
16
  /**
6
17
  * Meteor Methods for Auth0 Authentication
7
18
  *
@@ -9,6 +20,17 @@ import { verifyAuth0Token, findOrCreateUserFromAuth0 } from '../auth0-validator'
9
20
  * including token verification and user account creation/linking.
10
21
  */
11
22
 
23
+ /**
24
+ * Check if the user's Auth0 token has expired
25
+ */
26
+ const isTokenExpired = (user) => {
27
+ const auth0Data = user?.services?.auth0;
28
+ if (!auth0Data?.expiresAt) {
29
+ return true;
30
+ }
31
+ return new Date() >= new Date(auth0Data.expiresAt);
32
+ };
33
+
12
34
  Meteor.methods({
13
35
  /**
14
36
  * Authenticate user with Auth0 ID token
@@ -78,7 +100,7 @@ Meteor.methods({
78
100
  const auth0Id = tokenPayload.sub;
79
101
 
80
102
  // Check if Auth0 account is already linked to another user
81
- const existingUser = Meteor.users.findOne({
103
+ const existingUser = await Meteor.users.findOneAsync({
82
104
  'services.auth0.id': auth0Id,
83
105
  _id: { $ne: this.userId },
84
106
  });
@@ -91,15 +113,22 @@ Meteor.methods({
91
113
  }
92
114
 
93
115
  // Link Auth0 account to current user
94
- Meteor.users.update(this.userId, {
116
+ await Meteor.users.updateAsync(this.userId, {
95
117
  $set: {
96
- 'services.auth0': {
97
- id: auth0Id,
98
- email: tokenPayload.email,
118
+ // Profile data (client-accessible)
119
+ 'profile.auth0': {
99
120
  name: tokenPayload.name,
100
121
  picture: tokenPayload.picture,
122
+ email: tokenPayload.email,
101
123
  email_verified: tokenPayload.email_verified,
102
124
  },
125
+ // Token data (server-only)
126
+ 'services.auth0': {
127
+ id: auth0Id,
128
+ accessToken: tokenPayload.aud,
129
+ expiresAt: new Date(tokenPayload.exp * 1000),
130
+ issuedAt: new Date(tokenPayload.iat * 1000),
131
+ },
103
132
  },
104
133
  });
105
134
 
@@ -122,12 +151,12 @@ Meteor.methods({
122
151
  /**
123
152
  * Unlink Auth0 account from current user
124
153
  */
125
- 'auth0.unlinkAccount'() {
154
+ async 'auth0.unlinkAccount'() {
126
155
  if (!this.userId) {
127
156
  throw new Meteor.Error('not-authorized', 'Must be logged in to unlink Auth0 account');
128
157
  }
129
158
 
130
- const user = Meteor.users.findOne(this.userId);
159
+ const user = await Meteor.users.findOneAsync(this.userId);
131
160
 
132
161
  if (!user?.services?.auth0) {
133
162
  throw new Meteor.Error('no-auth0-account', 'No Auth0 account linked');
@@ -141,12 +170,102 @@ Meteor.methods({
141
170
  );
142
171
  }
143
172
 
144
- Meteor.users.update(this.userId, {
173
+ await Meteor.users.updateAsync(this.userId, {
145
174
  $unset: {
146
175
  'services.auth0': '',
176
+ 'profile.auth0': '',
147
177
  },
148
178
  });
149
179
 
150
180
  return { success: true };
151
181
  },
182
+
183
+ /**
184
+ * Check if the current user's Auth0 token is valid
185
+ * Returns token expiry status
186
+ */
187
+ async 'auth0.checkTokenValidity'() {
188
+ if (!this.userId) {
189
+ throw new Meteor.Error('not-authorized', 'Must be logged in');
190
+ }
191
+
192
+ const user = await Meteor.users.findOneAsync(this.userId);
193
+
194
+ if (!user?.services?.auth0) {
195
+ throw new Meteor.Error('no-auth0-account', 'No Auth0 account linked');
196
+ }
197
+
198
+ const expired = isTokenExpired(user);
199
+
200
+ return {
201
+ isValid: !expired,
202
+ expiresAt: user.services.auth0.expiresAt,
203
+ issuedAt: user.services.auth0.issuedAt,
204
+ };
205
+ },
206
+
207
+ /**
208
+ * Refresh Auth0 token and update Meteor user session
209
+ * This method validates the new token and updates the user's token data
210
+ */
211
+ async 'auth0.refreshToken'(idToken) {
212
+ if (!this.userId) {
213
+ throw new Meteor.Error('not-authorized', 'Must be logged in');
214
+ }
215
+
216
+ if (!idToken || typeof idToken !== 'string') {
217
+ throw new Meteor.Error('invalid-token', 'Invalid token provided');
218
+ }
219
+
220
+ try {
221
+ // Verify the new Auth0 token
222
+ const tokenPayload = await verifyAuth0Token(idToken);
223
+
224
+ // Get current user
225
+ const user = await Meteor.users.findOneAsync(this.userId);
226
+
227
+ if (!user?.services?.auth0) {
228
+ throw new Meteor.Error('no-auth0-account', 'No Auth0 account linked');
229
+ }
230
+
231
+ // Verify the token is for the same Auth0 user
232
+ if (user.services.auth0.id !== tokenPayload.sub) {
233
+ throw new Meteor.Error(
234
+ 'token-mismatch',
235
+ 'Token does not match current user Auth0 account'
236
+ );
237
+ }
238
+
239
+ // Update token data in services.auth0 (server-only)
240
+ await Meteor.users.updateAsync(this.userId, {
241
+ $set: {
242
+ 'services.auth0.accessToken': tokenPayload.aud,
243
+ 'services.auth0.expiresAt': new Date(tokenPayload.exp * 1000),
244
+ 'services.auth0.issuedAt': new Date(tokenPayload.iat * 1000),
245
+ // Also update profile data in case it changed
246
+ 'profile.auth0.name': tokenPayload.name,
247
+ 'profile.auth0.picture': tokenPayload.picture,
248
+ 'profile.auth0.email': tokenPayload.email,
249
+ 'profile.auth0.email_verified': tokenPayload.email_verified,
250
+ },
251
+ });
252
+
253
+ return {
254
+ success: true,
255
+ expiresAt: new Date(tokenPayload.exp * 1000),
256
+ };
257
+ } catch (error) {
258
+ console.error('Auth0 token refresh error:', error);
259
+
260
+ if (error instanceof Meteor.Error) {
261
+ throw error;
262
+ }
263
+
264
+ throw new Meteor.Error(
265
+ 'auth0-refresh-failed',
266
+ 'Failed to refresh Auth0 token',
267
+ error.message
268
+ );
269
+ }
270
+ },
152
271
  });
@@ -2,6 +2,17 @@ import { Meteor } from 'meteor/meteor';
2
2
  import { Accounts } from 'meteor/accounts-base';
3
3
  import { verifyAuth0Token, findOrCreateUserFromAuth0 } from '../auth0-validator';
4
4
 
5
+ // Hook into Meteor logout to clean up Auth0 session
6
+ Accounts.onLogout((options: any) => {
7
+ const user = options.user;
8
+
9
+ // If user has Auth0 account linked, we log the logout
10
+ // Note: Client-side should handle Auth0 logout via auth0Client.logout()
11
+ if (user?.services?.auth0) {
12
+ console.log(`User ${user._id} logged out (Auth0 ID: ${user.services.auth0.id})`);
13
+ }
14
+ });
15
+
5
16
  /**
6
17
  * Meteor Methods for Auth0 Authentication
7
18
  *
@@ -9,6 +20,17 @@ import { verifyAuth0Token, findOrCreateUserFromAuth0 } from '../auth0-validator'
9
20
  * including token verification and user account creation/linking.
10
21
  */
11
22
 
23
+ /**
24
+ * Check if the user's Auth0 token has expired
25
+ */
26
+ const isTokenExpired = (user: any): boolean => {
27
+ const auth0Data = user?.services?.auth0;
28
+ if (!auth0Data?.expiresAt) {
29
+ return true;
30
+ }
31
+ return new Date() >= new Date(auth0Data.expiresAt);
32
+ };
33
+
12
34
  Meteor.methods({
13
35
  /**
14
36
  * Authenticate user with Auth0 ID token
@@ -78,7 +100,7 @@ Meteor.methods({
78
100
  const auth0Id = tokenPayload.sub;
79
101
 
80
102
  // Check if Auth0 account is already linked to another user
81
- const existingUser = Meteor.users.findOne({
103
+ const existingUser = await Meteor.users.findOneAsync({
82
104
  'services.auth0.id': auth0Id,
83
105
  _id: { $ne: this.userId },
84
106
  });
@@ -91,15 +113,22 @@ Meteor.methods({
91
113
  }
92
114
 
93
115
  // Link Auth0 account to current user
94
- Meteor.users.update(this.userId, {
116
+ await Meteor.users.updateAsync(this.userId, {
95
117
  $set: {
96
- 'services.auth0': {
97
- id: auth0Id,
98
- email: tokenPayload.email,
118
+ // Profile data (client-accessible)
119
+ 'profile.auth0': {
99
120
  name: tokenPayload.name,
100
121
  picture: tokenPayload.picture,
122
+ email: tokenPayload.email,
101
123
  email_verified: tokenPayload.email_verified,
102
124
  },
125
+ // Token data (server-only)
126
+ 'services.auth0': {
127
+ id: auth0Id,
128
+ accessToken: tokenPayload.aud,
129
+ expiresAt: new Date(tokenPayload.exp * 1000),
130
+ issuedAt: new Date(tokenPayload.iat * 1000),
131
+ },
103
132
  },
104
133
  });
105
134
 
@@ -122,12 +151,12 @@ Meteor.methods({
122
151
  /**
123
152
  * Unlink Auth0 account from current user
124
153
  */
125
- 'auth0.unlinkAccount'() {
154
+ async 'auth0.unlinkAccount'() {
126
155
  if (!this.userId) {
127
156
  throw new Meteor.Error('not-authorized', 'Must be logged in to unlink Auth0 account');
128
157
  }
129
158
 
130
- const user = Meteor.users.findOne(this.userId);
159
+ const user = await Meteor.users.findOneAsync(this.userId);
131
160
 
132
161
  if (!user?.services?.auth0) {
133
162
  throw new Meteor.Error('no-auth0-account', 'No Auth0 account linked');
@@ -141,12 +170,102 @@ Meteor.methods({
141
170
  );
142
171
  }
143
172
 
144
- Meteor.users.update(this.userId, {
173
+ await Meteor.users.updateAsync(this.userId, {
145
174
  $unset: {
146
175
  'services.auth0': '',
176
+ 'profile.auth0': '',
147
177
  },
148
178
  });
149
179
 
150
180
  return { success: true };
151
181
  },
182
+
183
+ /**
184
+ * Check if the current user's Auth0 token is valid
185
+ * Returns token expiry status
186
+ */
187
+ async 'auth0.checkTokenValidity'() {
188
+ if (!this.userId) {
189
+ throw new Meteor.Error('not-authorized', 'Must be logged in');
190
+ }
191
+
192
+ const user = await Meteor.users.findOneAsync(this.userId);
193
+
194
+ if (!user?.services?.auth0) {
195
+ throw new Meteor.Error('no-auth0-account', 'No Auth0 account linked');
196
+ }
197
+
198
+ const expired = isTokenExpired(user);
199
+
200
+ return {
201
+ isValid: !expired,
202
+ expiresAt: user.services.auth0.expiresAt,
203
+ issuedAt: user.services.auth0.issuedAt,
204
+ };
205
+ },
206
+
207
+ /**
208
+ * Refresh Auth0 token and update Meteor user session
209
+ * This method validates the new token and updates the user's token data
210
+ */
211
+ async 'auth0.refreshToken'(idToken: string) {
212
+ if (!this.userId) {
213
+ throw new Meteor.Error('not-authorized', 'Must be logged in');
214
+ }
215
+
216
+ if (!idToken || typeof idToken !== 'string') {
217
+ throw new Meteor.Error('invalid-token', 'Invalid token provided');
218
+ }
219
+
220
+ try {
221
+ // Verify the new Auth0 token
222
+ const tokenPayload = await verifyAuth0Token(idToken);
223
+
224
+ // Get current user
225
+ const user = await Meteor.users.findOneAsync(this.userId);
226
+
227
+ if (!user?.services?.auth0) {
228
+ throw new Meteor.Error('no-auth0-account', 'No Auth0 account linked');
229
+ }
230
+
231
+ // Verify the token is for the same Auth0 user
232
+ if (user.services.auth0.id !== tokenPayload.sub) {
233
+ throw new Meteor.Error(
234
+ 'token-mismatch',
235
+ 'Token does not match current user Auth0 account'
236
+ );
237
+ }
238
+
239
+ // Update token data in services.auth0 (server-only)
240
+ await Meteor.users.updateAsync(this.userId, {
241
+ $set: {
242
+ 'services.auth0.accessToken': tokenPayload.aud,
243
+ 'services.auth0.expiresAt': new Date(tokenPayload.exp * 1000),
244
+ 'services.auth0.issuedAt': new Date(tokenPayload.iat * 1000),
245
+ // Also update profile data in case it changed
246
+ 'profile.auth0.name': tokenPayload.name,
247
+ 'profile.auth0.picture': tokenPayload.picture,
248
+ 'profile.auth0.email': tokenPayload.email,
249
+ 'profile.auth0.email_verified': tokenPayload.email_verified,
250
+ },
251
+ });
252
+
253
+ return {
254
+ success: true,
255
+ expiresAt: new Date(tokenPayload.exp * 1000),
256
+ };
257
+ } catch (error) {
258
+ console.error('Auth0 token refresh error:', error);
259
+
260
+ if (error instanceof Meteor.Error) {
261
+ throw error;
262
+ }
263
+
264
+ throw new Meteor.Error(
265
+ 'auth0-refresh-failed',
266
+ 'Failed to refresh Auth0 token',
267
+ error.message
268
+ );
269
+ }
270
+ },
152
271
  });
@@ -10,6 +10,15 @@ import jwksClient from 'jwks-rsa';
10
10
  * and creates/updates Meteor user accounts based on Auth0 user data.
11
11
  */
12
12
 
13
+ /**
14
+ * Normalize Auth0 domain by removing protocol and trailing slashes
15
+ */
16
+ const normalizeDomain = (domain) => {
17
+ return domain
18
+ .replace(/^https?:\/\//, '') // Remove http:// or https://
19
+ .replace(/\/$/, ''); // Remove trailing slash
20
+ };
21
+
13
22
  /**
14
23
  * Create JWKS client for verifying Auth0 tokens
15
24
  */
@@ -20,8 +29,10 @@ const getJwksClient = () => {
20
29
  throw new Meteor.Error('auth0-config-missing', 'Auth0 domain not configured');
21
30
  }
22
31
 
32
+ const normalizedDomain = normalizeDomain(auth0Domain);
33
+
23
34
  return jwksClient({
24
- jwksUri: `https://${auth0Domain}/.well-known/jwks.json`,
35
+ jwksUri: `https://${normalizedDomain}/.well-known/jwks.json`,
25
36
  cache: true,
26
37
  cacheMaxAge: 86400000, // 24 hours
27
38
  });
@@ -55,12 +66,14 @@ export const verifyAuth0Token = (token) => {
55
66
  return;
56
67
  }
57
68
 
69
+ const normalizedDomain = normalizeDomain(auth0Config.domain);
70
+
58
71
  jwt.verify(
59
72
  token,
60
73
  getKey,
61
74
  {
62
75
  audience: auth0Config.clientId,
63
- issuer: `https://${auth0Config.domain}/`,
76
+ issuer: `https://${normalizedDomain}/`,
64
77
  algorithms: ['RS256'],
65
78
  },
66
79
  (err, decoded) => {
@@ -80,20 +93,29 @@ export const verifyAuth0Token = (token) => {
80
93
  export const findOrCreateUserFromAuth0 = async (tokenPayload) => {
81
94
  const auth0Id = tokenPayload.sub;
82
95
  const email = tokenPayload.email;
96
+ const now = new Date();
83
97
 
84
98
  // Try to find existing user by Auth0 ID
85
- let user = Meteor.users.findOne({
99
+ let user = await Meteor.users.findOneAsync({
86
100
  'services.auth0.id': auth0Id,
87
101
  });
88
102
 
89
103
  if (user) {
90
- // Update user profile if needed
91
- Meteor.users.update(user._id, {
104
+ // Update user profile and token data
105
+ await Meteor.users.updateAsync(user._id, {
92
106
  $set: {
93
- 'services.auth0.email': email,
94
- 'services.auth0.name': tokenPayload.name,
95
- 'services.auth0.picture': tokenPayload.picture,
96
- 'services.auth0.email_verified': tokenPayload.email_verified,
107
+ // Profile data (client-accessible)
108
+ 'profile.auth0': {
109
+ name: tokenPayload.name,
110
+ picture: tokenPayload.picture,
111
+ email: email,
112
+ email_verified: tokenPayload.email_verified,
113
+ },
114
+ // Token data (server-only)
115
+ 'services.auth0.id': auth0Id,
116
+ 'services.auth0.accessToken': tokenPayload.aud,
117
+ 'services.auth0.expiresAt': new Date(tokenPayload.exp * 1000),
118
+ 'services.auth0.issuedAt': new Date(tokenPayload.iat * 1000),
97
119
  },
98
120
  });
99
121
  return user._id;
@@ -101,21 +123,28 @@ export const findOrCreateUserFromAuth0 = async (tokenPayload) => {
101
123
 
102
124
  // Try to find by email if email exists
103
125
  if (email) {
104
- user = Meteor.users.findOne({
126
+ user = await Meteor.users.findOneAsync({
105
127
  'emails.address': email,
106
128
  });
107
129
 
108
130
  if (user) {
109
131
  // Link Auth0 account to existing user
110
- Meteor.users.update(user._id, {
132
+ await Meteor.users.updateAsync(user._id, {
111
133
  $set: {
112
- 'services.auth0': {
113
- id: auth0Id,
114
- email: email,
134
+ // Profile data (client-accessible)
135
+ 'profile.auth0': {
115
136
  name: tokenPayload.name,
116
137
  picture: tokenPayload.picture,
138
+ email: email,
117
139
  email_verified: tokenPayload.email_verified,
118
140
  },
141
+ // Token data (server-only)
142
+ 'services.auth0': {
143
+ id: auth0Id,
144
+ accessToken: tokenPayload.aud,
145
+ expiresAt: new Date(tokenPayload.exp * 1000),
146
+ issuedAt: new Date(tokenPayload.iat * 1000),
147
+ },
119
148
  },
120
149
  });
121
150
  return user._id;
@@ -127,16 +156,23 @@ export const findOrCreateUserFromAuth0 = async (tokenPayload) => {
127
156
  emails: email ? [{ address: email, verified: tokenPayload.email_verified || false }] : [],
128
157
  profile: {
129
158
  name: tokenPayload.name || tokenPayload.nickname || 'Auth0 User',
130
- },
131
- services: {
159
+ // Auth0 profile data (client-accessible)
132
160
  auth0: {
133
- id: auth0Id,
134
- email: email,
135
161
  name: tokenPayload.name,
136
162
  picture: tokenPayload.picture,
163
+ email: email,
137
164
  email_verified: tokenPayload.email_verified,
138
165
  },
139
166
  },
167
+ services: {
168
+ // Token data (server-only)
169
+ auth0: {
170
+ id: auth0Id,
171
+ accessToken: tokenPayload.aud,
172
+ expiresAt: new Date(tokenPayload.exp * 1000),
173
+ issuedAt: new Date(tokenPayload.iat * 1000),
174
+ },
175
+ },
140
176
  });
141
177
 
142
178
  return userId;
@@ -23,6 +23,15 @@ interface Auth0TokenPayload {
23
23
  iat: number;
24
24
  }
25
25
 
26
+ /**
27
+ * Normalize Auth0 domain by removing protocol and trailing slashes
28
+ */
29
+ const normalizeDomain = (domain: string): string => {
30
+ return domain
31
+ .replace(/^https?:\/\//, '') // Remove http:// or https://
32
+ .replace(/\/$/, ''); // Remove trailing slash
33
+ };
34
+
26
35
  /**
27
36
  * Create JWKS client for verifying Auth0 tokens
28
37
  */
@@ -33,8 +42,10 @@ const getJwksClient = () => {
33
42
  throw new Meteor.Error('auth0-config-missing', 'Auth0 domain not configured');
34
43
  }
35
44
 
45
+ const normalizedDomain = normalizeDomain(auth0Domain);
46
+
36
47
  return jwksClient({
37
- jwksUri: `https://${auth0Domain}/.well-known/jwks.json`,
48
+ jwksUri: `https://${normalizedDomain}/.well-known/jwks.json`,
38
49
  cache: true,
39
50
  cacheMaxAge: 86400000, // 24 hours
40
51
  });
@@ -68,12 +79,14 @@ export const verifyAuth0Token = (token: string): Promise<Auth0TokenPayload> => {
68
79
  return;
69
80
  }
70
81
 
82
+ const normalizedDomain = normalizeDomain(auth0Config.domain);
83
+
71
84
  jwt.verify(
72
85
  token,
73
86
  getKey,
74
87
  {
75
88
  audience: auth0Config.clientId,
76
- issuer: `https://${auth0Config.domain}/`,
89
+ issuer: `https://${normalizedDomain}/`,
77
90
  algorithms: ['RS256'],
78
91
  },
79
92
  (err, decoded) => {
@@ -95,20 +108,29 @@ export const findOrCreateUserFromAuth0 = async (
95
108
  ): Promise<string> => {
96
109
  const auth0Id = tokenPayload.sub;
97
110
  const email = tokenPayload.email;
111
+ const now = new Date();
98
112
 
99
113
  // Try to find existing user by Auth0 ID
100
- let user = Meteor.users.findOne({
114
+ let user = await Meteor.users.findOneAsync({
101
115
  'services.auth0.id': auth0Id,
102
116
  });
103
117
 
104
118
  if (user) {
105
- // Update user profile if needed
106
- Meteor.users.update(user._id, {
119
+ // Update user profile and token data
120
+ await Meteor.users.updateAsync(user._id, {
107
121
  $set: {
108
- 'services.auth0.email': email,
109
- 'services.auth0.name': tokenPayload.name,
110
- 'services.auth0.picture': tokenPayload.picture,
111
- 'services.auth0.email_verified': tokenPayload.email_verified,
122
+ // Profile data (client-accessible)
123
+ 'profile.auth0': {
124
+ name: tokenPayload.name,
125
+ picture: tokenPayload.picture,
126
+ email: email,
127
+ email_verified: tokenPayload.email_verified,
128
+ },
129
+ // Token data (server-only)
130
+ 'services.auth0.id': auth0Id,
131
+ 'services.auth0.accessToken': tokenPayload.aud,
132
+ 'services.auth0.expiresAt': new Date(tokenPayload.exp * 1000),
133
+ 'services.auth0.issuedAt': new Date(tokenPayload.iat * 1000),
112
134
  },
113
135
  });
114
136
  return user._id;
@@ -116,21 +138,28 @@ export const findOrCreateUserFromAuth0 = async (
116
138
 
117
139
  // Try to find by email if email exists
118
140
  if (email) {
119
- user = Meteor.users.findOne({
141
+ user = await Meteor.users.findOneAsync({
120
142
  'emails.address': email,
121
143
  });
122
144
 
123
145
  if (user) {
124
146
  // Link Auth0 account to existing user
125
- Meteor.users.update(user._id, {
147
+ await Meteor.users.updateAsync(user._id, {
126
148
  $set: {
127
- 'services.auth0': {
128
- id: auth0Id,
129
- email: email,
149
+ // Profile data (client-accessible)
150
+ 'profile.auth0': {
130
151
  name: tokenPayload.name,
131
152
  picture: tokenPayload.picture,
153
+ email: email,
132
154
  email_verified: tokenPayload.email_verified,
133
155
  },
156
+ // Token data (server-only)
157
+ 'services.auth0': {
158
+ id: auth0Id,
159
+ accessToken: tokenPayload.aud,
160
+ expiresAt: new Date(tokenPayload.exp * 1000),
161
+ issuedAt: new Date(tokenPayload.iat * 1000),
162
+ },
134
163
  },
135
164
  });
136
165
  return user._id;
@@ -142,16 +171,23 @@ export const findOrCreateUserFromAuth0 = async (
142
171
  emails: email ? [{ address: email, verified: tokenPayload.email_verified || false }] : [],
143
172
  profile: {
144
173
  name: tokenPayload.name || tokenPayload.nickname || 'Auth0 User',
145
- },
146
- services: {
174
+ // Auth0 profile data (client-accessible)
147
175
  auth0: {
148
- id: auth0Id,
149
- email: email,
150
176
  name: tokenPayload.name,
151
177
  picture: tokenPayload.picture,
178
+ email: email,
152
179
  email_verified: tokenPayload.email_verified,
153
180
  },
154
181
  },
182
+ services: {
183
+ // Token data (server-only)
184
+ auth0: {
185
+ id: auth0Id,
186
+ accessToken: tokenPayload.aud,
187
+ expiresAt: new Date(tokenPayload.exp * 1000),
188
+ issuedAt: new Date(tokenPayload.iat * 1000),
189
+ },
190
+ },
155
191
  });
156
192
 
157
193
  return userId;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maka/maka-cli",
3
- "version": "5.1.46",
3
+ "version": "5.1.47",
4
4
  "type": "module",
5
5
  "summary": "A command line tool for scaffolding Meteor 2.x applications using either React.",
6
6
  "description": "A command line tool for scaffolding Meteor 2.x applications using React.",