@absolutejs/auth 0.26.0-beta.1 → 0.26.0-beta.3
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/dist/audit/config.d.ts +2 -1
- package/dist/audit/types.d.ts +1 -1
- package/dist/authorization/config.d.ts +19 -0
- package/dist/authorization/protectPermission.d.ts +52 -0
- package/dist/compliance/cipher.d.ts +5 -0
- package/dist/compliance/config.d.ts +18 -0
- package/dist/compliance/redaction.d.ts +8 -0
- package/dist/compliance/routes.d.ts +89 -0
- package/dist/htmx/index.js +494 -98
- package/dist/htmx/index.js.map +3 -3
- package/dist/index.d.ts +2539 -344
- package/dist/index.js +3679 -1619
- package/dist/index.js.map +33 -13
- package/dist/lockout/redisLockoutStore.d.ts +3 -0
- package/dist/providers/clients.d.ts +3 -3
- package/dist/routes/authorize.d.ts +2 -2
- package/dist/routes/protectRoute.d.ts +2 -2
- package/dist/scim/config.d.ts +55 -0
- package/dist/scim/inMemoryScimTokenStore.d.ts +2 -0
- package/dist/scim/postgresScimTokenStore.d.ts +102 -0
- package/dist/scim/routes.d.ts +296 -0
- package/dist/scim/serialize.d.ts +45 -0
- package/dist/scim/types.d.ts +52 -0
- package/dist/session/promote.d.ts +9 -2
- package/dist/sso/config.d.ts +104 -0
- package/dist/sso/discoveryRoute.d.ts +63 -0
- package/dist/sso/inMemorySsoConnectionStore.d.ts +2 -0
- package/dist/sso/oidcRoutes.d.ts +97 -0
- package/dist/sso/postgresSsoConnectionStore.d.ts +139 -0
- package/dist/sso/samlRoutes.d.ts +176 -0
- package/dist/sso/types.d.ts +39 -0
- package/dist/stores/redis.d.ts +5 -0
- package/dist/typebox.d.ts +1 -1
- package/dist/types.d.ts +36 -0
- package/dist/webauthn/adapter.d.ts +59 -0
- package/dist/webauthn/config.d.ts +35 -0
- package/dist/webauthn/inMemoryWebAuthnCredentialStore.d.ts +2 -0
- package/dist/webauthn/postgresWebAuthnCredentialStore.d.ts +172 -0
- package/dist/webauthn/routes.d.ts +155 -0
- package/dist/webauthn/types.d.ts +17 -0
- package/package.json +2 -2
package/dist/htmx/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// node_modules/citra/dist/index.js
|
|
3
|
-
var NUM_GENERATOR_BYTES = 32;
|
|
4
3
|
var BASE64_BLOCK_SIZE = 4;
|
|
4
|
+
var NUM_GENERATOR_BYTES = 32;
|
|
5
5
|
var anilistProfileQuery = `query {
|
|
6
6
|
Viewer {
|
|
7
7
|
id
|
|
@@ -227,6 +227,25 @@ var providers = defineProviders({
|
|
|
227
227
|
url: "https://auth.atlassian.com/oauth/token"
|
|
228
228
|
}
|
|
229
229
|
},
|
|
230
|
+
attio: {
|
|
231
|
+
authorizationUrl: "https://app.attio.com/authorize",
|
|
232
|
+
isOIDC: false,
|
|
233
|
+
isRefreshable: true,
|
|
234
|
+
profileRequest: {
|
|
235
|
+
authIn: "header",
|
|
236
|
+
encoding: "application/json",
|
|
237
|
+
method: "GET",
|
|
238
|
+
url: "https://api.attio.com/v2/self"
|
|
239
|
+
},
|
|
240
|
+
scopeRequired: true,
|
|
241
|
+
subject: ["workspace_id"],
|
|
242
|
+
subjectType: "string",
|
|
243
|
+
tokenRequest: {
|
|
244
|
+
authIn: "body",
|
|
245
|
+
encoding: "application/x-www-form-urlencoded",
|
|
246
|
+
url: "https://app.attio.com/oauth/token"
|
|
247
|
+
}
|
|
248
|
+
},
|
|
230
249
|
auth0: {
|
|
231
250
|
authorizationUrl: (config) => `https://${config.domain}/authorize`,
|
|
232
251
|
isOIDC: true,
|
|
@@ -391,6 +410,25 @@ var providers = defineProviders({
|
|
|
391
410
|
url: "https://www.bungie.net/Platform/App/OAuth/token"
|
|
392
411
|
}
|
|
393
412
|
},
|
|
413
|
+
close: {
|
|
414
|
+
authorizationUrl: "https://app.close.com/oauth2/authorize/",
|
|
415
|
+
isOIDC: false,
|
|
416
|
+
isRefreshable: true,
|
|
417
|
+
profileRequest: {
|
|
418
|
+
authIn: "header",
|
|
419
|
+
encoding: "application/json",
|
|
420
|
+
method: "GET",
|
|
421
|
+
url: "https://api.close.com/api/v1/me/"
|
|
422
|
+
},
|
|
423
|
+
scopeRequired: true,
|
|
424
|
+
subject: ["id"],
|
|
425
|
+
subjectType: "string",
|
|
426
|
+
tokenRequest: {
|
|
427
|
+
authIn: "body",
|
|
428
|
+
encoding: "application/x-www-form-urlencoded",
|
|
429
|
+
url: "https://api.close.com/oauth2/token/"
|
|
430
|
+
}
|
|
431
|
+
},
|
|
394
432
|
coinbase: {
|
|
395
433
|
authorizationUrl: "https://www.coinbase.com/oauth/authorize",
|
|
396
434
|
isOIDC: false,
|
|
@@ -660,6 +698,25 @@ var providers = defineProviders({
|
|
|
660
698
|
url: (config) => `${config.baseURL}/oauth/token`
|
|
661
699
|
}
|
|
662
700
|
},
|
|
701
|
+
gohighlevel: {
|
|
702
|
+
authorizationUrl: "https://marketplace.gohighlevel.com/oauth/chooselocation",
|
|
703
|
+
isOIDC: false,
|
|
704
|
+
isRefreshable: true,
|
|
705
|
+
profileRequest: {
|
|
706
|
+
authIn: "header",
|
|
707
|
+
encoding: "application/json",
|
|
708
|
+
method: "GET",
|
|
709
|
+
url: "https://services.leadconnectorhq.com/users/me"
|
|
710
|
+
},
|
|
711
|
+
scopeRequired: true,
|
|
712
|
+
subject: ["id"],
|
|
713
|
+
subjectType: "string",
|
|
714
|
+
tokenRequest: {
|
|
715
|
+
authIn: "body",
|
|
716
|
+
encoding: "application/x-www-form-urlencoded",
|
|
717
|
+
url: "https://services.leadconnectorhq.com/oauth/token"
|
|
718
|
+
}
|
|
719
|
+
},
|
|
663
720
|
google: {
|
|
664
721
|
authorizationUrl: "https://accounts.google.com/o/oauth2/v2/auth",
|
|
665
722
|
email: ["email"],
|
|
@@ -695,6 +752,25 @@ var providers = defineProviders({
|
|
|
695
752
|
url: "https://oauth2.googleapis.com/token"
|
|
696
753
|
}
|
|
697
754
|
},
|
|
755
|
+
hubspot: {
|
|
756
|
+
authorizationUrl: "https://app.hubspot.com/oauth/authorize",
|
|
757
|
+
isOIDC: false,
|
|
758
|
+
isRefreshable: true,
|
|
759
|
+
profileRequest: {
|
|
760
|
+
authIn: "header",
|
|
761
|
+
encoding: "application/json",
|
|
762
|
+
method: "GET",
|
|
763
|
+
url: "https://api.hubapi.com/oauth/v1/access-tokens/me"
|
|
764
|
+
},
|
|
765
|
+
scopeRequired: true,
|
|
766
|
+
subject: ["hub_id"],
|
|
767
|
+
subjectType: "number",
|
|
768
|
+
tokenRequest: {
|
|
769
|
+
authIn: "body",
|
|
770
|
+
encoding: "application/x-www-form-urlencoded",
|
|
771
|
+
url: "https://api.hubapi.com/oauth/v1/token"
|
|
772
|
+
}
|
|
773
|
+
},
|
|
698
774
|
intuit: {
|
|
699
775
|
authorizationUrl: "https://appcenter.intuit.com/connect/oauth2",
|
|
700
776
|
isOIDC: true,
|
|
@@ -975,6 +1051,31 @@ var providers = defineProviders({
|
|
|
975
1051
|
url: (config) => `https://${config.tenantId}.b2clogin.com/${config.tenantId}/oauth2/v2.0/token`
|
|
976
1052
|
}
|
|
977
1053
|
},
|
|
1054
|
+
monday: {
|
|
1055
|
+
authorizationUrl: "https://auth.monday.com/oauth2/authorize",
|
|
1056
|
+
isOIDC: false,
|
|
1057
|
+
isRefreshable: true,
|
|
1058
|
+
profileRequest: {
|
|
1059
|
+
authIn: "header",
|
|
1060
|
+
body: {
|
|
1061
|
+
query: "query { me { id name email } }"
|
|
1062
|
+
},
|
|
1063
|
+
encoding: "application/json",
|
|
1064
|
+
headers: {
|
|
1065
|
+
"Content-Type": "application/json"
|
|
1066
|
+
},
|
|
1067
|
+
method: "POST",
|
|
1068
|
+
url: "https://api.monday.com/v2"
|
|
1069
|
+
},
|
|
1070
|
+
scopeRequired: true,
|
|
1071
|
+
subject: ["data", "me", "id"],
|
|
1072
|
+
subjectType: "number",
|
|
1073
|
+
tokenRequest: {
|
|
1074
|
+
authIn: "body",
|
|
1075
|
+
encoding: "application/x-www-form-urlencoded",
|
|
1076
|
+
url: "https://auth.monday.com/oauth2/token"
|
|
1077
|
+
}
|
|
1078
|
+
},
|
|
978
1079
|
myanimelist: {
|
|
979
1080
|
authorizationUrl: "https://myanimelist.net/v1/oauth2/authorize",
|
|
980
1081
|
isOIDC: false,
|
|
@@ -1106,6 +1207,25 @@ var providers = defineProviders({
|
|
|
1106
1207
|
url: "https://www.patreon.com/api/oauth2/token"
|
|
1107
1208
|
}
|
|
1108
1209
|
},
|
|
1210
|
+
pipedrive: {
|
|
1211
|
+
authorizationUrl: "https://oauth.pipedrive.com/oauth/authorize",
|
|
1212
|
+
isOIDC: false,
|
|
1213
|
+
isRefreshable: true,
|
|
1214
|
+
profileRequest: {
|
|
1215
|
+
authIn: "header",
|
|
1216
|
+
encoding: "application/json",
|
|
1217
|
+
method: "GET",
|
|
1218
|
+
url: "https://api.pipedrive.com/v1/users/me"
|
|
1219
|
+
},
|
|
1220
|
+
scopeRequired: true,
|
|
1221
|
+
subject: ["data", "id"],
|
|
1222
|
+
subjectType: "number",
|
|
1223
|
+
tokenRequest: {
|
|
1224
|
+
authIn: "body",
|
|
1225
|
+
encoding: "application/x-www-form-urlencoded",
|
|
1226
|
+
url: "https://oauth.pipedrive.com/oauth/token"
|
|
1227
|
+
}
|
|
1228
|
+
},
|
|
1109
1229
|
polar: {
|
|
1110
1230
|
authorizationUrl: "https://polar.sh/oauth2/authorize",
|
|
1111
1231
|
isOIDC: true,
|
|
@@ -1673,6 +1793,32 @@ var providers = defineProviders({
|
|
|
1673
1793
|
url: "https://oauth.yandex.com/token"
|
|
1674
1794
|
}
|
|
1675
1795
|
},
|
|
1796
|
+
zoho: {
|
|
1797
|
+
authorizationUrl: (config) => `https://accounts.zoho.${config.region ?? "com"}/oauth/v2/auth`,
|
|
1798
|
+
isOIDC: false,
|
|
1799
|
+
isRefreshable: true,
|
|
1800
|
+
PKCEMethod: "S256",
|
|
1801
|
+
profileRequest: {
|
|
1802
|
+
authIn: "header",
|
|
1803
|
+
encoding: "application/json",
|
|
1804
|
+
method: "GET",
|
|
1805
|
+
url: (config) => `https://accounts.zoho.${config.region ?? "com"}/oauth/user/info`
|
|
1806
|
+
},
|
|
1807
|
+
revocationRequest: {
|
|
1808
|
+
authIn: "query",
|
|
1809
|
+
encoding: "application/x-www-form-urlencoded",
|
|
1810
|
+
tokenParamName: "token",
|
|
1811
|
+
url: (config) => `https://accounts.zoho.${config.region ?? "com"}/oauth/v2/token/revoke`
|
|
1812
|
+
},
|
|
1813
|
+
scopeRequired: true,
|
|
1814
|
+
subject: ["ZUID"],
|
|
1815
|
+
subjectType: "string",
|
|
1816
|
+
tokenRequest: {
|
|
1817
|
+
authIn: "body",
|
|
1818
|
+
encoding: "application/x-www-form-urlencoded",
|
|
1819
|
+
url: (config) => `https://accounts.zoho.${config.region ?? "com"}/oauth/v2/token`
|
|
1820
|
+
}
|
|
1821
|
+
},
|
|
1676
1822
|
zoom: {
|
|
1677
1823
|
authorizationUrl: "https://zoom.us/oauth/authorize",
|
|
1678
1824
|
email: ["email"],
|
|
@@ -1707,18 +1853,32 @@ var providers = defineProviders({
|
|
|
1707
1853
|
}
|
|
1708
1854
|
}
|
|
1709
1855
|
});
|
|
1710
|
-
var
|
|
1711
|
-
|
|
1712
|
-
if (!isValidProviderOption(option))
|
|
1856
|
+
var hasClientSecret = (credentials) => {
|
|
1857
|
+
if (typeof credentials !== "object" || credentials === null)
|
|
1713
1858
|
return false;
|
|
1714
|
-
const
|
|
1715
|
-
return
|
|
1859
|
+
const secret = Reflect.get(credentials, "clientSecret");
|
|
1860
|
+
return typeof secret === "string";
|
|
1716
1861
|
};
|
|
1717
|
-
var
|
|
1862
|
+
var isExpectedType = (value, kind) => {
|
|
1863
|
+
switch (kind) {
|
|
1864
|
+
case "string":
|
|
1865
|
+
return typeof value === "string";
|
|
1866
|
+
case "number":
|
|
1867
|
+
return typeof value === "number";
|
|
1868
|
+
case "boolean":
|
|
1869
|
+
return typeof value === "boolean";
|
|
1870
|
+
case "object":
|
|
1871
|
+
return typeof value === "object" && value !== null;
|
|
1872
|
+
default:
|
|
1873
|
+
return false;
|
|
1874
|
+
}
|
|
1875
|
+
};
|
|
1876
|
+
var isObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value) && Object.prototype.toString.call(value) === "[object Object]";
|
|
1877
|
+
var isOIDCProviderOption = (option) => {
|
|
1718
1878
|
if (!isValidProviderOption(option))
|
|
1719
1879
|
return false;
|
|
1720
1880
|
const provider = providers[option];
|
|
1721
|
-
return provider.
|
|
1881
|
+
return provider.isOIDC;
|
|
1722
1882
|
};
|
|
1723
1883
|
var isPKCEProviderOption = (option) => {
|
|
1724
1884
|
if (!isValidProviderOption(option))
|
|
@@ -1726,39 +1886,27 @@ var isPKCEProviderOption = (option) => {
|
|
|
1726
1886
|
const provider = providers[option];
|
|
1727
1887
|
return provider.PKCEMethod !== undefined;
|
|
1728
1888
|
};
|
|
1729
|
-
var
|
|
1889
|
+
var isRefreshableOAuth2Client = (providerName, _client) => isRefreshableProviderOption(providerName);
|
|
1890
|
+
var isRefreshableProviderOption = (option) => {
|
|
1730
1891
|
if (!isValidProviderOption(option))
|
|
1731
1892
|
return false;
|
|
1732
1893
|
const provider = providers[option];
|
|
1733
|
-
return provider.
|
|
1894
|
+
return provider.isRefreshable;
|
|
1734
1895
|
};
|
|
1735
|
-
var
|
|
1896
|
+
var isRevocableOAuth2Client = (providerName, _client) => isRevocableProviderOption(providerName);
|
|
1897
|
+
var isRevocableProviderOption = (option) => {
|
|
1736
1898
|
if (!isValidProviderOption(option))
|
|
1737
1899
|
return false;
|
|
1738
1900
|
const provider = providers[option];
|
|
1739
|
-
return provider.
|
|
1901
|
+
return provider.revocationRequest !== undefined;
|
|
1740
1902
|
};
|
|
1741
|
-
var
|
|
1742
|
-
|
|
1743
|
-
var hasClientSecret = (credentials) => {
|
|
1744
|
-
if (typeof credentials !== "object" || credentials === null)
|
|
1903
|
+
var isScopeRequiredProviderOption = (option) => {
|
|
1904
|
+
if (!isValidProviderOption(option))
|
|
1745
1905
|
return false;
|
|
1746
|
-
const
|
|
1747
|
-
return
|
|
1748
|
-
};
|
|
1749
|
-
var isObject = (x) => x !== null && typeof x === "object" && !Array.isArray(x) && Object.prototype.toString.call(x) === "[object Object]";
|
|
1750
|
-
var isExpectedType = (value, kind) => {
|
|
1751
|
-
switch (kind) {
|
|
1752
|
-
case "string":
|
|
1753
|
-
return typeof value === "string";
|
|
1754
|
-
case "number":
|
|
1755
|
-
return typeof value === "number";
|
|
1756
|
-
case "boolean":
|
|
1757
|
-
return typeof value === "boolean";
|
|
1758
|
-
case "object":
|
|
1759
|
-
return typeof value === "object" && value !== null;
|
|
1760
|
-
}
|
|
1906
|
+
const provider = providers[option];
|
|
1907
|
+
return provider.scopeRequired;
|
|
1761
1908
|
};
|
|
1909
|
+
var isValidProviderOption = (option) => Object.hasOwn(providers, option);
|
|
1762
1910
|
var createOAuth2FetchError = async (response) => {
|
|
1763
1911
|
const clone = response.clone();
|
|
1764
1912
|
const prefix = `HTTP ${response.status} ${response.statusText} for ${response.url}`;
|
|
@@ -1774,15 +1922,44 @@ ${text}`);
|
|
|
1774
1922
|
}
|
|
1775
1923
|
return new Error(prefix);
|
|
1776
1924
|
};
|
|
1777
|
-
var
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1925
|
+
var createOAuth2Request = ({
|
|
1926
|
+
url,
|
|
1927
|
+
body,
|
|
1928
|
+
authIn,
|
|
1929
|
+
headers,
|
|
1930
|
+
encoding,
|
|
1931
|
+
clientId,
|
|
1932
|
+
clientSecret
|
|
1933
|
+
}) => {
|
|
1934
|
+
const oauthHeaders = new Headers(headers);
|
|
1935
|
+
oauthHeaders.set("Accept", "application/json");
|
|
1936
|
+
oauthHeaders.set("User-Agent", "citra");
|
|
1937
|
+
if (authIn === "header") {
|
|
1938
|
+
if (!clientSecret) {
|
|
1939
|
+
throw new Error("clientSecret required for header auth");
|
|
1940
|
+
}
|
|
1941
|
+
oauthHeaders.set("Authorization", `Basic ${encodeBase64(`${clientId}:${clientSecret}`)}`);
|
|
1784
1942
|
}
|
|
1785
|
-
|
|
1943
|
+
if (encoding === "application/json") {
|
|
1944
|
+
oauthHeaders.set("Content-Type", "application/json");
|
|
1945
|
+
return new Request(url, {
|
|
1946
|
+
body: JSON.stringify(body),
|
|
1947
|
+
headers: oauthHeaders,
|
|
1948
|
+
method: "POST"
|
|
1949
|
+
});
|
|
1950
|
+
}
|
|
1951
|
+
oauthHeaders.set("Content-Type", "application/x-www-form-urlencoded");
|
|
1952
|
+
const entries = body instanceof URLSearchParams ? Array.from(body.entries()) : Object.entries(body).filter((entry) => typeof entry[1] === "string");
|
|
1953
|
+
const params = new URLSearchParams(entries);
|
|
1954
|
+
if (authIn === "body") {
|
|
1955
|
+
params.set("client_id", clientId);
|
|
1956
|
+
clientSecret && params.set("client_secret", clientSecret);
|
|
1957
|
+
}
|
|
1958
|
+
return new Request(url, {
|
|
1959
|
+
body: params.toString(),
|
|
1960
|
+
headers: oauthHeaders,
|
|
1961
|
+
method: "POST"
|
|
1962
|
+
});
|
|
1786
1963
|
};
|
|
1787
1964
|
var decodeBase64 = (input, toUint8Array = false) => {
|
|
1788
1965
|
const b64 = input.replace(/-/g, "+").replace(/_/g, "/") + "==".slice(0, (BASE64_BLOCK_SIZE - input.length % BASE64_BLOCK_SIZE) % BASE64_BLOCK_SIZE);
|
|
@@ -1796,12 +1973,6 @@ var decodeBase64 = (input, toUint8Array = false) => {
|
|
|
1796
1973
|
}
|
|
1797
1974
|
return bytes;
|
|
1798
1975
|
};
|
|
1799
|
-
var hmacSha256 = async (message, secret) => {
|
|
1800
|
-
const encoder = new TextEncoder;
|
|
1801
|
-
const key = await crypto.subtle.importKey("raw", encoder.encode(secret), { hash: "SHA-256", name: "HMAC" }, false, ["sign"]);
|
|
1802
|
-
const sigBuffer = await crypto.subtle.sign("HMAC", key, encoder.encode(message));
|
|
1803
|
-
return Array.from(new Uint8Array(sigBuffer)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1804
|
-
};
|
|
1805
1976
|
var decodeJWT = (tokenString) => {
|
|
1806
1977
|
const [headerSegment, payloadSegment, signatureSegment] = tokenString.split(".");
|
|
1807
1978
|
if (!headerSegment || !payloadSegment || !signatureSegment) {
|
|
@@ -1811,7 +1982,18 @@ var decodeJWT = (tokenString) => {
|
|
|
1811
1982
|
if (typeof decodedPayload !== "string") {
|
|
1812
1983
|
throw new Error("Expected JWT payload to be a UTF-8 string");
|
|
1813
1984
|
}
|
|
1814
|
-
|
|
1985
|
+
const claims = JSON.parse(decodedPayload);
|
|
1986
|
+
return claims;
|
|
1987
|
+
};
|
|
1988
|
+
var encodeBase64 = (input) => {
|
|
1989
|
+
let raw;
|
|
1990
|
+
if (typeof input === "string") {
|
|
1991
|
+
raw = input;
|
|
1992
|
+
} else {
|
|
1993
|
+
const bytes = input instanceof Uint8Array ? input : new Uint8Array(input);
|
|
1994
|
+
raw = bytes.reduce((acc, byte) => acc + String.fromCharCode(byte), "");
|
|
1995
|
+
}
|
|
1996
|
+
return btoa(raw);
|
|
1815
1997
|
};
|
|
1816
1998
|
var getWithingsProps = async (config) => {
|
|
1817
1999
|
const timestamp = Math.floor(Date.now() / 1000);
|
|
@@ -1822,7 +2004,9 @@ var getWithingsProps = async (config) => {
|
|
|
1822
2004
|
nonceUrl.searchParams.set("client_id", config.clientId);
|
|
1823
2005
|
nonceUrl.searchParams.set("timestamp", timestamp.toString());
|
|
1824
2006
|
nonceUrl.searchParams.set("signature", hashedSignature);
|
|
1825
|
-
const
|
|
2007
|
+
const nonceTarget = nonceUrl.toString();
|
|
2008
|
+
const nonceResponse = await fetch(nonceTarget, {
|
|
2009
|
+
...h2IfHttps(nonceTarget),
|
|
1826
2010
|
method: "POST"
|
|
1827
2011
|
});
|
|
1828
2012
|
const nonceData = await nonceResponse.json();
|
|
@@ -1834,44 +2018,15 @@ var getWithingsProps = async (config) => {
|
|
|
1834
2018
|
}
|
|
1835
2019
|
return;
|
|
1836
2020
|
};
|
|
1837
|
-
var
|
|
1838
|
-
url
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
const oauthHeaders = new Headers(headers);
|
|
1847
|
-
oauthHeaders.set("Accept", "application/json");
|
|
1848
|
-
oauthHeaders.set("User-Agent", "citra");
|
|
1849
|
-
if (authIn === "header") {
|
|
1850
|
-
if (!clientSecret) {
|
|
1851
|
-
throw new Error("clientSecret required for header auth");
|
|
1852
|
-
}
|
|
1853
|
-
oauthHeaders.set("Authorization", `Basic ${encodeBase64(`${clientId}:${clientSecret}`)}`);
|
|
1854
|
-
}
|
|
1855
|
-
if (encoding === "application/json") {
|
|
1856
|
-
oauthHeaders.set("Content-Type", "application/json");
|
|
1857
|
-
return new Request(url, {
|
|
1858
|
-
body: JSON.stringify(body),
|
|
1859
|
-
headers: oauthHeaders,
|
|
1860
|
-
method: "POST"
|
|
1861
|
-
});
|
|
1862
|
-
}
|
|
1863
|
-
oauthHeaders.set("Content-Type", "application/x-www-form-urlencoded");
|
|
1864
|
-
const entries = body instanceof URLSearchParams ? Array.from(body.entries()) : Object.entries(body).filter((entry) => typeof entry[1] === "string");
|
|
1865
|
-
const params = new URLSearchParams(entries);
|
|
1866
|
-
if (authIn === "body") {
|
|
1867
|
-
params.set("client_id", clientId);
|
|
1868
|
-
clientSecret && params.set("client_secret", clientSecret);
|
|
1869
|
-
}
|
|
1870
|
-
return new Request(url, {
|
|
1871
|
-
body: params.toString(),
|
|
1872
|
-
headers: oauthHeaders,
|
|
1873
|
-
method: "POST"
|
|
1874
|
-
});
|
|
2021
|
+
var h2IfHttps = (url) => {
|
|
2022
|
+
const init = url.startsWith("https://") ? { protocol: "http2" } : {};
|
|
2023
|
+
return init;
|
|
2024
|
+
};
|
|
2025
|
+
var hmacSha256 = async (message, secret) => {
|
|
2026
|
+
const encoder = new TextEncoder;
|
|
2027
|
+
const key = await crypto.subtle.importKey("raw", encoder.encode(secret), { hash: "SHA-256", name: "HMAC" }, false, ["sign"]);
|
|
2028
|
+
const sigBuffer = await crypto.subtle.sign("HMAC", key, encoder.encode(message));
|
|
2029
|
+
return Array.from(new Uint8Array(sigBuffer)).map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
1875
2030
|
};
|
|
1876
2031
|
var extractPropFromIdentity = (identity, keys, propType) => {
|
|
1877
2032
|
let value = identity;
|
|
@@ -1896,9 +2051,7 @@ var setPropInIdentity = (identity, keys, value) => {
|
|
|
1896
2051
|
let cursor = identity;
|
|
1897
2052
|
for (const key of keys.slice(0, -1)) {
|
|
1898
2053
|
const next = cursor[key];
|
|
1899
|
-
|
|
1900
|
-
cursor[key] = {};
|
|
1901
|
-
}
|
|
2054
|
+
cursor[key] = isObject(next) ? next : {};
|
|
1902
2055
|
cursor = cursor[key];
|
|
1903
2056
|
}
|
|
1904
2057
|
cursor[keys[keys.length - 1]] = value;
|
|
@@ -1930,18 +2083,251 @@ var createRandomBase64UrlGenerator = (length) => () => {
|
|
|
1930
2083
|
var generateCodeVerifier = createRandomBase64UrlGenerator(NUM_GENERATOR_BYTES);
|
|
1931
2084
|
var generateState = createRandomBase64UrlGenerator(NUM_GENERATOR_BYTES);
|
|
1932
2085
|
var base64Url = (input) => encodeBase64(input).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
2086
|
+
var ALG_ES256 = "ES256";
|
|
2087
|
+
var ALG_RS256 = "RS256";
|
|
2088
|
+
var CLOCK_SKEW_SECONDS = 60;
|
|
2089
|
+
var DEFAULT_SCOPES = ["openid", "email", "profile"];
|
|
2090
|
+
var JWKS_REFETCH_COOLDOWN_MS = 60000;
|
|
2091
|
+
var JWT_SEGMENT_COUNT = 3;
|
|
2092
|
+
var MILLISECONDS_PER_SECOND = 1000;
|
|
2093
|
+
var trimTrailingSlash = (value) => value.endsWith("/") ? value.slice(0, -1) : value;
|
|
2094
|
+
var fetchJson = async (url) => {
|
|
2095
|
+
const response = await fetch(url, {
|
|
2096
|
+
headers: { accept: "application/json" }
|
|
2097
|
+
});
|
|
2098
|
+
if (!response.ok) {
|
|
2099
|
+
throw new Error(`Request to ${url} failed with status ${response.status}`);
|
|
2100
|
+
}
|
|
2101
|
+
return response.json();
|
|
2102
|
+
};
|
|
2103
|
+
var decodeJsonSegment = (segment) => {
|
|
2104
|
+
const decoded = decodeBase64(segment);
|
|
2105
|
+
if (typeof decoded !== "string") {
|
|
2106
|
+
throw new Error("Expected a base64url-encoded JWT segment");
|
|
2107
|
+
}
|
|
2108
|
+
const parsed = JSON.parse(decoded);
|
|
2109
|
+
if (typeof parsed !== "object" || parsed === null) {
|
|
2110
|
+
throw new Error("Expected a JWT segment to decode to a JSON object");
|
|
2111
|
+
}
|
|
2112
|
+
return parsed;
|
|
2113
|
+
};
|
|
2114
|
+
var importVerificationKey = (jwk, alg) => {
|
|
2115
|
+
if (alg === ALG_RS256) {
|
|
2116
|
+
return crypto.subtle.importKey("jwk", jwk, { hash: "SHA-256", name: "RSASSA-PKCS1-v1_5" }, false, ["verify"]);
|
|
2117
|
+
}
|
|
2118
|
+
if (alg === ALG_ES256) {
|
|
2119
|
+
return crypto.subtle.importKey("jwk", jwk, { name: "ECDSA", namedCurve: "P-256" }, false, ["verify"]);
|
|
2120
|
+
}
|
|
2121
|
+
throw new Error(`Unsupported id_token signing algorithm "${alg}"`);
|
|
2122
|
+
};
|
|
2123
|
+
var verifySignature = (key, alg, signingInput, signature) => {
|
|
2124
|
+
const algorithm = alg === ALG_ES256 ? { hash: "SHA-256", name: "ECDSA" } : { name: "RSASSA-PKCS1-v1_5" };
|
|
2125
|
+
return crypto.subtle.verify(algorithm, key, new Uint8Array(signature), new TextEncoder().encode(signingInput));
|
|
2126
|
+
};
|
|
2127
|
+
var selectKey = (jwks, kid) => jwks.find((jwk) => kid === undefined || jwk.kid === kid);
|
|
2128
|
+
var assertClaims = (payload, expected) => {
|
|
2129
|
+
const aud = Reflect.get(payload, "aud");
|
|
2130
|
+
const exp = Reflect.get(payload, "exp");
|
|
2131
|
+
const iss = Reflect.get(payload, "iss");
|
|
2132
|
+
const sub = Reflect.get(payload, "sub");
|
|
2133
|
+
const audiences = Array.isArray(aud) ? aud : [aud];
|
|
2134
|
+
const nowSeconds = Math.floor(Date.now() / MILLISECONDS_PER_SECOND);
|
|
2135
|
+
if (iss !== expected.issuer) {
|
|
2136
|
+
throw new Error('id_token "iss" does not match the provider issuer');
|
|
2137
|
+
}
|
|
2138
|
+
if (typeof sub !== "string" || sub.length === 0) {
|
|
2139
|
+
throw new Error('id_token is missing "sub"');
|
|
2140
|
+
}
|
|
2141
|
+
if (!audiences.includes(expected.audience)) {
|
|
2142
|
+
throw new Error('id_token "aud" does not include the client id');
|
|
2143
|
+
}
|
|
2144
|
+
if (typeof exp !== "number" || exp + CLOCK_SKEW_SECONDS < nowSeconds) {
|
|
2145
|
+
throw new Error("id_token has expired");
|
|
2146
|
+
}
|
|
2147
|
+
if (expected.nonce !== undefined && Reflect.get(payload, "nonce") !== expected.nonce) {
|
|
2148
|
+
throw new Error('id_token "nonce" does not match');
|
|
2149
|
+
}
|
|
2150
|
+
const claims = { ...payload, aud, exp, iss, sub };
|
|
2151
|
+
return claims;
|
|
2152
|
+
};
|
|
2153
|
+
var verifyIdToken = async ({
|
|
2154
|
+
audience,
|
|
2155
|
+
idToken,
|
|
2156
|
+
issuer,
|
|
2157
|
+
jwks,
|
|
2158
|
+
nonce
|
|
2159
|
+
}) => {
|
|
2160
|
+
const segments = idToken.split(".");
|
|
2161
|
+
const [headerSegment, payloadSegment, signatureSegment] = segments;
|
|
2162
|
+
if (segments.length !== JWT_SEGMENT_COUNT || headerSegment === undefined || payloadSegment === undefined || signatureSegment === undefined) {
|
|
2163
|
+
throw new Error("Invalid id_token: expected three segments");
|
|
2164
|
+
}
|
|
2165
|
+
const header = decodeJsonSegment(headerSegment);
|
|
2166
|
+
const alg = Reflect.get(header, "alg");
|
|
2167
|
+
const kid = Reflect.get(header, "kid");
|
|
2168
|
+
if (typeof alg !== "string") {
|
|
2169
|
+
throw new Error('id_token header is missing "alg"');
|
|
2170
|
+
}
|
|
2171
|
+
const jwk = selectKey(jwks, typeof kid === "string" ? kid : undefined);
|
|
2172
|
+
if (jwk === undefined) {
|
|
2173
|
+
throw new Error('No JWKS key matches the id_token "kid"');
|
|
2174
|
+
}
|
|
2175
|
+
const key = await importVerificationKey(jwk, alg);
|
|
2176
|
+
const signature = decodeBase64(signatureSegment, true);
|
|
2177
|
+
if (typeof signature === "string") {
|
|
2178
|
+
throw new Error("Failed to decode the id_token signature");
|
|
2179
|
+
}
|
|
2180
|
+
const isValid = await verifySignature(key, alg, `${headerSegment}.${payloadSegment}`, signature);
|
|
2181
|
+
if (!isValid) {
|
|
2182
|
+
throw new Error("id_token signature verification failed");
|
|
2183
|
+
}
|
|
2184
|
+
return assertClaims(decodeJsonSegment(payloadSegment), {
|
|
2185
|
+
audience,
|
|
2186
|
+
issuer,
|
|
2187
|
+
nonce
|
|
2188
|
+
});
|
|
2189
|
+
};
|
|
2190
|
+
var toDiscoveryDocument = (value, expectedIssuer) => {
|
|
2191
|
+
if (typeof value !== "object" || value === null) {
|
|
2192
|
+
throw new Error("OIDC discovery document is not a JSON object");
|
|
2193
|
+
}
|
|
2194
|
+
const issuer = Reflect.get(value, "issuer");
|
|
2195
|
+
const authorizationEndpoint = Reflect.get(value, "authorization_endpoint");
|
|
2196
|
+
const tokenEndpoint = Reflect.get(value, "token_endpoint");
|
|
2197
|
+
const jwksUri = Reflect.get(value, "jwks_uri");
|
|
2198
|
+
const userinfoEndpoint = Reflect.get(value, "userinfo_endpoint");
|
|
2199
|
+
if (typeof issuer !== "string" || typeof authorizationEndpoint !== "string" || typeof tokenEndpoint !== "string" || typeof jwksUri !== "string") {
|
|
2200
|
+
throw new Error("OIDC discovery document is missing required endpoints");
|
|
2201
|
+
}
|
|
2202
|
+
if (trimTrailingSlash(issuer) !== trimTrailingSlash(expectedIssuer)) {
|
|
2203
|
+
throw new Error(`OIDC discovery issuer "${issuer}" does not match configured issuer "${expectedIssuer}"`);
|
|
2204
|
+
}
|
|
2205
|
+
return {
|
|
2206
|
+
authorization_endpoint: authorizationEndpoint,
|
|
2207
|
+
issuer,
|
|
2208
|
+
jwks_uri: jwksUri,
|
|
2209
|
+
token_endpoint: tokenEndpoint,
|
|
2210
|
+
userinfo_endpoint: typeof userinfoEndpoint === "string" ? userinfoEndpoint : undefined
|
|
2211
|
+
};
|
|
2212
|
+
};
|
|
2213
|
+
var fetchDiscoveryDocument = async (issuer) => {
|
|
2214
|
+
const base = trimTrailingSlash(issuer);
|
|
2215
|
+
const document = await fetchJson(`${base}/.well-known/openid-configuration`);
|
|
2216
|
+
return toDiscoveryDocument(document, issuer);
|
|
2217
|
+
};
|
|
2218
|
+
var fetchJwks = async (jwksUri) => {
|
|
2219
|
+
const body = await fetchJson(jwksUri);
|
|
2220
|
+
const value = Reflect.get(body ?? {}, "keys");
|
|
2221
|
+
if (!Array.isArray(value)) {
|
|
2222
|
+
throw new Error('JWKS response is missing a "keys" array');
|
|
2223
|
+
}
|
|
2224
|
+
const keys = value;
|
|
2225
|
+
return keys;
|
|
2226
|
+
};
|
|
2227
|
+
var createOIDCClient = async (config) => {
|
|
2228
|
+
const discovery = await fetchDiscoveryDocument(config.issuer);
|
|
2229
|
+
const scopes = config.scopes !== undefined && config.scopes.length > 0 ? config.scopes : DEFAULT_SCOPES;
|
|
2230
|
+
let cachedJwks;
|
|
2231
|
+
let jwksFetchedAtMs = 0;
|
|
2232
|
+
const resolveJwks = async (forceRefresh) => {
|
|
2233
|
+
const isStale = Date.now() - jwksFetchedAtMs > JWKS_REFETCH_COOLDOWN_MS;
|
|
2234
|
+
if (cachedJwks === undefined || forceRefresh && isStale) {
|
|
2235
|
+
cachedJwks = await fetchJwks(discovery.jwks_uri);
|
|
2236
|
+
jwksFetchedAtMs = Date.now();
|
|
2237
|
+
}
|
|
2238
|
+
return cachedJwks;
|
|
2239
|
+
};
|
|
2240
|
+
const createAuthorizationUrl = async (options) => {
|
|
2241
|
+
const url = new URL(discovery.authorization_endpoint);
|
|
2242
|
+
const challenge = await createS256CodeChallenge(options.codeVerifier);
|
|
2243
|
+
const { searchParams } = url;
|
|
2244
|
+
searchParams.set("client_id", config.clientId);
|
|
2245
|
+
searchParams.set("code_challenge", challenge);
|
|
2246
|
+
searchParams.set("code_challenge_method", "S256");
|
|
2247
|
+
searchParams.set("redirect_uri", config.redirectUri);
|
|
2248
|
+
searchParams.set("response_type", "code");
|
|
2249
|
+
searchParams.set("scope", (options.scope ?? scopes).join(" "));
|
|
2250
|
+
searchParams.set("state", options.state);
|
|
2251
|
+
if (options.nonce !== undefined) {
|
|
2252
|
+
searchParams.set("nonce", options.nonce);
|
|
2253
|
+
}
|
|
2254
|
+
return url;
|
|
2255
|
+
};
|
|
2256
|
+
const validateAuthorizationCode = async (options) => {
|
|
2257
|
+
const body = new URLSearchParams;
|
|
2258
|
+
body.set("client_id", config.clientId);
|
|
2259
|
+
body.set("client_secret", config.clientSecret);
|
|
2260
|
+
body.set("code", options.code);
|
|
2261
|
+
body.set("code_verifier", options.codeVerifier);
|
|
2262
|
+
body.set("grant_type", "authorization_code");
|
|
2263
|
+
body.set("redirect_uri", config.redirectUri);
|
|
2264
|
+
const response = await fetch(discovery.token_endpoint, {
|
|
2265
|
+
body,
|
|
2266
|
+
headers: {
|
|
2267
|
+
accept: "application/json",
|
|
2268
|
+
"content-type": "application/x-www-form-urlencoded"
|
|
2269
|
+
},
|
|
2270
|
+
method: "POST"
|
|
2271
|
+
});
|
|
2272
|
+
if (!response.ok) {
|
|
2273
|
+
throw new Error(`OIDC token request failed with status ${response.status}`);
|
|
2274
|
+
}
|
|
2275
|
+
const tokens = await response.json();
|
|
2276
|
+
return tokens;
|
|
2277
|
+
};
|
|
2278
|
+
const verifyToken = async (idToken, options) => {
|
|
2279
|
+
const jwks = await resolveJwks(false);
|
|
2280
|
+
const params = {
|
|
2281
|
+
audience: config.clientId,
|
|
2282
|
+
idToken,
|
|
2283
|
+
issuer: discovery.issuer,
|
|
2284
|
+
nonce: options?.nonce
|
|
2285
|
+
};
|
|
2286
|
+
try {
|
|
2287
|
+
return await verifyIdToken({ ...params, jwks });
|
|
2288
|
+
} catch (error) {
|
|
2289
|
+
const refreshed = await resolveJwks(true);
|
|
2290
|
+
if (refreshed === jwks)
|
|
2291
|
+
throw error;
|
|
2292
|
+
return verifyIdToken({ ...params, jwks: refreshed });
|
|
2293
|
+
}
|
|
2294
|
+
};
|
|
2295
|
+
const fetchUserProfile = async (accessToken) => {
|
|
2296
|
+
if (discovery.userinfo_endpoint === undefined) {
|
|
2297
|
+
throw new Error("OIDC provider does not expose a userinfo endpoint");
|
|
2298
|
+
}
|
|
2299
|
+
const response = await fetch(discovery.userinfo_endpoint, {
|
|
2300
|
+
headers: {
|
|
2301
|
+
accept: "application/json",
|
|
2302
|
+
authorization: `Bearer ${accessToken}`
|
|
2303
|
+
}
|
|
2304
|
+
});
|
|
2305
|
+
if (!response.ok) {
|
|
2306
|
+
throw new Error(`OIDC userinfo request failed with status ${response.status}`);
|
|
2307
|
+
}
|
|
2308
|
+
const profile = await response.json();
|
|
2309
|
+
return profile;
|
|
2310
|
+
};
|
|
2311
|
+
return {
|
|
2312
|
+
createAuthorizationUrl,
|
|
2313
|
+
discovery,
|
|
2314
|
+
fetchUserProfile,
|
|
2315
|
+
validateAuthorizationCode,
|
|
2316
|
+
verifyIdToken: verifyToken
|
|
2317
|
+
};
|
|
2318
|
+
};
|
|
2319
|
+
var oidcProviderOptions = Object.keys(providers).filter(isOIDCProviderOption);
|
|
2320
|
+
var pkceProviderOptions = Object.keys(providers).filter(isPKCEProviderOption);
|
|
1933
2321
|
var providerOptions = Object.keys(providers).filter(isValidProviderOption);
|
|
1934
2322
|
var refreshableProviderOptions = Object.keys(providers).filter(isRefreshableProviderOption);
|
|
1935
|
-
var oidcProviderOptions = Object.keys(providers).filter(isOIDCProviderOption);
|
|
1936
2323
|
var revocableProviderOptions = Object.keys(providers).filter(isRevocableProviderOption);
|
|
1937
2324
|
var scopeRequiredProviderOptions = Object.keys(providers).filter(isScopeRequiredProviderOption);
|
|
1938
|
-
var pkceProviderOptions = Object.keys(providers).filter(isPKCEProviderOption);
|
|
1939
2325
|
var createOAuth2Client = async (providerName, config) => {
|
|
1940
2326
|
const meta = providers[providerName];
|
|
1941
2327
|
const isConfigPropertyFunction = (cfgProp) => typeof cfgProp === "function";
|
|
1942
2328
|
const resolveConfigProp = async (cfgProp) => {
|
|
1943
2329
|
const result = isConfigPropertyFunction(cfgProp) ? cfgProp(config) : cfgProp;
|
|
1944
|
-
return
|
|
2330
|
+
return result;
|
|
1945
2331
|
};
|
|
1946
2332
|
const authorizationUrl = await resolveConfigProp(meta.authorizationUrl);
|
|
1947
2333
|
const tokenUrl = await resolveConfigProp(meta.tokenRequest.url);
|
|
@@ -1993,18 +2379,22 @@ var createOAuth2Client = async (providerName, config) => {
|
|
|
1993
2379
|
headerEntries = rawHeaders;
|
|
1994
2380
|
else if (rawHeaders && typeof rawHeaders === "object")
|
|
1995
2381
|
headerEntries = Object.entries(rawHeaders);
|
|
1996
|
-
const profileHeaders = Object.fromEntries(headerEntries.filter(([,
|
|
2382
|
+
const profileHeaders = Object.fromEntries(headerEntries.filter(([, value]) => value !== ""));
|
|
1997
2383
|
if (authIn === "header") {
|
|
1998
2384
|
profileHeaders.Authorization = `Bearer ${accessToken}`;
|
|
1999
2385
|
} else {
|
|
2000
2386
|
endpoint.searchParams.append("access_token", accessToken);
|
|
2001
2387
|
}
|
|
2002
2388
|
const init = { headers: profileHeaders, method };
|
|
2003
|
-
if (method === "POST" && resolvedBody
|
|
2389
|
+
if (method === "POST" && resolvedBody !== undefined) {
|
|
2004
2390
|
profileHeaders["Content-Type"] = encoding;
|
|
2005
2391
|
init.body = encoding === "application/json" ? JSON.stringify(resolvedBody) : new URLSearchParams(resolvedBody).toString();
|
|
2006
2392
|
}
|
|
2007
|
-
const
|
|
2393
|
+
const profileTarget = endpoint.toString();
|
|
2394
|
+
const response = await fetch(profileTarget, {
|
|
2395
|
+
...h2IfHttps(profileTarget),
|
|
2396
|
+
...init
|
|
2397
|
+
});
|
|
2008
2398
|
if (!response.ok)
|
|
2009
2399
|
throw await createOAuth2FetchError(response);
|
|
2010
2400
|
return response.json();
|
|
@@ -2029,7 +2419,9 @@ var createOAuth2Client = async (providerName, config) => {
|
|
|
2029
2419
|
encoding,
|
|
2030
2420
|
url: tokenUrl
|
|
2031
2421
|
});
|
|
2032
|
-
const response = await fetch(request
|
|
2422
|
+
const response = await fetch(request, {
|
|
2423
|
+
...h2IfHttps(request.url)
|
|
2424
|
+
});
|
|
2033
2425
|
if (!response.ok)
|
|
2034
2426
|
throw await createOAuth2FetchError(response);
|
|
2035
2427
|
return response.json();
|
|
@@ -2083,7 +2475,9 @@ var createOAuth2Client = async (providerName, config) => {
|
|
|
2083
2475
|
url: `${endpoint.toString()}?${tokenParamName}=${token}`
|
|
2084
2476
|
});
|
|
2085
2477
|
}
|
|
2086
|
-
const response = await fetch(request
|
|
2478
|
+
const response = await fetch(request, {
|
|
2479
|
+
...h2IfHttps(request.url)
|
|
2480
|
+
});
|
|
2087
2481
|
if (!response.ok)
|
|
2088
2482
|
throw await createOAuth2FetchError(response);
|
|
2089
2483
|
},
|
|
@@ -2115,7 +2509,9 @@ var createOAuth2Client = async (providerName, config) => {
|
|
|
2115
2509
|
encoding,
|
|
2116
2510
|
url: tokenUrl
|
|
2117
2511
|
});
|
|
2118
|
-
const response = await fetch(request
|
|
2512
|
+
const response = await fetch(request, {
|
|
2513
|
+
...h2IfHttps(request.url)
|
|
2514
|
+
});
|
|
2119
2515
|
if (!response.ok)
|
|
2120
2516
|
throw await createOAuth2FetchError(response);
|
|
2121
2517
|
return response.json();
|
|
@@ -2208,5 +2604,5 @@ export {
|
|
|
2208
2604
|
escapeHtml
|
|
2209
2605
|
};
|
|
2210
2606
|
|
|
2211
|
-
//# debugId=
|
|
2607
|
+
//# debugId=E36225136305103F64756E2164756E21
|
|
2212
2608
|
//# sourceMappingURL=index.js.map
|