Jmacaroons
Pure Java implementation of Macaroons: Cookies with Contextual Caveats for Decentralized Authorization in the Cloud. Android ready. Online playground available. Project is STALE
Install / Use
/learn @nitram509/JmacaroonsREADME
Macaroons are Better Than Cookies!
This Java library provides an implementation of macaroons[1], which are flexible authorization tokens that work great in distributed systems. Like cookies, macaroons are bearer tokens that enable applications to ascertain whether their holders' actions are authorized. But macaroons are better than cookies!
This project started as a port of libmacaroons[2] library. The primary goals are
- being compatible to libmacaroons
- having no external dependencies, except the Java Runtime
- being very much backward compatible, while using Java8
- focus on binary serialization format (currently, JSON format isn't supported)
- being the reference implementation in the Java community ;-)
There is a playground (testing environment) available, where you can build and verify macaroons online.
License
Usage/Import In Your Project
This library jmacaroons is available via Maven Central. Requires Java 1.11+
Maven
<dependency>
<groupId>com.github.nitram509</groupId>
<artifactId>jmacaroons</artifactId>
<version>0.5.0</version>
</dependency>
Gradle
compile 'com.github.nitram509:jmacaroons:0.5.0'
Build Status
Community & Badges
Creating Your First Macaroon
Lets create a simple macaroon Of course, this macaroon can be displayed in a more human-readable form for easy debugging
<!-- MARKDOWN-AUTO-DOCS:START (CODE:src=./src/example/java/com/github/nitram509/jmacaroons/examples/MacaroonsExamples.java&lines=37-47) --> <!-- The below code snippet is automatically added from ./src/example/java/com/github/nitram509/jmacaroons/examples/MacaroonsExamples.java --> Macaroon create() {
String location = "http://www.example.org";
String secretKey = "this is our super secret key; only we should know it";
String identifier = "we used our secret key";
Macaroon macaroon = Macaroon.create(location, secretKey, identifier);
System.out.println(macaroon.inspect());
// > location http://www.example.org
// > identifier we used our secret key
// > signature e3d9e02908526c4c0039ae15114115d97fdd68bf2ba379b342aaf0f617d0552f
return macaroon;
}
<!-- MARKDOWN-AUTO-DOCS:END -->
Serializing
Macaroons are serialized, using Base64 URL safe encoding RFC 4648. This way you can very easily append it to query string within URIs.
<!-- MARKDOWN-AUTO-DOCS:START (CODE:src=./src/example/java/com/github/nitram509/jmacaroons/examples/MacaroonsExamples.java&lines=49-54) --> <!-- The below code snippet is automatically added from ./src/example/java/com/github/nitram509/jmacaroons/examples/MacaroonsExamples.java --> void serialize() {
Macaroon macaroon = create();
String serialized = macaroon.serialize();
System.out.println("Serialized: " + serialized);
// Serialized: MDAyNGxvY2F0aW9uIGh0dHA6Ly93d3cuZXhhbXBsZS5vcmcKMDAyNmlkZW50aWZpZXIgd2UgdXNlZCBvdXIgc2VjcmV0IGtleQowMDJmc2lnbmF0dXJlIOPZ4CkIUmxMADmuFRFBFdl_3Wi_K6N5s0Kq8PYX0FUvCg
}
<!-- MARKDOWN-AUTO-DOCS:END -->
Alternatively, the V2 binary serializer format is supported.
<!-- MARKDOWN-AUTO-DOCS:START (CODE:src=./src/example/java/com/github/nitram509/jmacaroons/examples/MacaroonsExamples.java&lines=216-221) --> <!-- The below code snippet is automatically added from ./src/example/java/com/github/nitram509/jmacaroons/examples/MacaroonsExamples.java --> void serialize_v2_binary_format() {
Macaroon macaroon = create();
String serialized = macaroon.serialize(V2);
System.out.println("Serialized: " + serialized);
// Serialized: AgEWaHR0cDovL3d3dy5leGFtcGxlLm9yZwIWd2UgdXNlZCBvdXIgc2VjcmV0IGtleQAABiDj2eApCFJsTAA5rhURQRXZf91ovyujebNCqvD2F9BVLw
}
<!-- MARKDOWN-AUTO-DOCS:END -->
Note: Base64 URL safe is supported since v0.3.0. jmacaroons also de-serializes regular Base64 to maintain backward compatibility.
De-Serializing
<!-- MARKDOWN-AUTO-DOCS:START (CODE:src=./src/example/java/com/github/nitram509/jmacaroons/examples/MacaroonsExamples.java&lines=56-63) --> <!-- The below code snippet is automatically added from ./src/example/java/com/github/nitram509/jmacaroons/examples/MacaroonsExamples.java --> void deserialize() {
String serialized = create().serialize();
Macaroon macaroon = Macaroon.deserialize(serialized);
System.out.println(macaroon.inspect());
// > location http://www.example.org
// > identifier we used our secret key
// > signature e3d9e02908526c4c0039ae15114115d97fdd68bf2ba379b342aaf0f617d0552f
}
<!-- MARKDOWN-AUTO-DOCS:END -->
Verifying Your Macaroon
A verifier can only ever successfully verify a macaroon when provided with the macaroon and its corresponding secret - no secret, no authorization.
<!-- MARKDOWN-AUTO-DOCS:START (CODE:src=./src/example/java/com/github/nitram509/jmacaroons/examples/MacaroonsExamples.java&lines=65-71) --> <!-- The below code snippet is automatically added from ./src/example/java/com/github/nitram509/jmacaroons/examples/MacaroonsExamples.java --> void verify() throws InvalidKeyException, NoSuchAlgorithmException {
Macaroon macaroon = create();
MacaroonsVerifier verifier = new MacaroonsVerifier(macaroon);
String secret = "this is our super secret key; only we should know it";
boolean valid = verifier.isValid(secret);
// > True
}
<!-- MARKDOWN-AUTO-DOCS:END -->
Adding Caveats
When creating a new macaroon, you can add a caveat to our macaroon that restricts it to just the account number 3735928559.
<!-- MARKDOWN-AUTO-DOCS:START (CODE:src=./src/example/java/com/github/nitram509/jmacaroons/examples/MacaroonsExamples.java&lines=73-81) --> <!-- The below code snippet is automatically added from ./src/example/java/com/github/nitram509/jmacaroons/examples/MacaroonsExamples.java --> void addCaveat() throws InvalidKeyException, NoSuchAlgorithmException {
String location = "http://www.example.org";
String secretKey = "this is our super secret key; only we should know it";
String identifier = "we used our secret key";
Macaroon macaroon = Macaroon.builder(location, secretKey, identifier)
.addCaveat("account = 3735928559")
.build();
System.out.println(macaroon.inspect());
}
<!-- MARKDOWN-AUTO-DOCS:END -->
Because macaroon objects are immutable, they have to be modified
via Macaroon.builder(). Thus, a new macaroon object will be created.
void addCaveat_modify() throws InvalidKeyException, NoSuchAlgorithmException {
Macaroon macaroon = create();
macaroon = Macaroon.builder(macaroon)
.addCaveat("account = 3735928559")
.build();
System.out.println(macaroon.inspect());
// > location http://www.example.org
// > identifier we used our secret key
// > cid account = 3735928559
// > signature 1efe4763f290dbce0c1d08477367e11f4eee456a64933cf662d79772dbb82128
}
<!-- MARKDOWN-AUTO-DOCS:END -->
Verifying Macaroons With Caveats
The verifier should say that this macaroon is unauthorized because the verifier cannot prove that the caveat (account = 3735928559) is satisfied. We can see that it fails just as we would expect.
<!-- MARKDOWN-AUTO-DOCS:START (CODE:src=./src/example/java/com/github/nitram509/jmacaroons/examples/MacaroonsExamples.java&lines=95-104) --> <!-- The below code snippet is automatically added from ./src/example/java/com/github/nitram509/jmacaroons/examples/MacaroonsExamples.java --> void verify_required_caveats() throws InvalidKeyException, NoSuchAlgorithmException {
String location = "http://www.example.org";
String secretKey = "this is our super secret key; only we should know it";
String identifier = "we used our secret key";
Macaroon macaroon = Macaroon.builder(location, secretKey, identifier)
.addCaveat("account = 3735928559")
.build();
MacaroonsVerifier verifier = new MacaroonsVerifier(macaroon);
verifier.isValid(secretKey);
// > False
<!-- MARKDOWN-AUTO-DOCS:END -->
Caveats like these are called "exact caveats" because there is exactly one way to satisfy them. Either the account number is 3735928559, or it isn't. At verification time, the verifier will check each caveat in the macaroon against the list of satisfied caveats provided to "satisfyExact()". When it finds a match, it knows that the caveat holds and it can move onto the next caveat in the macaroon.
<!-- MARKDOWN-AUTORelated Skills
node-connect
340.5kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
84.2kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
340.5kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
commit-push-pr
84.2kCommit, push, and open a PR
