@abtnode/blocklet-services 1.16.26-beta-818ea1c5 → 1.16.26-beta-cca12425

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.
Files changed (268) hide show
  1. package/api/libs/auth/adapters/apple.js +78 -0
  2. package/api/libs/auth/adapters/auth0.js +56 -0
  3. package/api/libs/auth/adapters/facebook.js +34 -0
  4. package/api/libs/auth/adapters/github.js +75 -0
  5. package/api/libs/auth/adapters/google.js +66 -0
  6. package/api/libs/auth/adapters/twitter.js +22 -0
  7. package/api/libs/auth/index.js +170 -0
  8. package/api/routes/federated.js +23 -1
  9. package/api/routes/oauth.js +257 -102
  10. package/api/services/auth/connect/receive-transfer-app-owner.js +7 -1
  11. package/api/services/oauth/index.js +70 -0
  12. package/api/util/federated.js +2 -2
  13. package/dist/assets/{Add-DgrYlJHm.js → Add-B1KQFZbB.js} +1 -1
  14. package/dist/assets/{AddBox-Cqkk9Zb6.js → AddBox-C_rCPKut.js} +1 -1
  15. package/dist/assets/{Alert-CJmzHVEH.js → Alert-D7OuAtqq.js} +1 -1
  16. package/dist/assets/{ArrowDropDown-qzt62tXO.js → ArrowDropDown-DDSSUsgw.js} +1 -1
  17. package/dist/assets/{Avatar-VThXe6vp.js → Avatar-BKvod5qm.js} +1 -1
  18. package/dist/assets/{ButtonGroup-crNqYhpS.js → ButtonGroup-B93e6tqm.js} +1 -1
  19. package/dist/assets/{CheckCircle-hTa9NuuL.js → CheckCircle-DgHejmJm.js} +1 -1
  20. package/dist/assets/{ChevronRight-Zd4sj4Ik.js → ChevronRight-KVhjO69S.js} +1 -1
  21. package/dist/assets/{CloseOutlined-DzkxsEbx.js → CloseOutlined-BdzvaiT0.js} +1 -1
  22. package/dist/assets/{Delete-FPkQEM7j.js → Delete-Cmb6a-TW.js} +1 -1
  23. package/dist/assets/{DeleteOutline-BgceRLMI.js → DeleteOutline-C_JPQWoC.js} +1 -1
  24. package/dist/assets/{Done-Dw6uaCDC.js → Done-Cqcgj3XK.js} +1 -1
  25. package/dist/assets/{Download-D4aLfdYz.js → Download-DYDCm5jq.js} +1 -1
  26. package/dist/assets/{Edit-BQ8r9eYD.js → Edit-JQ58DRtF.js} +1 -1
  27. package/dist/assets/{EditIcon-DonYKsD8.js → EditIcon-BuHKvSX-.js} +1 -1
  28. package/dist/assets/Error-bprXV3Qz.js +1 -0
  29. package/dist/assets/{ExpandMore-BgRJle39.js → ExpandMore-CMOFmrRl.js} +1 -1
  30. package/dist/assets/{FilterList-Cr_Ge89T.js → FilterList-MfdHX_OT.js} +4 -4
  31. package/dist/assets/{FormControl-D-JqMY5K.js → FormControl-BrDVvV0w.js} +1 -1
  32. package/dist/assets/{FormControlLabel-CxQTOZJ1.js → FormControlLabel-Bj1raPoa.js} +1 -1
  33. package/dist/assets/{FormGroup-C-BEyape.js → FormGroup-CTmu8GWz.js} +1 -1
  34. package/dist/assets/{Google-DBpd-Csd.js → Google-B8EaETJY.js} +4 -4
  35. package/dist/assets/{Grid-BKo3Eu4m.js → Grid-BtGjPvuC.js} +1 -1
  36. package/dist/assets/{Hidden-BIcc-Ri_.js → Hidden-DWKJv_Ts.js} +1 -1
  37. package/dist/assets/{InfoOutlined-BAo9kLny.js → InfoOutlined-BItr8vqq.js} +1 -1
  38. package/dist/assets/{InputAdornment-eRc8XOzM.js → InputAdornment-CGuzdfXX.js} +1 -1
  39. package/dist/assets/{InputLabel-DIt1gEGj.js → InputLabel-DA3u5GP-.js} +1 -1
  40. package/dist/assets/{LastPage-DBc4oFoT.js → LastPage-DzK9EKF-.js} +1 -1
  41. package/dist/assets/{Launch-Cb7jRIVL.js → Launch-p5mGEt8g.js} +1 -1
  42. package/dist/assets/{LaunchOutlined-T5Mcb8X-.js → LaunchOutlined-BiGPJuP-.js} +1 -1
  43. package/dist/assets/{Link-BP6MkNEU.js → Link-C1R9I8T5.js} +1 -1
  44. package/dist/assets/{ListItemText-nUHfvFm4.js → ListItemText-9kG4LhTP.js} +1 -1
  45. package/dist/assets/{LockIcon-8lU2ELwJ.js → LockIcon-BYVTkf1W.js} +1 -1
  46. package/dist/assets/{Loop-BmbGUwLx.js → Loop-DoUT9kIB.js} +1 -1
  47. package/dist/assets/{MoreHoriz-Cr0ijI9A.js → MoreHoriz-B921hlWY.js} +1 -1
  48. package/dist/assets/{MoreVert-B1iH4_5V.js → MoreVert-C4XqRKE4.js} +1 -1
  49. package/dist/assets/{OpenInNew-CuPWiq3c.js → OpenInNew-DjHvMAdQ.js} +1 -1
  50. package/dist/assets/{Pagination-DcDob8Pn.js → Pagination-BlJxwbJW.js} +2 -2
  51. package/dist/assets/{PlayArrow-BMdF4bh0.js → PlayArrow-D4vxqsKR.js} +1 -1
  52. package/dist/assets/{RadioGroup-DjjhaMO1.js → RadioGroup-DFQHgpUl.js} +1 -1
  53. package/dist/assets/{Search-D5RGjNs-.js → Search-BuZJTzDq.js} +1 -1
  54. package/dist/assets/{Select-DCojKr5m.js → Select-0rvptrXH.js} +2 -2
  55. package/dist/assets/{ServerLogo-B8biNuwQ.js → ServerLogo-BS1tW7SN.js} +1 -1
  56. package/dist/assets/{Skeleton-D7s7WiMT.js → Skeleton-BQyN-0sJ.js} +3 -3
  57. package/dist/assets/{Slider-CQHQc72o.js → Slider-DaMMponj.js} +1 -1
  58. package/dist/assets/{Stepper-7vJEQecO.js → Stepper-ASzJY9Wh.js} +1 -1
  59. package/dist/assets/{TableRow-G7CkKHMa.js → TableRow-DQB9jM8W.js} +2 -2
  60. package/dist/assets/{TextField-BiWs3Roa.js → TextField-BHg3uTSC.js} +1 -1
  61. package/dist/assets/{Toolbar-BIHRXxj8.js → Toolbar-C3gbqZgN.js} +1 -1
  62. package/dist/assets/{ViewList-BlQf9CJF.js → ViewList-D9nsZOsI.js} +1 -1
  63. package/dist/assets/access-control-CZnhbObg.js +13 -0
  64. package/dist/assets/{actions-qRYQXtmd.js → actions-CFxlQnVb.js} +1 -1
  65. package/dist/assets/{add-component-core-Dk3cdqQl.js → add-component-core-CUisTrXt.js} +28 -28
  66. package/dist/assets/add-resource-Cg68DwqT.js +1 -0
  67. package/dist/assets/{addon-D37LISTV.js → addon-htB6vNOx.js} +1 -1
  68. package/dist/assets/{analytics-BVTyrWL4.js → analytics-D0Q9Rbgs.js} +8 -8
  69. package/dist/assets/api-DZcbXOwe.js +1 -0
  70. package/dist/assets/ar-B34f4HdK.js +1 -0
  71. package/dist/assets/ar-D80LEtfP.js +1 -0
  72. package/dist/assets/{audit-logs-DYpquiBv.js → audit-logs-DsHwARYT.js} +4 -4
  73. package/dist/assets/{button-8D8Sy6Jr.js → button-CtcdFik9.js} +1 -1
  74. package/dist/assets/{click-to-copy-BuvkZSYS.js → click-to-copy-DJVlTNr8.js} +1 -1
  75. package/dist/assets/{complete-C1W5A3f0.js → complete-CGMY06_W.js} +4 -4
  76. package/dist/assets/{component-D-wlgWPx.js → component-C8WremgG.js} +4 -4
  77. package/dist/assets/{config-BWOSsVWv.js → config-J8uHydLG.js} +2 -2
  78. package/dist/assets/{config-BBK_6yxt.js → config-ggazJABW.js} +1 -1
  79. package/dist/assets/{config-navigation-CCSF4RHD.js → config-navigation-COwAaVq0.js} +3 -3
  80. package/dist/assets/{config-space-DQlVUMKm.js → config-space-CPyb_eZ-.js} +1 -1
  81. package/dist/assets/confirm-BjHkE8rM.js +7 -0
  82. package/dist/assets/{connect-BkB_iVwc.js → connect-BbzlMR5b.js} +1 -1
  83. package/dist/assets/{connect-Cz-uMP2K.js → connect-BuLrPUuZ.js} +1 -1
  84. package/dist/assets/connect-to-BQINcy1q.js +54 -0
  85. package/dist/assets/{content-layout-BYCcnYXE.js → content-layout-af8Tk37j.js} +1 -1
  86. package/dist/assets/{dashboard-Bv2HZZCV.js → dashboard-CMs2secm.js} +8 -8
  87. package/dist/assets/de-CbWSpQPN.js +1 -0
  88. package/dist/assets/de-R8SpyfWD.js +1 -0
  89. package/dist/assets/{did-address-DZIM4xfX.js → did-address-BTH0hYaF.js} +1 -1
  90. package/dist/assets/domain-CMHk_z2Z.js +9 -0
  91. package/dist/assets/domain-list-DFffhAi2.js +12 -0
  92. package/dist/assets/es-D2WPMiOg.js +1 -0
  93. package/dist/assets/es-DQJXbL8c.js +1 -0
  94. package/dist/assets/exchange-passport-CQ4sTk73.js +1 -0
  95. package/dist/assets/{fallback-UK-V5wy0.js → fallback-BCzfpkx_.js} +1 -1
  96. package/dist/assets/fr-BU_67rSB.js +1 -0
  97. package/dist/assets/fr-ojA_joLk.js +1 -0
  98. package/dist/assets/{fuel-DYF8oUki.js → fuel-DAtablaD.js} +1 -1
  99. package/dist/assets/{fullpage-bvTUfN0w.js → fullpage-DY6OHAG0.js} +1 -1
  100. package/dist/assets/hi-BQAxHgmg.js +1 -0
  101. package/dist/assets/hi-D5wWKqkp.js +1 -0
  102. package/dist/assets/home-BfnWYBKb.js +1 -0
  103. package/dist/assets/id-BerMjI8d.js +1 -0
  104. package/dist/assets/id-Do44PG5N.js +1 -0
  105. package/dist/assets/iframe-Cet_1xjB.js +1 -0
  106. package/dist/assets/{index-CSYh4gtP.js → index-6HBsHgLl.js} +1 -1
  107. package/dist/assets/index-A5YVryx_.js +13 -0
  108. package/dist/assets/{index-BXhBzR7Q.js → index-BXCKXS5j.js} +7 -7
  109. package/dist/assets/index-BqsMd8kb.js +1290 -0
  110. package/dist/assets/{index-CCY6RJsj.js → index-Bt4rb2Bc.js} +1 -1
  111. package/dist/assets/index-BxJWfUoy.js +262 -0
  112. package/dist/assets/{index-Bw_Ej9Cn.js → index-ByPqHR4r.js} +9 -9
  113. package/dist/assets/{index-BdxvQHKQ.js → index-C5WzGzSj.js} +1 -1
  114. package/dist/assets/{index-C2vBcXqD.js → index-C9d9WjZ0.js} +1 -1
  115. package/dist/assets/index-CHX9AKKy.js +221 -0
  116. package/dist/assets/{index-B0WIUFz1.js → index-CKhzUQzq.js} +7 -7
  117. package/dist/assets/{index-XV3z9IXt.js → index-CZ9EO740.js} +7 -7
  118. package/dist/assets/{index-DMSZt2ZE.js → index-CgXB8tNh.js} +2 -2
  119. package/dist/assets/{index-DWCMi3WL.js → index-Cn1zzQRb.js} +1 -1
  120. package/dist/assets/{index-Ok0xRBbf.js → index-CnV_HSYC.js} +1 -1
  121. package/dist/assets/{index-DPLdk86R.js → index-D0TsVpOv.js} +1 -1
  122. package/dist/assets/{index-CNHAiDvM.js → index-DG2_Yw8Q.js} +1 -1
  123. package/dist/assets/index-DJI8p4CV.js +54 -0
  124. package/dist/assets/{index-C00VKsL6.js → index-DJvJ2Tmc.js} +1 -1
  125. package/dist/assets/{index-BgNMSa3N.js → index-DMPZsfun.js} +1 -1
  126. package/dist/assets/{index-BQaoZKI6.js → index-Dbqss1Fe.js} +8 -8
  127. package/dist/assets/index-DcHRVIKm.js +12 -0
  128. package/dist/assets/{index-DXjXo6f0.js → index-DtNxMhKO.js} +1 -1
  129. package/dist/assets/{index-BZzKaGvd.js → index-DthjsZ09.js} +1 -1
  130. package/dist/assets/{index-mNlCpV8P.js → index-Dylzk7Aa.js} +2 -2
  131. package/dist/assets/{index-dUNOHPzk.js → index-LSzcGKHj.js} +4 -4
  132. package/dist/assets/index-NdCq_QfL.js +234 -0
  133. package/dist/assets/{index-BKdL2tN6.js → index-e7NHyY1q.js} +15 -17
  134. package/dist/assets/{index-DuoMkzRH.js → index-o-Ihmjeu.js} +1 -1
  135. package/dist/assets/index-zxzrHJEP.js +11 -0
  136. package/dist/assets/{index.es-B_ckeqWW.js → index.es-BEaw1DFj.js} +3 -3
  137. package/dist/assets/index.esm-C9zb95qH.js +1 -0
  138. package/dist/assets/info-outline-rounded-BTpTFmEK.js +57 -0
  139. package/dist/assets/{invitation-DlUxWMm0.js → invitation-3r-X891j.js} +5 -5
  140. package/dist/assets/{invite-DVE8FTA3.js → invite-CrWjnaC0.js} +1 -1
  141. package/dist/assets/{issue-passport-DTrWwTY_.js → issue-passport-CCGBLWf8.js} +1 -1
  142. package/dist/assets/item-BNHxZM2p.js +1 -0
  143. package/dist/assets/ja-BO4aqMDb.js +1 -0
  144. package/dist/assets/ja-D8qblxbX.js +1 -0
  145. package/dist/assets/{jss-plugin-props-sort.esm-r4rT6imo.js → jss-plugin-props-sort.esm-CjzdPk2L.js} +4 -4
  146. package/dist/assets/ko-C8eR3MIF.js +1 -0
  147. package/dist/assets/ko-Cv-NiLYG.js +1 -0
  148. package/dist/assets/{launch-result-message-DKVZaiuQ.js → launch-result-message-D2CQfmUY.js} +1 -1
  149. package/dist/assets/{layout-r1uFQEiA.js → layout--pblhAfc.js} +2 -2
  150. package/dist/assets/{list-header-Bqj4L1f9.js → list-header-Br5YHewG.js} +1 -1
  151. package/dist/assets/localization-BwDw74Zq.js +1 -0
  152. package/dist/assets/{log-6WvRhIsI.js → log-BJjAfVl8.js} +1 -1
  153. package/dist/assets/{login-Cv7Mu9fV.js → login-BQQD6meW.js} +1 -1
  154. package/dist/assets/login-oauth-callback-Dmleuitf.js +1 -0
  155. package/dist/assets/logo-uploader-eDNDJkbn.js +127 -0
  156. package/dist/assets/{lost-passport-D1SohDZY.js → lost-passport-BWTuZR78.js} +3 -3
  157. package/dist/assets/{lottie-DGWD8Sx6.js → lottie-VJFQ68oA.js} +1 -1
  158. package/dist/assets/{notifications-B2-qZM1R.js → notifications-DwecRDR9.js} +2 -2
  159. package/dist/assets/overview-BMcMnUtN.js +12 -0
  160. package/dist/assets/{page-header-CNlMYlWe.js → page-header-EqcdM7n1.js} +1 -1
  161. package/dist/assets/{permission-rTeG4c6s.js → permission-D9TNO0kT.js} +1 -1
  162. package/dist/assets/{preferences-uQd05OUd.js → preferences-CN9EJ9NX.js} +1 -1
  163. package/dist/assets/pt-DfarVqto.js +1 -0
  164. package/dist/assets/pt-Dly5Myel.js +1 -0
  165. package/dist/assets/publish-resource-D7WXvm6u.js +1 -0
  166. package/dist/assets/{react-Cv1mxS7R.js → react-DP2ngoHF.js} +15 -15
  167. package/dist/assets/{redux-YC_VNDAW.js → redux-BpyOm6pS.js} +1 -1
  168. package/dist/assets/ru-C9Wh9IKf.js +1 -0
  169. package/dist/assets/ru-DeqfYyhe.js +1 -0
  170. package/dist/assets/{selector-UANcYMI5.js → selector-Dpt9nBq7.js} +3 -3
  171. package/dist/assets/session-DESbRLD5.js +1 -0
  172. package/dist/assets/setup-DFybTsc-.js +19 -0
  173. package/dist/assets/{slicedToArray-BDirS3b-.js → slicedToArray-SBrU6gJV.js} +2 -2
  174. package/dist/assets/spaces-DNtrZi8N.js +1 -0
  175. package/dist/assets/start-CgH7B6u8.js +186 -0
  176. package/dist/assets/{step-actions-DBbKtvUA.js → step-actions-BNn2aM9R.js} +1 -1
  177. package/dist/assets/{studio-DseQhKM-.js → studio-8gthozLh.js} +1 -1
  178. package/dist/assets/{switch-control-ChRbLafp.js → switch-control-DO3LxG2v.js} +1 -1
  179. package/dist/assets/th-B6Z2fc62.js +1 -0
  180. package/dist/assets/th-BEfUwHXu.js +1 -0
  181. package/dist/assets/{toUpper-aYHMIsgS.js → toUpper-C0A9jQTp.js} +1 -1
  182. package/dist/assets/transfer-Blq_vH7z.js +16 -0
  183. package/dist/assets/uniqBy-DN9WBw_C.js +1 -0
  184. package/dist/assets/{unsubscribe-CD5-lgl8.js → unsubscribe-CkiFEJUY.js} +1 -1
  185. package/dist/assets/{use-blocklet-info-for-connect-did-spaces-BZgwpcda.js → use-blocklet-info-for-connect-did-spaces-Car-GPAS.js} +1 -1
  186. package/dist/assets/useAsync-D0lrbkdE.js +1 -0
  187. package/dist/assets/{useFormControl-Dg1dn0q0.js → useFormControl-b_gcFCyO.js} +1 -1
  188. package/dist/assets/{useLocalStorage-Cz7o0KhC.js → useLocalStorage-Cvbq9kSP.js} +1 -1
  189. package/dist/assets/{useSetState-CabR74SD.js → useSetState-p_ztAK1J.js} +1 -1
  190. package/dist/assets/useSlot-MeMXV443.js +1 -0
  191. package/dist/assets/user-center-B0jK0JhV.js +1 -0
  192. package/dist/assets/user-sessions-gd1hKrWX.js +1 -0
  193. package/dist/assets/{util-nCKofCSa.js → util-BkBoAuZA.js} +1 -1
  194. package/dist/assets/vi-CVN1WwF4.js +1 -0
  195. package/dist/assets/vi-Di0ynKe6.js +1 -0
  196. package/dist/assets/wrap-locale-BONrbXck.js +1 -0
  197. package/dist/assets/{zh-BjdUZ9y0.js → zh-Bgfgr3pp.js} +2 -2
  198. package/dist/assets/zh-e04FR-Ps.js +1 -0
  199. package/dist/assets/zh-tw-DlepKNg6.js +1 -0
  200. package/dist/assets/zh-tw-kaMajsUI.js +1 -0
  201. package/dist/index.html +1 -1
  202. package/dist/service-worker.js +1 -1
  203. package/package.json +28 -27
  204. package/dist/assets/access-control-2ITAVZFE.js +0 -13
  205. package/dist/assets/add-resource-DXNOMO-U.js +0 -1
  206. package/dist/assets/api-DbKOEDhE.js +0 -1
  207. package/dist/assets/ar-CxvIl-Ji.js +0 -1
  208. package/dist/assets/ar-DkKlynAd.js +0 -1
  209. package/dist/assets/confirm-B_AqBUcU.js +0 -7
  210. package/dist/assets/connect-to-DY7ehzSR.js +0 -54
  211. package/dist/assets/de-3qOdEa6P.js +0 -1
  212. package/dist/assets/de-6SF_cfoR.js +0 -1
  213. package/dist/assets/domain-DhXcq2zb.js +0 -9
  214. package/dist/assets/domain-list-DgsYQNVw.js +0 -12
  215. package/dist/assets/es-7PLonVLT.js +0 -1
  216. package/dist/assets/es-Cgmj9Jck.js +0 -1
  217. package/dist/assets/exchange-passport-B9snHp27.js +0 -1
  218. package/dist/assets/fr-1I2E8gH_.js +0 -1
  219. package/dist/assets/fr-CEcEkJ0j.js +0 -1
  220. package/dist/assets/hi-B2Zih4k5.js +0 -1
  221. package/dist/assets/hi-O7fF0c9u.js +0 -1
  222. package/dist/assets/home-CU_IGJyv.js +0 -1
  223. package/dist/assets/id-BF9h-bev.js +0 -1
  224. package/dist/assets/id-Cvddby74.js +0 -1
  225. package/dist/assets/iframe-COW-8OqG.js +0 -1
  226. package/dist/assets/index-7_LDggGs.js +0 -11
  227. package/dist/assets/index-BiG0eNQl.js +0 -233
  228. package/dist/assets/index-Cqc114Nl.js +0 -54
  229. package/dist/assets/index-D04f13KU.js +0 -1304
  230. package/dist/assets/index-DGFV1Kbv.js +0 -13
  231. package/dist/assets/index-De-cDFqF.js +0 -262
  232. package/dist/assets/index-cOMrpLvw.js +0 -221
  233. package/dist/assets/index.esm-CymxS9uw.js +0 -1
  234. package/dist/assets/item-DJ0IfbAJ.js +0 -1
  235. package/dist/assets/ja-BRl6JgyC.js +0 -1
  236. package/dist/assets/ja-D-8llXBd.js +0 -1
  237. package/dist/assets/ko-BjJKFx_R.js +0 -1
  238. package/dist/assets/ko-wij13JCo.js +0 -1
  239. package/dist/assets/localization-CWTtfgBA.js +0 -1
  240. package/dist/assets/logo-uploader-Bd9nGCUT.js +0 -127
  241. package/dist/assets/overview--CbrsoGh.js +0 -12
  242. package/dist/assets/pt-CkaW7MUR.js +0 -1
  243. package/dist/assets/pt-DwFQV4Br.js +0 -1
  244. package/dist/assets/publish-resource-DOdKon0e.js +0 -1
  245. package/dist/assets/ru-1GZMliq9.js +0 -1
  246. package/dist/assets/ru-D1AQvTQo.js +0 -1
  247. package/dist/assets/session-CNTfeDhH.js +0 -1
  248. package/dist/assets/setup-Cv13s2jR.js +0 -19
  249. package/dist/assets/spaces-BRT7WRxg.js +0 -1
  250. package/dist/assets/start-Bcl56tnq.js +0 -186
  251. package/dist/assets/th-BXoQZNC4.js +0 -1
  252. package/dist/assets/th-Bc32gh20.js +0 -1
  253. package/dist/assets/transfer-DTM2UHnW.js +0 -16
  254. package/dist/assets/ua-parser-Ca9ZhtBr.js +0 -57
  255. package/dist/assets/uniqBy-BkV8rpNe.js +0 -1
  256. package/dist/assets/useAsync-COTc7rmY.js +0 -1
  257. package/dist/assets/useSlot-Ceul9GiJ.js +0 -1
  258. package/dist/assets/user-center-6Xre-wMU.js +0 -1
  259. package/dist/assets/user-sessions-B85QTl3T.js +0 -1
  260. package/dist/assets/vi-BnooUqta.js +0 -1
  261. package/dist/assets/vi-CRasBMRh.js +0 -1
  262. package/dist/assets/wrap-locale-bIxHxEdd.js +0 -1
  263. package/dist/assets/zh-B75pu7gp.js +0 -1
  264. package/dist/assets/zh-tw-BYDo5dcg.js +0 -1
  265. package/dist/assets/zh-tw-prJAtGne.js +0 -1
  266. /package/api/libs/auth/adapters/{auth0 → auth0-legacy}/authentication-client.js +0 -0
  267. /package/api/libs/auth/adapters/{auth0 → auth0-legacy}/index.js +0 -0
  268. /package/api/libs/auth/adapters/{auth0 → auth0-legacy}/management-client.js +0 -0
@@ -10,25 +10,27 @@ const last = require('lodash/last');
10
10
  const pick = require('lodash/pick');
11
11
  const uniqBy = require('lodash/uniqBy');
12
12
  const sortBy = require('lodash/sortBy');
13
+ const cloneDeep = require('lodash/cloneDeep');
13
14
  const joinUrl = require('url-join');
14
- const { getWalletDid } = require('@blocklet/meta/lib/did-utils');
15
+ const { getWalletDid, getConnectedAccounts, getSourceProvider } = require('@blocklet/meta/lib/did-utils');
15
16
  const formatContext = require('@abtnode/util/lib/format-context');
16
17
  const createTranslator = require('@abtnode/util/lib/translate');
17
18
  const { LOGIN_PROVIDER } = require('@blocklet/constant');
19
+ const { withHttps, withTrailingSlash } = require('ufo');
18
20
 
19
21
  const logger = require('../libs/logger')('oauth');
20
- const { AuthenticationClient } = require('../libs/auth/adapters/auth0');
22
+ const { OAuthClient } = require('../libs/auth');
23
+ const OAuthAuth0 = require('../libs/auth/adapters/auth0');
24
+ const OAuthAuth0Legacy = require('../libs/auth/adapters/auth0-legacy');
25
+ const OAuthGithub = require('../libs/auth/adapters/github');
26
+ const OAuthGoogle = require('../libs/auth/adapters/google');
27
+ const OAuthApple = require('../libs/auth/adapters/apple');
21
28
  const { getAvatarByEmail, transferPassport, getAvatarByUrl } = require('../libs/auth/utils');
22
29
  const initJwt = require('../libs/jwt');
23
30
  const { sendToUser } = require('../libs/notification');
24
31
  const { isInvitedUserOnly, createTokenFn, getDidConnectVersion } = require('../util');
25
32
  const { ApiError } = require('../util/error');
26
- const {
27
- getFederatedMaster,
28
- getOAuthUserInfo,
29
- shouldSyncFederated,
30
- getUserWithinFederated,
31
- } = require('../util/federated');
33
+ const federatedUtil = require('../util/federated');
32
34
 
33
35
  const PREFIX = WELLKNOWN_SERVICE_PATH_PREFIX;
34
36
 
@@ -40,65 +42,142 @@ const translations = {
40
42
  needInviteToLogin: '你需要被邀请才可以登录此应用',
41
43
  alreadyMainAccount: '当前邮箱已经绑定过该应用,不过你可以先以此邮箱登陆,然后再绑定钱包账户',
42
44
  alreadyBindWallet: '当前邮箱已经绑定过钱包账户 {did}。',
45
+ alreadyExist: '当前账户已存在,无法完成绑定操作。',
46
+ alreadyBindProvider: '当前账户绑定过 {provider},无法完成绑定操作。',
43
47
  oauthCantBeOwner: '第三方登录的账户不能成为应用的拥有者',
44
48
  oauthCantBindOauth: '第三方登录的账户无法绑定另一个第三方登录的账户',
49
+ cantUnbindWalletAccount: '不能解绑钱包账户',
50
+ cantUnbindSourceProvider: '不能解绑源账户',
51
+ accountNotExist: '第三方登录的账户不存在,无法完成解绑操作',
45
52
  },
46
53
  en: {
47
54
  needInviteToLogin: 'You need to be invited to login in to this app',
48
55
  alreadyMainAccount:
49
56
  'Your email has already logged in to the app, so binding cannot be completed. However, you can first log in with this email and then bind your wallet account.',
50
57
  alreadyBindWallet: 'Your account is already bond to wallet account {did}, so binding can not be completed.',
58
+ alreadyExist: 'Your account already exists, so binding cannot be completed.',
59
+ alreadyBindProvider: 'Your account is already bind to {provider}, so binding can not be completed.',
51
60
  oauthCantBeOwner: "Can't login oauth account as owner",
52
61
  oauthCantBindOauth: "Current account can't bind to a third party account",
62
+ cantUnbindWalletAccount: 'Cannot unbind wallet provider',
63
+ cantUnbindSourceProvider: 'Cannot unbind source provider',
64
+ accountNotExist: "Connected account is not exist, can't finish disconnect",
53
65
  },
54
66
  };
55
67
 
56
68
  const t = createTranslator({ translations });
57
69
 
58
- function getAuthClient(blocklet, provider) {
59
- const oauthConfig = blocklet?.settings?.oauth || {};
60
- const providerConfig = oauthConfig?.[provider];
61
- if (!providerConfig) {
62
- throw new ApiError(400, `Provider ${provider} is not valid`);
63
- }
64
- const authClient = new AuthenticationClient({
65
- domain: providerConfig.domain,
70
+ async function getOAuthUserInfo({ blocklet, provider, token, idToken, code, appPid }) {
71
+ let oauthInfo;
72
+ const authClient = getAuthClient(blocklet, provider, {
73
+ appPid,
74
+ legacy: Boolean(token && provider === 'auth0'),
66
75
  });
67
- return authClient;
68
- }
69
-
70
- async function login(req, node, options) {
71
- const blocklet = await req.getBlocklet();
72
- const { token, locale = 'en', provider, componentId, sourceAppPid = null, visitorId } = req.body;
73
-
74
- if (!blocklet.settings?.owner) {
75
- throw new ApiError(400, t('oauthCantBeOwner', locale));
76
+ // FIXME: @zhanghan 临时兼容 auth0 这种直接传 token 的方式,将来要废弃这种方式
77
+ if (token && provider === 'auth0') {
78
+ oauthInfo = await authClient.getProfile(token);
79
+ } else if (idToken) {
80
+ // FIXME: @zhanghan 临时提供 id_token 方案,将来要废弃这种方式
81
+ oauthInfo = await authClient.getProfile({ id_token: idToken });
82
+ } else if (code) {
83
+ const oauthToken = await authClient.getToken({ code });
84
+ oauthInfo = await authClient.getProfile(oauthToken);
76
85
  }
77
- const { did: teamDid, wallet: blockletWallet, secret, appUrl } = await req.getBlockletInfo();
86
+ return oauthInfo;
87
+ }
78
88
 
89
+ async function getOAuthFederatedUserInfo(req, { blocklet }) {
79
90
  let userWallet;
80
91
  let oauthInfo;
92
+ const { token, idToken, code, provider, sourceAppPid = null } = req.body;
93
+
94
+ // appUrl
95
+ const { wallet: blockletWallet } = await req.getBlockletInfo();
81
96
 
82
97
  // NOTICE: 如果是统一登录,则向 master 站点发起 oauth 用户信息查询请求,auth0 的账户信息必须由 master 来生成
83
98
  if (sourceAppPid) {
84
- const data = await getOAuthUserInfo({
99
+ const data = await federatedUtil.getOAuthUserInfo({
85
100
  request: req,
86
101
  blocklet,
87
102
  provider,
88
- token,
89
103
  sourceAppPid,
104
+ // 三种用于获取用户信息的方式,任选其一即可
105
+ token,
106
+ idToken,
107
+ code,
90
108
  });
91
109
  userWallet = data.wallet;
92
110
  oauthInfo = data.info;
93
111
  } else {
94
- const authClient = getAuthClient(blocklet, provider);
95
- oauthInfo = await authClient.getProfile(token);
112
+ oauthInfo = await getOAuthUserInfo({ blocklet, provider, code, idToken, token });
96
113
  userWallet = fromAppDid(oauthInfo.sub, blockletWallet.secretKey);
97
114
  }
115
+ return { info: oauthInfo, wallet: userWallet };
116
+ }
117
+
118
+ function getAuthClient(blocklet, provider, { legacy = false, appPid } = {}) {
119
+ const oauthConfig = cloneDeep(blocklet?.settings?.oauth || {});
120
+ const providerConfig = oauthConfig?.[provider];
121
+ if (!providerConfig) {
122
+ throw new ApiError(400, `Provider ${provider} is not valid`);
123
+ }
124
+ let appUrl = blocklet?.environmentObj?.BLOCKLET_APP_URL;
125
+ if (appPid) {
126
+ const federatedSites = blocklet.settings?.federated?.sites || [];
127
+ const loginSite = federatedSites.find((x) => x.appPid === appPid);
128
+ appUrl = loginSite.appUrl;
129
+ }
130
+ providerConfig.callbackUrl = `${appUrl}/.well-known/service/oauth/callback/${provider}`;
131
+
132
+ if (provider === 'auth0') {
133
+ if (legacy) {
134
+ // FIXME: @zhanghan 将来需要移除 legacy auth0 的支持
135
+ return new OAuthAuth0Legacy.AuthenticationClient({
136
+ domain: providerConfig.domain,
137
+ });
138
+ }
139
+ if (!providerConfig.clientSecret) {
140
+ throw new Error('missing client secret');
141
+ }
142
+ return new OAuthClient({
143
+ provider: OAuthAuth0({
144
+ // HACK: auth0 比较奇葩,它的 issuer 有斜杠后缀
145
+ issuer: withTrailingSlash(withHttps(providerConfig.domain)),
146
+ clientId: providerConfig.clientId,
147
+ clientSecret: providerConfig.clientSecret,
148
+ callbackUrl: providerConfig.callbackUrl,
149
+ }),
150
+ });
151
+ }
152
+
153
+ if (provider === 'github') {
154
+ return new OAuthClient({ provider: OAuthGithub(providerConfig) });
155
+ }
156
+ if (provider === 'google') {
157
+ return new OAuthClient({ provider: OAuthGoogle(providerConfig) });
158
+ }
159
+ if (provider === 'apple') {
160
+ return new OAuthClient({ provider: OAuthApple(providerConfig) });
161
+ }
162
+ return null;
163
+ }
164
+
165
+ async function login(req, node, options) {
166
+ const blocklet = await req.getBlocklet();
167
+ const { locale = 'en', provider, componentId, sourceAppPid = null, visitorId } = req.body;
168
+ if (!blocklet.settings?.owner) {
169
+ throw new ApiError(400, t('oauthCantBeOwner', locale));
170
+ }
171
+
172
+ const { did: teamDid, secret, appUrl } = await req.getBlockletInfo();
173
+ const { info: oauthInfo, wallet: userWallet } = await getOAuthFederatedUserInfo(req, {
174
+ blocklet,
175
+ });
98
176
 
99
177
  const userDid = userWallet.address;
100
178
  const userPk = userWallet.publicKey;
101
179
 
180
+ // FIXME: @zhanghan 这里通过 componentId 来判断权限的方式实际上没有意义,因为用户可以尝试从别的 url 来登录
102
181
  const config = await req.getServiceConfig(NODE_SERVICES.AUTH, { componentId });
103
182
  const nodeInfo = await req.getNodeInfo();
104
183
  const { dataDir } = await getApplicationInfo({ node, nodeInfo, teamDid });
@@ -106,17 +185,21 @@ async function login(req, node, options) {
106
185
 
107
186
  const lastLoginIp = get(req, 'headers[x-real-ip]') || '';
108
187
  let passport = { name: 'Guest', role: 'guest' };
109
- let currentUser = await getUserWithinFederated({ sourceAppPid, teamDid, userDid, userPk }, { node, blocklet });
188
+ let currentUser = await federatedUtil.getUserWithinFederated(
189
+ { sourceAppPid, teamDid, userDid, userPk },
190
+ { node, blocklet }
191
+ );
110
192
  let doc;
111
193
  let passportForLog;
112
- const fullName = currentUser?.fullName || oauthInfo?.nickname;
194
+ const fullName = currentUser?.fullName || oauthInfo?.name;
113
195
  const connectedAccount = {
114
196
  provider,
115
197
  did: userDid,
116
198
  pk: userPk,
117
199
  id: oauthInfo.sub,
200
+ userInfo: oauthInfo,
118
201
  };
119
- const masterSite = getFederatedMaster(blocklet);
202
+ const masterSite = federatedUtil.getFederatedMaster(blocklet);
120
203
  let profile;
121
204
  // 当前账户已存在,更新账户信息
122
205
  if (currentUser) {
@@ -159,7 +242,7 @@ async function login(req, node, options) {
159
242
 
160
243
  passportForLog = { name: 'Guest', role: 'guest' };
161
244
  profile = {
162
- fullName: oauthInfo.nickname,
245
+ fullName: oauthInfo.name,
163
246
  email: oauthInfo.email,
164
247
  avatar,
165
248
  };
@@ -202,7 +285,7 @@ async function login(req, node, options) {
202
285
  },
203
286
  });
204
287
 
205
- if (shouldSyncFederated(sourceAppPid, blocklet)) {
288
+ if (federatedUtil.shouldSyncFederated(sourceAppPid, blocklet)) {
206
289
  const syncUserData = {
207
290
  did: userDid,
208
291
  pk: userPk,
@@ -270,38 +353,14 @@ async function login(req, node, options) {
270
353
  }
271
354
 
272
355
  async function invite(req, node, options) {
273
- const {
274
- locale,
275
- inviteId,
276
- token,
277
- baseUrl,
278
- provider = LOGIN_PROVIDER.AUTH0,
279
- sourceAppPid = null,
280
- visitorId,
281
- } = req.body;
356
+ const { locale, inviteId, baseUrl, provider = LOGIN_PROVIDER.AUTH0, sourceAppPid = null, visitorId } = req.body;
282
357
  const blocklet = await req.getBlocklet();
283
- let userWallet;
284
- let oauthInfo;
285
358
 
286
- const { did: teamDid, wallet: blockletWallet, secret } = await req.getBlockletInfo();
359
+ const { did: teamDid, secret } = await req.getBlockletInfo();
287
360
 
288
- // NOTICE: 如果是统一登录,则向 master 站点发起 oauth 登录请求,auth0 的账户信息必须由 master 来生成
289
- if (sourceAppPid) {
290
- const data = await getOAuthUserInfo({
291
- request: req,
292
- blocklet,
293
- provider,
294
- token,
295
- sourceAppPid,
296
- locale,
297
- });
298
- userWallet = data.wallet;
299
- oauthInfo = data.info;
300
- } else {
301
- const authClient = getAuthClient(blocklet, provider);
302
- oauthInfo = await authClient.getProfile(token);
303
- userWallet = fromAppDid(oauthInfo.sub, blockletWallet.secretKey);
304
- }
361
+ const { info: oauthInfo, wallet: userWallet } = await getOAuthFederatedUserInfo(req, {
362
+ blocklet,
363
+ });
305
364
 
306
365
  const nodeInfo = await req.getNodeInfo();
307
366
  let userDid = userWallet.address;
@@ -309,7 +368,10 @@ async function invite(req, node, options) {
309
368
 
310
369
  let profile;
311
370
 
312
- const currentUser = await getUserWithinFederated({ sourceAppPid, teamDid, userDid, userPk }, { node, blocklet });
371
+ const currentUser = await federatedUtil.getUserWithinFederated(
372
+ { sourceAppPid, teamDid, userDid, userPk },
373
+ { node, blocklet }
374
+ );
313
375
 
314
376
  const { dataDir, name: applicationName } = await getApplicationInfo({ node, nodeInfo, teamDid });
315
377
  if (currentUser) {
@@ -321,12 +383,11 @@ async function invite(req, node, options) {
321
383
  userDid = currentUser.did;
322
384
  userPk = currentUser.pk;
323
385
  } else {
324
- const { picture, email, nickname: fullName } = oauthInfo;
325
- let avatar = picture ? await getAvatarByUrl(picture) : await getAvatarByEmail(email);
386
+ let avatar = oauthInfo.picture ? await getAvatarByUrl(oauthInfo.picture) : await getAvatarByEmail(oauthInfo.email);
326
387
  avatar = await extractUserAvatar(avatar, { dataDir });
327
388
  profile = {
328
- email,
329
- fullName,
389
+ email: oauthInfo.email,
390
+ fullName: oauthInfo.name,
330
391
  avatar: getUserAvatarUrl(baseUrl, avatar),
331
392
  };
332
393
  }
@@ -348,7 +409,7 @@ async function invite(req, node, options) {
348
409
  locale,
349
410
  provider,
350
411
  });
351
- const masterSite = getFederatedMaster(blocklet);
412
+ const masterSite = federatedUtil.getFederatedMaster(blocklet);
352
413
  const syncData = {
353
414
  users: [],
354
415
  };
@@ -381,6 +442,7 @@ async function invite(req, node, options) {
381
442
  id: oauthInfo.sub,
382
443
  did: userWallet.address,
383
444
  pk: userWallet.publicKey,
445
+ userInfo: oauthInfo,
384
446
  };
385
447
  await node.loginUser({
386
448
  teamDid,
@@ -391,7 +453,7 @@ async function invite(req, node, options) {
391
453
  sourceAppPid,
392
454
  },
393
455
  });
394
- if (shouldSyncFederated(sourceAppPid, blocklet)) {
456
+ if (federatedUtil.shouldSyncFederated(sourceAppPid, blocklet)) {
395
457
  const syncUserData = {
396
458
  did: userDid,
397
459
  pk: userPk,
@@ -422,7 +484,7 @@ async function invite(req, node, options) {
422
484
  },
423
485
  });
424
486
 
425
- if (shouldSyncFederated(sourceAppPid, blocklet)) {
487
+ if (federatedUtil.shouldSyncFederated(sourceAppPid, blocklet)) {
426
488
  node
427
489
  .syncFederated({
428
490
  did: teamDid,
@@ -470,22 +532,12 @@ async function invite(req, node, options) {
470
532
  // 给 DID Wallet 绑定 Auth0 的流程
471
533
  // eslint-disable-next-line no-unused-vars
472
534
  async function bind(req, node, options) {
473
- const { token, locale = 'en', provider, sourceAppPid } = req.body;
535
+ const { locale = 'en', provider, sourceAppPid } = req.body;
474
536
  const blocklet = await req.getBlocklet();
475
- const { did: teamDid, wallet: blockletWallet, appUrl } = await req.getBlockletInfo();
537
+ const { did: teamDid, appUrl } = await req.getBlockletInfo();
476
538
  const userDid = req.user.did;
477
539
 
478
- let userWallet;
479
- let userInfo;
480
- if (sourceAppPid) {
481
- const data = await getOAuthUserInfo({ blocklet, provider, token, request: req, sourceAppPid });
482
- userWallet = data.wallet;
483
- userInfo = data.info;
484
- } else {
485
- const authClient = getAuthClient(blocklet, provider);
486
- userInfo = await authClient.getProfile(token);
487
- userWallet = fromAppDid(userInfo.sub, blockletWallet.secretKey);
488
- }
540
+ const { info: oauthInfo, wallet: userWallet } = await getOAuthFederatedUserInfo(req, { blocklet });
489
541
  let oauthUser = await node.getUser({
490
542
  teamDid,
491
543
  user: { did: userWallet.address },
@@ -494,17 +546,16 @@ async function bind(req, node, options) {
494
546
  },
495
547
  });
496
548
  if (oauthUser) {
497
- if (oauthUser.sourceProvider === LOGIN_PROVIDER.AUTH0) {
498
- throw new ApiError(400, t('alreadyMainAccount', locale, { email: userInfo.email }));
499
- }
500
-
501
- throw new ApiError(400, t('alreadyBindWallet', locale, { email: userInfo.email, did: oauthUser.did }));
549
+ throw new ApiError(400, t('alreadyExist', locale, { email: oauthInfo.email, did: oauthUser.did }));
502
550
  }
503
551
 
504
552
  // NOTICE: 这里获得的 did 是当前登录用户的永久 did,无需再去查询 connectedAccount
505
553
  const bindUser = await node.getUser({ teamDid, user: { did: userDid } });
506
- if (bindUser.sourceProvider !== LOGIN_PROVIDER.WALLET) {
507
- throw new ApiError(400, t('oauthCantBindOauth', locale));
554
+
555
+ const connectAccounts = getConnectedAccounts(bindUser);
556
+
557
+ if (connectAccounts.some((x) => x.provider === provider)) {
558
+ throw new ApiError(400, t('alreadyBindProvider', locale, { provider }));
508
559
  }
509
560
 
510
561
  const mergePassport = (oauthUser?.passports || []).reduce((sum, cur) => {
@@ -515,13 +566,13 @@ async function bind(req, node, options) {
515
566
  if (!avatar) {
516
567
  const nodeInfo = await req.getNodeInfo();
517
568
  const { dataDir } = await getApplicationInfo({ node, nodeInfo, teamDid });
518
- avatar = userInfo.picture ? await getAvatarByUrl(userInfo.picture) : await getAvatarByEmail(userInfo.email);
569
+ avatar = oauthInfo.picture ? await getAvatarByUrl(oauthInfo.picture) : await getAvatarByEmail(oauthInfo.email);
519
570
  avatar = await extractUserAvatar(avatar, { dataDir });
520
571
  }
521
572
 
522
573
  const mergeProfile = {
523
- fullName: bindUser.fullName || userInfo.nickname,
524
- email: bindUser.email || userInfo.email,
574
+ fullName: bindUser.fullName || oauthInfo.name,
575
+ email: bindUser.email || oauthInfo.email,
525
576
  avatar,
526
577
  };
527
578
 
@@ -529,11 +580,12 @@ async function bind(req, node, options) {
529
580
 
530
581
  const connectedAccount = {
531
582
  provider,
532
- id: userInfo.sub,
583
+ id: oauthInfo.sub,
533
584
  did: userWallet.address,
534
585
  pk: userWallet.publicKey,
535
586
  firstLoginAt: currentTime,
536
587
  lastLoginAt: currentTime,
588
+ userInfo: oauthInfo,
537
589
  };
538
590
 
539
591
  await node.updateUser({
@@ -548,8 +600,8 @@ async function bind(req, node, options) {
548
600
  },
549
601
  });
550
602
 
551
- const masterSite = getFederatedMaster(blocklet);
552
- if (shouldSyncFederated(sourceAppPid, blocklet)) {
603
+ const masterSite = federatedUtil.getFederatedMaster(blocklet);
604
+ if (federatedUtil.shouldSyncFederated(sourceAppPid, blocklet)) {
553
605
  // NOTICE: 采用异步来更新,不阻塞接口的正常响应
554
606
  const syncUserData = {
555
607
  did: bindUser.did,
@@ -593,6 +645,55 @@ async function bind(req, node, options) {
593
645
  );
594
646
  }
595
647
 
648
+ async function unbind(req, node) {
649
+ const { locale = 'en', connectedAccount, sourceAppPid } = req.body;
650
+
651
+ const userDid = req.user.did;
652
+ const blocklet = await req.getBlocklet();
653
+ const { did: teamDid } = await req.getBlockletInfo();
654
+ const bindUser = await node.getUser({ teamDid, user: { did: userDid } });
655
+ if (connectedAccount.provider === LOGIN_PROVIDER.WALLET) {
656
+ throw new Error(t('cantUnbindWalletAccount', locale));
657
+ }
658
+ if (getSourceProvider(bindUser) === connectedAccount.provider) {
659
+ throw new Error(t('cantUnbindSourceProvider', locale));
660
+ }
661
+ if (
662
+ !getConnectedAccounts(bindUser).some(
663
+ (x) => x.provider === connectedAccount.provider && x.did === connectedAccount.did && x.pk === connectedAccount.pk
664
+ )
665
+ ) {
666
+ throw new Error(t('accountNotExist', locale));
667
+ }
668
+
669
+ await node.disconnectUserAccount({
670
+ teamDid,
671
+ connectedAccount,
672
+ });
673
+
674
+ const masterSite = federatedUtil.getFederatedMaster(blocklet);
675
+ if (federatedUtil.shouldSyncFederated(sourceAppPid, blocklet)) {
676
+ // NOTICE: 采用异步来更新,不阻塞接口的正常响应
677
+ const syncUserData = {
678
+ did: bindUser.did,
679
+ pk: bindUser.pk,
680
+ disconnectedAccount: connectedAccount,
681
+ };
682
+ node.syncFederated({
683
+ did: teamDid,
684
+ data: {
685
+ users: [
686
+ {
687
+ ...syncUserData,
688
+ action: 'disconnectAccount',
689
+ sourceAppPid: sourceAppPid || masterSite?.appPid,
690
+ },
691
+ ],
692
+ },
693
+ });
694
+ }
695
+ }
696
+
596
697
  module.exports = {
597
698
  init(server, node, options) {
598
699
  async function configsFn(req, res) {
@@ -686,6 +787,22 @@ module.exports = {
686
787
  server.post(`${prefix}/bind`, bindFn);
687
788
  server.post(`${prefixApi}/bind`, bindFn);
688
789
 
790
+ async function unbindFn(req, res) {
791
+ try {
792
+ await unbind(req, node, options);
793
+ res.status(200).json({});
794
+ } catch (err) {
795
+ logger.error('Failed unbind oauth', { error: err });
796
+ if (err instanceof ApiError) {
797
+ res.status(err.code).send(err.message);
798
+ return;
799
+ }
800
+ throw err;
801
+ }
802
+ }
803
+
804
+ server.post(`${prefixApi}/unbind`, unbindFn);
805
+
689
806
  async function switchFn(req, res) {
690
807
  const { did: userDid, provider } = req.user;
691
808
  const { passportId } = req.body;
@@ -751,11 +868,10 @@ module.exports = {
751
868
  server.post(`${prefixApi}/login`, loginFn);
752
869
 
753
870
  async function getUserFn(req, res) {
754
- const { token, provider } = req.body;
755
- const { wallet: blockletWallet } = await req.getBlockletInfo();
871
+ const { provider, token, idToken, code, appPid } = req.body;
756
872
  const blocklet = await req.getBlocklet();
757
- const authClient = getAuthClient(blocklet, provider);
758
- const oauthInfo = await authClient.getProfile(token);
873
+ const oauthInfo = await getOAuthUserInfo({ blocklet, provider, token, idToken, code, appPid });
874
+ const { wallet: blockletWallet } = await req.getBlockletInfo();
759
875
  const userWallet = fromAppDid(oauthInfo.sub, blockletWallet.secretKey);
760
876
 
761
877
  res.json({
@@ -765,5 +881,44 @@ module.exports = {
765
881
  }
766
882
  server.post(`${prefix}/getUser`, getUserFn);
767
883
  server.post(`${prefixApi}/getUser`, getUserFn);
884
+
885
+ server.get(`${prefix}/login/:provider`, async (req, res) => {
886
+ const { provider } = req.params;
887
+ const blocklet = await req.getBlocklet();
888
+ const avaliableProviderList = Object.keys(blocklet.settings?.oauth).filter(
889
+ (x) => blocklet.settings?.oauth[x]?.enabled === true
890
+ );
891
+ if (!avaliableProviderList.includes(provider)) {
892
+ res.status(400).send(`Provider: ${provider} is not supported`);
893
+ return;
894
+ }
895
+ const client = getAuthClient(blocklet, provider);
896
+ const authUrl = client.getAuthorizationUrl();
897
+ res.redirect(authUrl);
898
+ });
899
+ // HACK: apple 需要特殊处理,callback 使用的是 post 请求返回的,通过特殊处理转为 get 请求,转由前端继续处理
900
+ // 此处改为所有 provider 都兼容的模式
901
+ server.post(`${prefix}/callback/:provider`, (req, res) => {
902
+ /**
903
+ * @type {{code?: string, user?: {name: {firstName: string, lastName: string}, email: string}}}
904
+ */
905
+ const { code, user } = req.body;
906
+ // HACK: URL 构造函数必须传入一个域名
907
+ const redirectUrl = new URL(req.url, 'http://127.0.0.1');
908
+ if (code) {
909
+ redirectUrl.searchParams.set('code', code);
910
+ if (user) {
911
+ // FIXME: @zhanghan apple 的用户名信息只会在首次获取 code 时出现,该如何将这个值传递给另一个接口中?
912
+ // 即使如此处理,也无法完全解决后续无法获取到用户名的问题。如果一个已有的 oauth 配置配置到了另一个应用中,对于 apple 来说,该用户已经不是第一次登录了,就不会返回用户名信息
913
+ // redirectUrl.searchParams.set('name', `${user.name.firstName} ${user.name.lastName}`);
914
+ }
915
+ } else {
916
+ const error = 'true';
917
+ const errorDescription = 'invalid code';
918
+ redirectUrl.searchParams.set('error', error);
919
+ redirectUrl.searchParams.set('error_description', errorDescription);
920
+ }
921
+ res.redirect(`${redirectUrl.pathname}${redirectUrl.search}`);
922
+ });
768
923
  },
769
924
  };
@@ -252,7 +252,13 @@ module.exports = function createRoutes(node, _, createSessionToken) {
252
252
  locale,
253
253
  lastLoginIp: get(req, 'headers[x-real-ip]') || '',
254
254
  passports: [passport],
255
- connectedAccounts: [{ provider: LOGIN_PROVIDER.WALLET, did: userDid, pk: userPk }],
255
+ connectedAccounts: [
256
+ {
257
+ provider: LOGIN_PROVIDER.WALLET,
258
+ did: userDid,
259
+ pk: userPk,
260
+ },
261
+ ],
256
262
  ...profile,
257
263
  avatar,
258
264
  },
@@ -3,6 +3,8 @@ const { default: urlFriendly } = require('@blocklet/meta/lib/url-friendly');
3
3
  const { slugify } = require('transliteration');
4
4
  const { MAIN_CHAIN_ENDPOINT } = require('@abtnode/constant');
5
5
  const { getBlockletChainInfo } = require('@blocklet/meta/lib/util');
6
+ const jwt = require('jsonwebtoken');
7
+ const jwksClient = require('jwks-rsa');
6
8
 
7
9
  const logger = require('../../libs/logger')('blocklet-services:oauth');
8
10
 
@@ -78,7 +80,75 @@ async function migrateAccount({ wallet, blocklet, user }) {
78
80
  await Promise.all(waitingList);
79
81
  }
80
82
 
83
+ async function getJWK(kid, jwksUri) {
84
+ const client = jwksClient({
85
+ cache: true,
86
+ jwksUri,
87
+ });
88
+ const key = await new Promise((resolve, reject) => {
89
+ client.getSigningKey(kid, (error, result) => {
90
+ if (error) {
91
+ return reject(error);
92
+ }
93
+ return resolve(result);
94
+ });
95
+ });
96
+ return {
97
+ publicKey: key.getPublicKey(),
98
+ kid: key.kid,
99
+ alg: key.alg,
100
+ };
101
+ }
102
+
103
+ /**
104
+ * Verify the authenticity of a token by decoding, verifying the algorithm, and matching claims.
105
+ *
106
+ * @param {Object} params - Object containing idToken, jwksUri, nonce, iss, clientId
107
+ * @param {string} params.idToken - JWT token
108
+ * @param {string} params.jwksUri - JWK set URI
109
+ * @param {string} params.nonce - Nonce
110
+ * @param {string} params.iss - Issuer
111
+ * @param {string} params.clientId - Client ID
112
+ * @return {Object} Decoded JWT claims if token is valid
113
+ */
114
+ async function verifyIdToken(params) {
115
+ const decoded = jwt.decode(params.idToken, { complete: true });
116
+ const { kid, alg: jwtAlg } = decoded.header;
117
+
118
+ const { publicKey, alg: jwkAlg } = await getJWK(kid, params.jwksUri);
119
+
120
+ if (jwtAlg !== jwkAlg) {
121
+ throw new Error(`The alg does not match the jwk configuration - alg: ${jwtAlg} | expected: ${jwkAlg}`);
122
+ }
123
+
124
+ const jwtClaims = jwt.verify(params.idToken, publicKey, {
125
+ algorithms: [jwkAlg],
126
+ nonce: params.nonce,
127
+ });
128
+
129
+ if (jwtClaims?.iss !== params.iss) {
130
+ throw new Error(`The iss does not match the Apple URL - iss: ${jwtClaims.iss} | expected: ${params.iss}`);
131
+ }
132
+
133
+ const isFounded = [].concat(jwtClaims.aud).some((aud) => [].concat(params.clientId).includes(aud));
134
+
135
+ if (isFounded) {
136
+ ['email_verified', 'is_private_email'].forEach((field) => {
137
+ if (jwtClaims[field] !== undefined) {
138
+ jwtClaims[field] = Boolean(jwtClaims[field]);
139
+ }
140
+ });
141
+
142
+ return jwtClaims;
143
+ }
144
+
145
+ throw new Error(
146
+ `The aud parameter does not include this client - is: ${jwtClaims.aud} | expected: ${params.clientId}`
147
+ );
148
+ }
149
+
81
150
  module.exports = {
82
151
  declareAccount,
83
152
  migrateAccount,
153
+ verifyIdToken,
84
154
  };