From 6fb80b2d94a5282c8350278e299bfcb2d0b60d40 Mon Sep 17 00:00:00 2001 From: polwex Date: Sun, 22 Jun 2025 23:11:11 +0700 Subject: m --- app/neyre.hoon | 81 ++++++++++++++ app/zodiac.hoon | 86 --------------- desk.bill | 2 +- lib/coki.hoon | 92 ++++++++++++++++ lib/metamask.hoon | 147 +++++--------------------- sur/coki.hoon | 21 ++++ sur/neyre.hoon | 10 ++ sur/zodiac.hoon | 10 -- web/index.hoon | 8 +- web/login.hoon | 311 ++++-------------------------------------------------- web/router.hoon | 159 ++++++++++++++++------------ 11 files changed, 346 insertions(+), 581 deletions(-) create mode 100644 app/neyre.hoon delete mode 100644 app/zodiac.hoon create mode 100644 lib/coki.hoon create mode 100644 sur/coki.hoon create mode 100644 sur/neyre.hoon delete mode 100644 sur/zodiac.hoon diff --git a/app/neyre.hoon b/app/neyre.hoon new file mode 100644 index 0000000..e8f2c17 --- /dev/null +++ b/app/neyre.hoon @@ -0,0 +1,81 @@ +/- *neyre, coki +/+ dbug, cokil=coki +/= router /web/router +=| sessions:coki +=* sessions - +=| versioned-state +=* state - +%- agent:dbug +^- agent:gall +|_ =bowl:gall ++* this . + cokilib ~(. cokil [sessions bowl]) +++ on-init + ^- (quip card:agent:gall agent:gall) + :_ this + =/ weblib router(bowl bowl) + =/ weblib2 weblib(base-path /) + :~ (eyre-binding-card:weblib2 /) + (eyre-binding-card:weblib2 /[dap.bowl]) + == +:: +++ on-save + ^- vase + !>(state) +:: +++ on-load + |~ old-state=vase + ^- (quip card:agent:gall agent:gall) + =/ vs !<(versioned-state old-state) + ~& vs=vs + =. state + ?- -.vs + %0 vs + == + `this +:: +++ on-poke + |= [=mark =vase] + ^- (quip card:agent:gall agent:gall) + ?+ mark `this + %coki =^ cards sessions (handle-self-poke:cokilib !<(self-poke:coki vase)) + [cards this] + %handle-http-request + ~& "handling http" + =/ order !<([id=@ta req=inbound-request:eyre] vase) + =/ weblib ~(. router [bowl id.order req.order state sessions]) + =/ weblib2 weblib(base-path /) + :- route:weblib2 this + %noun ~& > s=sessions `this + == +:: +++ on-watch + |~ path + ^- (quip card:agent:gall agent:gall) + `this +:: +++ on-leave + |~ path + ^- (quip card:agent:gall agent:gall) + `this +:: +++ on-peek + |~ path + ^- (unit (unit cage)) + ~ +:: +++ on-agent + |~ [wire sign:agent:gall] + ^- (quip card:agent:gall agent:gall) + `this +:: +++ on-arvo + |~ [wire =sign-arvo] + ^- (quip card:agent:gall agent:gall) + `this +:: +++ on-fail + |~ [term tang] + ^- (quip card:agent:gall agent:gall) + `this +-- diff --git a/app/zodiac.hoon b/app/zodiac.hoon deleted file mode 100644 index 0718da1..0000000 --- a/app/zodiac.hoon +++ /dev/null @@ -1,86 +0,0 @@ -/- *zodiac -/+ dbug, metamask -/= router /web/router -=| versioned-state -=* state - -%- agent:dbug -^- agent:gall -|_ =bowl:gall -+* this . - metalib ~(. metamask [sessions.state bowl]) -++ on-init - ^- (quip card:agent:gall agent:gall) - :_ this - =/ weblib router(bowl bowl) - :~((eyre-binding-card:weblib /zodiac)) -:: -++ on-save - ^- vase - !>(state) -:: -++ on-load - |~ old-state=vase - ^- (quip card:agent:gall agent:gall) - =/ vs !<(versioned-state old-state) - ~& vs=vs - =. state - ?- -.vs - %0 vs - == - `this -:: -++ on-poke - |= [=mark =vase] - ^- (quip card:agent:gall agent:gall) - |^ - ?+ mark `this - %noun (on-poke-noun !<(* vase)) - %handle-http-request - =/ order !<([id=@ta req=inbound-request:eyre] vase) - =/ weblib ~(. router [bowl id.order req.order state]) - :- route:weblib this - == - ++ on-poke-noun |= a=* - ?: ?= [%logout @ @] a - ~& "loggingout" - =^ cards sessions.state (handle-logout:metalib +.a) - [cards this] - - =. sessions.state - ?+ a sessions.state - [%meta @t] (handle-meta:metalib +.a) - [%auth @p @ @uv] (handle-auth:metalib +.a) - == - `this - -- -:: -++ on-watch - |~ path - ^- (quip card:agent:gall agent:gall) - `this -:: -++ on-leave - |~ path - ^- (quip card:agent:gall agent:gall) - `this -:: -++ on-peek - |~ path - ^- (unit (unit cage)) - ~ -:: -++ on-agent - |~ [wire sign:agent:gall] - ^- (quip card:agent:gall agent:gall) - `this -:: -++ on-arvo - |~ [wire =sign-arvo] - ^- (quip card:agent:gall agent:gall) - `this -:: -++ on-fail - |~ [term tang] - ^- (quip card:agent:gall agent:gall) - `this --- diff --git a/desk.bill b/desk.bill index 32ce7b6..60b282a 100644 --- a/desk.bill +++ b/desk.bill @@ -1,2 +1,2 @@ -:~ %zodiac +:~ %neyre == diff --git a/lib/coki.hoon b/lib/coki.hoon new file mode 100644 index 0000000..39ea479 --- /dev/null +++ b/lib/coki.hoon @@ -0,0 +1,92 @@ +/- *coki +/+ server, sr=sortug +|_ [=sessions =bowl:gall] ++$ sess ^sessions +++ session-timeout + |% + ++ host ~d30 + ++ guest ~d7 + -- +++ session-hash + (~(raw og (shas %coki eny.bowl)) 128) +++ session-cookie-string |= [session=@ proven=@p desk=@tas] + ^- @t + =/ max-age=tape %- a-co:co + =/ its-a-me .=(src.bowl our.bowl) + =, session-timeout + (div (msec:milly ?:(its-a-me host guest)) 1.000) + %- crip + "urbcoki-{(trip desk)}-{(scow %p proven)}={(scow:parsing:sr %uv session)}; Path=/{(trip desk)}; HttpOnly; SameSite=Lax; Max-Age={max-age}" + :: "urbneo={(scow:parsing:sr %uv session)}; Path=/; Max-Age={max-age}" +++ validate-coki |= coki=@t ^- (unit @p) + =/ cookies (rush coki cock:de-purl:html) + ~& cookies=cookies + ?~ cookies ~ + =/ cokis=(list [@t @t]) u.cookies + |- + ?~ cokis ~ + =/ hd i.cokis + :: ?: (contains:string:sr (trip hd) "urbneo") + =/ hash (slaw:parsing:sr %uv +.hd) + ?~ hash $(cokis t.cokis) + =/ sess (~(get by users.sessions) u.hash) + ?~ sess $(cokis t.cokis) + `u.sess +++ send-self-poke |= poke=self-poke + ^- (list card:agent:gall) + :~ [%pass /gib %agent [our.bowl dap.bowl] %poke %coki !>(poke)] + == +:: handle-self-pokes +++ handle-self-poke |= poke=self-poke +?- -.poke + %meta :- ~ (handle-challenge +.poke) + %coki :- ~ (handle-coki +.poke) + %logout (handle-logout +.poke) +== +++ handle-challenge + |= new-challenge=@ ^- ^sessions + :: =? users.sessions + :: !(~(has by users.sessions) src.bowl) + :: (~(put by users.sessions) [src.bowl src.bowl]) + :: =? challenges.sessions + :: =(src.bowl (~(got by users.sessions) src.bowl)) + =. challenges.sessions + (~(put in challenges.sessions) new-challenge) + + sessions + + +++ parse-coki |= coki=@t + =/ cookies (rush coki cock:de-purl:html) + ?~ cookies users.sessions + =/ cokis=(list [@t @t]) u.cookies + |- + ?~ cokis users.sessions + =/ hd i.cokis + :: ?: (contains:string:sr (trip hd) "urbneo") + =/ hash (slaw:parsing:sr %uv +.hd) + ?~ hash $(cokis t.cokis) + =/ sess (~(get by users.sessions) u.hash) + ?~ sess $(cokis t.cokis) + (~(del by users.sessions) u.hash) + +++ logout |= [order-id=@t coki=@t redirect=@t] + (send-self-poke [%logout order-id coki redirect]) + +++ handle-logout |= [order-id=@t coki=@t redirect=@t] + ~& handling-logout=coki + =/ new-users (parse-coki coki) + :_ sessions(users new-users) + %+ give-simple-payload:app:server + order-id + (redirect:gen:server redirect) + + ++ handle-coki + |= [who=@p coki=@ =challenge] ^- ^sessions + :: ~& > "%ustj: Successful authentication of {} as {}." + =. users.sessions (~(put by users.sessions) coki who) + =. challenges.sessions (~(del in challenges.sessions) challenge) + sessions + + +-- diff --git a/lib/metamask.hoon b/lib/metamask.hoon index 3456692..09e2f66 100644 --- a/lib/metamask.hoon +++ b/lib/metamask.hoon @@ -1,70 +1,53 @@ -/+ naive, ethereum, server, sr=sortug -=> -|% -+$ challenges (set secret) -+$ secret @uv -+$ authorization - $: who=@p - =secret - adr=tape - sig=tape - == -:: +$ user-sessions (map coki=@ [proven=@p src=@p]) -+$ user-sessions (map coki=@ ship=@p) -+$ sessions - $: =challenges - users=user-sessions - == --- -|_ [=sessions =bowl:gall] -+$ sess ^sessions -:: state field to keep track of users logged with metamask - :: this goes on the router +/- coki +/+ naive, ethereum, sr=sortug, cokil=coki, server +|_ [=sessions:coki =bowl:gall] ++* cokilib ~(. cokil [sessions bowl]) + ++ serve-metamask-challenge |= eyre-id=@ta :: special-case MetaMask auth handling =/ new-challenge (sham [now eny]:bowl) - %+ weld (self-poke [%meta new-challenge]) + =/ jon (enjs-challenge new-challenge) + %+ weld (send-self-poke:cokilib [%meta new-challenge]) %+ give-simple-payload:app:server eyre-id - ^- simple-payload:http - :- :- 200 - ~[['Content-Type' 'application/json']] - `(as-octs:mimes:html (en:json:html (enjs-challenge new-challenge))) + (json-response:gen:server jon) + :: :: Modified from ~rabsef-bicrym's %mask by ~hanfel-dovned. ++ process-metamask-auth - |= [order-id=@t octs=(unit octs)] - ^- (list card:agent:gall) + |= [order-id=@t octs=(unit octs) redirect-path=@t base-slug=@t] + ^- (each (list card:agent:gall) [@ud @t]) =/ challenges challenges.sessions |^ - ?~ octs ~|(%empty-auth-request !!) + ?~ octs [%.n 403 %empty-auth-request] :: ?. =('auth' (cut 3 [0 4] q.u.octs)) :: *(list card:agent:gall) =/ jon (de:json:html q.u.octs) - ?~ jon ~|(%empty-auth-json !!) + ?~ jon [%.n 403 %empty-auth-json] =/ body=json u.jon =/ axn (dejs-action body) - =/ is-valid (validate who.axn secret.axn adr.axn sig.axn) - ~& >> signature-valid=[is-valid who.axn secret.axn adr.axn sig.axn] - ?. is-valid ~|(%bad-metamask-signature !!) + =/ is-valid (validate who.axn challenge.axn adr.axn sig.axn) + ~& >> signature-valid=[is-valid who.axn challenge.axn adr.axn sig.axn] + ?. is-valid [%.n 403 %bad-metamask-signature] - =/ coki-hash session-hash - =/ coki (session-cookie-string coki-hash who.axn) + =/ coki-hash session-hash:cokilib + =/ coki (session-cookie-string:cokilib coki-hash who.axn base-slug) + :- %.y %+ weld - (self-poke [%auth who.axn coki-hash secret.axn]) + (send-self-poke:cokilib [%coki who.axn coki-hash challenge.axn]) %+ give-simple-payload:app:server order-id ^- simple-payload:http :- :- 303 :~ ['set-cookie' coki] - ['location' '/zodiac'] + ['location' redirect-path] == =/ obj=json %- pairs:enjs:format :~([%login-ok [%b .y]]) `(as-octs:mimes:html (en:json:html obj)) ++ validate - |= [who=@p challenge=secret address=tape hancock=tape] + |= [who=@p =challenge:coki address=tape hancock=tape] ^- ? =/ addy (from-tape address) =/ cock (from-tape hancock) @@ -127,13 +110,13 @@ == ++ dejs-action |= jon=json - ^- authorization + ^- authorization:cokilib =, dejs:format %. jon %- ot :: :~ [%who (se %p)] :~ [%who ni] - [%secret (se %uv)] + [%challenge (se %uv)] [%address sa] [%signature sa] == @@ -145,87 +128,5 @@ %- pairs :~ [%challenge [%s (scot %uv chal)]] == - ++ self-poke - |= noun=* - ^- (list card:agent:gall) - :~ [%pass /gib %agent [our.bowl dap.bowl] %poke %noun !>(noun)] - == - - :: these are the poke handlers - ++ handle-meta - |= new-challenge=@ ^- ^sessions - :: =? users.sessions - :: !(~(has by users.sessions) src.bowl) - :: (~(put by users.sessions) [src.bowl src.bowl]) - :: =? challenges.sessions - :: =(src.bowl (~(got by users.sessions) src.bowl)) - =. challenges.sessions - (~(put in challenges.sessions) new-challenge) - sessions - ++ handle-auth - |= [who=@p coki=@ =secret] ^- ^sessions - :: ~& > "%ustj: Successful authentication of {} as {}." - =. users.sessions (~(put by users.sessions) coki who) - =. challenges.sessions (~(del in challenges.sessions) secret) - sessions - - ++ session-timeout - |% - ++ auth ~d30 - ++ guest ~d7 - -- - ++ session-hash - (~(raw og (shas %coki eny.bowl)) 128) - ++ session-cookie-string |= [session=@ proven=@p] - ^- @t - =/ max-age=tape %- a-co:co - =/ its-a-me .=(src.bowl our.bowl) - =, session-timeout - (div (msec:milly ?:(its-a-me auth guest)) 1.000) - %- crip - "urbneo-{(scow %p proven)}={(scow:parsing:sr %uv session)}; Path=/; Max-Age={max-age}" - :: "urbneo={(scow:parsing:sr %uv session)}; Path=/; Max-Age={max-age}" - ++ validate-coki |= coki=@t ^- (unit @p) - ~& >> validating-coki=coki - =/ cookies (rush coki cock:de-purl:html) - ~& cookies=cookies - ?~ cookies ~ - =/ cokis=(list [@t @t]) u.cookies - |- - ?~ cokis ~ - =/ hd i.cokis - :: ?: (contains:string:sr (trip hd) "urbneo") - ~& key=-.hd - =/ hash (slaw:parsing:sr %uv +.hd) - ?~ hash $(cokis t.cokis) - ~& hash=`@uv`u.hash - =/ sess (~(get by users.sessions) u.hash) - ~& sess=sess - ?~ sess $(cokis t.cokis) - `u.sess - ++ parse-coki |= coki=@t - =/ cookies (rush coki cock:de-purl:html) - ?~ cookies users.sessions - =/ cokis=(list [@t @t]) u.cookies - |- - ?~ cokis users.sessions - =/ hd i.cokis - :: ?: (contains:string:sr (trip hd) "urbneo") - ~& key=-.hd - =/ hash (slaw:parsing:sr %uv +.hd) - ?~ hash $(cokis t.cokis) - =/ sess (~(get by users.sessions) u.hash) - ?~ sess $(cokis t.cokis) - (~(del by users.sessions) u.hash) - - ++ logout |= [order-id=@t coki=@t] - (self-poke [%logout order-id coki]) - ++ handle-logout |= [order-id=@t coki=@t] - ~& handling-logout=coki - =/ new-users (parse-coki coki) - :_ sessions(users new-users) - %+ give-simple-payload:app:server - order-id - (redirect:gen:server '/zodiac/login') -- diff --git a/sur/coki.hoon b/sur/coki.hoon new file mode 100644 index 0000000..37adcd0 --- /dev/null +++ b/sur/coki.hoon @@ -0,0 +1,21 @@ +|% ++$ challenges (set challenge) ++$ challenge @uv ++$ authorization + $: who=@p + challenge=@uv + adr=tape + sig=tape + == +:: +$ user-sessions (map coki=@ [proven=@p src=@p]) ++$ user-sessions (map coki=@ ship=@p) ++$ sessions + $: =challenges + users=user-sessions + == ++$ self-poke + $% [%meta challenge=@uv] + [%coki who=@p coki=@ challenge=@uv] + [%logout eyre-id=@t coki=@t redirect=@t] + == +-- diff --git a/sur/neyre.hoon b/sur/neyre.hoon new file mode 100644 index 0000000..f004def --- /dev/null +++ b/sur/neyre.hoon @@ -0,0 +1,10 @@ +/+ metalib=metamask +|% ++$ versioned-state + $% state-0 +== ++$ state-0 + $: %0 + placeholder=@ + == +-- diff --git a/sur/zodiac.hoon b/sur/zodiac.hoon deleted file mode 100644 index 3d20610..0000000 --- a/sur/zodiac.hoon +++ /dev/null @@ -1,10 +0,0 @@ -/+ metalib=metamask -|% -+$ versioned-state - $% state-0 -== -+$ state-0 - $: %0 - sessions=sess:metalib - == --- 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 "" "" @@ -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 "" "" -++ 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] -- -- cgit v1.2.3