supaapps-auth 2.0.0-rc.9 → 2.1.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.
@@ -1,4 +1,4 @@
1
- import { AuthManagerEvent, UserTokenPayload } from './types';
1
+ import { AuthManagerEvent, PlatformCheckResponse, UserTokenPayload } from './types';
2
2
  export declare class AuthManager {
3
3
  private static instance;
4
4
  private authServer;
@@ -18,11 +18,24 @@ export declare class AuthManager {
18
18
  getLoginWithGoogleUri(): string;
19
19
  isLoggedIn(): Promise<boolean>;
20
20
  getAccessToken(mustBeLoggedIn?: boolean): Promise<string>;
21
- platformCheck(email: string, token: string): Promise<boolean>;
21
+ platformCheck(email: string): Promise<PlatformCheckResponse>;
22
22
  verifyEmail(email: string, token: string): Promise<boolean>;
23
23
  doPassReset(email: string, token: string, newPassword: string): Promise<boolean>;
24
24
  changeEmail(email: string): Promise<boolean>;
25
25
  initPasswordReset(email: string): Promise<boolean>;
26
+ /**
27
+ * Updates user account fields. Only sends fields present in the update object.
28
+ * For password, expects: { old: string, new: string }
29
+ */
30
+ updateAccount(update: {
31
+ firstName?: string;
32
+ lastName?: string;
33
+ email?: string;
34
+ password?: {
35
+ old: string;
36
+ new: string;
37
+ };
38
+ }): Promise<boolean>;
26
39
  changePassword(oldPassword: string, newPassword: string, email: string): Promise<boolean>;
27
40
  registerUsingEmail(firstName: string, lastName: string, email: string, password: string): Promise<void>;
28
41
  private saveTokens;
@@ -115,13 +115,24 @@ class AuthManager {
115
115
  }
116
116
  isLoggedIn() {
117
117
  return __awaiter(this, void 0, void 0, function* () {
118
- try {
119
- yield this.checkAccessToken();
120
- return true;
121
- }
122
- catch (error) {
118
+ const accessToken = localStorage.getItem('access_token');
119
+ const refreshToken = localStorage.getItem('refresh_token');
120
+ // If either token is missing, user is not logged in
121
+ if (!accessToken || !refreshToken) {
123
122
  return false;
124
123
  }
124
+ // If access token is expired, try to refresh
125
+ if (this.isTokenExpired(accessToken)) {
126
+ try {
127
+ yield this.refreshAccessToken();
128
+ return true;
129
+ }
130
+ catch (_a) {
131
+ return false;
132
+ }
133
+ }
134
+ // If access token is valid
135
+ return true;
125
136
  });
126
137
  }
127
138
  getAccessToken() {
@@ -139,7 +150,7 @@ class AuthManager {
139
150
  }
140
151
  });
141
152
  }
142
- platformCheck(email, token) {
153
+ platformCheck(email) {
143
154
  return __awaiter(this, void 0, void 0, function* () {
144
155
  const response = yield axios_1.default.post(`${this.authServer}auth/email/platform_check`, {
145
156
  realm_name: this.realmName,
@@ -148,7 +159,9 @@ class AuthManager {
148
159
  if (response.data.error || response.data.errors) {
149
160
  throw new Error(response.data.error || response.data.message);
150
161
  }
151
- return (response.status === 200) ? response.data : { 'platforms': [] };
162
+ return response.status === 200
163
+ ? response.data
164
+ : { platforms: [] };
152
165
  });
153
166
  }
154
167
  verifyEmail(email, token) {
@@ -208,6 +221,57 @@ class AuthManager {
208
221
  return response.status === 200 || response.status === 201;
209
222
  });
210
223
  }
224
+ /**
225
+ * Updates user account fields. Only sends fields present in the update object.
226
+ * For password, expects: { old: string, new: string }
227
+ */
228
+ updateAccount(update) {
229
+ return __awaiter(this, void 0, void 0, function* () {
230
+ const accessToken = localStorage.getItem('access_token');
231
+ if (!accessToken) {
232
+ throw new Error('Access token not found');
233
+ }
234
+ // Update name
235
+ if (update.firstName || update.lastName) {
236
+ const response = yield axios_1.default.post(`${this.authServer}auth/email/update_profile`, Object.assign(Object.assign({ realm_name: this.realmName }, (update.firstName && { first_name: update.firstName })), (update.lastName && { last_name: update.lastName })), {
237
+ headers: { Authorization: `Bearer ${accessToken}` },
238
+ });
239
+ if (response.data.error || response.data.errors) {
240
+ throw new Error(response.data.error || response.data.message);
241
+ }
242
+ }
243
+ // Update email
244
+ if (update.email) {
245
+ const response = yield axios_1.default.post(`${this.authServer}auth/email/change_email`, {
246
+ realm_name: this.realmName,
247
+ email: update.email,
248
+ }, {
249
+ headers: { Authorization: `Bearer ${accessToken}` },
250
+ });
251
+ if (response.data.error || response.data.errors) {
252
+ throw new Error(response.data.error || response.data.message);
253
+ }
254
+ }
255
+ // Update password
256
+ if (update.password && update.email) {
257
+ const response = yield axios_1.default.post(`${this.authServer}auth/email/change_pass`, {
258
+ realm_name: this.realmName,
259
+ email: update.email,
260
+ old_password: update.password.old,
261
+ new_password: update.password.new,
262
+ }, {
263
+ headers: { Authorization: `Bearer ${accessToken}` },
264
+ });
265
+ if (response.data.error || response.data.errors) {
266
+ throw new Error(response.data.error || response.data.message);
267
+ }
268
+ }
269
+ else if (update.password && !update.email) {
270
+ throw new Error('Email is required to change password');
271
+ }
272
+ return true;
273
+ });
274
+ }
211
275
  changePassword(oldPassword, newPassword, email) {
212
276
  return __awaiter(this, void 0, void 0, function* () {
213
277
  const accessToken = localStorage.getItem('access_token');
@@ -250,7 +314,9 @@ class AuthManager {
250
314
  localStorage.setItem('access_token', response.data.access_token);
251
315
  localStorage.setItem('refresh_token', response.data.refresh_token);
252
316
  this.onStateChange({
253
- type: byRefresh ? types_1.AuthEventType.USER_UPDATED : types_1.AuthEventType.USER_LOGGED_IN,
317
+ type: byRefresh
318
+ ? types_1.AuthEventType.USER_UPDATED
319
+ : types_1.AuthEventType.USER_LOGGED_IN,
254
320
  user: this.tokenToPayload(response.data.access_token),
255
321
  });
256
322
  const user = this.tokenToPayload(response.data.access_token);
@@ -322,7 +388,9 @@ class AuthManager {
322
388
  const userToken = {
323
389
  id: decodedToken.id,
324
390
  iss: decodedToken.iss,
325
- sub: typeof decodedToken.sub === 'string' ? parseInt(decodedToken.sub) : decodedToken.sub,
391
+ sub: typeof decodedToken.sub === 'string'
392
+ ? parseInt(decodedToken.sub)
393
+ : decodedToken.sub,
326
394
  first_name: decodedToken.first_name,
327
395
  last_name: decodedToken.last_name,
328
396
  email: decodedToken.email,
@@ -331,6 +399,7 @@ class AuthManager {
331
399
  exp: decodedToken.exp,
332
400
  scopes: decodedToken.scopes,
333
401
  realm: decodedToken.realm,
402
+ provider: decodedToken.provider,
334
403
  };
335
404
  const { data: publicKey } = yield axios_1.default.get(`${authServer}public/public_key`);
336
405
  const { data: algo } = yield axios_1.default.get(`${authServer}public/algo`);
package/dist/types.d.ts CHANGED
@@ -19,8 +19,22 @@ export interface UserTokenPayload {
19
19
  exp: number;
20
20
  scopes: string;
21
21
  realm: string;
22
+ provider: Platforms;
22
23
  }
23
24
  export interface AuthManagerEvent {
24
25
  type: AuthEventType;
25
26
  user?: UserTokenPayload;
26
27
  }
28
+ export interface PlatformCheckResponse {
29
+ platforms: Platforms[];
30
+ }
31
+ export declare enum Platforms {
32
+ PASSWORD = "password",
33
+ GOOGLE = "google",
34
+ FACEBOOK = "facebook",
35
+ TWITTER = "twitter",
36
+ GITHUB = "github",
37
+ APPLE = "apple",
38
+ LINKEDIN = "linkedin",
39
+ MICROSOFT = "microsoft"
40
+ }
package/dist/types.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.AuthEventType = void 0;
3
+ exports.Platforms = exports.AuthEventType = void 0;
4
4
  var AuthEventType;
5
5
  (function (AuthEventType) {
6
6
  AuthEventType["INITALIZED_IN"] = "initialized-logged-in";
@@ -11,3 +11,14 @@ var AuthEventType;
11
11
  AuthEventType["FAILED_MUST_LOGIN_CHECK"] = "failed-must-login";
12
12
  AuthEventType["REFRESH_FAILED"] = "refresh-failed";
13
13
  })(AuthEventType || (exports.AuthEventType = AuthEventType = {}));
14
+ var Platforms;
15
+ (function (Platforms) {
16
+ Platforms["PASSWORD"] = "password";
17
+ Platforms["GOOGLE"] = "google";
18
+ Platforms["FACEBOOK"] = "facebook";
19
+ Platforms["TWITTER"] = "twitter";
20
+ Platforms["GITHUB"] = "github";
21
+ Platforms["APPLE"] = "apple";
22
+ Platforms["LINKEDIN"] = "linkedin";
23
+ Platforms["MICROSOFT"] = "microsoft";
24
+ })(Platforms || (exports.Platforms = Platforms = {}));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "supaapps-auth",
3
- "version": "2.0.0-rc.9",
3
+ "version": "2.1.0",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -4,7 +4,12 @@ import {
4
4
  decode as jwtDecode,
5
5
  verify as jwtVerify,
6
6
  } from 'jsonwebtoken'; // Ensure jsonwebtoken is correctly imported
7
- import { AuthEventType, AuthManagerEvent, UserTokenPayload } from './types';
7
+ import {
8
+ AuthEventType,
9
+ AuthManagerEvent,
10
+ PlatformCheckResponse,
11
+ UserTokenPayload,
12
+ } from './types';
8
13
 
9
14
  export class AuthManager {
10
15
  private static instance: AuthManager | null = null;
@@ -44,16 +49,16 @@ export class AuthManager {
44
49
  onStateChange,
45
50
  );
46
51
  AuthManager.instance
47
- .checkAccessToken(true)
48
- .then((token) => {
49
- onStateChange({
50
- type: AuthEventType.INITALIZED_IN,
51
- user: AuthManager.instance.tokenToPayload(token),
52
+ .checkAccessToken(true)
53
+ .then((token) => {
54
+ onStateChange({
55
+ type: AuthEventType.INITALIZED_IN,
56
+ user: AuthManager.instance.tokenToPayload(token),
57
+ });
58
+ })
59
+ .catch(() => {
60
+ onStateChange({ type: AuthEventType.INITALIZED_OUT });
52
61
  });
53
- })
54
- .catch(() => {
55
- onStateChange({ type: AuthEventType.INITALIZED_OUT });
56
- });
57
62
  }
58
63
  return AuthManager.instance;
59
64
  }
@@ -95,7 +100,9 @@ export class AuthManager {
95
100
  return { verifier, challenge };
96
101
  }
97
102
 
98
- public async refreshAccessToken(isInitialization: boolean = false): Promise<string> {
103
+ public async refreshAccessToken(
104
+ isInitialization: boolean = false,
105
+ ): Promise<string> {
99
106
  try {
100
107
  const refreshToken = localStorage.getItem('refresh_token');
101
108
  if (!refreshToken) {
@@ -122,7 +129,9 @@ export class AuthManager {
122
129
  }
123
130
  }
124
131
 
125
- public async checkAccessToken(isInitilization: boolean = false): Promise<string> {
132
+ public async checkAccessToken(
133
+ isInitilization: boolean = false,
134
+ ): Promise<string> {
126
135
  const accessToken = localStorage.getItem('access_token');
127
136
  if (accessToken && this.isTokenExpired(accessToken)) {
128
137
  return this.refreshAccessToken(isInitilization);
@@ -149,15 +158,31 @@ export class AuthManager {
149
158
  }
150
159
 
151
160
  public async isLoggedIn(): Promise<boolean> {
152
- try {
153
- await this.checkAccessToken();
154
- return true;
155
- } catch (error) {
161
+ const accessToken = localStorage.getItem('access_token');
162
+ const refreshToken = localStorage.getItem('refresh_token');
163
+
164
+ // If either token is missing, user is not logged in
165
+ if (!accessToken || !refreshToken) {
156
166
  return false;
157
167
  }
168
+
169
+ // If access token is expired, try to refresh
170
+ if (this.isTokenExpired(accessToken)) {
171
+ try {
172
+ await this.refreshAccessToken();
173
+ return true;
174
+ } catch {
175
+ return false;
176
+ }
177
+ }
178
+
179
+ // If access token is valid
180
+ return true;
158
181
  }
159
182
 
160
- public async getAccessToken(mustBeLoggedIn: boolean = false): Promise<string> {
183
+ public async getAccessToken(
184
+ mustBeLoggedIn: boolean = false,
185
+ ): Promise<string> {
161
186
  try {
162
187
  return await this.checkAccessToken();
163
188
  } catch (error) {
@@ -170,23 +195,29 @@ export class AuthManager {
170
195
  }
171
196
  }
172
197
 
173
-
174
- public async platformCheck(email: string, token: string): Promise<boolean> {
198
+ public async platformCheck(
199
+ email: string,
200
+ ): Promise<PlatformCheckResponse> {
175
201
  const response = await axios.post(
176
- `${this.authServer}auth/email/platform_check`,
177
- {
178
- realm_name: this.realmName,
179
- email,
180
- },
202
+ `${this.authServer}auth/email/platform_check`,
203
+ {
204
+ realm_name: this.realmName,
205
+ email,
206
+ },
181
207
  );
182
208
  if (response.data.error || response.data.errors) {
183
209
  throw new Error(response.data.error || response.data.message);
184
210
  }
185
211
 
186
- return (response.status === 200) ? response.data : {'platforms': []};
212
+ return response.status === 200
213
+ ? response.data
214
+ : { platforms: [] };
187
215
  }
188
216
 
189
- public async verifyEmail(email: string, token: string): Promise<boolean> {
217
+ public async verifyEmail(
218
+ email: string,
219
+ token: string,
220
+ ): Promise<boolean> {
190
221
  const response = await axios.post(
191
222
  `${this.authServer}auth/email/verify`,
192
223
  {
@@ -202,15 +233,19 @@ export class AuthManager {
202
233
  return response.status === 200;
203
234
  }
204
235
 
205
- public async doPassReset(email: string, token: string, newPassword: string): Promise<boolean> {
236
+ public async doPassReset(
237
+ email: string,
238
+ token: string,
239
+ newPassword: string,
240
+ ): Promise<boolean> {
206
241
  const response = await axios.post(
207
- `${this.authServer}auth/email/do_pass_reset`,
208
- {
209
- realm_name: this.realmName,
210
- email,
211
- token,
212
- new_password: newPassword,
213
- },
242
+ `${this.authServer}auth/email/do_pass_reset`,
243
+ {
244
+ realm_name: this.realmName,
245
+ email,
246
+ token,
247
+ new_password: newPassword,
248
+ },
214
249
  );
215
250
  if (response.data.error || response.data.errors) {
216
251
  throw new Error(response.data.error || response.data.message);
@@ -256,7 +291,85 @@ export class AuthManager {
256
291
  return response.status === 200 || response.status === 201;
257
292
  }
258
293
 
259
- public async changePassword(oldPassword: string, newPassword: string, email: string): Promise<boolean> {
294
+ /**
295
+ * Updates user account fields. Only sends fields present in the update object.
296
+ * For password, expects: { old: string, new: string }
297
+ */
298
+ public async updateAccount(update: {
299
+ firstName?: string,
300
+ lastName?: string,
301
+ email?: string,
302
+ password?: { old: string, new: string, },
303
+ }): Promise<boolean> {
304
+ const accessToken = localStorage.getItem('access_token');
305
+ if (!accessToken) {
306
+ throw new Error('Access token not found');
307
+ }
308
+
309
+ // Update name
310
+ if (update.firstName || update.lastName) {
311
+ const response = await axios.post(
312
+ `${this.authServer}auth/email/update_profile`,
313
+ {
314
+ realm_name: this.realmName,
315
+ ...(update.firstName && { first_name: update.firstName }),
316
+ ...(update.lastName && { last_name: update.lastName }),
317
+ },
318
+ {
319
+ headers: { Authorization: `Bearer ${accessToken}` },
320
+ },
321
+ );
322
+ if (response.data.error || response.data.errors) {
323
+ throw new Error(response.data.error || response.data.message);
324
+ }
325
+ }
326
+
327
+ // Update email
328
+ if (update.email) {
329
+ const response = await axios.post(
330
+ `${this.authServer}auth/email/change_email`,
331
+ {
332
+ realm_name: this.realmName,
333
+ email: update.email,
334
+ },
335
+ {
336
+ headers: { Authorization: `Bearer ${accessToken}` },
337
+ },
338
+ );
339
+ if (response.data.error || response.data.errors) {
340
+ throw new Error(response.data.error || response.data.message);
341
+ }
342
+ }
343
+
344
+ // Update password
345
+ if (update.password && update.email) {
346
+ const response = await axios.post(
347
+ `${this.authServer}auth/email/change_pass`,
348
+ {
349
+ realm_name: this.realmName,
350
+ email: update.email,
351
+ old_password: update.password.old,
352
+ new_password: update.password.new,
353
+ },
354
+ {
355
+ headers: { Authorization: `Bearer ${accessToken}` },
356
+ },
357
+ );
358
+ if (response.data.error || response.data.errors) {
359
+ throw new Error(response.data.error || response.data.message);
360
+ }
361
+ } else if (update.password && !update.email) {
362
+ throw new Error('Email is required to change password');
363
+ }
364
+
365
+ return true;
366
+ }
367
+
368
+ public async changePassword(
369
+ oldPassword: string,
370
+ newPassword: string,
371
+ email: string,
372
+ ): Promise<boolean> {
260
373
  const accessToken = localStorage.getItem('access_token');
261
374
  if (!accessToken) {
262
375
  throw new Error('Access token not found');
@@ -281,10 +394,10 @@ export class AuthManager {
281
394
  }
282
395
 
283
396
  public async registerUsingEmail(
284
- firstName: string,
285
- lastName: string,
286
- email: string,
287
- password: string
397
+ firstName: string,
398
+ lastName: string,
399
+ email: string,
400
+ password: string,
288
401
  ): Promise<void> {
289
402
  const response = await axios.post(
290
403
  `${this.authServer}auth/email/register`,
@@ -301,27 +414,35 @@ export class AuthManager {
301
414
  }
302
415
 
303
416
  if (!response.data.access_token) {
304
- throw new Error('Something went wrong');
417
+ throw new Error('Something went wrong');
305
418
  }
306
419
 
307
420
  this.saveTokens(response, false);
308
421
  }
309
422
 
310
- private saveTokens(response: AxiosResponse, byRefresh: boolean): void {
423
+ private saveTokens(
424
+ response: AxiosResponse,
425
+ byRefresh: boolean,
426
+ ): void {
311
427
  localStorage.setItem('access_token', response.data.access_token);
312
428
  localStorage.setItem(
313
429
  'refresh_token',
314
430
  response.data.refresh_token,
315
431
  );
316
432
  this.onStateChange({
317
- type: byRefresh ? AuthEventType.USER_UPDATED : AuthEventType.USER_LOGGED_IN,
433
+ type: byRefresh
434
+ ? AuthEventType.USER_UPDATED
435
+ : AuthEventType.USER_LOGGED_IN,
318
436
  user: this.tokenToPayload(response.data.access_token),
319
- });
437
+ });
320
438
  const user = this.tokenToPayload(response.data.access_token);
321
439
  localStorage.setItem('user', JSON.stringify(user));
322
440
  }
323
441
 
324
- public async loginUsingEmail(email: string, password: string): Promise<void> {
442
+ public async loginUsingEmail(
443
+ email: string,
444
+ password: string,
445
+ ): Promise<void> {
325
446
  const response = await axios.post(
326
447
  `${this.authServer}auth/email/login`,
327
448
  {
@@ -394,18 +515,22 @@ export class AuthManager {
394
515
  }
395
516
 
396
517
  const userToken: UserTokenPayload = {
397
- id: decodedToken.id,
398
- iss: decodedToken.iss,
399
- sub: typeof decodedToken.sub === 'string' ? parseInt(decodedToken.sub) : decodedToken.sub,
400
- first_name: decodedToken.first_name,
401
- last_name: decodedToken.last_name,
402
- email: decodedToken.email,
403
- aud: decodedToken.aud,
404
- iat: decodedToken.iat,
405
- exp: decodedToken.exp,
406
- scopes: decodedToken.scopes,
407
- realm: decodedToken.realm,
408
- }
518
+ id: decodedToken.id,
519
+ iss: decodedToken.iss,
520
+ sub:
521
+ typeof decodedToken.sub === 'string'
522
+ ? parseInt(decodedToken.sub)
523
+ : decodedToken.sub,
524
+ first_name: decodedToken.first_name,
525
+ last_name: decodedToken.last_name,
526
+ email: decodedToken.email,
527
+ aud: decodedToken.aud,
528
+ iat: decodedToken.iat,
529
+ exp: decodedToken.exp,
530
+ scopes: decodedToken.scopes,
531
+ realm: decodedToken.realm,
532
+ provider: decodedToken.provider,
533
+ };
409
534
 
410
535
  const { data: publicKey } = await axios.get(
411
536
  `${authServer}public/public_key`,
@@ -419,7 +544,7 @@ export class AuthManager {
419
544
  const { data: revokedIds } = await axios.get(
420
545
  `${authServer}public/revoked_ids`,
421
546
  );
422
- if(revokedIds.includes(decodedToken.id)){
547
+ if (revokedIds.includes(decodedToken.id)) {
423
548
  throw new Error('Token is revoked');
424
549
  }
425
550
  return userToken;
package/src/types.ts CHANGED
@@ -21,9 +21,25 @@ export interface UserTokenPayload {
21
21
  exp: number;
22
22
  scopes: string;
23
23
  realm: string;
24
+ provider: Platforms;
24
25
  }
25
26
 
26
27
  export interface AuthManagerEvent {
27
28
  type: AuthEventType;
28
29
  user?: UserTokenPayload;
29
30
  }
31
+
32
+ export interface PlatformCheckResponse {
33
+ platforms: Platforms[];
34
+ }
35
+
36
+ export enum Platforms {
37
+ PASSWORD = 'password',
38
+ GOOGLE = 'google',
39
+ FACEBOOK = 'facebook',
40
+ TWITTER = 'twitter',
41
+ GITHUB = 'github',
42
+ APPLE = 'apple',
43
+ LINKEDIN = 'linkedin',
44
+ MICROSOFT = 'microsoft',
45
+ }