curtain-web-api 1.0.77 → 1.0.79

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.
@@ -98,16 +98,25 @@ export interface CreatePermanentLinkRequest {
98
98
  requested_expiry_months?: number;
99
99
  reason?: string;
100
100
  }
101
+ export interface AccessibleCurtain {
102
+ id: number;
103
+ link_id: string;
104
+ description: string;
105
+ created: string;
106
+ curtain_type: string;
107
+ }
101
108
  export interface CurtainCollection {
102
109
  id: number;
103
110
  created: string;
104
111
  updated: string;
105
112
  name: string;
106
113
  description: string;
114
+ enable: boolean;
107
115
  owner: number;
108
116
  owner_username: string;
109
117
  curtains: number[];
110
118
  curtain_count: number;
119
+ accessible_curtains: AccessibleCurtain[];
111
120
  }
112
121
  export interface CreateCurtainCollection {
113
122
  name: string;
@@ -117,6 +126,7 @@ export interface CreateCurtainCollection {
117
126
  export interface UpdateCurtainCollection {
118
127
  name?: string;
119
128
  description?: string;
129
+ enable?: boolean;
120
130
  curtains?: number[];
121
131
  }
122
132
  export interface AddCurtainToCollection {
@@ -257,7 +267,7 @@ export declare class CurtainWebAPI {
257
267
  count: number;
258
268
  results: CurtainCollection[];
259
269
  }, any, {}>>;
260
- getCurtainCollection(id: number): Promise<import("axios").AxiosResponse<CurtainCollection, any, {}>>;
270
+ getCurtainCollection(id: number, curtainType?: string): Promise<import("axios").AxiosResponse<CurtainCollection, any, {}>>;
261
271
  getCurtainCollectionSessions(id: number): Promise<import("axios").AxiosResponse<{
262
272
  collection_id: number;
263
273
  collection_name: string;
@@ -1,6 +1,6 @@
1
1
  import { User } from "./curtain-user";
2
2
  import axios, { AxiosHeaders } from "axios";
3
- import { arrayBufferToBase64String, base64ToArrayBuffer, encryptAESData, encryptAESKey, exportAESKey, generateAESKey } from "./curtain-encryption";
3
+ import { arrayBufferToBase64String, base64ToArrayBuffer, decodeJWTPayload, encryptAESData, encryptAESKey, exportAESKey, generateAESKey } from "./curtain-encryption";
4
4
  const base = "https://celsus.muttsu.xyz/";
5
5
  export const replacer = (key, value) => {
6
6
  if (value instanceof Map) {
@@ -175,6 +175,12 @@ export class CurtainWebAPI {
175
175
  });
176
176
  }
177
177
  checkIfRefreshTokenExpired() {
178
+ if (this.user.refresh_token) {
179
+ const payload = decodeJWTPayload(this.user.refresh_token);
180
+ if (payload && payload.exp) {
181
+ return Date.now() >= payload.exp * 1000;
182
+ }
183
+ }
178
184
  let now = new Date();
179
185
  let diff = (now.getTime() - this.user.lastRefreshTokenUpdate.getTime()) / 1000;
180
186
  diff = diff / 60 / 60;
@@ -957,10 +963,14 @@ export class CurtainWebAPI {
957
963
  }
958
964
  return this.axiosInstance.get(this.baseURL + "curtain-collections/", { headers: headers, params: params, responseType: "json" });
959
965
  }
960
- getCurtainCollection(id) {
966
+ getCurtainCollection(id, curtainType) {
961
967
  let headers = new AxiosHeaders();
962
968
  headers["Accept"] = "application/json";
963
- return this.axiosInstance.get(this.baseURL + "curtain-collections/" + id + "/", { headers: headers, responseType: "json" });
969
+ let params = new URLSearchParams();
970
+ if (curtainType) {
971
+ params.append("curtain_type", curtainType);
972
+ }
973
+ return this.axiosInstance.get(this.baseURL + "curtain-collections/" + id + "/", { headers: headers, params: params, responseType: "json" });
964
974
  }
965
975
  getCurtainCollectionSessions(id) {
966
976
  let headers = new AxiosHeaders();
@@ -37,3 +37,4 @@ export declare function encryptDataAES(data: string, publicKey: CryptoKey): Prom
37
37
  }>;
38
38
  export declare function decryptDataAES(encryptedKey: ArrayBuffer, encryptedData: string, iv: string, privateKey: CryptoKey): Promise<string>;
39
39
  export declare function hashData(data: string): Promise<string>;
40
+ export declare function decodeJWTPayload(token: string): any;
@@ -137,3 +137,24 @@ export async function hashData(data) {
137
137
  const hash = await crypto.subtle.digest("SHA-256", encoded);
138
138
  return arrayBufferToBase64String(hash);
139
139
  }
140
+ export function decodeJWTPayload(token) {
141
+ try {
142
+ const parts = token.split('.');
143
+ if (parts.length !== 3) {
144
+ return null;
145
+ }
146
+ const base64Url = parts[1];
147
+ let base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
148
+ const pad = base64.length % 4;
149
+ if (pad) {
150
+ base64 += new Array(5 - pad).join('=');
151
+ }
152
+ const jsonPayload = decodeURIComponent(atob(base64).split('').map(function (c) {
153
+ return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
154
+ }).join(''));
155
+ return JSON.parse(jsonPayload);
156
+ }
157
+ catch (e) {
158
+ return null;
159
+ }
160
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "curtain-web-api",
3
- "version": "1.0.77",
3
+ "version": "1.0.79",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "main": "./build/index.js",
@@ -3,6 +3,7 @@ import axios, {AxiosHeaders} from "axios";
3
3
  import {
4
4
  arrayBufferToBase64String, base64ToArrayBuffer,
5
5
  CurtainEncryption,
6
+ decodeJWTPayload,
6
7
  encryptAESData, encryptAESKey,
7
8
  encryptDataRSA, exportAESKey,
8
9
  generateAESKey
@@ -135,16 +136,26 @@ export interface CreatePermanentLinkRequest {
135
136
  reason?: string;
136
137
  }
137
138
 
139
+ export interface AccessibleCurtain {
140
+ id: number;
141
+ link_id: string;
142
+ description: string;
143
+ created: string;
144
+ curtain_type: string;
145
+ }
146
+
138
147
  export interface CurtainCollection {
139
148
  id: number;
140
149
  created: string;
141
150
  updated: string;
142
151
  name: string;
143
152
  description: string;
153
+ enable: boolean;
144
154
  owner: number;
145
155
  owner_username: string;
146
156
  curtains: number[];
147
157
  curtain_count: number;
158
+ accessible_curtains: AccessibleCurtain[];
148
159
  }
149
160
 
150
161
  export interface CreateCurtainCollection {
@@ -156,6 +167,7 @@ export interface CreateCurtainCollection {
156
167
  export interface UpdateCurtainCollection {
157
168
  name?: string;
158
169
  description?: string;
170
+ enable?: boolean;
159
171
  curtains?: number[];
160
172
  }
161
173
 
@@ -357,6 +369,12 @@ export class CurtainWebAPI {
357
369
  }
358
370
 
359
371
  checkIfRefreshTokenExpired() {
372
+ if (this.user.refresh_token) {
373
+ const payload = decodeJWTPayload(this.user.refresh_token);
374
+ if (payload && payload.exp) {
375
+ return Date.now() >= payload.exp * 1000;
376
+ }
377
+ }
360
378
  let now = new Date();
361
379
  let diff = (now.getTime() - this.user.lastRefreshTokenUpdate.getTime()) / 1000;
362
380
  diff = diff/60/60;
@@ -1239,10 +1257,14 @@ export class CurtainWebAPI {
1239
1257
  return this.axiosInstance.get<{count: number, results: CurtainCollection[]}>(this.baseURL + "curtain-collections/", {headers: headers, params: params, responseType: "json"});
1240
1258
  }
1241
1259
 
1242
- getCurtainCollection(id: number) {
1260
+ getCurtainCollection(id: number, curtainType?: string) {
1243
1261
  let headers = new AxiosHeaders();
1244
1262
  headers["Accept"] = "application/json";
1245
- return this.axiosInstance.get<CurtainCollection>(this.baseURL + "curtain-collections/" + id + "/", {headers: headers, responseType: "json"});
1263
+ let params = new URLSearchParams();
1264
+ if (curtainType) {
1265
+ params.append("curtain_type", curtainType);
1266
+ }
1267
+ return this.axiosInstance.get<CurtainCollection>(this.baseURL + "curtain-collections/" + id + "/", {headers: headers, params: params, responseType: "json"});
1246
1268
  }
1247
1269
 
1248
1270
  getCurtainCollectionSessions(id: number) {
@@ -1,176 +1,198 @@
1
- export function encryptDataRSA(data: string, publicKey: CryptoKey) {
2
- return crypto.subtle.encrypt({name: 'RSA-OAEP'}, publicKey, new TextEncoder().encode(data))
3
- }
4
-
5
- export function decryptDataRSA(data: string, privateKey: CryptoKey) {
6
- return crypto.subtle.decrypt({name: 'RSA-OAEP'}, privateKey, new TextEncoder().encode(data))
7
- }
8
-
9
- export async function importPublicKey(key: ArrayBuffer) {
10
- return await crypto.subtle.importKey('spki', key, {name: 'RSA-OAEP', hash: 'SHA-256'}, true, ['encrypt'])
11
- }
12
-
13
- export async function importPrivateKey(key: ArrayBuffer) {
14
- return await crypto.subtle.importKey('pkcs8', key, {name: 'RSA-OAEP', hash: 'SHA-256'}, true, ['decrypt'])
15
- }
16
-
17
- export async function generateKeyPair(modulusLength: number = 2048, publicExponent: Uint8Array = new Uint8Array([1, 0, 1])) {
18
- return await crypto.subtle.generateKey(
19
- {
20
- name: "RSA-OAEP",
21
- modulusLength: modulusLength,
22
- publicExponent: publicExponent,
23
- hash: "SHA-256",
24
- } as RsaHashedKeyGenParams,
25
- true,
26
- ["encrypt", "decrypt"],
27
- )
28
- }
29
-
30
- export async function exportPublicKey(key: CryptoKey) {
31
- return await crypto.subtle.exportKey('spki', key)
32
- }
33
-
34
- export async function exportPrivateKey(key: CryptoKey) {
35
- return await crypto.subtle.exportKey('pkcs8', key)
36
- }
37
-
38
- export async function importKey(key: ArrayBuffer, type: "public"|"private") {
39
- if (type === "public") {
40
- return await importPublicKey(key)
41
- } else {
42
- return await importPrivateKey(key)
43
- }
44
- }
45
-
46
- export async function exportKeyString(key: CryptoKey, type: "public"|"private"): Promise<string> {
47
- if (type === "public") {
48
- const k = await exportPublicKey(key)
49
- return arrayBufferToBase64String(k)
50
- } else {
51
- const k = await exportPrivateKey(key)
52
- return arrayBufferToBase64String(k)
53
- }
54
- }
55
-
56
- export async function saveToLocalStorage(key: CryptoKey, type: "public"|"private") {
57
- const k = await exportKeyString(key, type)
58
- localStorage.setItem(type + "_key", k)
59
- }
60
-
61
- export async function loadFromLocalStorage(type: "public"|"private") {
62
- const k = localStorage.getItem(type + "_key")
63
- if (k) {
64
- return await importKey(pemToArrayBuffer(k), type)
65
- }
66
- return undefined
67
- }
68
-
69
- export interface CurtainEncryption {
70
- encrypted: boolean,
71
- publicKey?: CryptoKey,
72
- e2e: boolean,
73
- }
74
-
75
- export function arrayBufferToBase64String(arrayBuffer: ArrayBuffer) {
76
- const byteArray = new Uint8Array(arrayBuffer)
77
- let byteString = ''
78
- for (let i=0; i<byteArray.byteLength; i++) {
79
- byteString += String.fromCharCode(byteArray[i])
80
- }
81
- return btoa(byteString)
82
- }
83
-
84
- export function removeLines(str_data: string) {
85
- return str_data.replace("\n", "");
86
- }
87
-
88
- export function base64ToArrayBuffer(b64: string): ArrayBuffer {
89
- const byteString = atob(b64);
90
- const byteArray = new Uint8Array(byteString.length);
91
- for(let i=0; i < byteString.length; i++) {
92
- byteArray[i] = byteString.charCodeAt(i);
93
- }
94
-
95
- return byteArray.buffer;
96
- }
97
-
98
- export function pemToArrayBuffer(pem: string) {
99
- const b64Lines = removeLines(pem);
100
- let b64Prefix = b64Lines.replace('-----BEGIN PRIVATE KEY-----', '');
101
- b64Prefix = b64Prefix.replace('-----BEGIN PUBLIC KEY-----', '');
102
- let b64Final = b64Prefix.replace('-----END PRIVATE KEY-----', '');
103
- b64Final = b64Final.replace('-----END PUBLIC KEY-----', '');
104
-
105
- return base64ToArrayBuffer(b64Final);
106
- }
107
-
108
- // a function to generate to encrypt an aes key arraybuffer with a public key
109
- export async function encryptAESKey(publicKey: CryptoKey, aesKey: ArrayBuffer) {
110
- return await crypto.subtle.encrypt({name: "RSA-OAEP"}, publicKey, aesKey)
111
- }
112
-
113
- // a function to generate an aes key in GCM mode with a length of 256 bits
114
- export async function generateAESKey() {
115
- return await crypto.subtle.generateKey(
116
- {
117
- name: "AES-GCM",
118
- length: 256,
119
- },
120
- true,
121
- ["encrypt", "decrypt"],
122
- )
123
- }
124
-
125
- // a function to encrypt a string with an aes key
126
- export async function encryptAESData(aesKey: CryptoKey, data: string) {
127
- const iv = crypto.getRandomValues(new Uint8Array(12))
128
- const enc = new TextEncoder()
129
- const encoded = enc.encode(data)
130
- const encrypted = await crypto.subtle.encrypt({name: "AES-GCM", iv: iv}, aesKey, encoded)
131
- return {encrypted: arrayBufferToBase64String(encrypted), iv: arrayBufferToBase64String(iv.buffer)}
132
- }
133
-
134
- // a function to decrypt a string with an aes key
135
- export async function decryptAESData(aesKey: CryptoKey, data: string, iv: string) {
136
- const dec = new TextDecoder()
137
- const decrypted = await crypto.subtle.decrypt({name: "AES-GCM", iv: base64ToArrayBuffer(iv)}, aesKey, base64ToArrayBuffer(data))
138
- return dec.decode(decrypted)
139
- }
140
-
141
- // a function to decrypt an aes key with a private key
142
- export async function decryptAESKey(privateKey: CryptoKey, encryptedKey: ArrayBuffer) {
143
- return await crypto.subtle.decrypt({name: "RSA-OAEP"}, privateKey, encryptedKey)
144
- }
145
-
146
- // a function to export an aes key to a string
147
- export async function exportAESKey(key: CryptoKey) {
148
- return await crypto.subtle.exportKey("raw", key)
149
- }
150
- // a function to import an aes key from a string
151
- export async function importAESKey(key: ArrayBuffer) {
152
- return await crypto.subtle.importKey("raw", key, "AES-GCM", true, ["encrypt", "decrypt"])
153
- }
154
-
155
- // a function to encrypt aes key with a public key and also use the aes key to encrypt a large string then return the encrypted aes key and the encrypted string
156
- export async function encryptDataAES(data: string, publicKey: CryptoKey) {
157
- const aesKey = await generateAESKey()
158
- const encryptedKey = await encryptAESKey(publicKey, await exportAESKey(aesKey))
159
- const encryptedData = await encryptAESData(aesKey, data)
160
- return {encryptedKey: arrayBufferToBase64String(encryptedKey), encryptedData: encryptedData}
161
- }
162
-
163
- // a function to decrypt an aes key with a private key and use the aes key to decrypt a large string
164
- export async function decryptDataAES(encryptedKey: ArrayBuffer, encryptedData: string, iv: string, privateKey: CryptoKey) {
165
- const aesKey = await decryptAESKey(privateKey, encryptedKey)
166
- //import aes key
167
- return await decryptAESData(await importAESKey(aesKey), encryptedData, iv)
168
- }
169
-
170
- // a function to calculate sha-256 hash of a large string
171
- export async function hashData(data: string) {
172
- const enc = new TextEncoder()
173
- const encoded = enc.encode(data)
174
- const hash = await crypto.subtle.digest("SHA-256", encoded)
175
- return arrayBufferToBase64String(hash)
176
- }
1
+ export function encryptDataRSA(data: string, publicKey: CryptoKey) {
2
+ return crypto.subtle.encrypt({name: 'RSA-OAEP'}, publicKey, new TextEncoder().encode(data))
3
+ }
4
+
5
+ export function decryptDataRSA(data: string, privateKey: CryptoKey) {
6
+ return crypto.subtle.decrypt({name: 'RSA-OAEP'}, privateKey, new TextEncoder().encode(data))
7
+ }
8
+
9
+ export async function importPublicKey(key: ArrayBuffer) {
10
+ return await crypto.subtle.importKey('spki', key, {name: 'RSA-OAEP', hash: 'SHA-256'}, true, ['encrypt'])
11
+ }
12
+
13
+ export async function importPrivateKey(key: ArrayBuffer) {
14
+ return await crypto.subtle.importKey('pkcs8', key, {name: 'RSA-OAEP', hash: 'SHA-256'}, true, ['decrypt'])
15
+ }
16
+
17
+ export async function generateKeyPair(modulusLength: number = 2048, publicExponent: Uint8Array = new Uint8Array([1, 0, 1])) {
18
+ return await crypto.subtle.generateKey(
19
+ {
20
+ name: "RSA-OAEP",
21
+ modulusLength: modulusLength,
22
+ publicExponent: publicExponent,
23
+ hash: "SHA-256",
24
+ } as RsaHashedKeyGenParams,
25
+ true,
26
+ ["encrypt", "decrypt"],
27
+ )
28
+ }
29
+
30
+ export async function exportPublicKey(key: CryptoKey) {
31
+ return await crypto.subtle.exportKey('spki', key)
32
+ }
33
+
34
+ export async function exportPrivateKey(key: CryptoKey) {
35
+ return await crypto.subtle.exportKey('pkcs8', key)
36
+ }
37
+
38
+ export async function importKey(key: ArrayBuffer, type: "public"|"private") {
39
+ if (type === "public") {
40
+ return await importPublicKey(key)
41
+ } else {
42
+ return await importPrivateKey(key)
43
+ }
44
+ }
45
+
46
+ export async function exportKeyString(key: CryptoKey, type: "public"|"private"): Promise<string> {
47
+ if (type === "public") {
48
+ const k = await exportPublicKey(key)
49
+ return arrayBufferToBase64String(k)
50
+ } else {
51
+ const k = await exportPrivateKey(key)
52
+ return arrayBufferToBase64String(k)
53
+ }
54
+ }
55
+
56
+ export async function saveToLocalStorage(key: CryptoKey, type: "public"|"private") {
57
+ const k = await exportKeyString(key, type)
58
+ localStorage.setItem(type + "_key", k)
59
+ }
60
+
61
+ export async function loadFromLocalStorage(type: "public"|"private") {
62
+ const k = localStorage.getItem(type + "_key")
63
+ if (k) {
64
+ return await importKey(pemToArrayBuffer(k), type)
65
+ }
66
+ return undefined
67
+ }
68
+
69
+ export interface CurtainEncryption {
70
+ encrypted: boolean,
71
+ publicKey?: CryptoKey,
72
+ e2e: boolean,
73
+ }
74
+
75
+ export function arrayBufferToBase64String(arrayBuffer: ArrayBuffer) {
76
+ const byteArray = new Uint8Array(arrayBuffer)
77
+ let byteString = ''
78
+ for (let i=0; i<byteArray.byteLength; i++) {
79
+ byteString += String.fromCharCode(byteArray[i])
80
+ }
81
+ return btoa(byteString)
82
+ }
83
+
84
+ export function removeLines(str_data: string) {
85
+ return str_data.replace("\n", "");
86
+ }
87
+
88
+ export function base64ToArrayBuffer(b64: string): ArrayBuffer {
89
+ const byteString = atob(b64);
90
+ const byteArray = new Uint8Array(byteString.length);
91
+ for(let i=0; i < byteString.length; i++) {
92
+ byteArray[i] = byteString.charCodeAt(i);
93
+ }
94
+
95
+ return byteArray.buffer;
96
+ }
97
+
98
+ export function pemToArrayBuffer(pem: string) {
99
+ const b64Lines = removeLines(pem);
100
+ let b64Prefix = b64Lines.replace('-----BEGIN PRIVATE KEY-----', '');
101
+ b64Prefix = b64Prefix.replace('-----BEGIN PUBLIC KEY-----', '');
102
+ let b64Final = b64Prefix.replace('-----END PRIVATE KEY-----', '');
103
+ b64Final = b64Final.replace('-----END PUBLIC KEY-----', '');
104
+
105
+ return base64ToArrayBuffer(b64Final);
106
+ }
107
+
108
+ // a function to generate to encrypt an aes key arraybuffer with a public key
109
+ export async function encryptAESKey(publicKey: CryptoKey, aesKey: ArrayBuffer) {
110
+ return await crypto.subtle.encrypt({name: "RSA-OAEP"}, publicKey, aesKey)
111
+ }
112
+
113
+ // a function to generate an aes key in GCM mode with a length of 256 bits
114
+ export async function generateAESKey() {
115
+ return await crypto.subtle.generateKey(
116
+ {
117
+ name: "AES-GCM",
118
+ length: 256,
119
+ },
120
+ true,
121
+ ["encrypt", "decrypt"],
122
+ )
123
+ }
124
+
125
+ // a function to encrypt a string with an aes key
126
+ export async function encryptAESData(aesKey: CryptoKey, data: string) {
127
+ const iv = crypto.getRandomValues(new Uint8Array(12))
128
+ const enc = new TextEncoder()
129
+ const encoded = enc.encode(data)
130
+ const encrypted = await crypto.subtle.encrypt({name: "AES-GCM", iv: iv}, aesKey, encoded)
131
+ return {encrypted: arrayBufferToBase64String(encrypted), iv: arrayBufferToBase64String(iv.buffer)}
132
+ }
133
+
134
+ // a function to decrypt a string with an aes key
135
+ export async function decryptAESData(aesKey: CryptoKey, data: string, iv: string) {
136
+ const dec = new TextDecoder()
137
+ const decrypted = await crypto.subtle.decrypt({name: "AES-GCM", iv: base64ToArrayBuffer(iv)}, aesKey, base64ToArrayBuffer(data))
138
+ return dec.decode(decrypted)
139
+ }
140
+
141
+ // a function to decrypt an aes key with a private key
142
+ export async function decryptAESKey(privateKey: CryptoKey, encryptedKey: ArrayBuffer) {
143
+ return await crypto.subtle.decrypt({name: "RSA-OAEP"}, privateKey, encryptedKey)
144
+ }
145
+
146
+ // a function to export an aes key to a string
147
+ export async function exportAESKey(key: CryptoKey) {
148
+ return await crypto.subtle.exportKey ("raw", key)
149
+ }
150
+ // a function to import an aes key from a string
151
+ export async function importAESKey(key: ArrayBuffer) {
152
+ return await crypto.subtle.importKey("raw", key, "AES-GCM", true, ["encrypt", "decrypt"])
153
+ }
154
+
155
+ // a function to encrypt aes key with a public key and also use the aes key to encrypt a large string then return the encrypted aes key and the encrypted string
156
+ export async function encryptDataAES(data: string, publicKey: CryptoKey) {
157
+ const aesKey = await generateAESKey()
158
+ const encryptedKey = await encryptAESKey(publicKey, await exportAESKey(aesKey))
159
+ const encryptedData = await encryptAESData(aesKey, data)
160
+ return {encryptedKey: arrayBufferToBase64String(encryptedKey), encryptedData: encryptedData}
161
+ }
162
+
163
+ // a function to decrypt an aes key with a private key and use the aes key to decrypt a large string
164
+ export async function decryptDataAES(encryptedKey: ArrayBuffer, encryptedData: string, iv: string, privateKey: CryptoKey) {
165
+ const aesKey = await decryptAESKey(privateKey, encryptedKey)
166
+ //import aes key
167
+ return await decryptAESData(await importAESKey(aesKey), encryptedData, iv)
168
+ }
169
+
170
+ // a function to calculate sha-256 hash of a large string
171
+ export async function hashData(data: string) {
172
+ const enc = new TextEncoder()
173
+ const encoded = enc.encode(data)
174
+ const hash = await crypto.subtle.digest("SHA-256", encoded)
175
+ return arrayBufferToBase64String(hash)
176
+ }
177
+
178
+ export function decodeJWTPayload(token: string) {
179
+ try {
180
+ const parts = token.split('.');
181
+ if (parts.length !== 3) {
182
+ return null;
183
+ }
184
+ const base64Url = parts[1];
185
+ let base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
186
+ const pad = base64.length % 4;
187
+ if (pad) {
188
+ base64 += new Array(5 - pad).join('=');
189
+ }
190
+ const jsonPayload = decodeURIComponent(atob(base64).split('').map(function(c) {
191
+ return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
192
+ }).join(''));
193
+
194
+ return JSON.parse(jsonPayload);
195
+ } catch (e) {
196
+ return null;
197
+ }
198
+ }