Package org.forgerock.macaroons
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.
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:
To verify the macaroon you would perform the same key derivation process: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);
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);
- 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
-
-
Nested Class Summary
Nested Classes Modifier and Type Class Description static class
Macaroon.Caveat
Represents a caveat on a macaroon.
-
Constructor Summary
Constructors Constructor Description Macaroon(String location, byte[] identifier, List<Macaroon.Caveat> caveats, byte[] tag)
Constructs a Macaroon from constituent parts.
-
Method Summary
All Methods Static Methods Instance Methods Concrete Methods Modifier and Type Method Description Macaroon
addFirstPartyCaveat(byte[] caveat)
Adds a first-party caveat to the macaroon.Macaroon
addFirstPartyCaveat(String caveat)
Adds a first-party caveat to the macaroon.Macaroon
addFirstPartyCaveat(JsonValue caveat)
Adds a first-party caveat to the macaroon.Macaroon
addThirdPartyCaveat(String location, byte[] caveatKey, byte[] identifier)
Adds a third-party caveat to the macaroon.Macaroon
addThirdPartyCaveat(String location, byte[] caveatKey, String identifier)
Adds a third-party caveat to the macaroon.Macaroon
copy()
Creates a copy of this macaroon.static Macaroon
create(Key key, String location, byte[] identifier)
Creates a fresh Macaroon with no caveats.static Macaroon
create(SigningKey key)
Creates a fresh Macaroon with no caveats, a blank location hint, and a random identifier.static Macaroon
create(SigningKey key, String location)
Creates a fresh Macaroon with no caveats and a random identifier.static Macaroon
create(SigningKey signingKey, String location, byte[] identifier)
Creates a fresh Macaroon with no caveats.static Macaroon
create(SigningKey signingKey, String location, String identifier)
Creates a fresh Macaroon with no caveats.static Macaroon
deserialize(String macaroon)
Deserializes a macaroon from the default format.static Macaroon
deserialize(SerializationFormat format, String macaroon)
Deserializes a macaroon from the given format.boolean
equals(Object other)
static SecretKey
generateKey()
Generates a random macaroon key.List<Macaroon.Caveat>
getCaveats()
Returns a list of caveats associated with this macaroon.Stream<Macaroon.Caveat>
getFirstPartyCaveats()
Returns all of the first-party caveats attached to this macaroon.String
getIdentifier()
A unique identifier for this Macaroon.String
getLocationHint()
An unauthenticated hint for where this macaroon is intended to be used.byte[]
getRawIdentifier()
Returns the raw bytes of the identifier.byte[]
getTag()
The HMAC authentication tag for this macaroon.Stream<Macaroon.Caveat>
getThirdPartyCaveats()
Returns all of the third-party caveats attached to this macaroon.int
hashCode()
List<Macaroon>
prepareForRequest(Macaroon... dischargeMacaroons)
Prepares this macaroon to be sent with a request by binding any discharge macaroons to it.String
serialize()
Serializes the macaroon to a string in the default format.String
serialize(SerializationFormat format)
Serializes the macaroon to a string in the given format.String
toString()
-
-
-
Constructor Detail
-
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. Useverify(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 Detail
-
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 aSigningKey
from aSecretsProvider
.- 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. UsegetRawIdentifier()
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 astime < 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. Usemacaroon.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(String)
-
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. Usemacaroon.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(byte[])
-
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. Usemacaroon.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:
addFirstPartyCaveat(String)
,JsonCaveatVerifier
-
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. Usemacaroon.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. Usemacaroon.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.
-
-