mirror of
https://github.com/urbit/shrub.git
synced 2024-12-24 03:14:30 +03:00
135 lines
3.9 KiB
Plaintext
135 lines
3.9 KiB
Plaintext
|
:: 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
|
||
|
/+ jose, pkcs, strandio
|
||
|
=, strand=strand:spider
|
||
|
=, format
|
||
|
=, jose
|
||
|
=, pkcs
|
||
|
^- 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
|
||
|
%: self-jwt
|
||
|
key kid iss
|
||
|
'https://www.googleapis.com/auth/devstorage.editor'
|
||
|
aud now.bowl
|
||
|
==
|
||
|
;< jot=@t bind:m (sign-jwt sot aud)
|
||
|
(pure:m !>(jot))
|
||
|
::
|
||
|
++ 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
|
||
|
need
|
||
|
==
|
||
|
:: construct and return a self-signed JWT issued now, expiring in ~h1.
|
||
|
:: TODO: maybe move this into lib/jose/hoon
|
||
|
::
|
||
|
++ self-jwt
|
||
|
|= [=key:rsa kid=@t iss=@t scope=@t aud=@t iat=@da]
|
||
|
^- @t
|
||
|
=/ job=json
|
||
|
%^ sign:jws key
|
||
|
:: the JWT's "header"
|
||
|
%: pairs:enjs
|
||
|
alg+s+'RS256'
|
||
|
typ+s+'JWT'
|
||
|
kid+s+kid
|
||
|
~
|
||
|
==
|
||
|
:: the JWT's "payload"
|
||
|
%: pairs:enjs
|
||
|
iss+s+iss
|
||
|
sub+s+iss :: per g.co, use iss for sub
|
||
|
scope+s+scope
|
||
|
aud+s+aud
|
||
|
iat+(sect:enjs iat)
|
||
|
exp+(sect:enjs (add iat ~h1))
|
||
|
~
|
||
|
==
|
||
|
?> ?=([%o *] job)
|
||
|
=* mep p.job
|
||
|
=+ :~ pod=(sa:dejs (~(got by mep) 'protected'))
|
||
|
pad=(sa:dejs (~(got by mep) 'payload'))
|
||
|
sig=(sa:dejs (~(got by mep) 'signature'))
|
||
|
==
|
||
|
%- crip :: XX
|
||
|
:(weld pod "." pad "." sig)
|
||
|
:: RPC to get a signed JWT. Probably only works with Google.
|
||
|
:: Described at:
|
||
|
:: https://developers.google.com/identity/protocols/oauth2/service-account
|
||
|
::
|
||
|
++ sign-jwt
|
||
|
|= [jot=@t url=@t]
|
||
|
=/ m (strand @t) ^- 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
|
||
|
['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])
|
||
|
?. ?=([%o [[%'id_token' %s @] ~ ~]] +.jon)
|
||
|
(strand-fail:strandio %bad-json ~[body])
|
||
|
(pure:m p.q.n.p.u.jon)
|
||
|
--
|