supaapps-auth 1.0.6 → 1.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.
package/README.md ADDED
@@ -0,0 +1,74 @@
1
+ # TODO
2
+
3
+
4
+ `npm i supaapps-auth`
5
+
6
+
7
+ - Initialize
8
+ ```ts
9
+ new AuthManager(
10
+ 'https://supaapps-auth-api.testing.sacl.io/',
11
+ 'root',
12
+ 'http://localhost:3001/exchange',
13
+ () => {
14
+ // redirect to login
15
+ }
16
+ );
17
+ ```
18
+
19
+
20
+ - Require login
21
+
22
+ ```ts
23
+ import {AuthManager} from "./AuthManager";
24
+
25
+ const authManager = AuthManager.getInstance();
26
+ authManager.mustBeLoggedIn().then((isLoggedIn) => {
27
+ if (isLoggedIn) {
28
+ // do something
29
+ }
30
+ });
31
+
32
+ // or
33
+
34
+ AuthManager.getInstance().mustBeLoggedIn();
35
+ ```
36
+
37
+
38
+ - Get user info
39
+
40
+ ```ts
41
+ authManager.mustBeLoggedIn().then(
42
+ (isLoggedIn) => {
43
+ isLoggedIn && authManager.getAccessToken().then(
44
+ (token) => {
45
+ const decodedToken = JSON.parse(atob(token.split('.')[1]));
46
+ // access info for example
47
+ // decodedToken.first_name
48
+ }
49
+ );
50
+ }
51
+ )
52
+ ```
53
+
54
+
55
+
56
+ Get login uri
57
+
58
+ ```ts
59
+ AuthManager.getInstance().getLoginWithGoogleUri()
60
+ ```
61
+
62
+ - Validate access token
63
+
64
+ ```typescript
65
+ import { AuthManager } from './AuthManager';
66
+
67
+ const isValid = await AuthManager.validateToken(BEARER_HEADER_OR_ACCESS_TOKEN)
68
+ // or
69
+ AuthManager.validateToken(BEARER_OR_ACCESS_TOKEN).then((isValid) => {
70
+ if (isValid) {
71
+ // token is valid
72
+ }
73
+ })
74
+ ```
@@ -13,4 +13,5 @@ export declare class AuthManager {
13
13
  isLoggedIn(): Promise<boolean>;
14
14
  getAccessToken(): Promise<string>;
15
15
  loginUsingPkce(code: any): Promise<void>;
16
+ static validateToken(authServer: string, bearerToken: string): Promise<boolean>;
16
17
  }
@@ -10,6 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.AuthManager = void 0;
13
+ const axios_1 = require("axios");
13
14
  const crypto_1 = require("crypto");
14
15
  class AuthManager {
15
16
  constructor(authServer, realmName, redirectUri, loginCallback) {
@@ -171,6 +172,40 @@ class AuthManager {
171
172
  });
172
173
  });
173
174
  }
175
+ static validateToken(authServer, bearerToken) {
176
+ return __awaiter(this, void 0, void 0, function* () {
177
+ return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
178
+ try {
179
+ const accessToken = bearerToken.includes('Bearer ') ? bearerToken.replace('Bearer ', '') : bearerToken;
180
+ const decodedToken = accessToken ? JSON.parse(atob(accessToken.split('.')[1])) : null;
181
+ if (!decodedToken) {
182
+ return resolve(false);
183
+ }
184
+ const currentTime = Date.now() / 1000;
185
+ if (decodedToken.exp < currentTime) {
186
+ return resolve(false);
187
+ }
188
+ const { data: publicKey } = yield axios_1.default.get(`${authServer}public/public_key`);
189
+ const { data: algo } = yield axios_1.default.get(`${authServer}public/algo`);
190
+ const jwt = require('jsonwebtoken');
191
+ jwt.verify(accessToken, publicKey, { algorithms: [algo] }, (error, payload) => {
192
+ if (error) {
193
+ return resolve(false);
194
+ }
195
+ axios_1.default.get(`${authServer}public/revoked_ids`).then(({ data: revokedIds }) => {
196
+ if (revokedIds && revokedIds.includes(decodedToken['id'])) {
197
+ return resolve(false);
198
+ }
199
+ return resolve(true);
200
+ });
201
+ });
202
+ }
203
+ catch (error) {
204
+ reject(error);
205
+ }
206
+ }));
207
+ });
208
+ }
174
209
  }
175
210
  exports.AuthManager = AuthManager;
176
211
  AuthManager.instance = null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "supaapps-auth",
3
- "version": "1.0.6",
3
+ "version": "1.1.0",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -12,7 +12,8 @@
12
12
  "license": "MIT",
13
13
  "dependencies": {
14
14
  "axios": "^1.6.7",
15
- "crypto": "^1.0.1"
15
+ "crypto": "^1.0.1",
16
+ "jsonwebtoken": "^9.0.2"
16
17
  },
17
18
  "devDependencies": {
18
19
  "@types/node": "^20.11.10",
@@ -28,9 +28,9 @@ export class AuthManager {
28
28
 
29
29
  private toBase64Url = (base64String: string) => {
30
30
  return base64String
31
- .replace(/\+/g, '-')
32
- .replace(/\//g, '_')
33
- .replace(/=+$/, '');
31
+ .replace(/\+/g, '-')
32
+ .replace(/\//g, '_')
33
+ .replace(/=+$/, '');
34
34
  };
35
35
  private generatePKCEPair = () => {
36
36
  const NUM_OF_BYTES = 32; // This will generate a verifier of sufficient length
@@ -38,13 +38,13 @@ export class AuthManager {
38
38
 
39
39
  // Generate code verifier
40
40
  const newCodeVerifier = this.toBase64Url(
41
- randomBytes(NUM_OF_BYTES).toString('base64'),
41
+ randomBytes(NUM_OF_BYTES).toString('base64'),
42
42
  );
43
43
 
44
44
  // Generate code challenge
45
45
  const hash = createHash(HASH_ALG)
46
- .update(newCodeVerifier)
47
- .digest('base64');
46
+ .update(newCodeVerifier)
47
+ .digest('base64');
48
48
  const newCodeChallenge = this.toBase64Url(hash);
49
49
 
50
50
  return { newCodeVerifier, newCodeChallenge };
@@ -72,7 +72,7 @@ export class AuthManager {
72
72
 
73
73
  if (this.authServer && this.realmName && this.redirectUri) {
74
74
  return `${this.authServer}auth/login_with_google?realm_name=${this.realmName}` +
75
- `&redirect_uri=${encodeURIComponent(this.redirectUri)}&code_challenge=${codeChallenge}&code_challenge_method=S256`
75
+ `&redirect_uri=${encodeURIComponent(this.redirectUri)}&code_challenge=${codeChallenge}&code_challenge_method=S256`
76
76
  }
77
77
  }
78
78
  public async isLoggedIn(): Promise<boolean> {
@@ -145,24 +145,24 @@ export class AuthManager {
145
145
  code_verifier: codeVerifier,
146
146
  }),
147
147
  })
148
- .then((response) => {
149
- localStorage.removeItem('codeVerifier');
150
- localStorage.removeItem('codeChallenge');
151
- if (response.status !== 200) {
152
- throw new Error('Failed to exchange code for token');
153
- }
154
- return response.json();
155
- })
156
- .then((exchangeJson) => {
157
- localStorage.setItem('access_token', exchangeJson.access_token);
158
- localStorage.setItem('refresh_token', exchangeJson.refresh_token);
159
- resolve();
160
- })
161
- .catch((error) => {
162
- localStorage.removeItem('codeVerifier');
163
- localStorage.removeItem('codeChallenge');
164
- reject(error);
165
- });
148
+ .then((response) => {
149
+ localStorage.removeItem('codeVerifier');
150
+ localStorage.removeItem('codeChallenge');
151
+ if (response.status !== 200) {
152
+ throw new Error('Failed to exchange code for token');
153
+ }
154
+ return response.json();
155
+ })
156
+ .then((exchangeJson) => {
157
+ localStorage.setItem('access_token', exchangeJson.access_token);
158
+ localStorage.setItem('refresh_token', exchangeJson.refresh_token);
159
+ resolve();
160
+ })
161
+ .catch((error) => {
162
+ localStorage.removeItem('codeVerifier');
163
+ localStorage.removeItem('codeChallenge');
164
+ reject(error);
165
+ });
166
166
  }
167
167
  } catch (error) {
168
168
  reject(error);
@@ -170,4 +170,38 @@ export class AuthManager {
170
170
  });
171
171
  }
172
172
 
173
+ public static async validateToken(authServer: string, bearerToken: string): Promise<boolean> {
174
+ return new Promise<boolean>(async (resolve, reject) => {
175
+ try {
176
+ const accessToken = bearerToken.includes('Bearer ') ? bearerToken.replace('Bearer ', '') : bearerToken;
177
+ const decodedToken = accessToken ? JSON.parse(atob(accessToken.split('.')[1])) : null;
178
+
179
+ if (!decodedToken) {
180
+ return resolve(false);
181
+ }
182
+
183
+ const currentTime = Date.now() / 1000;
184
+ if (decodedToken.exp < currentTime) {
185
+ return resolve(false);
186
+ }
187
+
188
+ const { data: publicKey } = await axios.get(`${authServer}public/public_key`);
189
+ const { data: algo } = await axios.get(`${authServer}public/algo`);
190
+ const jwt = require('jsonwebtoken');
191
+ jwt.verify(accessToken, publicKey, { algorithms: [algo] }, (error, payload) => {
192
+ if (error) {
193
+ return resolve(false);
194
+ }
195
+ axios.get(`${authServer}public/revoked_ids`).then(({ data: revokedIds }) => {
196
+ if (revokedIds && (revokedIds as number[]).includes(decodedToken['id'])) {
197
+ return resolve(false);
198
+ }
199
+ return resolve(true);
200
+ });
201
+ });
202
+ } catch (error) {
203
+ reject(error);
204
+ }
205
+ })
206
+ }
173
207
  }