{"id":20081817,"url":"https://github.com/pragmaticivan/gandalf","last_synced_at":"2025-07-05T06:07:54.079Z","repository":{"id":62429713,"uuid":"116778107","full_name":"pragmaticivan/gandalf","owner":"pragmaticivan","description":"Elixir Oauth2 Provider implementation","archived":false,"fork":false,"pushed_at":"2018-04-17T04:16:51.000Z","size":75,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-06-01T17:36:29.605Z","etag":null,"topics":["elixir","oauth2"],"latest_commit_sha":null,"homepage":"","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pragmaticivan.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-01-09T06:54:12.000Z","updated_at":"2024-07-20T11:43:52.000Z","dependencies_parsed_at":"2022-11-01T20:04:14.460Z","dependency_job_id":null,"html_url":"https://github.com/pragmaticivan/gandalf","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/pragmaticivan/gandalf","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pragmaticivan%2Fgandalf","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pragmaticivan%2Fgandalf/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pragmaticivan%2Fgandalf/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pragmaticivan%2Fgandalf/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pragmaticivan","download_url":"https://codeload.github.com/pragmaticivan/gandalf/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pragmaticivan%2Fgandalf/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":263692913,"owners_count":23496944,"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":["elixir","oauth2"],"created_at":"2024-11-13T15:40:25.146Z","updated_at":"2025-07-05T06:07:54.052Z","avatar_url":"https://github.com/pragmaticivan.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Gandalf\n\n[![CircleCI](https://circleci.com/gh/pragmaticivan/gandalf.svg?style=shield\u0026circle-token=0da837341417ce07c7fcee52a2c98581973cd622)](https://circleci.com/gh/pragmaticivan/gandalf)\n[![Hex Version](https://img.shields.io/hexpm/v/gandalf.svg)](https://hex.pm/packages/gandalf)\n[![codecov](https://codecov.io/gh/pragmaticivan/gandalf/branch/master/graph/badge.svg?token=MB04JleUFo)](https://codecov.io/gh/pragmaticivan/gandalf)\n\nElixir Oauth2 Provider implementation forked from https://github.com/mustafaturan/authable and heavily modified\n\nOAuth2 Provider implementation modules and helpers using `plug`, `ecto` and `postgres` for any `elixir` application.\n\n## Installation\n\nThe package can be installed as:\n\n  1. Add gandalf to your list of dependencies in `mix.exs`:\n\n      Only for ecto versions \u003e 2.0\n\n```elixir\n    def deps do\n      [{:gandalf, \"~\u003e 0.3.0\"}]\n    end\n```\n\n  2. Add gandalf configurations to your `config/config.exs` file:\n\n  *Important:* You should update `Gandalf.Repo` with your own repo!\n\n```elixir\n    config :gandalf,\n      ecto_repos: [Gandalf.Repo],\n      repo: Gandalf.Repo,\n      expires_in: %{\n        access_token: 3600,\n        refresh_token: 24 * 3600,\n        authorization_code: 300,\n          session_token: 30 * 24 * 3600\n        },\n        grant_types: %{\n          authorization_code: Gandalf.GrantType.AuthorizationCode,\n          client_credentials: Gandalf.GrantType.ClientCredentials,\n          password: Gandalf.GrantType.Password,\n          refresh_token: Gandalf.GrantType.RefreshToken\n        },\n        auth_strategies: %{\n          headers: %{\n            \"authorization\" =\u003e [\n              {~r/Basic ([a-zA-Z\\-_\\+=]+)/, Gandalf.Authentication.Basic},\n              {~r/Bearer ([a-zA-Z\\-_\\+=]+)/, Gandalf.Authentication.Bearer},\n            ],\n            \"x-api-token\" =\u003e [\n              {~r/([a-zA-Z\\-_\\+=]+)/, Gandalf.Authentication.Bearer}\n            ]\n          },\n          query_params: %{\n            \"access_token\" =\u003e Gandalf.Authentication.Bearer\n          },\n          sessions: %{\n            \"session_token\" =\u003e Gandalf.Authentication.Session\n          }\n        },\n        scopes: ~w(read write session),\n        renderer: Gandalf.Renderer.RestApi\n```\n\n        If you want to disable a grant type then delete from grant types config.\n\n        If you want to add a new grant type then add your own module with `authorize(params)` function and return a `Gandalf.Model.Token` struct.\n\n  3. Add database configurations for the `Gandalf.Repo` on env config files:\n\n  *Important:* You should update `Gandalf.Repo` with your own repo!\n\n```elixir\n    config :gandalf, Gandalf.Repo,\n      adapter: Ecto.Adapters.Postgres,\n      username: \"\",\n      password: \"\",\n      database: \"\",\n      hostname: \"\",\n      pool_size: 10\n```\n\n  4. Run migrations for Gandalf.Repo (Note: all id fields are UUID type):\n\n  *Important:* You should update `Gandalf.Repo` with your own repo!\n\n```elixir\n    mix ecto.migrate -r Gandalf.Repo\n```\n\n  5. You are ready to go!\n\n## Usage\n\nPlease refer to hex docs for each module, function details and samples https://hexdocs.pm/gandalf.\n\n### Authentication\n\nGandalf supports 3 main authentication types by default using `Plug.Conn`. You can add or remove authentication types using configuration. On successful authentication, resource owner automatically set on `conn.assigns[:current_user]` immutable.\n\n1. **Sessions**. Reads session for configured `sessions` keys and passes to the matched authenticator to authenticate.\n\n2. **Query Params**. Reads query params for configured `query_params` keys and passes to the matched authenticator to authenticate.\n\n3. **Headers**. Reads headers for configured `headers` keys and passes to the matched authenticator to authenticate.\n\n#### Examples\n\nConfigure your application OAuth2 scopes on configuration. Then add `import Gandalf.Plug.Authenticate` with scopes into your controller.\n\n```elixir\ndefmodule SomeModule.AppController do\n  use SomeModule.Web, :controller\n  plug Gandalf.Plug.Authenticate, [scopes: ~w(read write)]\n\n  def index(conn, _params) do\n    # access to current user on successful authentication\n    # ...\n    # current_user = conn.assigns[:current_user]\n  end\nend\n\ndefmodule SomeModule.AppController do\n  use SomeModule.Web, :controller\n\n  plug Gandalf.Plug.Authenticate, [scopes: ~w(read write)] when action in [:create]\n\n  def index(conn, _params) do\n    # anybody can call this action\n    # ...\n  end\n\n  def create(conn, _params) do\n    # only logged in users can access this action\n    # ...\n    # current_user = conn.assigns[:current_user]\n  end\nend\n\n# if you need to allow a resource only unauthorized then\ndefmodule SomeModule.AppController do\n  use SomeModule.Web, :controller\n  plug Gandalf.Plug.UnauthorizedOnly when action in [:register]\n\n  def register(conn, _params) do\n    # only not logged in user can access this action\n    # ...\n  end\nend\n```\n\nOn failure of authentication, gandalf renders as a RestApi json format, if you need to change the format file you need to implement the behaviour of `Gandalf.Renderer` and then change the `renderer` configuration.\n\n### OAuth2 Authorization\n\nCurrently, gandalf library supports by default `authorization code`, `client credentials`, `password`, and `refresh token` OAuth2 authorizations. You can add or remove grant types using configuration.\n\n#### Examples\n\nTo authorize a client for resources, all you need to do is calling `OAuth2.authorize` method with necessary params, on successful authorization `Gandalf.Model.Token` struct will return, on failure `{:error, errors, http_status_code}`.\n\n```elixir\n# For authorization_code grant type\nGandalf.OAuth2.authorize(%{\n  \"grant_type\" =\u003e \"authorization_code\",\n  \"client_id\" =\u003e \"52024ca6-cf1d-4a9d-bfb6-9bc5023ad56e\",\n  \"client_secret\" =\u003e \"Wi7Y_Q5LU4iIwJArgqXq2Q\",\n  \"redirect_uri\" =\u003e \"http://localhost:4000/oauth2/callbacks\",\n  \"code\" =\u003e \"W_hb8JEDmeYChsNfOGCmbQ\",\n  \"scope\" =\u003e \"read\" # optional\n})\n\n# For client_credentials grant type\nGandalf.OAuth2.authorize(%{\n  \"grant_type\" =\u003e \"client_credentials\",\n  \"client_id\" =\u003e \"52024ca6-cf1d-4a9d-bfb6-9bc5023ad56e\",\n  \"client_secret\" =\u003e \"Wi7Y_Q5LU4iIwJArgqXq2Q\",\n  \"scope\" =\u003e \"read\" # optional\n})\n\n# For password grant type\nGandalf.OAuth2.authorize(%{\n  \"grant_type\" =\u003e \"password\",\n  \"email\" =\u003e \"foo@example.com\",\n  \"password\" =\u003e \"12345678\",\n  \"client_id\" =\u003e \"52024ca6-cf1d-4a9d-bfb6-9bc5023ad56e\",\n  \"scope\" =\u003e \"read\" # optional\n})\n\n# For refresh_token grant type\nGandalf.OAuth2.authorize(%{\n  \"grant_type\" =\u003e \"refresh_token\",\n  \"client_id\" =\u003e \"52024ca6-cf1d-4a9d-bfb6-9bc5023ad56e\",\n  \"client_secret\" =\u003e \"Wi7Y_Q5LU4iIwJArgqXq2Q\",\n  \"refresh_token\" =\u003e \"XJaVz3lCFC9IfifBriA-dw\",\n  \"scope\" =\u003e \"read\" # optional\n})\n\n# You can adjust token expiration durations from configuration.\n```\n\n### How a 'OAuth2 Resource Owner' can authorize clients?\n\nAuthorizing client may mean installing client or giving permission to a client to make OAuth2 Authorization requests and allowing resources with selected scopes. To authorize a client for a resource owner, you need to call `OAuth2.authorize_app` function.\n\n#### Examples\n\n```elixir\nGandalf.OAuth2.authorize_app(user, %{\n  \"client_id\" =\u003e \"52024ca6-cf1d-4a9d-bfb6-9bc5023ad56e\",\n  \"redirect_uri\" =\u003e \"http://localhost:4000/oauth2/callbacks\",\n  \"scope\" =\u003e \"read,write\"\n})\n```\n\n### Changing models\n\nTo change models, you have two options:\n\n1. You may change the module name from configuration,\n2. You may copy Gandalf.Model.XXX and update it on your app.\n\n## Test\n\nTo run tests, jump into gandalf directory and run the command:\n\n```shell\nmix test\n```\n\n## Contributing\n\n### Issues, Bugs, Documentation, Enhancements\n\n1. Fork the project\n\n2. Make your improvements and write your tests.\n\n3. Make a pull request.\n\n### To add new strategy:\n\nGandalf is an extensible module, you can create your strategy and share as hex package(Which can be listed on Wiki pages).\n\n## Todo\n\n- HMAC Auth will be added as a new external strategy\n\n## References\n\n* https://tools.ietf.org/html/rfc6749\n\n* https://tools.ietf.org/html/rfc6750\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpragmaticivan%2Fgandalf","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpragmaticivan%2Fgandalf","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpragmaticivan%2Fgandalf/lists"}