{"id":19894882,"url":"https://github.com/kzemek/macaroons","last_synced_at":"2025-11-04T21:02:56.648Z","repository":{"id":35898002,"uuid":"40184608","full_name":"kzemek/macaroons","owner":"kzemek","description":"An Erlang Macaroons library compatible with libmacaroons","archived":false,"fork":false,"pushed_at":"2018-03-27T23:53:32.000Z","size":297,"stargazers_count":29,"open_issues_count":1,"forks_count":3,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-06-23T10:51:11.706Z","etag":null,"topics":["authorization","erlang","libmacaroons","macaroons","tokens"],"latest_commit_sha":null,"homepage":"","language":"Erlang","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kzemek.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-08-04T12:52:57.000Z","updated_at":"2024-03-16T12:53:26.000Z","dependencies_parsed_at":"2022-09-08T18:00:50.550Z","dependency_job_id":null,"html_url":"https://github.com/kzemek/macaroons","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/kzemek/macaroons","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kzemek%2Fmacaroons","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kzemek%2Fmacaroons/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kzemek%2Fmacaroons/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kzemek%2Fmacaroons/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kzemek","download_url":"https://codeload.github.com/kzemek/macaroons/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kzemek%2Fmacaroons/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":282713022,"owners_count":26714760,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-11-04T02:00:05.887Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["authorization","erlang","libmacaroons","macaroons","tokens"],"created_at":"2024-11-12T18:34:59.347Z","updated_at":"2025-11-04T21:02:56.612Z","avatar_url":"https://github.com/kzemek.png","language":"Erlang","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003e Note: This README file is a slightly adapted version of README from\n\u003e [rescrv/libmacaroons].\n\nMacaroons are Better Than Cookies!\n==================================\n\nThis library is [libmacaroons]-compatible Erlang implementation of [macaroons].\nMacaroons are flexible authorization tokens that work great in distributed\nsystems.  Like cookies, macaroons are bearer tokens that enable applications to\nascertain whether their holders' actions are authorized.  But macaroons are\nbetter than cookies!\n\nWhy Macaroons?\n--------------\n\nMacaroons are great for authorization because they're similar enough to cookies\nto be immediately usable by developers, but they include several features not\npresent in cookies or other token-based authorization schemes.  In particular:\n\n - Delegation with Contextual Caveats (i.e., confinement of the usage context):  \n   Macaroons support delegation.  Give your macaroon to another user, and they\n   can act on your behalf, with the same authority.  Cookies permit delegation\n   as well, but the remaining features of macaroons make it much more safe and\n   practical to pass around macaroons than cookies.  In particular, macaroons\n   can limit when, where, and by whom the delegated authority can be exercised\n   (e.g., within one minute, from a machine that holds a certain key, or by a\n   certain logged-in user), by using attenuation and third-party caveats.\n\n - Attenuation:  Macaroons enable users to add caveats to the macaroon that\n   attenuate how, when, and where it may be used.  Unlike cookies, macaroons\n   permit their holder to attenuate them before delegating.  Whereas cookies and\n   authorization tokens enable an application to get access to all of your data\n   and to perform actions on your behalf with your full privileges, macaroons\n   enable you to restrict what they can do. Those questionable startups that\n   \"just want the address book, we swear it,\" become a whole lot more secure\n   when the target application supports macaroons, because macaroons enable you\n   to add caveats that restrict what the application can do.\n\n - Proof-Carrying:  Macaroons are efficient, because they carry their own proof\n   of authorization---cryptographically secured, of course.  A macaroon's\n   caveats are constructed using chained HMAC functions, which makes it really\n   easy to add a caveat, but impossible to remove a caveat.  When you attenuate\n   a macaroon and give it to another application, there is no way to strip the\n   caveats from the macaroon.  It's easy for the entity that created a macaroon\n   to verify the embedded proof, but others cannot.\n\n - Third-Party Caveats:  Macaroons allow caveats to specify predicates that are\n   enforced by third parties.  A macaroon with a third-party caveat will only be\n   authorized when the third party certifies that the caveat is satisfied.  This\n   enables loosely coupled distributed systems to work together to authorize\n   requests.  For example, a data store can provide macaroons that are\n   authorized if and only if the application's authentication service says that\n   the user is authenticated.  The user obtains a proof that it is\n   authenticated from the authentication service, and presents this proof\n   alongside the original macaroon to the storage service.  The storage service\n   can verify that the user is indeed authenticated, without knowing anything\n   about the authentication service's implementation---in a standard\n   implementation, the storage service can authorize the request without even\n   communicating with the authentication service.\n\n - Simple Verification:  Macaroons eliminate complexity in the authorization\n   code of your application.  Instead of hand-coding complex conditionals in\n   each routine that deals with authorization, and hoping that this logic is\n   globally consistent, you construct a general verifier for macaroons.  This\n   verifier knows how to soundly check the proofs embedded within macaroons to\n   see if they do indeed authorize access.\n\n - Decoupled Authorization Logic:  Macaroons separate the policy of your\n   application (who can access what, when), from the mechanism (the code that\n   actually upholds this policy).  Because of the way the verifier is\n   constructed, it is agnostic to the actual underlying policies it is\n   enforcing.  It simply observes the policy (in the form of an embedded proof)\n   and certifies that the proof is correct.  The policy itself is specified when\n   macaroons are created, attenuated, and shared.  You can easily audit this\n   code within your application, and ensure that it is upheld everywhere.\n\nThe rest of this document walks through the specifics of macaroons and see just\nhow easy authorization can be.  So pour a fresh cup of espresso to enjoy\nalongside your macaroons and read on!\n\nInstalling Macaroons\n--------------------\n\nBuilding `macaroons` has the following requirements:\n\n* [libsodium] installed on the system\n* C compiler\n* Erlang compiled with enabled dirty NIFs\n* [rebar]\n\n`macaroons` uses [enacl] to provide libsodium cryptographic functions in Erlang.\n\nIncluding `macaroons` is trivial, provided your Erlang application uses [rebar]\nfor dependency management. Simply add the library to the list of dependencies in\nrebar.conf:\n\n```Erlang\n{deps, [\n    {macaroons, \"1.0.2\", {git, \"https://github.com/kzemek/macaroons.git\", {tag, \"1.0.2\"}}}\n]}.\n```\n\nCreating Your First Macaroon\n----------------------------\n\nImagine that you ran a bank, and were looking to use macaroons as the basis of\nyour authorization logic.  Assuming you already installed libmacaroons, you can\ncreate a macaroon like this:\n\n```Erlang\n1\u003e Secret = \"this is our super secret key; only we should know it\".\n2\u003e Public = \"we used our secret key\".\n3\u003e Location = \"http://mybank/\".\n4\u003e M = macaroon:create(Location, Secret, Public).\n```\n\nWe've created our first macaroon!\n\nYou can see here that it took three pieces of information to create a macaroon.\nWe start with a secret.  Here, we just have English text, but in reality we\nwould want to use something more random and less predictable (see below for a\nsuggestion on how to generate one).  The public portion tells us which secret we\nused to create the macaroon, but doesn't give anyone else a clue as to the\ncontents of the secret.  Anyone in possession of the macaroon can see the public\nportion:\n\n```Erlang\n5\u003e macaroon:identifier(M).\n\u003c\u003c\"we used our secret key\"\u003e\u003e\n```\n\nThis public portion, known as the macaroon's identifier, can be anything that\nenables us to remember our secret.  In this example, we know that the string 'we\nused our secret key' always refers to this secret.  We could just as easily keep\na database mapping public macaroon identities to private secrets, or encrypt the\npublic portion using a key known only to us.  The only requirement here is that\nour application be able to remember the secret given the public portion, and\nthat no one else is able to guess the secret.\n\nOur macaroon includes some additional information that enables us to add caveats\nand figure out where the macaroon should be used.  The macaroon's location gives\na hint to the user about where the macaroon is accepted for authorization.  This\nhint is a free-form string maintained to help applications figure out where to\nuse macaroons; the libmacaroons library (and by extension, the Erlang wrapper)\ndoes not ascribe any meaning to this location.\n\nEach macaroon also has a signature that is the key used to add caveats and\nverify the macaroon.  The signature is computed by the macaroons library, and is\nunique to each macaroon.  Applications should never need to directly work with\nthe signature of the macaroon.  Any entity in possession of the macaroon's\nsignature should be assumed to possess the macaroon itself.\n\nBoth of these pieces of information are publicly accessible:\n\n```Erlang\n6\u003e macaroon:location(M).\n\u003c\u003c\"http://mybank/\"\u003e\u003e\n7\u003e macaroon:signature(M).\n\u003c\u003c\"e3d9e02908526c4c0039ae15114115d97fdd68bf2ba379b342aaf0f617d0552f\"\u003e\u003e\n```\n\nWe can share this macaroon with others by serializing it.  The serialized form\nis pure-ASCII, and is safe for inclusion in secure email, a standard HTTPS\ncookie, or a URL.  We can get the serialized form with:\n\n```Erlang\n8\u003e macaroon:serialize(M).\n{ok,\u003c\u003c\"MDAxY2xvY2F0aW9uIGh0dHA6Ly9teWJhbmsvCjAwMjZpZGVudGlmaWVyIHdlIHVzZWQgb3VyIHNlY3JldCBrZXkKMDAyZnNpZ25hdHVyZSDj\"...\u003e\u003e}\n```\n\nOf course, this serialized form can be displayed in a more human-readable form\nfor easy debugging:\n\n```Erlang\n9\u003e io:format(\"~s\", [macaroon:inspect(M)]).\nlocation http://mybank/\nidentifier we used our secret key\nsignature e3d9e02908526c4c0039ae15114115d97fdd68bf2ba379b342aaf0f617d0552f\nok\n```\n\nAdding Caveats\n--------------\n\nAt this point, we have an unconstrained macaroon that authorizes everything\nwithin our bank.  In practice, such a macaroon is dangerous, because very few\npeople should hold such power.  Let's add a caveat to our macaroon that\nrestricts it to just the account number 3735928559.\n\n```Erlang\n10\u003e macaroon:add_first_party_caveat(M, \"account = 3735928559\").\n```\n\nThis new macaroon includes the same identifier and location that our old\nmacaroon from our initial macaroon, but includes the additional caveat that\nrestricts the bank account.  The signature of this new macaroon is different,\nand incorporates the new caveat we've just added.  An entity in possession of\nthis new macaroon cannot simply remove our new caveat to construct the old\nmacaroon:\n\n```Erlang\n11\u003e M2 = macaroon:add_first_party_caveat(M, \"account = 3735928559\").\n12\u003e io:format(\"~s\", [macaroon:inspect(M2)]).\nlocation http://mybank/\nidentifier we used our secret key\ncid account = 3735928559\nsignature 1efe4763f290dbce0c1d08477367e11f4eee456a64933cf662d79772dbb82128\nok\n```\n\nOf course, we can add a few more caveats, and the macaroon's signature will\nchange with each of them.\n\n```Erlang\n13\u003e M3 = macaroon:add_first_party_caveat(M2, \"time \u003c 2020-01-01T00:00\").\n14\u003e macaroon:signature(M3).\n\u003c\u003c\"b5f06c8c8ef92f6c82c6ff282cd1f8bd1849301d09a2db634ba182536a611c49\"\u003e\u003e\n15\u003e M4 = macaroon:add_first_party_caveat(M3, \"email = alice@example.org\").\n16\u003e macaroon:signature(M4).\n\u003c\u003c\"ddf553e46083e55b8d71ab822be3d8fcf21d6bf19c40d617bb9fb438934474b6\"\u003e\u003e\n17\u003e io:format(\"~s\", [macaroon:inspect(M4)]).\nlocation http://mybank/\nidentifier we used our secret key\ncid account = 3735928559\ncid time \u003c 2020-01-01T00:00\ncid email = alice@example.org\nsignature ddf553e46083e55b8d71ab822be3d8fcf21d6bf19c40d617bb9fb438934474b6\nok\n```\n\nThe combination of all caveats in this macaroon authorize alice@example.org to\naccess account 3735928559 until the end of 2019.  Alice may present this\nmacaroon to the bank any time she wishes to prove to the bank that she is\nauthorized to access her account.  Ideally, she'll transmit the serialized form\nof the macaroon to the bank:\n\n```Erlang\n18\u003e {ok, Msg} = macaroon:serialize(M4).\n{ok,\u003c\u003c\"MDAxY2xvY2F0aW9uIGh0dHA6Ly9teWJhbmsvCjAwMjZpZGVudGlmaWVyIHdlIHVzZWQgb3VyIHNlY3JldCBrZXkKMDAxZGNpZCBhY2NvdW50\"...\u003e\u003e}\n19\u003e % send msg to the bank\n```\n\nVerifying Macaroons\n-------------------\n\nOur bank application's purpose is to protect users accounts from unauthorized\naccess.  For that reason, it cannot just accept anything that looks like a\nmacaroon---that would defeat the point of using macaroons in the first place.\nSo how can we ensure that only authorized users access the bank?\n\nWe can determine whether a request is authorized through a process called\nverification.  First, we construct a verifier that can determine whether the\ncaveats on macaroons are satisfied.  We can then use our verifier to determine\nwhether a given macaroon is authorized in the context of the request.  For\nexample, our bank account application knows the account number specified in the\nrequest, and can specify `account = #` when building the verifier.  The\nverifier can then check that this matches the information within the macaroon,\nand authorize the macaroon if it does indeed match.\n\nLet's walk through the verification process for Alice's macaroon that we\nconstructed in the previous section.  The first step, of course, is for the bank\nto deserialize the macaroon from the message.  This converts the macaroon into a\nform we can work with.\n\n```Erlang\n1\u003e {ok, M} = macaroon:deserialize(Msg).\n2\u003e io:format(\"~s\", [macaroon:inspect(M)]).\nlocation http://mybank/\nidentifier we used our secret key\ncid account = 3735928559\ncid time \u003c 2020-01-01T00:00\ncid email = alice@example.org\nsignature ddf553e46083e55b8d71ab822be3d8fcf21d6bf19c40d617bb9fb438934474b6\nok\n```\n\nWe have the same macaroon that Alice believes authorizes her to access her own\naccount, but we must verify this for ourselves.  One (very flawed) way we could\ntry to verify this macaroon would be to manually parse it and authorize the\nrequest if its caveats are true.  But handling things this way completely\nsidesteps all the crypto-goodness that macaroons are built upon.\n\nAnother approach to verification would be to use libmacaroons's built-in\nverifier to process the macaroon.  The verifier hides many of the details of the\nverification process, and provides a natural way to work with many kinds of\ncaveats.  The verifier itself is constructed once, and may be re-used to verify\nmultiple macaroons.\n\n```Erlang\n3\u003e V = macaroon_verifier:create().\n```\n\nLet's go ahead and try to verify the macaroon to see if the request is\nauthorized.  To verify the request, we need to provide the verifier with Alice's\nmacaroon, and the secret that was used to construct it.  In a real application,\nwe would retrieve the secret using `macaroon:identifier(M)`; here, we know the\nsecret and provide it directly.  A verifier can only ever successfully verify\nthe macaroon when provided with the macaroon and its corresponding secret---no\nsecret, no authorization.\n\nIntuitively, our verifier should say that this macaroon is unauthorized because\nour verifier cannot prove that any of the caveats we've added are satisfied.  We\ncan see that it fails just as we would expect:\n\n```Erlang\n4\u003e macaroon_verifier:verify(V, M, Secret).\n{error,{unverified_caveat,\u003c\u003c\"account = 3735928559\"\u003e\u003e}}\n```\n\nWe can inform the verifier of the caveats used by our application using two\ndifferent techniques.  The first technique is to directly provide the verifier\nwith the caveats that match the context of the request.  For example, every\naccount-level action in a typical banking application is performed by a specific\nuser and targets a specific account.  This information is fixed for each\nrequest, and is known at the time of the request.  We can tell the verifier\ndirectly about these caveats like so:\n\n```Erlang\n5\u003e V1 = macaroon_verifier:satisfy_exact(V, \"account = 3735928559\").\n6\u003e V2 = macaroon_verifier:satisfy_exact(V1, \"email = alice@example.org\").\n```\n\nCaveats like these are called *exact caveats* because there is exactly one way\nto satisfy them.  Either the account number is 3735928559, or it isn't.  At\nverification time, the verifier will check each caveat in the macaroon against\nthe list of satisfied caveats provided to `satisfyExact`.  When it finds a\nmatch, it knows that the caveat holds and it can move onto the next caveat in\nthe macaroon.\n\nGenerally, you will specify multiple true statements as exact caveats, and let\nthe verifier decide which are relevant to each macaroon at verification time.\nIf you provide all exact caveats known to your application to the verifier, it\nbecomes trivial to change policy decisions about authorization.  The server\nperforming authorization can treat the verifier as a black-box and does not need\nto change when changing the authorization policy.  The actual policy is enforced\nwhen macaroons are minted and when caveats are embedded.  In our banking\nexample, we could provide some additional satisfied caveats to the verifier,\nto describe some (or all) of the properties that are known about the current\nrequest.  In this manner, the verifier can be made more general, and be\n\"future-proofed\", so that it will still function correctly even if somehow  \nthe authorization policy for Alice changes; for example, by adding the three\nfollowing facts, the verifier will continue to work even if Alice decides to\nself-attenuate her macaroons to be only usable from her IP address and browser:\n\n```Erlang\n7\u003e V3 = macaroon_verifier:satisfy_exact(V2, \"IP = 127.0.0.1\").\n8\u003e V4 = macaroon_verifier:satisfy_exact(V3, \"browser = Chrome\").\n9\u003e V5 = macaroon_verifier:satisfy_exact(V4, \"action = deposit\").\n```\n\nAlthough it's always possible to satisfy a caveat within a macaroon by providing\nit directly to the verifier, doing so can be quite tedious.  Consider the caveat\non access time embedded within Alice's macaroon.  While an authorization routine\ncould provide the exact caveat `time \u003c 2020-01-01T00:00`, doing so would\nrequire inspecting the macaroon before building the verifier.  Just like using\nMD5 to hash passwords, inspecting a macaroon's structure to build a verifier for\nit is considered to be very bad practice, and should be violently demonized in\nHacker News discussions with vague, slightly inaccurate allusions to pbkdf2.\n\nSo how can we tell our verifier that the caveat on access time is satisfied?  We\ncould provide many exact caveats of the form `time \u003c YYYY-mm-ddTHH:MM`, but\nthis reeks of inefficiency. The second technique for satisfying caveats provides\na more general solution.\n\nCalled *general caveats*, the second technique for informing the verifier that\na caveat is satisfied allows for expressive caveats.  Whereas exact caveats are\nchecked by simple byte-wise equality, general caveats are checked using an\napplication-provided callback that returns true if and only if the caveat is\ntrue within the context of the request.  There's no limit on the contents of a\ngeneral caveat, so long as the callback understands how to determine whether it\nis satisfied.\n\nWe can verify the time caveat on Alice's macaroon by writing a function that\nchecks the current time against the time specified by the caveat (the example\nuses [qdate] library for date operations):\n\n```Erlang\n10\u003e CheckTime =\n10\u003e     fun\n10\u003e         (\u003c\u003c\"time \u003c \", Date/binary\u003e\u003e) -\u003e\n10\u003e             qdate:compare(erlang:now(), '\u003c=', Date);\n10\u003e         (_) -\u003e\n10\u003e             false\n10\u003e     end.\n```\n\nThis callback processes all caveats that begin with `time \u003c `, and returns\n`true` if the specified time has not yet passed.  We can see that our caveat\ndoes indeed return `true` when the caveat holds, and `false` otherwise:\n\n```Erlang\n11\u003e CheckTime(\u003c\u003c\"time \u003c 2020-01-01T00:00\"\u003e\u003e).\ntrue\n12\u003e CheckTime(\u003c\u003c\"time \u003c 2014-01-01T00:00\"\u003e\u003e).\nfalse\n13\u003e CheckTime(\u003c\u003c\"account = 3735928559\"\u003e\u003e).\nfalse\n```\n\nWe can provide the `CheckTime` function directly to the verifier, so that it\nmay check time-based predicates.\n\n```Erlang\n14\u003e V6 = macaroon_verifier:satisfy_general(V5, CheckTime).\n```\n\nIt's finally time to verify our macaroon!  Now that we've informed the verifier\nof all the various caveats that our application could embed within a macaroon,\nwe can expect that the verification step will succeed.\n\n```Erlang\n15\u003e macaroon_verifier:verify(V6, M, Secret).\nok\n```\n\nMore importantly, the verifier will also work for macaroons we've not yet seen,\nlike one that only permits Alice to deposit into her account:\n\n```Erlang\n16\u003e N = macaroon:add_first_party_caveat(M, \"action = deposit\").\n17\u003e macaroon_verifier:verify(V6, N, Secret).\nok\n```\n\nEqually important is the verifier's ability to reject improper macaroons because\nthey are not authorized.  An improper macaroon could have additional caveats not\nknown to the verifier, or have false general caveats.  Worse, an unauthorized\nmacaroon could be an attempt by a determined attacker to break into our bank.\nThe verifier we've built will reject all of these scenarios:\n\n```Erlang\n18\u003e % Unknown caveat\n18\u003e N2 = macaroon:add_first_party_caveat(M, \"OS = Windows XP\").\n19\u003e macaroon_verifier:verify(V6, N2, Secret).\n{error,{unverified_caveat,\u003c\u003c\"OS = Windows XP\"\u003e\u003e}}\n20\u003e\n20\u003e % False caveat\n20\u003e N3 = macaroon:add_first_party_caveat(M, \"time \u003c 2014-01-01T00:00\").\n21\u003e macaroon_verifier:verify(V6, N3, Secret).\n{error,{unverified_caveat,\u003c\u003c\"time \u003c 2020-01-01T00:00\"\u003e\u003e}}\n22\u003e\n22\u003e % Bad secret\n22\u003e macaroon_verifier:verify(V6, M, \"this is not the secret we were looking for\").\n{error,{bad_signature_for_macaroon,\u003c\u003c\"we used our secret key\"\u003e\u003e}}\n23\u003e\n23\u003e % Incompetent hackers trying to change the signature\n23\u003e {ok, N4} = macaroon:deserialize(\u003c\u003c\"MDAxY2xvY2F0aW9uIGh0dHA6Ly9teWJhbmsvCjAwMjZpZGVudGlmaWVyIHdlIHVzZWQgb3VyIHNl\\nY3JldCBrZXkKMDAxZGNpZCBhY2NvdW50ID0gMzczNTkyODU1OQowMDIwY2lkIHRpbWUgPCAyMDIw\\nLTAxLTAxVDAwOjAwCjAwMjJjaWQgZW1haWwgPSBhbGljZUBleGFtcGxlLm9yZwowMDJmc2lnbmF0\\ndXJlID8f19FL+bkC9p/aoMmIecC7GxdOcLVyUnrv6lJMM7NSCg==\\n\"\u003e\u003e).\n24\u003e io:format(\"~s\", [macaroon:inspect(N4)]).\nlocation http://mybank/\nidentifier we used our secret key\ncid account = 3735928559\ncid time \u003c 2020-01-01T00:00\ncid email = alice@example.org\nsignature 3f1fd7d14bf9b902f69fdaa0c98879c0bb1b174e70b572527aefea524c33b352\nok\n25\u003e macaroon:signature(M) =:= macaroon:signature(N4).\nfalse\n26\u003e macaroon_verifier:verify(V6, N4, Secret).\n{error,{bad_signature_for_macaroon,\u003c\u003c\"we used our secret key\"\u003e\u003e}}\n```\n\nUsing Third-Party Caveats\n-------------------------\n\nLike first-party caveats, third-party caveats restrict the context in which a\nmacaroon is authorized, but with a different form of restriction.  Where a\nfirst-party caveat is checked directly within the verifier, a third-party caveat\nis checked by the third-party, who provides a discharge macaroon to prove that\nthe original third-party caveat is true.  The discharge macaroon is recursively\ninspected by the verifier; if it verifies successfully, the discharge macaroon\nserves as a proof that the original third-party caveat is satisfied.  Of course,\nnothing stops discharge macaroons from containing embedded first- or third-party\ncaveats for the verifier to consider during verification.\n\nLet's rework the above example to provide Alice with access to her account only\nafter she authenticates with a service that is separate from the service\nprocessing her banking transactions.\n\nAs before, we'll start by constructing a new macaroon with the caveat that is\nlimited to Alice's bank account.\n\n```Erlang\n1\u003e Secret = \"this is a different super-secret key; never use the same secret twice\".\n2\u003e Public = \"we used our other secret key\".\n3\u003e Location = \"http://mybank/\".\n4\u003e M = macaroon:create(Location, Secret, Public).\n5\u003e M2 = macaroon:add_first_party_caveat(M, \"account = 3735928559\").\n6\u003e io:format(\"~s\", [macaroon:inspect(M2)]).\nlocation http://mybank/\nidentifier we used our other secret key\ncid account = 3735928559\nsignature 1434e674ad84fdfdc9bc1aa00785325c8b6d57341fc7ce200ba4680c80786dda\nok\n```\n\nSo far, so good.  Now let's add a third party caveat to this macaroon that\nrequires that Alice authenticate with `http://auth.mybank/` before being\nauthorized access to her account.  To add a third-party caveat we'll need to\nspecify a location hint, generate a secret, and somehow be able to identify this\nsecret later.  Like the location used when creating a macaroon, the location\nused when adding a third-party caveat is simply a hint to the application that\nis uninterpreted by libmacaroons.\n\nThe secret and identity are handled differently than during creation, however,\nbecause they need to be known to the third party.  To do this, we'll provide the\nthird-party with a randomly generated `CaveatKey` and the predicate we wish\nfor it to check.  In response, it will give us an identifier that we can embed\nwithin the macaroon.  Later, when we need to construct a discharge macaroon, we\ncan provide it with just this identifier, from which it can recall the key and\npredicate to check.\n\nHere's what this looks like in code:\n\n```Erlang\n7\u003e % you'll likely want to use a higher entropy source to generate this key\n7\u003e CaveatKey = \"4; guaranteed random by a fair toss of the dice\".\n8\u003e Predicate = \"user = Alice\".\n9\u003e % send_to_auth(CaveatKey, Predicate).\n9\u003e % Identifier = recv_from_auth().\n9\u003e Identifier = \"this was how we remind auth of key/pred\".\n10\u003e M3 = macaroon:add_third_party_caveat(M2, \"http://auth.mybank/\", CaveatKey, Identifier).\n11\u003e io:format(\"~s\", [macaroon:inspect(M3)]).\nlocation http://mybank/\nidentifier we used our other secret key\ncid account = 3735928559\ncid this was how we remind auth of key/pred\nvid f678186d92de0589225da7827ed26509ed33eb83601b9e96b21cdee80f4a9834176b77f06f3fc3b59a1547231362797158ca61226681a7946fa2781bef9a95e746f52e91af74f9ae\ncl http://auth.mybank/\nsignature 9a986629809a40171d4cc32c3814fbf9e0d82f0b1eebb781fc3675d204e6066a\nok\n```\n\nWe now have a macaroon with a third-party caveat.  The most interesting thing to\nnote about this macaroon is that it includes no information that reveals the\npredicate it encodes.  The third-party service knows the key and the predicate,\nand internally associates them with the identifier, but the identifier itself is\narbitrary and betrays no information about the predicate to others.  The service\nat `http://auth.mybank/` can authenticate that the user is Alice, and provide\nproof that the caveat is satisfied, without revealing Alice's identity.  Other\nservices can verify M and its associated discharge macaroon, without knowing the\npredicates the third-parties verified.\n\nThe process for discharging third party caveats starts with the holder of the\ninitial root macaroon, Alice.  Alice looks at the macaroon for the list of third\nparty caveat (location, identifier) pairs that must be addressed.\n\n```Erlang\n12\u003e macaroon:third_party_caveats(M3).\n[{\u003c\u003c\"http://auth.mybank/\"\u003e\u003e,\n  \u003c\u003c\"this was how we remind auth of key/pred\"\u003e\u003e}]\n```\n\nIn a real application, we'd look at these third party caveats, and contact each\nlocation to retrieve the requisite discharge macaroons.  We would include the\nidentifier for the caveat in the request itself, so that the server can recall\nthe secret used to create the third-party caveat.  The server can then generate\nand return a new macaroon that discharges the caveat:\n\n```Erlang\n13\u003e D = macaroon:create(\"http://auth.mybank/\", CaveatKey, Identifier).\n14\u003e D2 = macaroon:add_first_party_caveat(D, \"time \u003c 2020-01-01T00:00\").\n15\u003e io:format(\"~s\", [macaroon:inspect(D2)]).\nlocation http://auth.mybank/\nidentifier this was how we remind auth of key/pred\ncid time \u003c 2020-01-01T00:00\nsignature 2ed1049876e9d5840950274b579b0770317df54d338d9d3039c7c67d0d91d63c\nok\n```\n\nThis new macaroon enables the verifier to determine that the third party caveat\nis satisfied.  Our target service added a time-limiting caveat to this macaroon\nthat ensures that this discharge macaroon does not last forever.  This ensures\nthat Alice (or, at least someone authenticated as Alice) cannot use the\ndischarge macaroon indefinitely and will eventually have to re-authenticate.\n\nOnce Alice has both the root macaroon and the discharge macaroon in her\npossession, she can make the request to the target service.  Making a request\nwith discharge macaroons is only slightly more complicated than making requests\nwith a single macaroon.  In addition to serializing and transmitting all\ninvolved macaroons, there is preparation step that binds the discharge macaroons\nto the root macaroon.  This binding step ensures that the discharge macaroon is\nuseful only when presented alongside the root macaroon.  The root macaroon is\nused to bind the discharge macaroons like this:\n\n```Erlang\n16\u003e DP = macaroon:prepare_for_request(M3, D2).\n```\n\nIf we were to look at the signatures on these prepared discharge macaroons, we\nwould see that the binding process has irreversibly altered their signature(s).\n\n```Erlang\n17\u003e macaroon:signature(D2).\n\u003c\u003c\"2ed1049876e9d5840950274b579b0770317df54d338d9d3039c7c67d0d91d63c\"\u003e\u003e\n18\u003e macaroon:signature(DP).\n\u003c\u003c\"dd3690773cd08867dc544d7a84a26aae2581f56a21f77f886812865abdea9554\"\u003e\u003e\n```\n\nThe root macaroon `M3` and its discharge macaroons `DP` are ready for the\nrequest.  Alice can serialize them all and send them to the bank to prove she is\nauthorized to access her account.  The bank can verify them using the same\nverifier we built before.  We provide the discharge macaroons as a third\nargument to the verify call:\n\n```Erlang\n19\u003e macaroon_verifier:verify(V6, M3, Secret, [DP]).\nok\n```\n\nWithout the `prepareForRequest` call, the verification would fail:\n\n```Erlang\n20\u003e macaroon_verifier:verify(V6, M3, Secret, [D2]).\n{error,{bad_signature_for_macaroon,\u003c\u003c\"this was how we remind auth of key/pred\"\u003e\u003e}}\n```\n\nChoosing Secrets\n----------------\n\nFor clarity, we've generated human-readable secrets that we use as the root keys\nof all of our macaroons.  In practice, this is terribly insecure and can lead to\nmacaroons that can easily be forged because the secret is too predictable.  To\navoid this, we recommend generating secrets using a sufficient number of\nsuitably random bytes.  Because the bytes are a secret key, they should be drawn\nfrom a source with enough entropy to ensure that the key cannot be guessed\nbefore the macaroon falls out of use.\n\nThe `macaroon` module exposes a constant that is the ideal number of bytes\nthese secret keys should contain.  Any shorter is wasting an opportunity for\nsecurity.\n\n```Erlang\n1\u003e macaroon:suggested_secret_length().\n32\n```\n\nThird-Party Caveats with Public Keys\n------------------------------------\n\nPublic key cryptography can enable much more efficient schemes for adding\nthird-party caveats.  In the above example where we added a third-party caveat,\nthe caveat's identifier was generated by the third party and retrieved with in\none round trip.  We can eliminate the round trip when the third party has a\nwell-known public key.  We can encrypt the caveat's secret, and the predicate to\nbe checked using this public key, and use the ciphertext directly as the\ncaveat's identifier.  This saves a round trip, and frees the third party from\nhaving to remember an association between identifiers and key/predicate pairs.\n\n[rescrv/libmacaroons]: https://github.com/rescrv/libmacaroons/blob/ca1e875b06862e6cd515a4e3c0e5b124ef502366/README\n[libmacaroons]: https://github.com/rescrv/libmacaroons\n[macaroons]: http://research.google.com/pubs/pub41892.html\n[qdate]: https://github.com/choptastic/qdate\n[libsodium]: https://github.com/jedisct1/libsodium\n[rebar]: https://rebar3.org\n[enacl]: https://github.com/jlouis/enacl\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkzemek%2Fmacaroons","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkzemek%2Fmacaroons","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkzemek%2Fmacaroons/lists"}