mirror of
https://github.com/urbit/shrub.git
synced 2024-12-19 00:13:12 +03:00
aa93ac49c1
Since we only have one JWT (the thing we produce is an "access token", not a JWT), we can just call it jot.
145 lines
4.1 KiB
Plaintext
145 lines
4.1 KiB
Plaintext
:: Gets a Google Storage access token.
|
|
::
|
|
:: This thread produces a pair of [access-token expires-at], where
|
|
:: access-token is a @t that can be used as a bearer token to talk to the
|
|
:: GCP Storage API on behalf of some service account, and expires-at is
|
|
:: a @da after which the token will stop working and need to be refreshed.
|
|
::
|
|
:: It expects settings-store to contain relevant fields from a GCP service
|
|
:: account JSON file, generally as poked by sh/poke-gcp-account-json.
|
|
:: Specifically, it depends on the `token_uri`, `client_email`,
|
|
:: `private_key_id`, and `private_key` fields. If these fields are not
|
|
:: in settings-store at the time the thread is run, it will fail.
|
|
::
|
|
:: The thread works by first constructing a self-signed JWT using the
|
|
:: fields in settings-store. Then, it sends this JWT to the specified
|
|
:: token URI (usually https://oauth2.googleapis.com/token), which responds
|
|
:: with a bearer token and expiry.
|
|
::
|
|
::
|
|
/- spider, settings
|
|
/+ jose, pkcs, primitive-rsa, strandio
|
|
=, strand=strand:spider
|
|
=, rsa=primitive-rsa
|
|
^- thread:spider
|
|
|^
|
|
|= *
|
|
=/ m (strand ,vase)
|
|
^- form:m
|
|
;< =bowl:spider bind:m get-bowl:strandio
|
|
;< iss=@t bind:m (read-setting %client-email)
|
|
;< =key:rsa bind:m read-private-key
|
|
;< kid=@t bind:m (read-setting %private-key-id)
|
|
;< aud=@t bind:m (read-setting %token-uri)
|
|
=/ jot=@t
|
|
%: make-jwt
|
|
key kid iss
|
|
'https://www.googleapis.com/auth/cloud-platform'
|
|
aud now.bowl
|
|
==
|
|
;< p=[access-token=@t expires-at=@da] bind:m
|
|
(get-access-token jot aud now.bowl)
|
|
(pure:m !>(p))
|
|
::
|
|
++ read-setting
|
|
|= key=term
|
|
=/ m (strand @t) ^- form:m
|
|
;< has=? bind:m
|
|
%+ scry:strandio ?
|
|
/gx/settings-store/has-entry/gcp-store/[key]/noun
|
|
?. has
|
|
(strand-fail:strandio %no-setting key ~)
|
|
;< =data:settings bind:m
|
|
%+ scry:strandio
|
|
data:settings
|
|
/gx/settings-store/entry/gcp-store/[key]/settings-data
|
|
?> ?=([%entry %s @] data)
|
|
(pure:m p.val.data)
|
|
::
|
|
++ read-private-key
|
|
=/ m (strand ,key:rsa) ^- form:m
|
|
;< dat=@t bind:m (read-setting %private-key)
|
|
%- pure:m
|
|
%. dat
|
|
;: cork
|
|
to-wain:format
|
|
ring:de:pem:pkcs8:pkcs
|
|
need
|
|
==
|
|
:: construct and return a self-signed JWT issued now, expiring in ~h1.
|
|
:: TODO: maybe move this into lib/jose/hoon
|
|
::
|
|
++ make-jwt
|
|
|= [=key:rsa kid=@t iss=@t scope=@t aud=@t iat=@da]
|
|
^- @t
|
|
=/ job=json
|
|
=, enjs:format
|
|
%^ sign:jws:jose key
|
|
:: the JWT's "header"
|
|
%: pairs
|
|
alg+s+'RS256'
|
|
typ+s+'JWT'
|
|
kid+s+kid
|
|
~
|
|
==
|
|
:: the JWT's "payload"
|
|
%: pairs
|
|
iss+s+iss
|
|
sub+s+iss :: per g.co, use iss for sub
|
|
scope+s+scope
|
|
aud+s+aud
|
|
iat+(sect iat)
|
|
exp+(sect (add iat ~h1))
|
|
~
|
|
==
|
|
=/ [pod=@t pad=@t sig=@t]
|
|
=, dejs:format
|
|
((ot 'protected'^so 'payload'^so 'signature'^so ~) job)
|
|
(rap 3 (join '.' `(list @t)`~[pod pad sig]))
|
|
:: RPC to get an access token. Probably only works with Google.
|
|
:: Described at:
|
|
:: https://developers.google.com/identity/protocols/oauth2/service-account
|
|
::
|
|
++ get-access-token
|
|
|= [jot=@t url=@t now=@da]
|
|
=/ m (strand ,[@t @da]) ^- form:m
|
|
;< ~ bind:m
|
|
%: send-request:strandio
|
|
method=%'POST'
|
|
url=url
|
|
header-list=['Content-Type'^'application/json' ~]
|
|
^= body
|
|
%- some %- as-octt:mimes:html
|
|
%- en-json:html
|
|
%: pairs:enjs:format
|
|
:- 'grant_type'
|
|
s+'urn:ietf:params:oauth:grant-type:jwt-bearer'
|
|
assertion+s+jot
|
|
~
|
|
==
|
|
==
|
|
;< rep=client-response:iris bind:m
|
|
take-client-response:strandio
|
|
?> ?=(%finished -.rep)
|
|
?~ full-file.rep
|
|
(strand-fail:strandio %no-response ~)
|
|
=/ body=@t q.data.u.full-file.rep
|
|
=/ jon=(unit json) (de-json:html body)
|
|
?~ jon
|
|
(strand-fail:strandio %bad-body ~[body])
|
|
=* job u.jon
|
|
~| job
|
|
=, dejs:format
|
|
=/ [typ=@t exp=@dr tok=@t]
|
|
%. job
|
|
%: ot
|
|
'token_type'^so
|
|
'expires_in'^(cu |=(a=@ (mul a ~s1)) ni)
|
|
'access_token'^so
|
|
~
|
|
==
|
|
?> =('Bearer' typ)
|
|
%- pure:m
|
|
[tok (add exp now)]
|
|
--
|