supaapps-auth 1.1.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -59,6 +59,17 @@ Get login uri
59
59
  AuthManager.getInstance().getLoginWithGoogleUri()
60
60
  ```
61
61
 
62
+
63
+ - Log user out
64
+
65
+ ```ts
66
+ await authManager.logout();
67
+ // or
68
+ authManager.logout().then(() => {
69
+ // user is now logged out
70
+ })
71
+ ```
72
+
62
73
  - Validate access token
63
74
 
64
75
  ```typescript
@@ -8,10 +8,13 @@ export declare class AuthManager {
8
8
  static getInstance<T>(): AuthManager;
9
9
  private toBase64Url;
10
10
  private generatePKCEPair;
11
+ private refreshAccessToken;
12
+ private checkAccessToken;
11
13
  mustBeLoggedIn(): Promise<boolean>;
12
14
  getLoginWithGoogleUri(): string;
13
15
  isLoggedIn(): Promise<boolean>;
14
16
  getAccessToken(): Promise<string>;
15
17
  loginUsingPkce(code: any): Promise<void>;
18
+ logout(): Promise<void>;
16
19
  static validateToken(authServer: string, bearerToken: string): Promise<boolean>;
17
20
  }
@@ -48,6 +48,74 @@ class AuthManager {
48
48
  }
49
49
  return AuthManager.instance;
50
50
  }
51
+ refreshAccessToken() {
52
+ return __awaiter(this, void 0, void 0, function* () {
53
+ return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
54
+ try {
55
+ const refreshToken = localStorage.getItem('refresh_token');
56
+ if (!refreshToken) {
57
+ throw new Error('No refresh token found');
58
+ }
59
+ const decodedRefreshToken = JSON.parse(atob(refreshToken.split('.')[1]));
60
+ if (decodedRefreshToken) {
61
+ const currentTime = Date.now() / 1000;
62
+ if (decodedRefreshToken.exp < currentTime) {
63
+ throw new Error('Refresh token expired');
64
+ }
65
+ }
66
+ yield fetch(`${this.authServer}auth/refresh`, {
67
+ method: 'POST',
68
+ headers: {
69
+ 'Content-Type': 'application/json',
70
+ },
71
+ body: JSON.stringify({
72
+ refresh_token: refreshToken,
73
+ }),
74
+ })
75
+ .then((response) => {
76
+ if (response.status !== 200) {
77
+ throw new Error('Failed to refresh the token');
78
+ }
79
+ return response.json();
80
+ })
81
+ .then((exchangeJson) => {
82
+ localStorage.setItem('refresh_token', exchangeJson.refresh_token);
83
+ localStorage.setItem('access_token', exchangeJson.access_token);
84
+ resolve(exchangeJson.access_token);
85
+ })
86
+ .catch((error) => {
87
+ reject(error);
88
+ });
89
+ }
90
+ catch (error) {
91
+ reject(error);
92
+ }
93
+ }));
94
+ });
95
+ }
96
+ checkAccessToken() {
97
+ return __awaiter(this, void 0, void 0, function* () {
98
+ return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
99
+ try {
100
+ let accessToken = localStorage.getItem('access_token');
101
+ if (!accessToken) {
102
+ accessToken = yield this.refreshAccessToken();
103
+ }
104
+ else {
105
+ const decodedToken = accessToken ? JSON.parse(atob(accessToken.split('.')[1])) : null;
106
+ const currentTime = Date.now() / 1000;
107
+ if (decodedToken && decodedToken.exp < currentTime) {
108
+ accessToken = yield this.refreshAccessToken();
109
+ }
110
+ }
111
+ resolve(accessToken);
112
+ }
113
+ catch (error) {
114
+ reject(error);
115
+ }
116
+ }));
117
+ });
118
+ }
51
119
  mustBeLoggedIn() {
52
120
  return __awaiter(this, void 0, void 0, function* () {
53
121
  return new Promise((resolve, reject) => {
@@ -76,56 +144,30 @@ class AuthManager {
76
144
  isLoggedIn() {
77
145
  return __awaiter(this, void 0, void 0, function* () {
78
146
  // todo here: check if refresh token is expired and if so, try to refresh, then update token
79
- return new Promise((resolve, reject) => {
147
+ return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
80
148
  try {
81
- const accessToken = localStorage.getItem('access_token');
82
- if (!accessToken) {
83
- return resolve(false);
84
- }
85
- // decode access token and check if it's expired
86
- const decodedToken = accessToken ? JSON.parse(atob(accessToken.split('.')[1])) : null;
87
- if (decodedToken) {
88
- const currentTime = Date.now() / 1000;
89
- if (decodedToken.exp < currentTime) {
90
- // add refresh check here instead and
91
- localStorage.removeItem('access_token');
92
- return resolve(false);
93
- }
94
- }
149
+ yield this.checkAccessToken();
95
150
  return resolve(true);
96
151
  }
97
152
  catch (error) {
98
153
  reject(error);
99
154
  }
100
- });
155
+ }));
101
156
  });
102
157
  }
103
158
  getAccessToken() {
104
159
  return __awaiter(this, void 0, void 0, function* () {
105
160
  // todo here: check if refresh token is expired and if so, try to refresh, then update token
106
161
  // otherwise throw error
107
- return new Promise((resolve, reject) => {
162
+ return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
108
163
  try {
109
- const accessToken = localStorage.getItem('access_token');
110
- if (!accessToken) {
111
- throw new Error('No access token found');
112
- }
113
- // decode access token and check if it's expired
114
- const decodedToken = accessToken ? JSON.parse(atob(accessToken.split('.')[1])) : null;
115
- if (decodedToken) {
116
- const currentTime = Date.now() / 1000;
117
- if (decodedToken.exp < currentTime) {
118
- // add refresh check here instead and
119
- localStorage.removeItem('access_token');
120
- throw new Error('Access token expired');
121
- }
122
- }
164
+ const accessToken = yield this.checkAccessToken();
123
165
  return resolve(accessToken);
124
166
  }
125
167
  catch (error) {
126
168
  reject(error);
127
169
  }
128
- });
170
+ }));
129
171
  });
130
172
  }
131
173
  loginUsingPkce(code) {
@@ -172,6 +214,34 @@ class AuthManager {
172
214
  });
173
215
  });
174
216
  }
217
+ logout() {
218
+ return __awaiter(this, void 0, void 0, function* () {
219
+ return new Promise((resolve, reject) => {
220
+ try {
221
+ const bearerToken = localStorage.getItem('access_token');
222
+ localStorage.removeItem('access_token');
223
+ localStorage.removeItem('refresh_token');
224
+ fetch(`${this.authServer}auth/logout`, {
225
+ method: 'POST',
226
+ headers: {
227
+ 'Content-Type': 'application/json',
228
+ 'Authorization': `Bearer ${bearerToken}`,
229
+ },
230
+ }).then((response) => {
231
+ if (response.status !== 200) {
232
+ throw new Error('Failed to attempt logout');
233
+ }
234
+ resolve();
235
+ }).catch((error) => {
236
+ reject(error);
237
+ });
238
+ }
239
+ catch (error) {
240
+ reject(error);
241
+ }
242
+ });
243
+ });
244
+ }
175
245
  static validateToken(authServer, bearerToken) {
176
246
  return __awaiter(this, void 0, void 0, function* () {
177
247
  return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "supaapps-auth",
3
- "version": "1.1.0",
3
+ "version": "1.3.0",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -1,7 +1,6 @@
1
1
  import axios from 'axios';
2
2
  import { createHash, randomBytes } from 'crypto';
3
3
 
4
-
5
4
  export class AuthManager {
6
5
  private static instance: AuthManager | null = null;
7
6
  private readonly authServer: string | null = null;
@@ -50,6 +49,72 @@ export class AuthManager {
50
49
  return { newCodeVerifier, newCodeChallenge };
51
50
  };
52
51
 
52
+ private async refreshAccessToken(): Promise<string> {
53
+ return new Promise(async (resolve, reject) => {
54
+ try {
55
+ const refreshToken: string | null = localStorage.getItem('refresh_token');
56
+ if (!refreshToken) {
57
+ throw new Error('No refresh token found');
58
+ }
59
+ const decodedRefreshToken = JSON.parse(atob(refreshToken.split('.')[1]));
60
+ if (decodedRefreshToken) {
61
+ const currentTime = Date.now() / 1000;
62
+ if (decodedRefreshToken.exp < currentTime) {
63
+ throw new Error('Refresh token expired');
64
+ }
65
+ }
66
+ await fetch(`${this.authServer}auth/refresh`, {
67
+ method: 'POST',
68
+ headers: {
69
+ 'Content-Type': 'application/json',
70
+ },
71
+ body: JSON.stringify({
72
+ refresh_token: refreshToken,
73
+ }),
74
+ })
75
+ .then((response) => {
76
+ if (response.status !== 200) {
77
+ throw new Error('Failed to refresh the token');
78
+ }
79
+ return response.json();
80
+ })
81
+ .then((exchangeJson) => {
82
+ localStorage.setItem('refresh_token', exchangeJson.refresh_token);
83
+ localStorage.setItem('access_token', exchangeJson.access_token);
84
+ resolve(exchangeJson.access_token);
85
+ })
86
+ .catch((error) => {
87
+ reject(error);
88
+ });
89
+ } catch (error) {
90
+ reject(error);
91
+ }
92
+ });
93
+ }
94
+
95
+ private async checkAccessToken(): Promise<string> {
96
+ return new Promise(async (resolve, reject) => {
97
+ try {
98
+ let accessToken: string | null = localStorage.getItem('access_token');
99
+
100
+ if (!accessToken) {
101
+ accessToken = await this.refreshAccessToken();
102
+ } else {
103
+ const decodedToken = accessToken ? JSON.parse(atob(accessToken.split('.')[1])) : null;
104
+ const currentTime = Date.now() / 1000;
105
+
106
+ if (decodedToken && decodedToken.exp < currentTime) {
107
+ accessToken = await this.refreshAccessToken();
108
+ }
109
+ }
110
+
111
+ resolve(accessToken);
112
+ } catch (error) {
113
+ reject(error);
114
+ }
115
+ });
116
+ }
117
+
53
118
  public async mustBeLoggedIn(): Promise<boolean> {
54
119
  return new Promise((resolve, reject) => {
55
120
  this.isLoggedIn().then((isLoggedIn) => {
@@ -75,25 +140,12 @@ export class AuthManager {
75
140
  `&redirect_uri=${encodeURIComponent(this.redirectUri)}&code_challenge=${codeChallenge}&code_challenge_method=S256`
76
141
  }
77
142
  }
143
+
78
144
  public async isLoggedIn(): Promise<boolean> {
79
145
  // todo here: check if refresh token is expired and if so, try to refresh, then update token
80
- return new Promise((resolve, reject) => {
146
+ return new Promise(async (resolve, reject) => {
81
147
  try {
82
- const accessToken: string | null = localStorage.getItem('access_token');
83
- if (!accessToken) {
84
- return resolve(false);
85
- }
86
- // decode access token and check if it's expired
87
- const decodedToken = accessToken ? JSON.parse(atob(accessToken.split('.')[1])) : null;
88
- if (decodedToken) {
89
- const currentTime = Date.now() / 1000;
90
- if (decodedToken.exp < currentTime) {
91
- // add refresh check here instead and
92
- localStorage.removeItem('access_token');
93
- return resolve(false);
94
- }
95
- }
96
-
148
+ await this.checkAccessToken();
97
149
  return resolve(true);
98
150
  } catch (error) {
99
151
  reject(error);
@@ -104,22 +156,9 @@ export class AuthManager {
104
156
  public async getAccessToken(): Promise<string> {
105
157
  // todo here: check if refresh token is expired and if so, try to refresh, then update token
106
158
  // otherwise throw error
107
- return new Promise((resolve, reject) => {
159
+ return new Promise(async (resolve, reject) => {
108
160
  try {
109
- const accessToken: string | null = localStorage.getItem('access_token');
110
- if (!accessToken) {
111
- throw new Error('No access token found');
112
- }
113
- // decode access token and check if it's expired
114
- const decodedToken = accessToken ? JSON.parse(atob(accessToken.split('.')[1])) : null;
115
- if (decodedToken) {
116
- const currentTime = Date.now() / 1000;
117
- if (decodedToken.exp < currentTime) {
118
- // add refresh check here instead and
119
- localStorage.removeItem('access_token');
120
- throw new Error('Access token expired');
121
- }
122
- }
161
+ const accessToken = await this.checkAccessToken();
123
162
 
124
163
  return resolve(accessToken);
125
164
  } catch (error) {
@@ -170,6 +209,32 @@ export class AuthManager {
170
209
  });
171
210
  }
172
211
 
212
+ public async logout(): Promise<void> {
213
+ return new Promise((resolve, reject) => {
214
+ try {
215
+ const bearerToken = localStorage.getItem('access_token');
216
+ localStorage.removeItem('access_token');
217
+ localStorage.removeItem('refresh_token');
218
+ fetch(`${this.authServer}auth/logout`, {
219
+ method: 'POST',
220
+ headers: {
221
+ 'Content-Type': 'application/json',
222
+ 'Authorization': `Bearer ${bearerToken}`,
223
+ },
224
+ }).then((response) => {
225
+ if (response.status !== 200) {
226
+ throw new Error('Failed to attempt logout')
227
+ }
228
+ resolve();
229
+ }).catch((error) => {
230
+ reject(error);
231
+ })
232
+ } catch (error) {
233
+ reject(error);
234
+ }
235
+ })
236
+ }
237
+
173
238
  public static async validateToken(authServer: string, bearerToken: string): Promise<boolean> {
174
239
  return new Promise<boolean>(async (resolve, reject) => {
175
240
  try {