Class Macaroon

java.lang.Object
org.forgerock.macaroons.Macaroon

public final class Macaroon extends Object
A macaroon is a cryptographically protected token which can be attenuated by appending caveats. A macaroon has an identifier (unique string) and a location hint describing where it is intended to be used. A caveat is a condition that restricts the use of a macaroon. Anybody can append a caveat to a macaroon, but only the holder of the secret key can remove one. It is important that caveats are only used to restrict the use of a token, not to expand its use or add additional claims. There are two main types of caveats:
  • First-party caveats are simple restrictions on the context in which a macaroon can be used, such as limiting the expiry time or time of day it can be used, or other conditions that can be checked locally at the point of use. A first-party caveat is a simple string that describes the condition such as time < 2020-01-01T00:00:00Z.
  • Third-party caveats require the client to obtain a proof from an external service that some additional condition has been satisfied. For example, you might append a caveat requiring that the user of the macaroon is also authenticated using your company's authentication service. To prove this condition is satisfied the client would login at the authentication service and obtain a discharge macaroon that is tied to the 3rd-party caveat. The discharge macaroon can itself have caveats that must be satisfied by the client before it can be used. A 3rd-party caveat consists of a location hint for the client to know where the 3rd-party service is, a random caveat key that will be used to sign the discharge macaroon, and an identifier that informs the external service what the condition to be satisfied is.
Note: the location hint for the macaroon as a whole and for any 3rd-party caveats is not authenticated. These hints are intended to be used by the client, which is not able to validate them. It is important that the client only connects to services from a whitelist of known and trusted services, otherwise it may be vulnerable to SSRF attacks.

This library is compatible with the de-facto standard libmacaroons library

Note on HSM compatibility

The de facto standard for macaroons is the libmacaroons C library. The author of this library made a design decision that the key passed to the macaroon library is not used directly but is first hashed with a constant well-known string to derive a key. This means that macaroons are not directly compatible with a HSM or KMS that hides the raw bytes of the key material from the application. If you need to use macaroons in an environment that requires the use of a HSM, it is recommended that you use the HSM key to wrap or derive a unique key for each macaroon and use the identifier to store the wrapped key or salt. For example:

 Key hsmKey = ...;
 byte[] salt = secureRandomBytes(20); // Or whatever macaroon id you were going to use
 // See forgerock-utils for HKDFKeyGenerator
 Key macaroonKey = HKDFKeyGenerator.expandKey(hsmKey, "HmacSHA256", salt, 32);
 Macaroon m = Macaroon.create(macaroonKey, location, salt);
 
To verify the macaroon you would perform the same key derivation process:

 Key hsmKey = ...;
 Macaroon m = Macaroon.parse(input);
 byte[] salt = m.getRawIdentifier();
 Key macaroonKey = HKDFKeyGenerator.expandKey(hsmKey, "HmacSHA256", salt, 32);
 VerifierResult result = m.verify(macaroonKey, verifier);
 
See Also:
  • Constructor Details

    • Macaroon

      public Macaroon(String location, byte[] identifier, List<Macaroon.Caveat> caveats, byte[] tag)
      Constructs a Macaroon from constituent parts. Note: this constructor doesn't attempt to validate the authentication tag. Use verify(VerificationKey, MacaroonVerifier) to verify the signature and any caveats.
      Parameters:
      location - the location hint. May be null.
      identifier - the identifier.
      caveats - the list of caveats.
      tag - the HMAC authentication tag.
  • Method Details

    • generateKey

      public static SecretKey generateKey()
      Generates a random macaroon key. This method is intended for creating random keys to use for discharge macaroons for third-party caveats. For authorization macaroons you should use a SigningKey from a SecretsProvider.
      Returns:
      a random macaroon key.
    • create

      public static Macaroon create(Key key, String location, byte[] identifier)
      Creates a fresh Macaroon with no caveats. This constructor allows an arbitrary binary identifier to be used. This may cause compatibility issues with any macaroon libraries that expect the identifier to be a valid UTF-8 string.

      Note: In most cases you should prefer to use create(SigningKey, String, byte[]). This method is provided primarily for creating discharge macaroons for 3rd-party caveats, where the key will be a single-use key negotiated for that one caveat.

      Parameters:
      key - the key. Not null. The raw bytes of the key must be available.
      location - the location hint. May be null.
      identifier - the macaroon identifier. Not null.
      Returns:
      the fresh macaroon.
    • create

      public static Macaroon create(SigningKey signingKey, String location, byte[] identifier)
      Creates a fresh Macaroon with no caveats. This constructor allows an arbitrary binary identifier to be used. This may cause compatibility issues with any macaroon libraries that expect the identifier to be a valid UTF-8 string.
      Parameters:
      signingKey - the key. Not null. Must be extractable.
      location - the location hint. May be null.
      identifier - the macaroon identifier. Not null.
      Returns:
      the fresh macaroon.
    • create

      public static Macaroon create(SigningKey signingKey, String location, String identifier)
      Creates a fresh Macaroon with no caveats.
      Parameters:
      signingKey - the key. Not null. Must be extractable.
      location - the location hint. May be null.
      identifier - the macaroon identifier. Not null.
      Returns:
      the fresh macaroon.
    • create

      public static Macaroon create(SigningKey key, String location)
      Creates a fresh Macaroon with no caveats and a random identifier.
      Parameters:
      key - the key. Not null. Must be extractable.
      location - the location hint. May be null.
      Returns:
      the fresh macaroon.
    • create

      public static Macaroon create(SigningKey key)
      Creates a fresh Macaroon with no caveats, a blank location hint, and a random identifier.
      Parameters:
      key - the key. Not null. Must be extractable.
      Returns:
      the fresh macaroon.
    • getLocationHint

      public String getLocationHint()
      An unauthenticated hint for where this macaroon is intended to be used. This hint is intended for consumption by the client and is (deliberately) not included in the Macaroon authentication tag. A client shouldn't rely on this value being accurate and should only present a Macaroon to trusted services.
      Returns:
      the location hint, typically a URI. May be null.
    • getIdentifier

      public String getIdentifier()
      A unique identifier for this Macaroon. The syntax and semantics of the identifier are unspecified and application-specific. There is no guarantee that the identifier is unique for each macaroon. This getter method assumes that the identifier is UTF-8 encoded. Use getRawIdentifier() if the identifier may be binary.
      Returns:
      the macaroon unique identifier.
    • getRawIdentifier

      public byte[] getRawIdentifier()
      Returns the raw bytes of the identifier. The identifier is copied before being returned.
      Returns:
      the raw bytes of the identifier.
    • getCaveats

      public List<Macaroon.Caveat> getCaveats()
      Returns a list of caveats associated with this macaroon. Caveats are conditions that restrict the way in which a macaroon can be used. A first-party caveat is just a string that describes a local condition on use such as time < 2020-01-01T00:00:00Z to prevent a macaroon being used after midnight on the 1st January 2020. A third-party caveat is intended to be verified by an external service and additionally has a verifier id (which is an encrypted macaroon key) and location hint.
      Returns:
      an unmodifiable list of the caveats associated with this macaroon.
    • getFirstPartyCaveats

      public Stream<Macaroon.Caveat> getFirstPartyCaveats()
      Returns all of the first-party caveats attached to this macaroon.
      Returns:
      the first-party caveats.
    • getThirdPartyCaveats

      public Stream<Macaroon.Caveat> getThirdPartyCaveats()
      Returns all of the third-party caveats attached to this macaroon.
      Returns:
      the third-party caveats.
    • getTag

      public byte[] getTag()
      The HMAC authentication tag for this macaroon. The tag authenticates the identifier and all caveats. Note: the authentication tag doesn't cover any location hints in the macaroon or any 3rd-party caveats. A copy of the tag is returned to prevent any changes to the macaroon tag.
      Returns:
      a copy of authentication tag.
    • addFirstPartyCaveat

      public Macaroon addFirstPartyCaveat(byte[] caveat)
      Adds a first-party caveat to the macaroon. A first-party caveat is a simple condition on the use of a macaroon such as the time of day that it can be used. This method mutates the macaroon and destroys the old authentication tag. Use macaroon.copy().addFirstPartyCaveat(...) if you wish to preserve a copy of the original macaroon.
      Parameters:
      caveat - the caveat to add.
      Returns:
      this macaroon.
      See Also:
    • addFirstPartyCaveat

      public Macaroon addFirstPartyCaveat(String caveat)
      Adds a first-party caveat to the macaroon. A first-party caveat is a simple condition on the use of a macaroon such as the time of day that it can be used. This method mutates the macaroon and destroys the old authentication tag. Use macaroon.copy().addFirstPartyCaveat(...) if you wish to preserve a copy of the original macaroon.
      Parameters:
      caveat - the caveat to add.
      Returns:
      this macaroon.
      See Also:
    • addFirstPartyCaveat

      public Macaroon addFirstPartyCaveat(JsonValue caveat)
      Adds a first-party caveat to the macaroon. A first-party caveat is a simple condition on the use of a macaroon such as the time of day that it can be used. This method mutates the macaroon and destroys the old authentication tag. Use macaroon.copy().addFirstPartyCaveat(...) if you wish to preserve a copy of the original macaroon.

      In a JSON object caveat each key-value pair is interpreted as a separate first party caveat. For example, the following JSON object expresses two caveats - one restricting the expiry time, and the other the intended audience:

      
       {
           "exp": 1577836800,
           "aud": ["https://api.example.com"]
       }
       
      Parameters:
      caveat - the caveat to add.
      Returns:
      this macaroon.
      See Also:
    • addThirdPartyCaveat

      public Macaroon addThirdPartyCaveat(String location, byte[] caveatKey, byte[] identifier)
      Adds a third-party caveat to the macaroon. A third-party caveat is a condition on the use of a macaroon that needs to be checked by a third-party service. This method mutates the macaroon and destroys the old authentication tag. Use macaroon.copy().addThirdPartyCaveat(...) if you wish to preserve a copy of the original macaroon.
      Parameters:
      location - a hint for the location of the 3rd-party service.
      caveatKey - a unique key that will be used by the 3rd-party service to sign a discharge macaroon. The key should be of high entropy. The key will be encrypted when added to the macaroon and decrypted when the macaroon is verified. Macaroon.generateKey().getEncoded() is a suitable source for the key.
      identifier - an identifier that will be used by the 3rd-party to determine what condition needs to be satisfied and the caveat key to use to sign the discharge macaroon. Suitable methods of generating this identifier include encrypting the details using a public key of the 3rd-party service or negotiating an identifier (ticket) with the service using an out-of-band protocol.
      Returns:
      this macaroon.
    • addThirdPartyCaveat

      public Macaroon addThirdPartyCaveat(String location, byte[] caveatKey, String identifier)
      Adds a third-party caveat to the macaroon. A third-party caveat is a condition on the use of a macaroon that needs to be checked by a third-party service. This method mutates the macaroon and destroys the old authentication tag. Use macaroon.copy().addThirdPartyCaveat(...) if you wish to preserve a copy of the original macaroon.
      Parameters:
      location - a hint for the location of the 3rd-party service.
      caveatKey - a unique key that will be used by the 3rd-party service to sign a discharge macaroon. The key should be of high entropy. The key will be encrypted when added to the macaroon and decrypted when the macaroon is verified. Macaroon.generateKey().getEncoded() is a suitable source for the key.
      identifier - an identifier that will be used by the 3rd-party to determine what condition needs to be satisfied and the caveat key to use to sign the discharge macaroon. Suitable methods of generating this identifier include encrypting the details using a public key of the 3rd-party service or negotiating an identifier (ticket) with the service using an out-of-band protocol.
      Returns:
      this macaroon.
    • copy

      public Macaroon copy()
      Creates a copy of this macaroon. Changes to the copy or the original have no impact on the other macaroon.
      Returns:
      a copy of this macaroon.
    • serialize

      public String serialize()
      Serializes the macaroon to a string in the default format. The format is guaranteed to be URL-safe.
      Returns:
      the serialized string form of the macaroon.
    • serialize

      public String serialize(SerializationFormat format)
      Serializes the macaroon to a string in the given format.
      Parameters:
      format - the format.
      Returns:
      the serialized string.
    • deserialize

      public static Macaroon deserialize(String macaroon)
      Deserializes a macaroon from the default format.
      Parameters:
      macaroon - the macaroon string.
      Returns:
      the deserialized string.
      Throws:
      MacaroonEncodingException - if the string is not a valid macaroon.
    • deserialize

      public static Macaroon deserialize(SerializationFormat format, String macaroon)
      Deserializes a macaroon from the given format.
      Parameters:
      format - the format.
      macaroon - the macaroon string.
      Returns:
      the deserialized string.
      Throws:
      MacaroonEncodingException - if the string is not a valid macaroon.
    • prepareForRequest

      public List<Macaroon> prepareForRequest(Macaroon... dischargeMacaroons)
      Prepares this macaroon to be sent with a request by binding any discharge macaroons to it. A new copy of the discharge macaroons is returned with the signature altered so that they can only be used in combination with this macaroon. This prevents the recipient using the discharge macaroons to discharge a 3rd-party caveat on a macaroon of their own, if the discharge key was reused. The original discharge macaroons are not altered.
      Parameters:
      dischargeMacaroons - the discharge macaroons to bind to this macaroon.
      Returns:
      an altered version of the discharge macaroons, bound to this macaroon.
    • equals

      public boolean equals(Object other)
      Overrides:
      equals in class Object
    • hashCode

      public int hashCode()
      Overrides:
      hashCode in class Object
    • toString

      public String toString()
      Overrides:
      toString in class Object