{"id":13507098,"url":"https://github.com/ueberauth/guardian","last_synced_at":"2025-05-13T15:10:39.226Z","repository":{"id":39787718,"uuid":"37552628","full_name":"ueberauth/guardian","owner":"ueberauth","description":"Elixir Authentication","archived":false,"fork":false,"pushed_at":"2025-03-25T18:35:46.000Z","size":1241,"stargazers_count":3481,"open_issues_count":1,"forks_count":384,"subscribers_count":49,"default_branch":"master","last_synced_at":"2025-04-23T18:55:06.216Z","etag":null,"topics":["authentication","elixir","guardian","jwt","jwt-tokens","phoenix","plug"],"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/ueberauth.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":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-06-16T19:53:40.000Z","updated_at":"2025-04-23T11:06:42.000Z","dependencies_parsed_at":"2023-01-21T06:01:17.416Z","dependency_job_id":"9c38a9b2-aea0-4101-bcbd-ac728c54e0c9","html_url":"https://github.com/ueberauth/guardian","commit_stats":{"total_commits":422,"total_committers":173,"mean_commits":2.439306358381503,"dds":0.7559241706161137,"last_synced_commit":"eafa1e430c9c75b0768a9fd72b18021187d2e15b"},"previous_names":[],"tags_count":32,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ueberauth%2Fguardian","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ueberauth%2Fguardian/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ueberauth%2Fguardian/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ueberauth%2Fguardian/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ueberauth","download_url":"https://codeload.github.com/ueberauth/guardian/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253969248,"owners_count":21992263,"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":["authentication","elixir","guardian","jwt","jwt-tokens","phoenix","plug"],"created_at":"2024-08-01T02:00:23.792Z","updated_at":"2025-05-13T15:10:34.205Z","avatar_url":"https://github.com/ueberauth.png","language":"Elixir","funding_links":[],"categories":["Elixir","Authentication","Tools","Libraries","Uncategorized"],"sub_categories":["Mesh networks","Elixir","Uncategorized"],"readme":"# Guardian\n\n\u003e An authentication library for use with Elixir applications.\n\n![Build Status](https://github.com/ueberauth/guardian/actions/workflows/ci.yml/badge.svg)\n[![Codecov](https://codecov.io/gh/ueberauth/guardian/branch/master/graph/badge.svg)](https://codecov.io/gh/ueberauth/guardian)\n[![Inline docs](https://inch-ci.org/github/ueberauth/guardian.svg)](https://inch-ci.org/github/ueberauth/guardian)\n[![Module Version](https://img.shields.io/hexpm/v/guardian.svg)](https://hex.pm/packages/guardian)\n[![Hex Docs](https://img.shields.io/badge/hex-docs-lightgreen.svg)](https://hexdocs.pm/guardian/)\n[![Total Download](https://img.shields.io/hexpm/dt/guardian.svg)](https://hex.pm/packages/guardian)\n[![License](https://img.shields.io/hexpm/l/guardian.svg)](https://github.com/ueberauth/guardian/blob/master/LICENSE)\n[![Last Updated](https://img.shields.io/github/last-commit/ueberauth/guardian.svg)](https://github.com/ueberauth/guardian/commits/master)\n\nGuardian is a token based authentication library for use with Elixir applications.\n\nGuardian remains a functional system. It integrates with Plug but can be used outside of it. If you're implementing a TCP/UDP protocol directly or want to utilize your authentication via channels in Phoenix, Guardian can work for you.\n\nThe core currency of authentication in Guardian is the _token_.\nBy default [JSON Web Tokens](https://jwt.io) are supported out of the box but you can use any token that:\n\n* Has the concept of a key-value payload\n* Is tamper proof\n* Can serialize to a String\n* Has a supporting module that implements the `Guardian.Token` behaviour\n\nYou can use Guardian tokens to authenticate:\n\n* Web endpoints (Plug/Phoenix/X)\n* Channels/Sockets (Phoenix - optional)\n* Any other system you can imagine. If you can attach an authentication token you can authenticate it.\n\nTokens should be able to contain any assertions (claims) that a developer wants to make and may contain both standard and application specific information encoded within them.\n\nGuardian also allows you to configure multiple token types/configurations in a single application.\n\n## Documentation\n\nAPI documentation is available at [https://hexdocs.pm/guardian](https://hexdocs.pm/guardian)\n\n## Installation\n\n\nAdd Guardian to your application to your list of dependencies in `mix.exs`:\n\n```elixir\ndefp deps do\n  [\n    {:guardian, \"~\u003e 2.3\"}\n  ]\nend\n```\n\nIn order to leverage Guardian we'll need first create an \"implementation module\" which includes Guardian's functionality and the code for encoding and decoding our token's values.\nTo do this, create a module that uses `Guardian` and implements the `subject_for_token/2` and `resource_from_claims/1` function.\n\n```elixir\ndefmodule MyApp.Guardian do\n  use Guardian, otp_app: :my_app\n\n  def subject_for_token(%{id: id}, _claims) do\n    # You can use any value for the subject of your token but\n    # it should be useful in retrieving the resource later, see\n    # how it is being used on `resource_from_claims/1` function.\n    # A unique `id` is a good subject, a non-unique email address\n    # is a poor subject.\n    sub = to_string(id)\n    {:ok, sub}\n  end\n  def subject_for_token(_, _) do\n    {:error, :reason_for_error}\n  end\n\n  def resource_from_claims(%{\"sub\" =\u003e id}) do\n    # Here we'll look up our resource from the claims, the subject can be\n    # found in the `\"sub\"` key. In above `subject_for_token/2` we returned\n    # the resource id so here we'll rely on that to look it up.\n    resource = MyApp.get_resource_by_id(id)\n    {:ok,  resource}\n  end\n  def resource_from_claims(_claims) do\n    {:error, :reason_for_error}\n  end\nend\n```\n\nNext we need to add our configuration to `config/config.exs`:\n\n```elixir\nconfig :my_app, MyApp.Guardian,\n       issuer: \"my_app\",\n       secret_key: \"Secret key. You can use `mix guardian.gen.secret` to get one\"\n```\n\nCongrats! We have a working Guardian implementation.\n\n## Basics\n\n```elixir\n# encode a token for a resource\n{:ok, token, claims} = MyApp.Guardian.encode_and_sign(resource)\n\n# decode and verify a token\n{:ok, claims} = MyApp.Guardian.decode_and_verify(token)\n\n# revoke a token (use GuardianDb or something similar if you need revoke to actually track a token)\n{:ok, claims} = MyApp.Guardian.revoke(token)\n\n# Refresh a token before it expires\n{:ok, _old_stuff, {new_token, new_claims}} = MyApp.Guardian.refresh(token)\n\n# Exchange a token of type \"refresh\" for a new token of type \"access\"\n{:ok, _old_stuff, {new_token, new_claims}} = MyApp.Guardian.exchange(token, \"refresh\", \"access\")\n\n# Lookup a resource directly from a token\n{:ok, resource, claims} = MyApp.Guardian.resource_from_token(token)\n```\n\nWith Plug:\n\n```elixir\n# The token/resource/claims will be stored on the connection.\n# The token will also be stored in the session (if fetched)\nconn = MyApp.Guardian.Plug.sign_in(conn, resource)\n\n# Optionally with claims and options\nconn = MyApp.Guardian.Plug.sign_in(conn, resource, %{some: \"claim\"}, ttl: {1, :minute})\n\n# remove from session (if fetched) and revoke the token\n# can also clear the remember me token, if the option :clear_remember_me is set\nconn = MyApp.Guardian.Plug.sign_out(conn)\n\n# Set a \"refresh\" token directly on a cookie.\n# Can be used in conjunction with `Guardian.Plug.VerifyCookie` and `Guardian.Plug.SlidingCookie`\nconn = MyApp.Guardian.Plug.remember_me(conn, resource)\n\n# Fetch the information from the current connection\ntoken = MyApp.Guardian.Plug.current_token(conn)\nclaims = MyApp.Guardian.Plug.current_claims(conn)\nresource = MyApp.Guardian.Plug.current_resource(conn)\n```\n\nCreating with custom claims and options:\n\n```elixir\n# Add custom claims to a token\n{:ok, token, claims} = MyApp.Guardian.encode_and_sign(resource, %{some: \"claim\"})\n\n# Create a specific token type (i.e. \"access\"/\"refresh\" etc)\n{:ok, token, claims} = MyApp.Guardian.encode_and_sign(resource, %{}, token_type: \"refresh\")\n\n# Customize the time to live (ttl) of the token\n{:ok, token, claims} = MyApp.Guardian.encode_and_sign(resource, %{}, ttl: {1, :minute})\n\n# Customize the secret\n{:ok, token, claims} = MyApp.Guardian.encode_and_sign(resource, %{}, secret: \"custom\")\n{:ok, token, claims} = MyApp.Guardian.encode_and_sign(resource, %{}, secret: {SomeMod, :some_func, [\"some\", \"args\"]})\n\n# Require an \"auth_time\" claim to be added.\n{:ok, token, claims} = MyApp.Guardian.encode_and_sign(resource, %{}, auth_time: true)\n```\n\nDecoding tokens:\n\n```elixir\n# Check some literal claims. (i.e. this is an access token)\n{:ok, claims} = MyApp.Guardian.decode_and_verify(token, %{\"typ\" =\u003e \"access\"})\n\n# Use a custom secret\n{:ok, claims} = MyApp.Guardian.decode_and_verify(token, %{}, secret: \"custom\")\n{:ok, claims} = MyApp.Guardian.decode_and_verify(token, %{}, secret: {SomeMod, :some_func, [\"some\", \"args\"]})\n\n# Specify a maximum age (since end user authentication time). If the token has an\n# `auth_time` claim and it is older than the `max_age` allows, the token will be invalid.\n{:ok, claims} = MyApp.Guardian.decode_and_verify(token, %{}, max_age: {2, :hours})\n```\n\nIf you need dynamic verification for JWT tokens, please see the documentation for `Guardian.Token.Jwt` and `Guardian.Token.Jwt.SecretFetcher`.\n\n## Configuration\n\nThe following configuration is available to all implementation modules.\n\n* `token_module` - The module that implements the functions for dealing with tokens. Default `Guardian.Token.Jwt`.\n\nGuardian can handle tokens of any type that implements the `Guardian.Token` behaviour.\nEach token module will have its own configuration requirements. Please see below for the JWT configuration.\n\nAll configuration values may be provided in two ways.\n\n1. In your config files\n2. As a Keyword list to your call to `use Guardian` in your implementation module.\n\nAny options given to `use Guardian` have precedence over config values found in the config files.\n\nSome configuration may be required by your `token_module`.\n\n### Configuration values\n\nGuardian supports resolving configuration options at runtime, to that we use the following syntax:\n\n* `{MyModule, :func, [:some, :args]}` Calls the function on the module with args\n\nThese are evaluated at runtime and any value that you fetch via\n\n`MyApp.Guardian.config(key, default)` will be resolved using this scheme.\n\nSee `Guardian.Config.resolve_value/1` for more information.\n\n### JWT (Configuration)\n\nThe default token type of `Guardian` is JWT. It accepts many options but you really only _need_ to specify the `issuer` and `secret_key`.\n\n#### Required configuration (JWT)\n\n* `issuer` - The issuer of the token. Your application name/id\n* `secret_key` - The secret key to use for the implementation module.\n  This may be any resolvable value for `Guardian.Config`.\n\n#### Optional configuration (JWT)\n\n* `token_verify_module` - default `Guardian.Token.Jwt.Verify`. The module that verifies the claims\n* `allowed_algos` - The allowed algos to use for encoding and decoding.\n  See `JOSE` for available. Default `[\"HS512\"]`\n* `ttl` - The default time to live for all tokens. See the type in Guardian.ttl\n* `token_ttl` a map of `token_type` to `ttl`. Set specific ttls for specific types of tokens\n* `allowed_drift` The drift that is allowed when decoding/verifying a token in milliseconds\n* `verify_issuer` Default false\n* `secret_fetcher` A module used to fetch the secret. Default: `Guardian.Token.Jwt.SecretFetcher`\n* `auth_time` Include an `auth_time` claim to denote the end user authentication time. Default false.\n* `max_age` Specify the maximum time (since the end user authentication) the token will be valid.\n  Format is the same as `ttl`. Implies `auth_time` unless `auth_time` is set explicitly to `false`.\n\nSee the [OpenID Connect Core specification](https://openid.net/specs/openid-connect-core-1_0.html)\nfor more details about `auth_time` and `max_age` behaviour.\n\n## Secrets (JWT)\n\nSecrets can be simple strings or more complicated `JOSE` secret schemes.\n\nThe simplest way to use the JWT module is to provide a simple String. (`mix guardian.gen.secret` works great)\n\nAlternatively you can use a module and function by adding `secret_key: {MyModule, :function_name, [:some, :args]}`.\n\nMore advanced secret information can be found below.\n\n## Using options in calls\n\nAlmost all of the functions provided by `Guardian` utilize options as the last argument.\nThese options are passed from the initiating call through to the `token_module` and also your `callbacks`. See the documentation for your `token_module` (`Guardian.Token.Jwt` by default) for more information.\n\n## Hooks\n\nEach implementation module (modules that `use Guardian`) implement callbacks for the `Guardian` behaviour. By default, these are just pass-through but you can implement your own version to tweak the behaviour of your tokens.\n\nThe callbacks are:\n\n* `after_encode_and_sign`\n* `after_sign_in`\n* `before_sign_out`\n* `build_claims` - Use this to tweak the claims that you include in your token\n* `default_token_type` - default is `\"access\"`\n* `on_exchange`\n* `on_revoke`\n* `on_refresh`\n* `on_verify`\n* `verify_claims` - You can add custom validations for your tokens in this callback\n\n## Plugs\n\nGuardian provides various plugs to help work with web requests in Elixir.\nGuardian's plugs are optional and will not be compiled if you're not using Plug in your application.\n\nAll plugs need to be in a `pipeline`.\nA pipeline is just a way to get the implementation module and error handler\ninto the connection for use downstream. More information can be found in the `Pipelines` section.\n\n### Plugs and keys (advanced usage)\n\nAll Plugs and related functions provided by `Guardian` have the concept of a `key`.\nA `key` specifies a label that is used to keep tokens separate so that you can have multiple token/resource/claims active in a single request.\n\nIn your plug pipeline you may use something like:\n\n```elixir\nplug Guardian.Plug.VerifyHeader, key: :impersonate\nplug Guardian.Plug.EnsureAuthenticated, key: :impersonate\n```\n\nIn your action handler:\n\n```elixir\nresource = MyApp.Guardian.Plug.current_resource(conn, key: :impersonate)\nclaims = MyApp.Guardian.Plug.current_claims(conn, key: :impersonate)\n```\n\n### Plugs out of the box\n\n#### `Guardian.Plug.VerifyHeader`\n\nLook for a token in the header and verify it\n\n#### `Guardian.Plug.VerifySession`\n\nLook for a token in the session and verify it\n\n#### `Guardian.Plug.VerifyCookie`\n**NOTE**: this plug is deprecated. Please use `:refresh_from_cookie` option in `Guardian.Plug.VerifyHeader` or `Guardian.Plug.VerifySession`\n\nLook for a token in cookies and exchange it for an access token\n\n#### `Guardian.Plug.SlidingCookie`\n\nReplace the token in cookies with a new one when a configured minimum TTL\nis remaining.\n\n#### `Guardian.Plug.EnsureAuthenticated`\n\nMake sure that a token was found and is valid\n\n#### `Guardian.Plug.EnsureNotAuthenticated`\n\nMake sure no one is logged in\n\n#### `Guardian.Plug.LoadResource`\n\nIf a token was found, load the resource for it\n\nSee the documentation for each Plug for more information.\n\n### Pipelines\n\nA pipeline is a way to collect together the various plugs for a particular authentication scheme.\n\nApart from keeping an authentication flow together, pipelines provide downstream information for error handling and which implementation module to use. You can provide this separately but we recommend creating a pipeline plug.\n\n#### Create a custom pipeline\n\n```elixir\ndefmodule MyApp.AuthAccessPipeline do\n  use Guardian.Plug.Pipeline, otp_app: :my_app\n\n  plug Guardian.Plug.VerifySession, claims: %{\"typ\" =\u003e \"access\"}\n  plug Guardian.Plug.VerifyHeader, claims: %{\"typ\" =\u003e \"access\"}\n  plug Guardian.Plug.EnsureAuthenticated\n  plug Guardian.Plug.LoadResource, allow_blank: true\nend\n```\n\nBy default, the LoadResource plug will return an error if no resource can be found.\nYou can override this behaviour using the `allow_blank: true` option.\n\nAdd your implementation module and error handler to your configuration:\n\n```elixir\nconfig :my_app, MyApp.AuthAccessPipeline,\n  module: MyApp.Guardian,\n  error_handler: MyApp.AuthErrorHandler\n```\n\nBy using a pipeline, apart from keeping your auth logic together, you're instructing downstream plugs to use a particular implementation module and error handler.\n\nIf you wanted to do that manually:\n\n```elixir\nplug Guardian.Plug.Pipeline, module: MyApp.Guardian,\n                             error_handler: MyApp.AuthErrorHandler\n\nplug Guardian.Plug.VerifySession\n```\n\n### Plug Error Handlers\n\nThe error handler is a module that implements an `auth_error` function:\n\n```elixir\ndefmodule MyApp.AuthErrorHandler do\n  import Plug.Conn\n\n  @behaviour Guardian.Plug.ErrorHandler\n\n  @impl Guardian.Plug.ErrorHandler\n  def auth_error(conn, {type, _reason}, _opts) do\n    body = Jason.encode!(%{message: to_string(type)})\n\n    conn\n    |\u003e put_resp_content_type(\"application/json\")\n    |\u003e send_resp(401, body)\n  end\nend\n```\n\n### Phoenix\n\nGuardian and Phoenix are perfect together, but to get the most out of the integration be sure to include the [`guardian_phoenix`](https://github.com/ueberauth/guardian_phoenix) library.\n\nSee the Guardian Phoenix documentation for more information.\n\n## Permissions\n\nPermissions can be encoded into your token as an optional add-in.\n\nEncoding permissions into a token is useful in some areas of authorization.\nThe permissions provided by `Guardian.Permissions` have one level of nesting.\n\nFor example:\n\n* `users -\u003e profile_read`\n* `users -\u003e profile_write`\n* `users -\u003e followers_read`\n* `users -\u003e followers_write`\n* `admin -\u003e all_users_read`\n* `admin -\u003e all_users_write`\n\nOnce a permission is granted it is valid for as long as the token is valid.\nSince the permission is valid for the life of a token it is not suitable to encode highly dynamic information into a token. These permissions are similar in intent to OAuth scopes. Very useful as a broad grant to an area of code for 3rd party services / other microservices. If you have a requirement to look up permissions from your database for a particular user on each request, these are not the permissions you're looking for.\n\nPlease see `Guardian.Permissions` for more information.\n\n## Tracking Tokens\n\nWhen using tokens, depending on the type of token you use, nothing may happen by default when you `revoke` a token.\n\nFor example, JWT tokens by default are not tracked by the application.\nThe fact that they are signed with the correct secret and are not expired is usually how validation of if a token is active or not. Depending on your use-case this may not be enough for your application needs.\nIf you need to track and revoke individual tokens, you may need to use something like\n[GuardianDb](https://github.com/ueberauth/guardian_db).\n\nThis will record each token issued in your database, confirm it is still valid on each access and then finally when you `revoke` (called on sign_out or manually) invalidate the token.\n\nFor more in-depth documentation please see the [GuardianDb README](https://github.com/ueberauth/guardian_db/blob/master/README.md).\n\n## Best testing practices\n\n### How to add the token to a request (the Phoenix way)\n\nAssuming you are using the default authentication scheme `Bearer` for\nthe `Authorization` header:\n\n```elixir\ndefmodule HelloWeb.AuthControllerTest do\n  use HelloWeb.ConnCase\n  import HelloWeb.Guardian\n\n test \"GET /auth/me\", %{conn: conn} do\n    user = insert(:user) # See https://github.com/thoughtbot/ex_machina\n\n    {:ok, token, _} = encode_and_sign(user, %{}, token_type: :access)\n\n    conn = conn\n    |\u003e put_req_header(\"authorization\", \"Bearer \" \u003c\u003e token)\n    |\u003e get(auth_path(conn, :me))\n\n    # Assert things here\n  end\n\nend\n```\n\n## Related projects\n\n* [GuardianDb](https://github.com/ueberauth/guardian_db) - Token tracking in the database\n* [GuardianPhoenix](https://github.com/ueberauth/guardian_phoenix) - Phoenix integration\n* [sans_password](https://hex.pm/packages/sans_password) - A simple, passwordless authentication system based on Guardian.\n* [protego](https://hex.pm/packages/protego) - Flexible authentication solution for Elixir/Phoenix with Guardian.\n\n## More advanced secrets\n\nBy specifying a binary, the default behavior is to treat the key as an [`\"oct\"`](https://tools.ietf.org/html/rfc7518#section-6.4) key type (short for octet sequence). This key type may be used with the `\"HS256\"`, `\"HS384\"`, and `\"HS512\"` signature algorithms.\n\nAlternatively, a configuration value that resolves to:\n\n* `Map`\n* `Function`\n* `%JOSE.JWK{} Struct`\n\nMay be specified for other key types. A full list of example key types is available [here](https://gist.github.com/potatosalad/925a8b74d85835e285b9).\n\nSee the [key generation docs](https://hexdocs.pm/jose/key-generation.html) from Jose for how to generate your own keys.\n\nTo get off the ground quickly, set your `secret_key` in your Guardian config with the output of either:\n\n```bash\n$ mix guardian.gen.secret`\n```\n\nor\n\n```elixir\niex\u003e JOSE.JWS.generate_key(%{\"alg\" =\u003e \"HS512\"}) |\u003e JOSE.JWK.to_map |\u003e elem(1) |\u003e Map.take([\"k\", \"kty\"])\n```\n\nAfter running `$ mix deps.get` because JOSE is one of Guardian's dependencies:\n\n```elixir\n## Map ##\n\nconfig :my_app, MyApp.Guardian,\n  allowed_algos: [\"ES512\"],\n  secret_key: %{\n    \"crv\" =\u003e \"P-521\",\n    \"d\" =\u003e \"axDuTtGavPjnhlfnYAwkHa4qyfz2fdseppXEzmKpQyY0xd3bGpYLEF4ognDpRJm5IRaM31Id2NfEtDFw4iTbDSE\",\n    \"kty\" =\u003e \"EC\",\n    \"x\" =\u003e \"AL0H8OvP5NuboUoj8Pb3zpBcDyEJN907wMxrCy7H2062i3IRPF5NQ546jIJU3uQX5KN2QB_Cq6R_SUqyVZSNpIfC\",\n    \"y\" =\u003e \"ALdxLuo6oKLoQ-xLSkShv_TA0di97I9V92sg1MKFava5hKGST1EKiVQnZMrN3HO8LtLT78SNTgwJSQHAXIUaA-lV\"\n  }\n\n## Tuple ##\n# If, for example, you have your secret key stored externally (in this example, we're using Redix).\n\n# defined elsewhere\ndefmodule MySecretKey do\n  def fetch do\n    # Bad practice for example purposes only.\n    # An already established connection should be used and possibly cache the value locally.\n    {:ok, conn} = Redix.start_link\n    rsa_jwk = conn\n      |\u003e Redix.command!([\"GET my-rsa-key\"])\n      |\u003e JOSE.JWK.from_binary\n    Redix.stop(conn)\n    rsa_jwk\n  end\nend\n\nconfig :my_app, MyApp.Guardian,\n  allowed_algos: [\"RS512\"],\n  secret_key: {MySecretKey, :fetch, []}\n\n## %JOSE.JWK{} Struct ##\n# Useful if you store your secret key in an encrypted JSON file with the passphrase in an environment variable.\n\n# defined elsewhere\ndefmodule MySecretKey do\n  def fetch do\n    System.get_env(\"SECRET_KEY_PASSPHRASE\") |\u003e JOSE.JWK.from_file(System.get_env(\"SECRET_KEY_FILE\"))\n  end\nend\n\nconfig :my_app, MyApp.Guardian,\n  allowed_algos: [\"Ed25519\"],\n  secret_key: {MySecretKey, :fetch, []}\n```\n### Private/Public Key pairs\n\nA full example of how to configure guardian to use private/public key files as secrets, can be found [here](https://github.com/ueberauth/guardian_pemfile_config_example).\n\n### Key Rotation\n\nGuardian provides a `Guardian.Token.Jwt.SecretFetcher` behaviour that allows custom keys to be used for signing and verifying requests.\nThis makes it possible to rotate private keys while maintaining a list of valid public keys that can be used both for validating signatures as well as serving public keys to external services.\n\nBelow is a simple example of how this can be implemented using a `GenServer`.\n\n\n```elixir\ndefmodule MyApp.Guardian.KeyServer do\n  @moduledoc ~S\"\"\"\n  A simple GenServer implementation of a custom `Guardian.Token.Jwt.SecretFetcher`\n  This is appropriate for development but should not be used in production\n  due to questionable private key storage, lack of multi-node support,\n  node restart durability, and public key garbage collection.\n  \"\"\"\n\n  use GenServer\n\n  @behaviour Guardian.Token.Jwt.SecretFetcher\n\n  @impl Guardian.Token.Jwt.SecretFetcher\n  # This will always return a valid key as a new one will be generated\n  # if it does not already exist.\n  def fetch_signing_secret(_mod, _opts),\n    do: {:ok, GenServer.call(__MODULE__, :fetch_private_key)}\n\n  @impl Guardian.Token.Jwt.SecretFetcher\n  # This assumes that the adapter properly assigned a key id (kid)\n  # to the signing key. Make sure it's there! with something like\n  # JOSE.JWK.merge(jwk, %{\"kid\" =\u003e JOSE.JWK.thumbprint(jwk)})\n  # see https://tools.ietf.org/html/rfc7515#section-4.1.4\n  # for details\n  def fetch_verifying_secret(_mod, %{\"kid\" =\u003e kid}, _opts) do\n    case GenServer.call(__MODULE__, {:fetch_public_key, kid}) do\n      {:ok, public_key} -\u003e {:ok, public_key}\n      :error -\u003e {:error, :secret_not_found}\n    end\n  end\n\n  def fetch_verifying_secret(_, _, _), do: {:error, :secret_not_found}\n\n  # This is not a defined callback for the SecretFetcher, but could be useful\n  # for providing an endpoint that external services could use to verify tokens\n  # for themselves.\n  def fetch_verifying_secrets,\n    do: GenServer.call(__MODULE__, :fetch_public_keys)\n\n  # Expire the private key so that a new one will be generated on the next\n  # signing request. The public key associated with the old private key should\n  # be stored at the very least as long as the largest possible \"exp\"\n  # (https://tools.ietf.org/html/rfc7519#section-4.1.4) value for any token\n  # signed by the old private key before this method was called.\n  def expire_private_key,\n    do: GenServer.cast(__MODULE__, :expire_private_key)\n\n  # Generate a new key pair along with the key ID (kid)\n  @spec generate_keypair() :: {:ok, JOSE.JWK.t(), JOSE.JWK.t(), String.t()}\n  def generate_keypair() do\n    # Choose an appropriate signing algorithm for your security needs.\n    private_key = JOSE.JWK.generate_key({:okp, :Ed25519})\n\n    # Generate a kid by using the key's thumbprint\n    # https://tools.ietf.org/html/draft-ietf-jose-jwk-thumbprint-08#section-1\n    kid = JOSE.JWK.thumbprint(private_key)\n\n    # Update the private key to contain the \"kid\"\n    private_key = JOSE.JWK.merge(private_key, %{\"kid\" =\u003e kid})\n\n    # Create a public key based on the private key. It will carry the same \"kid\"\n    public_key = JOSE.JWK.to_public(private_key)\n\n    {:ok, private_key, public_key, kid}\n  end\n\n  def start_link(_opts) do\n    GenServer.start_link(__MODULE__, :ok, name: __MODULE__)\n  end\n\n  def init(_opts) do\n    {:ok, %{private_key: nil, public_keys: %{}}}\n  end\n\n  # Callbacks\n\n  def handle_cast(:expire_private_key, state),\n    do: {:noreply, %{state | private_key: nil}}\n\n  # Generate a new signing key if one does not already exist\n  def handle_call(:fetch_private_key, _from, %{private_key: nil, public_keys: key_list}) do\n    {:ok, private_key, public_key, kid} = generate_keypair()\n\n    {:reply, private_key,\n     %{\n       private_key: private_key,\n       public_keys: Map.put(key_list, kid, public_key)\n     }}\n  end\n\n  def handle_call(:fetch_private_key, _from, %{private_key: private_key} = state),\n    do: {:reply, private_key, state}\n\n  def handle_call({:fetch_public_key, kid}, _from, %{public_keys: public_keys} = state),\n    do: {:reply, Map.fetch(public_keys, kid), state}\n\n  def handle_call(:fetch_public_keys, _from, %{public_keys: public_keys} = state),\n    do: {:reply, Map.values(public_keys), state}\nend\n```\n\nUpdate Guardian's configuration to use the custom KeyServer:\n\n```elixir\n## config/config.exs\n\nconfig :my_app, MyApp.Guardian,\n  issuer: \"myapp\",\n  allowed_algos: [\"Ed25519\"],\n  secret_fetcher: MyApp.Guardian.KeyServer\n```\n\nStart the KeyServer in the supervision tree so it can serve requests:\n\n```elixir\n## lib/my_app/application.ex\n\ndef start(_type, _args) do\n  # List all child processes to be supervised\n  children =\n  [\n    MyAppWeb.Endpoint,\n    MyApp.Guardian.KeyServer\n  ]\n\n  # See https://hexdocs.pm/elixir/Supervisor.html\n  # for other strategies and supported options\n  opts = [strategy: :one_for_one, name: MyApp.Supervisor]\n  Supervisor.start_link(children, opts)\nend\n```\n\n## Copyright and License\n\nCopyright (c) 2015 Daniel Neighman\n\nThis library is MIT licensed. See the [LICENSE](https://github.com/ueberauth/guardian/blob/master/LICENSE) for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fueberauth%2Fguardian","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fueberauth%2Fguardian","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fueberauth%2Fguardian/lists"}