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 +11 -0
- package/dist/AuthManager.d.ts +3 -0
- package/dist/AuthManager.js +102 -32
- package/package.json +1 -1
- package/src/AuthManager.ts +97 -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,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
|
-
|
|
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 =
|
|
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
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,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
|
-
|
|
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
|
|
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 {
|