Documentation ¶
Index ¶
- Constants
- Variables
- func AddFirstPartyCaveats(m *macaroon.Macaroon, caveats ...Caveat) error
- func AddToContext(ctx context.Context, key ContextKey, value interface{}) context.Context
- func EncodeCaveat(c Caveat) string
- func EncodeIdentifier(w io.Writer, id *Identifier) error
- func FromContext(ctx context.Context, key ContextKey) interface{}
- func FromHeader(header *http.Header) (*macaroon.Macaroon, lntypes.Preimage, error)
- func HasCaveat(m *macaroon.Macaroon, cond string) (string, bool)
- func IsPaymentRequired(err error) bool
- func SetHeader(header *http.Header, mac *macaroon.Macaroon, preimage fmt.Stringer) error
- func UseLogger(logger btclog.Logger)
- func VerifyCaveats(caveats []Caveat, satisfiers ...Satisfier) error
- type Caveat
- func DecodeCaveat(s string) (Caveat, error)
- func NewCapabilitiesCaveat(serviceName string, capabilities string) Caveat
- func NewCaveat(condition string, value string) Caveat
- func NewServicesCaveat(services ...Service) (Caveat, error)
- func NewTimeoutCaveat(serviceName string, numSeconds int64, now func() time.Time) Caveat
- type ClientInterceptor
- type ContextKey
- type FileStore
- type Identifier
- type MacaroonCredential
- type Satisfier
- type ServerInterceptor
- type Service
- type ServiceTier
- type Store
- type Token
- type TokenID
Constants ¶
const ( // GRPCErrCode is the error code we receive from a gRPC call if the // server expects a payment. GRPCErrCode = codes.Internal // GRPCErrCodeNew is the new error code we received for a "402 payment // required" error with version google.golang.org/grpc v1.41.0. This // error should not really be returned by any aperture instance since // we never deployed any version with google.golang.org/grpc v1.41.0 in // the wild as there were other issues with it as well. So this is just // here for completeness and shouldn't be used. GRPCErrCodeNew = codes.Unknown // GRPCErrMessage is the error message we receive from a gRPC call in // conjunction with the GRPCErrCode to signal the client that a payment // is required to access the service. GRPCErrMessage = "payment required" // AuthHeader is is the HTTP response header that contains the payment // challenge. AuthHeader = "WWW-Authenticate" // DefaultMaxCostSats is the default maximum amount in satoshis that we // are going to pay for an L402 automatically. Does not include routing // fees. DefaultMaxCostSats = 1000 // DefaultMaxRoutingFeeSats is the default maximum routing fee in // satoshis that we are going to pay to acquire an L402 token. DefaultMaxRoutingFeeSats = 10 // PaymentTimeout is the maximum time we allow a payment to take before // we stop waiting for it. PaymentTimeout = 60 * time.Second )
const ( // HeaderAuthorization is the HTTP header field name that is used to // send the L402 by REST clients. HeaderAuthorization = "Authorization" // HeaderMacaroonMD is the HTTP header field name that is used to send // the L402 by certain REST and gRPC clients. HeaderMacaroonMD = "Grpc-Metadata-Macaroon" // HeaderMacaroon is the HTTP header field name that is used to send the // L402 by our own gRPC clients. HeaderMacaroon = "Macaroon" )
const ( // LatestVersion is the latest version used for minting new L402s. LatestVersion = 0 // SecretSize is the size in bytes of a L402's secret, also known as // the root key of the macaroon. SecretSize = 32 // TokenIDSize is the size in bytes of an L402's ID encoded in its // macaroon identifier. TokenIDSize = 32 )
const ( // CondServices is the condition used for a services caveat. CondServices = "services" // CondCapabilitiesSuffix is the condition suffix used for a service's // capabilities caveat. For example, the condition of a capabilities // caveat for a service named `loop` would be `loop_capabilities`. CondCapabilitiesSuffix = "_capabilities" // CondTimeoutSuffix is the condition suffix used for a service's // timeout caveat. CondTimeoutSuffix = "_valid_until" )
const (
// PreimageKey is the key used for a payment preimage caveat.
PreimageKey = "preimage"
)
const Subsystem = "L402"
Subsystem defines the sub system name of this package.
Variables ¶
var ( // ErrNoServices is an error returned when we attempt to decode the // services included in a caveat. ErrNoServices = errors.New("no services found") // ErrInvalidService is an error returned when we attempt to decode a // service with an invalid format. ErrInvalidService = errors.New("service must be of the form " + "\"name:tier\"") )
var ( // ErrInvalidCaveat is an error returned when we attempt to decode a // caveat with an invalid format. ErrInvalidCaveat = errors.New("caveat must be of the form " + "\"condition=value\"") )
var ( // ErrNoToken is the error returned when the store doesn't contain a // token yet. ErrNoToken = errors.New("no token in store") )
var ( // ErrUnknownVersion is an error returned when attempting to decode an // L402 identifier with an unknown version. ErrUnknownVersion = errors.New("unknown L402 version") )
var ( // KeyTokenID is the key under which we store the client's token ID in // the request context. KeyTokenID = ContextKey{"tokenid"} )
Functions ¶
func AddFirstPartyCaveats ¶
AddFirstPartyCaveats adds a set of caveats as first-party caveats to a macaroon.
func AddToContext ¶
func AddToContext(ctx context.Context, key ContextKey, value interface{}) context.Context
AddToContext adds the given value to the context for easy retrieval later on.
func EncodeCaveat ¶
EncodeCaveat encodes a caveat into its string representation.
func EncodeIdentifier ¶
func EncodeIdentifier(w io.Writer, id *Identifier) error
EncodeIdentifier encodes an L402's identifier according to its version.
func FromContext ¶
func FromContext(ctx context.Context, key ContextKey) interface{}
FromContext tries to extract a value from the given context.
func FromHeader ¶
FromHeader tries to extract authentication information from HTTP headers. There are two supported formats that can be sent in three different header fields:
- Authorization: L402 <macBase64>:<preimageHex>
- Grpc-Metadata-Macaroon: <macHex>
- Macaroon: <macHex>
If only the macaroon is sent in header 2 or three then it is expected to have a caveat with the preimage attached to it.
func HasCaveat ¶
HasCaveat checks whether the given macaroon has a caveat with the given condition, and if so, returns its value. If multiple caveats with the same condition exist, then the value of the last one is returned.
func IsPaymentRequired ¶
IsPaymentRequired inspects an error to find out if it's the specific gRPC error returned by the server to indicate a payment is required to access the service.
func SetHeader ¶
SetHeader sets the provided authentication elements as the default/standard HTTP header for the L402 protocol.
func UseLogger ¶
UseLogger uses a specified Logger to output package logging info. This should be used in preference to SetLogWriter if the caller is also using btclog.
func VerifyCaveats ¶
VerifyCaveats determines whether every relevant caveat of an L402 holds true. A caveat is considered relevant if a satisfier is provided for it, which is what we'll use as their evaluation.
NOTE: The caveats provided should be in the same order as in the L402 to ensure the correctness of each satisfier's SatisfyPrevious.
Types ¶
type Caveat ¶
type Caveat struct { // Condition serves as a way to identify a caveat and how to satisfy it. Condition string // Value is what will be used to satisfy a caveat. This can be as // flexible as needed, as long as it can be encoded into a string. Value string }
Caveat is a predicate that can be applied to an L402 in order to restrict its use in some form. Caveats are evaluated during L402 verification after the L402's signature is verified. The predicate of each caveat must hold true in order to successfully validate an L402.
func DecodeCaveat ¶
DecodeCaveat decodes a caveat from its string representation.
func NewCapabilitiesCaveat ¶
NewCapabilitiesCaveat creates a new capabilities caveat for the given service.
func NewServicesCaveat ¶
NewServicesCaveat creates a new services caveat with the provided caveats.
func NewTimeoutCaveat ¶
NewTimeoutCaveat creates a new caveat that will result in a macaroon being valid for numSeconds after the current time.
type ClientInterceptor ¶
type ClientInterceptor struct {
// contains filtered or unexported fields
}
ClientInterceptor is a gRPC client interceptor that can handle L402 authentication challenges with embedded payment requests. It uses a connection to lnd to automatically pay for an authentication token.
func NewInterceptor ¶
func NewInterceptor(lnd *lndclient.LndServices, store Store, rpcCallTimeout time.Duration, maxCost, maxFee btcutil.Amount, allowInsecure bool) *ClientInterceptor
NewInterceptor creates a new gRPC client interceptor that uses the provided lnd connection to automatically acquire and pay for L402 tokens, unless the indicated store already contains a usable token.
func (*ClientInterceptor) StreamInterceptor ¶
func (i *ClientInterceptor) StreamInterceptor(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error)
StreamInterceptor is an interceptor method that can be used directly by gRPC for streaming calls. If the store contains a token, it is attached as credentials to every stream establishment call before patching it through. The response error is also intercepted for every initial stream initiation. If there is an error returned and it is indicating a payment challenge, a token is acquired and paid for automatically. The original request is then repeated back to the server, now with the new token attached.
func (*ClientInterceptor) UnaryInterceptor ¶
func (i *ClientInterceptor) UnaryInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error
UnaryInterceptor is an interceptor method that can be used directly by gRPC for unary calls. If the store contains a token, it is attached as credentials to every call before patching it through. The response error is also intercepted for every call. If there is an error returned and it is indicating a payment challenge, a token is acquired and paid for automatically. The original request is then repeated back to the server, now with the new token attached.
type ContextKey ¶
type ContextKey struct {
Name string
}
ContextKey is the type that we use to identify L402 specific values in the request context. We wrap the string inside a struct because of this comment in the context API: "The provided key must be comparable and should not be of type string or any other built-in type to avoid collisions between packages using context."
type FileStore ¶
type FileStore struct {
// contains filtered or unexported fields
}
FileStore is an implementation of the Store interface that files to save the serialized tokens. There is always just one current token that is either pending or fully paid.
func NewFileStore ¶
NewFileStore creates a new file based token store, creating its file in the provided directory. If the directory does not exist, it will be created.
func (*FileStore) AllTokens ¶
AllTokens returns all tokens that the store has knowledge of, even if they might be expired. The tokens are mapped by their identifying attribute like file name or storage key.
NOTE: This is part of the Store interface.
func (*FileStore) CurrentToken ¶
CurrentToken returns the token that is currently contained in the store or an error if there is none.
NOTE: This is part of the Store interface.
func (*FileStore) RemovePendingToken ¶
RemovePendingToken removes a pending token from the store or returns ErrNoToken if there is no pending token.
func (*FileStore) StoreToken ¶
StoreToken saves a token to the store, overwriting any old token if there is one.
NOTE: This is part of the Store interface.
type Identifier ¶
type Identifier struct { // Version is the version of an L402. Having a version allows us to // introduce new fields to the identifier in a backwards-compatible // manner. Version uint16 // PaymentHash is the payment hash linked to an L402. Verification of // an L402 depends on a valid payment, which is enforced by ensuring a // preimage is provided that hashes to our payment hash. PaymentHash lntypes.Hash // TokenID is the unique identifier of an L402. TokenID TokenID }
Identifier contains the static identifying details of an L402. This is intended to be used as the identifier of the macaroon within an L402.
func DecodeIdentifier ¶
func DecodeIdentifier(r io.Reader) (*Identifier, error)
DecodeIdentifier decodes an L402's identifier according to its version.
type MacaroonCredential ¶
type MacaroonCredential struct { *macaroon.Macaroon // AllowInsecure specifies if the macaroon is allowed to be sent over // insecure transport channels. This should only ever be set to true if // the insecure connection is proxied through tor and the destination // address is an onion service. AllowInsecure bool }
MacaroonCredential wraps a macaroon to implement the credentials.PerRPCCredentials interface.
func NewMacaroonCredential ¶
func NewMacaroonCredential(m *macaroon.Macaroon, allowInsecure bool) MacaroonCredential
NewMacaroonCredential returns a copy of the passed macaroon wrapped in a MacaroonCredential struct which implements PerRPCCredentials.
func (MacaroonCredential) GetRequestMetadata ¶
func (m MacaroonCredential) GetRequestMetadata(_ context.Context, _ ...string) (map[string]string, error)
GetRequestMetadata implements the PerRPCCredentials interface. This method is required in order to pass the wrapped macaroon into the gRPC context. With this, the macaroon will be available within the request handling scope of the ultimate gRPC server implementation.
func (MacaroonCredential) RequireTransportSecurity ¶
func (m MacaroonCredential) RequireTransportSecurity() bool
RequireTransportSecurity implements the PerRPCCredentials interface.
type Satisfier ¶
type Satisfier struct { // Condition is the condition of the caveat we'll attempt to satisfy. Condition string // SatisfyPrevious ensures a caveat is in accordance with a previous one // with the same condition. This is needed since caveats of the same // condition can be used multiple times as long as they enforce more // permissions than the previous. // // For example, we have a caveat that only allows us to use an L402 for // 7 more days. We can add another caveat that only allows for 3 more // days of use and lend it to another party. SatisfyPrevious func(previous Caveat, current Caveat) error // SatisfyFinal satisfies the final caveat of an L402. If multiple // caveats with the same condition exist, this will only be executed // once all previous caveats are also satisfied. SatisfyFinal func(Caveat) error }
Satisfier provides a generic interface to satisfy a caveat based on its condition.
func NewCapabilitiesSatisfier ¶
NewCapabilitiesSatisfier implements a satisfier to determine whether the target capability for a service is authorized for a given L402.
func NewServicesSatisfier ¶
NewServicesSatisfier implements a satisfier to determine whether the target service is authorized for a given L402.
TODO(wilmer): Add tier verification?
func NewTimeoutSatisfier ¶
NewTimeoutSatisfier checks if an L402 is expired or not. The Satisfier takes a service name to set as the condition prefix and currentTimestamp to compare against the expiration(s) in the caveats. The expiration time is retrieved from the caveat values themselves. The satisfier will also make sure that each subsequent caveat of the same condition only has increasingly strict expirations.
type ServerInterceptor ¶
type ServerInterceptor struct{}
ServerInterceptor is a gRPC server interceptor that extracts the token ID from the request context if an L402 is present.
func (*ServerInterceptor) StreamInterceptor ¶
func (i *ServerInterceptor) StreamInterceptor(srv interface{}, ss grpc.ServerStream, _ *grpc.StreamServerInfo, handler grpc.StreamHandler) error
StreamInterceptor is an stream gRPC server interceptor that inspects incoming streams for authentication tokens. If an L402 authentication token is found in the initial stream establishment request, its token ID is extracted and treated as client ID. The extracted ID is then attached to the request context in a format that is easy to extract by request handlers.
func (*ServerInterceptor) UnaryInterceptor ¶
func (i *ServerInterceptor) UnaryInterceptor(ctx context.Context, req interface{}, _ *grpc.UnaryServerInfo, handler grpc.UnaryHandler) ( resp interface{}, err error)
UnaryInterceptor is an unary gRPC server interceptor that inspects incoming calls for authentication tokens. If an L402 authentication token is found in the request, its token ID is extracted and treated as client ID. The extracted ID is then attached to the request context in a format that is easy to extract by request handlers.
type Service ¶
type Service struct { // Name is the name of the L402-enabled service. Name string // Tier is the tier of the L402-enabled service. Tier ServiceTier // RecipientLud16 of service price recipient. RecipientLud16 string // Price of service L402 in satoshis. Price int64 }
Service contains the details of an L402-enabled service.
type ServiceTier ¶
type ServiceTier uint8
ServiceTier represents the different possible tiers of an L402-enabled service.
const ( // BaseTier is the base tier of an L402-enabled service. This tier // should be used for any new L402s that are not part of a service tier // upgrade. BaseTier ServiceTier = iota )
type Store ¶
type Store interface { // CurrentToken returns the token that is currently contained in the // store or an error if there is none. CurrentToken() (*Token, error) // AllTokens returns all tokens that the store has knowledge of, even // if they might be expired. The tokens are mapped by their identifying // attribute like file name or storage key. AllTokens() (map[string]*Token, error) // StoreToken saves a token to the store. Old tokens should be kept for // accounting purposes but marked as invalid somehow. StoreToken(*Token) error // RemovePendingToken removes a pending token from the store or returns // ErrNoToken if there is no pending token. RemovePendingToken() error }
Store is an interface that allows users to store and retrieve an L402 token.
type Token ¶
type Token struct { // PaymentHash is the hash of the L402 invoice that needs to be paid. // Knowing the preimage to this hash is seen as proof of payment by the // authentication server. PaymentHash lntypes.Hash // Preimage is the proof of payment indicating that the token has been // paid for if set. If the preimage is empty, the payment might still // be in transit. Preimage lntypes.Preimage // AmountPaid is the total amount in msat that the user paid to get the // token. This does not include routing fees. AmountPaid lnwire.MilliSatoshi // RoutingFeePaid is the total amount in msat that the user paid in // routing fee to get the token. RoutingFeePaid lnwire.MilliSatoshi // TimeCreated is the moment when this token was created. TimeCreated time.Time // contains filtered or unexported fields }
Token is the main type to store an L402 token in.
func (*Token) BaseMacaroon ¶
func (t *Token) BaseMacaroon() *macaroon.Macaroon
BaseMacaroon returns the base macaroon as received from the authentication server.
func (*Token) IsValid ¶
IsValid returns true if the timestamp contained in the base macaroon is not yet expired.
func (*Token) PaidMacaroon ¶
PaidMacaroon returns the base macaroon with the proof of payment (preimage) added as a first-party-caveat.
type TokenID ¶
type TokenID [TokenIDSize]byte
TokenID is the type that stores the token identifier of an L402 token.
func MakeIDFromString ¶
MakeIDFromString parses the hex encoded string and parses it into a token ID.