@muze-nl/metro-oidc 0.3.2 → 0.3.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/README.md CHANGED
@@ -24,7 +24,7 @@ async function fetchMovies() {
24
24
  ````
25
25
 
26
26
  The OIDC middleware will automatically discover the configuration of the issuer, as well as do a dynamic client registration, if you haven't set a `client_info.client_id`.
27
- It will then configure the correct OAuth2 settings and handle the request with [metro oauth2](https://github.com/muze-nl/metro-oauth2) middleware. It may redirect the browser to let the user login with the OIDC issuer. You can skip the automatic configuration step, if you provide the `openid_configuration` parameter set yourself, the oidcmw middleware will only do this once, and store the information in localStorage. The same with the client_info.
27
+ It will then configure the correct OAuth2 settings and handle the request with [metro oauth2](https://github.com/muze-nl/metro-oauth2) middleware. It may redirect the browser to let the user login with the OIDC issuer. You can skip the automatic configuration step, if you provide the `openid_configuration` parameter set yourself. If you don't, the oidcmw middleware will only run the discovery process once, and store the information in localStorage. The same with the client_info and dynamic registration.
28
28
 
29
29
  ## Security features
30
30
 
@@ -32,3 +32,16 @@ metro.oidc uses OAuth2.1 by default, including PKCE and DPoP. The Keypair used i
32
32
 
33
33
  You can disable PCKE by setting `options.client_info.code_verifier` to false.
34
34
  You can disable DPoP by setting `options.use_dpop` to false.
35
+
36
+ ## id_token
37
+
38
+ The metro oidc middleware doesn't use the id_token, or verify any of its contents. This is left up to you, if you need it. You can retrieve the id_token by calling the `idToken` function, after the user is logged in:
39
+
40
+ ```javascript
41
+ import oidc from '@muze-nl/metro-oidc'
42
+
43
+ let id_token = oidc.idToken()
44
+ ```
45
+
46
+ [project-stage-badge: Experimental]: https://img.shields.io/badge/Project%20Stage-Experimental-yellow.svg
47
+ [project-stage-page]: https://blog.pother.ca/project-stages/
package/dist/browser.js CHANGED
@@ -599,13 +599,13 @@
599
599
  // node_modules/@muze-nl/metro-oauth2/src/oauth2.mjs
600
600
  var oauth2_exports = {};
601
601
  __export(oauth2_exports, {
602
- authorizePopup: () => authorizePopup,
603
602
  base64url_encode: () => base64url_encode,
604
603
  createState: () => createState,
605
604
  default: () => oauth2mw,
606
605
  generateCodeChallenge: () => generateCodeChallenge,
607
606
  generateCodeVerifier: () => generateCodeVerifier,
608
607
  getExpires: () => getExpires,
608
+ isAuthorized: () => isAuthorized,
609
609
  isExpired: () => isExpired,
610
610
  isRedirected: () => isRedirected
611
611
  });
@@ -632,15 +632,15 @@
632
632
  return false;
633
633
  }
634
634
  }
635
- return error("data does not match oneOf patterns", data, patterns);
635
+ return error2("data does not match oneOf patterns", data, patterns);
636
636
  };
637
637
  var anyOf = (...patterns) => (data) => {
638
638
  if (!Array.isArray(data)) {
639
- return error("data is not an array", data, "anyOf");
639
+ return error2("data is not an array", data, "anyOf");
640
640
  }
641
641
  for (let value of data) {
642
642
  if (oneOf(...patterns)(value)) {
643
- return error("data does not match anyOf patterns", value, patterns);
643
+ return error2("data does not match anyOf patterns", value, patterns);
644
644
  }
645
645
  }
646
646
  return false;
@@ -652,10 +652,10 @@
652
652
  }
653
653
  let url2 = new URL(data);
654
654
  if (url2.href != data) {
655
- return error("data is not a valid url", data, "validURL");
655
+ return error2("data is not a valid url", data, "validURL");
656
656
  }
657
657
  } catch (e) {
658
- return error("data is not a valid url", data, "validURL");
658
+ return error2("data is not a valid url", data, "validURL");
659
659
  }
660
660
  return false;
661
661
  }
@@ -666,28 +666,28 @@
666
666
  let problems = [];
667
667
  if (pattern === Boolean) {
668
668
  if (typeof data != "boolean") {
669
- problems.push(error("data is not a boolean", data, pattern));
669
+ problems.push(error2("data is not a boolean", data, pattern));
670
670
  }
671
671
  } else if (pattern === Number) {
672
672
  if (typeof data != "number") {
673
- problems.push(error("data is not a number", data, pattern));
673
+ problems.push(error2("data is not a number", data, pattern));
674
674
  }
675
675
  } else if (pattern instanceof RegExp) {
676
676
  if (Array.isArray(data)) {
677
677
  let index = data.findIndex((element) => fails(element, pattern, root));
678
678
  if (index > -1) {
679
- problems.push(error("data[" + index + "] does not match pattern", data[index], pattern));
679
+ problems.push(error2("data[" + index + "] does not match pattern", data[index], pattern));
680
680
  }
681
681
  } else if (!pattern.test(data)) {
682
- problems.push(error("data does not match pattern", data, pattern));
682
+ problems.push(error2("data does not match pattern", data, pattern));
683
683
  }
684
684
  } else if (pattern instanceof Function) {
685
685
  if (pattern(data, root)) {
686
- problems.push(error("data does not match function", data, pattern));
686
+ problems.push(error2("data does not match function", data, pattern));
687
687
  }
688
688
  } else if (Array.isArray(pattern)) {
689
689
  if (!Array.isArray(data)) {
690
- problems.push(error("data is not an array", data, []));
690
+ problems.push(error2("data is not an array", data, []));
691
691
  }
692
692
  for (p of pattern) {
693
693
  let problem = fails(data, p, root);
@@ -701,10 +701,10 @@
701
701
  if (Array.isArray(data)) {
702
702
  let index = data.findIndex((element) => fails(element, pattern, root));
703
703
  if (index > -1) {
704
- problems.push(error("data[" + index + "] does not match pattern", data[index], pattern));
704
+ problems.push(error2("data[" + index + "] does not match pattern", data[index], pattern));
705
705
  }
706
706
  } else if (!data || typeof data != "object") {
707
- problems.push(error("data is not an object, pattern is", data, pattern));
707
+ problems.push(error2("data is not an object, pattern is", data, pattern));
708
708
  } else {
709
709
  if (data instanceof URLSearchParams) {
710
710
  data = Object.fromEntries(data);
@@ -715,7 +715,7 @@
715
715
  if (result) {
716
716
  if (!p2 || typeof p2 == "string") {
717
717
  p2 = {};
718
- problems.push(error(p2, data[wKey], wVal));
718
+ problems.push(error2(p2, data[wKey], wVal));
719
719
  }
720
720
  p2[wKey] = result.problems;
721
721
  }
@@ -723,7 +723,7 @@
723
723
  }
724
724
  } else {
725
725
  if (pattern != data) {
726
- problems.push(error("data and pattern are not equal", data, pattern));
726
+ problems.push(error2("data and pattern are not equal", data, pattern));
727
727
  }
728
728
  }
729
729
  if (problems.length) {
@@ -738,7 +738,7 @@
738
738
  this.details = details;
739
739
  }
740
740
  };
741
- function error(message, found, expected) {
741
+ function error2(message, found, expected) {
742
742
  return {
743
743
  message,
744
744
  found,
@@ -1108,13 +1108,19 @@
1108
1108
  }
1109
1109
  return true;
1110
1110
  }
1111
- async function authorizePopup(authorizationCodeURL) {
1112
- return new Promise((resolve, reject) => {
1113
- addEventListener("oauth2authorized", (evt) => {
1114
- resolve(event.authorization_code);
1115
- }, { once: true });
1116
- window.open(authorizationCodeURL);
1117
- });
1111
+ function isAuthorized(tokens) {
1112
+ if (typeof tokens == "string") {
1113
+ tokens = tokenStore(tokens).tokens;
1114
+ }
1115
+ let accessToken = tokens.get("access_token");
1116
+ if (accessToken && !isExpired(accessToken)) {
1117
+ return true;
1118
+ }
1119
+ let refreshToken = tokens.get("refresh_token");
1120
+ if (refreshToken) {
1121
+ return true;
1122
+ }
1123
+ return false;
1118
1124
  }
1119
1125
 
1120
1126
  // node_modules/@muze-nl/metro-oauth2/src/oauth2.mockserver.mjs
@@ -1129,7 +1135,7 @@
1129
1135
  "Content-Type": "application/json"
1130
1136
  }
1131
1137
  };
1132
- var badRequest = (error4) => {
1138
+ var badRequest = (error5) => {
1133
1139
  return {
1134
1140
  status: 400,
1135
1141
  statusText: "Bad Request",
@@ -1138,11 +1144,11 @@
1138
1144
  },
1139
1145
  body: JSON.stringify({
1140
1146
  error: "invalid_request",
1141
- error_description: error4
1147
+ error_description: error5
1142
1148
  })
1143
1149
  };
1144
1150
  };
1145
- var error2;
1151
+ var error3;
1146
1152
  var pkce = {};
1147
1153
  function oauth2mockserver(options = {}) {
1148
1154
  const defaultOptions = {
@@ -1154,12 +1160,12 @@
1154
1160
  let url2 = everything_default.url(req.url);
1155
1161
  switch (url2.pathname) {
1156
1162
  case "/authorize/":
1157
- if (error2 = fails(url2.searchParams, {
1163
+ if (error3 = fails(url2.searchParams, {
1158
1164
  response_type: "code",
1159
1165
  client_id: "mockClientId",
1160
1166
  state: Optional(/.*/)
1161
1167
  })) {
1162
- return everything_default.response(badRequest(error2));
1168
+ return everything_default.response(badRequest(error3));
1163
1169
  }
1164
1170
  if (url2.searchParams.has("code_challenge")) {
1165
1171
  if (!url2.searchParams.has("code_challenge_method")) {
@@ -1181,17 +1187,17 @@
1181
1187
  req.data.forEach((value, key) => body[key] = value);
1182
1188
  req = req.with({ body });
1183
1189
  }
1184
- if (error2 = fails(req, {
1190
+ if (error3 = fails(req, {
1185
1191
  method: "POST",
1186
1192
  data: {
1187
1193
  grant_type: oneOf("refresh_token", "authorization_code")
1188
1194
  }
1189
1195
  })) {
1190
- return everything_default.response(badRequest(error2));
1196
+ return everything_default.response(badRequest(error3));
1191
1197
  }
1192
1198
  switch (req.data.grant_type) {
1193
1199
  case "refresh_token":
1194
- if (error2 = fails(req.data, oneOf({
1200
+ if (error3 = fails(req.data, oneOf({
1195
1201
  refresh_token: "mockRefreshToken",
1196
1202
  client_id: "mockClientId",
1197
1203
  client_secret: "mockClientSecret"
@@ -1200,11 +1206,11 @@
1200
1206
  client_id: "mockClientId",
1201
1207
  code_verifier: /.+/
1202
1208
  }))) {
1203
- return everything_default.response(badRequest(error2));
1209
+ return everything_default.response(badRequest(error3));
1204
1210
  }
1205
1211
  break;
1206
1212
  case "access_token":
1207
- if (error2 = fails(req.data, oneOf({
1213
+ if (error3 = fails(req.data, oneOf({
1208
1214
  client_id: "mockClientId",
1209
1215
  client_secret: "mockClientSecret"
1210
1216
  }, {
@@ -1213,7 +1219,7 @@
1213
1219
  //FIXME: check that this matches code_verifier
1214
1220
  code_challenge_method: "S256"
1215
1221
  }))) {
1216
- return everything_default.response(badRequest(error2));
1222
+ return everything_default.response(badRequest(error3));
1217
1223
  }
1218
1224
  break;
1219
1225
  }
@@ -1329,6 +1335,43 @@
1329
1335
  throw everything_default.metroError("metro.oidcmw: Error while fetching " + issuer + ".wellknown/oauth_authorization_server", res);
1330
1336
  }
1331
1337
 
1338
+ // node_modules/@muze-nl/metro-oauth2/src/oauth2.popup.mjs
1339
+ function handleRedirect() {
1340
+ let params2 = new URLSearchParams(window.location.search);
1341
+ if (!params2.has("code") && window.location.hash) {
1342
+ let query = window.location.hash.substr(1);
1343
+ params2 = new URLSearchParams("?" + query);
1344
+ }
1345
+ let parent = window.parent ? window.parent : window.opener;
1346
+ if (params2.has("code")) {
1347
+ parent.postMessage({
1348
+ authorization_code: params2.get("code")
1349
+ }, window.location.origin);
1350
+ } else if (params2.has("error")) {
1351
+ parent.postMessage({
1352
+ error
1353
+ }, window.location.origin);
1354
+ } else {
1355
+ parent.postMessage({
1356
+ error: "Could not find an authorization_code"
1357
+ }, window.location.origin);
1358
+ }
1359
+ }
1360
+ function authorizePopup(authorizationCodeURL) {
1361
+ return new Promise((resolve, reject) => {
1362
+ addEventListener("message", (evt) => {
1363
+ if (event.data.authorization_code) {
1364
+ resolve(event.data.authorization_code);
1365
+ } else if (event.data.error) {
1366
+ reject(event.data.error);
1367
+ } else {
1368
+ reject("Unknown authorization error");
1369
+ }
1370
+ }, { once: true });
1371
+ window.open(authorizationCodeURL);
1372
+ });
1373
+ }
1374
+
1332
1375
  // node_modules/@muze-nl/metro-oauth2/src/keysstore.mjs
1333
1376
  function keysStore() {
1334
1377
  return new Promise((resolve, reject) => {
@@ -1638,7 +1681,9 @@
1638
1681
  discover: oauth2_discovery_exports,
1639
1682
  tokenstore: tokenStore,
1640
1683
  dpopmw,
1641
- keysstore: keysStore
1684
+ keysstore: keysStore,
1685
+ authorizePopup,
1686
+ popupHandleRedirect: handleRedirect
1642
1687
  });
1643
1688
  if (!globalThis.metro.oauth2) {
1644
1689
  globalThis.metro.oauth2 = oauth2;
@@ -1667,7 +1712,7 @@
1667
1712
  function Required2(pattern) {
1668
1713
  return function _Required(data, root, path) {
1669
1714
  if (data == null || typeof data == "undefined") {
1670
- return error3("data is required", data, pattern || "any value", path);
1715
+ return error4("data is required", data, pattern || "any value", path);
1671
1716
  } else if (typeof pattern != "undefined") {
1672
1717
  return fails2(data, pattern, root, path);
1673
1718
  } else {
@@ -1692,17 +1737,17 @@
1692
1737
  return false;
1693
1738
  }
1694
1739
  }
1695
- return error3("data does not match oneOf patterns", data, patterns, path);
1740
+ return error4("data does not match oneOf patterns", data, patterns, path);
1696
1741
  };
1697
1742
  }
1698
1743
  function anyOf2(...patterns) {
1699
1744
  return function _anyOf(data, root, path) {
1700
1745
  if (!Array.isArray(data)) {
1701
- return error3("data is not an array", data, "anyOf", path);
1746
+ return error4("data is not an array", data, "anyOf", path);
1702
1747
  }
1703
1748
  for (let value of data) {
1704
1749
  if (oneOf2(...patterns)(value)) {
1705
- return error3("data does not match anyOf patterns", value, patterns, path);
1750
+ return error4("data does not match anyOf patterns", value, patterns, path);
1706
1751
  }
1707
1752
  }
1708
1753
  return false;
@@ -1716,7 +1761,7 @@
1716
1761
  }
1717
1762
  problems = problems.filter(Boolean);
1718
1763
  if (problems.length) {
1719
- return error3("data does not match all given patterns", data, patterns, path, problems);
1764
+ return error4("data does not match all given patterns", data, patterns, path, problems);
1720
1765
  }
1721
1766
  };
1722
1767
  }
@@ -1728,29 +1773,29 @@
1728
1773
  let url2 = new URL(data);
1729
1774
  if (url2.href != data) {
1730
1775
  if (!(url2.href + "/" == data || url2.href == data + "/")) {
1731
- return error3("data is not a valid url", data, "validURL", path);
1776
+ return error4("data is not a valid url", data, "validURL", path);
1732
1777
  }
1733
1778
  }
1734
1779
  } catch (e) {
1735
- return error3("data is not a valid url", data, "validURL", path);
1780
+ return error4("data is not a valid url", data, "validURL", path);
1736
1781
  }
1737
1782
  }
1738
1783
  function validEmail(data, root, path) {
1739
1784
  if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(data)) {
1740
- return error3("data is not a valid email", data, "validEmail", path);
1785
+ return error4("data is not a valid email", data, "validEmail", path);
1741
1786
  }
1742
1787
  }
1743
1788
  function instanceOf(constructor) {
1744
1789
  return function _instanceOf(data, root, path) {
1745
1790
  if (!(data instanceof constructor)) {
1746
- return error3("data is not an instanceof pattern", data, constructor, path);
1791
+ return error4("data is not an instanceof pattern", data, constructor, path);
1747
1792
  }
1748
1793
  };
1749
1794
  }
1750
1795
  function not(pattern) {
1751
1796
  return function _not(data, root, path) {
1752
1797
  if (!fails2(data, pattern, root, path)) {
1753
- return error3("data matches pattern, when required not to", data, pattern, path);
1798
+ return error4("data matches pattern, when required not to", data, pattern, path);
1754
1799
  }
1755
1800
  };
1756
1801
  }
@@ -1761,29 +1806,29 @@
1761
1806
  let problems = [];
1762
1807
  if (pattern === Boolean) {
1763
1808
  if (typeof data != "boolean" && !(data instanceof Boolean)) {
1764
- problems.push(error3("data is not a boolean", data, pattern, path));
1809
+ problems.push(error4("data is not a boolean", data, pattern, path));
1765
1810
  }
1766
1811
  } else if (pattern === Number) {
1767
1812
  if (typeof data != "number" && !(data instanceof Number)) {
1768
- problems.push(error3("data is not a number", data, pattern, path));
1813
+ problems.push(error4("data is not a number", data, pattern, path));
1769
1814
  }
1770
1815
  } else if (pattern === String) {
1771
1816
  if (typeof data != "string" && !(data instanceof String)) {
1772
- problems.push(error3("data is not a string", data, pattern, path));
1817
+ problems.push(error4("data is not a string", data, pattern, path));
1773
1818
  }
1774
1819
  if (data == "") {
1775
- problems.push(error3("data is an empty string, which is not allowed", data, pattern, path));
1820
+ problems.push(error4("data is an empty string, which is not allowed", data, pattern, path));
1776
1821
  }
1777
1822
  } else if (pattern instanceof RegExp) {
1778
1823
  if (Array.isArray(data)) {
1779
1824
  let index = data.findIndex((element, index2) => fails2(element, pattern, root, path + "[" + index2 + "]"));
1780
1825
  if (index > -1) {
1781
- problems.push(error3("data[" + index + "] does not match pattern", data[index], pattern, path + "[" + index + "]"));
1826
+ problems.push(error4("data[" + index + "] does not match pattern", data[index], pattern, path + "[" + index + "]"));
1782
1827
  }
1783
1828
  } else if (typeof data == "undefined") {
1784
- problems.push(error3("data is undefined, should match pattern", data, pattern, path));
1829
+ problems.push(error4("data is undefined, should match pattern", data, pattern, path));
1785
1830
  } else if (!pattern.test(data)) {
1786
- problems.push(error3("data does not match pattern", data, pattern, path));
1831
+ problems.push(error4("data does not match pattern", data, pattern, path));
1787
1832
  }
1788
1833
  } else if (pattern instanceof Function) {
1789
1834
  let problem = pattern(data, root, path);
@@ -1796,7 +1841,7 @@
1796
1841
  }
1797
1842
  } else if (Array.isArray(pattern)) {
1798
1843
  if (!Array.isArray(data)) {
1799
- problems.push(error3("data is not an array", data, [], path));
1844
+ problems.push(error4("data is not an array", data, [], path));
1800
1845
  }
1801
1846
  for (let p2 of pattern) {
1802
1847
  for (let index of data.keys()) {
@@ -1812,10 +1857,10 @@
1812
1857
  if (Array.isArray(data)) {
1813
1858
  let index = data.findIndex((element, index2) => fails2(element, pattern, root, path + "[" + index2 + "]"));
1814
1859
  if (index > -1) {
1815
- problems.push(error3("data[" + index + "] does not match pattern", data[index], pattern, path + "[" + index + "]"));
1860
+ problems.push(error4("data[" + index + "] does not match pattern", data[index], pattern, path + "[" + index + "]"));
1816
1861
  }
1817
1862
  } else if (!data || typeof data != "object") {
1818
- problems.push(error3("data is not an object, pattern is", data, pattern, path));
1863
+ problems.push(error4("data is not an object, pattern is", data, pattern, path));
1819
1864
  } else {
1820
1865
  if (data instanceof URLSearchParams) {
1821
1866
  data = Object.fromEntries(data);
@@ -1836,7 +1881,7 @@
1836
1881
  }
1837
1882
  } else {
1838
1883
  if (pattern != data) {
1839
- problems.push(error3("data and pattern are not equal", data, pattern, path));
1884
+ problems.push(error4("data and pattern are not equal", data, pattern, path));
1840
1885
  }
1841
1886
  }
1842
1887
  if (problems.length) {
@@ -1844,7 +1889,7 @@
1844
1889
  }
1845
1890
  return false;
1846
1891
  }
1847
- function error3(message, found, expected, path, problems) {
1892
+ function error4(message, found, expected, path, problems) {
1848
1893
  let result = {
1849
1894
  message,
1850
1895
  found,
@@ -1862,13 +1907,13 @@
1862
1907
  if (options.filter((o) => root.hasOwnKey(o)).length > 0) {
1863
1908
  return false;
1864
1909
  }
1865
- return error3("root data must have all of", root, options);
1910
+ return error4("root data must have all of", root, options);
1866
1911
  };
1867
1912
  var MustInclude = (...options) => (value) => {
1868
1913
  if (Array.isArray(value) && options.filter((o) => !value.includes(o)).length == 0) {
1869
1914
  return false;
1870
1915
  } else {
1871
- return error3("data must be an array which includes", value, options);
1916
+ return error4("data must be an array which includes", value, options);
1872
1917
  }
1873
1918
  };
1874
1919
  var validJWA = [