@cloudsignal/pwa-sdk 1.1.1 → 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 +67 -1
- package/dist/chunk-IMM7VF4N.js +97 -0
- package/dist/chunk-IMM7VF4N.js.map +1 -0
- package/dist/hmac-LWLR6F7Z.js +3 -0
- package/dist/hmac-LWLR6F7Z.js.map +1 -0
- package/dist/index.cjs +296 -100
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +150 -9
- package/dist/index.d.ts +150 -9
- package/dist/index.global.js +7 -7
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +182 -101
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -7,6 +7,114 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
7
7
|
* https://cloudsignal.io
|
|
8
8
|
* MIT License
|
|
9
9
|
*/
|
|
10
|
+
var __defProp = Object.defineProperty;
|
|
11
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
12
|
+
var __esm = (fn, res) => function __init() {
|
|
13
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
14
|
+
};
|
|
15
|
+
var __export = (target, all) => {
|
|
16
|
+
for (var name in all)
|
|
17
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
// src/utils/hmac.ts
|
|
21
|
+
var hmac_exports = {};
|
|
22
|
+
__export(hmac_exports, {
|
|
23
|
+
generateAuthHeaders: () => generateAuthHeaders,
|
|
24
|
+
generateHMACSignature: () => generateHMACSignature,
|
|
25
|
+
isValidUUID: () => isValidUUID,
|
|
26
|
+
makeAuthenticatedRequest: () => makeAuthenticatedRequest
|
|
27
|
+
});
|
|
28
|
+
function toHex(buffer) {
|
|
29
|
+
return Array.from(new Uint8Array(buffer)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
30
|
+
}
|
|
31
|
+
function toBase64(buffer) {
|
|
32
|
+
return btoa(String.fromCharCode(...new Uint8Array(buffer)));
|
|
33
|
+
}
|
|
34
|
+
async function generateHMACSignature(secret, organizationId, timestamp, method, url, body = "") {
|
|
35
|
+
const encoder = new TextEncoder();
|
|
36
|
+
let path;
|
|
37
|
+
let query;
|
|
38
|
+
try {
|
|
39
|
+
const urlObj = url.startsWith("http") ? new URL(url) : new URL(url, "https://pwa.cloudsignal.app");
|
|
40
|
+
path = urlObj.pathname;
|
|
41
|
+
query = urlObj.search ? urlObj.search.slice(1) : "";
|
|
42
|
+
} catch {
|
|
43
|
+
const queryIndex = url.indexOf("?");
|
|
44
|
+
if (queryIndex > -1) {
|
|
45
|
+
path = url.slice(0, queryIndex);
|
|
46
|
+
query = url.slice(queryIndex + 1);
|
|
47
|
+
} else {
|
|
48
|
+
path = url;
|
|
49
|
+
query = "";
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
const canonicalParts = [
|
|
53
|
+
method.toUpperCase(),
|
|
54
|
+
path,
|
|
55
|
+
query,
|
|
56
|
+
organizationId,
|
|
57
|
+
timestamp
|
|
58
|
+
];
|
|
59
|
+
if (body && body.length > 0) {
|
|
60
|
+
const bodyHashBuffer = await crypto.subtle.digest("SHA-256", encoder.encode(body));
|
|
61
|
+
const bodyHashHex = toHex(bodyHashBuffer);
|
|
62
|
+
canonicalParts.push(bodyHashHex);
|
|
63
|
+
}
|
|
64
|
+
const canonicalString = canonicalParts.join("\n");
|
|
65
|
+
const key = await crypto.subtle.importKey(
|
|
66
|
+
"raw",
|
|
67
|
+
encoder.encode(secret),
|
|
68
|
+
{ name: "HMAC", hash: "SHA-256" },
|
|
69
|
+
false,
|
|
70
|
+
["sign"]
|
|
71
|
+
);
|
|
72
|
+
const signature = await crypto.subtle.sign("HMAC", key, encoder.encode(canonicalString));
|
|
73
|
+
return toBase64(signature);
|
|
74
|
+
}
|
|
75
|
+
async function generateAuthHeaders(organizationId, organizationSecret, method, url, body) {
|
|
76
|
+
const timestamp = Math.floor(Date.now() / 1e3).toString();
|
|
77
|
+
const signature = await generateHMACSignature(
|
|
78
|
+
organizationSecret,
|
|
79
|
+
organizationId,
|
|
80
|
+
timestamp,
|
|
81
|
+
method,
|
|
82
|
+
url,
|
|
83
|
+
body || ""
|
|
84
|
+
);
|
|
85
|
+
return {
|
|
86
|
+
"X-CloudSignal-Organization-ID": organizationId,
|
|
87
|
+
"X-CloudSignal-Timestamp": timestamp,
|
|
88
|
+
"X-CloudSignal-Signature": signature,
|
|
89
|
+
"Content-Type": "application/json"
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
async function makeAuthenticatedRequest(organizationId, organizationSecret, method, url, body) {
|
|
93
|
+
const bodyStr = body ? JSON.stringify(body) : void 0;
|
|
94
|
+
const headers = await generateAuthHeaders(
|
|
95
|
+
organizationId,
|
|
96
|
+
organizationSecret,
|
|
97
|
+
method,
|
|
98
|
+
url,
|
|
99
|
+
bodyStr
|
|
100
|
+
);
|
|
101
|
+
const options = {
|
|
102
|
+
method,
|
|
103
|
+
headers
|
|
104
|
+
};
|
|
105
|
+
if (bodyStr) {
|
|
106
|
+
options.body = bodyStr;
|
|
107
|
+
}
|
|
108
|
+
return fetch(url, options);
|
|
109
|
+
}
|
|
110
|
+
function isValidUUID(value) {
|
|
111
|
+
if (!value || typeof value !== "string") return false;
|
|
112
|
+
return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value);
|
|
113
|
+
}
|
|
114
|
+
var init_hmac = __esm({
|
|
115
|
+
"src/utils/hmac.ts"() {
|
|
116
|
+
}
|
|
117
|
+
});
|
|
10
118
|
|
|
11
119
|
// src/utils/fingerprint.ts
|
|
12
120
|
async function generateBrowserFingerprint() {
|
|
@@ -893,80 +1001,20 @@ var InstallationManager = class {
|
|
|
893
1001
|
}
|
|
894
1002
|
};
|
|
895
1003
|
|
|
896
|
-
// src/
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
function
|
|
901
|
-
return btoa(String.fromCharCode(...new Uint8Array(buffer)));
|
|
902
|
-
}
|
|
903
|
-
async function generateHMACSignature(secret, organizationId, timestamp, method, url, body = "") {
|
|
904
|
-
const encoder = new TextEncoder();
|
|
905
|
-
let path;
|
|
906
|
-
let query;
|
|
907
|
-
try {
|
|
908
|
-
const urlObj = url.startsWith("http") ? new URL(url) : new URL(url, "https://pwa.cloudsignal.app");
|
|
909
|
-
path = urlObj.pathname;
|
|
910
|
-
query = urlObj.search ? urlObj.search.slice(1) : "";
|
|
911
|
-
} catch {
|
|
912
|
-
const queryIndex = url.indexOf("?");
|
|
913
|
-
if (queryIndex > -1) {
|
|
914
|
-
path = url.slice(0, queryIndex);
|
|
915
|
-
query = url.slice(queryIndex + 1);
|
|
916
|
-
} else {
|
|
917
|
-
path = url;
|
|
918
|
-
query = "";
|
|
919
|
-
}
|
|
920
|
-
}
|
|
921
|
-
const canonicalParts = [
|
|
922
|
-
method.toUpperCase(),
|
|
923
|
-
path,
|
|
924
|
-
query,
|
|
925
|
-
organizationId,
|
|
926
|
-
timestamp
|
|
927
|
-
];
|
|
928
|
-
if (body && body.length > 0) {
|
|
929
|
-
const bodyHashBuffer = await crypto.subtle.digest("SHA-256", encoder.encode(body));
|
|
930
|
-
const bodyHashHex = toHex(bodyHashBuffer);
|
|
931
|
-
canonicalParts.push(bodyHashHex);
|
|
932
|
-
}
|
|
933
|
-
const canonicalString = canonicalParts.join("\n");
|
|
934
|
-
const key = await crypto.subtle.importKey(
|
|
935
|
-
"raw",
|
|
936
|
-
encoder.encode(secret),
|
|
937
|
-
{ name: "HMAC", hash: "SHA-256" },
|
|
938
|
-
false,
|
|
939
|
-
["sign"]
|
|
940
|
-
);
|
|
941
|
-
const signature = await crypto.subtle.sign("HMAC", key, encoder.encode(canonicalString));
|
|
942
|
-
return toBase64(signature);
|
|
943
|
-
}
|
|
944
|
-
async function generateAuthHeaders(organizationId, organizationSecret, method, url, body) {
|
|
945
|
-
const timestamp = Math.floor(Date.now() / 1e3).toString();
|
|
946
|
-
const signature = await generateHMACSignature(
|
|
947
|
-
organizationSecret,
|
|
948
|
-
organizationId,
|
|
949
|
-
timestamp,
|
|
950
|
-
method,
|
|
951
|
-
url,
|
|
952
|
-
body || ""
|
|
953
|
-
);
|
|
1004
|
+
// src/PushNotificationManager.ts
|
|
1005
|
+
init_hmac();
|
|
1006
|
+
|
|
1007
|
+
// src/utils/jwt-auth.ts
|
|
1008
|
+
function generateJWTHeaders(token, organizationId) {
|
|
954
1009
|
return {
|
|
1010
|
+
"Authorization": `Bearer ${token}`,
|
|
955
1011
|
"X-CloudSignal-Organization-ID": organizationId,
|
|
956
|
-
"X-CloudSignal-Timestamp": timestamp,
|
|
957
|
-
"X-CloudSignal-Signature": signature,
|
|
958
1012
|
"Content-Type": "application/json"
|
|
959
1013
|
};
|
|
960
1014
|
}
|
|
961
|
-
async function
|
|
1015
|
+
async function makeJWTAuthenticatedRequest(config, method, url, body) {
|
|
962
1016
|
const bodyStr = body ? JSON.stringify(body) : void 0;
|
|
963
|
-
|
|
964
|
-
organizationId,
|
|
965
|
-
organizationSecret,
|
|
966
|
-
method,
|
|
967
|
-
url,
|
|
968
|
-
bodyStr
|
|
969
|
-
);
|
|
1017
|
+
let headers = generateJWTHeaders(config.token, config.organizationId);
|
|
970
1018
|
const options = {
|
|
971
1019
|
method,
|
|
972
1020
|
headers
|
|
@@ -974,11 +1022,81 @@ async function makeAuthenticatedRequest(organizationId, organizationSecret, meth
|
|
|
974
1022
|
if (bodyStr) {
|
|
975
1023
|
options.body = bodyStr;
|
|
976
1024
|
}
|
|
977
|
-
|
|
1025
|
+
let response = await fetch(url, options);
|
|
1026
|
+
if (response.status === 401 && config.onTokenExpired) {
|
|
1027
|
+
try {
|
|
1028
|
+
const newToken = await config.onTokenExpired();
|
|
1029
|
+
if (newToken) {
|
|
1030
|
+
headers = generateJWTHeaders(newToken, config.organizationId);
|
|
1031
|
+
options.headers = headers;
|
|
1032
|
+
response = await fetch(url, options);
|
|
1033
|
+
}
|
|
1034
|
+
} catch (refreshError) {
|
|
1035
|
+
console.warn("[CloudSignal PWA] Token refresh failed:", refreshError);
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
return response;
|
|
978
1039
|
}
|
|
979
|
-
function
|
|
980
|
-
if (
|
|
981
|
-
|
|
1040
|
+
function createAuthContext(config) {
|
|
1041
|
+
if (config.userToken) {
|
|
1042
|
+
return {
|
|
1043
|
+
mode: "jwt",
|
|
1044
|
+
organizationId: config.organizationId,
|
|
1045
|
+
userToken: config.userToken,
|
|
1046
|
+
onTokenExpired: config.onTokenExpired
|
|
1047
|
+
};
|
|
1048
|
+
}
|
|
1049
|
+
if (!config.organizationSecret) {
|
|
1050
|
+
throw new Error("Either userToken or organizationSecret is required");
|
|
1051
|
+
}
|
|
1052
|
+
return {
|
|
1053
|
+
mode: "hmac",
|
|
1054
|
+
organizationId: config.organizationId,
|
|
1055
|
+
organizationSecret: config.organizationSecret
|
|
1056
|
+
};
|
|
1057
|
+
}
|
|
1058
|
+
async function makeAuthenticatedRequestWithContext(authContext, method, url, body, onTokenExpired) {
|
|
1059
|
+
if (authContext.mode === "jwt") {
|
|
1060
|
+
if (!authContext.userToken) {
|
|
1061
|
+
throw new Error("userToken required for JWT auth mode");
|
|
1062
|
+
}
|
|
1063
|
+
return makeJWTAuthenticatedRequest(
|
|
1064
|
+
{
|
|
1065
|
+
token: authContext.userToken,
|
|
1066
|
+
organizationId: authContext.organizationId,
|
|
1067
|
+
// Allow override of token expired callback
|
|
1068
|
+
onTokenExpired: onTokenExpired || authContext.onTokenExpired
|
|
1069
|
+
},
|
|
1070
|
+
method,
|
|
1071
|
+
url,
|
|
1072
|
+
body
|
|
1073
|
+
);
|
|
1074
|
+
}
|
|
1075
|
+
const { makeAuthenticatedRequest: makeAuthenticatedRequest4 } = await Promise.resolve().then(() => (init_hmac(), hmac_exports));
|
|
1076
|
+
if (!authContext.organizationSecret) {
|
|
1077
|
+
throw new Error("organizationSecret required for HMAC auth mode");
|
|
1078
|
+
}
|
|
1079
|
+
return makeAuthenticatedRequest4(
|
|
1080
|
+
authContext.organizationId,
|
|
1081
|
+
authContext.organizationSecret,
|
|
1082
|
+
method,
|
|
1083
|
+
url,
|
|
1084
|
+
body
|
|
1085
|
+
);
|
|
1086
|
+
}
|
|
1087
|
+
function updateAuthToken(authContext, newToken) {
|
|
1088
|
+
if (authContext.mode !== "jwt") {
|
|
1089
|
+
return {
|
|
1090
|
+
mode: "jwt",
|
|
1091
|
+
organizationId: authContext.organizationId,
|
|
1092
|
+
userToken: newToken,
|
|
1093
|
+
onTokenExpired: authContext.onTokenExpired
|
|
1094
|
+
};
|
|
1095
|
+
}
|
|
1096
|
+
return {
|
|
1097
|
+
...authContext,
|
|
1098
|
+
userToken: newToken
|
|
1099
|
+
};
|
|
982
1100
|
}
|
|
983
1101
|
|
|
984
1102
|
// src/utils/storage.ts
|
|
@@ -1224,14 +1342,19 @@ var PushNotificationManager = class {
|
|
|
1224
1342
|
this.vapidPublicKey = null;
|
|
1225
1343
|
this.serviceUrl = options.serviceUrl;
|
|
1226
1344
|
this.organizationId = options.organizationId;
|
|
1227
|
-
this.organizationSecret = options.organizationSecret;
|
|
1228
1345
|
this.serviceId = options.serviceId;
|
|
1229
1346
|
this.debug = options.debug ?? false;
|
|
1230
1347
|
this.deviceDetector = new DeviceDetector();
|
|
1348
|
+
this.authContext = createAuthContext({
|
|
1349
|
+
organizationId: options.organizationId,
|
|
1350
|
+
organizationSecret: options.organizationSecret,
|
|
1351
|
+
userToken: options.userToken
|
|
1352
|
+
});
|
|
1231
1353
|
this.onRegistered = options.onRegistered;
|
|
1232
1354
|
this.onUnregistered = options.onUnregistered;
|
|
1233
1355
|
this.onError = options.onError;
|
|
1234
1356
|
this.onPermissionDenied = options.onPermissionDenied;
|
|
1357
|
+
this.onTokenExpired = options.onTokenExpired;
|
|
1235
1358
|
this.registrationId = getRegistrationId(this.organizationId, this.serviceId);
|
|
1236
1359
|
}
|
|
1237
1360
|
/**
|
|
@@ -1258,6 +1381,19 @@ var PushNotificationManager = class {
|
|
|
1258
1381
|
isRegistered() {
|
|
1259
1382
|
return this.registrationId !== null;
|
|
1260
1383
|
}
|
|
1384
|
+
/**
|
|
1385
|
+
* Get current authentication mode
|
|
1386
|
+
*/
|
|
1387
|
+
getAuthMode() {
|
|
1388
|
+
return this.authContext.mode;
|
|
1389
|
+
}
|
|
1390
|
+
/**
|
|
1391
|
+
* Update JWT token (for token refresh or upgrading from HMAC to JWT)
|
|
1392
|
+
*/
|
|
1393
|
+
updateToken(token) {
|
|
1394
|
+
this.authContext = updateAuthToken(this.authContext, token);
|
|
1395
|
+
this.log(`Auth mode: ${this.authContext.mode}`);
|
|
1396
|
+
}
|
|
1261
1397
|
/**
|
|
1262
1398
|
* Register for push notifications
|
|
1263
1399
|
*/
|
|
@@ -1307,12 +1443,12 @@ var PushNotificationManager = class {
|
|
|
1307
1443
|
language: options.language || navigator.language || "en-US"
|
|
1308
1444
|
};
|
|
1309
1445
|
const url = `${this.serviceUrl}/api/v1/registration/register`;
|
|
1310
|
-
const response = await
|
|
1311
|
-
this.
|
|
1312
|
-
this.organizationSecret,
|
|
1446
|
+
const response = await makeAuthenticatedRequestWithContext(
|
|
1447
|
+
this.authContext,
|
|
1313
1448
|
"POST",
|
|
1314
1449
|
url,
|
|
1315
|
-
registrationData
|
|
1450
|
+
registrationData,
|
|
1451
|
+
this.onTokenExpired
|
|
1316
1452
|
);
|
|
1317
1453
|
if (!response.ok) {
|
|
1318
1454
|
const errorText = await response.text();
|
|
@@ -1351,12 +1487,12 @@ var PushNotificationManager = class {
|
|
|
1351
1487
|
this.pushSubscription = null;
|
|
1352
1488
|
}
|
|
1353
1489
|
const url = `${this.serviceUrl}/api/v1/registration/unregister`;
|
|
1354
|
-
const response = await
|
|
1355
|
-
this.
|
|
1356
|
-
this.organizationSecret,
|
|
1490
|
+
const response = await makeAuthenticatedRequestWithContext(
|
|
1491
|
+
this.authContext,
|
|
1357
1492
|
"POST",
|
|
1358
1493
|
url,
|
|
1359
|
-
{ registration_id: this.registrationId }
|
|
1494
|
+
{ registration_id: this.registrationId },
|
|
1495
|
+
this.onTokenExpired
|
|
1360
1496
|
);
|
|
1361
1497
|
if (!response.ok) {
|
|
1362
1498
|
this.log(`Backend unregistration failed: ${response.status}`, "warn");
|
|
@@ -1382,9 +1518,8 @@ var PushNotificationManager = class {
|
|
|
1382
1518
|
throw new Error("Not registered for push notifications");
|
|
1383
1519
|
}
|
|
1384
1520
|
const url = `${this.serviceUrl}/api/v1/registration/update`;
|
|
1385
|
-
const response = await
|
|
1386
|
-
this.
|
|
1387
|
-
this.organizationSecret,
|
|
1521
|
+
const response = await makeAuthenticatedRequestWithContext(
|
|
1522
|
+
this.authContext,
|
|
1388
1523
|
"POST",
|
|
1389
1524
|
url,
|
|
1390
1525
|
{
|
|
@@ -1393,7 +1528,8 @@ var PushNotificationManager = class {
|
|
|
1393
1528
|
timezone: preferences.timezone,
|
|
1394
1529
|
language: preferences.language,
|
|
1395
1530
|
is_active: preferences.isActive
|
|
1396
|
-
}
|
|
1531
|
+
},
|
|
1532
|
+
this.onTokenExpired
|
|
1397
1533
|
);
|
|
1398
1534
|
if (!response.ok) {
|
|
1399
1535
|
throw new Error(`Update failed: ${response.status}`);
|
|
@@ -1416,11 +1552,12 @@ var PushNotificationManager = class {
|
|
|
1416
1552
|
return null;
|
|
1417
1553
|
}
|
|
1418
1554
|
const url = `${this.serviceUrl}/api/v1/registration/status/${this.registrationId}`;
|
|
1419
|
-
const response = await
|
|
1420
|
-
this.
|
|
1421
|
-
this.organizationSecret,
|
|
1555
|
+
const response = await makeAuthenticatedRequestWithContext(
|
|
1556
|
+
this.authContext,
|
|
1422
1557
|
"GET",
|
|
1423
|
-
url
|
|
1558
|
+
url,
|
|
1559
|
+
void 0,
|
|
1560
|
+
this.onTokenExpired
|
|
1424
1561
|
);
|
|
1425
1562
|
if (!response.ok) {
|
|
1426
1563
|
if (response.status === 404) {
|
|
@@ -1510,6 +1647,7 @@ var PushNotificationManager = class {
|
|
|
1510
1647
|
};
|
|
1511
1648
|
|
|
1512
1649
|
// src/HeartbeatManager.ts
|
|
1650
|
+
init_hmac();
|
|
1513
1651
|
var DEFAULT_INTERVALS = {
|
|
1514
1652
|
"4g": 3e4,
|
|
1515
1653
|
// 30 seconds
|
|
@@ -1533,13 +1671,18 @@ var HeartbeatManager = class {
|
|
|
1533
1671
|
this.batteryManager = null;
|
|
1534
1672
|
this.serviceUrl = options.serviceUrl;
|
|
1535
1673
|
this.organizationId = options.organizationId;
|
|
1536
|
-
this.organizationSecret = options.organizationSecret;
|
|
1537
1674
|
this.debug = options.debug ?? false;
|
|
1538
1675
|
this.onHeartbeatSent = options.onHeartbeatSent;
|
|
1539
1676
|
this.onHeartbeatError = options.onHeartbeatError;
|
|
1540
1677
|
this.onIntervalChanged = options.onIntervalChanged;
|
|
1541
1678
|
this.onPausedForBattery = options.onPausedForBattery;
|
|
1542
1679
|
this.onResumedFromBattery = options.onResumedFromBattery;
|
|
1680
|
+
this.onTokenExpired = options.onTokenExpired;
|
|
1681
|
+
this.authContext = createAuthContext({
|
|
1682
|
+
organizationId: options.organizationId,
|
|
1683
|
+
organizationSecret: options.organizationSecret,
|
|
1684
|
+
userToken: options.userToken
|
|
1685
|
+
});
|
|
1543
1686
|
this.config = {
|
|
1544
1687
|
enabled: options.config?.enabled ?? true,
|
|
1545
1688
|
interval: options.config?.interval ?? 3e4,
|
|
@@ -1630,6 +1773,19 @@ var HeartbeatManager = class {
|
|
|
1630
1773
|
isHeartbeatRunning() {
|
|
1631
1774
|
return this.isRunning;
|
|
1632
1775
|
}
|
|
1776
|
+
/**
|
|
1777
|
+
* Get current authentication mode
|
|
1778
|
+
*/
|
|
1779
|
+
getAuthMode() {
|
|
1780
|
+
return this.authContext.mode;
|
|
1781
|
+
}
|
|
1782
|
+
/**
|
|
1783
|
+
* Update JWT token (for token refresh or upgrading from HMAC to JWT)
|
|
1784
|
+
*/
|
|
1785
|
+
updateToken(token) {
|
|
1786
|
+
this.authContext = updateAuthToken(this.authContext, token);
|
|
1787
|
+
this.log(`Auth mode: ${this.authContext.mode}`);
|
|
1788
|
+
}
|
|
1633
1789
|
/**
|
|
1634
1790
|
* Send a single heartbeat
|
|
1635
1791
|
*/
|
|
@@ -1640,11 +1796,12 @@ var HeartbeatManager = class {
|
|
|
1640
1796
|
}
|
|
1641
1797
|
try {
|
|
1642
1798
|
const url = `${this.serviceUrl}/api/v1/registration/heartbeat/${this.registrationId}`;
|
|
1643
|
-
const response = await
|
|
1644
|
-
this.
|
|
1645
|
-
this.organizationSecret,
|
|
1799
|
+
const response = await makeAuthenticatedRequestWithContext(
|
|
1800
|
+
this.authContext,
|
|
1646
1801
|
"POST",
|
|
1647
|
-
url
|
|
1802
|
+
url,
|
|
1803
|
+
void 0,
|
|
1804
|
+
this.onTokenExpired
|
|
1648
1805
|
);
|
|
1649
1806
|
if (!response.ok) {
|
|
1650
1807
|
throw new Error(`Heartbeat failed: ${response.status}`);
|
|
@@ -2960,7 +3117,7 @@ var IOSInstallBanner = class {
|
|
|
2960
3117
|
|
|
2961
3118
|
// src/CloudSignalPWA.ts
|
|
2962
3119
|
var DEFAULT_SERVICE_URL = "https://pwa.cloudsignal.app";
|
|
2963
|
-
var SDK_VERSION = "1.
|
|
3120
|
+
var SDK_VERSION = "1.2.0";
|
|
2964
3121
|
var CloudSignalPWA = class {
|
|
2965
3122
|
constructor(config) {
|
|
2966
3123
|
this.initialized = false;
|
|
@@ -2974,6 +3131,14 @@ var CloudSignalPWA = class {
|
|
|
2974
3131
|
this.config = config;
|
|
2975
3132
|
this.serviceUrl = config.serviceUrl || DEFAULT_SERVICE_URL;
|
|
2976
3133
|
this.debug = config.debug ?? false;
|
|
3134
|
+
if (!config.organizationSecret && !config.userToken) {
|
|
3135
|
+
throw new Error("Either organizationSecret or userToken must be provided");
|
|
3136
|
+
}
|
|
3137
|
+
this.authContext = createAuthContext({
|
|
3138
|
+
organizationId: config.organizationId,
|
|
3139
|
+
organizationSecret: config.organizationSecret,
|
|
3140
|
+
userToken: config.userToken
|
|
3141
|
+
});
|
|
2977
3142
|
this.deviceDetector = new DeviceDetector();
|
|
2978
3143
|
this.serviceWorkerManager = new ServiceWorkerManager({
|
|
2979
3144
|
config: config.serviceWorker,
|
|
@@ -2993,6 +3158,8 @@ var CloudSignalPWA = class {
|
|
|
2993
3158
|
serviceUrl: this.serviceUrl,
|
|
2994
3159
|
organizationId: config.organizationId,
|
|
2995
3160
|
organizationSecret: config.organizationSecret,
|
|
3161
|
+
userToken: config.userToken,
|
|
3162
|
+
onTokenExpired: config.onTokenExpired,
|
|
2996
3163
|
serviceId: config.serviceId,
|
|
2997
3164
|
debug: this.debug,
|
|
2998
3165
|
onRegistered: (reg) => {
|
|
@@ -3010,6 +3177,8 @@ var CloudSignalPWA = class {
|
|
|
3010
3177
|
serviceUrl: this.serviceUrl,
|
|
3011
3178
|
organizationId: config.organizationId,
|
|
3012
3179
|
organizationSecret: config.organizationSecret,
|
|
3180
|
+
userToken: config.userToken,
|
|
3181
|
+
onTokenExpired: config.onTokenExpired,
|
|
3013
3182
|
config: config.heartbeat,
|
|
3014
3183
|
debug: this.debug,
|
|
3015
3184
|
onHeartbeatSent: () => this.emit("heartbeat:sent", { timestamp: Date.now() }),
|
|
@@ -3118,12 +3287,12 @@ var CloudSignalPWA = class {
|
|
|
3118
3287
|
async downloadConfig() {
|
|
3119
3288
|
try {
|
|
3120
3289
|
const url = `${this.serviceUrl}/api/v1/config/download`;
|
|
3121
|
-
const response = await
|
|
3122
|
-
this.
|
|
3123
|
-
this.config.organizationSecret,
|
|
3290
|
+
const response = await makeAuthenticatedRequestWithContext(
|
|
3291
|
+
this.authContext,
|
|
3124
3292
|
"POST",
|
|
3125
3293
|
url,
|
|
3126
|
-
{ organization_id: this.config.organizationId }
|
|
3294
|
+
{ organization_id: this.config.organizationId },
|
|
3295
|
+
this.config.onTokenExpired
|
|
3127
3296
|
);
|
|
3128
3297
|
if (!response.ok) {
|
|
3129
3298
|
throw new Error(`Config download failed: ${response.status}`);
|
|
@@ -3225,6 +3394,27 @@ var CloudSignalPWA = class {
|
|
|
3225
3394
|
return this.pushNotificationManager.requestPermission();
|
|
3226
3395
|
}
|
|
3227
3396
|
// ============================================
|
|
3397
|
+
// Authentication
|
|
3398
|
+
// ============================================
|
|
3399
|
+
/**
|
|
3400
|
+
* Get current authentication mode
|
|
3401
|
+
*/
|
|
3402
|
+
getAuthMode() {
|
|
3403
|
+
return this.authContext.mode;
|
|
3404
|
+
}
|
|
3405
|
+
/**
|
|
3406
|
+
* Set JWT user token (for upgrading anonymous to authenticated, or token refresh)
|
|
3407
|
+
* After calling this, you should call registerForPush() again to re-register with user context
|
|
3408
|
+
* @param token - JWT token from identity provider
|
|
3409
|
+
*/
|
|
3410
|
+
setUserToken(token) {
|
|
3411
|
+
this.authContext = updateAuthToken(this.authContext, token);
|
|
3412
|
+
this.pushNotificationManager.updateToken(token);
|
|
3413
|
+
this.heartbeatManager.updateToken(token);
|
|
3414
|
+
this.emit("auth:tokenUpdated", { mode: this.authContext.mode });
|
|
3415
|
+
this.log(`Auth mode updated to: ${this.authContext.mode}`);
|
|
3416
|
+
}
|
|
3417
|
+
// ============================================
|
|
3228
3418
|
// Device Information
|
|
3229
3419
|
// ============================================
|
|
3230
3420
|
/**
|
|
@@ -3944,7 +4134,8 @@ var NotificationPermissionPrompt = class {
|
|
|
3944
4134
|
};
|
|
3945
4135
|
|
|
3946
4136
|
// src/index.ts
|
|
3947
|
-
|
|
4137
|
+
init_hmac();
|
|
4138
|
+
var VERSION = "1.2.0";
|
|
3948
4139
|
|
|
3949
4140
|
exports.CloudSignalPWA = CloudSignalPWA;
|
|
3950
4141
|
exports.DeviceDetector = DeviceDetector;
|
|
@@ -3960,20 +4151,25 @@ exports.PushNotificationManager = PushNotificationManager;
|
|
|
3960
4151
|
exports.ServiceWorkerManager = ServiceWorkerManager;
|
|
3961
4152
|
exports.VERSION = VERSION;
|
|
3962
4153
|
exports.WakeLockManager = WakeLockManager;
|
|
4154
|
+
exports.createAuthContext = createAuthContext;
|
|
3963
4155
|
exports.default = CloudSignalPWA_default;
|
|
3964
4156
|
exports.detectBrowserLanguage = detectBrowserLanguage;
|
|
3965
4157
|
exports.deviceDetector = deviceDetector;
|
|
3966
4158
|
exports.generateAuthHeaders = generateAuthHeaders;
|
|
3967
4159
|
exports.generateBrowserFingerprint = generateBrowserFingerprint;
|
|
3968
4160
|
exports.generateHMACSignature = generateHMACSignature;
|
|
4161
|
+
exports.generateJWTHeaders = generateJWTHeaders;
|
|
3969
4162
|
exports.generateTrackingId = generateTrackingId;
|
|
3970
4163
|
exports.getRegistrationId = getRegistrationId;
|
|
3971
4164
|
exports.getStorageItem = getStorageItem;
|
|
3972
4165
|
exports.isValidUUID = isValidUUID;
|
|
3973
4166
|
exports.makeAuthenticatedRequest = makeAuthenticatedRequest;
|
|
4167
|
+
exports.makeAuthenticatedRequestWithContext = makeAuthenticatedRequestWithContext;
|
|
4168
|
+
exports.makeJWTAuthenticatedRequest = makeJWTAuthenticatedRequest;
|
|
3974
4169
|
exports.removeRegistrationId = removeRegistrationId;
|
|
3975
4170
|
exports.removeStorageItem = removeStorageItem;
|
|
3976
4171
|
exports.setRegistrationId = setRegistrationId;
|
|
3977
4172
|
exports.setStorageItem = setStorageItem;
|
|
4173
|
+
exports.updateAuthToken = updateAuthToken;
|
|
3978
4174
|
//# sourceMappingURL=index.cjs.map
|
|
3979
4175
|
//# sourceMappingURL=index.cjs.map
|