mirror of
https://github.com/urbit/shrub.git
synced 2025-01-05 19:46:50 +03:00
Merge remote-tracking branches 'ault011/instagram', 'ohaitch/markdown-hax-url', 'ault011/digitalocean' and 'ohaitch/procedural-security-drivers'
Conflicts: sec/com/dropboxapi.hoon Many security drivers in this merge. A small markdown fix also snuck into one of the octopus' arms as parent 3.
This commit is contained in:
commit
3eb7493671
@ -1,6 +1,6 @@
|
||||
:: Twitter daemon
|
||||
::
|
||||
:::: /hook/core/twit/app
|
||||
:::: /hoon/twit/app
|
||||
::
|
||||
/+ twitter, talk
|
||||
::
|
||||
|
@ -1,5 +1,5 @@
|
||||
::
|
||||
:::: /hoon/curl/gen
|
||||
:::: /hoon/curl-hiss/gen
|
||||
::
|
||||
/? 310
|
||||
/- sole
|
||||
|
@ -1,6 +1,6 @@
|
||||
:: Display twitter feed
|
||||
::
|
||||
:::: /hook/core/twitter-feed/app
|
||||
:::: /hoon/twitter-feed/twit/gen
|
||||
::
|
||||
/+ sh-utils
|
||||
!:
|
||||
|
@ -1,8 +1,33 @@
|
||||
!:
|
||||
=+ keys=@t
|
||||
|= bal/(bale keys)
|
||||
?~ key.bal
|
||||
~|(%basic-auth-no-key ~_(leaf+"Run |init-auth-basic {<`path`dom.bal>}" !!))
|
||||
=+ aut=authorization+(cat 3 'Basic ' key.bal)
|
||||
~& aut=`{@tas @t}`aut
|
||||
|=(a/hiss [%send %_(a q.q (~(add ja q.q.a) -.aut +.aut))])
|
||||
:: Basic authentication
|
||||
::
|
||||
:::: /hoon/basic-auth/lib
|
||||
::
|
||||
|%
|
||||
++ keys @t
|
||||
--
|
||||
::
|
||||
::::
|
||||
::
|
||||
|_ {bal/(bale keys) $~}
|
||||
++ auth
|
||||
|%
|
||||
++ header
|
||||
^- cord
|
||||
?~ key.bal
|
||||
~_ leaf+"Run |init-auth-basic {<`path`dom.bal>}"
|
||||
~|(%basic-auth-no-key !!)
|
||||
(cat 3 'Basic ' key.bal)
|
||||
--
|
||||
::
|
||||
++ add-auth-header
|
||||
|= a/hiss ^- hiss
|
||||
~& auth+(earn p.a)
|
||||
%_(a q.q (~(add ja q.q.a) %authorization header:auth))
|
||||
::
|
||||
++ standard
|
||||
|%
|
||||
++ out-adding-header
|
||||
|= a/hiss ^- sec-move
|
||||
[%send (add-auth-header a)]
|
||||
--
|
||||
--
|
||||
|
@ -210,7 +210,7 @@
|
||||
|= a/tape ^- tape
|
||||
?~ a ~
|
||||
?: ?| [?=(^ q)]:(alp 1^1 a)
|
||||
(~(has in (silt "!*'();:@&=+$,/?/%.~_")) i.a) :: XX reparse
|
||||
(~(has in (silt "#!*'();:@&=+$,/?/%.~_")) i.a) :: XX reparse
|
||||
==
|
||||
[i.a $(a t.a)]
|
||||
(weld (urle (trip i.a)) $(a t.a))
|
||||
|
25
lib/hep-to-cab.hoon
Normal file
25
lib/hep-to-cab.hoon
Normal file
@ -0,0 +1,25 @@
|
||||
:: rewrite query string keys
|
||||
::
|
||||
:::: /hoon/hep-to-cab/lib
|
||||
::
|
||||
/? 310
|
||||
::
|
||||
:::: ~fyr
|
||||
::
|
||||
=< term
|
||||
|%
|
||||
++ gsub :: replace chars
|
||||
|= {a/@t b/@t t/@t}
|
||||
^- @t
|
||||
?: =('' t) t
|
||||
%+ mix (lsh 3 1 $(t (rsh 3 1 t)))
|
||||
=+ c=(end 3 1 t)
|
||||
?:(=(a c) b c)
|
||||
::
|
||||
++ term |=(a/^term (gsub '-' '_' a)) :: single atom
|
||||
++ path |=(a/^path (turn a term)) :: path elements
|
||||
++ quay :: query string keys
|
||||
|= a/^quay ^+ a
|
||||
%+ turn a
|
||||
|=({p/@t q/@t} [(term p) q])
|
||||
--
|
49
lib/interpolate.hoon
Normal file
49
lib/interpolate.hoon
Normal file
@ -0,0 +1,49 @@
|
||||
:: /foo/:bar/baz interpolation syntax
|
||||
::
|
||||
:::: /hoon/interpolate/lib
|
||||
::
|
||||
/? 310
|
||||
::
|
||||
:::: ~fyr
|
||||
::
|
||||
=< into-url
|
||||
|%
|
||||
++ parse-url
|
||||
|= a/$@(cord:purl purl) ^- purl
|
||||
?^ a a
|
||||
~| bad-url+a
|
||||
(rash a auri:epur)
|
||||
::
|
||||
++ add-query
|
||||
|= {a/$@(@t purl) b/quay} ^- purl
|
||||
?@ a $(a (parse-url a)) :: deal with cord
|
||||
a(r (weld r.a b))
|
||||
::
|
||||
++ into-url
|
||||
|= {a/$@(cord purl) b/(unit hart) c/(list (pair term knot))}
|
||||
^- purl
|
||||
?@ a $(a (parse-url a)) :: deal with cord
|
||||
%_ a
|
||||
p ?^(b u.b p.a)
|
||||
q.q (into-path q.q.a c)
|
||||
==
|
||||
::
|
||||
++ into-path :: [/a/:b/c [%b 'foo']~] -> /a/foo/c
|
||||
=+ replacable=|=(a/knot `(unit term)`(rush a ;~(pfix col sym)))
|
||||
|= {a/path b/(list (pair term knot))} ^- path
|
||||
?~ a ?~(b ~ ~|(unused-values+b !!))
|
||||
=+ (replacable i.a)
|
||||
?~ - [i.a $(a t.a)] :: literal value
|
||||
?~ b ~|(no-value+u !!)
|
||||
?. =(u p.i.b) ~|(mismatch+[u p.i.b] !!)
|
||||
[q.i.b $(a t.a, b t.b)]
|
||||
::
|
||||
++ into-path-partial :: [/a/:b/c [d+'bar' b+'foo']~] -> [/a/foo/c [d+'bar']~]
|
||||
|= {pax/path quy/quay} ^- {path quay}
|
||||
=+ ^= inline :: required names
|
||||
%- ~(gas in *(set term))
|
||||
(murn pax replacable:into-path)
|
||||
=^ inter quy
|
||||
(skid quy |=({a/knot @} (~(has in inline) a)))
|
||||
[(into-path pax inter) quy]
|
||||
--
|
295
lib/oauth1.hoon
295
lib/oauth1.hoon
@ -2,6 +2,7 @@
|
||||
::
|
||||
:::: /hoon/oauth1/lib
|
||||
::
|
||||
/+ interpolate, hep-to-cab
|
||||
|%
|
||||
++ keys cord:{key/@t sec/@t} :: app key pair
|
||||
++ token :: user keys
|
||||
@ -15,20 +16,7 @@
|
||||
::::
|
||||
::
|
||||
|%
|
||||
++ fass :: rewrite quay
|
||||
|= a/quay
|
||||
%+ turn a
|
||||
|= {p/@t q/@t} ^+ +<
|
||||
[(gsub '-' '_' p) q]
|
||||
::
|
||||
++ gsub :: replace chars
|
||||
|= {a/@t b/@t t/@t}
|
||||
^- @t
|
||||
?: =('' t) t
|
||||
%+ mix (lsh 3 1 $(t (rsh 3 1 t)))
|
||||
=+ c=(end 3 1 t)
|
||||
?:(=(a c) b c)
|
||||
::
|
||||
++ parse-url parse-url:interpolate
|
||||
++ join
|
||||
|= {a/cord b/(list cord)}
|
||||
?~ b ''
|
||||
@ -43,7 +31,7 @@
|
||||
++ to-header
|
||||
|= a/quay ^- tape
|
||||
%+ joint ", "
|
||||
(turn a |=({k/@t v/@t} `tape`~[k '="' v '"'])) :: normalized later
|
||||
(turn a |=({k/@t v/@t} `tape`~[k '="' v '"'])) :: normalized later
|
||||
::
|
||||
:: partial tail:earn for sorting
|
||||
++ encode-pairs
|
||||
@ -52,7 +40,7 @@
|
||||
|= {k/@t v/@t} ^- tape
|
||||
:(weld (urle (trip k)) "=" (urle (trip v)))
|
||||
::
|
||||
++ parse-pairs :: x-form-urlencoded
|
||||
++ parse-pairs :: x-form-urlencoded
|
||||
|= bod/(unit octs) ^- quay-enc
|
||||
~| %parsing-body
|
||||
?~ bod ~
|
||||
@ -60,6 +48,7 @@
|
||||
::
|
||||
++ post-quay
|
||||
|= {a/purl b/quay} ^- hiss
|
||||
=. b (quay:hep-to-cab b)
|
||||
=- [a %post - ?~(b ~ (some (tact +:(tail:earn b))))]
|
||||
(my content-type+['application/x-www-form-urlencoded']~ ~)
|
||||
::
|
||||
@ -69,60 +58,30 @@
|
||||
=- (mean (flop `tang`[>a< -]))
|
||||
(turn (lore (crip b)) |=(c/cord leaf+(trip c)))
|
||||
::
|
||||
++ dbg-post `purl`[`hart`[| `6.000 [%& /localhost]] `pork``/testing `quay`/]
|
||||
++ bad-response |=(a/@u ?:(=(2 (div a 100)) | ~&(bad-httr+a &)))
|
||||
++ quay-keys |-($@(knot {$ $})) :: improper tree
|
||||
++ grab-quay :: ?=({@t @t @t} ((grab-quay *httr) %key1 %key2 %key3))
|
||||
|* {a/httr b/quay-keys}
|
||||
~| bad-quay+r.a
|
||||
=+ quy=(rash q:(need r.a) yquy:urlp)
|
||||
~| quy
|
||||
=+ all=(malt quy)
|
||||
++ grab-quay :: ?=({@t @t @t} (grab-quay r:*httr %key1 %key2 %key3))
|
||||
|* {a/(unit octs) b/quay-keys}
|
||||
=+ ~| bad-quay+a
|
||||
c=(rash q:(need `(unit octs)`a) yquy:urlp)
|
||||
~| grab-quay+[c b]
|
||||
=+ all=(malt c)
|
||||
%. b
|
||||
|* b/quay-keys
|
||||
?@ b ~|(b (~(got by all) b))
|
||||
[(..$ -.b) (..$ +.b)]
|
||||
::
|
||||
++ parse-url
|
||||
|= a/$@(cord:purl purl) ^- purl
|
||||
?^ a a
|
||||
~| bad-url+a
|
||||
(rash a auri:epur)
|
||||
::
|
||||
++ interpolate-url
|
||||
|= {a/$@(cord purl) b/(unit hart) c/(list (pair term knot))}
|
||||
^- purl
|
||||
?@ a $(a (parse-url a)) :: deal with cord
|
||||
%_ a
|
||||
p ?^(b u.b p.a)
|
||||
q.q (interpolate-path q.q.a c)
|
||||
==
|
||||
::
|
||||
++ interpolate-path :: [/a/:b/c [%b 'foo']~] -> /a/foo/c
|
||||
|= {a/path b/(list (pair term knot))} ^- path
|
||||
?~ a ?~(b ~ ~|(unused-values+b !!))
|
||||
=+ (rush i.a ;~(pfix col sym))
|
||||
?~ - [i.a $(a t.a)] :: not interpolable
|
||||
?~ b ~|(no-value+u !!)
|
||||
?. =(u p.i.b) ~|(mismatch+[u p.i.b] !!)
|
||||
[q.i.b $(a t.a, b t.b)]
|
||||
--
|
||||
!:
|
||||
::::
|
||||
::
|
||||
|= {request/$@(@t purl) dialog/$@(@t purl) code-exchange/$@(@t purl)}
|
||||
=+ :+ dialog-url=(parse-url dialog)
|
||||
exchange-url=(parse-url code-exchange)
|
||||
token-reqs-url=(parse-url request)
|
||||
|_ {done/* (bale keys) tok/token}
|
||||
+- core-move $^({sec-move _done} sec-move) :: stateful
|
||||
|_ {(bale keys) tok/token}
|
||||
++ consumer-key key:decode-keys
|
||||
++ consumer-secret sec:decode-keys
|
||||
++ decode-keys :: XX from bale w/ typed %jael
|
||||
^- {key/@t sec/@t $~}
|
||||
?. =(~ `@`key)
|
||||
~| %oauth-bad-keys
|
||||
((hard {cid/@t cis/@t $~}) (lore key))
|
||||
((hard {key/@t sec/@t $~}) (lore key))
|
||||
%+ mean-wall %oauth-no-keys
|
||||
"""
|
||||
Run |init-oauth1 {<`path`dom>}
|
||||
@ -130,41 +89,40 @@
|
||||
{(trip oauth-callback)}
|
||||
"""
|
||||
::
|
||||
++ exchange-token
|
||||
|= a/$@(@t purl) ^- hiss
|
||||
(post-quay (parse-url a) ~)
|
||||
::
|
||||
++ request-token
|
||||
|= a/$@(@t purl) ^- hiss
|
||||
(post-quay (parse-url a) oauth-callback+oauth-callback ~)
|
||||
::
|
||||
++ our-host .^(hart %e /(scot %p our)/host/fake)
|
||||
++ oauth-callback
|
||||
~& [%oauth-warning "Make sure this urbit ".
|
||||
"is running on {(earn our-host `~ ~)}"]
|
||||
%- crip %- earn
|
||||
%^ interpolate-url 'https://our-host/~/ac/:domain/:user/in'
|
||||
%^ interpolate 'https://our-host/~/ac/:domain/:user/in'
|
||||
`our-host
|
||||
:~ domain+(join '.' (flop dom))
|
||||
user+(scot %ta usr)
|
||||
==
|
||||
::
|
||||
++ auth-url
|
||||
|= url/$@(@t purl) ^- purl
|
||||
%+ add-query:interpolate url
|
||||
%- quay:hep-to-cab
|
||||
?. ?=({$request-token ^} tok)
|
||||
~|(%no-token-for-dialog !!)
|
||||
:- oauth-token+oauth-token.tok
|
||||
?~(usr ~ [screen-name+usr]~)
|
||||
::
|
||||
++ toke-url
|
||||
|= quy/quay ^- purl
|
||||
%_ dialog-url
|
||||
r (fass ?~(usr quy [screen-name+usr quy]))
|
||||
==
|
||||
++ grab-token-response
|
||||
|= a/httr ^- {tok/@t sec/@t}
|
||||
(grab-quay r.a 'oauth_token' 'oauth_token_secret')
|
||||
::
|
||||
++ token-exchange (post-quay exchange-url ~)
|
||||
++ token-request (post-quay token-reqs-url oauth-callback+oauth-callback ~)
|
||||
::
|
||||
:: use token to sign authorization header. requires:
|
||||
:: ++ res (res-handle-reqt handle-token) :: take request token
|
||||
:: ++ bak (res-save-access handle-token) :: obtained access token
|
||||
++ out-math
|
||||
^- $-(hiss $%({$send hiss} {$show purl}))
|
||||
?~ tok
|
||||
_[%send (add-auth ~ token-request)]
|
||||
?: ?=($request-token -.tok)
|
||||
_[%show (toke-url oauth-token+oauth-token.tok ~)]
|
||||
|= a/hiss ^- {$send hiss}
|
||||
[%send (add-auth [oauth-token+oauth-token.tok]~ a)]
|
||||
::
|
||||
++ in-oauth-token
|
||||
|= a/quay ^- sec-move
|
||||
++ check-token-quay
|
||||
|= a/quay ^+ %&
|
||||
=. a (sort a aor)
|
||||
?. ?=({{$'oauth_token' oauth-token/@t} {$'oauth_verifier' @t} $~} a)
|
||||
~|(no-token+a !!)
|
||||
@ -173,54 +131,15 @@
|
||||
?. =(oauth-token.tok oauth-token.q.i.a)
|
||||
~| wrong-token+[id=usr q.i.a]
|
||||
~|(%multiple-tokens-unsupported !!)
|
||||
[%send (add-auth a token-exchange)]
|
||||
%&
|
||||
::
|
||||
++ token-response ['oauth_token' 'oauth_token_secret']
|
||||
+- bak-save-access
|
||||
|= handle/$-(token _done)
|
||||
%- (res-parse token-response)
|
||||
|= access-token/{tok/@t sec/@t} ^- core-move
|
||||
[[%redo ~] (handle `token`[%access-token access-token])]
|
||||
::
|
||||
+- res-parse
|
||||
|* para/quay-keys
|
||||
|= handle/$-(_?~(para ~ (grab-quay *httr para)) core-move)
|
||||
|= a/httr ^- core-move
|
||||
?: (bad-response p.a)
|
||||
[%give a]
|
||||
:: [%redo ~] :: handle 4xx?
|
||||
(handle (grab-quay a para))
|
||||
::
|
||||
++ res-give |=(a/httr [%give a])
|
||||
+- res-handle-reqt
|
||||
|= handle/$-(token _done) ^- $-(httr core-move)
|
||||
?~ tok
|
||||
(res-save-reqt handle)
|
||||
res-give
|
||||
::
|
||||
+- res-save-reqt
|
||||
|= handle/$-(token _done) ^- $-(httr core-move)
|
||||
%- (res-parse token-response 'oauth_callback_confirmed')
|
||||
|= {request-token/{tok/@t sec/@t} cof/term} ^- core-move
|
||||
?. =(%true cof)
|
||||
~|(%callback-rejected !!)
|
||||
[[%redo ~] (handle `token`[%request-token request-token])]
|
||||
::
|
||||
::
|
||||
++ add-auth
|
||||
=< |= $: auq/quay :: extra oauth parameters
|
||||
hiz/{purl meth hed/math (unit octs)}
|
||||
==
|
||||
^- hiss
|
||||
~& add-auth+(earn -.hiz)
|
||||
%_ hiz
|
||||
hed (~(add ja hed.hiz) %authorization (authorization auq hiz))
|
||||
==
|
||||
++ auth
|
||||
|%
|
||||
++ authorization
|
||||
++ header
|
||||
|= {auq/quay url/purl med/meth math bod/(unit octs)}
|
||||
^- cord
|
||||
=^ quy url [r.url url(r ~)] :: query string handled separately
|
||||
=. auq (fass (weld auq auth-quay))
|
||||
=. auq (quay:hep-to-cab (weld auq computed-query))
|
||||
=+ ^- qen/quay-enc :: semi-encoded for sorting
|
||||
%+ weld (parse-pairs bod)
|
||||
(encode-pairs (weld auq quy))
|
||||
@ -229,7 +148,7 @@
|
||||
=. auq ['oauth_signature'^(crip (urle sig)) auq]
|
||||
(crip "OAuth {(to-header auq)}")
|
||||
::
|
||||
++ auth-quay
|
||||
++ computed-query
|
||||
^- quay
|
||||
:~ oauth-consumer-key+consumer-key
|
||||
oauth-nonce+(scot %uw (shaf %non eny))
|
||||
@ -256,4 +175,136 @@
|
||||
(trip ?^(tok token-secret.tok ''))
|
||||
==
|
||||
--
|
||||
::
|
||||
++ add-auth-header
|
||||
|= {extra/quay request/{url/purl meth hed/math (unit octs)}}
|
||||
^- hiss
|
||||
:: =. url.request [| `6.000 [%& /localhost]] :: for use with unix nc
|
||||
~& add-auth-header+(earn url.request)
|
||||
%_ request
|
||||
hed
|
||||
(~(add ja hed.request) %authorization (header:auth extra request))
|
||||
==
|
||||
:: expected semantics, to be copied and modified if anything doesn't work
|
||||
++ standard
|
||||
|* {done/* save/$-(token *)} :: save/$-(token _done)
|
||||
|%
|
||||
++ save ^-($-(token _done) ^save) :: shadow(type canary)
|
||||
++ core-move $^({sec-move _done} sec-move) :: stateful
|
||||
::
|
||||
:: use token to sign authorization header. expects:
|
||||
:: ++ res res-handle-request-token :: save request token
|
||||
:: ++ in (in-token-exhange 'http://...') :: handle callback
|
||||
++ out-add-header
|
||||
|= {request-url/$@(@t purl) dialog-url/$@(@t purl)}
|
||||
::
|
||||
|= a/hiss ^- $%({$send hiss} {$show purl})
|
||||
?- tok
|
||||
$~
|
||||
[%send (add-auth-header ~ (request-token request-url))]
|
||||
::
|
||||
{$access-token ^}
|
||||
[%send (add-auth-header [oauth-token+oauth-token.tok]~ a)]
|
||||
::
|
||||
{$request-token ^}
|
||||
[%show (auth-url dialog-url)]
|
||||
==
|
||||
::
|
||||
:: If no token is saved, the http response we just got has a request token
|
||||
++ res-handle-request-token
|
||||
|= a/httr ^- core-move
|
||||
?^ tok [%give a]
|
||||
?. =(%true (grab-quay r.a 'oauth_callback_confirmed'))
|
||||
~|(%callback-rejected !!)
|
||||
=+ request-token=(grab-token-response a)
|
||||
[[%redo ~] (save `token`[%request-token request-token])]
|
||||
::
|
||||
:: Exchange oauth_token in query string for access token. expects:
|
||||
:: ++ bak bak-save-token :: save access token
|
||||
++ in-exchange-token
|
||||
|= exchange-url/$@(@t purl)
|
||||
::
|
||||
|= a/quay ^- sec-move
|
||||
?> (check-token-quay a)
|
||||
[%send (add-auth-header a (exchange-token exchange-url))]
|
||||
::
|
||||
:: If a valid access token has been returned, save it
|
||||
++ bak-save-token
|
||||
|= a/httr ^- core-move
|
||||
?: (bad-response p.a)
|
||||
[%give a] :: [%redo ~] :: handle 4xx?
|
||||
=+ access-token=(grab-token-response a)
|
||||
[[%redo ~] (save `token`[%access-token access-token])]
|
||||
--
|
||||
--
|
||||
::
|
||||
:::: Example "standard" sec/ core:
|
||||
::
|
||||
::
|
||||
:: ::
|
||||
:: :::: /hoon/my-api/com/sec
|
||||
:: ::
|
||||
:: /+ oauth1
|
||||
:: ::
|
||||
:: ::::
|
||||
:: ::
|
||||
:: |_ {bal/(bale keys:oauth1) tok/token:oauth1}
|
||||
:: ++ aut (~(standard oauth1 bal tok) . |=(tok/token:oauth1 +>(tok tok)))
|
||||
:: ++ out
|
||||
:: %+ out-add-header:aut
|
||||
:: request-token='https://my-api.com/request_token'
|
||||
:: oauth-dialog='https://my-api.com/authorize'
|
||||
:: ::
|
||||
:: ++ res res-handle-request-token:aut
|
||||
:: ++ in
|
||||
:: %- in-exchagne-token:aut
|
||||
:: exchange-url='https://my-api.com/access_token'
|
||||
:: ::
|
||||
:: ++ bak bak-save-token:aut
|
||||
:: --
|
||||
::
|
||||
::
|
||||
:::: Equivalent imperative code:
|
||||
::
|
||||
::
|
||||
:: ::
|
||||
:: :::: /hoon/my-api/com/sec
|
||||
:: ::
|
||||
:: /+ oauth1
|
||||
:: ::
|
||||
:: ::::
|
||||
:: ::
|
||||
:: |_ {bal/(bale keys:oauth1) tok/token:oauth1}
|
||||
:: ++ aut ~(. oauth1 bal tok)
|
||||
:: ++ out :: add header
|
||||
:: =+ aut
|
||||
:: |= req/hiss ^- $%({$send hiss} {$show purl})
|
||||
:: ?~ tok
|
||||
:: [%send (add-auth-header ~ (request-token 'https://my-api.com/request_token'))]
|
||||
:: ?: ?=($request-token -.tok)
|
||||
:: [%show (auth-url 'https://my-api.com/authorize')]
|
||||
:: [%send (add-auth-header [oauth-token+ouath-token.tok]~ req)]
|
||||
:: ::
|
||||
:: ++ res :: handle request token
|
||||
:: =+ aut
|
||||
:: |= res/httr ^- $%({{$redo $~} _..res} {$give httr})
|
||||
:: ?^ tok [%give a]
|
||||
:: ?> =(%true (grab r.res 'oauth_callback_confirmed'))
|
||||
:: =. tok [%request-token (grab-token-response res)]
|
||||
:: [[%redo ~] ..res]
|
||||
:: ::
|
||||
:: ++ in :: exchange token
|
||||
:: =+ aut
|
||||
:: |= inp/quay ^- {$send hiss}
|
||||
:: ?> (check-token-quay inp)
|
||||
:: :- %send
|
||||
:: (add-auth-header inp (exchange-token 'https://my-api.com/access_token'))
|
||||
:: ::
|
||||
:: ++ bak :: save token
|
||||
:: =+ aut
|
||||
:: |= bak/httr ^- $%({{$redo $~} _..bak} {$give httr})
|
||||
:: ?: (bad-response bak) [%give bak]
|
||||
:: =. tok [%access-token (grab-token-response res)]
|
||||
:: [[%redo ~] ..bak]
|
||||
:: --
|
||||
::
|
||||
|
441
lib/oauth2.hoon
441
lib/oauth2.hoon
@ -1,80 +1,47 @@
|
||||
:: OAuth 2.0 %authorization
|
||||
::
|
||||
:::: /hoon/oauth2/lib
|
||||
::
|
||||
/+ hep-to-cab, interpolate
|
||||
|%
|
||||
++ fass :: rewrite quay
|
||||
|= a/quay
|
||||
%+ turn a
|
||||
|= {p/@t q/@t} ^+ +<
|
||||
[(gsub '-' '_' p) q]
|
||||
::
|
||||
++ gsub :: replace chars
|
||||
|= {a/@t b/@t t/@t}
|
||||
^- @t
|
||||
?: =('' t) t
|
||||
%+ mix (lsh 3 1 $(t (rsh 3 1 t)))
|
||||
=+ c=(end 3 1 t)
|
||||
?:(=(a c) b c)
|
||||
::
|
||||
++ join
|
||||
++ parse-url parse-url:interpolate
|
||||
++ join
|
||||
|= {a/cord b/(list cord)}
|
||||
?~ b ''
|
||||
(rap 3 |-([i.b ?~(t.b ~ [a $(b t.b)])]))
|
||||
::
|
||||
++ post-quay
|
||||
|= {a/purl b/quay} ^- hiss
|
||||
=. b (quay:hep-to-cab b)
|
||||
=- [a %post - ?~(b ~ (some (tact +:(tail:earn b))))]
|
||||
(my content-type+['application/x-www-form-urlencoded']~ ~)
|
||||
::
|
||||
++ mean-wall !.
|
||||
|= {a/term b/tape} ^+ !!
|
||||
=- (mean (flop `tang`[>a< -]))
|
||||
(turn (lore (crip b)) |=(c/cord leaf+(trip c)))
|
||||
::
|
||||
++ dbg-post `purl`[`hart`[| `6.000 [%& /localhost]] `pork``/testing `quay`/]
|
||||
++ bad-response |=(a/@u ?:(=(2 (div a 100)) | ~&(bad-httr+a &)))
|
||||
++ grab-json
|
||||
|* {a/httr b/fist:jo}
|
||||
~| bad-json+r.a
|
||||
~| (poja q:(need r.a))
|
||||
(need (;~(biff poja b) q:(need r.a)))
|
||||
::
|
||||
++ parse-url
|
||||
|= a/$@(cord:purl purl) ^- purl
|
||||
?^ a a
|
||||
~| bad-url+a
|
||||
(rash a auri:epur)
|
||||
::
|
||||
++ interpolate-url
|
||||
|= {a/$@(cord purl) b/(unit hart) c/(list (pair term knot))}
|
||||
^- purl
|
||||
?@ a $(a (parse-url a)) :: deal with cord
|
||||
%_ a
|
||||
p ?^(b u.b p.a)
|
||||
q.q (interpolate-path q.q.a c)
|
||||
==
|
||||
::
|
||||
++ interpolate-path :: [/a/:b/c [%b 'foo']~] -> /a/foo/c
|
||||
|= {a/path b/(list (pair term knot))} ^- path
|
||||
?~ a ?~(b ~ ~|(unused-values+b !!))
|
||||
=+ (rush i.a ;~(pfix col sym))
|
||||
?~ - [i.a $(a t.a)] :: not interpolable
|
||||
?~ b ~|(no-value+u !!)
|
||||
?. =(u p.i.b) ~|(mismatch+[u p.i.b] !!)
|
||||
[q.i.b $(a t.a, b t.b)]
|
||||
--
|
||||
::
|
||||
::::
|
||||
::
|
||||
|%
|
||||
++ token ?($~ @t)
|
||||
++ refresh {tok/token needed/@da pending/_`?`|}
|
||||
++ refresh {tok/token expiry/@da pending/_`?`|}
|
||||
++ both-tokens {token refresh}
|
||||
++ keys cord:{cid/@t cis/@t}
|
||||
++ core-move |*(a/* $^({sec-move _a} sec-move)) ::here's a change
|
||||
--
|
||||
::
|
||||
::::
|
||||
::
|
||||
|= {dialog/$@(cord:purl purl) code-exchange/$@(cord:purl purl)}
|
||||
=+ :+ state-usr=|
|
||||
dialog-url=(parse-url dialog)
|
||||
exchange-url=(parse-url code-exchange)
|
||||
|_ {(bale keys) scope/(list cord)}
|
||||
=+ state-usr=|
|
||||
|_ {(bale keys) tok/token}
|
||||
++ client-id cid:decode-keys
|
||||
++ client-secret cis:decode-keys
|
||||
++ decode-keys :: XX from bale w/ typed %jael
|
||||
@ -89,55 +56,32 @@
|
||||
{(trip redirect-uri)}
|
||||
"""
|
||||
::
|
||||
++ our-host .^(hart %e /(scot %p our)/host/fake)
|
||||
++ auth-url
|
||||
|= {scopes/(list @t) url/$@(@t purl)} ^- purl
|
||||
~& [%oauth-warning "Make sure this urbit ".
|
||||
"is running on {(earn our-host `~ ~)}"]
|
||||
^- purl
|
||||
%_ dialog-url
|
||||
r
|
||||
%+ welp r.dialog-url
|
||||
%- fass
|
||||
:~ state+?.(state-usr '' (pack usr /''))
|
||||
client-id+client-id
|
||||
redirect-uri+redirect-uri
|
||||
scope+(join ' ' scope)
|
||||
==
|
||||
%+ add-query:interpolate url
|
||||
%- quay:hep-to-cab
|
||||
:~ state+?.(state-usr '' (pack usr /''))
|
||||
client-id+client-id
|
||||
redirect-uri+redirect-uri
|
||||
scope+(join ' ' scopes)
|
||||
==
|
||||
::
|
||||
++ our-host .^(hart %e /(scot %p our)/host/fake)
|
||||
++ redirect-uri
|
||||
%- crip %- earn
|
||||
%^ interpolate-url 'https://our-host/~/ac/:domain/:user/in'
|
||||
%^ interpolate 'https://our-host/~/ac/:domain/:user/in'
|
||||
`our-host
|
||||
:~ domain+(join '.' (flop dom))
|
||||
user+?:(state-usr '_state' (scot %ta usr))
|
||||
==
|
||||
::
|
||||
::
|
||||
++ out-filtered
|
||||
|= {tok/token aut/$-(hiss hiss)}
|
||||
|= a/hiss ^- sec-move
|
||||
?~(tok [%show auth-url] [%send (aut a)])
|
||||
::
|
||||
++ out-quay
|
||||
|= {nam/knot tok/token}
|
||||
%+ out-filtered tok
|
||||
|=(a/hiss %_(a r.p :_(r.p.a nam^`@t`tok)))
|
||||
::
|
||||
++ out-math
|
||||
|= ber/token
|
||||
=+ hed=(cat 3 'Bearer ' `@t`ber)
|
||||
%+ out-filtered ber
|
||||
|= a/hiss ^+ a
|
||||
:: =. p.a dbg-post
|
||||
%_(a q.q (~(add ja q.q.a) %authorization hed))
|
||||
::
|
||||
++ toke-req
|
||||
|= {grant-type/cord quy/quay} ^- {$send hiss}
|
||||
:+ %send exchange-url
|
||||
:+ %post (malt ~[content-type+~['application/x-www-form-urlencoded']])
|
||||
=- `(tact +:(tail:earn -))
|
||||
%- fass
|
||||
++ request-token
|
||||
|= {a/$@(@t purl) grant-type/cord quy/quay} ^- hiss
|
||||
%+ post-quay (parse-url a)
|
||||
%- quay:hep-to-cab
|
||||
%+ welp quy
|
||||
:~ client-id+client-id
|
||||
client-secret+client-secret
|
||||
@ -145,60 +89,295 @@
|
||||
grant-type+grant-type
|
||||
==
|
||||
::
|
||||
++ in-code
|
||||
|= a/quay ^- sec-move
|
||||
=+ code=~|(%no-code (~(got by (malt a)) %code))
|
||||
(toke-req 'authorization_code' code+code ~)
|
||||
++ request-token-by-code
|
||||
|=({a/$@(@t purl) b/@t} (request-token a 'authorization_code' code+b ~))
|
||||
::
|
||||
++ token-type 'token_type'^(cu cass sa):jo
|
||||
++ expires-in 'expires_in'^ni:jo
|
||||
++ access-token 'access_token'^so:jo
|
||||
++ refresh-token 'refresh_token'^so:jo
|
||||
++ bak-save-access
|
||||
|* {done/* handle/$-(cord:token *)} :: $+(token _done)
|
||||
%- (bak-parse done access-token ~)
|
||||
|=(tok/cord:token [[%redo ~] (handle tok)])
|
||||
++ grab-token
|
||||
|= a/httr ^- axs/@t
|
||||
(grab-json a (ot 'access_token'^so ~):jo)
|
||||
::
|
||||
++ bak-parse
|
||||
|* {done/* parse/(pole {knot fist}:jo)}
|
||||
|= handle/$-(_?~(parse ~ (need *(ot:jo parse))) (core-move done))
|
||||
|= a/httr ^- (core-move done)
|
||||
?: (bad-response p.a)
|
||||
[%give a]
|
||||
:: [%redo ~] :: handle 4xx?
|
||||
(handle (grab-json a (ot:jo parse)))
|
||||
++ grab-expiring-token
|
||||
|= a/httr ^- {axs/@t exp/@u}
|
||||
(grab-json a (ot 'access_token'^so 'expires_in'^ni ~):jo)
|
||||
::
|
||||
++ res-give |=(a/httr [%give a])
|
||||
++ grab-both-tokens
|
||||
|= a/httr ^- {axs/@t exp/@u ref/@t}
|
||||
(grab-json a (ot 'access_token'^so 'expires_in'^ni 'refresh_token'^so ~):jo)
|
||||
::
|
||||
++ auth
|
||||
?~ tok ~|(%no-bearer-token !!)
|
||||
|%
|
||||
++ header `cord`(cat 3 'Bearer ' `@t`tok)
|
||||
++ query `cord`tok
|
||||
--
|
||||
::
|
||||
++ add-auth-header
|
||||
|= request/{url/purl meth hed/math (unit octs)}
|
||||
^+ request
|
||||
:: =. p.url.request [| `6.000 [%& /localhost]] :: for use with unix nc
|
||||
~& add-auth-header+(earn url.request)
|
||||
request(hed (~(add ja hed.request) %authorization header:auth))
|
||||
::
|
||||
++ add-auth-query
|
||||
|= {token-name/cord request/{url/purl meth math (unit octs)}}
|
||||
^+ request
|
||||
:: =. p.url.request [| `6.000 [%& /localhost]] :: for use with unix nc
|
||||
~& add-auth-query+(earn url.request)
|
||||
request(r.url [[token-name query:auth] r.url.request])
|
||||
::
|
||||
++ re
|
||||
|* cor/* :: XX redundant with *export, but type headaches
|
||||
|_ {ref/refresh export/$-(refresh _cor)}
|
||||
++ out-fix-expired
|
||||
|= default/$-(hiss sec-move)
|
||||
^- $-(hiss (core-move cor))
|
||||
?~ tok.ref default
|
||||
?. (lth needed.ref (add now ~m59.s30))
|
||||
default
|
||||
|= a/hiss
|
||||
:_ (export ref(pending &))
|
||||
(toke-req 'refresh_token' refresh-token+tok.ref ~)
|
||||
|_ ref/refresh
|
||||
++ needs-refresh ?~(tok.ref | is-expired)
|
||||
++ is-expired (lth expiry.ref (add now ~m5))
|
||||
++ update
|
||||
|= exp/@u ^+ ref
|
||||
ref(pending |, expiry (add now (mul ~s1 exp)))
|
||||
::
|
||||
++ res-handle-refreshed
|
||||
|= {handle-access/_=>(cor |=(@t +>)) default/$-(httr sec-move)}
|
||||
^- $-(httr (core-move cor))
|
||||
?. pending.ref default
|
||||
%- (bak-parse cor expires-in access-token ~)
|
||||
|= {exp/@u tok/axs/@t} ^- {sec-move _cor}
|
||||
=. +>.handle-access
|
||||
(export tok.ref (add now (mul ~s1 exp)) |)
|
||||
[[%redo ~] (handle-access axs.tok)]
|
||||
++ update-if-needed
|
||||
|= exchange-url/$@(@t purl)
|
||||
^- {(unit hiss) refresh}
|
||||
?~ tok.ref `ref
|
||||
?. is-expired `ref
|
||||
:_ ref(pending &)
|
||||
`(request-token exchange-url 'refresh_token' refresh-token+tok.ref ~)
|
||||
--
|
||||
::
|
||||
:: expected semantics, to be copied and modified if anything doesn't work
|
||||
++ standard
|
||||
|* {done/* save/$-(token *)}
|
||||
|%
|
||||
++ save ^-($-(token _done) ^save) :: shadow(type canary)
|
||||
++ core-move $^({sec-move _done} sec-move) :: stateful
|
||||
::
|
||||
++ bak-save-tokens
|
||||
|= handle-access/_=>(cor |=(@t +>))
|
||||
%- (bak-parse cor expires-in access-token refresh-token ~)
|
||||
|= {exp/@u tok/{axs/@t ref/@t}} ^- {sec-move _cor}
|
||||
=. +>.handle-access
|
||||
(export ref.tok (add now (mul ~s1 exp)) |)
|
||||
[[%redo ~] (handle-access axs.tok)]
|
||||
:: Insert token into query string. expects:
|
||||
:: ++ in (in-code-to-token 'http://...') :: handle callback
|
||||
++ out-add-query-param
|
||||
|= {token-name/knot scopes/(list cord) dialog/$@(@t purl)}
|
||||
::
|
||||
|= a/hiss ^- $%({$send hiss} {$show purl})
|
||||
?~ tok [%show (auth-url scopes dialog)]
|
||||
[%send (add-auth-query token-name a)]
|
||||
::
|
||||
:: Add token as a header. expects:
|
||||
:: ++ in (in-code-to-token 'http://...') :: handle callback
|
||||
++ out-add-header
|
||||
|= {scopes/(list cord) dialog/$@(@t purl)}
|
||||
::
|
||||
|= a/hiss ^- sec-move
|
||||
?~ tok [%show (auth-url scopes dialog)]
|
||||
[%send (add-auth-header a)]
|
||||
::
|
||||
:: Exchange code in query string for access token. expects:
|
||||
:: ++ bak bak-save-token :: save access token
|
||||
++ in-code-to-token
|
||||
|= exchange-url/$@(@t purl)
|
||||
::
|
||||
|= a/quay ^- sec-move
|
||||
=+ code=~|(%no-code (~(got by (malt a)) %code))
|
||||
[%send (request-token-by-code exchange-url code)]
|
||||
::
|
||||
:: If an access token has been returned, save it
|
||||
++ bak-save-token
|
||||
|= a/httr ^- core-move
|
||||
?: (bad-response p.a)
|
||||
[%give a] :: [%redo ~] :: handle 4xx?
|
||||
[[%redo ~] (save `token`(grab-token a))]
|
||||
--
|
||||
::
|
||||
++ standard-refreshing
|
||||
|* {done/* ref/refresh save/$-({token refresh} *)}
|
||||
=+ s=(standard done |=(tok/token (save tok ref)))
|
||||
|%
|
||||
++ save ^-($-(both-tokens _done) ^save) :: shadow(type canary)
|
||||
++ core-move $^({sec-move _done} sec-move) :: stateful
|
||||
::
|
||||
:: See ++out-add-query-param:standard
|
||||
:: Refresh token if we have an expired one, ask for authentication if none is present,
|
||||
:: insert auth token into the query string if it's valid. expects:
|
||||
:: ++ in (in-code-to-token 'http://...') :: handle callback
|
||||
:: ++ res res-save-after-refresh
|
||||
++ out-refresh-or-add-query-param
|
||||
|= {exchange/$@(@t purl) s-args/{knot (list cord) $@(@t purl)}}
|
||||
::
|
||||
|= a/hiss ^- core-move
|
||||
=^ upd ref (~(update-if-needed re ref) exchange)
|
||||
?^ upd [[%send u.upd] (save tok ref)]
|
||||
%.(a (out-add-query-param.s s-args))
|
||||
::
|
||||
:: See ++out-add-header:standard
|
||||
:: Refresh token if we have an expired one, ask for authentication if none is present,
|
||||
:: add token as a header if it's valid. expects:
|
||||
:: ++ in (in-code-to-token 'http://...') :: handle callback
|
||||
:: ++ res res-save-after-refresh
|
||||
++ out-refresh-or-add-header
|
||||
|= {exchange/$@(@t purl) s-args/{(list cord) dialog/$@(@t purl)}}
|
||||
::
|
||||
|= a/hiss ^- core-move
|
||||
=^ upd ref (~(update-if-needed re ref) exchange)
|
||||
?^ upd [[%send u.upd] (save tok ref)]
|
||||
%.(a (out-add-header.s s-args))
|
||||
::
|
||||
:: If the last request refreshed the access token, save it.
|
||||
++ res-save-after-refresh
|
||||
|= a/httr ^- core-move
|
||||
?. pending.ref [%give a]
|
||||
=+ `{axs/token exp/@u}`(grab-expiring-token a)
|
||||
=. ref (~(update re ref) exp)
|
||||
[[%redo ~] (save axs ref)]
|
||||
::
|
||||
:: Exchange code in query string for access and refresh tokens. expects:
|
||||
:: ++ bak bak-save-both-tokens :: save access token
|
||||
++ in-code-to-token in-code-to-token.s
|
||||
::
|
||||
:: If valid access and refresh tokens have been returned, save them
|
||||
++ bak-save-both-tokens
|
||||
|= a/httr ^- core-move
|
||||
=+ `{axs/token exp/@u ref-new/token}`(grab-both-tokens a)
|
||||
=. tok.ref ref-new
|
||||
=. ref (~(update re ref) exp)
|
||||
[[%redo ~] (save axs ref)]
|
||||
--
|
||||
--
|
||||
::
|
||||
:: XX move-me
|
||||
::
|
||||
::
|
||||
:::: Example "standard" sec/ core:
|
||||
::
|
||||
::
|
||||
:: ::
|
||||
:: :::: /hoon/my-api/com/sec
|
||||
:: ::
|
||||
:: /+ oauth2
|
||||
:: ::
|
||||
:: ::::
|
||||
:: ::
|
||||
:: |_ {bal/(bale keys:oauth2) tok/token:oauth2}
|
||||
:: ++ aut (~(standard oauth2 bal tok) . |=(tok/token:oauth2 +>(tok tok)))
|
||||
:: ++ out
|
||||
:: %+ out-add-header:aut scope=/full
|
||||
:: oauth-dialog='https://my-api.com/authorize'
|
||||
:: ::
|
||||
:: ++ in
|
||||
:: %- in-code-to-token:aut
|
||||
:: exchange-url='https://my-api.com/access_token'
|
||||
:: ::
|
||||
:: ++ bak bak-save-token:aut
|
||||
:: --
|
||||
::
|
||||
::
|
||||
:::: Equivalent imperative code:
|
||||
::
|
||||
::
|
||||
:: ::
|
||||
:: :::: /hoon/my-api/com/sec
|
||||
:: ::
|
||||
:: /+ oauth2
|
||||
:: ::
|
||||
:: ::::
|
||||
:: ::
|
||||
:: |_ {bal/(bale keys:oauth2) tok/token:oauth2}
|
||||
:: ++ aut ~(. oauth2 bal tok)
|
||||
:: ++ out :: add header
|
||||
:: =+ aut
|
||||
:: |= req/hiss ^- $%({$send hiss} {$show purl})
|
||||
:: ?~ tok
|
||||
:: [%show (auth-url scope=/full 'https://my-api.com/authorize')]
|
||||
:: [%send (add-auth-header req)]
|
||||
:: ::
|
||||
:: ++ in :: code to token
|
||||
:: =+ aut
|
||||
:: |= inp/quay ^- {$send hiss}
|
||||
:: =+ code=~|(%no-code (~(got by (malt inp)) %code))
|
||||
:: [%send (request-token-by-code 'https://my-api.com/access_token' code)]
|
||||
:: ::
|
||||
:: ++ bak :: save token
|
||||
:: =+ aut
|
||||
:: |= bak/httr ^- $%({{$redo $~} _..bak} {$give httr})
|
||||
:: ?: (bad-response bak) [%give bak]
|
||||
:: =. tok (grab-token bak)
|
||||
:: [[%redo ~] ..bak]
|
||||
:: --
|
||||
::
|
||||
::: :::
|
||||
::::: ::
|
||||
::: :::
|
||||
::
|
||||
:::: Example "standard-refreshing" sec/ core:
|
||||
::
|
||||
::
|
||||
:: ::
|
||||
:: :::: /hoon/my-api/com/sec
|
||||
:: ::
|
||||
:: /+ oauth2
|
||||
:: ::
|
||||
:: ::::
|
||||
:: ::
|
||||
:: |_ {bal/(bale keys:oauth2) tok/token:oauth2 ref/refresh:oauth2}
|
||||
:: ++ aut
|
||||
:: %^ ~(standard-refreshing oauth2 bal tok) . ref
|
||||
:: |=({tok/token ref/refresh}:oauth2 +>(tok tok, ref ref))
|
||||
:: ::
|
||||
:: ++ exchange-url 'https://my-api.com/access_token'
|
||||
:: ++ out
|
||||
:: %^ out-refresh-or-add-header:aut exchange-url
|
||||
:: scope=/full
|
||||
:: oauth-dialog='https://my-api.com/authorize'
|
||||
:: ::
|
||||
:: ++ res res-save-after-refresh:aut
|
||||
:: ++ in (in-code-to-token:aut exchange-url)
|
||||
:: ++ bak bak-save-both-tokens:aut
|
||||
:: --
|
||||
::
|
||||
::
|
||||
:::: Equivalent imperative code:
|
||||
::
|
||||
::
|
||||
:: ::
|
||||
:: :::: /hoon/my-api/com/sec
|
||||
:: ::
|
||||
:: /+ oauth2
|
||||
:: ::
|
||||
:: ::::
|
||||
:: ::
|
||||
:: |_ {bal/(bale keys:oauth2) axs/token:oauth2 ref/refresh:oauth2}
|
||||
:: ++ aut ~(. oauth2 bal axs)
|
||||
:: ++ exchange-url 'https://my-api.com/access_token'
|
||||
:: ++ out :: refresh or add header
|
||||
:: =+ aut
|
||||
:: |= req/hiss ^- $^({{$send hiss} _..out} $%({$send hiss} {$show purl}))
|
||||
:: ?~ axs
|
||||
:: [%show (auth-url scope=/full 'https://my-api.com/authorize')]
|
||||
:: =^ upd ref (~(update-if-needed re ref) exchange-url)
|
||||
:: ?^ upd [[%send u.upd] ..out]
|
||||
:: [%send (add-auth-header req)]
|
||||
:: ::
|
||||
:: ++ res :: save after refresh
|
||||
:: =+ aut
|
||||
:: |= a/httr ^- $^({{$redo $~} _..res} {$give httr})
|
||||
:: ?. pending.ref [%give a]
|
||||
:: =+ `{axs/token exp/@u}`(grab-expiring-token a)
|
||||
:: [[%redo ~] ..out(axs axs, ref (~(update re ref) exp))]
|
||||
:: ::
|
||||
:: ++ in :: exchange token
|
||||
:: =+ aut
|
||||
:: |= inp/quay ^- {$send hiss}
|
||||
:: =+ code=~|(%no-code (~(got by (malt inp)) %code))
|
||||
:: [%send (request-token-by-code exchange-url code)]
|
||||
::
|
||||
:: ++ bak :: save both tokens
|
||||
:: =+ aut
|
||||
:: |= a/httr ^- {{$redo $~} _..res}
|
||||
:: =+ `{axs/token exp/@u ref-new/token}`(grab-both-tokens a)
|
||||
:: =. tok.ref ref-new
|
||||
:: [[%redo ~] ..bak(axs axs, ref (~(update re ref) exp))]
|
||||
:: ::
|
||||
:: ::
|
||||
:: ++ bak
|
||||
:: =+ aut
|
||||
:: |= bak/httr ^- $%({{$redo $~} _..bak} {$give httr})
|
||||
:: ?: (bad-response bak) [%give bak]
|
||||
:: =. tok (grab-token bak)
|
||||
:: [[%redo ~] ..bak]
|
||||
:: --
|
||||
::
|
||||
|
@ -4,49 +4,18 @@
|
||||
::
|
||||
/? 314
|
||||
/- twitter
|
||||
/+ interpolate, hep-to-cab
|
||||
=+ sur-twit:^twitter :: XX
|
||||
!:
|
||||
:::: functions
|
||||
::
|
||||
|%
|
||||
++ fass :: rewrite path
|
||||
|= a/path
|
||||
%+ turn a
|
||||
|=(b/@t (gsub '-' '_' b))
|
||||
::
|
||||
++ gsub :: replace chars
|
||||
|= {a/@t b/@t t/@t}
|
||||
^- @t
|
||||
?: =('' t) t
|
||||
%+ mix (lsh 3 1 $(t (rsh 3 1 t)))
|
||||
=+ c=(end 3 1 t)
|
||||
?:(=(a c) b c)
|
||||
::
|
||||
++ join
|
||||
|= {a/char b/(list @t)} ^- @t
|
||||
%+ rap 3
|
||||
?~ b ~
|
||||
|-(?~(t.b b [i.b a $(b t.b)]))
|
||||
::
|
||||
++ interpolate-some :: [/a/:b/c [d+'bar' b+'foo']~] -> [/a/foo/c [d+'bar']~]
|
||||
|= {pax/path quy/quay} ^- {path quay}
|
||||
=+ ^= inline :: required names
|
||||
%- ~(gas in *(set term))
|
||||
(murn pax replacable:interpolate-path)
|
||||
=^ inter quy
|
||||
(skid quy |=({a/knot @} (~(has in inline) a)))
|
||||
[(interpolate-path pax inter) quy]
|
||||
::
|
||||
++ interpolate-path :: [/a/:b/c [%b 'foo']~] -> /a/foo/c
|
||||
=+ replacable=|=(a/knot `(unit term)`(rush a ;~(pfix col sym)))
|
||||
|= {a/path b/(list (pair term knot))} ^- path
|
||||
?~ a ?~(b ~ ~|(unused-values+b !!))
|
||||
=+ (replacable i.a)
|
||||
?~ - [i.a $(a t.a)] :: literal value
|
||||
?~ b ~|(no-value+u !!)
|
||||
?. =(u p.i.b) ~|(mismatch+[u p.i.b] !!)
|
||||
[q.i.b $(a t.a, b t.b)]
|
||||
::
|
||||
++ valve :: produce request
|
||||
|= {med/?($get $post) pax/path quy/quay}
|
||||
^- hiss
|
||||
@ -82,7 +51,7 @@
|
||||
++ parse :: json reparsers
|
||||
|%
|
||||
++ ce |*({a/_* b/fist:jo} (cu:jo |=(c/a c) b)) :: output type
|
||||
++ fasp |*(a/{@tas *} [(gsub '-' '_' -.a) +.a]) :: XX usable electroplating
|
||||
++ fasp |*(a/{@tas *} [(hep-to-cab -.a) +.a]) :: XX usable electroplating
|
||||
++ user (cook crip (plus ;~(pose aln cab)))
|
||||
++ mean (ot errors+(ar (ot message+so code+ni ~)) ~):jo
|
||||
++ stat
|
||||
@ -149,12 +118,13 @@
|
||||
quy/quay
|
||||
==
|
||||
^- {path quay}
|
||||
%+ interpolate-some (fass pax)
|
||||
%+ into-path-partial:interpolate
|
||||
(path:hep-to-cab pax)
|
||||
=- (weld - quy)
|
||||
%+ turn ban
|
||||
|= p/param
|
||||
^- {@t @t}
|
||||
:- (gsub '-' '_' -.p)
|
||||
:- (hep-to-cab -.p)
|
||||
?+ -.p p.p :: usually plain text
|
||||
?($source-id $target-id) (tid:print p.p)
|
||||
?($follow $id $name $user-id) (lid:print p.p)
|
||||
|
@ -1,6 +1,6 @@
|
||||
:: Twitter status
|
||||
::
|
||||
:::: /hoon/stat/twit/mar
|
||||
:::: /hoon/status/twit/mar
|
||||
::
|
||||
/+ twitter, httr-to-json
|
||||
|_ stat:twitter
|
||||
|
41
sec/com/asana.hoon
Normal file
41
sec/com/asana.hoon
Normal file
@ -0,0 +1,41 @@
|
||||
:: Test url +https://app.asana.com/api/1.0/users/me
|
||||
::
|
||||
:::: /hoon/asana/com/sec
|
||||
::
|
||||
/+ oauth2
|
||||
::
|
||||
::::
|
||||
::
|
||||
|%
|
||||
++ dialog-url 'https://app.asana.com/-/oauth_authorize?response_type=code'
|
||||
++ exchange-url 'https://app.asana.com/-/oauth_token'
|
||||
--
|
||||
::
|
||||
::::
|
||||
::
|
||||
|_ {bal/(bale keys:oauth2) tok/token:oauth2}
|
||||
:: ++aut is a "standard oauth2" core, which implements the
|
||||
:: most common handling of oauth2 semantics. see lib/oauth2 for more details,
|
||||
:: and examples at the bottom of the file.
|
||||
++ aut (~(standard oauth2 bal tok) . |=(tok/token:oauth2 +>(tok tok)))
|
||||
++ out (out-add-header:aut scope=~ dialog-url)
|
||||
::
|
||||
++ in (in-code-to-token:aut exchange-url)
|
||||
++ bak bak-save-token:aut
|
||||
--
|
||||
:: create a developer app by logging into https://app.asana.com/, and clicking
|
||||
:: "My Profile Settings" > Apps > "Manage my developer apps"
|
||||
|
||||
:: Be sure to be on https://localhost:8443 and to have registered
|
||||
:: 'http://localhost:8443/~/ac/asana.com/~./in' as the redirect URI.
|
||||
:: (If unable to change port number of ship, change the redirect URI port in %eyre)
|
||||
|
||||
:: |init-oauth2 /com/asana
|
||||
|
||||
:: Enter this sample command to get your user information:
|
||||
:: +https://app.asana.com/api/1.0/users/me
|
||||
|
||||
:: Before you receive the response, you'll have to clink on the link.
|
||||
:: If you successfully auth, you should receive the response in the dojo.
|
||||
|
||||
|
40
sec/com/digitalocean.hoon
Normal file
40
sec/com/digitalocean.hoon
Normal file
@ -0,0 +1,40 @@
|
||||
:: Test url +https://api.digitalocean.com/v2/account
|
||||
::
|
||||
:::: /hoon/digitalocean/com/sec
|
||||
::
|
||||
/+ oauth2
|
||||
::
|
||||
::::
|
||||
::
|
||||
|%
|
||||
++ dialog-url 'https://cloud.digitalocean.com/v1/oauth/authorize?response_type=code'
|
||||
++ exchange-url 'https://cloud.digitalocean.com/v1/oauth/token'
|
||||
--
|
||||
::
|
||||
::::
|
||||
::
|
||||
|_ {bal/(bale keys:oauth2) tok/token:oauth2}
|
||||
:: ++aut is a "standard oauth2" core, which implements the
|
||||
:: most common handling of oauth2 semantics. see lib/oauth2 for more details,
|
||||
:: and examples at the bottom of the file.
|
||||
++ aut (~(standard oauth2 bal tok) . |=(tok/token:oauth2 +>(tok tok)))
|
||||
++ out (out-add-header:aut scope=~[%read %write] dialog-url)
|
||||
::
|
||||
++ in (in-code-to-token:aut exchange-url)
|
||||
++ bak bak-save-token:aut
|
||||
--
|
||||
:: create a developer app on https://cloud.digitalocean.com/settings/api/applications/new
|
||||
:: to get a client id and secret
|
||||
|
||||
:: Be sure to be on https://localhost:8443 and to have registered
|
||||
:: 'http://localhost:8443/~/ac/digitalocean.com/~./in' as the redirect URI.
|
||||
:: (If unable to change port number of ship, change the redirect URI port in %eyre)
|
||||
|
||||
:: |init-oauth2 |init-oauth2 /com/digitalocean
|
||||
|
||||
:: Enter home this sample command to get your user information:
|
||||
:: +https://api.digitalocean.com/v2/account
|
||||
:: Before you receive the response, you'll have to clink on the link.
|
||||
:: If you successfully auth, you should receive the response in the dojo.
|
||||
|
||||
|
@ -1,22 +1,41 @@
|
||||
:: Test url +https://api.dropboxapi.com/2/files/list_folder
|
||||
:: Test url +https://api.dropboxapi.com/2/users/get_current_account &json ~
|
||||
::
|
||||
:::: /hoon/dropboxapi/com/sec
|
||||
::
|
||||
/+ oauth2
|
||||
::
|
||||
::::
|
||||
::
|
||||
=+ ^= aut
|
||||
%+ oauth2
|
||||
'https://www.dropbox.com/1/oauth2/authorize?response_type=code'
|
||||
'https://api.dropboxapi.com/1/oauth2/token'
|
||||
|_ {(bale keys:oauth2) tok/token.aut}
|
||||
++ aut ~(. ^aut +<- /)
|
||||
++ out
|
||||
|= a/hiss
|
||||
=; mow ~& db-authorized+mow mow
|
||||
%. a
|
||||
(out-math:aut tok)
|
||||
++ in in-code:aut
|
||||
++ bak (bak-save-access:aut . |=(tok/token:aut +>(tok tok)))
|
||||
|%
|
||||
++ dialog-url 'https://www.dropbox.com/1/oauth2/authorize?response_type=code'
|
||||
++ exchange-url 'https://api.dropboxapi.com/1/oauth2/token'
|
||||
--
|
||||
::
|
||||
::
|
||||
::::
|
||||
::
|
||||
|_ {bal/(bale keys:oauth2) tok/token:oauth2}
|
||||
:: ++aut is a "standard oauth2" core, which implements the
|
||||
:: most common handling of oauth2 semantics. see lib/oauth2 for more details,
|
||||
:: and examples at the bottom of the file.
|
||||
++ aut (~(standard oauth2 bal tok) . |=(tok/token:oauth2 +>(tok tok)))
|
||||
++ out (out-add-header:aut scope=~ dialog-url)
|
||||
::
|
||||
++ in (in-code-to-token:aut exchange-url)
|
||||
++ bak bak-save-token:aut
|
||||
--
|
||||
:: create a developer app on https://www.dropbox.com/developers-v1/apps to get a
|
||||
:: client id and secret.
|
||||
|
||||
:: Be sure to be on https://localhost:8443 and to have registered
|
||||
:: 'http://localhost:8443/~/ac/dropboxapi.com/~./in' as the redirect URI.
|
||||
:: (If unable to change port number of ship, change the redirect URI port in %eyre)
|
||||
|
||||
:: |init-oauth2 |init-oauth2 /com/dropbox
|
||||
|
||||
:: Enter this sample command to show your user info:
|
||||
:: +https://api.dropboxapi.com/2/users/get_current_account &json ~
|
||||
|
||||
:: Before you receive the response, you'll have to click on the link in the
|
||||
:: dojo to authenticate yourself.
|
||||
|
||||
:: You should receive a response listing the contents of that directory.
|
||||
|
@ -6,21 +6,37 @@
|
||||
::
|
||||
::::
|
||||
::
|
||||
=+ ^= aut
|
||||
%+ oauth2
|
||||
dialog='https://www.facebook.com/dialog/oauth?response_type=code'
|
||||
exchange='https://graph.facebook.com/v2.3/oauth/access_token'
|
||||
|_ {bal/(bale keys.aut) access-token/token.aut}
|
||||
++ auth ~(. aut bal /'user_about_me'/'user_posts')
|
||||
++ out (out-quay:auth key='access_token' value=access-token)
|
||||
++ in in-code:auth
|
||||
++ bak
|
||||
%- (bak-parse:auth . access-token.aut expires-in.aut ~)
|
||||
|= {access-token/@t expires-in/@u}
|
||||
?: (lth expires-in ^~((div ~d7 ~s1))) :: short-lived token
|
||||
%^ toke-req:auth grant-type='fb_exchange_token'
|
||||
[key='fb_exchange_token' value=access-token]
|
||||
~
|
||||
[[%redo ~] ..bak(access-token access-token)]
|
||||
::++ wyp ~
|
||||
|%
|
||||
++ dialog-url 'https://www.facebook.com/dialog/oauth?response_type=code'
|
||||
++ exchange-url 'https://graph.facebook.com/v2.3/oauth/access_token'
|
||||
--
|
||||
::
|
||||
::::
|
||||
::
|
||||
|_ {bal/(bale keys:oauth2) access-token/token:oauth2}
|
||||
:: ++aut is a "standard oauth2" core, which implements the
|
||||
:: most common handling of oauth2 semantics. see lib/oauth2 for more details,
|
||||
:: and examples at the bottom of the file.
|
||||
++ aut
|
||||
%+ ~(standard oauth2 bal access-token) .
|
||||
|=(access-token/token:oauth2 +>(access-token access-token))
|
||||
::
|
||||
++ out
|
||||
%^ out-add-query-param:aut 'access_token'
|
||||
scope=~['user_about_me' 'user_posts']
|
||||
dialog-url
|
||||
::
|
||||
++ in (in-code-to-token:aut exchange-url)
|
||||
::
|
||||
++ bak
|
||||
|= a/httr ^- core-move:aut
|
||||
?: (bad-response:aut p.a)
|
||||
[%give a] :: [%redo ~] :: handle 4xx?
|
||||
=+ `{access-token/@t expires-in/@u}`(grab-expiring-token:aut a)
|
||||
?. (lth expires-in ^~((div ~d7 ~s1))) :: short-lived token
|
||||
[[%redo ~] ..bak(access-token access-token)]
|
||||
:- %send
|
||||
%^ request-token:aut exchange-url
|
||||
grant-type='fb_exchange_token'
|
||||
[key='fb_exchange_token' value=access-token]~
|
||||
--
|
||||
|
@ -5,5 +5,6 @@
|
||||
/+ basic-auth
|
||||
!:
|
||||
|_ {bal/(bale keys:basic-auth) $~}
|
||||
++ out (basic-auth bal)
|
||||
++ aut ~(standard basic-auth bal ~)
|
||||
++ out out-adding-header:aut
|
||||
--
|
||||
|
@ -2,7 +2,7 @@
|
||||
::
|
||||
:::: /hoon/googleapis/com/sec
|
||||
::
|
||||
/+ oauth2
|
||||
/+ oauth2, interpolate, hep-to-cab
|
||||
::
|
||||
::::
|
||||
::
|
||||
@ -18,40 +18,38 @@
|
||||
++ auth-usr
|
||||
|= usr/iden
|
||||
=+ lon=(fall (slaw %t usr) usr)
|
||||
=< .(state-usr &)
|
||||
%- oauth2
|
||||
:_ exchange='https://www.googleapis.com/oauth2/v4/token'
|
||||
^= dialog
|
||||
%* . (need (epur 'https://accounts.google.com/o/oauth2/v2/auth'))
|
||||
r
|
||||
%- fass:oauth2
|
||||
:~ login-hint+?~(lon '' (crip (rash lon suffix-email)))
|
||||
access-type+%offline
|
||||
response-type+%code
|
||||
prompt+%consent
|
||||
==
|
||||
%+ add-query:interpolate 'https://accounts.google.com/o/oauth2/v2/auth'
|
||||
%- quay:hep-to-cab
|
||||
:~ login-hint+?~(lon '' (crip (rash lon suffix-email)))
|
||||
access-type+%offline
|
||||
response-type+%code
|
||||
prompt+%consent
|
||||
==
|
||||
--
|
||||
!:
|
||||
::::
|
||||
::
|
||||
|_ {bal/(bale keys:oauth2) user-state}
|
||||
++ auth-re ~(. (re:auth .) ref |=(a/_ref +>(ref a)))
|
||||
++ auth ~(. (auth-usr usr.bal) bal scopes)
|
||||
++ scopes
|
||||
:~ 'https://mail.google.com'
|
||||
'https://www.googleapis.com/auth/plus.me'
|
||||
'https://www.googleapis.com/auth/userinfo.email'
|
||||
==
|
||||
::
|
||||
++ out (out-fix-expired:auth-re (out-math:auth ber))
|
||||
++ res |=(a/httr ((res-handle-refreshed:auth-re save-access res-give:auth) a))
|
||||
::
|
||||
++ save-access |=(a/cord:[token:oauth2] +>(ber a))
|
||||
::
|
||||
++ in
|
||||
|= a/quay
|
||||
(in-code:auth a)
|
||||
++ bak |=(a/httr ((bak-save-tokens:auth-re save-access) a))
|
||||
++ upd *user-state
|
||||
++ exchange-url 'https://www.googleapis.com/oauth2/v4/token'
|
||||
--
|
||||
!:
|
||||
::::
|
||||
::
|
||||
|_ {bal/(bale keys:oauth2) own/user-state}
|
||||
:: ++auth is a "standard refreshing oauth2" core, which implements the
|
||||
:: most common handling of oauth2 semantics. see lib/oauth2 for more details,
|
||||
:: and examples at the bottom of the file.
|
||||
++ auth
|
||||
=+ a=~(standard-refreshing oauth2 bal ber.own)
|
||||
(a(state-usr &) ..auth ref.own |=(a/user-state ..auth(own a)))
|
||||
::
|
||||
++ out (out-refresh-or-add-header:auth exchange-url scopes dialog-url)
|
||||
++ dialog-url (auth-usr usr.bal)
|
||||
::
|
||||
++ res res-save-after-refresh:auth
|
||||
::
|
||||
++ in (in-code-to-token:auth exchange-url)
|
||||
++ bak bak-save-both-tokens:auth
|
||||
:: ++ upd *user-state
|
||||
--
|
||||
|
42
sec/com/instagram.hoon
Normal file
42
sec/com/instagram.hoon
Normal file
@ -0,0 +1,42 @@
|
||||
:: Test url +https://api.instagram.com/v1/users/self
|
||||
::
|
||||
:::: /hoon/instagram/com/sec
|
||||
::
|
||||
/+ oauth2
|
||||
::
|
||||
::::
|
||||
::
|
||||
|%
|
||||
++ dialog-url 'https://api.instagram.com/oauth/authorize?response_type=code'
|
||||
++ exchange-url 'https://api.instagram.com/oauth/access_token'
|
||||
--
|
||||
::
|
||||
::::
|
||||
::
|
||||
|_ {bal/(bale keys:oauth2) tok/token:oauth2}
|
||||
:: ++aut is a "standard oauth2" core, which implements the
|
||||
:: most common handling of oauth2 semantics. see lib/oauth2 for more details,
|
||||
:: and examples at the bottom of the file.
|
||||
++ aut (~(standard oauth2 bal tok) . |=(tok/token:oauth2 +>(tok tok)))
|
||||
++ out
|
||||
%^ out-add-query-param:aut 'access_token'
|
||||
scope=~[%basic]
|
||||
dialog-url
|
||||
::
|
||||
++ in (in-code-to-token:aut exchange-url)
|
||||
++ bak bak-save-token:aut
|
||||
--
|
||||
:: create a developer app on https://www.instagram.com/developer/ to get a
|
||||
:: client id and secret
|
||||
|
||||
:: Be sure to be on https://localhost:8443, and to have registered
|
||||
:: http://localhost:8443/~/ac/instagram.com/~./in as the redirect URI.
|
||||
:: (If unable to change port number of ship, change the redirect URI port in %eyre)
|
||||
:: |init-oauth2 |init-oauth2 /com/instagram
|
||||
|
||||
:: Enter this sample command to get your user information:
|
||||
:: +https://api.instagram.com/v1/users/self
|
||||
|
||||
:: Before you receive the response, you'll have to clink on the link to
|
||||
:: authenicate yourself. You should then receive the response.
|
||||
|
@ -6,13 +6,16 @@
|
||||
::
|
||||
::::
|
||||
::
|
||||
=+ ^= aut
|
||||
%+ oauth2
|
||||
'https://slack.com/oauth/authorize'
|
||||
'https://slack.com/api/oauth.access'
|
||||
|_ {(bale keys:oauth2) tok/token.aut}
|
||||
++ aut ~(. ^aut +<- /client/admin)
|
||||
++ out (out-quay:aut 'token'^tok)
|
||||
++ in in-code:aut
|
||||
++ bak (bak-save-access:aut . |=(tok/token:aut +>(tok tok)))
|
||||
|_ {bal/(bale keys:oauth2) tok/token:oauth2}
|
||||
:: ++aut is a "standard oauth2" core, which implements the
|
||||
:: most common handling of oauth2 semantics. see lib/oauth2 for more details,
|
||||
:: and examples at the bottom of the file.
|
||||
++ aut (~(standard oauth2 bal tok) . |=(tok/token:oauth2 +>(tok tok)))
|
||||
++ out
|
||||
%^ out-add-query-param:aut 'token'
|
||||
scope=~[%client %admin]
|
||||
oauth-dialog='https://slack.com/oauth/authorize'
|
||||
::
|
||||
++ in (in-code-to-token:aut url='https://slack.com/api/oauth.access')
|
||||
++ bak bak-save-token:aut
|
||||
--
|
||||
|
@ -1,22 +1,27 @@
|
||||
:: Test url +https://api.twitter.com/1.1/account/verify_credentials.json
|
||||
::
|
||||
::
|
||||
:::: /hoon/twitter/com/sec
|
||||
::
|
||||
/+ oauth1
|
||||
!:
|
||||
::::
|
||||
::
|
||||
=+ ^= aut
|
||||
%^ oauth1
|
||||
'https://api.twitter.com/oauth/request_token'
|
||||
'https://api.twitter.com/oauth/authorize'
|
||||
'https://api.twitter.com/oauth/access_token'
|
||||
|_ {(bale keys:oauth1) tok/token:oauth1}
|
||||
++ aut ~(. ^aut . +<- +<+) :: XX electroplating
|
||||
++ out out-math:aut
|
||||
++ in in-oauth-token:aut
|
||||
++ bak (bak-save-access:aut save-token)
|
||||
++ res (res-handle-reqt:aut save-token)
|
||||
++ save-token |=(tok/token:aut +>(tok tok))
|
||||
::++ wyp ~
|
||||
|_ {bal/(bale keys:oauth1) tok/token:oauth1}
|
||||
:: ++aut is a "standard oauth1" core, which implements the
|
||||
:: most common handling of oauth1 semantics. see lib/oauth1 for more details,
|
||||
:: and examples at the bottom of the file.
|
||||
++ aut (~(standard oauth1 bal tok) . |=(tok/token:oauth1 +>(tok tok)))
|
||||
++ out
|
||||
%+ out-add-header:aut
|
||||
token-request='https://api.twitter.com/oauth/request_token'
|
||||
oauth-dialog='https://api.twitter.com/oauth/authorize'
|
||||
::
|
||||
++ res res-handle-request-token:aut
|
||||
::
|
||||
++ in
|
||||
%- in-exchange-token:aut
|
||||
exchange-url='https://api.twitter.com/oauth/access_token'
|
||||
::
|
||||
++ bak bak-save-token:aut
|
||||
:: ++ wyp ~
|
||||
--
|
||||
|
Loading…
Reference in New Issue
Block a user