supaapps-auth 1.1.0 → 1.2.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 +11 -0
- package/dist/AuthManager.d.ts +3 -0
- package/dist/AuthManager.js +107 -32
- package/package.json +1 -1
- package/src/AuthManager.ts +99 -32
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
|
package/dist/AuthManager.d.ts
CHANGED
|
@@ -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
|
}
|
package/dist/AuthManager.js
CHANGED
|
@@ -48,6 +48,79 @@ 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
|
+
const accessToken = localStorage.getItem('access_token');
|
|
101
|
+
if (!accessToken) {
|
|
102
|
+
throw new Error('No access token found');
|
|
103
|
+
}
|
|
104
|
+
// decode access token and check if it's expired
|
|
105
|
+
const decodedToken = accessToken
|
|
106
|
+
? JSON.parse(atob(accessToken.split('.')[1]))
|
|
107
|
+
: null;
|
|
108
|
+
if (decodedToken) {
|
|
109
|
+
const currentTime = Date.now() / 1000;
|
|
110
|
+
if (decodedToken.exp < currentTime) {
|
|
111
|
+
// refreshing expired token
|
|
112
|
+
const newAccessToken = yield this.refreshAccessToken();
|
|
113
|
+
return resolve(newAccessToken);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
resolve(accessToken);
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
reject(error);
|
|
120
|
+
}
|
|
121
|
+
}));
|
|
122
|
+
});
|
|
123
|
+
}
|
|
51
124
|
mustBeLoggedIn() {
|
|
52
125
|
return __awaiter(this, void 0, void 0, function* () {
|
|
53
126
|
return new Promise((resolve, reject) => {
|
|
@@ -76,56 +149,30 @@ class AuthManager {
|
|
|
76
149
|
isLoggedIn() {
|
|
77
150
|
return __awaiter(this, void 0, void 0, function* () {
|
|
78
151
|
// todo here: check if refresh token is expired and if so, try to refresh, then update token
|
|
79
|
-
return new Promise((resolve, reject) => {
|
|
152
|
+
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
|
|
80
153
|
try {
|
|
81
|
-
|
|
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
|
-
}
|
|
154
|
+
yield this.checkAccessToken();
|
|
95
155
|
return resolve(true);
|
|
96
156
|
}
|
|
97
157
|
catch (error) {
|
|
98
158
|
reject(error);
|
|
99
159
|
}
|
|
100
|
-
});
|
|
160
|
+
}));
|
|
101
161
|
});
|
|
102
162
|
}
|
|
103
163
|
getAccessToken() {
|
|
104
164
|
return __awaiter(this, void 0, void 0, function* () {
|
|
105
165
|
// todo here: check if refresh token is expired and if so, try to refresh, then update token
|
|
106
166
|
// otherwise throw error
|
|
107
|
-
return new Promise((resolve, reject) => {
|
|
167
|
+
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
|
|
108
168
|
try {
|
|
109
|
-
const accessToken =
|
|
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
|
-
}
|
|
169
|
+
const accessToken = yield this.checkAccessToken();
|
|
123
170
|
return resolve(accessToken);
|
|
124
171
|
}
|
|
125
172
|
catch (error) {
|
|
126
173
|
reject(error);
|
|
127
174
|
}
|
|
128
|
-
});
|
|
175
|
+
}));
|
|
129
176
|
});
|
|
130
177
|
}
|
|
131
178
|
loginUsingPkce(code) {
|
|
@@ -172,6 +219,34 @@ class AuthManager {
|
|
|
172
219
|
});
|
|
173
220
|
});
|
|
174
221
|
}
|
|
222
|
+
logout() {
|
|
223
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
224
|
+
return new Promise((resolve, reject) => {
|
|
225
|
+
try {
|
|
226
|
+
const bearerToken = localStorage.getItem('access_token');
|
|
227
|
+
localStorage.removeItem('access_token');
|
|
228
|
+
localStorage.removeItem('refresh_token');
|
|
229
|
+
fetch(`${this.authServer}auth/logout`, {
|
|
230
|
+
method: 'POST',
|
|
231
|
+
headers: {
|
|
232
|
+
'Content-Type': 'application/json',
|
|
233
|
+
'Authorization': `Bearer ${bearerToken}`,
|
|
234
|
+
},
|
|
235
|
+
}).then((response) => {
|
|
236
|
+
if (response.status !== 200) {
|
|
237
|
+
throw new Error('Failed to attempt logout');
|
|
238
|
+
}
|
|
239
|
+
resolve();
|
|
240
|
+
}).catch((error) => {
|
|
241
|
+
reject(error);
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
catch (error) {
|
|
245
|
+
reject(error);
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
}
|
|
175
250
|
static validateToken(authServer, bearerToken) {
|
|
176
251
|
return __awaiter(this, void 0, void 0, function* () {
|
|
177
252
|
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
|
package/package.json
CHANGED
package/src/AuthManager.ts
CHANGED
|
@@ -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,74 @@ 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
|
+
private async checkAccessToken(): Promise<string> {
|
|
95
|
+
return new Promise(async (resolve, reject) => {
|
|
96
|
+
try {
|
|
97
|
+
const accessToken: string | null = localStorage.getItem('access_token');
|
|
98
|
+
if (!accessToken) {
|
|
99
|
+
throw new Error('No access token found');
|
|
100
|
+
}
|
|
101
|
+
// decode access token and check if it's expired
|
|
102
|
+
const decodedToken = accessToken
|
|
103
|
+
? JSON.parse(atob(accessToken.split('.')[1]))
|
|
104
|
+
: null;
|
|
105
|
+
if (decodedToken) {
|
|
106
|
+
const currentTime = Date.now() / 1000;
|
|
107
|
+
if (decodedToken.exp < currentTime) {
|
|
108
|
+
// refreshing expired token
|
|
109
|
+
const newAccessToken = await this.refreshAccessToken();
|
|
110
|
+
return resolve(newAccessToken);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
resolve(accessToken);
|
|
114
|
+
} catch (error) {
|
|
115
|
+
reject(error);
|
|
116
|
+
}
|
|
117
|
+
})
|
|
118
|
+
}
|
|
119
|
+
|
|
53
120
|
public async mustBeLoggedIn(): Promise<boolean> {
|
|
54
121
|
return new Promise((resolve, reject) => {
|
|
55
122
|
this.isLoggedIn().then((isLoggedIn) => {
|
|
@@ -75,25 +142,12 @@ export class AuthManager {
|
|
|
75
142
|
`&redirect_uri=${encodeURIComponent(this.redirectUri)}&code_challenge=${codeChallenge}&code_challenge_method=S256`
|
|
76
143
|
}
|
|
77
144
|
}
|
|
145
|
+
|
|
78
146
|
public async isLoggedIn(): Promise<boolean> {
|
|
79
147
|
// todo here: check if refresh token is expired and if so, try to refresh, then update token
|
|
80
|
-
return new Promise((resolve, reject) => {
|
|
148
|
+
return new Promise(async (resolve, reject) => {
|
|
81
149
|
try {
|
|
82
|
-
|
|
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
|
-
|
|
150
|
+
await this.checkAccessToken();
|
|
97
151
|
return resolve(true);
|
|
98
152
|
} catch (error) {
|
|
99
153
|
reject(error);
|
|
@@ -104,22 +158,9 @@ export class AuthManager {
|
|
|
104
158
|
public async getAccessToken(): Promise<string> {
|
|
105
159
|
// todo here: check if refresh token is expired and if so, try to refresh, then update token
|
|
106
160
|
// otherwise throw error
|
|
107
|
-
return new Promise((resolve, reject) => {
|
|
161
|
+
return new Promise(async (resolve, reject) => {
|
|
108
162
|
try {
|
|
109
|
-
const accessToken
|
|
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
|
-
}
|
|
163
|
+
const accessToken = await this.checkAccessToken();
|
|
123
164
|
|
|
124
165
|
return resolve(accessToken);
|
|
125
166
|
} catch (error) {
|
|
@@ -170,6 +211,32 @@ export class AuthManager {
|
|
|
170
211
|
});
|
|
171
212
|
}
|
|
172
213
|
|
|
214
|
+
public async logout(): Promise<void> {
|
|
215
|
+
return new Promise((resolve, reject) => {
|
|
216
|
+
try {
|
|
217
|
+
const bearerToken = localStorage.getItem('access_token');
|
|
218
|
+
localStorage.removeItem('access_token');
|
|
219
|
+
localStorage.removeItem('refresh_token');
|
|
220
|
+
fetch(`${this.authServer}auth/logout`, {
|
|
221
|
+
method: 'POST',
|
|
222
|
+
headers: {
|
|
223
|
+
'Content-Type': 'application/json',
|
|
224
|
+
'Authorization': `Bearer ${bearerToken}`,
|
|
225
|
+
},
|
|
226
|
+
}).then((response) => {
|
|
227
|
+
if (response.status !== 200) {
|
|
228
|
+
throw new Error('Failed to attempt logout')
|
|
229
|
+
}
|
|
230
|
+
resolve();
|
|
231
|
+
}).catch((error) => {
|
|
232
|
+
reject(error);
|
|
233
|
+
})
|
|
234
|
+
} catch (error) {
|
|
235
|
+
reject(error);
|
|
236
|
+
}
|
|
237
|
+
})
|
|
238
|
+
}
|
|
239
|
+
|
|
173
240
|
public static async validateToken(authServer: string, bearerToken: string): Promise<boolean> {
|
|
174
241
|
return new Promise<boolean>(async (resolve, reject) => {
|
|
175
242
|
try {
|