{"id":19821571,"url":"https://github.com/doawoo/macaroon","last_synced_at":"2025-10-21T17:44:19.758Z","repository":{"id":45539921,"uuid":"312212286","full_name":"doawoo/macaroon","owner":"doawoo","description":"\"Cookies with Contextual Caveats\" for Elixir","archived":false,"fork":false,"pushed_at":"2023-08-03T21:45:48.000Z","size":131,"stargazers_count":51,"open_issues_count":1,"forks_count":7,"subscribers_count":3,"default_branch":"main","last_synced_at":"2024-05-31T10:07:45.417Z","etag":null,"topics":["caveat","cookies","elixir","macaroons","verification"],"latest_commit_sha":null,"homepage":"","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/doawoo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2020-11-12T08:23:35.000Z","updated_at":"2024-02-02T16:02:03.000Z","dependencies_parsed_at":"2022-09-06T03:41:30.244Z","dependency_job_id":null,"html_url":"https://github.com/doawoo/macaroon","commit_stats":{"total_commits":51,"total_committers":4,"mean_commits":12.75,"dds":0.05882352941176472,"last_synced_commit":"56cf057b89ca77375b5f66bf2157a64b03764479"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doawoo%2Fmacaroon","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doawoo%2Fmacaroon/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doawoo%2Fmacaroon/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doawoo%2Fmacaroon/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/doawoo","download_url":"https://codeload.github.com/doawoo/macaroon/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224256777,"owners_count":17281602,"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","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":["caveat","cookies","elixir","macaroons","verification"],"created_at":"2024-11-12T10:29:31.211Z","updated_at":"2025-10-21T17:44:14.393Z","avatar_url":"https://github.com/doawoo.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Macaroons (For Elixir)\n\n\n![Elixir CI](https://github.com/doawoo/macaroon/workflows/Elixir%20CI/badge.svg?branch=main)\n[![Coverage Status](https://coveralls.io/repos/github/doawoo/macaroon/badge.svg?branch=main)](https://coveralls.io/github/doawoo/macaroon?branch=main)\n![MIT License](https://img.shields.io/badge/License-MIT-important)\n![Hex.pm](https://img.shields.io/hexpm/v/macaroon)\n\n\nCookies but better. For Elixir.\n\n**Fully Functional But Probably Needs More Testing :)**\n\nRequires: libsodium (can usually be easily installed using your favorite package manager)\n\n---\n\n## Table of Contents\n\n* [What Are They?](https://github.com/doawoo/macaroon#what-are-they)\n  * [Basic Summary](https://github.com/doawoo/macaroon#basic-summary)\n  * [Caveats](https://github.com/doawoo/macaroon#caveats)\n  * [Verification](https://github.com/doawoo/macaroon#verification)\n  * [Discharging](https://github.com/doawoo/macaroon#discharging)\n    * [Well-Known RSA public key](https://github.com/doawoo/macaroon#well-known-rsa-public-key)\n    * [Round Trip](https://github.com/doawoo/macaroon#round-trip)\n* [Examples](https://github.com/doawoo/macaroon#examples)\n  * [Create a Macaroon](https://github.com/doawoo/macaroon#creating-a-macaroon)\n  * [Adding Caveats](https://github.com/doawoo/macaroon#adding-caveats)\n  * [Verification](https://github.com/doawoo/macaroon#verification-1)\n  * [Serialization and Deserialize](https://github.com/doawoo/macaroon#serialization-and-deserialize)\n    * [JSON](https://github.com/doawoo/macaroon#json)\n    * [Binary](https://github.com/doawoo/macaroon#binary)\n* [Misc](https://github.com/doawoo/macaroon#misc)\n  * [Building on Apple Silicon](https://github.com/doawoo/macaroon#building-on-apple-silicon)\n  * [Building on Windows](https://github.com/doawoo/macaroon#building-on-windows)\n---\n\nIf you'd like to know all the details about Macaroons, I encourage you to read the [research paper](https://research.google/pubs/pub41892/)!\n\nI'll summarize it up a bit below:\n\n## What are they?\n\n### Basic Summary\n\nMacaroons are bearer credentials, similar to cookies, API tokens, or JWTs. They're presented upon each of a client's request. Where Macaroons differ from most bearer credentials are the fact that they securely embed caveats (permissions, reasons, capabilities, etc.) inside the credential itself. These caveats are signed using a secret key, so the target service can trust the credential as it is presented along with the client's request. The target service can evaluate the request, and the caveats to see if the operation is allowed.\n\n### Caveats\n\nCaveats are simple statements that define what capabilities, identities, or authority the Macaroon holds.\n\nHere's an example list of caveats a Macaroon may hold pertaining to an imaginary file sharing service:\n\n```\n1. user_id = 1234\n2. user_upload_limit = 4MB\n3. user_download_limit = 100MB\n4. upload_namespace=/users/1234/*\n5. timestamp \u003c= 1/10/2021-5:48:47PM\n```\n\nWith the examples above, the service should respect the requested operation should it meet the Macaroon's declared and signed caveats.\n\nThese caveats can contain any information in any string-based format. It's up to the service author to design the predicate language used.\n\n### Verification\n\nWhen operating a service, you can verify a Macaroon \"exactly\" or \"generally\". \n\nExact verification means the data of the caveat must match byte-per-byte. \n\nGeneral verification allows the service author to provide simple callbacks which receive the caveat and can return `true` or `false` to indicate if it is met.\n\n### Discharging\n\nWhen you want to have a third-party validate a caveat, you must have them issue you a \"discharge\" Macaroon that can prove that specific caveat.\nThere are 2 well know ways to do this:\n\n#### Well-Known RSA public key\n\n(this is my favorite method of third-party proof!)\n\n1. Establish a relationship between the two servers (in this case a public/private RSA key pair)\n2. Encrypt your third-party predicate using the `add_rsa_third_party_caveat/5` function\n3. Send this Macaroon to the client -- which will read the location and send the caveat id to the third-party server\n4. The third-party server will use the `decrypt_rsa_third_party_caveat/3` function to take apart the cipher text into the predicate and the root key\n5. The third-party server will create a discharge Macaroon using the root key extracted from the cipher text in step 4 -- bind it to the original Macaroon\n6. Client will receive the new discharge Macaroon, and send that AND the original Macaroon back to the first-party service for verification\n\n#### Round Trip\n\n1. Generate a nonce, then make some form of remote call out to the third-party service informing it of that random nonce\n2. The third-party service should return a unique ID, use this unique ID as the caveat ID in the third-party caveat. associate the unique ID with the random nonce that was generated\n3. Send this Macaroon to the client -- which will read the location and send the caveat id to the third-party server\n4. The third-party server will use the nonce to look up what needs to be verified\n5. The third-party server will create a discharge Macaroon using the nonce you sent it as the root key -- bind it to the original Macaroon\n6. Client will receive the new discharge Macaroon, and send that AND the original Macaroon back to the first-party service for verification\n\n## Examples\n\n### Creating a Macaroon\n\n```elixir\nm = Macaroon.create_macaroon(\"http://my.cool.app\", \"public_id\", \"SUPER_SECRET_KEY_DO_NOT_SHARE\")\n```\n\n### Adding Caveats\n\n```elixir\nm = Macaroon.create_macaroon(\"http://my.cool.app\", \"public_id\", \"SUPER_SECRET_KEY_DO_NOT_SHARE\")\n  |\u003e Macaroon.add_first_party_caveat(\"upload_limit = 4MB\")\n  |\u003e Macaroon.add_first_party_caveat(\"upload_namespace = /users/1234/*\")\n  |\u003e Macaroon.add_third_party_caveat(\"https://auth.another.app\", \"PREDICATE_HOPEFULLY_ENCRYPTED\", \"RANDOM_SECRET_NONCE_KEY\")\n```\n\n### Verification\n\n```elixir\nalias Macaroon.Verification\n\nresult = Verification.satisfy_exact(\"upload_limit = 4MB\")\n  |\u003e Verification.satisfy_exact(\"upload_namespace = /users/1234/*\")\n  |\u003e Verification.satisfy_exact(\"time \u003c 2022-01-01T00:00\")\n  |\u003e Verification.verify(macaroon, \"SUPER_SECRET_KEY_DO_NOT_SHARE\")\n\n# result will be {:ok, macaroon} or {:error, reason_for_failure}\n```\n\n### Serialization and Deserialize\n\n#### JSON\n\n```elixir\n{:ok, json_string} = Macaroon.create_macaroon(\"http://my.cool.app\", \"public_id\", \"SUPER_SECRET_KEY\")\n  |\u003e Macaroon.serialize(:json)\n\nmacaroon = Macaroon.deserialize(json_string, :json)\n```\n\n#### Binary\n\n```elixir\n{:ok, url_base64_string} = Macaroon.create_macaroon(\"http://my.cool.app\", \"public_id\", \"SUPER_SECRET_KEY\")\n  |\u003e Macaroon.serialize(:binary)\n\nmacaroon = Macaroon.deserialize(url_base64_string, :binary)\n```\n\n## Misc\n\n### Building on Apple Silicon\n\nWhile the `enacl` dependency is awaiting some PRs to fix the build flags on Apple Silicon machines, you can work around this easily:\n\n*BEFORE* you run `mix deps.compile` do the following\n\n1. Install libsodium via Homebrew: `brew install libsodium`\n2. Export the environment variables so Clang can find the library: \n\n```\nexport CPATH=/opt/homebrew/include\nexport LIBRARY_PATH=/opt/homebrew/lib\n```\n\n3. Export some extra C, C++ and Linker flags to build a dual-arch library (instead of just an x86_64 one):\n\n  ```\n  export CFLAGS=\"-arch arm64\"\n  export CXXFLAGS=\"-arch arm64\"\n  export LDFLAGS=\"-arch arm64\"\n  ```\n\n4. Done! Now run `mix deps.compile`\n\n### Building on Windows\n\n(I really recommend using the Windows Linux Subsystem. It makes installing libsodium and most other things much easier. But if you must run this natively on Windows, follow these tips!)\n\n1. Download the latest release of libsodium, compile it using Visual Studio's compiler using x86 ReleaseDLL config. \n2. Take note of the full path where the `.dll`, `.lib` are generated. Also note where the `include` directory is located.\n3. Rename the generated `.lib` to `.dll.a`.\n\nThen using a Developer Command Prompt navigate to your project:\n\n1. `set lib=%lib%;\u003cPATH_TO_FOLDER_THAT_CONTAINS_libsodium.dll.a\u003e`\n2. `set include=%include%;\u003cPATH_TO_FOLDER_THAT_CONTAINS_sodium.h\u003e`\n3. `mix deps.get` and `mix deps.compile`\n\n---\n\n\u003cp align=\"center\"\u003e\n  🍪 Baked with 🐾 by Digit (@doawoo) | https://puppy.surf\n\u003c/p\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdoawoo%2Fmacaroon","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdoawoo%2Fmacaroon","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdoawoo%2Fmacaroon/lists"}