{"id":15631959,"url":"https://github.com/ianwalter/bouncer","last_synced_at":"2025-04-27T06:51:08.255Z","repository":{"id":57480490,"uuid":"44213117","full_name":"ianwalter/bouncer","owner":"ianwalter","description":"Token-based authorization and session management for Phoenix (Elixir)","archived":false,"fork":false,"pushed_at":"2019-10-24T20:38:15.000Z","size":78,"stargazers_count":28,"open_issues_count":8,"forks_count":3,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-30T08:32:11.105Z","etag":null,"topics":["authorization","phoenix"],"latest_commit_sha":null,"homepage":"","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ianwalter.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.MD","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null},"funding":{"github":"ianwalter"}},"created_at":"2015-10-14T00:09:42.000Z","updated_at":"2023-09-01T08:50:24.000Z","dependencies_parsed_at":"2022-09-26T17:41:15.275Z","dependency_job_id":null,"html_url":"https://github.com/ianwalter/bouncer","commit_stats":null,"previous_names":[],"tags_count":15,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ianwalter%2Fbouncer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ianwalter%2Fbouncer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ianwalter%2Fbouncer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ianwalter%2Fbouncer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ianwalter","download_url":"https://codeload.github.com/ianwalter/bouncer/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251099734,"owners_count":21536153,"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":["authorization","phoenix"],"created_at":"2024-10-03T10:42:10.661Z","updated_at":"2025-04-27T06:51:08.232Z","avatar_url":"https://github.com/ianwalter.png","language":"Elixir","funding_links":["https://github.com/sponsors/ianwalter"],"categories":[],"sub_categories":[],"readme":"# Bouncer (beta)\n\u003e Token-based authorization and session management for Phoenix (Elixir)\n\n[![Hex Version](https://img.shields.io/hexpm/v/bouncer.svg)](https://hex.pm/packages/bouncer)\n\n## Why\n\nI needed a way to authorize API requests to my Phoenix application.\n[Addict](https://github.com/trenpixster/addict) didn't fit the bill since it\nuses Phoenix's built-in session system. Phoenix uses cookies to authorize\nrequests but when dealing with an API, it's easier to deal with an Authorization\nheader. Phoenix's session system also uses memory or ETS to store session data\nand this wouldn't work for my application which would be scaled horizontally and\nso would be running on multiple machines. Redis is great at solving this problem\nbecause it's crazy-fast and can be accessed by multiple machines. The ecosystem\naround Redis is strong so working with the session data is pretty easy.\n\n[Guardian](https://github.com/hassox/guardian) also wouldn’t work because it\nuses JSON Web Tokens (JWT) as the basis for it’s authorization scheme. JWTs can\nwork but [I don’t believe it’s a better system than the traditional session-based system](https://medium.com/@IanWalter/ole-thank-you-for-your-response-it-s-exactly-the-kind-of-feedback-i-was-looking-for-117df9438ccc#.icimd0nwv). \nJWTs don't provide a way of immediately invalidating\nuser sessions instead relying on short token lifetimes. The ability to\nimmediately invalidate a session is a feature that I find useful in certain situations (i.e. when a user resets their password).\n\n## Features\n\n* Creating a session returns a token that can be used in the authorization\n  header of each API request.\n* Backed by Redis so it's able to be used in a multi-server or multi-container\n  environment without configuring sticky sessions. Also, Redis is pretty fast.\n* Simple API to create, update, and destroy session data.\n* Simple API to generate, verify, and regenerate email verification or password\n  reset tokens.\n\n## Installation\n\nBouncer is [available in Hex](https://hex.pm/packages/bouncer), the package can\nbe installed as:\n\n  1. Add bouncer to your list of dependencies in `mix.exs`:\n\n    ```elixir\n    def deps do\n      [{:bouncer, \"~\u003e 0.3.0\"}]\n    end\n    ```\n\n  2. Ensure bouncer is started before your application:\n\n    ```elixir\n    def application do\n      [applications: [:bouncer]]\n    end\n    ```\n\n## Requirements \u0026 Configuration\n\nBouncer requires the [Phoenix framework](http://www.phoenixframework.org/)\nbecause it uses it's Token module to generate tokens that are used both as an\nAuthorization header and a session key. Despite this requirement, I imagine it\ncould be used with any [Plug](https://github.com/elixir-lang/plug)-based\nframework. Bouncer provides a plug that can be used to authorize a request for\ncertain controllers and/or controller actions:\n\n```elixir\n# This would be added near the top of a UserController for example\nplug Bouncer.Plugs.Authorize when action in [:show, :update, :delete]\n```\n\nBouncer only has one session store adapter so far: [Redis](http://redis.io/).\nBouncer uses the fantastic [Redix](https://github.com/whatyouhide/redix) library\nto interface with Redis and we've added a module called Bouncer.RedixPool that\nwill pool connections to Redis. Here's what you would put in your environment's\nconfiguration file:\n\n```elixir\n# config/dev.exs\nconfig :bouncer,\n  adapter: Bouncer.Adapters.Redis,\n  redis: \"redis://somehost:6379/1\"\n```\n\nThe second configuration option, `redis`, is not necessary if your Redis\ninstance is on localhost and using the default port. You might want to specify\na different database (i.e. `redis://localhost:6379/2`) in your test\nconfiguration file.\n\n## Documentation\n\nThe source is really small so reading through it should be straight-forward but\nthe full package documentation is available at https://hexdocs.pm/bouncer.\n\n## Example of a SessionController\n\nHere's and example of how you can use the\n[Bouncer.Session](http://hexdocs.pm/bouncer/Bouncer.Session.html) API in\nyour application:\n\n```elixir\n# web/controllers/session_controller.ex\ndefmodule MyApp.SessionController do\n  use MyApp.Web, :controller\n\n  alias MyApp.User\n  alias MyApp.UserView\n  alias Bouncer.Session\n  alias Comeonin.Bcrypt\n\n  plug Bouncer.Plugs.Authorize when action in [:delete]\n\n  def create(conn, %{\"user\" =\u003e user_params}) do\n    case Repo.get_by(User, %{username: user_params[\"username\"]}) do\n      nil -\u003e\n        Bcrypt.dummy_checkpw()\n        send_resp(conn, :bad_request, \"\")\n\n      user -\u003e\n        if Bcrypt.checkpw(user_params[\"password\"], user.encrypted_password) do\n          user_map = User.to_map(user, true)\n          {:ok, token} = Session.generate(conn, user_map)\n\n          conn\n          |\u003e put_status(:created)\n          |\u003e render(\"create.json\", %{user: user_map, token: token})\n        else\n          send_resp(conn, :bad_request, \"\")\n        end\n    end\n  end\n\n  def delete conn, _params do\n    if user = conn.private.current_user do\n      case Session.destroy conn.private.auth_token, user[\"id\"] do\n        {:ok, _} -\u003e send_resp conn, :no_content, \"\"\n        _ -\u003e send_resp conn, :bad_request, \"\"\n      end\n    else\n      send_resp conn, :unauthorized, \"\"\n    end\n  end\nend\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fianwalter%2Fbouncer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fianwalter%2Fbouncer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fianwalter%2Fbouncer/lists"}