github.com/storacha/go-ucanto@v0.7.2/core/delegation/delegate.go (about) 1 package delegation 2 3 import ( 4 "fmt" 5 6 "github.com/storacha/go-ucanto/core/dag/blockstore" 7 "github.com/storacha/go-ucanto/core/ipld/block" 8 "github.com/storacha/go-ucanto/core/ipld/codec/cbor" 9 "github.com/storacha/go-ucanto/core/ipld/hash/sha256" 10 "github.com/storacha/go-ucanto/ucan" 11 udm "github.com/storacha/go-ucanto/ucan/datamodel/ucan" 12 ) 13 14 // Option is an option configuring a UCAN delegation. 15 type Option func(cfg *delegationConfig) error 16 17 type delegationConfig struct { 18 exp *int 19 noexp bool 20 nbf int 21 nnc string 22 fct []ucan.FactBuilder 23 prf Proofs 24 } 25 26 // WithExpiration configures the expiration time in UTC seconds since Unix 27 // epoch. 28 func WithExpiration(exp int) Option { 29 return func(cfg *delegationConfig) error { 30 cfg.exp = &exp 31 cfg.noexp = false 32 return nil 33 } 34 } 35 36 // WithNoExpiration configures the UCAN to never expire. 37 // 38 // WARNING: this will cause the delegation to be valid FOREVER, unless revoked. 39 func WithNoExpiration() Option { 40 return func(cfg *delegationConfig) error { 41 cfg.exp = nil 42 cfg.noexp = true 43 return nil 44 } 45 } 46 47 // WithNotBefore configures the time in UTC seconds since Unix epoch when the 48 // UCAN will become valid. 49 func WithNotBefore(nbf int) Option { 50 return func(cfg *delegationConfig) error { 51 cfg.nbf = nbf 52 return nil 53 } 54 } 55 56 // WithNonce configures the nonce value for the UCAN. 57 func WithNonce(nnc string) Option { 58 return func(cfg *delegationConfig) error { 59 cfg.nnc = nnc 60 return nil 61 } 62 } 63 64 // WithFacts configures the facts for the UCAN. 65 func WithFacts(fct []ucan.FactBuilder) Option { 66 return func(cfg *delegationConfig) error { 67 cfg.fct = fct 68 return nil 69 } 70 } 71 72 // WithProof configures the proof(s) for the UCAN. If the `issuer` of this 73 // `Delegation` is not the resource owner / service provider, for the delegated 74 // capabilities, the `proofs` must contain valid `Proof`s containing 75 // delegations to the `issuer`. 76 func WithProof(prf ...Proof) Option { 77 return func(cfg *delegationConfig) error { 78 cfg.prf = prf 79 return nil 80 } 81 } 82 83 // Delegate creates a new signed token with a given `options.issuer`. If 84 // expiration is not set it defaults to 30 seconds from now. Returns UCAN in 85 // primary IPLD representation. 86 func Delegate[C ucan.CaveatBuilder](issuer ucan.Signer, audience ucan.Principal, capabilities []ucan.Capability[C], options ...Option) (Delegation, error) { 87 cfg := delegationConfig{} 88 for _, opt := range options { 89 if err := opt(&cfg); err != nil { 90 return nil, err 91 } 92 } 93 94 bs, err := blockstore.NewBlockStore() 95 if err != nil { 96 return nil, err 97 } 98 99 links, err := cfg.prf.WriteInto(bs) 100 if err != nil { 101 return nil, err 102 } 103 104 opts := []ucan.Option{ 105 ucan.WithFacts(cfg.fct), 106 ucan.WithNonce(cfg.nnc), 107 ucan.WithNotBefore(cfg.nbf), 108 ucan.WithProof(links...), 109 } 110 if cfg.noexp { 111 opts = append(opts, ucan.WithNoExpiration()) 112 } 113 if cfg.exp != nil { 114 opts = append(opts, ucan.WithExpiration(*cfg.exp)) 115 } 116 117 data, err := ucan.Issue(issuer, audience, capabilities, opts...) 118 if err != nil { 119 return nil, fmt.Errorf("issuing UCAN: %w", err) 120 } 121 122 rt, err := block.Encode(data.Model(), udm.Type(), cbor.Codec, sha256.Hasher) 123 if err != nil { 124 return nil, fmt.Errorf("encoding UCAN: %w", err) 125 } 126 127 err = bs.Put(rt) 128 if err != nil { 129 return nil, fmt.Errorf("adding delegation root to store: %w", err) 130 } 131 132 return NewDelegation(rt, bs) 133 }