{"id":13507463,"url":"https://github.com/coryodaniel/speakeasy","last_synced_at":"2025-04-07T13:06:46.197Z","repository":{"id":46750252,"uuid":"145180018","full_name":"coryodaniel/speakeasy","owner":"coryodaniel","description":"Middleware based authorization for Absinthe GraphQL powered by Bodyguard","archived":false,"fork":false,"pushed_at":"2023-09-14T03:58:00.000Z","size":63,"stargazers_count":84,"open_issues_count":0,"forks_count":8,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-31T11:05:21.478Z","etag":null,"topics":[],"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/coryodaniel.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,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-08-18T01:21:52.000Z","updated_at":"2025-02-16T06:31:15.000Z","dependencies_parsed_at":"2024-05-01T15:27:40.041Z","dependency_job_id":"5eb60a78-0f11-4834-8371-24e609aa4bbd","html_url":"https://github.com/coryodaniel/speakeasy","commit_stats":{"total_commits":34,"total_committers":5,"mean_commits":6.8,"dds":0.1470588235294118,"last_synced_commit":"ed52d23d0e4080f1fa805335770e0bdcbe8b73db"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coryodaniel%2Fspeakeasy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coryodaniel%2Fspeakeasy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coryodaniel%2Fspeakeasy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coryodaniel%2Fspeakeasy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/coryodaniel","download_url":"https://codeload.github.com/coryodaniel/speakeasy/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247657281,"owners_count":20974345,"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":[],"created_at":"2024-08-01T02:00:34.271Z","updated_at":"2025-04-07T13:06:46.169Z","avatar_url":"https://github.com/coryodaniel.png","language":"Elixir","funding_links":[],"categories":["Authorization"],"sub_categories":[],"readme":"# Speakeasy\n\n[![Build Status](https://travis-ci.org/coryodaniel/speakeasy.svg?branch=master)](https://travis-ci.org/coryodaniel/speakeasy)\n[![Coverage Status](https://coveralls.io/repos/github/coryodaniel/speakeasy/badge.svg?branch=master)](https://coveralls.io/github/coryodaniel/speakeasy?branch=master)\n[![Hex.pm](http://img.shields.io/hexpm/v/speakeasy.svg?style=flat)](https://hex.pm/packages/speakeasy) [![Hex.pm](https://img.shields.io/hexpm/dt/speakeasy.svg?style=flat)](https://hex.pm/packages/speakeasy)\n[![Documentation](https://img.shields.io/badge/documentation-on%20hexdocs-green.svg)](https://hexdocs.pm/speakeasy/)\n![Hex.pm](https://img.shields.io/hexpm/l/speakeasy.svg?style=flat)\n\n[Speakeasy](https://hexdocs.pm/speakeasy/readme.html) is authentication agnostic middleware based authorization for [Absinthe](https://hexdocs.pm/absinthe) GraphQL powered by [Bodyguard](https://hexdocs.pm/bodyguard).\n\n[Docs](https://hexdocs.pm/speakeasy/readme.html)\n\n## Installation\n\n[Speakeasy](https://hex.pm/packages/speakeasy) can be installed\nby adding `speakeasy` to your list of dependencies in `mix.exs`:\n\n```elixir\ndef deps do\n  [\n    {:speakeasy, \"~\u003e 0.3\"}\n  ]\nend\n```\n\n## Configuration\n\nConfiguration can be done in each Absinthe middleware call, but you can set global defaults as well.\n\n```elixir\nconfig :speakeasy,\n  user_key: :current_user,                # the key the current user will be under in the GraphQL context\n  authn_error_message: :unauthenticated  # default authentication failure message\n```\n\n_Note:_ no `authz_error_message` is provided because it is set from Bodyguard.\n\n## Usage\n\n**tl;dr:** A full example authentication, authorizing, loading, and resolving an Absinthe schema:\n\n_This example assumes:_\n\n- You are authorizing a standard phoenix context\n- You already have a [bodyguard policy](https://github.com/schrockwell/bodyguard#policies)\n- Your `:current_user` is already in the Absinthe context _or_ you are using [`Speakeasy.Plug`](#speakeasy-plug)\n\n```elixir\ndefmodule MyApp.Schema.PostTypes do\n  use Absinthe.Schema.Notation\n  alias Spectra.Posts\n\n  object :post do\n    field(:id, non_null(:id))\n    field(:name, non_null(:string))\n  end\n\n  object :post_mutations do\n    @desc \"Create post\"\n    field :create_post, type: :post do\n      arg(:name, non_null(:string))\n      middleware(Speakeasy.Authn)\n      middleware(Speakeasy.Authz, {Posts, :create_post})\n      middleware(Speakeasy.Resolve, \u0026Posts.create_post/2)\n      middleware(MyApp.Middleware.ChangesetErrors) # :D\n    end\n\n    @desc \"Update post\"\n    field :update_post, type: :post do\n      arg(:name, non_null(:string))\n      middleware(Speakeasy.Authn)\n      middleware(Speakeasy.Authz, {Posts, :update_post})\n      middleware(Speakeasy.Resolve, \u0026Posts.update_post/3)\n      middleware(MyApp.Middleware.ChangesetErrors) # :D\n    end\n  end\n\n  object :post_queries do\n    @desc \"Get posts\"\n    field :posts, list_of(:post) do\n      middleware(Speakeasy.Authn)\n      middleware(Speakeasy.Resolve, fn(attrs, user) -\u003e MyApp.Posts.search(attrs, user) end)\n    end\n\n    @desc \"Get post\"\n    field :post, type: :post do\n      arg(:id, non_null(:string))\n      middleware(Speakeasy.Authn)\n      middleware(Speakeasy.LoadResourceByID, \u0026Posts.get_post/1)\n      middleware(Speakeasy.Authz, {Posts, :get_post})\n      middleware(Speakeasy.Resolve)\n    end\n  end\nend\n```\n\nAnd of course you can use Absinthe's resolve function as well:\n\n```elixir\n@desc \"Get post\"\nfield :post, type: :post do\n  arg(:id, non_null(:string))\n  middleware(Speakeasy.Authn)\n  middleware(Speakeasy.LoadResourceByID, \u0026Posts.get_post/1)\n  middleware(Speakeasy.Authz, {Posts, :get_post})\n  resolve(fn(_parent, _args, ctx) -\u003e\n    {:ok, ctx[:speakeasy].resource}\n  end)\nend\n```\n\n### Middleware\n\nSpeakeasy is a collection of Absinthe middleware:\n\n- [Speakeasy.Authn](https://hexdocs.pm/speakeasy/Speakeasy.Authn.html#content) - Resolution middleware for Absinthe.\n\n- [Speakeasy.LoadResource](https://hexdocs.pm/speakeasy/Speakeasy.LoadResource.html#content) - Loads a resource into the speakeasy context.\n\n- [Speakeasy.LoadResourceById](https://hexdocs.pm/speakeasy/Speakeasy.LoadResourceByID.html#content) - A convenience middleware to `LoadResource` using the `:id` in the Absinthe arguments.\n\n- [Speakeasy.LoadResourceBy](https://hexdocs.pm/speakeasy/Speakeasy.LoadResourceBy.html#content) - A convenience middleware to `LoadResource` using a value from the attrs with the given key in the Absinthe arguments.\n\n- [Speakeasy.AuthZ](https://hexdocs.pm/speakeasy/Speakeasy.Authz.html#content) - Authorization middleware for Absinthe.\n\n- [Speakeasy.Resolve](https://hexdocs.pm/speakeasy/Speakeasy.Resolve.html#content) - Resolution middleware for Absinthe.\n\n### Speakeasy.Plug\n\nSpeakeasy includes a Plug for loading the current user into the Absinthe context. It isn't required if you already have a method for loading the user into your Absinthe context.\n\n```elixir\ndefmodule MyAppWeb.Router do\n  use MyAppWeb, :router\n\n  pipeline :graphql do\n    plug(Speakeasy.Plug, load_user: \u0026MyApp.Users.whoami/1, user_key: :current_user)\n  end\nend\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcoryodaniel%2Fspeakeasy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcoryodaniel%2Fspeakeasy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcoryodaniel%2Fspeakeasy/lists"}