@abtnode/blocklet-services 1.16.41-beta-20250325-054737-39c060ca → 1.16.41-beta-20250329-232306-53c45e3b
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/api/emails/_components/actions.js +9 -0
- package/api/emails/{components → _components}/article.js +1 -1
- package/api/emails/{components → _components}/asset.js +2 -2
- package/api/emails/{components → _components}/attachments.js +15 -13
- package/api/emails/{components → _components}/compose.js +2 -2
- package/api/emails/_components/content.js +15 -0
- package/api/emails/{components → _components}/footer.js +1 -1
- package/api/emails/{components → _components}/powered-by.js +1 -1
- package/api/emails/{components → _components}/token.js +2 -2
- package/api/emails/{components → _components}/transaction.js +1 -1
- package/api/emails/{pages → _templates}/notification.js +10 -10
- package/api/emails/_templates/verify-code-body.js +49 -0
- package/api/emails/_templates/verify-code.js +37 -0
- package/api/index.js +59 -71
- package/api/libs/auth/utils.js +20 -1
- package/api/libs/connect/session.js +55 -3
- package/api/libs/email.js +48 -16
- package/api/middlewares/body-parser.js +10 -0
- package/api/routes/blocklet.js +17 -4
- package/api/routes/federated.js +36 -13
- package/api/routes/mcp.js +87 -0
- package/api/routes/user-session.js +16 -1
- package/api/routes/user.js +442 -40
- package/api/services/auth/connect/issue-passport.js +4 -3
- package/api/services/auth/connect/receive-transfer-app-owner.js +17 -1
- package/api/services/auth/connect/verify-destroy.js +20 -0
- package/api/services/auth/connect/verify-elevated.js +20 -0
- package/api/services/auth/index.js +0 -2
- package/api/services/auth/session.js +1 -1
- package/api/services/kyc/index.js +1 -1
- package/api/services/mcp/server.js +37 -0
- package/api/services/notification/queue.js +5 -4
- package/api/services/studio/index.js +4 -0
- package/api/util/federated.js +16 -0
- package/dist/assets/{ArrowDropDown-BZ7nr-4O.js → ArrowDropDown-odHjNJIs.js} +1 -1
- package/dist/assets/{CheckCircle-V33X02sL.js → CheckCircle-BqOSvJMp.js} +1 -1
- package/dist/assets/{ChevronLeft-BhAwkTPM.js → ChevronLeft-Dv7rlUf5.js} +1 -1
- package/dist/assets/{ChevronRight-BFn5n61K.js → ChevronRight-CWeJn1iz.js} +1 -1
- package/dist/assets/{Community-CJcr3sxm.js → Community-CMFBTMi_.js} +1 -1
- package/dist/assets/{DeleteOutline-CI9d5QQr.js → DeleteOutline-Cr-TyrUV.js} +1 -1
- package/dist/assets/{Done-CvHaNROm.js → Done-C1TA1wbA.js} +1 -1
- package/dist/assets/{Download-CA6PpJRL.js → Download-DxhnUTM_.js} +1 -1
- package/dist/assets/{EditIcon-d_J6X2-M.js → EditIcon-Bgmg6UE0.js} +1 -1
- package/dist/assets/{Email-Cv_Ltuyk.js → Email-BphlAPta.js} +1 -1
- package/dist/assets/{Error-CkH40x6R.js → Error-pXXvJFxN.js} +1 -1
- package/dist/assets/{ExpandLess-DPn_CrO1.js → ExpandLess-Dc_dI20M.js} +1 -1
- package/dist/assets/{Google-cBME17GZ.js → Google-DNUuYFIA.js} +1 -1
- package/dist/assets/{Holiday-DtecZXIQ.js → Holiday-DKefG3Bb.js} +1 -1
- package/dist/assets/{InfoOutlined-DNOhP8aN.js → InfoOutlined-CEUhR4DY.js} +1 -1
- package/dist/assets/{Launch-DR5uY37Q.js → Launch-BUTOr3cR.js} +1 -1
- package/dist/assets/{LaunchOutlined-DzKl31aT.js → LaunchOutlined-QHq-RUJt.js} +1 -1
- package/dist/assets/{Location-CdiPklKa.js → Location-BuaxMxu4.js} +1 -1
- package/dist/assets/{LockIcon-CzTeIv2K.js → LockIcon-CQ1tExIY.js} +1 -1
- package/dist/assets/{Meeting-DQYhfakU.js → Meeting-Cs60QtvP.js} +1 -1
- package/dist/assets/{MoreHoriz-DvRtrl2X.js → MoreHoriz-D1SOL47a.js} +1 -1
- package/dist/assets/{OffSick-DwG4gWQF.js → OffSick-DinPaelp.js} +1 -1
- package/dist/assets/{Phone-CETYuRms.js → Phone-BmouKMyi.js} +1 -1
- package/dist/assets/{PlayArrow-B_DkcrSm.js → PlayArrow-D7cGSxeN.js} +1 -1
- package/dist/assets/{QuestionMarkCircle-CJiBom-M.js → QuestionMarkCircle-Dt9XJWEe.js} +1 -1
- package/dist/assets/{ServerLogo-Ow_B_zvr.js → ServerLogo-B33ZRE5q.js} +1 -1
- package/dist/assets/{Timezone-BX4VhFOS.js → Timezone-CyMhRxlx.js} +1 -1
- package/dist/assets/{ViewList-CVmPM3e1.js → ViewList-vu1qOJ6P.js} +1 -1
- package/dist/assets/{WorkingRemotely-DIBVduhu.js → WorkingRemotely-BP_VIHeM.js} +1 -1
- package/dist/assets/access-control-CpxcJItK.js +14 -0
- package/dist/assets/{actions-DqC3kJCr.js → actions-4oGfCMlR.js} +1 -1
- package/dist/assets/add-component-core-CE0nArG4.js +761 -0
- package/dist/assets/add-resource-BYM4JwzE.js +1 -0
- package/dist/assets/{addon-BUKk8W1Q.js → addon-B9bb2bvM.js} +1 -1
- package/dist/assets/analytics-BXLe73MI.js +11 -0
- package/dist/assets/api-D9Yi7Zdr.js +1 -0
- package/dist/assets/appearance-DX7SoW1u.js +1 -0
- package/dist/assets/ar-2k9jaPIk.js +3 -0
- package/dist/assets/{audit-logs-CzmQ7awq.js → audit-logs-D8H5E0fC.js} +3 -3
- package/dist/assets/{base32-CwsOMJzz.js → base32-BNpDT-6Q.js} +1 -1
- package/dist/assets/{branding-COlKYnkW.js → branding-BW1rhy8d.js} +2 -2
- package/dist/assets/bundle-avatar-DHwmIozH.js +1 -0
- package/dist/assets/button-CNnuiac8.js +1 -0
- package/dist/assets/{click-to-copy-BF_uUMNm.js → click-to-copy-B0HMuCN_.js} +1 -1
- package/dist/assets/complete-BNf0-iqY.js +45 -0
- package/dist/assets/{component-D_dh8l-k.js → component-1r0VSsqq.js} +2 -2
- package/dist/assets/{config-D7fEb50y.js → config-BeD5_8kr.js} +2 -2
- package/dist/assets/config-CqHmZdZv.js +1 -0
- package/dist/assets/{config-navigation-D8XsruhL.js → config-navigation-DtN0_qFZ.js} +3 -3
- package/dist/assets/{config-space-BsNunj5H.js → config-space-DlwZewcm.js} +1 -1
- package/dist/assets/{confirm-SUs_DctB.js → confirm-DmMN-34s.js} +2 -2
- package/dist/assets/connect-BfSOEYV-.js +1 -0
- package/dist/assets/{connect-B1JNOfKg.js → connect-CC9dcVMV.js} +1 -1
- package/dist/assets/{connect-to-D_r9o9e2.js → connect-to-CzffwD9G.js} +1 -1
- package/dist/assets/{content-layout-BQE-NWxj.js → content-layout-7vu3yv_f.js} +1 -1
- package/dist/assets/dashboard-C_Q4TjPa.js +216 -0
- package/dist/assets/de-B9cLhykn.js +3 -0
- package/dist/assets/{delete-confirm-CIzTutM8.js → delete-confirm-BTaU_6Kx.js} +1 -1
- package/dist/assets/did-address-BC-0j4Pt.js +1 -0
- package/dist/assets/{domain-Cs9dCGZy.js → domain-CLMRXecU.js} +1 -1
- package/dist/assets/{domain-action-card-CmEjTVIf.js → domain-action-card-DwQ7Q8wY.js} +2 -2
- package/dist/assets/domains-DF7TtN5q.js +1 -0
- package/dist/assets/{email-DcW8pu9K.js → email-mj0bVFDS.js} +2 -2
- package/dist/assets/es-BoQohonz.js +3 -0
- package/dist/assets/{exchange-passport-SiIAETh-.js → exchange-passport--DeUPzbW.js} +1 -1
- package/dist/assets/form-text-input-BYF6lVnE.js +11 -0
- package/dist/assets/fr-doSYAOrt.js +3 -0
- package/dist/assets/{fuel-BkpixTuh.js → fuel-D-kOZuF6.js} +1 -1
- package/dist/assets/{fullpage-BK45P92Z.js → fullpage-DO8Hcbkl.js} +1 -1
- package/dist/assets/{get-safe-url-BPBKhLNl.js → get-safe-url-BKl2A9x2.js} +1 -1
- package/dist/assets/{get-safe-url-D5pnveC6.js → get-safe-url-QFq5JNoE.js} +1 -1
- package/dist/assets/hi-B_BwhpD8.js +1 -0
- package/dist/assets/{home-qm_lBtgD.js → home-CpnMpXiw.js} +1 -1
- package/dist/assets/id-BgYIZCvk.js +3 -0
- package/dist/assets/{iframe-MARzuPzd.js → iframe-B9mCpo4I.js} +1 -1
- package/dist/assets/{index-ZhFE0VPc.js → index-79U1RPaq.js} +1 -1
- package/dist/assets/{index-RkynV8Hy.js → index-B4Q2DAdn.js} +11 -11
- package/dist/assets/index-BJ2lJo7L.js +346 -0
- package/dist/assets/{index-De6c2gZ_.js → index-BMSA5TdD.js} +1 -1
- package/dist/assets/{index-DkVrxjpZ.js → index-BVOYP6aR.js} +2 -2
- package/dist/assets/{index-MSp2Bfol.js → index-BZvVDfZ4.js} +1 -1
- package/dist/assets/index-BsUr7wGb.js +104 -0
- package/dist/assets/index-C44fECmB.js +138 -0
- package/dist/assets/{index-BRMrRxju.js → index-CGK3FEjY.js} +2 -2
- package/dist/assets/{index-DQKEK30u.js → index-CJCg9yIK.js} +1 -1
- package/dist/assets/index-CmKAznDh.js +109 -0
- package/dist/assets/{index-CjVqsxIQ.js → index-Ct7s2LPI.js} +17 -17
- package/dist/assets/{index-BwhP13EW.js → index-DQ_RzIwU.js} +2 -2
- package/dist/assets/{index-BhEkFtB1.js → index-D_wVtHmh.js} +1 -1
- package/dist/assets/{index-BJQAwe5r.js → index-DqC2o5PB.js} +1 -1
- package/dist/assets/index-DyTFEgKr.js +1 -0
- package/dist/assets/index-eCY24sH9.js +137 -0
- package/dist/assets/{index-9twdGxCF.js → index-fWGZM-oP.js} +1 -1
- package/dist/assets/index-gTQQ3SoE.js +290 -0
- package/dist/assets/index-gcSQTx25.js +1 -0
- package/dist/assets/{index-D9S8aCU6.js → index-m8CaSxXx.js} +1 -1
- package/dist/assets/{index-CcooKt06.js → index-u-lA6P_E.js} +1 -1
- package/dist/assets/invitation-B8Qx_pFq.js +176 -0
- package/dist/assets/invite-BBaF_vyc.js +1 -0
- package/dist/assets/{issue-passport-UNdxuvTV.js → issue-passport-DC-ly7xg.js} +1 -1
- package/dist/assets/{item-vIB3V7Bi.js → item-BSGXym2I.js} +1 -1
- package/dist/assets/ja-FMMLI8YD.js +3 -0
- package/dist/assets/ko-C0kmRXYE.js +3 -0
- package/dist/assets/{launch-result-message-irP4_5v3.js → launch-result-message-ByEG8r_7.js} +1 -1
- package/dist/assets/{layout-C6s5HhIK.js → layout-BqhTD729.js} +1 -1
- package/dist/assets/list-BHs8uNT5.js +200 -0
- package/dist/assets/{list-header-s-ciXcTm.js → list-header-BmTDmRY0.js} +1 -1
- package/dist/assets/localization-daPAWMzR.js +1 -0
- package/dist/assets/{log-lc82BnEg.js → log-o_Cadt8j.js} +7 -7
- package/dist/assets/{login-BMWayJcu.js → login-BQozrLDm.js} +1 -1
- package/dist/assets/{login-oauth-callback-CNErXZTB.js → login-oauth-callback-D7lIww1c.js} +1 -1
- package/dist/assets/{logo-uploader-CEXNuQSC.js → logo-uploader-CUQ3aWZJ.js} +3 -3
- package/dist/assets/{lost-passport-CvNrb0v1.js → lost-passport-DkETH2Lu.js} +3 -3
- package/dist/assets/{omit-DVwO9e3E.js → omit-DZNQhOf-.js} +1 -1
- package/dist/assets/{open-window-B6eFUa8p.js → open-window-BOl-kTC2.js} +1 -1
- package/dist/assets/{overview-mT5IG7r5.js → overview-Bxm05EH6.js} +1 -1
- package/dist/assets/{page-header-B4LQuuxg.js → page-header-Dm1v9v5q.js} +1 -1
- package/dist/assets/{permission-C4cBxu8K.js → permission-B5jnw9r2.js} +1 -1
- package/dist/assets/{preferences-tDE3WRRj.js → preferences-uri2RXdB.js} +1 -1
- package/dist/assets/profile-embed-DnIQcD-H.js +1 -0
- package/dist/assets/pt-C2UJZK-O.js +1 -0
- package/dist/assets/publish-resource-V53U1NNv.js +1 -0
- package/dist/assets/{react-beautiful-dnd.esm-BnCmTyEw.js → react-beautiful-dnd.esm-B9MfX9Xl.js} +4 -4
- package/dist/assets/{relative-time-W13XZ7qT.js → relative-time-RQnsWZQc.js} +1 -1
- package/dist/assets/ru-DDA5s4-r.js +1 -0
- package/dist/assets/sdk-BKSsfgrc.js +1 -0
- package/dist/assets/{session-D0E4yKh1.js → session-6ZeN-aF2.js} +1 -1
- package/dist/assets/{setup-vmtHxx8b.js → setup-7Gq9K_8k.js} +3 -3
- package/dist/assets/{shorten-label-CYUVOsQQ.js → shorten-label-DkFTGSoy.js} +1 -1
- package/dist/assets/simple-select-CqakAZFe.js +1 -0
- package/dist/assets/{slicedToArray-W0RilTJp.js → slicedToArray-BuQur6Mi.js} +2 -2
- package/dist/assets/{start-B6iWRYkb.js → start-BSUY3DBl.js} +1 -1
- package/dist/assets/{status-CBD3RDbA.js → status-C52-BFuY.js} +1 -1
- package/dist/assets/{step-actions-D1bVdqNc.js → step-actions-CA74dwgt.js} +1 -1
- package/dist/assets/{studio-CI-t_7sU.js → studio-CAnZfyBM.js} +1 -1
- package/dist/assets/{switch-control-BqINsLim.js → switch-control-BBlLGjaH.js} +1 -1
- package/dist/assets/th-DmqOUn4C.js +1 -0
- package/dist/assets/{traffic-kGqrk2bv.js → traffic-CJDIGmp5.js} +3 -3
- package/dist/assets/{transfer-ii7u2wA5.js → transfer-cG2e24sz.js} +1 -1
- package/dist/assets/{unsubscribe-DvsPQr2X.js → unsubscribe-afXXIHEf.js} +1 -1
- package/dist/assets/{use-mobile-CVbqh2Dx.js → use-mobile-CUT5hy9q.js} +1 -1
- package/dist/assets/{useAsync-D2OhgEkV.js → useAsync-DM8qaMe4.js} +1 -1
- package/dist/assets/{useAsyncRetry-Dju2R4r6.js → useAsyncRetry-B7SbzXVI.js} +2 -2
- package/dist/assets/{useLocalStorage-B4mjl5_Q.js → useLocalStorage-Dd4pybDP.js} +1 -1
- package/dist/assets/user-center-T8Xw464s.js +67 -0
- package/dist/assets/{util-CVH97CBw.js → util-C_BCTHfw.js} +1 -1
- package/dist/assets/{util-BT3jBHtZ.js → util-YyWTpLT7.js} +1 -1
- package/dist/assets/vendor-arcblock-DvaaLvvM.js +2361 -0
- package/dist/assets/{vendor-hooks-CYyfK9g3.js → vendor-hooks-sw5wvnca.js} +2 -2
- package/dist/assets/vendor-mui-core-DxqV1NVn.js +266 -0
- package/dist/assets/{vendor-mui-x-BTReKX4-.js → vendor-mui-x-BjL1xCkk.js} +1 -1
- package/dist/assets/{vendor-utils-CbeKtci8.js → vendor-utils-DR57WNxP.js} +7 -7
- package/dist/assets/{vendor-ux-did-connect-Dg7MsmIi.js → vendor-ux-did-connect-Bciw9Ypu.js} +57 -53
- package/dist/assets/vi-Cf__CtPD.js +1 -0
- package/dist/assets/wrap-locale-CX50Vz0w.js +1 -0
- package/dist/assets/zh-NXspK2yu.js +4 -0
- package/dist/assets/zh-tw-DYYDVX5I.js +3 -0
- package/dist/index.html +6 -6
- package/dist/service-worker.js +2 -2
- package/package.json +41 -36
- package/api/emails/components/actions.js +0 -9
- package/api/emails/components/content.js +0 -15
- package/dist/assets/ExternalIssuerIcon-BLzCUF1d.js +0 -1
- package/dist/assets/access-control-Dk5vBHfS.js +0 -13
- package/dist/assets/add-component-core-DAx5yIIg.js +0 -765
- package/dist/assets/add-resource-DkZqrFbC.js +0 -1
- package/dist/assets/analytics-BpEvUU_V.js +0 -11
- package/dist/assets/api-CvDkueg1.js +0 -1
- package/dist/assets/ar-CVfYi9he.js +0 -3
- package/dist/assets/bundle-avatar-mGTppfmO.js +0 -1
- package/dist/assets/button-Bp47Zc1i.js +0 -1
- package/dist/assets/complete-BGei1NKc.js +0 -45
- package/dist/assets/config-C4ca73g2.js +0 -1
- package/dist/assets/connect-CHnKwaTE.js +0 -1
- package/dist/assets/dashboard-CcevSjZB.js +0 -218
- package/dist/assets/de-B4s5NdlK.js +0 -3
- package/dist/assets/did-address-DnGQ03FF.js +0 -1
- package/dist/assets/domains-ByJLKnQ7.js +0 -1
- package/dist/assets/es-C48Qrhnr.js +0 -3
- package/dist/assets/form-text-input-B4Ezw4UV.js +0 -11
- package/dist/assets/fr-C_zb-sN3.js +0 -3
- package/dist/assets/hi-Bd1v0vAT.js +0 -1
- package/dist/assets/id-CAHN8LyN.js +0 -3
- package/dist/assets/index-2hZd332d.js +0 -138
- package/dist/assets/index-BMxUr1Yy.js +0 -346
- package/dist/assets/index-BSEtdOzl.js +0 -134
- package/dist/assets/index-BvH3rra0.js +0 -1
- package/dist/assets/index-C482NgPS.js +0 -104
- package/dist/assets/index-CZIXrPaX.js +0 -109
- package/dist/assets/index-CqnBzijY.js +0 -137
- package/dist/assets/index-mdmh1Xx6.js +0 -1
- package/dist/assets/invitation-ehDH3V7F.js +0 -176
- package/dist/assets/invite-BVZfCzI6.js +0 -1
- package/dist/assets/ja-DTJzPeZ-.js +0 -3
- package/dist/assets/ko--y3FSpCf.js +0 -3
- package/dist/assets/list-DS45KBMo.js +0 -195
- package/dist/assets/localization-BAzVJOkA.js +0 -1
- package/dist/assets/login-bg-CBktcoss.jpg +0 -0
- package/dist/assets/profile-embed-CNrAUity.js +0 -1
- package/dist/assets/pt-vs00P-F0.js +0 -1
- package/dist/assets/publish-resource-ApDQigSb.js +0 -1
- package/dist/assets/ru-CMsriCRQ.js +0 -1
- package/dist/assets/sdk-CdntdIAz.js +0 -1
- package/dist/assets/simple-select-CI23fC_x.js +0 -1
- package/dist/assets/th-_91ROtZM.js +0 -1
- package/dist/assets/user-center-Bi7zYk4z.js +0 -79
- package/dist/assets/vendor-arcblock-CCyyB91P.js +0 -2361
- package/dist/assets/vendor-mui-core-DSM7ZGuY.js +0 -266
- package/dist/assets/vi-BAo1CsMy.js +0 -1
- package/dist/assets/wrap-locale-BMEB19si.js +0 -1
- package/dist/assets/zh-8LRwh-dv.js +0 -4
- package/dist/assets/zh-tw-C8jGxySi.js +0 -3
- /package/api/emails/{components → _components}/copyright.js +0 -0
- /package/api/emails/{components → _components}/dapp.js +0 -0
- /package/api/emails/{components → _components}/header.js +0 -0
- /package/api/emails/{components → _components}/layout.js +0 -0
- /package/api/emails/{libs → _libs}/chain.js +0 -0
- /package/api/emails/{libs → _libs}/config.js +0 -0
- /package/api/emails/{libs → _libs}/constant.js +0 -0
- /package/api/emails/{libs → _libs}/explorer-url.js +0 -0
- /package/api/emails/{libs → _libs}/func.js +0 -0
- /package/api/emails/{libs → _libs}/highlight.js +0 -0
- /package/api/emails/{libs → _libs}/style.js +0 -0
- /package/api/emails/{libs → _libs}/util.js +0 -0
package/api/routes/user.js
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
|
-
const {
|
|
1
|
+
const {
|
|
2
|
+
WELLKNOWN_SERVICE_PATH_PREFIX,
|
|
3
|
+
SECURITY_RULE_DEFAULT_ID,
|
|
4
|
+
ROLES,
|
|
5
|
+
VERIFY_CODE_TTL,
|
|
6
|
+
PASSPORT_STATUS,
|
|
7
|
+
} = require('@abtnode/constant');
|
|
8
|
+
const JWT = require('@arcblock/jwt');
|
|
9
|
+
const { render } = require('@react-email/components');
|
|
2
10
|
const { messages, getApplicationInfo } = require('@abtnode/auth/lib/auth');
|
|
3
11
|
const { fromAppDid } = require('@arcblock/did-ext');
|
|
4
|
-
const { extractUserAvatar } = require('@abtnode/util/lib/user');
|
|
12
|
+
const { extractUserAvatar, getUserAvatarUrl } = require('@abtnode/util/lib/user');
|
|
5
13
|
const formatContext = require('@abtnode/util/lib/format-context');
|
|
6
14
|
const createTranslator = require('@abtnode/util/lib/translate');
|
|
7
15
|
const { fromBase64 } = require('@ocap/util');
|
|
@@ -12,8 +20,9 @@ const merge = require('lodash/merge');
|
|
|
12
20
|
const omitBy = require('lodash/omitBy');
|
|
13
21
|
const omit = require('lodash/omit');
|
|
14
22
|
const uniq = require('lodash/uniq');
|
|
23
|
+
const last = require('lodash/last');
|
|
24
|
+
const sortBy = require('lodash/sortBy');
|
|
15
25
|
const { getLastUsedPassport } = require('@abtnode/auth/lib/passport');
|
|
16
|
-
const { verify, decode } = require('@arcblock/jwt');
|
|
17
26
|
const { getWallet } = require('@blocklet/meta/lib/did-utils');
|
|
18
27
|
const { Joi } = require('@arcblock/validator');
|
|
19
28
|
const { parse } = require('@abtnode/core/lib/util/ua');
|
|
@@ -21,7 +30,11 @@ const { checkInvitedUserOnly } = require('@abtnode/auth/lib/oauth');
|
|
|
21
30
|
const getRequestIP = require('@abtnode/util/lib/get-request-ip');
|
|
22
31
|
const formatError = require('@abtnode/util/lib/format-error');
|
|
23
32
|
const CustomError = require('@abtnode/util/lib/custom-error');
|
|
33
|
+
const { callFederated } = require('@abtnode/auth/lib/util/federated');
|
|
24
34
|
const { xss } = require('@blocklet/xss');
|
|
35
|
+
const { withQuery } = require('ufo');
|
|
36
|
+
const cors = require('cors');
|
|
37
|
+
|
|
25
38
|
const { createTokenFn, getDidConnectVersion } = require('../util');
|
|
26
39
|
const initJwt = require('../libs/jwt');
|
|
27
40
|
const { getAvatarByUrl } = require('../libs/auth/utils');
|
|
@@ -31,8 +44,13 @@ const { getAvatarByEmail } = require('../libs/auth/utils');
|
|
|
31
44
|
const logger = require('../libs/logger')('user');
|
|
32
45
|
const ensureBlocklet = require('../middlewares/ensure-blocklet');
|
|
33
46
|
const checkUser = require('../middlewares/check-user');
|
|
34
|
-
const { getUserWithinFederated, getUserAvatarUrl } = require('../util/federated');
|
|
47
|
+
const { getUserWithinFederated, getUserAvatarUrl: getRawUserAvatarUrl } = require('../util/federated');
|
|
35
48
|
const { Profile } = require('../state/profile');
|
|
49
|
+
const { emailSchema } = require('../socket/channel/did');
|
|
50
|
+
const { sendEmail } = require('../libs/email');
|
|
51
|
+
const federatedUtil = require('../util/federated');
|
|
52
|
+
const { VerifyCodeBody } = require('../emails/_templates/verify-code-body');
|
|
53
|
+
const { checkFederatedCall } = require('../middlewares/check-federated');
|
|
36
54
|
|
|
37
55
|
const validateUser = (user) => {
|
|
38
56
|
try {
|
|
@@ -57,12 +75,25 @@ const translations = {
|
|
|
57
75
|
needComponentId: '缺少登录参数: componentId',
|
|
58
76
|
userInfoError: '登录用户信息有误',
|
|
59
77
|
notExist: '用户不存在',
|
|
78
|
+
//
|
|
79
|
+
emailInvalid: '邮箱格式不正确',
|
|
80
|
+
emailSendSuccess: '验证码发送成功',
|
|
81
|
+
emailAlreadySent: '我们已发送验证电子邮件。请检查您的收件箱或垃圾邮件文件夹。如果您没有看到它,您可以稍后再试。',
|
|
82
|
+
emailVerifySuccess: '邮箱验证成功',
|
|
83
|
+
emailTitle: '登录 {appName}',
|
|
60
84
|
},
|
|
61
85
|
en: {
|
|
62
86
|
notAllowed: 'Your have been revoked access to this blocklet',
|
|
63
87
|
needComponentId: 'componentId is required when login user',
|
|
64
88
|
userInfoError: 'Login user info is invalid',
|
|
65
89
|
notExist: 'User does not exist',
|
|
90
|
+
//
|
|
91
|
+
emailInvalid: 'Invalid email format',
|
|
92
|
+
emailSendSuccess: 'Verification code sent successfully',
|
|
93
|
+
emailAlreadySent:
|
|
94
|
+
"We've sent a verification email. Please check your inbox and spam folder. If you don't see it, you can try again in a few minutes.",
|
|
95
|
+
emailVerifySuccess: 'Email verified successfully',
|
|
96
|
+
emailTitle: 'Login to {appName}',
|
|
66
97
|
},
|
|
67
98
|
};
|
|
68
99
|
|
|
@@ -367,7 +398,7 @@ async function verifyUserSig({ userDid, signature, teamDid, sourceAppPid, userPk
|
|
|
367
398
|
const userDidWallet = getWallet(currentUser);
|
|
368
399
|
|
|
369
400
|
// HACK: @zhanghan 钱包传递的签名使用的是 didWallet 账户,但这个账户可能是 原生的 oauth 账户,所以需要找到对应的 did-wallet 账户
|
|
370
|
-
const valid = verify(signature, userDidWallet.pk);
|
|
401
|
+
const valid = await JWT.verify(signature, userDidWallet.pk);
|
|
371
402
|
if (!valid) {
|
|
372
403
|
throw new Error('invalid signature');
|
|
373
404
|
}
|
|
@@ -395,7 +426,7 @@ function checkUserSig({ node }) {
|
|
|
395
426
|
}
|
|
396
427
|
|
|
397
428
|
const userDidWallet = getWallet(user);
|
|
398
|
-
const valid = verify(userSig, userDidWallet.pk);
|
|
429
|
+
const valid = await JWT.verify(userSig, userDidWallet.pk);
|
|
399
430
|
if (!valid) {
|
|
400
431
|
logger.error('Verify sig failed', {
|
|
401
432
|
userDid,
|
|
@@ -412,7 +443,7 @@ function checkUserSig({ node }) {
|
|
|
412
443
|
fullName: user.fullName,
|
|
413
444
|
walletOS,
|
|
414
445
|
};
|
|
415
|
-
const resData = decode(userSig, true);
|
|
446
|
+
const resData = JWT.decode(userSig, true);
|
|
416
447
|
// NOTICE: set decode data to req.body, it's not a good practice, but we need it in next middleware
|
|
417
448
|
req.body = pick(resData, ['notifications']);
|
|
418
449
|
}
|
|
@@ -462,7 +493,7 @@ const profileSetSchema = Joi.object({
|
|
|
462
493
|
metadata: Joi.object({
|
|
463
494
|
email: Joi.string().email().optional(),
|
|
464
495
|
phone: Joi.object({
|
|
465
|
-
phoneNumber: Joi.string().optional(),
|
|
496
|
+
phoneNumber: Joi.string().allow('').optional(),
|
|
466
497
|
country: Joi.string().optional(),
|
|
467
498
|
}).optional(),
|
|
468
499
|
bio: Joi.string().allow('').max(200).optional(),
|
|
@@ -501,6 +532,17 @@ const spaceGatewaySchema = Joi.object({
|
|
|
501
532
|
|
|
502
533
|
module.exports = {
|
|
503
534
|
init(server, node, options) {
|
|
535
|
+
const ensureCors = cors(async (req, callback) => {
|
|
536
|
+
const domains = await federatedUtil.getTrustedDomains({ node, req, blocklet: req.blocklet });
|
|
537
|
+
const host = req?.get('host') || req?.headers?.host;
|
|
538
|
+
if (domains.includes(host)) {
|
|
539
|
+
callback(null, { origin: true });
|
|
540
|
+
} else {
|
|
541
|
+
callback(null, { origin: false });
|
|
542
|
+
}
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
// 应用后端自行登录逻辑(SDK 中调用)
|
|
504
546
|
server.post([`${prefix}/login`, `${prefixApi}/login`], verifySig, async (req, res) => {
|
|
505
547
|
try {
|
|
506
548
|
const data = await login(req, node, options);
|
|
@@ -797,6 +839,365 @@ module.exports = {
|
|
|
797
839
|
}
|
|
798
840
|
);
|
|
799
841
|
|
|
842
|
+
// master 帮 member 进行邮件代发
|
|
843
|
+
server.post(
|
|
844
|
+
`${prefixApi}/email/forwardSendCode`,
|
|
845
|
+
ensureBlocklet(),
|
|
846
|
+
checkFederatedCall({ mode: 'memberToMaster', allowStatus: ['approved'] }),
|
|
847
|
+
async (req, res) => {
|
|
848
|
+
const { blocklet, verifyData } = req;
|
|
849
|
+
const teamDid = blocklet.appPid;
|
|
850
|
+
const { subject, code, magicLink, appName, locale } = verifyData;
|
|
851
|
+
|
|
852
|
+
const emailBody = await render(
|
|
853
|
+
VerifyCodeBody({
|
|
854
|
+
code,
|
|
855
|
+
magicLink,
|
|
856
|
+
appName,
|
|
857
|
+
locale,
|
|
858
|
+
})
|
|
859
|
+
);
|
|
860
|
+
const result = await sendEmail(
|
|
861
|
+
subject,
|
|
862
|
+
{
|
|
863
|
+
title: t('emailTitle', locale, { appName }),
|
|
864
|
+
body: emailBody,
|
|
865
|
+
},
|
|
866
|
+
{
|
|
867
|
+
teamDid,
|
|
868
|
+
node,
|
|
869
|
+
locale,
|
|
870
|
+
raw: true,
|
|
871
|
+
launcher: true,
|
|
872
|
+
}
|
|
873
|
+
);
|
|
874
|
+
res.json(result);
|
|
875
|
+
}
|
|
876
|
+
);
|
|
877
|
+
|
|
878
|
+
server.post(`${prefixApi}/email/sendCode`, ensureBlocklet(), ensureCors, async (req, res) => {
|
|
879
|
+
const { locale = 'en' } = req.query;
|
|
880
|
+
const { blocklet } = req;
|
|
881
|
+
const teamDid = blocklet.appPid;
|
|
882
|
+
const { email, useCode = true, useMagicLink = true, sourceAppPid = null } = req.body;
|
|
883
|
+
|
|
884
|
+
const { error, value: subject } = emailSchema.validate(email);
|
|
885
|
+
|
|
886
|
+
if (error) {
|
|
887
|
+
res.status(400).send({ error: t('emailInvalid', locale), code: 'email_invalid' });
|
|
888
|
+
return;
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
if (await node.isSubjectSent({ teamDid, subject })) {
|
|
892
|
+
res.status(400).send({
|
|
893
|
+
error: t('emailAlreadySent', locale),
|
|
894
|
+
code: 'email_already_sent',
|
|
895
|
+
});
|
|
896
|
+
return;
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
const blockletInfo = await req.getBlockletInfo();
|
|
900
|
+
const doc = await node.createVerifyCode({ teamDid, subject, purpose: 'login' });
|
|
901
|
+
const now = Math.floor(Date.now() / 1000);
|
|
902
|
+
const magicLinkToken = JWT.signV2(blockletInfo.wallet.address, blockletInfo.wallet.secretKey, {
|
|
903
|
+
exp: String(now + VERIFY_CODE_TTL),
|
|
904
|
+
data: {
|
|
905
|
+
id: doc.id,
|
|
906
|
+
code: doc.code,
|
|
907
|
+
},
|
|
908
|
+
});
|
|
909
|
+
logger.info('Email login: create verify code', { teamDid, doc });
|
|
910
|
+
|
|
911
|
+
const emailData = {
|
|
912
|
+
appName: blockletInfo.name,
|
|
913
|
+
locale,
|
|
914
|
+
};
|
|
915
|
+
if (useCode) {
|
|
916
|
+
emailData.code = doc.code;
|
|
917
|
+
}
|
|
918
|
+
if (useMagicLink) {
|
|
919
|
+
emailData.magicLink = withQuery(req.get('referer'), {
|
|
920
|
+
magicToken: magicLinkToken,
|
|
921
|
+
sourceAppPid,
|
|
922
|
+
});
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
let result;
|
|
926
|
+
if (sourceAppPid) {
|
|
927
|
+
// 通知 master 去发送邮件
|
|
928
|
+
result = await federatedUtil.sendEmailVerifyCode({
|
|
929
|
+
blocklet,
|
|
930
|
+
blockletInfo,
|
|
931
|
+
request: req,
|
|
932
|
+
params: {
|
|
933
|
+
...emailData,
|
|
934
|
+
subject,
|
|
935
|
+
},
|
|
936
|
+
});
|
|
937
|
+
} else {
|
|
938
|
+
const emailBody = await render(VerifyCodeBody(emailData));
|
|
939
|
+
result = await sendEmail(
|
|
940
|
+
subject,
|
|
941
|
+
{
|
|
942
|
+
title: t('emailTitle', locale, { appName: blockletInfo.name }),
|
|
943
|
+
body: emailBody,
|
|
944
|
+
},
|
|
945
|
+
{
|
|
946
|
+
teamDid,
|
|
947
|
+
node,
|
|
948
|
+
locale,
|
|
949
|
+
raw: true,
|
|
950
|
+
launcher: true,
|
|
951
|
+
}
|
|
952
|
+
);
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
logger.info('Email login: send verify code', { teamDid, result });
|
|
956
|
+
await node.sendVerifyCode({ teamDid, code: doc.code });
|
|
957
|
+
|
|
958
|
+
// 返回 verify code 的 id,用于后续状态的监听
|
|
959
|
+
res.json({ id: doc.id });
|
|
960
|
+
});
|
|
961
|
+
server.get(`${prefixApi}/email/status`, ensureBlocklet(), ensureCors, async (req, res) => {
|
|
962
|
+
const { codeId } = req.query;
|
|
963
|
+
const { blocklet } = req;
|
|
964
|
+
const teamDid = blocklet.appPid;
|
|
965
|
+
const doc = await node.getVerifyCode({ teamDid, id: codeId });
|
|
966
|
+
logger.info('Email login: check verify code status', { teamDid, codeId, doc });
|
|
967
|
+
if (doc) {
|
|
968
|
+
res.json({ verified: doc.verified, id: doc.id });
|
|
969
|
+
} else {
|
|
970
|
+
res.json({ verified: false, id: undefined });
|
|
971
|
+
}
|
|
972
|
+
});
|
|
973
|
+
server.post(`${prefixApi}/email/login`, ensureBlocklet(), ensureCors, async (req, res) => {
|
|
974
|
+
try {
|
|
975
|
+
const { locale = 'en' } = req.query;
|
|
976
|
+
const { blocklet } = req;
|
|
977
|
+
const teamDid = blocklet.appPid;
|
|
978
|
+
let passport = { name: 'Guest', role: 'guest' };
|
|
979
|
+
|
|
980
|
+
const { code, magicToken, sourceAppPid = null } = req.body;
|
|
981
|
+
const blockletInfo = await req.getBlockletInfo();
|
|
982
|
+
let finalCode = code;
|
|
983
|
+
if (!code && magicToken) {
|
|
984
|
+
const valid = await JWT.verify(magicToken, blockletInfo.wallet.publicKey);
|
|
985
|
+
if (!valid) {
|
|
986
|
+
logger.error('Email login: Invalid magic token', { teamDid, magicToken });
|
|
987
|
+
throw new CustomError(401, 'Invalid magic link');
|
|
988
|
+
}
|
|
989
|
+
const decodeData = JWT.decode(magicToken, true);
|
|
990
|
+
if (!decodeData?.data?.code) {
|
|
991
|
+
logger.error('Email login: failed to parse magicToken data', { teamDid, magicToken, decodeData });
|
|
992
|
+
throw new CustomError(400, 'Invalid magic token format');
|
|
993
|
+
}
|
|
994
|
+
finalCode = decodeData?.data?.code;
|
|
995
|
+
}
|
|
996
|
+
const verifyCodeResult = await node.consumeVerifyCode({ teamDid, code: finalCode });
|
|
997
|
+
if (!verifyCodeResult) {
|
|
998
|
+
throw new CustomError(400, 'Invalid verify code');
|
|
999
|
+
}
|
|
1000
|
+
logger.info('Email login: consume verify code', { teamDid, code, result: verifyCodeResult });
|
|
1001
|
+
|
|
1002
|
+
// 保持跟 oauth 账户的格式一致
|
|
1003
|
+
const sub = `email|${verifyCodeResult.subject}`;
|
|
1004
|
+
let userWallet;
|
|
1005
|
+
if (sourceAppPid) {
|
|
1006
|
+
const masterSite = federatedUtil.getFederatedMaster(blocklet);
|
|
1007
|
+
const { permanentWallet } = blockletInfo;
|
|
1008
|
+
const result = await callFederated({
|
|
1009
|
+
action: 'getUser',
|
|
1010
|
+
site: masterSite,
|
|
1011
|
+
permanentWallet,
|
|
1012
|
+
data: {
|
|
1013
|
+
userSub: sub,
|
|
1014
|
+
},
|
|
1015
|
+
});
|
|
1016
|
+
userWallet = result?.wallet;
|
|
1017
|
+
} else {
|
|
1018
|
+
userWallet = fromAppDid(sub, blockletInfo.wallet.secretKey);
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
const lastLoginIp = getRequestIP(req);
|
|
1022
|
+
|
|
1023
|
+
const userDid = userWallet.address;
|
|
1024
|
+
const userPk = userWallet.publicKey;
|
|
1025
|
+
|
|
1026
|
+
let doc;
|
|
1027
|
+
let passportForLog;
|
|
1028
|
+
let profile;
|
|
1029
|
+
let currentUser = await node.getUser({ teamDid, user: { did: userWallet.address } });
|
|
1030
|
+
const connectedAccount = {
|
|
1031
|
+
provider: LOGIN_PROVIDER.EMAIL,
|
|
1032
|
+
did: userDid,
|
|
1033
|
+
pk: userPk,
|
|
1034
|
+
id: sub,
|
|
1035
|
+
};
|
|
1036
|
+
|
|
1037
|
+
if (currentUser) {
|
|
1038
|
+
profile = {
|
|
1039
|
+
fullName: currentUser.fullName,
|
|
1040
|
+
email: currentUser.email,
|
|
1041
|
+
avatar: currentUser.avatar,
|
|
1042
|
+
};
|
|
1043
|
+
const allPassports = currentUser.passports || [];
|
|
1044
|
+
const validPassports = allPassports.filter((item) => item.status === PASSPORT_STATUS.VALID);
|
|
1045
|
+
const lastUsedPassport = last(sortBy(validPassports, 'lastLoginAt'));
|
|
1046
|
+
passportForLog = lastUsedPassport;
|
|
1047
|
+
if (lastUsedPassport) {
|
|
1048
|
+
passport = pick(lastUsedPassport, ['id', 'name', 'role', 'scope']);
|
|
1049
|
+
}
|
|
1050
|
+
doc = await node.loginUser({
|
|
1051
|
+
teamDid,
|
|
1052
|
+
user: {
|
|
1053
|
+
did: currentUser.did,
|
|
1054
|
+
pk: currentUser.pk,
|
|
1055
|
+
locale,
|
|
1056
|
+
lastLoginIp,
|
|
1057
|
+
sourceAppPid,
|
|
1058
|
+
passport: lastUsedPassport,
|
|
1059
|
+
connectedAccount,
|
|
1060
|
+
},
|
|
1061
|
+
});
|
|
1062
|
+
} else {
|
|
1063
|
+
await checkNeedInvite({ req, node, teamDid, locale });
|
|
1064
|
+
currentUser = {
|
|
1065
|
+
did: userWallet.address,
|
|
1066
|
+
pk: userWallet.publicKey,
|
|
1067
|
+
};
|
|
1068
|
+
const nodeInfo = await req.getNodeInfo();
|
|
1069
|
+
const { dataDir } = await getApplicationInfo({ node, nodeInfo, teamDid });
|
|
1070
|
+
let avatar = await getAvatarByEmail(verifyCodeResult.subject);
|
|
1071
|
+
avatar = await extractUserAvatar(avatar, { dataDir });
|
|
1072
|
+
profile = {
|
|
1073
|
+
fullName: verifyCodeResult.subject.split('@')[0],
|
|
1074
|
+
email: verifyCodeResult.subject,
|
|
1075
|
+
avatar,
|
|
1076
|
+
// email 登录默认认为邮箱已验证
|
|
1077
|
+
emailVerified: true,
|
|
1078
|
+
};
|
|
1079
|
+
// 实际创建用户
|
|
1080
|
+
doc = await node.loginUser({
|
|
1081
|
+
teamDid,
|
|
1082
|
+
user: {
|
|
1083
|
+
did: currentUser.did,
|
|
1084
|
+
pk: currentUser.pk,
|
|
1085
|
+
...profile,
|
|
1086
|
+
locale,
|
|
1087
|
+
lastLoginIp,
|
|
1088
|
+
sourceAppPid,
|
|
1089
|
+
connectedAccount,
|
|
1090
|
+
},
|
|
1091
|
+
});
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
await node.createAuditLog(
|
|
1095
|
+
{
|
|
1096
|
+
action: 'login',
|
|
1097
|
+
args: {
|
|
1098
|
+
teamDid,
|
|
1099
|
+
userDid: currentUser.did || userDid,
|
|
1100
|
+
passport: passportForLog,
|
|
1101
|
+
provider: LOGIN_PROVIDER.EMAIL,
|
|
1102
|
+
sourceAppPid,
|
|
1103
|
+
},
|
|
1104
|
+
context: formatContext(Object.assign(req, { user: doc })),
|
|
1105
|
+
result: doc,
|
|
1106
|
+
},
|
|
1107
|
+
node
|
|
1108
|
+
);
|
|
1109
|
+
|
|
1110
|
+
const ua = req.get('user-agent');
|
|
1111
|
+
const visitorId = req.get('x-blocklet-visitor-id');
|
|
1112
|
+
const walletDeviceMessageToken = req.get('wallet-device-message-token');
|
|
1113
|
+
const walletDeviceId = req.get('wallet-device-id');
|
|
1114
|
+
const userSessionDoc = await node.upsertUserSession({
|
|
1115
|
+
teamDid,
|
|
1116
|
+
userDid: currentUser.did,
|
|
1117
|
+
visitorId,
|
|
1118
|
+
appPid: teamDid,
|
|
1119
|
+
passportId: passport?.id,
|
|
1120
|
+
status: 'online',
|
|
1121
|
+
ua: null,
|
|
1122
|
+
lastLoginIp,
|
|
1123
|
+
extra: {
|
|
1124
|
+
walletOS: 'web',
|
|
1125
|
+
walletDeviceMessageToken,
|
|
1126
|
+
walletDeviceId,
|
|
1127
|
+
},
|
|
1128
|
+
});
|
|
1129
|
+
|
|
1130
|
+
if (federatedUtil.shouldSyncFederated(sourceAppPid, blocklet)) {
|
|
1131
|
+
const syncUserData = {
|
|
1132
|
+
did: currentUser.did,
|
|
1133
|
+
pk: currentUser.pk,
|
|
1134
|
+
...profile,
|
|
1135
|
+
avatar: getUserAvatarUrl(blockletInfo.appUrl, profile.avatar),
|
|
1136
|
+
connectedAccount: [connectedAccount],
|
|
1137
|
+
inviter: doc.inviter,
|
|
1138
|
+
};
|
|
1139
|
+
const masterSite = federatedUtil.getFederatedMaster(blocklet);
|
|
1140
|
+
|
|
1141
|
+
node
|
|
1142
|
+
.syncFederated({
|
|
1143
|
+
did: teamDid,
|
|
1144
|
+
data: {
|
|
1145
|
+
users: [
|
|
1146
|
+
{
|
|
1147
|
+
...syncUserData,
|
|
1148
|
+
action: 'connectAccount',
|
|
1149
|
+
sourceAppPid: sourceAppPid || masterSite?.appPid,
|
|
1150
|
+
},
|
|
1151
|
+
],
|
|
1152
|
+
},
|
|
1153
|
+
})
|
|
1154
|
+
.then(() => {
|
|
1155
|
+
node.syncUserSession({
|
|
1156
|
+
teamDid,
|
|
1157
|
+
userDid: userSessionDoc.userDid,
|
|
1158
|
+
visitorId: userSessionDoc.visitorId,
|
|
1159
|
+
passportId: passport?.id,
|
|
1160
|
+
targetAppPid: sourceAppPid,
|
|
1161
|
+
ua,
|
|
1162
|
+
lastLoginIp,
|
|
1163
|
+
extra: {
|
|
1164
|
+
walletOS: 'web',
|
|
1165
|
+
walletDeviceMessageToken,
|
|
1166
|
+
walletDeviceId,
|
|
1167
|
+
},
|
|
1168
|
+
});
|
|
1169
|
+
});
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
const { createSessionToken } = initJwt(node, options);
|
|
1173
|
+
const createToken = createTokenFn(createSessionToken);
|
|
1174
|
+
const sessionConfig = blocklet.settings?.session || {};
|
|
1175
|
+
const { sessionToken, refreshToken } = createToken(
|
|
1176
|
+
currentUser.did,
|
|
1177
|
+
{
|
|
1178
|
+
secret: blockletInfo.secret,
|
|
1179
|
+
passport,
|
|
1180
|
+
role: passport.scope === 'passport' ? passport.role : ROLES.GUEST,
|
|
1181
|
+
fullName: doc.fullName,
|
|
1182
|
+
provider: LOGIN_PROVIDER.EMAIL,
|
|
1183
|
+
walletOS: 'web',
|
|
1184
|
+
emailVerified: !!doc?.emailVerified,
|
|
1185
|
+
phoneVerified: !!doc?.phoneVerified,
|
|
1186
|
+
},
|
|
1187
|
+
{ ...sessionConfig, didConnectVersion: getDidConnectVersion(req) }
|
|
1188
|
+
);
|
|
1189
|
+
|
|
1190
|
+
return res.status(200).json({
|
|
1191
|
+
sessionToken,
|
|
1192
|
+
refreshToken,
|
|
1193
|
+
visitorId: userSessionDoc.visitorId,
|
|
1194
|
+
});
|
|
1195
|
+
} catch (error) {
|
|
1196
|
+
logger.error('Email login failed', { error });
|
|
1197
|
+
return res.status(400).send(error.message);
|
|
1198
|
+
}
|
|
1199
|
+
});
|
|
1200
|
+
|
|
800
1201
|
server.put(`${prefixApi}/notification/webhook`, checkUser, ensureBlocklet(), async (req, res) => {
|
|
801
1202
|
const { value, error } = webhookSetSchema.validate(req.body);
|
|
802
1203
|
if (error) {
|
|
@@ -865,8 +1266,8 @@ module.exports = {
|
|
|
865
1266
|
updateData.email = metadata.email;
|
|
866
1267
|
}
|
|
867
1268
|
|
|
868
|
-
if (metadata.phone
|
|
869
|
-
updateData.phone = metadata.phone
|
|
1269
|
+
if (metadata.phone) {
|
|
1270
|
+
updateData.phone = metadata.phone?.phoneNumber || null;
|
|
870
1271
|
}
|
|
871
1272
|
}
|
|
872
1273
|
|
|
@@ -875,7 +1276,7 @@ module.exports = {
|
|
|
875
1276
|
updateData.address = address;
|
|
876
1277
|
}
|
|
877
1278
|
|
|
878
|
-
const user = await node.
|
|
1279
|
+
const user = await node.updateUserInfoAndSync({
|
|
879
1280
|
teamDid,
|
|
880
1281
|
user: updateData,
|
|
881
1282
|
});
|
|
@@ -937,35 +1338,6 @@ module.exports = {
|
|
|
937
1338
|
}
|
|
938
1339
|
);
|
|
939
1340
|
|
|
940
|
-
// public api
|
|
941
|
-
server.get(`${prefixApi}`, ensureBlocklet(), async (req, res) => {
|
|
942
|
-
const inputSchema = Joi.DID().required();
|
|
943
|
-
const { error, value } = inputSchema.validate(req.query.did);
|
|
944
|
-
if (error) {
|
|
945
|
-
res.status(400).send({ error: error.details[0].message });
|
|
946
|
-
return;
|
|
947
|
-
}
|
|
948
|
-
|
|
949
|
-
const { blocklet } = req;
|
|
950
|
-
const teamDid = blocklet.appPid;
|
|
951
|
-
const user = await node.getUser({ teamDid, user: { did: value } });
|
|
952
|
-
if (!user || !user?.approved) {
|
|
953
|
-
res.status(404).send({ error: 'User not found' });
|
|
954
|
-
return;
|
|
955
|
-
}
|
|
956
|
-
if (user.avatar) {
|
|
957
|
-
user.avatar = getUserAvatarUrl(user.avatar, blocklet);
|
|
958
|
-
}
|
|
959
|
-
let returnFields = ['avatar', 'did', 'fullName', 'sourceAppPid', 'createdAt', 'email', 'phone', 'metadata'];
|
|
960
|
-
|
|
961
|
-
// 如果是自己查询,可以返回 address
|
|
962
|
-
const reqUser = req.user;
|
|
963
|
-
if (reqUser?.did === user.did) {
|
|
964
|
-
returnFields = returnFields.concat(['address']);
|
|
965
|
-
}
|
|
966
|
-
res.json(pick(user, returnFields));
|
|
967
|
-
});
|
|
968
|
-
|
|
969
1341
|
// 支持用户更新 DID Space 信息
|
|
970
1342
|
server.put(`${prefixApi}/updateDidSpace`, checkUser, ensureBlocklet(), async (req, res) => {
|
|
971
1343
|
try {
|
|
@@ -1043,5 +1415,35 @@ module.exports = {
|
|
|
1043
1415
|
res.status(400).send(formatError(err));
|
|
1044
1416
|
}
|
|
1045
1417
|
});
|
|
1418
|
+
|
|
1419
|
+
// public api
|
|
1420
|
+
// 获取用户可以公开的信息
|
|
1421
|
+
server.get(`${prefixApi}`, ensureBlocklet(), async (req, res) => {
|
|
1422
|
+
const inputSchema = Joi.DID().required();
|
|
1423
|
+
const { error, value } = inputSchema.validate(req.query.did);
|
|
1424
|
+
if (error) {
|
|
1425
|
+
res.status(400).send({ error: error.details[0].message });
|
|
1426
|
+
return;
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
const { blocklet } = req;
|
|
1430
|
+
const teamDid = blocklet.appPid;
|
|
1431
|
+
const user = await node.getUser({ teamDid, user: { did: value } });
|
|
1432
|
+
if (!user || !user?.approved) {
|
|
1433
|
+
res.status(404).send({ error: 'User not found' });
|
|
1434
|
+
return;
|
|
1435
|
+
}
|
|
1436
|
+
if (user.avatar) {
|
|
1437
|
+
user.avatar = getRawUserAvatarUrl(user.avatar, blocklet);
|
|
1438
|
+
}
|
|
1439
|
+
let returnFields = ['avatar', 'did', 'fullName', 'sourceAppPid', 'createdAt', 'email', 'phone', 'metadata'];
|
|
1440
|
+
|
|
1441
|
+
// 如果是自己查询,可以返回 address
|
|
1442
|
+
const reqUser = req.user;
|
|
1443
|
+
if (reqUser?.did === user.did) {
|
|
1444
|
+
returnFields = returnFields.concat(['address']);
|
|
1445
|
+
}
|
|
1446
|
+
res.json(pick(user, returnFields));
|
|
1447
|
+
});
|
|
1046
1448
|
},
|
|
1047
1449
|
};
|
|
@@ -26,18 +26,18 @@ module.exports = function createRoutes(node, authenticator, createSessionToken)
|
|
|
26
26
|
|
|
27
27
|
claims: {
|
|
28
28
|
signature: async ({ extraParams, context: { request, baseUrl, didwallet } }) => {
|
|
29
|
-
const { id, locale } = extraParams;
|
|
29
|
+
const { id, locale, inviteId } = extraParams;
|
|
30
30
|
checkWalletVersion({ didwallet, locale });
|
|
31
31
|
const nodeInfo = await node.getNodeInfo();
|
|
32
32
|
const teamDid = request.get('x-blocklet-did');
|
|
33
33
|
|
|
34
|
-
return createIssuePassportRequest({ node, nodeInfo, teamDid, id, locale, baseUrl });
|
|
34
|
+
return createIssuePassportRequest({ node, nodeInfo, teamDid, id, locale, baseUrl, inviteId });
|
|
35
35
|
},
|
|
36
36
|
},
|
|
37
37
|
|
|
38
38
|
onAuth: async ({ userDid, userPk, claims, extraParams, updateSession, req, baseUrl }) => {
|
|
39
39
|
// NOTICE: 和 did-connect 相关的 visitorId 需要从 extraParams 中获取,不能从 headers 中获取
|
|
40
|
-
const { locale, id, visitorId, inviter } = extraParams;
|
|
40
|
+
const { locale, inviteId, id, visitorId, inviter } = extraParams;
|
|
41
41
|
const sourceAppPid = getSourceAppPid(req);
|
|
42
42
|
const nodeInfo = await node.getNodeInfo();
|
|
43
43
|
const blocklet = await req.getBlocklet();
|
|
@@ -64,6 +64,7 @@ module.exports = function createRoutes(node, authenticator, createSessionToken)
|
|
|
64
64
|
statusEndpointBaseUrl,
|
|
65
65
|
updateSession,
|
|
66
66
|
endpoint,
|
|
67
|
+
inviteId,
|
|
67
68
|
});
|
|
68
69
|
|
|
69
70
|
// NOTICE: 找回通行证的前提是用户已经存在,不存在需要更新 sourceAppPid 的情况,所以不会包含 sourceAppPid 字段
|
|
@@ -19,6 +19,7 @@ const getRequestIP = require('@abtnode/util/lib/get-request-ip');
|
|
|
19
19
|
const { getBlockletChainInfo, isInProgress } = require('@blocklet/meta/lib/util');
|
|
20
20
|
const { ROLES, MAIN_CHAIN_ENDPOINT } = require('@abtnode/constant');
|
|
21
21
|
const { LOGIN_PROVIDER } = require('@blocklet/constant');
|
|
22
|
+
const { PASSPORT_SOURCE, PASSPORT_LOG_ACTION, PASSPORT_ISSUE_ACTION } = require('@abtnode/constant');
|
|
22
23
|
|
|
23
24
|
const { createTokenFn, getDidConnectVersion } = require('../../../util');
|
|
24
25
|
const logger = require('../../../libs/logger')('blocklet-service:transfer-blocklet-owner');
|
|
@@ -238,7 +239,7 @@ module.exports = function createRoutes(node, _, createSessionToken) {
|
|
|
238
239
|
|
|
239
240
|
const vc = await createPassportVC(vcParams);
|
|
240
241
|
const role = getRoleFromLocalPassport(get(vc, 'credentialSubject.passport'));
|
|
241
|
-
const passport = createUserPassport(vc, { role });
|
|
242
|
+
const passport = createUserPassport(vc, { role, source: PASSPORT_SOURCE.ISSUE });
|
|
242
243
|
|
|
243
244
|
// NOTICE: owner 必须是 did-wallet 账户,这里不应该去查询 connectedAccount
|
|
244
245
|
const user = await getUser(node, appPid, userDid);
|
|
@@ -305,6 +306,21 @@ module.exports = function createRoutes(node, _, createSessionToken) {
|
|
|
305
306
|
);
|
|
306
307
|
}
|
|
307
308
|
|
|
309
|
+
if (passport) {
|
|
310
|
+
await node.createPassportLog(
|
|
311
|
+
appPid,
|
|
312
|
+
{
|
|
313
|
+
passportId: passport.id,
|
|
314
|
+
action: PASSPORT_LOG_ACTION.ISSUE,
|
|
315
|
+
operatorDid: userDid,
|
|
316
|
+
metadata: {
|
|
317
|
+
action: PASSPORT_ISSUE_ACTION.ISSUE_ON_RECEIVE_TRANSFER_APP_OWNER,
|
|
318
|
+
},
|
|
319
|
+
},
|
|
320
|
+
req
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
|
|
308
324
|
// NOTICE: must NOT use appDid here if update BLOCKLET_APP_SK
|
|
309
325
|
await node.configBlocklet(
|
|
310
326
|
{
|
|
@@ -5,6 +5,7 @@ const { getSourceAppPid } = require('@blocklet/sdk/lib/util/login');
|
|
|
5
5
|
const { fromBase64 } = require('@ocap/util');
|
|
6
6
|
const { LOGIN_PROVIDER } = require('@blocklet/constant');
|
|
7
7
|
|
|
8
|
+
const { PASSPORT_LOG_ACTION } = require('@abtnode/constant');
|
|
8
9
|
const logger = require('../../../libs/logger')();
|
|
9
10
|
const { createTokenFn, getDidConnectVersion } = require('../../../util');
|
|
10
11
|
const { PASSPORT_VC_TYPES } = require('../../../libs/auth/utils');
|
|
@@ -59,11 +60,30 @@ module.exports = function createRoutes(node, authenticator, createSessionToken)
|
|
|
59
60
|
challenge,
|
|
60
61
|
types: PASSPORT_VC_TYPES,
|
|
61
62
|
trustedIssuers: await getTrustedIssuers(blocklet, { sourceAppPid }),
|
|
63
|
+
action: 'verify-destroy',
|
|
62
64
|
});
|
|
65
|
+
|
|
63
66
|
if (!ALLOWED_ROLES.includes(role)) {
|
|
64
67
|
throw new Error(messages.notAllowed[locale]);
|
|
65
68
|
}
|
|
66
69
|
|
|
70
|
+
if (passport) {
|
|
71
|
+
await node.createPassportLog(
|
|
72
|
+
blocklet.appPid,
|
|
73
|
+
{
|
|
74
|
+
passportId: passport.id,
|
|
75
|
+
action: PASSPORT_LOG_ACTION.USED,
|
|
76
|
+
operatorDid: userDid,
|
|
77
|
+
metadata: {
|
|
78
|
+
action: 'verify-destroy',
|
|
79
|
+
ownerDid: userDid,
|
|
80
|
+
userDid: user.did,
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
request
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
67
87
|
const parsed = JSON.parse(fromBase64(payload).toString());
|
|
68
88
|
const session = await node.startSession({ data: { ...parsed, type: 'destroy', operator: userDid } });
|
|
69
89
|
|