fedipod-server 0.21.0 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -4
- package/dist/claims.d.ts +3 -1
- package/dist/claims.js +20 -7
- package/dist/components/context.jsonld +6 -0
- package/dist/handler.d.ts +29 -1
- package/dist/handler.js +89 -4
- package/dist/handler.jsonld +36 -1
- package/lib/client/c2s.mjs +100 -3
- package/lib/client/masto/accounts.mjs +44 -4
- package/lib/client/masto/body.mjs +90 -3
- package/lib/client/masto/index.mjs +5 -1
- package/lib/client/masto/instance.mjs +11 -3
- package/lib/client/masto/media.mjs +3 -57
- package/lib/client/masto/oauth.mjs +66 -9
- package/lib/client/masto/render.mjs +62 -5
- package/lib/client/masto/statuses.mjs +25 -1
- package/lib/client/masto/timelines.mjs +128 -6
- package/lib/connections/tagfeed.mjs +42 -5
- package/lib/core/as2.mjs +36 -5
- package/lib/core/contexts/index.mjs +14 -0
- package/lib/core/contexts/litepub-0.1.json +60 -0
- package/lib/core/contexts/map.json +3 -1
- package/lib/core/contexts/quotes.json +18 -0
- package/lib/core/graphview.mjs +41 -3
- package/lib/core/intake/activities.mjs +136 -2
- package/lib/core/intake/activity.mjs +79 -1
- package/lib/core/intake/group.mjs +37 -3
- package/lib/core/intake/index.mjs +32 -2
- package/lib/core/intake/notes.mjs +60 -3
- package/lib/core/lease.mjs +12 -2
- package/lib/core/profile-page.mjs +104 -0
- package/lib/core/publisher/collections.mjs +6 -1
- package/lib/core/publisher/index.mjs +35 -17
- package/lib/core/publisher/notes.mjs +104 -3
- package/lib/core/shapes/activitystreams.ttl +28 -0
- package/lib/core/shapes/index.mjs +26 -1
- package/lib/core/wire-quotes.mjs +71 -0
- package/lib/core/wire.mjs +59 -103
- package/lib/device/admin/routes/lifecycle.mjs +9 -0
- package/lib/device/admin/static.mjs +4 -1
- package/lib/device/admin/surface.mjs +26 -2
- package/lib/device/certs.mjs +18 -2
- package/lib/device/cli/commands/setup.mjs +6 -2
- package/lib/device/cli/commands/state.mjs +3 -1
- package/lib/device/gateway-move.mjs +58 -0
- package/lib/device/migrate.mjs +3 -1
- package/lib/device/setup.mjs +67 -7
- package/lib/gateway/front-core.mjs +307 -36
- package/lib/gateway/gateway-core.mjs +15 -6
- package/lib/gateway/notices.mjs +84 -0
- package/lib/gateway/quiet.mjs +184 -0
- package/lib/pod/root.mjs +6 -3
- package/lib/pod/state.mjs +8 -7
- package/lib/pod/transport.mjs +5 -1
- package/lib/session/README.md +123 -0
- package/lib/session/demo.html +94 -0
- package/lib/session/fedi-account.mjs +346 -0
- package/lib/session/fedi-login.mjs +123 -0
- package/lib/session/oidc-session.mjs +233 -0
- package/lib/session/package.json +41 -0
- package/package.json +1 -1
- package/run-agent.mjs +28 -0
- package/vendor/gate.cjs +6 -3
- package/web/admin/bar.css +66 -0
- package/web/admin/bar.js +38 -0
- package/web/admin/client/index.html +31 -3
- package/web/admin/client-pick.js +79 -0
- package/web/admin/gateway.js +22 -0
- package/web/admin/index.html +54 -13
- package/web/admin/notices-bar.js +90 -0
- package/web/admin/oauth-signin.mjs +37 -0
- package/web/admin/record.js +13 -1
- package/web/admin/setup/index.html +31 -3
- package/web/admin/tokens.css +15 -3
- package/web/admin/upkeep.js +4 -5
- package/web/app/README.md +14 -10
- package/web/app/admin-facade.mjs +51 -12
- package/web/app/agent.mjs +157 -19
- package/web/app/authorize_interaction.html +56 -0
- package/web/app/authorize_interaction.js +69 -0
- package/web/app/boot.mjs +233 -95
- package/web/app/dist/boot.js +477 -569
- package/web/app/dist/boot.js.map +4 -4
- package/web/app/dist/sw.js +1861 -532
- package/web/app/dist/sw.js.map +4 -4
- package/web/app/gateway-move.mjs +60 -0
- package/web/app/index.html +40 -49
- package/web/app/keys-browser.mjs +12 -15
- package/web/app/keystore.mjs +19 -36
- package/web/app/oidc-session.mjs +5 -188
- package/web/app/signup.mjs +172 -132
- package/web/app/sw-src.mjs +1 -1
- package/web/app/update.js +66 -0
- package/web/front/admin.html +5 -3
- package/web/front/new-account.html +2 -2
- package/web/front/notices.html +69 -0
- package/web/front/notices.js +107 -0
- package/web/front/run.html +15 -8
- package/web/front/run.js +124 -36
- package/web/icons/forum.svg +11 -0
- package/web/icons/gateway.svg +14 -0
- package/web/icons/local.svg +11 -0
- package/web/app/index.html~ +0 -152
- package/web/app/site/_headers +0 -9
- package/web/app/site/_redirects +0 -22
- package/web/app/site/admin/actors.js +0 -145
- package/web/app/site/admin/bar.css +0 -60
- package/web/app/site/admin/bar.js +0 -61
- package/web/app/site/admin/client/client.js +0 -66
- package/web/app/site/admin/client/index.html +0 -43
- package/web/app/site/admin/common.js +0 -23
- package/web/app/site/admin/connections.js +0 -112
- package/web/app/site/admin/gateway.js +0 -111
- package/web/app/site/admin/group.js +0 -258
- package/web/app/site/admin/index.html +0 -564
- package/web/app/site/admin/record.js +0 -378
- package/web/app/site/admin/setup/index.html +0 -215
- package/web/app/site/admin/setup/setup.js +0 -326
- package/web/app/site/admin/tokens.css +0 -56
- package/web/app/site/admin/upkeep.js +0 -178
- package/web/app/site/admin/window.css +0 -55
- package/web/app/site/admin/window.js +0 -136
- package/web/app/site/app/404.html +0 -40
- package/web/app/site/app/_headers +0 -2
- package/web/app/site/app/apple-touch-icon.png +0 -0
- package/web/app/site/app/assets/ICONS-DIPJeU0C.js +0 -2
- package/web/app/site/app/assets/ICONS-DIPJeU0C.js.map +0 -1
- package/web/app/site/app/assets/ScheduledAtField-D-Abfo4J.js +0 -183
- package/web/app/site/app/assets/ScheduledAtField-D-Abfo4J.js.map +0 -1
- package/web/app/site/app/assets/Temml-CCjoacWt.woff2 +0 -0
- package/web/app/site/app/assets/__vite-browser-external-C7iut881.js +0 -2
- package/web/app/site/app/assets/__vite-browser-external-C7iut881.js.map +0 -1
- package/web/app/site/app/assets/boosts-carousel-BiOaNdMT.jpg +0 -0
- package/web/app/site/app/assets/catch-up-O5q5C75Z.png +0 -0
- package/web/app/site/app/assets/chunk-aKtaBQYM.js +0 -1
- package/web/app/site/app/assets/compose-CFEx7wb4.js +0 -2
- package/web/app/site/app/assets/compose-CFEx7wb4.js.map +0 -1
- package/web/app/site/app/assets/compose-ZJN5xSb4.js +0 -35
- package/web/app/site/app/assets/compose-ZJN5xSb4.js.map +0 -1
- package/web/app/site/app/assets/debug.module-YFQgwnIr.js +0 -52
- package/web/app/site/app/assets/debug.module-YFQgwnIr.js.map +0 -1
- package/web/app/site/app/assets/dom-p1YcJ5Rw.js +0 -2
- package/web/app/site/app/assets/dom-p1YcJ5Rw.js.map +0 -1
- package/web/app/site/app/assets/home-mobile-dark@2x-Dc-J8-SM.png +0 -0
- package/web/app/site/app/assets/home-mobile-light@2x-Bd3fod8m.png +0 -0
- package/web/app/site/app/assets/home-tablet-dark@2x-BgcNI8le.png +0 -0
- package/web/app/site/app/assets/home-tablet-light@2x-BW2RV9T6.png +0 -0
- package/web/app/site/app/assets/icons/add-circle-line-WD0mwORD.js +0 -1
- package/web/app/site/app/assets/icons/alert-line-CpFUMf9s.js +0 -1
- package/web/app/site/app/assets/icons/android-2-line-Bzxnrd3S.js +0 -1
- package/web/app/site/app/assets/icons/announcement-line-5nwaJFJm.js +0 -1
- package/web/app/site/app/assets/icons/arrow-down-circle-line-a7Id9-Iv.js +0 -1
- package/web/app/site/app/assets/icons/arrow-down-line-B8uzXzq6.js +0 -1
- package/web/app/site/app/assets/icons/arrow-left-line-CGrGT9C-.js +0 -1
- package/web/app/site/app/assets/icons/arrow-right-line-CE-HERfX.js +0 -1
- package/web/app/site/app/assets/icons/arrow-to-up-line-CvdQjmcy.js +0 -1
- package/web/app/site/app/assets/icons/arrow-up-circle-line-DEparSNF.js +0 -1
- package/web/app/site/app/assets/icons/arrow-up-line-C7yBeugc.js +0 -1
- package/web/app/site/app/assets/icons/arrows-right-line-BtbXmJGP.js +0 -1
- package/web/app/site/app/assets/icons/at-line-CF66Bznk.js +0 -1
- package/web/app/site/app/assets/icons/attachment-line-BY8It-l9.js +0 -1
- package/web/app/site/app/assets/icons/blockquote-line-BS6pU_hm.js +0 -1
- package/web/app/site/app/assets/icons/board-line-C0UWXyA6.js +0 -1
- package/web/app/site/app/assets/icons/bookmark-line-1CtX5JLF.js +0 -1
- package/web/app/site/app/assets/icons/building-5-line-Dd59MzbO.js +0 -1
- package/web/app/site/app/assets/icons/bus-2-line-6_dvA0GG.js +0 -1
- package/web/app/site/app/assets/icons/calendar-day-line-Crpl_fmu.js +0 -1
- package/web/app/site/app/assets/icons/calendar-month-line-vommts2C.js +0 -1
- package/web/app/site/app/assets/icons/calendar-time-add-line-BU_DTlQk.js +0 -1
- package/web/app/site/app/assets/icons/camera-line-Cv69MEei.js +0 -1
- package/web/app/site/app/assets/icons/celebrate-line-B8DvIoi0.js +0 -1
- package/web/app/site/app/assets/icons/chart-bar-line-FK-QOmtF.js +0 -1
- package/web/app/site/app/assets/icons/chart-line-line-BGcJAFWw.js +0 -1
- package/web/app/site/app/assets/icons/chat-3-line-YFK2-hwz.js +0 -1
- package/web/app/site/app/assets/icons/check-circle-line-DsAxFr5-.js +0 -1
- package/web/app/site/app/assets/icons/clipboard-line-BBp364gJ.js +0 -1
- package/web/app/site/app/assets/icons/close-circle-line-CICdiuGz.js +0 -1
- package/web/app/site/app/assets/icons/close-line-DYrFWcjv.js +0 -1
- package/web/app/site/app/assets/icons/cloud-line-tiQAbah8.js +0 -1
- package/web/app/site/app/assets/icons/code-line-fAl8iiV3.js +0 -1
- package/web/app/site/app/assets/icons/comment-2-line-CR3g7WeB.js +0 -1
- package/web/app/site/app/assets/icons/copy-2-line-CLW69yF5.js +0 -1
- package/web/app/site/app/assets/icons/delete-2-line-B8xmbr4n.js +0 -1
- package/web/app/site/app/assets/icons/document-3-line-DBGNhAg2.js +0 -1
- package/web/app/site/app/assets/icons/document-line-C7mb9sb7.js +0 -1
- package/web/app/site/app/assets/icons/down-line-BGqSSThd.js +0 -1
- package/web/app/site/app/assets/icons/edit-4-line-DcgGaEhx.js +0 -1
- package/web/app/site/app/assets/icons/emoji-2-line-D8YgnM-y.js +0 -1
- package/web/app/site/app/assets/icons/exit-line-Dc07BS-x.js +0 -1
- package/web/app/site/app/assets/icons/external-link-line-BvpSkmjO.js +0 -1
- package/web/app/site/app/assets/icons/eye-2-line-CiWXx-f_.js +0 -1
- package/web/app/site/app/assets/icons/eye-close-line-BgTaSJ4P.js +0 -1
- package/web/app/site/app/assets/icons/filter-2-line-B_mR2usy.js +0 -1
- package/web/app/site/app/assets/icons/filter-line-C_nm-3cU.js +0 -1
- package/web/app/site/app/assets/icons/flag-1-line-C2HB05I0.js +0 -1
- package/web/app/site/app/assets/icons/forbid-circle-line-D3LTT9yd.js +0 -1
- package/web/app/site/app/assets/icons/formula-line-DLrt2d6j.js +0 -1
- package/web/app/site/app/assets/icons/grid-line-CgUmEikf.js +0 -1
- package/web/app/site/app/assets/icons/group-line-C5fnX0wU.js +0 -1
- package/web/app/site/app/assets/icons/hand-finger-2-line-BGgxbuL9.js +0 -1
- package/web/app/site/app/assets/icons/hashtag-line-CyTEEXBb.js +0 -1
- package/web/app/site/app/assets/icons/heart-crack-line-BZddbO2C.js +0 -1
- package/web/app/site/app/assets/icons/heart-line-CJ7K47J4.js +0 -1
- package/web/app/site/app/assets/icons/history-2-line-kWWvFzqv.js +0 -1
- package/web/app/site/app/assets/icons/history-line-bGQiwbTH.js +0 -1
- package/web/app/site/app/assets/icons/home-3-line-fRU1zeLG.js +0 -1
- package/web/app/site/app/assets/icons/information-line-iNk6-sOY.js +0 -1
- package/web/app/site/app/assets/icons/keyboard-line-DsCyuQNz.js +0 -1
- package/web/app/site/app/assets/icons/layout-4-line-CZkOX52Z.js +0 -1
- package/web/app/site/app/assets/icons/layout-5-line-B_znt5wO.js +0 -1
- package/web/app/site/app/assets/icons/left-line-eUw0tya4.js +0 -1
- package/web/app/site/app/assets/icons/lightning-line-DaKoDdix.js +0 -1
- package/web/app/site/app/assets/icons/link-2-line-DCqLmMA7.js +0 -1
- package/web/app/site/app/assets/icons/list-check-line-BAxosBh9.js +0 -1
- package/web/app/site/app/assets/icons/lock-line-CPoaXeUW.js +0 -1
- package/web/app/site/app/assets/icons/mail-line-B0P8aa1L.js +0 -1
- package/web/app/site/app/assets/icons/moon-line-CEqNAToP.js +0 -1
- package/web/app/site/app/assets/icons/more-1-fill-Dw2e5xab.js +0 -1
- package/web/app/site/app/assets/icons/more-3-line-BIiVjYTz.js +0 -1
- package/web/app/site/app/assets/icons/notification-line-BZZq2Gtu.js +0 -1
- package/web/app/site/app/assets/icons/pencil-line-CKh8-A1v.js +0 -1
- package/web/app/site/app/assets/icons/photo-album-line-Bnfg2u40.js +0 -1
- package/web/app/site/app/assets/icons/pin-line-DpTolyhs.js +0 -1
- package/web/app/site/app/assets/icons/play-fill-B0-G_8lX.js +0 -1
- package/web/app/site/app/assets/icons/qrcode-2-line-B6SPCaHn.js +0 -1
- package/web/app/site/app/assets/icons/quill-pen-line-B6LUrnCP.js +0 -1
- package/web/app/site/app/assets/icons/quote-left-fill-hQZ1cj_6.js +0 -1
- package/web/app/site/app/assets/icons/radar-line-CKyroTth.js +0 -1
- package/web/app/site/app/assets/icons/react-line-CoIKhhtK.js +0 -1
- package/web/app/site/app/assets/icons/refresh-2-line-D0mTF97S.js +0 -1
- package/web/app/site/app/assets/icons/right-line-B3KWJaqc.js +0 -1
- package/web/app/site/app/assets/icons/rocket-line-4KZl537D.js +0 -1
- package/web/app/site/app/assets/icons/round-fill-C69NTrZ0.js +0 -1
- package/web/app/site/app/assets/icons/round-line-B9k_1uo-.js +0 -1
- package/web/app/site/app/assets/icons/route-line-B28ReHcE.js +0 -1
- package/web/app/site/app/assets/icons/rows-4-line-CrBniagM.js +0 -1
- package/web/app/site/app/assets/icons/scan-line-DSf74YdP.js +0 -1
- package/web/app/site/app/assets/icons/search-2-line-B6geIU1z.js +0 -1
- package/web/app/site/app/assets/icons/settings-3-line-azJwK47w.js +0 -1
- package/web/app/site/app/assets/icons/settings-6-line-C_5zc5YR.js +0 -1
- package/web/app/site/app/assets/icons/share-2-line-D5MxWjrm.js +0 -1
- package/web/app/site/app/assets/icons/share-forward-line-Cp61fZzZ.js +0 -1
- package/web/app/site/app/assets/icons/sparkles-2-line-DYYyXTaW.js +0 -1
- package/web/app/site/app/assets/icons/sparkles-line-C2hY1s6I.js +0 -1
- package/web/app/site/app/assets/icons/time-line-BF3eet1v.js +0 -1
- package/web/app/site/app/assets/icons/transfer-4-line-oisfvTBw.js +0 -1
- package/web/app/site/app/assets/icons/translate-line-BFWol2Ce.js +0 -1
- package/web/app/site/app/assets/icons/unlock-line-X_x5vwv3.js +0 -1
- package/web/app/site/app/assets/icons/upload-3-line-DgzwUJeW.js +0 -1
- package/web/app/site/app/assets/icons/user-4-line-BVSynZCp.js +0 -1
- package/web/app/site/app/assets/icons/user-add-2-line-BlGS5Q8q.js +0 -1
- package/web/app/site/app/assets/icons/user-add-line-BbXdHG4z.js +0 -1
- package/web/app/site/app/assets/icons/user-edit-line-BFAHOV0X.js +0 -1
- package/web/app/site/app/assets/icons/user-follow-line-BXux0wSd.js +0 -1
- package/web/app/site/app/assets/icons/user-star-line-DdYlpHLU.js +0 -1
- package/web/app/site/app/assets/icons/user-warning-line-Cj43j0_E.js +0 -1
- package/web/app/site/app/assets/icons/user-x-line-DIDgNlPH.js +0 -1
- package/web/app/site/app/assets/icons/volume-line-Bs390KQt.js +0 -1
- package/web/app/site/app/assets/icons/volume-mute-line-DzlzIqFZ.js +0 -1
- package/web/app/site/app/assets/icons/walk-line-yNXfB-md.js +0 -1
- package/web/app/site/app/assets/icons/world-2-line-CenkGIwj.js +0 -1
- package/web/app/site/app/assets/icons/zoom-in-line-CaENUphf.js +0 -1
- package/web/app/site/app/assets/icons/zoom-out-line-BDNk0EWo.js +0 -1
- package/web/app/site/app/assets/instances-BNPptnn-.json +0 -333
- package/web/app/site/app/assets/locales/ar-SA-CfheRbCu.js +0 -1
- package/web/app/site/app/assets/locales/ca-ES-DHRW86Tz.js +0 -1
- package/web/app/site/app/assets/locales/cs-CZ-CKoqEuA1.js +0 -1
- package/web/app/site/app/assets/locales/de-DE-ByRPMRFy.js +0 -1
- package/web/app/site/app/assets/locales/eo-UY-CPC7wLCS.js +0 -1
- package/web/app/site/app/assets/locales/es-ES-dJa0Fd9t.js +0 -1
- package/web/app/site/app/assets/locales/eu-ES-C3djlPR2.js +0 -1
- package/web/app/site/app/assets/locales/fa-IR-DTT490Ck.js +0 -1
- package/web/app/site/app/assets/locales/fi-FI-B23MCPuz.js +0 -1
- package/web/app/site/app/assets/locales/fr-FR-BcaQsO5C.js +0 -1
- package/web/app/site/app/assets/locales/gl-ES-BR-CVKBN.js +0 -1
- package/web/app/site/app/assets/locales/he-IL-CfheRbCu.js +0 -1
- package/web/app/site/app/assets/locales/hu-HU-CfheRbCu.js +0 -1
- package/web/app/site/app/assets/locales/it-IT-C1136UFV.js +0 -1
- package/web/app/site/app/assets/locales/ja-JP-CRZmRAhD.js +0 -1
- package/web/app/site/app/assets/locales/kab-Cz8xH_TT.js +0 -1
- package/web/app/site/app/assets/locales/ko-KR-CG9UiY4o.js +0 -1
- package/web/app/site/app/assets/locales/lt-LT-CJcI6sHZ.js +0 -1
- package/web/app/site/app/assets/locales/nb-NO-CfheRbCu.js +0 -1
- package/web/app/site/app/assets/locales/nl-NL-DxN4Yc1t.js +0 -1
- package/web/app/site/app/assets/locales/oc-FR-CfheRbCu.js +0 -1
- package/web/app/site/app/assets/locales/pl-PL-DkwGXf6P.js +0 -1
- package/web/app/site/app/assets/locales/pseudo-LOCALE-CQ4XxkQ0.js +0 -1
- package/web/app/site/app/assets/locales/pt-BR-nGL0HdFS.js +0 -1
- package/web/app/site/app/assets/locales/pt-PT-Cp17aj2z.js +0 -1
- package/web/app/site/app/assets/locales/ru-RU-0Y8cIoaK.js +0 -1
- package/web/app/site/app/assets/locales/th-TH-CfheRbCu.js +0 -1
- package/web/app/site/app/assets/locales/tok-CfheRbCu.js +0 -1
- package/web/app/site/app/assets/locales/tr-TR-CfheRbCu.js +0 -1
- package/web/app/site/app/assets/locales/uk-UA-C5E3dFBf.js +0 -1
- package/web/app/site/app/assets/locales/zh-CN-BHJi7_et.js +0 -1
- package/web/app/site/app/assets/locales/zh-TW-CfheRbCu.js +0 -1
- package/web/app/site/app/assets/main-BdqNbG-a.js +0 -26
- package/web/app/site/app/assets/main-BdqNbG-a.js.map +0 -1
- package/web/app/site/app/assets/mock-home-DGhR__He.js +0 -2
- package/web/app/site/app/assets/mock-home-DGhR__He.js.map +0 -1
- package/web/app/site/app/assets/multi-column-ROck0NVt.jpg +0 -0
- package/web/app/site/app/assets/multi-hashtag-timeline-DH5INVHi.jpg +0 -0
- package/web/app/site/app/assets/nested-comments-thread-hv59kZLc.jpg +0 -0
- package/web/app/site/app/assets/polyfill-force-H8sUkLGp.js +0 -2
- package/web/app/site/app/assets/polyfill-force-H8sUkLGp.js.map +0 -1
- package/web/app/site/app/assets/pwa-viewport-ogKbF51K.js +0 -20
- package/web/app/site/app/assets/pwa-viewport-ogKbF51K.js.map +0 -1
- package/web/app/site/app/assets/style-B1iLPTPR.css +0 -2
- package/web/app/site/app/assets/temml-DuyIQMei.js +0 -97
- package/web/app/site/app/assets/temml-DuyIQMei.js.map +0 -1
- package/web/app/site/app/assets/tinyld.light.node-DtbBhLYC.js +0 -2
- package/web/app/site/app/assets/tinyld.light.node-DtbBhLYC.js.map +0 -1
- package/web/app/site/app/assets/year-in-posts-B2lywqdx.png +0 -0
- package/web/app/site/app/assets/year-in-posts-DJv_star.js +0 -3
- package/web/app/site/app/assets/year-in-posts-DJv_star.js.map +0 -1
- package/web/app/site/app/compose/index.html +0 -20
- package/web/app/site/app/favicon.ico +0 -0
- package/web/app/site/app/index.html +0 -73
- package/web/app/site/app/logo-192.png +0 -0
- package/web/app/site/app/logo-512.png +0 -0
- package/web/app/site/app/logo-badge-72.png +0 -0
- package/web/app/site/app/logo-maskable-512.png +0 -0
- package/web/app/site/app/logo-monochrome-512.png +0 -0
- package/web/app/site/app/logo-monochrome-maskable-512.png +0 -0
- package/web/app/site/app/manifest.webmanifest +0 -1
- package/web/app/site/app/og-image-2.jpg +0 -0
- package/web/app/site/app/og-image.png +0 -0
- package/web/app/site/app/robots.txt +0 -2
- package/web/app/site/app/version.json +0 -1
- package/web/app/site/boot.js +0 -34277
- package/web/app/site/index.html +0 -251
- package/web/app/site/sw.js +0 -71331
package/web/app/dist/boot.js
CHANGED
|
@@ -22024,196 +22024,6 @@ var require_browser_ponyfill = __commonJS({
|
|
|
22024
22024
|
}
|
|
22025
22025
|
});
|
|
22026
22026
|
|
|
22027
|
-
// web/app/pod-auth.mjs
|
|
22028
|
-
var b64u = (buf) => btoa(String.fromCharCode(...new Uint8Array(buf))).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
22029
|
-
var enc = (o) => b64u(new TextEncoder().encode(JSON.stringify(o)));
|
|
22030
|
-
var sha256 = async (str) => crypto.subtle.digest("SHA-256", new TextEncoder().encode(str));
|
|
22031
|
-
async function accountFetch(url, { method = "GET", body, token } = {}) {
|
|
22032
|
-
const headers = { accept: "application/json" };
|
|
22033
|
-
if (body !== void 0) headers["content-type"] = "application/json";
|
|
22034
|
-
if (token) headers.authorization = `CSS-Account-Token ${token}`;
|
|
22035
|
-
const res = await fetch(url, { method, headers, body: body === void 0 ? void 0 : JSON.stringify(body) });
|
|
22036
|
-
let json = null;
|
|
22037
|
-
try {
|
|
22038
|
-
json = await res.json();
|
|
22039
|
-
} catch {
|
|
22040
|
-
}
|
|
22041
|
-
return { status: res.status, json };
|
|
22042
|
-
}
|
|
22043
|
-
async function createAccountWithPod({ issuer, email, password, podName }) {
|
|
22044
|
-
const origin = issuer.replace(/\/+$/, "");
|
|
22045
|
-
const accountRoot = `${origin}/.account/`;
|
|
22046
|
-
let token;
|
|
22047
|
-
const login = await accountFetch(`${accountRoot}login/password/`, { method: "POST", body: { email, password } });
|
|
22048
|
-
if (login.status < 400 && login.json?.authorization) {
|
|
22049
|
-
token = login.json.authorization;
|
|
22050
|
-
} else {
|
|
22051
|
-
const create = await accountFetch(`${accountRoot}account/`, { method: "POST" });
|
|
22052
|
-
if (!create.json?.authorization) {
|
|
22053
|
-
throw new Error(`could not create an account at ${origin} (HTTP ${create.status}) \u2014 is sign-up open there? ${create.json?.message || ""}`);
|
|
22054
|
-
}
|
|
22055
|
-
token = create.json.authorization;
|
|
22056
|
-
const pwCreate = (await accountFetch(accountRoot, { token })).json?.controls?.password?.create;
|
|
22057
|
-
if (!pwCreate) throw new Error("this server is not a CSS v7 account API (no password control)");
|
|
22058
|
-
const pw = await accountFetch(pwCreate, { method: "POST", token, body: { email, password } });
|
|
22059
|
-
if (pw.status >= 400) throw new Error(`could not set the password (HTTP ${pw.status}): ${pw.json?.message || ""}`);
|
|
22060
|
-
}
|
|
22061
|
-
const podCreate = (await accountFetch(accountRoot, { token })).json?.controls?.account?.pod;
|
|
22062
|
-
if (!podCreate) throw new Error("this server does not offer pod creation through its account API");
|
|
22063
|
-
const findOwn = async () => {
|
|
22064
|
-
const pods = (await accountFetch(podCreate, { token })).json?.pods || {};
|
|
22065
|
-
return Object.entries(pods).find(([url]) => {
|
|
22066
|
-
try {
|
|
22067
|
-
const u = new URL(url);
|
|
22068
|
-
return u.hostname === podName || u.hostname.startsWith(`${podName}.`) || u.pathname.split("/").filter(Boolean).includes(podName);
|
|
22069
|
-
} catch {
|
|
22070
|
-
return false;
|
|
22071
|
-
}
|
|
22072
|
-
}) || null;
|
|
22073
|
-
};
|
|
22074
|
-
const owned = Object.keys((await accountFetch(podCreate, { token })).json?.pods || {});
|
|
22075
|
-
if (owned.length && !await findOwn()) {
|
|
22076
|
-
throw new Error(`${email} already has a pod on ${new URL(origin).host} \u2014 one account, one pod. A second identity needs its own account. Nothing was created.`);
|
|
22077
|
-
}
|
|
22078
|
-
const made = await accountFetch(podCreate, { method: "POST", token, body: { name: podName } });
|
|
22079
|
-
let pod = made.json?.pod || made.json?.podBaseUrl || null;
|
|
22080
|
-
let webId = made.json?.webId || null;
|
|
22081
|
-
if (made.status >= 400 || !pod) {
|
|
22082
|
-
const own = await findOwn();
|
|
22083
|
-
if (own) {
|
|
22084
|
-
pod = own[0];
|
|
22085
|
-
webId = webId || own[1]?.webId || null;
|
|
22086
|
-
} else if (made.status >= 400) throw new Error(`pod creation failed (HTTP ${made.status}): ${made.json?.message || ""}`);
|
|
22087
|
-
}
|
|
22088
|
-
if (!pod) throw new Error("the server did not report a pod URL");
|
|
22089
|
-
return { pod: pod.endsWith("/") ? pod : pod + "/", webId, accountToken: token };
|
|
22090
|
-
}
|
|
22091
|
-
async function mintCredential({ issuer, email, password, webId, podUrl, accountToken, name = "fedipod" }) {
|
|
22092
|
-
const origin = issuer.replace(/\/+$/, "");
|
|
22093
|
-
const accountRoot = `${origin}/.account/`;
|
|
22094
|
-
let token = accountToken;
|
|
22095
|
-
if (!token) {
|
|
22096
|
-
const tryLogin = async (id) => {
|
|
22097
|
-
const r = await accountFetch(`${accountRoot}login/password/`, { method: "POST", body: { email: id, password } });
|
|
22098
|
-
return r.status < 400 && r.json?.authorization ? r.json.authorization : null;
|
|
22099
|
-
};
|
|
22100
|
-
const ownsPod = async (tok) => {
|
|
22101
|
-
if (!podUrl) return true;
|
|
22102
|
-
try {
|
|
22103
|
-
const ctl = (await accountFetch(accountRoot, { token: tok })).json?.controls?.account?.webId;
|
|
22104
|
-
const links = ctl ? (await accountFetch(ctl, { token: tok })).json?.webIdLinks : null;
|
|
22105
|
-
return Object.keys(links || {}).some((w) => new URL(w).origin === new URL(podUrl).origin);
|
|
22106
|
-
} catch {
|
|
22107
|
-
return false;
|
|
22108
|
-
}
|
|
22109
|
-
};
|
|
22110
|
-
token = await tryLogin(email);
|
|
22111
|
-
let sub = null;
|
|
22112
|
-
try {
|
|
22113
|
-
sub = podUrl ? new URL(podUrl).host.split(".")[0] : null;
|
|
22114
|
-
} catch {
|
|
22115
|
-
}
|
|
22116
|
-
if (sub && sub !== email && (!token || !await ownsPod(token))) {
|
|
22117
|
-
const alt = await tryLogin(sub);
|
|
22118
|
-
if (alt) token = alt;
|
|
22119
|
-
}
|
|
22120
|
-
if (!token) throw new Error("account login failed \u2014 check the email or username and the password");
|
|
22121
|
-
}
|
|
22122
|
-
const controls = (await accountFetch(accountRoot, { token })).json?.controls;
|
|
22123
|
-
const ccUrl = controls?.account?.clientCredentials;
|
|
22124
|
-
if (!ccUrl) throw new Error("this server is not a CSS account API (no clientCredentials control)");
|
|
22125
|
-
let wid = webId;
|
|
22126
|
-
if (!wid) {
|
|
22127
|
-
const linkCtl = controls?.account?.webId;
|
|
22128
|
-
const links = linkCtl ? (await accountFetch(linkCtl, { token })).json?.webIdLinks : null;
|
|
22129
|
-
const all = links ? Object.keys(links) : [];
|
|
22130
|
-
if (!all.length) throw new Error("no WebID is linked to this account");
|
|
22131
|
-
if (podUrl) {
|
|
22132
|
-
let origins = [];
|
|
22133
|
-
try {
|
|
22134
|
-
origins = all.filter((w) => new URL(w).origin === new URL(podUrl).origin);
|
|
22135
|
-
} catch {
|
|
22136
|
-
}
|
|
22137
|
-
if (!origins.length) {
|
|
22138
|
-
throw new Error(`the account you signed in with does not own ${new URL(podUrl).origin}. It is linked to ${all.length} WebID${all.length > 1 ? "s" : ""} \u2014 on ${all.map((w) => new URL(w).host).join(", ")} \u2014 none of them this pod. Sign in with the account that owns this pod, or let FediPod make you a new one.`);
|
|
22139
|
-
}
|
|
22140
|
-
wid = origins[0];
|
|
22141
|
-
} else {
|
|
22142
|
-
wid = all[0];
|
|
22143
|
-
}
|
|
22144
|
-
}
|
|
22145
|
-
const made = await accountFetch(ccUrl, { method: "POST", token, body: { name, webId: wid } });
|
|
22146
|
-
if (made.status >= 400 || !made.json?.secret) throw new Error(`mint failed (HTTP ${made.status}): ${made.json?.message || ""}`);
|
|
22147
|
-
const tokenEndpoint = await discoverTokenEndpoint(origin);
|
|
22148
|
-
return {
|
|
22149
|
-
clientId: made.json.id,
|
|
22150
|
-
secret: made.json.secret,
|
|
22151
|
-
webId: wid,
|
|
22152
|
-
tokenEndpoint,
|
|
22153
|
-
resource: made.json.resource,
|
|
22154
|
-
issuerOrigin: origin,
|
|
22155
|
-
// Carried so sign-up can hand it straight back to revokeCredential. It is a
|
|
22156
|
-
// session token for the ACCOUNT — never stored, never written to the pod,
|
|
22157
|
-
// and gone with the tab.
|
|
22158
|
-
accountToken: token
|
|
22159
|
-
};
|
|
22160
|
-
}
|
|
22161
|
-
async function revokeCredential({ resource, accountToken }) {
|
|
22162
|
-
if (!resource || !accountToken) return false;
|
|
22163
|
-
try {
|
|
22164
|
-
const r = await accountFetch(resource, { method: "DELETE", token: accountToken });
|
|
22165
|
-
return r.status < 400;
|
|
22166
|
-
} catch {
|
|
22167
|
-
return false;
|
|
22168
|
-
}
|
|
22169
|
-
}
|
|
22170
|
-
async function discoverTokenEndpoint(issuer) {
|
|
22171
|
-
const origin = issuer.replace(/\/+$/, "");
|
|
22172
|
-
try {
|
|
22173
|
-
const res = await fetch(`${origin}/.well-known/openid-configuration`, { headers: { accept: "application/json" } });
|
|
22174
|
-
if (res.ok) {
|
|
22175
|
-
const doc = await res.json();
|
|
22176
|
-
if (doc.token_endpoint) return doc.token_endpoint;
|
|
22177
|
-
}
|
|
22178
|
-
} catch {
|
|
22179
|
-
}
|
|
22180
|
-
return `${origin}/.oidc/token`;
|
|
22181
|
-
}
|
|
22182
|
-
async function makeDpopSession({ clientId, secret, tokenEndpoint }) {
|
|
22183
|
-
const proofKey = await crypto.subtle.generateKey({ name: "ECDSA", namedCurve: "P-256" }, false, ["sign"]);
|
|
22184
|
-
const jwk = await crypto.subtle.exportKey("jwk", proofKey.publicKey);
|
|
22185
|
-
const publicJwk2 = { kty: jwk.kty, crv: jwk.crv, x: jwk.x, y: jwk.y };
|
|
22186
|
-
const proof = async (htm, htu, ath) => {
|
|
22187
|
-
const header = { typ: "dpop+jwt", alg: "ES256", jwk: publicJwk2 };
|
|
22188
|
-
const payload = { htm, htu: htu.split("#")[0], jti: crypto.randomUUID(), iat: Math.floor(Date.now() / 1e3), ...ath ? { ath } : {} };
|
|
22189
|
-
const data = `${enc(header)}.${enc(payload)}`;
|
|
22190
|
-
const sig = await crypto.subtle.sign({ name: "ECDSA", hash: "SHA-256" }, proofKey.privateKey, new TextEncoder().encode(data));
|
|
22191
|
-
return `${data}.${b64u(sig)}`;
|
|
22192
|
-
};
|
|
22193
|
-
let token = null;
|
|
22194
|
-
let expiresAt = 0;
|
|
22195
|
-
const refresh = async () => {
|
|
22196
|
-
const basic = btoa(`${encodeURIComponent(clientId)}:${encodeURIComponent(secret)}`);
|
|
22197
|
-
const res = await fetch(tokenEndpoint, {
|
|
22198
|
-
method: "POST",
|
|
22199
|
-
headers: { authorization: `Basic ${basic}`, "content-type": "application/x-www-form-urlencoded", dpop: await proof("POST", tokenEndpoint) },
|
|
22200
|
-
body: "grant_type=client_credentials&scope=webid"
|
|
22201
|
-
});
|
|
22202
|
-
const tok = await res.json().catch(() => ({}));
|
|
22203
|
-
if (!tok.access_token) throw new Error(`token request failed (HTTP ${res.status}): ${tok.error || ""}`);
|
|
22204
|
-
token = tok.access_token;
|
|
22205
|
-
expiresAt = Date.now() + Math.max(30, (tok.expires_in || 300) - 30) * 1e3;
|
|
22206
|
-
return token;
|
|
22207
|
-
};
|
|
22208
|
-
const authFetch = async (url, init = {}) => {
|
|
22209
|
-
if (!token || Date.now() > expiresAt) await refresh();
|
|
22210
|
-
const ath = b64u(await sha256(token));
|
|
22211
|
-
const headers = { ...init.headers || {}, authorization: `DPoP ${token}`, dpop: await proof(init.method || "GET", url, ath) };
|
|
22212
|
-
return fetch(url, { ...init, headers });
|
|
22213
|
-
};
|
|
22214
|
-
return { fetch: authFetch, refresh };
|
|
22215
|
-
}
|
|
22216
|
-
|
|
22217
22027
|
// web/app/keystore.mjs
|
|
22218
22028
|
var PEM = (der, label) => {
|
|
22219
22029
|
const b64 = btoa(String.fromCharCode(...new Uint8Array(der)));
|
|
@@ -22245,7 +22055,6 @@ async function generateKeys() {
|
|
|
22245
22055
|
}
|
|
22246
22056
|
return rec;
|
|
22247
22057
|
}
|
|
22248
|
-
var PBKDF2_ITERATIONS = 31e4;
|
|
22249
22058
|
async function deriveAesKey(password, salt, iterations) {
|
|
22250
22059
|
const base = await crypto.subtle.importKey("raw", new TextEncoder().encode(password), "PBKDF2", false, ["deriveKey"]);
|
|
22251
22060
|
return crypto.subtle.deriveKey(
|
|
@@ -22256,26 +22065,10 @@ async function deriveAesKey(password, salt, iterations) {
|
|
|
22256
22065
|
["encrypt", "decrypt"]
|
|
22257
22066
|
);
|
|
22258
22067
|
}
|
|
22259
|
-
var toB64 = (buf) => btoa(String.fromCharCode(...new Uint8Array(buf)));
|
|
22260
22068
|
var fromB64 = (s) => Uint8Array.from(atob(s), (c) => c.charCodeAt(0));
|
|
22261
22069
|
function isKeyEnvelope(doc) {
|
|
22262
22070
|
return !!doc && doc.v === 1 && typeof doc.ct === "string" && typeof doc.salt === "string";
|
|
22263
22071
|
}
|
|
22264
|
-
async function wrapKeys(keysRecord, password) {
|
|
22265
|
-
const salt = crypto.getRandomValues(new Uint8Array(16));
|
|
22266
|
-
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
22267
|
-
const aes = await deriveAesKey(password, salt, PBKDF2_ITERATIONS);
|
|
22268
|
-
const plaintext = new TextEncoder().encode(JSON.stringify(keysRecord));
|
|
22269
|
-
const ct = await crypto.subtle.encrypt({ name: "AES-GCM", iv }, aes, plaintext);
|
|
22270
|
-
return {
|
|
22271
|
-
v: 1,
|
|
22272
|
-
kdf: "PBKDF2-SHA256",
|
|
22273
|
-
iterations: PBKDF2_ITERATIONS,
|
|
22274
|
-
salt: toB64(salt),
|
|
22275
|
-
iv: toB64(iv),
|
|
22276
|
-
ct: toB64(ct)
|
|
22277
|
-
};
|
|
22278
|
-
}
|
|
22279
22072
|
async function unwrapKeys(envelope, password) {
|
|
22280
22073
|
if (!envelope || envelope.v !== 1) throw new Error("not a key envelope");
|
|
22281
22074
|
const aes = await deriveAesKey(password, fromB64(envelope.salt), envelope.iterations);
|
|
@@ -33000,7 +32793,7 @@ var PodTransport = class {
|
|
|
33000
32793
|
// leaves the private trees world-readable. $rdf.sym() also throws on an
|
|
33001
32794
|
// illegal IRI, so a pod URL with something odd in it fails here rather than
|
|
33002
32795
|
// silently producing a document that means something else.
|
|
33003
|
-
aclDoc(targetUrl, publicModes, { appendAgents = [], aclUrl = null } = {}) {
|
|
32796
|
+
aclDoc(targetUrl, publicModes, { appendAgents = [], readAgents = [], aclUrl = null } = {}) {
|
|
33004
32797
|
const url = aclUrl || targetUrl + ".acl";
|
|
33005
32798
|
const doc = namedNode2(url);
|
|
33006
32799
|
const target = namedNode2(targetUrl);
|
|
@@ -33016,6 +32809,7 @@ var PodTransport = class {
|
|
|
33016
32809
|
authorize(namedNode2(url + "#public"), ACL("agentClass"), FOAF("Agent"), publicModes);
|
|
33017
32810
|
}
|
|
33018
32811
|
appendAgents.forEach((webId, i) => authorize(namedNode2(url + `#gw${i}`), ACL("agent"), namedNode2(webId), ["Append"]));
|
|
32812
|
+
readAgents.forEach((webId, i) => authorize(namedNode2(url + `#r${i}`), ACL("agent"), namedNode2(webId), ["Read"]));
|
|
33019
32813
|
authorize(
|
|
33020
32814
|
namedNode2(url + "#owner"),
|
|
33021
32815
|
ACL("agent"),
|
|
@@ -33206,44 +33000,18 @@ var BrowserRemotePod = class extends PodTransport {
|
|
|
33206
33000
|
};
|
|
33207
33001
|
|
|
33208
33002
|
// lib/pod/state.mjs
|
|
33209
|
-
var
|
|
33003
|
+
var readKeys = (pod, urls) => pod.getJson(urls.state + "keys.json");
|
|
33210
33004
|
var readConfig = (pod, urls) => pod.getJson(urls.state + "config.json");
|
|
33211
|
-
var
|
|
33005
|
+
var writeKeys = (pod, urls, keys) => pod.putJson(urls.state + "keys.json", keys, "application/json");
|
|
33212
33006
|
var writeConfig = (pod, urls, config) => pod.putJson(urls.state + "config.json", config, "application/json");
|
|
33213
|
-
async function provisionKey(pod, { stateUrl, keysUrl,
|
|
33007
|
+
async function provisionKey(pod, { stateUrl, keysUrl, keys }) {
|
|
33214
33008
|
await pod.setAcl(stateUrl, []);
|
|
33215
|
-
await pod.putJson(keysUrl,
|
|
33009
|
+
await pod.putJson(keysUrl, keys, "application/json");
|
|
33216
33010
|
}
|
|
33217
33011
|
|
|
33218
33012
|
// lib/pod/root.mjs
|
|
33219
33013
|
var OWNER_LOOKUP_MS = 5e3;
|
|
33220
33014
|
var PUBLIC_DOC_MAX_BYTES = 1024 * 1024;
|
|
33221
|
-
async function podLayout(fetchImpl, providerOrigin, { timeoutMs = OWNER_LOOKUP_MS } = {}) {
|
|
33222
|
-
let origin;
|
|
33223
|
-
try {
|
|
33224
|
-
origin = new URL(providerOrigin).origin;
|
|
33225
|
-
} catch {
|
|
33226
|
-
return null;
|
|
33227
|
-
}
|
|
33228
|
-
let res;
|
|
33229
|
-
try {
|
|
33230
|
-
res = await fetchImpl(
|
|
33231
|
-
`${origin}/.well-known/solid`,
|
|
33232
|
-
{ headers: { accept: "text/turtle" }, signal: AbortSignal.timeout(timeoutMs) }
|
|
33233
|
-
);
|
|
33234
|
-
} catch {
|
|
33235
|
-
return null;
|
|
33236
|
-
}
|
|
33237
|
-
if (res.status === 501) return "host";
|
|
33238
|
-
if (res.status !== 200) return null;
|
|
33239
|
-
let body = "";
|
|
33240
|
-
try {
|
|
33241
|
-
body = await readCapped(res, 64 * 1024);
|
|
33242
|
-
} catch {
|
|
33243
|
-
return null;
|
|
33244
|
-
}
|
|
33245
|
-
return /ns\/pim\/space#Storage|pim:Storage/u.test(body) ? "path" : null;
|
|
33246
|
-
}
|
|
33247
33015
|
async function resourceExists(fetchImpl, url, { timeoutMs = OWNER_LOOKUP_MS } = {}) {
|
|
33248
33016
|
try {
|
|
33249
33017
|
const res = await fetchImpl(
|
|
@@ -33256,6 +33024,15 @@ async function resourceExists(fetchImpl, url, { timeoutMs = OWNER_LOOKUP_MS } =
|
|
|
33256
33024
|
}
|
|
33257
33025
|
}
|
|
33258
33026
|
|
|
33027
|
+
// lib/pod/urls.mjs
|
|
33028
|
+
function podBaseOfWebId(webId) {
|
|
33029
|
+
const u = new URL(webId);
|
|
33030
|
+
u.hash = "";
|
|
33031
|
+
u.search = "";
|
|
33032
|
+
const dir = u.pathname.replace(/profile\/card$/u, "").replace(/[^/]*$/u, "");
|
|
33033
|
+
return `${u.origin}${dir.endsWith("/") ? dir : dir + "/"}`;
|
|
33034
|
+
}
|
|
33035
|
+
|
|
33259
33036
|
// web/app/idb-kv.mjs
|
|
33260
33037
|
var DB = "fedipod-accounts";
|
|
33261
33038
|
function open() {
|
|
@@ -33314,7 +33091,7 @@ function handleProblem(handle) {
|
|
|
33314
33091
|
return null;
|
|
33315
33092
|
}
|
|
33316
33093
|
var PROGRESS = /* @__PURE__ */ new Map();
|
|
33317
|
-
var progressKey = (a) => [
|
|
33094
|
+
var progressKey = (webId, a) => [webId, a.handle, a.shape || "pod"].join("|");
|
|
33318
33095
|
async function assertFrontNameFree(frontOrigin, handle) {
|
|
33319
33096
|
const res = await fetch(
|
|
33320
33097
|
`${frontOrigin.replace(/\/$/, "")}/api/handle?handle=${encodeURIComponent(handle)}`,
|
|
@@ -33324,18 +33101,18 @@ async function assertFrontNameFree(frontOrigin, handle) {
|
|
|
33324
33101
|
if (!d) throw new Error(`${new URL(frontOrigin).host} did not answer whether @${handle} is free`);
|
|
33325
33102
|
if (!d.available) throw new Error(d.reason || `the name @${handle}@${new URL(frontOrigin).host} is taken \u2014 choose another handle`);
|
|
33326
33103
|
}
|
|
33327
|
-
async function signUp(answers, { onStep = () => {
|
|
33104
|
+
async function signUp(answers, { session, onStep = () => {
|
|
33328
33105
|
}, frontOrigin = null } = {}) {
|
|
33329
|
-
const {
|
|
33106
|
+
const { handle } = answers;
|
|
33330
33107
|
const bad = handleProblem(handle);
|
|
33331
33108
|
if (bad) throw new Error(bad);
|
|
33332
|
-
if (!
|
|
33333
|
-
if (!password) throw new Error("a password is required");
|
|
33334
|
-
if (mode === "existing" && !answers.pod) throw new Error("a pod address is required");
|
|
33109
|
+
if (!session?.webId || typeof session.fetch !== "function") throw new Error("sign in at your pod first");
|
|
33335
33110
|
const wantsFront = answers.shape === "front";
|
|
33336
33111
|
if (wantsFront && !frontOrigin) throw new Error("an address at the gateway needs a gateway, and this page has none");
|
|
33337
33112
|
if (wantsFront) await assertFrontNameFree(frontOrigin, handle);
|
|
33338
|
-
const
|
|
33113
|
+
const webId = session.webId;
|
|
33114
|
+
const pod = podBaseOfWebId(webId);
|
|
33115
|
+
const key = progressKey(webId, answers);
|
|
33339
33116
|
const prog = PROGRESS.get(key) || {};
|
|
33340
33117
|
PROGRESS.set(key, prog);
|
|
33341
33118
|
const step = (key2) => ({
|
|
@@ -33343,30 +33120,15 @@ async function signUp(answers, { onStep = () => {
|
|
|
33343
33120
|
ok: (note) => onStep(key2, "ok", note),
|
|
33344
33121
|
skip: (note) => onStep(key2, "skipped", note)
|
|
33345
33122
|
});
|
|
33346
|
-
|
|
33347
|
-
const acct = step("account");
|
|
33123
|
+
const podStep = step("pod");
|
|
33348
33124
|
if (!prog.pod) {
|
|
33349
|
-
|
|
33350
|
-
|
|
33351
|
-
|
|
33352
|
-
|
|
33353
|
-
prog.webId = made.webId;
|
|
33354
|
-
accountToken = made.accountToken;
|
|
33355
|
-
acct.ok(made.pod);
|
|
33356
|
-
} else {
|
|
33357
|
-
const brought = answers.pod.endsWith("/") ? answers.pod : answers.pod + "/";
|
|
33358
|
-
acct.running("checking your pod");
|
|
33359
|
-
const head = await fetch(brought, { method: "HEAD" }).catch(() => null);
|
|
33360
|
-
if (!head || head.status >= 400) throw new Error(`the pod at ${brought} did not answer (HTTP ${head?.status || "no response"})`);
|
|
33361
|
-
if (await resourceExists(fetch, actorUrlFor(brought))) throw new Error("The pod already hosts a FediPod account. If you want a second account, put it on a different pod.");
|
|
33362
|
-
prog.pod = brought;
|
|
33363
|
-
acct.skip("using the pod you brought");
|
|
33364
|
-
}
|
|
33125
|
+
podStep.running("checking your pod");
|
|
33126
|
+
if (await resourceExists(session.fetch, actorUrlFor(pod))) throw new Error("The pod already hosts a FediPod account. If you want a second account, put it on a different pod.");
|
|
33127
|
+
prog.pod = pod;
|
|
33128
|
+
podStep.ok(pod);
|
|
33365
33129
|
} else {
|
|
33366
|
-
|
|
33130
|
+
podStep.ok(prog.pod);
|
|
33367
33131
|
}
|
|
33368
|
-
const pod = prog.pod;
|
|
33369
|
-
const webId = prog.webId || null;
|
|
33370
33132
|
const pathPod = new URL(pod).pathname !== "/";
|
|
33371
33133
|
const fronted = pathPod || wantsFront;
|
|
33372
33134
|
if (fronted && !frontOrigin) {
|
|
@@ -33375,20 +33137,6 @@ async function signUp(answers, { onStep = () => {
|
|
|
33375
33137
|
if (pathPod && !wantsFront) await assertFrontNameFree(frontOrigin, handle);
|
|
33376
33138
|
const actorUrl = actorUrlFor(pod);
|
|
33377
33139
|
const frontActor = fronted ? `${frontOrigin.replace(/\/$/, "")}/u/${handle}/ap/actor` : null;
|
|
33378
|
-
const cred = step("credential");
|
|
33379
|
-
let credential;
|
|
33380
|
-
if (!prog.credential) {
|
|
33381
|
-
cred.running("minting a credential for this browser");
|
|
33382
|
-
credential = await mintCredential({ issuer, email, password, webId, podUrl: pod, accountToken });
|
|
33383
|
-
credential.remotePod = pod;
|
|
33384
|
-
credential.root = AP_ROOT;
|
|
33385
|
-
prog.credential = credential;
|
|
33386
|
-
cred.ok();
|
|
33387
|
-
} else {
|
|
33388
|
-
credential = prog.credential;
|
|
33389
|
-
cred.ok();
|
|
33390
|
-
}
|
|
33391
|
-
const session = await makeDpopSession(credential);
|
|
33392
33140
|
let gateway = answers.gateway || prog.gateway || null;
|
|
33393
33141
|
if (frontOrigin && !gateway) {
|
|
33394
33142
|
const gw = step("gateway");
|
|
@@ -33413,22 +33161,22 @@ async function signUp(answers, { onStep = () => {
|
|
|
33413
33161
|
} else if (frontOrigin && gateway) {
|
|
33414
33162
|
step("gateway").ok();
|
|
33415
33163
|
}
|
|
33164
|
+
const remote = new BrowserRemotePod(session, { webId, role: "signup", log: () => {
|
|
33165
|
+
} });
|
|
33416
33166
|
const keysStep = step("keys");
|
|
33417
33167
|
let keys;
|
|
33418
33168
|
if (!prog.keysStored) {
|
|
33419
|
-
keysStep.running("making your signing key and
|
|
33169
|
+
keysStep.running("making your signing key and storing it on your pod");
|
|
33420
33170
|
keys = await generateKeys();
|
|
33421
33171
|
keys.mintedFor = gateway?.frontActor || actorUrl;
|
|
33422
|
-
const remote = new BrowserRemotePod(session, { webId: credential.webId, role: "signup", log: () => {
|
|
33423
|
-
} });
|
|
33424
33172
|
try {
|
|
33425
33173
|
await provisionKey(remote, {
|
|
33426
33174
|
stateUrl: `${pod}${AP_ROOT}ap-state/`,
|
|
33427
33175
|
keysUrl: keysDocFor(pod),
|
|
33428
|
-
|
|
33176
|
+
keys
|
|
33429
33177
|
});
|
|
33430
33178
|
} catch (e) {
|
|
33431
|
-
throw new Error(`could not store the signing key on the pod (${e.message}).
|
|
33179
|
+
throw new Error(`could not store the signing key on the pod (${e.message}). You are signed in as ${webId} \u2014 that WebID must own ${pod} and its ${AP_ROOT} must be writable by it.`);
|
|
33432
33180
|
}
|
|
33433
33181
|
await cacheOpenedKeys(actorUrl, keys);
|
|
33434
33182
|
prog.keys = keys;
|
|
@@ -33443,40 +33191,108 @@ async function signUp(answers, { onStep = () => {
|
|
|
33443
33191
|
root: AP_ROOT,
|
|
33444
33192
|
handle,
|
|
33445
33193
|
name: handle,
|
|
33446
|
-
issuer:
|
|
33194
|
+
issuer: String(session.issuer || "").replace(/\/+$/, ""),
|
|
33447
33195
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
33448
33196
|
...gateway ? { gateway } : {}
|
|
33449
33197
|
};
|
|
33450
|
-
const cfgRemote = new BrowserRemotePod(session, { webId: credential.webId, role: "signup", log: () => {
|
|
33451
|
-
} });
|
|
33452
33198
|
try {
|
|
33453
|
-
await writeConfig(
|
|
33199
|
+
await writeConfig(remote, { state: `${pod}${AP_ROOT}ap-state/` }, config);
|
|
33454
33200
|
} catch (e) {
|
|
33455
33201
|
throw new Error(`could not store the config on the pod (${e.message})`);
|
|
33456
33202
|
}
|
|
33457
|
-
const revoked = await revokeCredential({
|
|
33458
|
-
resource: credential.resource,
|
|
33459
|
-
accountToken: credential.accountToken
|
|
33460
|
-
});
|
|
33461
|
-
delete credential.accountToken;
|
|
33462
|
-
if (!revoked && credential.resource) {
|
|
33463
|
-
onStep("credential", "ok", "this browser is ready (the setup credential could not be revoked automatically \u2014 you can remove it from your pod's account page)");
|
|
33464
|
-
}
|
|
33465
33203
|
PROGRESS.delete(key);
|
|
33466
33204
|
const host = gateway?.frontActor ? new URL(gateway.frontActor).host : new URL(pod).host;
|
|
33467
33205
|
return {
|
|
33468
|
-
credential,
|
|
33469
33206
|
config,
|
|
33470
33207
|
actorUrl,
|
|
33208
|
+
pod,
|
|
33471
33209
|
address: `@${handle}@${host}`,
|
|
33472
33210
|
// The opened keys, for booting the agent in THIS browser session right away.
|
|
33473
|
-
// The durable copy on the pod is wrapped under the account password; this
|
|
33474
|
-
// browser also holds an opened one in IndexedDB (above), which is what the
|
|
33475
|
-
// worker reads. A fresh browser asks for the password once and makes its own.
|
|
33476
33211
|
keys,
|
|
33477
33212
|
keysPublic: { rsa: keys.rsa.publicPem, ed25519: keys.ed25519?.publicPem || null }
|
|
33478
33213
|
};
|
|
33479
33214
|
}
|
|
33215
|
+
async function readAccount(session) {
|
|
33216
|
+
const pod = podBaseOfWebId(session.webId);
|
|
33217
|
+
const remote = new BrowserRemotePod(session, { webId: session.webId, role: "signup", log: () => {
|
|
33218
|
+
} });
|
|
33219
|
+
const config = await readConfig(remote, { state: `${pod}${AP_ROOT}ap-state/` }).catch(() => null);
|
|
33220
|
+
if (!config) return null;
|
|
33221
|
+
const frontActor = config.gateway?.frontActor || null;
|
|
33222
|
+
let frontHost = null;
|
|
33223
|
+
try {
|
|
33224
|
+
frontHost = frontActor ? new URL(frontActor).host : null;
|
|
33225
|
+
} catch {
|
|
33226
|
+
frontHost = null;
|
|
33227
|
+
}
|
|
33228
|
+
let podHost = "";
|
|
33229
|
+
try {
|
|
33230
|
+
podHost = new URL(config.remotePod || pod).host;
|
|
33231
|
+
} catch {
|
|
33232
|
+
podHost = "";
|
|
33233
|
+
}
|
|
33234
|
+
return { pod, config, frontActor, frontHost, address: `@${config.handle}@${frontHost || podHost}` };
|
|
33235
|
+
}
|
|
33236
|
+
async function moveIn(answers, { session, onStep = () => {
|
|
33237
|
+
}, frontOrigin = null } = {}) {
|
|
33238
|
+
const { handle } = answers;
|
|
33239
|
+
const bad = handleProblem(handle);
|
|
33240
|
+
if (bad) throw new Error(bad);
|
|
33241
|
+
if (!session?.webId || typeof session.fetch !== "function") throw new Error("sign in at your pod first");
|
|
33242
|
+
if (!frontOrigin) throw new Error("a move needs a gateway to move to, and this page has none");
|
|
33243
|
+
const origin = frontOrigin.replace(/\/$/, "");
|
|
33244
|
+
const step = (key) => ({ running: (note) => onStep(key, "running", note), ok: (note) => onStep(key, "ok", note) });
|
|
33245
|
+
const podStep = step("pod");
|
|
33246
|
+
podStep.running("reading your account on your pod");
|
|
33247
|
+
const here = await readAccount(session);
|
|
33248
|
+
if (!here?.frontActor) throw new Error("this pod holds no account at another gateway to move here");
|
|
33249
|
+
if (here.frontHost === new URL(origin).host) throw new Error("this account already lives at this gateway");
|
|
33250
|
+
const { pod, config: old } = here;
|
|
33251
|
+
const remote = new BrowserRemotePod(session, { webId: session.webId, role: "signup", log: () => {
|
|
33252
|
+
} });
|
|
33253
|
+
const urls = { state: `${pod}${AP_ROOT}ap-state/` };
|
|
33254
|
+
const keys = await readKeys(remote, urls);
|
|
33255
|
+
if (!keys?.rsa) throw new Error("could not read this account's signing key on the pod");
|
|
33256
|
+
if (isKeyEnvelope(keys)) throw new Error(`this account's key is still under a password from an earlier version \u2014 sign in once at ${here.frontHost} first, then come back`);
|
|
33257
|
+
podStep.ok(here.address);
|
|
33258
|
+
const gw = step("gateway");
|
|
33259
|
+
gw.running(`taking your address at ${new URL(origin).host}`);
|
|
33260
|
+
await assertFrontNameFree(origin, handle);
|
|
33261
|
+
const res = await session.fetch(`${origin}/api/attach`, {
|
|
33262
|
+
method: "POST",
|
|
33263
|
+
headers: { "content-type": "application/json" },
|
|
33264
|
+
body: JSON.stringify({ handle, podHome: `${pod}${AP_ROOT}`, actorUrl: actorUrlFor(pod), kind: old.kind || "person", fronted: true })
|
|
33265
|
+
});
|
|
33266
|
+
const d = await res.json().catch(() => ({}));
|
|
33267
|
+
if (res.status !== 201 || !d.hmacSecret) throw new Error(`could not attach to this gateway (HTTP ${res.status}): ${d.error || ""}`);
|
|
33268
|
+
const gateway = {
|
|
33269
|
+
url: `${origin}/u/${handle}/ap/inbox/`,
|
|
33270
|
+
frontActor: String(d.frontActor || `${origin}/u/${handle}/ap/actor`),
|
|
33271
|
+
hmacSecret: d.hmacSecret,
|
|
33272
|
+
mode: "trust"
|
|
33273
|
+
};
|
|
33274
|
+
gw.ok();
|
|
33275
|
+
const keysStep = step("keys");
|
|
33276
|
+
keysStep.running("moving your key and account record to the new address");
|
|
33277
|
+
const aliases = [.../* @__PURE__ */ new Set([...old.aliases || [], here.frontActor])];
|
|
33278
|
+
const config = {
|
|
33279
|
+
...old,
|
|
33280
|
+
handle,
|
|
33281
|
+
gateway,
|
|
33282
|
+
aliases,
|
|
33283
|
+
movedFrom: { actor: here.frontActor, gateway: new URL(here.frontActor).origin, handle: old.handle, at: (/* @__PURE__ */ new Date()).toISOString() }
|
|
33284
|
+
};
|
|
33285
|
+
keys.mintedFor = gateway.frontActor;
|
|
33286
|
+
try {
|
|
33287
|
+
await writeKeys(remote, urls, keys);
|
|
33288
|
+
await writeConfig(remote, urls, config);
|
|
33289
|
+
} catch (e) {
|
|
33290
|
+
throw new Error(`could not update your account on the pod (${e.message})`);
|
|
33291
|
+
}
|
|
33292
|
+
await cacheOpenedKeys(actorUrlFor(pod), keys);
|
|
33293
|
+
keysStep.ok();
|
|
33294
|
+
return { config, address: `@${handle}@${new URL(origin).host}`, movedFrom: config.movedFrom };
|
|
33295
|
+
}
|
|
33480
33296
|
|
|
33481
33297
|
// lib/pod/actor.mjs
|
|
33482
33298
|
var ACCEPT_AP = 'application/activity+json, application/ld+json; profile="https://www.w3.org/ns/activitystreams"';
|
|
@@ -33491,54 +33307,12 @@ async function readIssuer(actorUrl, fetchImpl = fetch) {
|
|
|
33491
33307
|
}
|
|
33492
33308
|
}
|
|
33493
33309
|
|
|
33494
|
-
// lib/
|
|
33495
|
-
function podBaseOfWebId(webId) {
|
|
33496
|
-
const u = new URL(webId);
|
|
33497
|
-
u.hash = "";
|
|
33498
|
-
u.search = "";
|
|
33499
|
-
const dir = u.pathname.replace(/profile\/card$/u, "").replace(/[^/]*$/u, "");
|
|
33500
|
-
return `${u.origin}${dir.endsWith("/") ? dir : dir + "/"}`;
|
|
33501
|
-
}
|
|
33502
|
-
|
|
33503
|
-
// web/app/oidc-session.mjs
|
|
33504
|
-
var DB2 = "fedipod-oidc";
|
|
33310
|
+
// lib/session/oidc-session.mjs
|
|
33505
33311
|
var STORE = "session";
|
|
33506
|
-
var
|
|
33507
|
-
var
|
|
33508
|
-
var
|
|
33509
|
-
var rand = (n = 32) =>
|
|
33510
|
-
function idb() {
|
|
33511
|
-
return new Promise((res, rej) => {
|
|
33512
|
-
const r = indexedDB.open(DB2, 1);
|
|
33513
|
-
r.onupgradeneeded = () => r.result.createObjectStore(STORE);
|
|
33514
|
-
r.onsuccess = () => res(r.result);
|
|
33515
|
-
r.onerror = () => rej(r.error);
|
|
33516
|
-
});
|
|
33517
|
-
}
|
|
33518
|
-
async function idbGet(key) {
|
|
33519
|
-
const db = await idb();
|
|
33520
|
-
return new Promise((res, rej) => {
|
|
33521
|
-
const t = db.transaction(STORE).objectStore(STORE).get(key);
|
|
33522
|
-
t.onsuccess = () => res(t.result);
|
|
33523
|
-
t.onerror = () => rej(t.error);
|
|
33524
|
-
});
|
|
33525
|
-
}
|
|
33526
|
-
async function idbPut(key, val) {
|
|
33527
|
-
const db = await idb();
|
|
33528
|
-
return new Promise((res, rej) => {
|
|
33529
|
-
const t = db.transaction(STORE, "readwrite").objectStore(STORE).put(val, key);
|
|
33530
|
-
t.onsuccess = () => res();
|
|
33531
|
-
t.onerror = () => rej(t.error);
|
|
33532
|
-
});
|
|
33533
|
-
}
|
|
33534
|
-
async function idbDel(key) {
|
|
33535
|
-
const db = await idb();
|
|
33536
|
-
return new Promise((res, rej) => {
|
|
33537
|
-
const t = db.transaction(STORE, "readwrite").objectStore(STORE).delete(key);
|
|
33538
|
-
t.onsuccess = () => res();
|
|
33539
|
-
t.onerror = () => rej(t.error);
|
|
33540
|
-
});
|
|
33541
|
-
}
|
|
33312
|
+
var b64u = (buf) => btoa(String.fromCharCode(...new Uint8Array(buf))).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
33313
|
+
var enc = (o) => b64u(new TextEncoder().encode(JSON.stringify(o)));
|
|
33314
|
+
var sha256 = (s) => crypto.subtle.digest("SHA-256", new TextEncoder().encode(s));
|
|
33315
|
+
var rand = (n = 32) => b64u(crypto.getRandomValues(new Uint8Array(n)));
|
|
33542
33316
|
async function dpopKey() {
|
|
33543
33317
|
return crypto.subtle.generateKey({ name: "ECDSA", namedCurve: "P-256" }, false, ["sign"]);
|
|
33544
33318
|
}
|
|
@@ -33549,9 +33323,9 @@ async function publicJwk(pair) {
|
|
|
33549
33323
|
async function dpopProof(pair, jwk, htm, htu, ath) {
|
|
33550
33324
|
const header = { typ: "dpop+jwt", alg: "ES256", jwk };
|
|
33551
33325
|
const payload = { htm, htu: htu.split("#")[0], jti: crypto.randomUUID(), iat: Math.floor(Date.now() / 1e3), ...ath ? { ath } : {} };
|
|
33552
|
-
const data = `${
|
|
33326
|
+
const data = `${enc(header)}.${enc(payload)}`;
|
|
33553
33327
|
const sig = await crypto.subtle.sign({ name: "ECDSA", hash: "SHA-256" }, pair.privateKey, new TextEncoder().encode(data));
|
|
33554
|
-
return `${data}.${
|
|
33328
|
+
return `${data}.${b64u(sig)}`;
|
|
33555
33329
|
}
|
|
33556
33330
|
var jwtPayload = (jwt) => {
|
|
33557
33331
|
try {
|
|
@@ -33582,129 +33356,188 @@ async function registerClient(cfg, redirectUri, clientName) {
|
|
|
33582
33356
|
if (!res.ok) throw new Error(`client registration failed (HTTP ${res.status})`);
|
|
33583
33357
|
return (await res.json()).client_id;
|
|
33584
33358
|
}
|
|
33585
|
-
|
|
33586
|
-
|
|
33587
|
-
|
|
33588
|
-
|
|
33589
|
-
|
|
33590
|
-
|
|
33591
|
-
|
|
33592
|
-
|
|
33593
|
-
issuer,
|
|
33594
|
-
client_id,
|
|
33595
|
-
redirectUri,
|
|
33596
|
-
verifier,
|
|
33597
|
-
state,
|
|
33598
|
-
pair,
|
|
33599
|
-
tokenEndpoint: cfg.token_endpoint,
|
|
33600
|
-
authorizationEndpoint: cfg.authorization_endpoint,
|
|
33601
|
-
revocationEndpoint: cfg.revocation_endpoint || null
|
|
33602
|
-
});
|
|
33603
|
-
const url = new URL(cfg.authorization_endpoint);
|
|
33604
|
-
for (const [k, v] of Object.entries({
|
|
33605
|
-
client_id,
|
|
33606
|
-
redirect_uri: redirectUri,
|
|
33607
|
-
response_type: "code",
|
|
33608
|
-
scope: "openid webid offline_access",
|
|
33609
|
-
code_challenge: challenge,
|
|
33610
|
-
code_challenge_method: "S256",
|
|
33611
|
-
state,
|
|
33612
|
-
prompt: "consent"
|
|
33613
|
-
})) url.searchParams.set(k, v);
|
|
33614
|
-
return { authorizationUrl: url.href };
|
|
33615
|
-
}
|
|
33616
|
-
async function completeLogin({ currentUrl }) {
|
|
33617
|
-
const u = new URL(currentUrl);
|
|
33618
|
-
const code = u.searchParams.get("code");
|
|
33619
|
-
const state = u.searchParams.get("state");
|
|
33620
|
-
if (!code) return null;
|
|
33621
|
-
const p = await idbGet("pending");
|
|
33622
|
-
if (!p || p.state !== state) throw new Error("login state mismatch");
|
|
33623
|
-
const jwk = await publicJwk(p.pair);
|
|
33624
|
-
const res = await fetch(p.tokenEndpoint, {
|
|
33625
|
-
method: "POST",
|
|
33626
|
-
headers: { "content-type": "application/x-www-form-urlencoded", dpop: await dpopProof(p.pair, jwk, "POST", p.tokenEndpoint) },
|
|
33627
|
-
body: new URLSearchParams({ grant_type: "authorization_code", code, redirect_uri: p.redirectUri, client_id: p.client_id, code_verifier: p.verifier })
|
|
33628
|
-
});
|
|
33629
|
-
const tok = await res.json().catch(() => ({}));
|
|
33630
|
-
if (!res.ok || !tok.access_token) throw new Error(`token exchange failed (HTTP ${res.status}): ${tok.error || ""}`);
|
|
33631
|
-
const webId = jwtPayload(tok.access_token).webid || jwtPayload(tok.id_token || "").webid || null;
|
|
33632
|
-
const session = {
|
|
33633
|
-
issuer: p.issuer,
|
|
33634
|
-
client_id: p.client_id,
|
|
33635
|
-
tokenEndpoint: p.tokenEndpoint,
|
|
33636
|
-
pair: p.pair,
|
|
33637
|
-
// Where to hand the refresh token back at sign-out. Kept on the session
|
|
33638
|
-
// because discovery is a network round trip and sign-out should not need
|
|
33639
|
-
// one — see signOut().
|
|
33640
|
-
revocationEndpoint: p.revocationEndpoint || null,
|
|
33641
|
-
refreshToken: tok.refresh_token || null,
|
|
33642
|
-
accessToken: tok.access_token,
|
|
33643
|
-
expiresAt: Date.now() + Math.max(30, tok.expires_in || 300) * 1e3,
|
|
33644
|
-
webId
|
|
33645
|
-
};
|
|
33646
|
-
await idbPut("session", session);
|
|
33647
|
-
await idbDel("pending");
|
|
33648
|
-
return sessionHandle(session);
|
|
33649
|
-
}
|
|
33650
|
-
async function getSession() {
|
|
33651
|
-
const s = await idbGet("session");
|
|
33652
|
-
return s ? sessionHandle(s) : null;
|
|
33653
|
-
}
|
|
33654
|
-
async function signOut() {
|
|
33655
|
-
const s = await idbGet("session").catch(() => null);
|
|
33656
|
-
if (s?.revocationEndpoint && s.refreshToken) {
|
|
33657
|
-
try {
|
|
33658
|
-
await fetch(s.revocationEndpoint, {
|
|
33659
|
-
method: "POST",
|
|
33660
|
-
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
33661
|
-
body: new URLSearchParams({
|
|
33662
|
-
token: s.refreshToken,
|
|
33663
|
-
token_type_hint: "refresh_token",
|
|
33664
|
-
client_id: s.client_id
|
|
33665
|
-
})
|
|
33666
|
-
});
|
|
33667
|
-
} catch {
|
|
33668
|
-
}
|
|
33359
|
+
function solidOidcSession({ dbName = "solid-oidc-session", clientName = "Solid app" } = {}) {
|
|
33360
|
+
function idb() {
|
|
33361
|
+
return new Promise((res, rej) => {
|
|
33362
|
+
const r = indexedDB.open(dbName, 1);
|
|
33363
|
+
r.onupgradeneeded = () => r.result.createObjectStore(STORE);
|
|
33364
|
+
r.onsuccess = () => res(r.result);
|
|
33365
|
+
r.onerror = () => rej(r.error);
|
|
33366
|
+
});
|
|
33669
33367
|
}
|
|
33670
|
-
|
|
33671
|
-
|
|
33672
|
-
|
|
33673
|
-
|
|
33674
|
-
|
|
33675
|
-
|
|
33676
|
-
|
|
33677
|
-
|
|
33678
|
-
|
|
33679
|
-
const
|
|
33368
|
+
async function idbGet(key) {
|
|
33369
|
+
const db = await idb();
|
|
33370
|
+
return new Promise((res, rej) => {
|
|
33371
|
+
const t = db.transaction(STORE).objectStore(STORE).get(key);
|
|
33372
|
+
t.onsuccess = () => res(t.result);
|
|
33373
|
+
t.onerror = () => rej(t.error);
|
|
33374
|
+
});
|
|
33375
|
+
}
|
|
33376
|
+
async function idbPut(key, val) {
|
|
33377
|
+
const db = await idb();
|
|
33378
|
+
return new Promise((res, rej) => {
|
|
33379
|
+
const t = db.transaction(STORE, "readwrite").objectStore(STORE).put(val, key);
|
|
33380
|
+
t.onsuccess = () => res();
|
|
33381
|
+
t.onerror = () => rej(t.error);
|
|
33382
|
+
});
|
|
33383
|
+
}
|
|
33384
|
+
async function idbDel(key) {
|
|
33385
|
+
const db = await idb();
|
|
33386
|
+
return new Promise((res, rej) => {
|
|
33387
|
+
const t = db.transaction(STORE, "readwrite").objectStore(STORE).delete(key);
|
|
33388
|
+
t.onsuccess = () => res();
|
|
33389
|
+
t.onerror = () => rej(t.error);
|
|
33390
|
+
});
|
|
33391
|
+
}
|
|
33392
|
+
async function beginLogin2({ issuer, redirectUri, clientName: name = clientName, returnTo = null }) {
|
|
33393
|
+
const cfg = await discover(issuer);
|
|
33394
|
+
const client_id = await registerClient(cfg, redirectUri, name);
|
|
33395
|
+
const pair = await dpopKey();
|
|
33396
|
+
const verifier = rand(48);
|
|
33397
|
+
const challenge = b64u(await sha256(verifier));
|
|
33398
|
+
const state = rand(16);
|
|
33399
|
+
await idbPut("pending", {
|
|
33400
|
+
issuer,
|
|
33401
|
+
client_id,
|
|
33402
|
+
redirectUri,
|
|
33403
|
+
verifier,
|
|
33404
|
+
state,
|
|
33405
|
+
pair,
|
|
33406
|
+
returnTo,
|
|
33407
|
+
tokenEndpoint: cfg.token_endpoint,
|
|
33408
|
+
authorizationEndpoint: cfg.authorization_endpoint,
|
|
33409
|
+
revocationEndpoint: cfg.revocation_endpoint || null
|
|
33410
|
+
});
|
|
33411
|
+
const url = new URL(cfg.authorization_endpoint);
|
|
33412
|
+
for (const [k, v] of Object.entries({
|
|
33413
|
+
client_id,
|
|
33414
|
+
redirect_uri: redirectUri,
|
|
33415
|
+
response_type: "code",
|
|
33416
|
+
scope: "openid webid offline_access",
|
|
33417
|
+
code_challenge: challenge,
|
|
33418
|
+
code_challenge_method: "S256",
|
|
33419
|
+
state,
|
|
33420
|
+
prompt: "consent"
|
|
33421
|
+
})) url.searchParams.set(k, v);
|
|
33422
|
+
return { authorizationUrl: url.href };
|
|
33423
|
+
}
|
|
33424
|
+
async function completeLogin2({ currentUrl }) {
|
|
33425
|
+
const u = new URL(currentUrl);
|
|
33426
|
+
const code = u.searchParams.get("code");
|
|
33427
|
+
const state = u.searchParams.get("state");
|
|
33428
|
+
if (!code) return null;
|
|
33429
|
+
const p = await idbGet("pending");
|
|
33430
|
+
if (!p || p.state !== state) throw new Error("login state mismatch");
|
|
33431
|
+
const jwk = await publicJwk(p.pair);
|
|
33432
|
+
const res = await fetch(p.tokenEndpoint, {
|
|
33680
33433
|
method: "POST",
|
|
33681
|
-
headers: { "content-type": "application/x-www-form-urlencoded", dpop: await dpopProof(
|
|
33682
|
-
body: new URLSearchParams({ grant_type: "
|
|
33434
|
+
headers: { "content-type": "application/x-www-form-urlencoded", dpop: await dpopProof(p.pair, jwk, "POST", p.tokenEndpoint) },
|
|
33435
|
+
body: new URLSearchParams({ grant_type: "authorization_code", code, redirect_uri: p.redirectUri, client_id: p.client_id, code_verifier: p.verifier })
|
|
33683
33436
|
});
|
|
33684
33437
|
const tok = await res.json().catch(() => ({}));
|
|
33685
|
-
if (!res.ok || !tok.access_token) {
|
|
33686
|
-
|
|
33687
|
-
|
|
33688
|
-
|
|
33689
|
-
|
|
33690
|
-
|
|
33691
|
-
|
|
33692
|
-
|
|
33693
|
-
|
|
33694
|
-
|
|
33695
|
-
|
|
33696
|
-
|
|
33697
|
-
|
|
33698
|
-
|
|
33699
|
-
|
|
33700
|
-
|
|
33701
|
-
|
|
33702
|
-
|
|
33703
|
-
return
|
|
33704
|
-
}
|
|
33705
|
-
|
|
33438
|
+
if (!res.ok || !tok.access_token) throw new Error(`token exchange failed (HTTP ${res.status}): ${tok.error || ""}`);
|
|
33439
|
+
const webId = jwtPayload(tok.access_token).webid || jwtPayload(tok.id_token || "").webid || null;
|
|
33440
|
+
const session = {
|
|
33441
|
+
issuer: p.issuer,
|
|
33442
|
+
client_id: p.client_id,
|
|
33443
|
+
tokenEndpoint: p.tokenEndpoint,
|
|
33444
|
+
pair: p.pair,
|
|
33445
|
+
// Where to hand the refresh token back at sign-out. Kept on the session
|
|
33446
|
+
// because discovery is a network round trip and sign-out should not need
|
|
33447
|
+
// one — see signOut().
|
|
33448
|
+
revocationEndpoint: p.revocationEndpoint || null,
|
|
33449
|
+
refreshToken: tok.refresh_token || null,
|
|
33450
|
+
accessToken: tok.access_token,
|
|
33451
|
+
expiresAt: Date.now() + Math.max(30, tok.expires_in || 300) * 1e3,
|
|
33452
|
+
webId
|
|
33453
|
+
};
|
|
33454
|
+
await idbPut("session", session);
|
|
33455
|
+
await idbDel("pending");
|
|
33456
|
+
return Object.assign(sessionHandle(session), p.returnTo ? { returnTo: p.returnTo } : {});
|
|
33457
|
+
}
|
|
33458
|
+
async function getSession2() {
|
|
33459
|
+
const s = await idbGet("session");
|
|
33460
|
+
return s ? sessionHandle(s) : null;
|
|
33461
|
+
}
|
|
33462
|
+
async function signOut2() {
|
|
33463
|
+
const s = await idbGet("session").catch(() => null);
|
|
33464
|
+
if (s?.revocationEndpoint && s.refreshToken) {
|
|
33465
|
+
try {
|
|
33466
|
+
await fetch(s.revocationEndpoint, {
|
|
33467
|
+
method: "POST",
|
|
33468
|
+
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
33469
|
+
body: new URLSearchParams({
|
|
33470
|
+
token: s.refreshToken,
|
|
33471
|
+
token_type_hint: "refresh_token",
|
|
33472
|
+
client_id: s.client_id
|
|
33473
|
+
})
|
|
33474
|
+
});
|
|
33475
|
+
} catch {
|
|
33476
|
+
}
|
|
33477
|
+
}
|
|
33478
|
+
await idbDel("session");
|
|
33479
|
+
await idbDel("pending");
|
|
33480
|
+
}
|
|
33481
|
+
function sessionHandle(s) {
|
|
33482
|
+
let { accessToken, expiresAt, refreshToken } = s;
|
|
33483
|
+
const jwkP = publicJwk(s.pair);
|
|
33484
|
+
const refresh = async () => {
|
|
33485
|
+
if (!refreshToken) throw new Error("session expired and there is no refresh token \u2014 sign in again");
|
|
33486
|
+
const jwk = await jwkP;
|
|
33487
|
+
let res;
|
|
33488
|
+
try {
|
|
33489
|
+
res = await fetch(s.tokenEndpoint, {
|
|
33490
|
+
method: "POST",
|
|
33491
|
+
headers: { "content-type": "application/x-www-form-urlencoded", dpop: await dpopProof(s.pair, jwk, "POST", s.tokenEndpoint) },
|
|
33492
|
+
body: new URLSearchParams({ grant_type: "refresh_token", refresh_token: refreshToken, client_id: s.client_id, scope: "openid webid offline_access" })
|
|
33493
|
+
});
|
|
33494
|
+
} catch (e) {
|
|
33495
|
+
throw new Error(`${e.message} \u2014 renewing your sign-in at ${new URL(s.tokenEndpoint).host}`);
|
|
33496
|
+
}
|
|
33497
|
+
const tok = await res.json().catch(() => ({}));
|
|
33498
|
+
if (res.status === 429) {
|
|
33499
|
+
throw new Error(`${new URL(s.tokenEndpoint).host} is asking us to slow down \u2014 your sign-in could not be renewed just now`);
|
|
33500
|
+
}
|
|
33501
|
+
if (!res.ok || !tok.access_token) {
|
|
33502
|
+
await idbDel("session");
|
|
33503
|
+
throw new Error("refresh failed \u2014 sign in again");
|
|
33504
|
+
}
|
|
33505
|
+
accessToken = tok.access_token;
|
|
33506
|
+
expiresAt = Date.now() + Math.max(30, tok.expires_in || 300) * 1e3;
|
|
33507
|
+
if (tok.refresh_token) refreshToken = tok.refresh_token;
|
|
33508
|
+
const now = await idbGet("session");
|
|
33509
|
+
if (now && now.webId !== s.webId) {
|
|
33510
|
+
throw new Error("signed in as somebody else while this session was refreshing");
|
|
33511
|
+
}
|
|
33512
|
+
await idbPut("session", { ...s, accessToken, expiresAt, refreshToken });
|
|
33513
|
+
};
|
|
33514
|
+
const send = async (url, init) => {
|
|
33515
|
+
const jwk = await jwkP;
|
|
33516
|
+
const ath = b64u(await sha256(accessToken));
|
|
33517
|
+
const headers = { ...init.headers || {}, authorization: `DPoP ${accessToken}`, dpop: await dpopProof(s.pair, jwk, init.method || "GET", url, ath) };
|
|
33518
|
+
return fetch(url, { ...init, headers });
|
|
33519
|
+
};
|
|
33520
|
+
const authFetch = async (url, init = {}) => {
|
|
33521
|
+
if (Date.now() > expiresAt - 3e4) await refresh();
|
|
33522
|
+
const res = await send(url, init);
|
|
33523
|
+
if (res.status === 401 && refreshToken) {
|
|
33524
|
+
try {
|
|
33525
|
+
await refresh();
|
|
33526
|
+
} catch {
|
|
33527
|
+
return res;
|
|
33528
|
+
}
|
|
33529
|
+
return send(url, init);
|
|
33530
|
+
}
|
|
33531
|
+
return res;
|
|
33532
|
+
};
|
|
33533
|
+
return { webId: s.webId, issuer: s.issuer, fetch: authFetch, refresh, signOut: signOut2 };
|
|
33534
|
+
}
|
|
33535
|
+
return { beginLogin: beginLogin2, completeLogin: completeLogin2, getSession: getSession2, signOut: signOut2 };
|
|
33706
33536
|
}
|
|
33707
33537
|
|
|
33538
|
+
// web/app/oidc-session.mjs
|
|
33539
|
+
var { beginLogin, completeLogin, getSession, signOut } = solidOidcSession({ dbName: "fedipod-oidc", clientName: "FediPod" });
|
|
33540
|
+
|
|
33708
33541
|
// web/app/boot.mjs
|
|
33709
33542
|
var REDIRECT = `${location.origin}/`;
|
|
33710
33543
|
async function bootWorker({ reset = false } = {}) {
|
|
@@ -33755,7 +33588,7 @@ async function readAccountState() {
|
|
|
33755
33588
|
const urls = { state };
|
|
33756
33589
|
const [cfg, doc] = await Promise.all([
|
|
33757
33590
|
readConfig(remote, urls),
|
|
33758
|
-
|
|
33591
|
+
readKeys(remote, urls)
|
|
33759
33592
|
]);
|
|
33760
33593
|
if (!cfg) throw new Error(`could not read this account's config under ${state}`);
|
|
33761
33594
|
const actorUrl = `${cfg.remotePod}${cfg.root || AP_ROOT}ap/actor`;
|
|
@@ -33763,27 +33596,46 @@ async function readAccountState() {
|
|
|
33763
33596
|
}
|
|
33764
33597
|
window.fedipodUnlock = async (password) => {
|
|
33765
33598
|
if (!password) throw new Error("Enter your password.");
|
|
33766
|
-
const { doc, actorUrl } = await readAccountState();
|
|
33599
|
+
const { remote, urls, doc, actorUrl } = await readAccountState();
|
|
33767
33600
|
if (!doc) throw new Error("could not read this account's key on the pod");
|
|
33768
|
-
if (!isKeyEnvelope(doc)) throw new Error("this account's key is not
|
|
33601
|
+
if (!isKeyEnvelope(doc)) throw new Error("this account's key is not under a password \u2014 nothing to unlock");
|
|
33769
33602
|
const rec = await unwrapKeys(doc, password);
|
|
33603
|
+
await writeKeys(remote, urls, rec);
|
|
33770
33604
|
await cacheOpenedKeys(actorUrl, rec);
|
|
33771
33605
|
await bootWorker();
|
|
33772
33606
|
};
|
|
33773
|
-
window.fedipodNewKey = async (
|
|
33774
|
-
if (!password) throw new Error("Enter the password you use for your pod now.");
|
|
33607
|
+
window.fedipodNewKey = async () => {
|
|
33775
33608
|
const { remote, urls, cfg, actorUrl } = await readAccountState();
|
|
33776
33609
|
const keys = await generateKeys();
|
|
33777
33610
|
keys.mintedFor = cfg.gateway?.frontActor || actorUrl;
|
|
33778
|
-
await
|
|
33611
|
+
await writeKeys(remote, urls, keys);
|
|
33779
33612
|
await cacheOpenedKeys(actorUrl, keys);
|
|
33780
33613
|
await bootWorker();
|
|
33781
33614
|
};
|
|
33782
|
-
|
|
33783
|
-
|
|
33784
|
-
|
|
33615
|
+
var SIGNUP_RETURN = "signup";
|
|
33616
|
+
var issuerOf = (v) => {
|
|
33617
|
+
let s = String(v || "").trim();
|
|
33618
|
+
if (!s) throw new Error("A pod provider is required.");
|
|
33619
|
+
if (!/^https?:\/\//i.test(s)) s = "https://" + s;
|
|
33620
|
+
return new URL(s).origin;
|
|
33621
|
+
};
|
|
33622
|
+
window.fedipodPodLogin = async ({ issuer }) => {
|
|
33623
|
+
const { authorizationUrl } = await beginLogin({ issuer: issuerOf(issuer), redirectUri: REDIRECT, returnTo: SIGNUP_RETURN });
|
|
33785
33624
|
location.href = authorizationUrl;
|
|
33786
33625
|
};
|
|
33626
|
+
window.fedipodSignup = async ({ onStep, ...answers }) => {
|
|
33627
|
+
const session = await getSession();
|
|
33628
|
+
if (!session) throw new Error("Sign in at your pod first.");
|
|
33629
|
+
await signUp(answers, { session, onStep, frontOrigin: location.origin });
|
|
33630
|
+
await bootWorker({ reset: true });
|
|
33631
|
+
};
|
|
33632
|
+
window.fedipodMoveIn = async ({ onStep, ...answers }) => {
|
|
33633
|
+
const session = await getSession();
|
|
33634
|
+
if (!session) throw new Error("Sign in at your pod first.");
|
|
33635
|
+
await moveIn(answers, { session, onStep, frontOrigin: location.origin });
|
|
33636
|
+
await bootWorker({ reset: true });
|
|
33637
|
+
};
|
|
33638
|
+
var moveFrom = null;
|
|
33787
33639
|
function parseAddress(input) {
|
|
33788
33640
|
let s = String(input || "").trim();
|
|
33789
33641
|
if (s.startsWith("@")) s = s.slice(1);
|
|
@@ -33810,6 +33662,7 @@ async function podForFrontedAddress(handle) {
|
|
|
33810
33662
|
`/.well-known/webfinger?resource=${encodeURIComponent(`acct:${handle}@${location.host}`)}`,
|
|
33811
33663
|
{ headers: { accept: "application/jrd+json, application/json" } }
|
|
33812
33664
|
).catch(() => null);
|
|
33665
|
+
if (res?.status === 410) throw new Error(`@${handle}@${location.host} is closed: nothing on the pod behind it was touched, but the address is gone for good.`);
|
|
33813
33666
|
if (!res || res.status >= 400) throw new Error(`nobody at this site is called @${handle}@${location.host}`);
|
|
33814
33667
|
const doc = await res.json().catch(() => ({}));
|
|
33815
33668
|
const podActorId = (doc.aliases || []).find((a) => /\/ap\/actor$/u.test(String(a)));
|
|
@@ -33830,8 +33683,16 @@ window.fedipodSignin = async ({ address }) => {
|
|
|
33830
33683
|
};
|
|
33831
33684
|
window.fedipodOnLoad = async () => {
|
|
33832
33685
|
if (new URLSearchParams(location.search).get("code")) {
|
|
33833
|
-
await completeLogin({ currentUrl: location.href });
|
|
33686
|
+
const done = await completeLogin({ currentUrl: location.href });
|
|
33834
33687
|
history.replaceState({}, "", REDIRECT);
|
|
33688
|
+
if (done?.returnTo === SIGNUP_RETURN) {
|
|
33689
|
+
const here = await readAccount(done).catch(() => null);
|
|
33690
|
+
if (!here) return "signup";
|
|
33691
|
+
if (here.frontHost && here.frontHost !== location.host) {
|
|
33692
|
+
moveFrom = here;
|
|
33693
|
+
return "move-in";
|
|
33694
|
+
}
|
|
33695
|
+
}
|
|
33835
33696
|
await bootWorker({ reset: true });
|
|
33836
33697
|
return "signed-in";
|
|
33837
33698
|
}
|
|
@@ -33865,6 +33726,7 @@ window.fedipodHandleProblem = handleProblem;
|
|
|
33865
33726
|
if (typeof document !== "undefined") (async () => {
|
|
33866
33727
|
const $ = (id) => document.getElementById(id);
|
|
33867
33728
|
const params = new URLSearchParams(location.search);
|
|
33729
|
+
let pendingIdentity = false;
|
|
33868
33730
|
const doUnlock = async () => {
|
|
33869
33731
|
$("unlock-error").textContent = "";
|
|
33870
33732
|
const btn = $("unlock-go");
|
|
@@ -33892,13 +33754,12 @@ if (typeof document !== "undefined") (async () => {
|
|
|
33892
33754
|
const btn = $("unlock-newkey-go");
|
|
33893
33755
|
btn.disabled = true;
|
|
33894
33756
|
try {
|
|
33895
|
-
await window.fedipodNewKey(
|
|
33757
|
+
await window.fedipodNewKey();
|
|
33896
33758
|
$("unlock-password").value = "";
|
|
33897
33759
|
location.href = "/admin/client/";
|
|
33898
33760
|
} catch (err) {
|
|
33899
33761
|
$("unlock-error").textContent = err.message || String(err);
|
|
33900
33762
|
btn.disabled = false;
|
|
33901
|
-
$("unlock-password").select();
|
|
33902
33763
|
}
|
|
33903
33764
|
};
|
|
33904
33765
|
$("unlock-newkey-go")?.addEventListener("click", doNewKey);
|
|
@@ -33926,20 +33787,86 @@ if (typeof document !== "undefined") (async () => {
|
|
|
33926
33787
|
$("unlock-password").focus();
|
|
33927
33788
|
return;
|
|
33928
33789
|
}
|
|
33790
|
+
const once = (key) => {
|
|
33791
|
+
try {
|
|
33792
|
+
if (sessionStorage.getItem(key)) return false;
|
|
33793
|
+
sessionStorage.setItem(key, "1");
|
|
33794
|
+
return true;
|
|
33795
|
+
} catch {
|
|
33796
|
+
return false;
|
|
33797
|
+
}
|
|
33798
|
+
};
|
|
33799
|
+
const reload = () => location.reload();
|
|
33800
|
+
const signIn = () => {
|
|
33801
|
+
location.href = "/?add";
|
|
33802
|
+
};
|
|
33803
|
+
const known = {
|
|
33804
|
+
"sign-in-refused": { title: "Your pod refused this sign-in", retry: "Sign in again", go: signIn },
|
|
33805
|
+
"pod-busy": { title: "Your pod is asking for a pause", retry: "Reload", go: reload, wait: 45 },
|
|
33806
|
+
"pod-error": { title: "Your pod had an error", retry: "Reload", go: reload, wait: 20 },
|
|
33807
|
+
"pod-unreachable": { title: "Your pod could not be reached", retry: "Reload", go: reload, wait: 20 },
|
|
33808
|
+
// A session with no account behind it is a sign-up that has not
|
|
33809
|
+
// happened yet — the person who closed the tab between the pod's
|
|
33810
|
+
// login and the identity screen lands here.
|
|
33811
|
+
"no-account-here": { title: "No FediPod account in that pod", retry: "Create one on this pod", go: () => showIdentity() },
|
|
33812
|
+
"no-account": { title: "No FediPod account in that pod", retry: "Create one on this pod", go: () => showIdentity() },
|
|
33813
|
+
"device-account": { title: "This account is run from a device", retry: "Use another pod", go: signIn },
|
|
33814
|
+
"address-closed": { title: "This address is closed", retry: "Sign in with another account", go: signIn }
|
|
33815
|
+
}[e.code];
|
|
33816
|
+
if (e.code === "sign-in-refused" && once("fedipod-renewing")) {
|
|
33817
|
+
$("loading").hidden = true;
|
|
33818
|
+
$("hero").hidden = true;
|
|
33819
|
+
$("landing").hidden = true;
|
|
33820
|
+
$("brand").hidden = false;
|
|
33821
|
+
$("running").hidden = false;
|
|
33822
|
+
$("running-title").textContent = "Renewing your sign-in";
|
|
33823
|
+
$("run-error").style.color = "var(--sub)";
|
|
33824
|
+
$("run-error").style.borderLeftColor = "var(--line)";
|
|
33825
|
+
$("run-error").textContent = "Your pod would not take the sign-in this browser is holding. Renewing it now.";
|
|
33826
|
+
$("run-actions").hidden = true;
|
|
33827
|
+
const session = await getSession().catch(() => null);
|
|
33828
|
+
if (session) {
|
|
33829
|
+
try {
|
|
33830
|
+
await session.refresh();
|
|
33831
|
+
location.reload();
|
|
33832
|
+
return;
|
|
33833
|
+
} catch {
|
|
33834
|
+
}
|
|
33835
|
+
try {
|
|
33836
|
+
const { authorizationUrl } = await beginLogin({ issuer: session.issuer, redirectUri: REDIRECT });
|
|
33837
|
+
location.href = authorizationUrl;
|
|
33838
|
+
return;
|
|
33839
|
+
} catch {
|
|
33840
|
+
}
|
|
33841
|
+
}
|
|
33842
|
+
}
|
|
33929
33843
|
console.error(e);
|
|
33930
33844
|
$("loading").hidden = true;
|
|
33931
33845
|
$("hero").hidden = true;
|
|
33932
33846
|
$("landing").hidden = true;
|
|
33933
33847
|
$("brand").hidden = false;
|
|
33934
33848
|
$("running").hidden = false;
|
|
33935
|
-
$("running-title").textContent = "Signed in, but the agent could not start";
|
|
33849
|
+
$("running-title").textContent = known ? known.title : "Signed in, but the agent could not start";
|
|
33936
33850
|
$("run-error").style.whiteSpace = "pre-wrap";
|
|
33937
|
-
$("run-error").textContent = (e.message || String(e)) + (e.detail ? `
|
|
33851
|
+
$("run-error").textContent = (e.message || String(e)) + (!known && e.detail ? `
|
|
33938
33852
|
|
|
33939
33853
|
${e.detail}` : "");
|
|
33940
33854
|
$("run-actions").hidden = false;
|
|
33941
|
-
$("run-
|
|
33942
|
-
$("run-retry").
|
|
33855
|
+
$("run-actions").prepend($("run-retry"));
|
|
33856
|
+
$("run-retry").textContent = known ? known.retry : "Reload";
|
|
33857
|
+
$("run-retry").addEventListener("click", known ? known.go : reload);
|
|
33858
|
+
if (known?.wait && once(`fedipod-waited-${e.code}`)) {
|
|
33859
|
+
let left = known.wait;
|
|
33860
|
+
const tick = () => {
|
|
33861
|
+
$("run-retry").textContent = left > 0 ? `Trying again in ${left}s` : "Trying again\u2026";
|
|
33862
|
+
if (left-- <= 0) {
|
|
33863
|
+
clearInterval(timer);
|
|
33864
|
+
location.reload();
|
|
33865
|
+
}
|
|
33866
|
+
};
|
|
33867
|
+
const timer = setInterval(tick, 1e3);
|
|
33868
|
+
tick();
|
|
33869
|
+
}
|
|
33943
33870
|
$("run-back").textContent = "Sign out";
|
|
33944
33871
|
$("run-back").addEventListener("click", async () => {
|
|
33945
33872
|
try {
|
|
@@ -33954,8 +33881,11 @@ ${e.detail}` : "");
|
|
|
33954
33881
|
location.href = "/admin/client/";
|
|
33955
33882
|
return;
|
|
33956
33883
|
}
|
|
33957
|
-
|
|
33958
|
-
|
|
33884
|
+
if (state === "signup" || state === "move-in") pendingIdentity = true;
|
|
33885
|
+
else {
|
|
33886
|
+
$("loading").hidden = true;
|
|
33887
|
+
$("landing").hidden = false;
|
|
33888
|
+
}
|
|
33959
33889
|
}
|
|
33960
33890
|
const showLanding = () => {
|
|
33961
33891
|
$("pane-form").hidden = true;
|
|
@@ -33971,6 +33901,29 @@ ${e.detail}` : "");
|
|
|
33971
33901
|
$("pane-form").hidden = false;
|
|
33972
33902
|
goStep(1);
|
|
33973
33903
|
};
|
|
33904
|
+
let signedPod = "";
|
|
33905
|
+
async function showIdentity() {
|
|
33906
|
+
const session = await getSession();
|
|
33907
|
+
if (!session) {
|
|
33908
|
+
showLanding();
|
|
33909
|
+
return;
|
|
33910
|
+
}
|
|
33911
|
+
signedPod = podBaseOfWebId(session.webId);
|
|
33912
|
+
$("loading").hidden = true;
|
|
33913
|
+
$("hero").hidden = true;
|
|
33914
|
+
$("landing").hidden = true;
|
|
33915
|
+
$("running").hidden = true;
|
|
33916
|
+
$("brand").hidden = false;
|
|
33917
|
+
$("pane-form").hidden = false;
|
|
33918
|
+
$("signed-pod").textContent = signedPod;
|
|
33919
|
+
$("movein-note").hidden = !moveFrom;
|
|
33920
|
+
if (moveFrom) {
|
|
33921
|
+
$("movein-from").textContent = moveFrom.address;
|
|
33922
|
+
if (!f().handle.value) f().handle.value = moveFrom.config.handle;
|
|
33923
|
+
$("submit").textContent = "Move your account here";
|
|
33924
|
+
}
|
|
33925
|
+
goStep(2);
|
|
33926
|
+
}
|
|
33974
33927
|
const doSignin = async () => {
|
|
33975
33928
|
$("signin-error").textContent = "";
|
|
33976
33929
|
try {
|
|
@@ -34000,91 +33953,52 @@ ${e.detail}` : "");
|
|
|
34000
33953
|
return "";
|
|
34001
33954
|
}
|
|
34002
33955
|
};
|
|
34003
|
-
const
|
|
34004
|
-
|
|
34005
|
-
const
|
|
34006
|
-
return
|
|
33956
|
+
const registerUrl = () => {
|
|
33957
|
+
if (!providerHost()) return "";
|
|
33958
|
+
const origin = new URL(providerUrl()).origin;
|
|
33959
|
+
return f().provider.value ? `${origin}/.account/login/password/register/` : `${origin}/`;
|
|
34007
33960
|
};
|
|
34008
|
-
const
|
|
34009
|
-
let v = f().pod.value.trim();
|
|
34010
|
-
if (!v) return "";
|
|
34011
|
-
if (!/^https?:\/\//i.test(v)) v = "https://" + v;
|
|
34012
|
-
if (!v.endsWith("/")) v += "/";
|
|
33961
|
+
const pathPod = () => {
|
|
34013
33962
|
try {
|
|
34014
|
-
return new URL(
|
|
34015
|
-
} catch {
|
|
34016
|
-
return "";
|
|
34017
|
-
}
|
|
34018
|
-
};
|
|
34019
|
-
const isPathPod = (u) => {
|
|
34020
|
-
try {
|
|
34021
|
-
return new URL(u).pathname !== "/";
|
|
33963
|
+
return new URL(signedPod).pathname !== "/";
|
|
34022
33964
|
} catch {
|
|
34023
33965
|
return false;
|
|
34024
33966
|
}
|
|
34025
33967
|
};
|
|
34026
|
-
const
|
|
34027
|
-
|
|
34028
|
-
const learnLayout = async () => {
|
|
34029
|
-
const origin = providerHost() ? new URL(providerUrl()).origin : "";
|
|
34030
|
-
if (!origin) {
|
|
34031
|
-
layout = null;
|
|
34032
|
-
return;
|
|
34033
|
-
}
|
|
34034
|
-
if (!layouts.has(origin)) layouts.set(origin, podLayout(fetch, origin).catch(() => null));
|
|
34035
|
-
const known = await layouts.get(origin);
|
|
34036
|
-
if (providerHost() && new URL(providerUrl()).origin === origin) {
|
|
34037
|
-
layout = known;
|
|
34038
|
-
applyShape();
|
|
34039
|
-
previewAddr();
|
|
34040
|
-
}
|
|
34041
|
-
};
|
|
34042
|
-
const pathPod = () => f().mode.value === "existing" ? isPathPod(podUrl()) : layout === "path";
|
|
34043
|
-
const shape = () => pathPod() ? "front" : f().shape.value;
|
|
34044
|
-
const answers = () => {
|
|
34045
|
-
const mode = f().mode.value;
|
|
34046
|
-
const a = {
|
|
34047
|
-
mode,
|
|
34048
|
-
handle: f().handle.value.trim().toLowerCase(),
|
|
34049
|
-
email: f().email.value.trim(),
|
|
34050
|
-
password: f().password.value,
|
|
34051
|
-
issuer: providerUrl(),
|
|
34052
|
-
shape: shape()
|
|
34053
|
-
};
|
|
34054
|
-
if (mode === "new") a.podName = f().podName.value.trim().toLowerCase();
|
|
34055
|
-
else a.pod = podUrl();
|
|
34056
|
-
return a;
|
|
34057
|
-
};
|
|
33968
|
+
const shape = () => pathPod() || moveFrom ? "front" : f().shape.value;
|
|
33969
|
+
const answers = () => ({ handle: f().handle.value.trim().toLowerCase(), shape: shape() });
|
|
34058
33970
|
const previewAddr = () => {
|
|
34059
33971
|
const handle = f().handle.value.trim().toLowerCase();
|
|
34060
|
-
|
|
33972
|
+
let host = "";
|
|
33973
|
+
try {
|
|
33974
|
+
host = shape() === "front" ? location.host : new URL(signedPod).host;
|
|
33975
|
+
} catch {
|
|
33976
|
+
host = "";
|
|
33977
|
+
}
|
|
34061
33978
|
$("preview").textContent = handle && host ? `@${handle}@${host}` : "@\u2026@\u2026";
|
|
34062
33979
|
};
|
|
34063
33980
|
const applyShape = () => {
|
|
34064
|
-
const fixed = pathPod();
|
|
33981
|
+
const fixed = pathPod() || !!moveFrom;
|
|
34065
33982
|
for (const r of f().shape) {
|
|
34066
33983
|
if (fixed) r.checked = r.value === "front";
|
|
34067
33984
|
}
|
|
34068
33985
|
$("shape-group").hidden = fixed;
|
|
34069
|
-
$("shape-hint").hidden = !fixed;
|
|
33986
|
+
$("shape-hint").hidden = !fixed || !!moveFrom;
|
|
34070
33987
|
};
|
|
34071
33988
|
const applyMode = () => {
|
|
34072
33989
|
const existing = f().mode.value === "existing";
|
|
34073
|
-
$("
|
|
34074
|
-
$("podname-field").hidden = existing;
|
|
33990
|
+
$("newpod-field").hidden = existing;
|
|
34075
33991
|
$("provider-other-field").hidden = f().provider.value !== "";
|
|
33992
|
+
const url = registerUrl();
|
|
33993
|
+
$("register-link").href = url || "#";
|
|
33994
|
+
$("register-link").textContent = providerHost() ? `Create your pod at ${providerHost()}` : "Create your pod at your provider";
|
|
34076
33995
|
};
|
|
34077
33996
|
for (const el of $("form").elements) for (const evt of ["input", "change"]) el.addEventListener(evt, () => {
|
|
34078
33997
|
applyMode();
|
|
34079
33998
|
applyShape();
|
|
34080
33999
|
previewAddr();
|
|
34081
34000
|
});
|
|
34082
|
-
for (const evt of ["input", "change"]) {
|
|
34083
|
-
$("provider").addEventListener(evt, learnLayout);
|
|
34084
|
-
$("providerOther").addEventListener(evt, learnLayout);
|
|
34085
|
-
}
|
|
34086
34001
|
applyMode();
|
|
34087
|
-
learnLayout();
|
|
34088
34002
|
const STEP_IDS = ["step-1", "step-2"];
|
|
34089
34003
|
const FOCUS = { 1: "provider", 2: "handle" };
|
|
34090
34004
|
const goStep = (n) => {
|
|
@@ -34096,22 +34010,11 @@ ${e.detail}` : "");
|
|
|
34096
34010
|
if (n === 2) {
|
|
34097
34011
|
applyShape();
|
|
34098
34012
|
previewAddr();
|
|
34099
|
-
learnLayout();
|
|
34100
34013
|
}
|
|
34101
34014
|
if (FOCUS[n]) $(FOCUS[n]).focus();
|
|
34102
34015
|
};
|
|
34103
34016
|
const validateStep1 = () => {
|
|
34104
34017
|
if (!providerHost()) return f().provider.value === "" ? "A pod provider address is required under Other\u2026." : "A valid pod provider URL is required.";
|
|
34105
|
-
if (f().mode.value === "existing") {
|
|
34106
|
-
if (!podUrl()) return "A pod address is required, like https://alice.solidcommunity.net/ or https://server.example/alice/.";
|
|
34107
|
-
} else {
|
|
34108
|
-
const sub = f().podName.value.trim().toLowerCase();
|
|
34109
|
-
if (!sub) return "A pod username/subdomain is required.";
|
|
34110
|
-
const sp = window.fedipodHandleProblem(sub);
|
|
34111
|
-
if (sp) return `Pod username: ${sp}`;
|
|
34112
|
-
}
|
|
34113
|
-
if (!f().email.value.trim()) return "A pod email is required.";
|
|
34114
|
-
if (!f().password.value) return "A pod password is required.";
|
|
34115
34018
|
return null;
|
|
34116
34019
|
};
|
|
34117
34020
|
const validateStep2 = () => {
|
|
@@ -34124,38 +34027,39 @@ ${e.detail}` : "");
|
|
|
34124
34027
|
showLanding();
|
|
34125
34028
|
goStep(1);
|
|
34126
34029
|
});
|
|
34127
|
-
$("to-2").addEventListener("click", () => {
|
|
34030
|
+
$("to-2").addEventListener("click", async () => {
|
|
34128
34031
|
const e = validateStep1();
|
|
34129
34032
|
if (e) {
|
|
34130
34033
|
$("err-1").textContent = e;
|
|
34131
34034
|
return;
|
|
34132
34035
|
}
|
|
34133
|
-
|
|
34036
|
+
$("err-1").textContent = "";
|
|
34037
|
+
$("to-2").disabled = true;
|
|
34038
|
+
try {
|
|
34039
|
+
await window.fedipodPodLogin({ issuer: providerUrl() });
|
|
34040
|
+
} catch (err) {
|
|
34041
|
+
$("err-1").textContent = err.message || String(err);
|
|
34042
|
+
$("to-2").disabled = false;
|
|
34043
|
+
}
|
|
34134
34044
|
});
|
|
34135
34045
|
$("back-1").addEventListener("click", () => goStep(1));
|
|
34136
34046
|
const backToForm = (errMsg) => {
|
|
34137
34047
|
$("running").hidden = true;
|
|
34138
34048
|
$("brand").hidden = false;
|
|
34139
34049
|
$("pane-form").hidden = false;
|
|
34140
|
-
goStep(
|
|
34141
|
-
if (errMsg) $("
|
|
34050
|
+
goStep(2);
|
|
34051
|
+
if (errMsg) $("form-error").textContent = errMsg;
|
|
34142
34052
|
};
|
|
34143
34053
|
$("run-retry").addEventListener("click", () => backToForm($("run-error").textContent));
|
|
34144
34054
|
$("run-back").addEventListener("click", () => {
|
|
34145
34055
|
showLanding();
|
|
34146
34056
|
goStep(1);
|
|
34147
34057
|
});
|
|
34148
|
-
const LABELS = {
|
|
34058
|
+
const LABELS = { pod: "Checking your pod", keys: "Making your signing key", gateway: "Connecting your mail door" };
|
|
34059
|
+
const MOVE_LABELS = { pod: "Reading your account on your pod", gateway: "Taking your address here", keys: "Moving your key and account record" };
|
|
34149
34060
|
$("form").addEventListener("submit", async (e) => {
|
|
34150
34061
|
e.preventDefault();
|
|
34151
34062
|
$("form-error").textContent = "";
|
|
34152
|
-
const e1 = validateStep1();
|
|
34153
|
-
if (e1) {
|
|
34154
|
-
$("form-error").textContent = e1;
|
|
34155
|
-
goStep(1);
|
|
34156
|
-
$("err-1").textContent = e1;
|
|
34157
|
-
return;
|
|
34158
|
-
}
|
|
34159
34063
|
const e2 = validateStep2();
|
|
34160
34064
|
if (e2) {
|
|
34161
34065
|
$("form-error").textContent = e2;
|
|
@@ -34171,22 +34075,26 @@ ${e.detail}` : "");
|
|
|
34171
34075
|
const steps = $("steps");
|
|
34172
34076
|
steps.textContent = "";
|
|
34173
34077
|
const mark = {};
|
|
34078
|
+
const labels = moveFrom ? MOVE_LABELS : LABELS;
|
|
34174
34079
|
const onStep = (k, st2) => {
|
|
34175
34080
|
if (!mark[k]) {
|
|
34176
34081
|
const li = document.createElement("li");
|
|
34177
34082
|
steps.appendChild(li);
|
|
34178
34083
|
mark[k] = li;
|
|
34179
34084
|
}
|
|
34180
|
-
mark[k].textContent = (st2 === "ok" ? "\u2713 " : st2 === "running" ? "\u2026 " : "") + (
|
|
34085
|
+
mark[k].textContent = (st2 === "ok" ? "\u2713 " : st2 === "running" ? "\u2026 " : "") + (labels[k] || k);
|
|
34181
34086
|
};
|
|
34182
34087
|
try {
|
|
34183
|
-
await window.
|
|
34088
|
+
if (moveFrom) await window.fedipodMoveIn({ handle: a.handle, onStep });
|
|
34089
|
+
else await window.fedipodSignup({ ...a, onStep });
|
|
34090
|
+
location.href = "/admin/client/";
|
|
34184
34091
|
} catch (err) {
|
|
34185
34092
|
$("running-title").textContent = "Setup did not finish";
|
|
34186
34093
|
$("run-error").textContent = err.message || String(err);
|
|
34187
34094
|
$("run-actions").hidden = false;
|
|
34188
34095
|
}
|
|
34189
34096
|
});
|
|
34097
|
+
if (pendingIdentity) await showIdentity();
|
|
34190
34098
|
})();
|
|
34191
34099
|
export {
|
|
34192
34100
|
parseAddress
|