2021-02-13 05:47:29 +03:00
|
|
|
:: Thread that gets a JWT for use with Google Storage.
|
|
|
|
::
|
|
|
|
:: This thread expects settings-store to contain relevant fields from a GCP
|
|
|
|
:: service account JSON file, e.g. as poked by urbit/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 gives us a JWT signed
|
|
|
|
:: by Google. This token can then be used as a bearer token for requests to
|
|
|
|
:: Google Storage.
|
|
|
|
::
|
|
|
|
:: The returned token has an expiration time of 60 minutes after the time at
|
|
|
|
:: which the thread was called.
|
|
|
|
::
|
|
|
|
::
|
|
|
|
/- spider, settings
|
2021-02-23 22:41:18 +03:00
|
|
|
/+ jose, pkcs, primitive-rsa, strandio
|
2021-02-13 05:47:29 +03:00
|
|
|
=, strand=strand:spider
|
2021-02-23 22:41:18 +03:00
|
|
|
=, rsa=primitive-rsa
|
2021-02-13 05:47:29 +03:00
|
|
|
^- 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)
|
|
|
|
=/ sot=@t
|
2021-02-24 01:34:48 +03:00
|
|
|
%: make-jwt
|
2021-02-13 05:47:29 +03:00
|
|
|
key kid iss
|
2021-02-24 01:18:59 +03:00
|
|
|
'https://www.googleapis.com/auth/cloud-platform'
|
2021-02-13 05:47:29 +03:00
|
|
|
aud now.bowl
|
|
|
|
==
|
2021-02-24 01:40:05 +03:00
|
|
|
;< p=[tok=@t exp=@da] bind:m
|
|
|
|
(get-access-token sot aud now.bowl)
|
2021-02-24 01:18:59 +03:00
|
|
|
(pure:m !>(p))
|
2021-02-13 05:47:29 +03:00
|
|
|
::
|
|
|
|
++ 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
|
2021-02-23 22:41:18 +03:00
|
|
|
ring:de:pem:pkcs8:pkcs
|
2021-02-13 05:47:29 +03:00
|
|
|
need
|
|
|
|
==
|
|
|
|
:: construct and return a self-signed JWT issued now, expiring in ~h1.
|
|
|
|
:: TODO: maybe move this into lib/jose/hoon
|
|
|
|
::
|
2021-02-24 01:34:48 +03:00
|
|
|
++ make-jwt
|
2021-02-13 05:47:29 +03:00
|
|
|
|= [=key:rsa kid=@t iss=@t scope=@t aud=@t iat=@da]
|
|
|
|
^- @t
|
|
|
|
=/ job=json
|
2021-02-23 22:41:18 +03:00
|
|
|
=, enjs:format
|
|
|
|
%^ sign:jws:jose key
|
2021-02-13 05:47:29 +03:00
|
|
|
:: the JWT's "header"
|
2021-02-23 22:41:18 +03:00
|
|
|
%: pairs
|
2021-02-13 05:47:29 +03:00
|
|
|
alg+s+'RS256'
|
|
|
|
typ+s+'JWT'
|
|
|
|
kid+s+kid
|
|
|
|
~
|
|
|
|
==
|
|
|
|
:: the JWT's "payload"
|
2021-02-23 22:41:18 +03:00
|
|
|
%: pairs
|
2021-02-13 05:47:29 +03:00
|
|
|
iss+s+iss
|
|
|
|
sub+s+iss :: per g.co, use iss for sub
|
|
|
|
scope+s+scope
|
|
|
|
aud+s+aud
|
2021-02-23 22:41:18 +03:00
|
|
|
iat+(sect iat)
|
|
|
|
exp+(sect (add iat ~h1))
|
2021-02-13 05:47:29 +03:00
|
|
|
~
|
|
|
|
==
|
2021-02-23 22:41:18 +03:00
|
|
|
=/ [pod=@t pad=@t sig=@t]
|
|
|
|
=, dejs:format
|
|
|
|
((ot 'protected'^so 'payload'^so 'signature'^so ~) job)
|
|
|
|
(rap 3 (join '.' `(list @t)`~[pod pad sig]))
|
2021-02-24 01:34:48 +03:00
|
|
|
:: RPC to get an access token. Probably only works with Google.
|
2021-02-13 05:47:29 +03:00
|
|
|
:: Described at:
|
|
|
|
:: https://developers.google.com/identity/protocols/oauth2/service-account
|
|
|
|
::
|
2021-02-24 01:34:48 +03:00
|
|
|
++ get-access-token
|
2021-02-24 01:40:05 +03:00
|
|
|
|= [jot=@t url=@t now=@da]
|
|
|
|
=/ m (strand ,[@t @da]) ^- form:m
|
2021-02-13 05:47:29 +03:00
|
|
|
;< ~ bind:m
|
|
|
|
%: send-request:strandio
|
|
|
|
method=%'POST'
|
|
|
|
url=url
|
|
|
|
header-list=['Content-Type'^'application/json' ~]
|
|
|
|
^= body
|
|
|
|
%- some %- as-octt:mimes:html
|
|
|
|
%- en-json:html
|
2021-02-23 22:41:18 +03:00
|
|
|
%: pairs:enjs:format
|
2021-02-24 01:34:48 +03:00
|
|
|
:- 'grant_type'
|
|
|
|
s+'urn:ietf:params:oauth:grant-type:jwt-bearer'
|
2021-02-13 05:47:29 +03:00
|
|
|
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])
|
2021-02-23 22:41:18 +03:00
|
|
|
=* job u.jon
|
2021-02-24 01:18:59 +03:00
|
|
|
~| job
|
2021-02-23 22:41:18 +03:00
|
|
|
=, dejs:format
|
2021-02-24 01:34:48 +03:00
|
|
|
=/ [typ=@t exp=@dr tok=@t]
|
|
|
|
%. job
|
|
|
|
%: ot
|
|
|
|
'token_type'^so
|
|
|
|
'expires_in'^(cu |=(a=@ (mul a ~s1)) ni)
|
|
|
|
'access_token'^so
|
|
|
|
~
|
|
|
|
==
|
2021-02-24 01:18:59 +03:00
|
|
|
?> =('Bearer' typ)
|
|
|
|
%- pure:m
|
2021-02-24 01:40:05 +03:00
|
|
|
[tok (add exp now)]
|
2021-02-13 05:47:29 +03:00
|
|
|
--
|