diff options
Diffstat (limited to 'web')
-rw-r--r-- | web/index.hoon | 8 | ||||
-rw-r--r-- | web/login.hoon | 311 | ||||
-rw-r--r-- | web/router.hoon | 159 |
3 files changed, 117 insertions, 361 deletions
diff --git a/web/index.hoon b/web/index.hoon index 4342d84..c9aa43a 100644 --- a/web/index.hoon +++ b/web/index.hoon @@ -1,5 +1,5 @@ /= sigil-html /web/sigil/sigil -|_ bowl:gall +|_ [bowl:gall base-slug=tape] ++ favicon %+ weld "<svg width='10' height='10' viewBox='0 0 10 10' xmlns='http://www.w3.org/2000/svg'>" "<circle r='3.09' cx='5' cy='5' /></svg>" @@ -18,7 +18,11 @@ ;p:"You are logged" ;+ sigil == - ;form(action "/zodiac/logout", method "get") + ;form(action "/{base-slug}/poke", method "get") + ;button(type "submit"):"Test" + == + + ;form(action "/{base-slug}/logout", method "get") ;button(id "logout-button", type "submit"):"Logout" == == diff --git a/web/login.hoon b/web/login.hoon index b4b1084..aa1d57d 100644 --- a/web/login.hoon +++ b/web/login.hoon @@ -1,291 +1,8 @@ -|_ bowl:gall -++ auth-styling - ''' - @import url("https://rsms.me/inter/inter.css"); - @font-face { - font-family: "Source Code Pro"; - src: url("https://storage.googleapis.com/media.urbit.org/fonts/scp-regular.woff"); - font-weight: 400; - font-display: swap; - } - :root { - --red-soft: #FFEFEC; - --red: #FF6240; - --gray-100: #E5E5E5; - --gray-400: #999999; - --gray-800: #333333; - --white: #FFFFFF; - } - html { - font-family: Inter, sans-serif; - height: 100%; - margin: 0; - width: 100%; - background: var(--white); - color: var(--gray-800); - -webkit-font-smoothing: antialiased; - line-height: 1.5; - font-size: 16px; - font-weight: 600; - display: flex; - flex-flow: row nowrap; - justify-content: center; - } - body { - display: flex; - flex-flow: column nowrap; - justify-content: center; - max-width: 300px; - padding: 1rem; - width: 100%; - } - body.local #eauth, - body.eauth #local { - display: none; - min-height: 100%; - } - #eauth input { - /*NOTE dumb hack to get approx equal height with #local */ - margin-bottom: 15px; - } - body nav { - background: var(--gray-100); - border-radius: 2rem; - display: flex; - justify-content: space-around; - overflow: hidden; - margin-bottom: 1rem; - } - body nav div { - width: 50%; - padding: 0.5rem 1rem; - text-align: center; - cursor: pointer; - } - body.local nav div.local, - body.eauth nav div.eauth { - background: var(--gray-800); - color: var(--white); - cursor: default; - } - nav div.local { - border-right: none; - border-top-right-radius: 0; - border-bottom-right-radius: 0; - } - nav div.eauth { - border-left: none; - border-top-left-radius: 0; - border-bottom-left-radius: 0; - } - body > *, - form > input { - width: 100%; - } - form { - display: flex; - flex-flow: column; - align-items: flex-start; - } - input { - background: var(--gray-100); - border: 2px solid transparent; - padding: 0.5rem; - border-radius: 0.5rem; - font-size: inherit; - color: var(--gray-800); - box-shadow: none; - width: 100%; - } - input:disabled { - background: var(--gray-100); - color: var(--gray-400); - } - input:focus { - outline: none; - background: var(--white); - border-color: var(--gray-400); - } - input:invalid:not(:focus) { - background: var(--red-soft); - border-color: var(--red); - outline: none; - color: var(--red); - } - button[type=submit] { - margin-top: 1rem; - } - button[type=submit], a.button { - font-size: 1rem; - padding: 0.5rem 1rem; - border-radius: 0.5rem; - background: var(--gray-800); - color: var(--white); - border: none; - font-weight: 600; - text-decoration: none; - } - input:invalid ~ button[type=submit] { - border-color: currentColor; - background: var(--gray-100); - color: var(--gray-400); - pointer-events: none; - } - span.guest, span.guest a { - color: var(--gray-400); - } - span.failed { - display: flex; - flex-flow: row nowrap; - height: 1rem; - align-items: center; - margin-top: 0.875rem; - color: var(--red); - } - span.failed svg { - height: 1rem; - margin-right: 0.25rem; - } - span.failed path { - fill: transparent; - stroke-width: 2px; - stroke-linecap: round; - stroke: currentColor; - } - .mono { - font-family: 'Source Code Pro', monospace; - } - @media all and (prefers-color-scheme: dark) { - :root { - --white: #000000; - --gray-800: #E5E5E5; - --gray-400: #808080; - --gray-100: #333333; - --red-soft: #7F1D1D; - } - } - @media screen and (min-width: 30em) { - html { - font-size: 14px; - } - } - ''' +|_ [bowl:gall desk=tape redirect-str=tape] ++ favicon %+ weld "<svg width='10' height='10' viewBox='0 0 10 10' xmlns='http://www.w3.org/2000/svg'>" "<circle r='3.09' cx='5' cy='5' /></svg>" -++ landscape ^- manx -=/ eauth=(unit ?) [~ %|] -=/ failed=? .n -=/ identity=identity:eyre [%ours ~] -=/ redirect-url=(unit @t) ~ -=/ redirect-str ?~(redirect-url "" (trip u.redirect-url)) - ;html - ;head - ;meta(charset "utf-8"); - ;meta(name "viewport", content "width=device-width, initial-scale=1, shrink-to-fit=no"); - ;link(rel "icon", type "image/svg+xml", href (weld "data:image/svg+xml;utf8," favicon)); - ;title:"Urbit" - ;style:"{(trip auth-styling)}" - ;style:"{?^(eauth "" "nav \{ display: none; }")}" - ;script:"our = '{(scow %p our)}';" - ;script:''' - let name, pass; - function setup(isEauth) { - name = document.getElementById('name'); - pass = document.getElementById('pass'); - if (isEauth) goEauth(); else goLocal(); - } - function goLocal() { - document.body.className = 'local'; - pass.focus(); - } - function goEauth() { - document.body.className = 'eauth'; - name.focus(); - } - function doEauth() { - if (name.value == our) { - event.preventDefault(); - goLocal(); - } - } - ''' - == - ;body - =class "{?:(=(`& eauth) "eauth" "local")}" - =onload "setup({?:(=(`& eauth) "true" "false")})" - ;div#local - ;p:"Urbit ID" - ;input(value "{(scow %p our)}", disabled "true", class "mono"); - ;+ ?: =(%ours -.identity) - ;div - ;p:"Already authenticated" - ;a.button/"{(trip (fall redirect-url '/'))}":"Continue" - == - ;form(action "/~/login", method "post", enctype "application/x-www-form-urlencoded") - ;p:"Access Key" - ;input - =type "password" - =name "password" - =id "pass" - =placeholder "sampel-ticlyt-migfun-falmel" - =class "mono" - =required "true" - =minlength "27" - =maxlength "27" - =pattern "((?:[a-z]\{6}-)\{3}(?:[a-z]\{6}))"; - ;input(type "hidden", name "redirect", value redirect-str); - ;+ ?. failed ;span; - ;span.failed - ;svg(xmlns "http://www.w3.org/2000/svg", viewBox "0 0 16 16") - ;path(d "m8 8 4-4M8 8 4 4m4 4-4 4m4-4 4 4"); - == - Key is incorrect - == - ;button(type "submit"):"Continue" - == - == - ;div#eauth - ;form(action "/~/login", method "post", onsubmit "return doEauth()") - ;p:"Urbit ID Metamask Login" - ;input.mono - =name "name" - =id "name" - =placeholder "{(scow %p our)}" - =required "true" - =minlength "4" - =maxlength "57" - =pattern "~((([a-z]\{6})\{1,2}-\{0,2})+|[a-z]\{3})"; - ;p - ; You will be redirected to your own web interface to authorize - ; logging in to - ;span.mono:"{(scow %p our)}" - ; . - == - ;input(type "hidden", name "redirect", value redirect-str); - ;button(name "eauth", type "submit"):"Continue" - == - == - ;* ?: ?=(%ours -.identity) ~ - =+ as="proceed as{?:(?=(%fake -.identity) " guest" "")}" - ;+ ;span.guest.mono - ; Or try to - ;a/"{(trip (fall redirect-url '/'))}":"{as}" - ; . - == - == - ;script:''' - var failSpan = document.querySelector('.failed'); - if (failSpan) { - document.querySelector("input[type=password]") - .addEventListener('keyup', function (event) { - failSpan.style.display = 'none'; - }); - } - ''' - == ++ $ -=/ redirect-str "/forum" ;html ;head ;meta(charset "utf-8"); @@ -294,7 +11,7 @@ == ;body ;main#login-page.white - ;h1.tc:"Zodiac Login" + ;h1.tc:"Login" ;button(id "mauth"):"Login via 🦊MetaMask »" ;script(type "module"):"{metamask-script}" :: ;script(type "importmap"):"{import-script}" @@ -316,8 +33,17 @@ "ethers": "/node_modules/ethers/" }} ''' +++ fetch-urls ^- tape +""" +const sigilUrl = "/{desk}/sigil/"; +const secretUrl= "/{desk}/metamask"; +const authUrl= "/{desk}/auth"; +const redirectUrl = "{redirect-str}"; + +""" ++ metamask-script ^~ + %+ weld fetch-urls %- trip ''' import { ethers } from "https://cdnjs.cloudflare.com/ajax/libs/ethers/6.7.0/ethers.min.js"; @@ -377,7 +103,7 @@ async function fetchSigil(ship) { try { - const response = await fetch('/zodiac/sigil/' + ship); + const response = await fetch(sigilUrl + ship); if (response.ok) { const data = await response.text(); return data; @@ -390,7 +116,7 @@ } async function fetchSecret() { try { - const response = await fetch('/zodiac/metamask'); + const response = await fetch(secretUrl); if (response.ok) { const data = await response.json(); return data.challenge; @@ -428,21 +154,20 @@ async function metamaskLogin(account, point){ // Fetch the secret from the server - const secret = await fetchSecret(); - console.log({secret}); + const challenge = await fetchSecret(); const signature = await window.ethereum.request({ method: "personal_sign", - params: [secret, account], + params: [challenge, account], }); - const response = await fetch('/zodiac/auth', { + const response = await fetch(authUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ who: Number(point), - secret: secret, + challenge, address: account, signature: signature }), @@ -450,7 +175,7 @@ if (response.ok) { // location.reload(); - window.location.replace('/zodiac'); + window.location.replace(redirectUrl); } else { alert("Login failed. Please try again."); } diff --git a/web/router.hoon b/web/router.hoon index f5fc388..c554e41 100644 --- a/web/router.hoon +++ b/web/router.hoon @@ -1,22 +1,14 @@ -/- sur=zodiac -/+ server, metamask, sr=sortug +/- sur=neyre, coki +/+ server, metamask, sr=sortug, cokil=coki /= login-page /web/login /= index-page /web/index /= sigil-html /web/sigil/sigil -|_ [=bowl:gall eyre-id=@ta req=inbound-request:eyre state=versioned-state:sur] -+* metalib ~(. metamask [sessions.state bowl]) -++ session-timeout ~d300 -++ session-cookie-string - |= [session=@uv extend=?] - ^- @t - %- crip - =; max-age=tape - :: "urbauth-{(scow %p src.bowl)}={(scow %uv session)}; Path=/; Max-Age={max-age}" - "ucm-{(scow %p src.bowl)}={(scow %uv session)}; Path=/; Max-Age={max-age}" - %+ scow:parsing:sr %ud - ?. extend 0 - (div (msec:milly session-timeout) 1.000) +=+ base-path=`path`/coki +|_ [=bowl:gall eyre-id=@ta req=inbound-request:eyre state=versioned-state:sur =sessions:coki] + ++* cokilib ~(. cokil [sessions bowl]) + metalib ~(. metamask [sessions bowl]) ++ get-file-at |= [base=path file=path ext=@ta] @@ -32,30 +24,14 @@ %- as-octs:mimes:html .^(@ %cx path) -++ handle-get-request - |= [headers=header-list:http request-line:server] - ^- simple-payload:http - ?~ ext $(ext `%html, site [%index ~]) - ?: ?=([%zodiac *] site) $(site +.site) - :: serve dynamic session.js - :: - ?: =([/session `%js] [site ext]) - %- js-response:gen:server - %- as-octt:mimes:html - """ - window.ship = '{(scow %p src.bowl)}'; - """ - =/ file=(unit octs) - (get-file-at /web site u.ext) - ?~ file ~& "file not found" not-found:gen:server - ?+ u.ext not-found:gen:server - %html (html-response:gen:server u.file) - %js (js-response:gen:server u.file) - %css (css-response:gen:server u.file) - %png (png-response:gen:server u.file) - %woff (woff-response:gen:server u.file) - %woff2 (woff2-response:gen:server u.file) - == + + ++ serve-json |= jon=json + (send-response (json-response:gen:server jon)) +++ serve-error |= [status=@ud msg=@t] + =/ octs (as-octs:mimes:html msg) + =/ headers=header-list:http [['content-type' 'text/plain'] ~] + =/ sp=simple-payload:http [[status headers] `octs] + (send-response sp) ++ ebail @@ -78,17 +54,14 @@ ++ send-response |= =simple-payload:http - =/ cookie ['set-cookie' (session-cookie-string 0vublog .y)] - =. headers.response-header.simple-payload - [cookie headers.response-header.simple-payload] %+ give-simple-payload:app:server eyre-id simple-payload ++ validate-coki =/ coki (get-header:http 'cookie' header-list.request.req) ?~ coki ~ - (validate-coki:metalib u.coki) + (validate-coki:cokilib u.coki) -++ login-ajax +++ login-ajax |= base-slug=@tas =/ referer (get-header:http 'referer' header-list.request.req) ~& referer=referer ?~ referer .n @@ -96,33 +69,88 @@ ?~ parsed .n =/ =path q.q.u.parsed ~& login-ajax=path - ?= [%zodiac %login ~] path -++ route + ?~ path .n + =/ hd i.path =/ tl t.path + ~& >> [slug=base-slug hd tl] + =/ same-base .=(i.path base-slug) + ?& same-base ?=([%login ~] tl) == + +++ route ^- (list card:agent:gall) + =. base-path ?: ?=([%coki ~] base-path) /[dap.bowl] base-path + =/ bpath (spat base-path) + + =/ base-slug =/ bp `(list @t)`base-path ?~ bp dap.bowl i.bp + =/ subpaths /[base-slug] =/ rl (parse-request-line:server url.request.req) - =/ sitepath=path /[(head site.rl)] + =. site.rl ?~ site.rl site.rl ?: .=(base-slug i.site.rl) t.site.rl site.rl =/ pat=(pole knot) site.rl - ?: ?=([%zodiac %login ~] pat) serve-login-page - ?: login-ajax (serve-unauthed pat) + ~& > pat=pat + ~& [url.request.req base-path base-slug bpath pat subpaths src.bowl] + + |^ + ?: ?=([%login ~] pat) serve-login-page + ?: (login-ajax base-slug) serve-unauthed =/ muser validate-coki - ?~ muser redirect-to-login + ~& >>> muser=muser + ?~ muser (redirect (weld subpaths /login)) =. src.bowl u.muser + serve-authed + + ++ serve-authed ?+ pat ebail - [%zodiac %logout *] (logout:metalib eyre-id (need (get-header:http 'cookie' header-list.request.req))) - [%zodiac ~] serve-root + [%logout *] (logout:cokilib eyre-id (need (get-header:http 'cookie' header-list.request.req)) bpath) + :: [~] serve-root + ~ serve-root :: [site=@t *] (send-response (handle-get-request header-list.request.req rl)) + :: + [%poke *] ~& >> bowl=bowl (redirect base-path) + == + ++ serve-unauthed + ?+ pat ebail + [%sigil point=@ ~] (serve-sigil point.pat) + [%metamask *] (serve-metamask-challenge:metalib eyre-id) + [%auth *] + =/ res (process-metamask-auth:metalib eyre-id body.request.req bpath base-slug) + ?- -.res %& +.res %| (serve-error +.res) == + == -++ serve-unauthed |= pat=(pole knot) -?+ pat ebail - [%zodiac %metamask rest=*] (serve-metamask-challenge:metalib eyre-id) - [%zodiac %auth rest=*] (process-metamask-auth:metalib eyre-id body.request.req) - [%zodiac %sigil point=@ ~] (serve-sigil point.pat) -== -++ redirect-to-login - (send-response (redirect:gen:server '/zodiac/login')) -++ serve-root - (send-response (manx-payload (index-page bowl))) + ++ redirect |= =path + (send-response (redirect:gen:server (spat path))) + ++ serve-root + (send-response (manx-payload (index-page bowl (trip base-slug)))) + + ++ serve-login-page + (send-response (manx-payload (login-page bowl (trip base-slug) (trip (spat base-path))))) + + ++ handle-get-request + |= [headers=header-list:http request-line:server] + ^- simple-payload:http + ?~ ext $(ext `%html, site [%index ~]) + =/ pat `(pole knot)`site + ?: ?&(?=([slug=@t *] pat) .=(slug.pat base-slug)) $(site +.site) + :: serve dynamic session.js + :: + ?: =([/session `%js] [site ext]) + %- js-response:gen:server + %- as-octt:mimes:html + """ + window.ship = '{(scow %p src.bowl)}'; + """ + =/ file=(unit octs) + (get-file-at /web site u.ext) + ?~ file ~& "file not found" not-found:gen:server + ?+ u.ext not-found:gen:server + %html (html-response:gen:server u.file) + %js (js-response:gen:server u.file) + %css (css-response:gen:server u.file) + %png (png-response:gen:server u.file) + %woff (woff-response:gen:server u.file) + %woff2 (woff2-response:gen:server u.file) + == + + + -- :: -++ serve-login-page (send-response (manx-payload (login-page bowl))) ++ serve-sigil |= param=@t =/ sip (slaw:parsing:sr %ud param) ?~ sip ebail @@ -130,9 +158,8 @@ (send-response (manx-payload sigil-div)) -++ eyre-binding-card -|= =path - ~& > adding-binding=[path dap.bowl] +++ eyre-binding-card |= =path + ~& >> adding-binding=[path dap.bowl] [%pass /eyre/connect %arvo %e %connect [~ path] dap.bowl] -- |