localmed/ruby-macaroons — pull request #9 by ecordell (2014-10-15 22:45:04+00:00)
Title: Verify third party caveats

This allows verifying third party caveats with discharge macaroons.

How does a third party caveat get verified?

- You pass the verifier your macaroon as well as a list of "discharge macaroons" - macaroons you've received from other services that act as holder-of-key proofs of the claims in your third-party caveats. (They're then bound to the root macaroon so that they can't be used if someone else got a hold of them in transit).

- The verifier loops through your caveats, building a hash in parallel that it expects to match the macaroon signature when the whole things is processed.

- Third party caveats have a caveat_id which matches the id of a discharge macaroon. This macaroon is processed recursively by the verifier (in the implementation, a new verifier is constructed, so each recursive steps creates a new "child" verifier) to ensure all caveats in the discharge macaroon are met as well.

- If the discharge macaroon is valid, the third party caveat is valid.

- If all first and third party caveats on the root macaroon are valid, and the hashes match, authorization was successful.

Example:

```ruby
m = Macaroon.new(
'http://mybank/',
'we used our other secret key',
'this is a different super-secret key; never use the same secret twice'
)
m.add_first_party_caveat('account = 3735928559')
caveat_key = '4; guaranteed random by a fair toss of the dice'
identifier = 'this was how we remind auth of key/pred'
m.add_third_party_caveat(caveat_key, identifier, 'http://auth.mybank/')

discharge = Macaroon.new(
'http://auth.mybank/',
identifier,
caveat_key
)
discharge.add_first_party_caveat('time < 2015-01-01T00:00')
protected_discharge = m.prepare_for_request(discharge)

v = Macaroon::Verifier.new()
v.satisfy_exact('account = 3735928559')
v.satisfy_exact('time < 2015-01-01T00:00')
verified = v.verify(
macaroon: m,
key: 'this is a different super-secret key; never use the same secret twice',
discharge_macaroons: [protected_discharge]
)
=> true
```

I scrapped the implementation from the python library and wrote this to be more understandable and use less memory. At some point I'd like to update the python library to read more like this does.