Code Monkey home page Code Monkey logo

guardian's Introduction

Guardian

An authentication library for use with Elixir applications.

Build Status Codecov Inline docs Module Version Hex Docs Total Download License Last Updated

Guardian is a token based authentication library for use with Elixir applications.

Guardian 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.

The core currency of authentication in Guardian is the token. By default JSON Web Tokens are supported out of the box but you can use any token that:

  • Has the concept of a key-value payload
  • Is tamper proof
  • Can serialize to a String
  • Has a supporting module that implements the Guardian.Token behaviour

You can use Guardian tokens to authenticate:

  • Web endpoints (Plug/Phoenix/X)
  • Channels/Sockets (Phoenix - optional)
  • Any other system you can imagine. If you can attach an authentication token you can authenticate it.

Tokens 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.

Guardian also allows you to configure multiple token types/configurations in a single application.

Documentation

API documentation is available at https://hexdocs.pm/guardian

Installation

Add Guardian to your application to your list of dependencies in mix.exs:

defp deps do
  [
    {:guardian, "~> 2.3"}
  ]
end

In 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. To do this, create a module that uses Guardian and implements the subject_for_token/2 and resource_from_claims/1 function.

defmodule MyApp.Guardian do
  use Guardian, otp_app: :my_app

  def subject_for_token(%{id: id}, _claims) do
    # You can use any value for the subject of your token but
    # it should be useful in retrieving the resource later, see
    # how it being used on `resource_from_claims/1` function.
    # A unique `id` is a good subject, a non-unique email address
    # is a poor subject.
    sub = to_string(id)
    {:ok, sub}
  end
  def subject_for_token(_, _) do
    {:error, :reason_for_error}
  end

  def resource_from_claims(%{"sub" => id}) do
    # Here we'll look up our resource from the claims, the subject can be
    # found in the `"sub"` key. In above `subject_for_token/2` we returned
    # the resource id so here we'll rely on that to look it up.
    resource = MyApp.get_resource_by_id(id)
    {:ok,  resource}
  end
  def resource_from_claims(_claims) do
    {:error, :reason_for_error}
  end
end

Next we need to add our configuration to config/config.exs:

config :my_app, MyApp.Guardian,
       issuer: "my_app",
       secret_key: "Secret key. You can use `mix guardian.gen.secret` to get one"

Congrats! We have a working Guardian implementation.

Basics

# encode a token for a resource
{:ok, token, claims} = MyApp.Guardian.encode_and_sign(resource)

# decode and verify a token
{:ok, claims} = MyApp.Guardian.decode_and_verify(token)

# revoke a token (use GuardianDb or something similar if you need revoke to actually track a token)
{:ok, claims} = MyApp.Guardian.revoke(token)

# Refresh a token before it expires
{:ok, _old_stuff, {new_token, new_claims}} = MyApp.Guardian.refresh(token)

# Exchange a token of type "refresh" for a new token of type "access"
{:ok, _old_stuff, {new_token, new_claims}} = MyApp.Guardian.exchange(token, "refresh", "access")

# Lookup a resource directly from a token
{:ok, resource, claims} = MyApp.Guardian.resource_from_token(token)

With Plug:

# The token/resource/claims will be stored on the connection.
# The token will also be stored in the session (if fetched)
conn = MyApp.Guardian.Plug.sign_in(conn, resource)

# Optionally with claims and options
conn = MyApp.Guardian.Plug.sign_in(conn, resource, %{some: "claim"}, ttl: {1, :minute})

# remove from session (if fetched) and revoke the token
# can also clear the remember me token, if the option :clear_remember_me is set
conn = MyApp.Guardian.Plug.sign_out(conn)

# Set a "refresh" token directly on a cookie.
# Can be used in conjunction with `Guardian.Plug.VerifyCookie` and `Guardian.Plug.SlidingCookie`
conn = MyApp.Guardian.Plug.remember_me(conn, resource)

# Fetch the information from the current connection
token = MyApp.Guardian.Plug.current_token(conn)
claims = MyApp.Guardian.Plug.current_claims(conn)
resource = MyApp.Guardian.Plug.current_resource(conn)

Creating with custom claims and options:

# Add custom claims to a token
{:ok, token, claims} = MyApp.Guardian.encode_and_sign(resource, %{some: "claim"})

# Create a specific token type (i.e. "access"/"refresh" etc)
{:ok, token, claims} = MyApp.Guardian.encode_and_sign(resource, %{}, token_type: "refresh")

# Customize the time to live (ttl) of the token
{:ok, token, claims} = MyApp.Guardian.encode_and_sign(resource, %{}, ttl: {1, :minute})

# Customize the secret
{:ok, token, claims} = MyApp.Guardian.encode_and_sign(resource, %{}, secret: "custom")
{:ok, token, claims} = MyApp.Guardian.encode_and_sign(resource, %{}, secret: {SomeMod, :some_func, ["some", "args"]})

# Require an "auth_time" claim to be added.
{:ok, token, claims} = MyApp.Guardian.encode_and_sign(resource, %{}, auth_time: true)

Decoding tokens:

# Check some literal claims. (i.e. this is an access token)
{:ok, claims} = MyApp.Guardian.decode_and_verify(token, %{"typ" => "access"})

# Use a custom secret
{:ok, claims} = MyApp.Guardian.decode_and_verify(token, %{}, secret: "custom")
{:ok, claims} = MyApp.Guardian.decode_and_verify(token, %{}, secret: {SomeMod, :some_func, ["some", "args"]})

# Specify a maximum age (since end user authentication time). If the token has an
# `auth_time` claim and it is older than the `max_age` allows, the token will be invalid.
{:ok, claims} = MyApp.Guardian.decode_and_verify(token, %{}, max_age: {2, :hours})

If you need dynamic verification for JWT tokens, please see the documentation for Guardian.Token.Jwt and Guardian.Token.Jwt.SecretFetcher.

Configuration

The following configuration is available to all implementation modules.

  • token_module - The module that implements the functions for dealing with tokens. Default Guardian.Token.Jwt.

Guardian can handle tokens of any type that implements the Guardian.Token behaviour. Each token module will have its own configuration requirements. Please see below for the JWT configuration.

All configuration values may be provided in two ways.

  1. In your config files
  2. As a Keyword list to your call to use Guardian in your implementation module.

Any options given to use Guardian have precedence over config values found in the config files.

Some configuration may be required by your token_module.

Configuration values

Guardian supports resolving configuration options at runtime, to that we use the following syntax:

  • {MyModule, :func, [:some, :args]} Calls the function on the module with args

These are evaluated at runtime and any value that you fetch via

MyApp.Guardian.config(key, default) will be resolved using this scheme.

See Guardian.Config.resolve_value/1 for more information.

JWT (Configuration)

The default token type of Guardian is JWT. It accepts many options but you really only need to specify the issuer and secret_key.

Required configuration (JWT)

  • issuer - The issuer of the token. Your application name/id
  • secret_key - The secret key to use for the implementation module. This may be any resolvable value for Guardian.Config.

Optional configuration (JWT)

  • token_verify_module - default Guardian.Token.Jwt.Verify. The module that verifies the claims
  • allowed_algos - The allowed algos to use for encoding and decoding. See JOSE for available. Default ["HS512"]
  • ttl - The default time to live for all tokens. See the type in Guardian.ttl
  • token_ttl a map of token_type to ttl. Set specific ttls for specific types of tokens
  • allowed_drift The drift that is allowed when decoding/verifying a token in milliseconds
  • verify_issuer Default false
  • secret_fetcher A module used to fetch the secret. Default: Guardian.Token.Jwt.SecretFetcher
  • auth_time Include an auth_time claim to denote the end user authentication time. Default false.
  • max_age Specify the maximum time (since the end user authentication) the token will be valid. Format is the same as ttl. Implies auth_time unless auth_time is set explicitly to false.

See the OpenID Connect Core specification for more details about auth_time and max_age behaviour.

Secrets (JWT)

Secrets can be simple strings or more complicated JOSE secret schemes.

The simplest way to use the JWT module is to provide a simple String. (mix guardian.gen.secret works great)

Alternatively you can use a module and function by adding secret_key: {MyModule, :function_name, [:some, :args]}.

More advanced secret information can be found below.

Using options in calls

Almost all of the functions provided by Guardian utilize options as the last argument. These 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.

Hooks

Each 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.

The callbacks are:

  • after_encode_and_sign
  • after_sign_in
  • before_sign_out
  • build_claims - Use this to tweak the claims that you include in your token
  • default_token_type - default is "access"
  • on_exchange
  • on_revoke
  • on_refresh
  • on_verify
  • verify_claims - You can add custom validations for your tokens in this callback

Plugs

Guardian provides various plugs to help work with web requests in Elixir. Guardian's plugs are optional and will not be compiled if you're not using Plug in your application.

All plugs need to be in a pipeline. A pipeline is just a way to get the implementation module and error handler into the connection for use downstream. More information can be found in the Pipelines section.

Plugs and keys (advanced usage)

All Plugs and related functions provided by Guardian have the concept of a key. A 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.

In your plug pipeline you may use something like:

plug Guardian.Plug.VerifyHeader, key: :impersonate
plug Guardian.Plug.EnsureAuthenticated, key: :impersonate

In your action handler:

resource = MyApp.Guardian.Plug.current_resource(conn, key: :impersonate)
claims = MyApp.Guardian.Plug.current_claims(conn, key: :impersonate)

Plugs out of the box

Guardian.Plug.VerifyHeader

Look for a token in the header and verify it

Guardian.Plug.VerifySession

Look for a token in the session and verify it

Guardian.Plug.VerifyCookie

NOTE: this plug is deprecated. Please use :refresh_from_cookie option in Guardian.Plug.VerifyHeader or Guardian.Plug.VerifySession

Look for a token in cookies and exchange it for an access token

Guardian.Plug.SlidingCookie

Replace the token in cookies with a new one when a configured minimum TTL is remaining.

Guardian.Plug.EnsureAuthenticated

Make sure that a token was found and is valid

Guardian.Plug.EnsureNotAuthenticated

Make sure no one is logged in

Guardian.Plug.LoadResource

If a token was found, load the resource for it

See the documentation for each Plug for more information.

Pipelines

A pipeline is a way to collect together the various plugs for a particular authentication scheme.

Apart 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.

Create a custom pipeline

defmodule MyApp.AuthAccessPipeline do
  use Guardian.Plug.Pipeline, otp_app: :my_app

  plug Guardian.Plug.VerifySession, claims: %{"typ" => "access"}
  plug Guardian.Plug.VerifyHeader, claims: %{"typ" => "access"}
  plug Guardian.Plug.EnsureAuthenticated
  plug Guardian.Plug.LoadResource, allow_blank: true
end

By default, the LoadResource plug will return an error if no resource can be found. You can override this behaviour using the allow_blank: true option.

Add your implementation module and error handler to your configuration:

config :my_app, MyApp.AuthAccessPipeline,
  module: MyApp.Guardian,
  error_handler: MyApp.AuthErrorHandler

By using a pipeline, apart from keeping your auth logic together, you're instructing downstream plugs to use a particular implementation module and error handler.

If you wanted to do that manually:

plug Guardian.Plug.Pipeline, module: MyApp.Guardian,
                             error_handler: MyApp.AuthErrorHandler

plug Guardian.Plug.VerifySession

Plug Error Handlers

The error handler is a module that implements an auth_error function:

defmodule MyApp.AuthErrorHandler do
  import Plug.Conn

  @behaviour Guardian.Plug.ErrorHandler

  @impl Guardian.Plug.ErrorHandler
  def auth_error(conn, {type, _reason}, _opts) do
    body = Jason.encode!(%{message: to_string(type)})

    conn
    |> put_resp_content_type("application/json")
    |> send_resp(401, body)
  end
end

Phoenix

Guardian and Phoenix are perfect together, but to get the most out of the integration be sure to include the guardian_phoenix library.

See the Guardian Phoenix documentation for more information.

Permissions

Permissions can be encoded into your token as an optional add-in.

Encoding permissions into a token is useful in some areas of authorization. The permissions provided by Guardian.Permissions have one level of nesting.

For example:

  • users -> profile_read
  • users -> profile_write
  • users -> followers_read
  • users -> followers_write
  • admin -> all_users_read
  • admin -> all_users_write

Once a permission is granted it is valid for as long as the token is valid. Since 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.

Please see Guardian.Permissions for more information.

Tracking Tokens

When using tokens, depending on the type of token you use, nothing may happen by default when you revoke a token.

For example, JWT tokens by default are not tracked by the application. The 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. If you need to track and revoke individual tokens, you may need to use something like GuardianDb.

This 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.

For more in-depth documentation please see the GuardianDb README.

Best testing practices

How to add the token to a request (the Phoenix way)

Assuming you are using the default authentication scheme Bearer for the Authorization header:

defmodule HelloWeb.AuthControllerTest do
  use HelloWeb.ConnCase
  import HelloWeb.Guardian

 test "GET /auth/me", %{conn: conn} do
    user = insert(:user) # See https://github.com/thoughtbot/ex_machina

    {:ok, token, _} = encode_and_sign(user, %{}, token_type: :access)

    conn = conn
    |> put_req_header("authorization", "Bearer " <> token)
    |> get(auth_path(conn, :me))

    # Assert things here
  end

end

Related projects

  • GuardianDb - Token tracking in the database
  • GuardianPhoenix - Phoenix integration
  • sans_password - A simple, passwordless authentication system based on Guardian.
  • protego - Flexible authentication solution for Elixir/Phoenix with Guardian.

More advanced secrets

By specifying a binary, the default behavior is to treat the key as an "oct" key type (short for octet sequence). This key type may be used with the "HS256", "HS384", and "HS512" signature algorithms.

Alternatively, a configuration value that resolves to:

  • Map
  • Function
  • %JOSE.JWK{} Struct

May be specified for other key types. A full list of example key types is available here.

See the key generation docs from Jose for how to generate your own keys.

To get off the ground quickly, set your secret_key in your Guardian config with the output of either:

$ mix guardian.gen.secret`

or

iex> JOSE.JWS.generate_key(%{"alg" => "HS512"}) |> JOSE.JWK.to_map |> elem(1) |> Map.take(["k", "kty"])

After running $ mix deps.get because JOSE is one of Guardian's dependencies:

## Map ##

config :my_app, MyApp.Guardian,
  allowed_algos: ["ES512"],
  secret_key: %{
    "crv" => "P-521",
    "d" => "axDuTtGavPjnhlfnYAwkHa4qyfz2fdseppXEzmKpQyY0xd3bGpYLEF4ognDpRJm5IRaM31Id2NfEtDFw4iTbDSE",
    "kty" => "EC",
    "x" => "AL0H8OvP5NuboUoj8Pb3zpBcDyEJN907wMxrCy7H2062i3IRPF5NQ546jIJU3uQX5KN2QB_Cq6R_SUqyVZSNpIfC",
    "y" => "ALdxLuo6oKLoQ-xLSkShv_TA0di97I9V92sg1MKFava5hKGST1EKiVQnZMrN3HO8LtLT78SNTgwJSQHAXIUaA-lV"
  }

## Tuple ##
# If, for example, you have your secret key stored externally (in this example, we're using Redix).

# defined elsewhere
defmodule MySecretKey do
  def fetch do
    # Bad practice for example purposes only.
    # An already established connection should be used and possibly cache the value locally.
    {:ok, conn} = Redix.start_link
    rsa_jwk = conn
      |> Redix.command!(["GET my-rsa-key"])
      |> JOSE.JWK.from_binary
    Redix.stop(conn)
    rsa_jwk
  end
end

config :my_app, MyApp.Guardian,
  allowed_algos: ["RS512"],
  secret_key: {MySecretKey, :fetch, []}

## %JOSE.JWK{} Struct ##
# Useful if you store your secret key in an encrypted JSON file with the passphrase in an environment variable.

# defined elsewhere
defmodule MySecretKey do
  def fetch do
    System.get_env("SECRET_KEY_PASSPHRASE") |> JOSE.JWK.from_file(System.get_env("SECRET_KEY_FILE"))
  end
end

config :my_app, MyApp.Guardian,
  allowed_algos: ["Ed25519"],
  secret_key: {MySecretKey, :fetch, []}

Private/Public Key pairs

A full example of how to configure guardian to use private/public key files as secrets, can be found here.

Key Rotation

Guardian provides a Guardian.Token.Jwt.SecretFetcher behaviour that allows custom keys to be used for signing and verifying requests. This 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.

Below is a simple example of how this can be implemented using a GenServer.

defmodule MyApp.Guardian.KeyServer do
  @moduledoc ~S"""
  A simple GenServer implementation of a custom `Guardian.Token.Jwt.SecretFetcher`
  This is appropriate for development but should not be used in production
  due to questionable private key storage, lack of multi-node support,
  node restart durability, and public key garbage collection.
  """

  use GenServer

  @behaviour Guardian.Token.Jwt.SecretFetcher

  @impl Guardian.Token.Jwt.SecretFetcher
  # This will always return a valid key as a new one will be generated
  # if it does not already exist.
  def fetch_signing_secret(_mod, _opts),
    do: {:ok, GenServer.call(__MODULE__, :fetch_private_key)}

  @impl Guardian.Token.Jwt.SecretFetcher
  # This assumes that the adapter properly assigned a key id (kid)
  # to the signing key. Make sure it's there! with something like
  # JOSE.JWK.merge(jwk, %{"kid" => JOSE.JWK.thumbprint(jwk)})
  # see https://tools.ietf.org/html/rfc7515#section-4.1.4
  # for details
  def fetch_verifying_secret(_mod, %{"kid" => kid}, _opts) do
    case GenServer.call(__MODULE__, {:fetch_public_key, kid}) do
      {:ok, public_key} -> {:ok, public_key}
      :error -> {:error, :secret_not_found}
    end
  end

  def fetch_verifying_secret(_, _, _), do: {:error, :secret_not_found}

  # This is not a defined callback for the SecretFetcher, but could be useful
  # for providing an endpoint that external services could use to verify tokens
  # for themselves.
  def fetch_verifying_secrets,
    do: GenServer.call(__MODULE__, :fetch_public_keys)

  # Expire the private key so that a new one will be generated on the next
  # signing request. The public key associated with the old private key should
  # be stored at the very least as long as the largest possible "exp"
  # (https://tools.ietf.org/html/rfc7519#section-4.1.4) value for any token
  # signed by the old private key before this method was called.
  def expire_private_key,
    do: GenServer.cast(__MODULE__, :expire_private_key)

  # Generate a new key pair along with the key ID (kid)
  @spec generate_keypair() :: {:ok, JOSE.JWK.t(), JOSE.JWK.t(), String.t()}
  def generate_keypair() do
    # Choose an appropriate signing algorithm for your security needs.
    private_key = JOSE.JWK.generate_key({:okp, :Ed25519})

    # Generate a kid by using the key's thumbprint
    # https://tools.ietf.org/html/draft-ietf-jose-jwk-thumbprint-08#section-1
    kid = JOSE.JWK.thumbprint(private_key)

    # Update the private key to contain the "kid"
    private_key = JOSE.JWK.merge(private_key, %{"kid" => kid})

    # Create a public key based on the private key. It will carry the same "kid"
    public_key = JOSE.JWK.to_public(private_key)

    {:ok, private_key, public_key, kid}
  end

  def start_link(_opts) do
    GenServer.start_link(__MODULE__, :ok, name: __MODULE__)
  end

  def init(_opts) do
    {:ok, %{private_key: nil, public_keys: %{}}}
  end

  # Callbacks

  def handle_cast(:expire_private_key, state),
    do: {:noreply, %{state | private_key: nil}}

  # Generate a new signing key if one does not already exist
  def handle_call(:fetch_private_key, _from, %{private_key: nil, public_keys: key_list}) do
    {:ok, private_key, public_key, kid} = generate_keypair()

    {:reply, private_key,
     %{
       private_key: private_key,
       public_keys: Map.put(key_list, kid, public_key)
     }}
  end

  def handle_call(:fetch_private_key, _from, %{private_key: private_key} = state),
    do: {:reply, private_key, state}

  def handle_call({:fetch_public_key, kid}, _from, %{public_keys: public_keys} = state),
    do: {:reply, Map.fetch(public_keys, kid), state}

  def handle_call(:fetch_public_keys, _from, %{public_keys: public_keys} = state),
    do: {:reply, Map.values(public_keys), state}
end

Update Guardian's configuration to use the custom KeyServer:

## config/config.exs

config :my_app, MyApp.Guardian,
  issuer: "myapp",
  allowed_algos: ["Ed25519"],
  secret_fetcher: MyApp.Guardian.KeyServer

Start the KeyServer in the supervision tree so it can serve requests:

## lib/my_app/application.ex

def start(_type, _args) do
  # List all child processes to be supervised
  children =
  [
    MyAppWeb.Endpoint,
    MyApp.Guardian.KeyServer
  ]

  # See https://hexdocs.pm/elixir/Supervisor.html
  # for other strategies and supported options
  opts = [strategy: :one_for_one, name: MyApp.Supervisor]
  Supervisor.start_link(children, opts)
end

Copyright and License

Copyright (c) 2015 Daniel Neighman

This library is MIT licensed. See the LICENSE for details.

guardian's People

Contributors

aaronjensen avatar aaronrenner avatar andreaazzini avatar asummers avatar bbhoss avatar doomspork avatar ericsullivan avatar geofflane avatar giddie avatar hanspagh avatar hassox avatar janpieper avatar kianmeng avatar koobob avatar manukall avatar michaelforrest avatar mwri avatar nolman avatar pedroassumpcao avatar pera avatar potatosalad avatar rbino avatar ricn avatar samhamilton avatar scrogson avatar stephanerob avatar svileng avatar tarzan avatar victorlcampos avatar yordis avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

guardian's Issues

Guardian.Plug.sign_in is not injecting permissions

I'm using and updated version of the Guardian example it seems that whatever combination of permissions I try to inject (for instance, "Guardian.Plug.sign_in(user, :token, perms: %{ default: :write_profile })" it won work.
When inspecting claims I always see: ..."pem" => %{"default" => -1},

Having problems with permissions and Guardian.Plug.sign_in

It is entirely possible that something is configured incorrectly on my end, however I think I followed the setup guides pretty well.

I set my permissions in config like so:

# config.ex

config :guardian, Guardian,
  issuer:        "Apollo",
  ttl:           {30, :days},
  verify_issuer: true,
  secret_key:    "4AvxMVXpO7XjeO+AVPwh1SQi+jz5rd3PtI4I0ifxbuZAshlZ2SZMKPSvr3gqZzXf",
  serializer:    Apollo.GuardianSerializer,
  permissions: %{client:                  [:insert, :update, :delete, :show],
                 client_type:             [:insert, :update, :delete, :show],
                 custom_field_definition: [:insert, :update, :delete, :show],
                 custom_field_section:    [:insert, :update, :delete, :show],
                 event_type:              [:insert, :update, :delete, :show],
                 group:                   [:insert, :update, :delete, :show],
                 item:                    [:insert, :update, :delete, :show],
                 item_type:               [:insert, :update, :delete, :show],
                 location:                [:insert, :update, :delete, :show],
                 location_type:           [:insert, :update, :delete, :show],
                 user:                    [:insert, :update, :delete, :show]}

And I am assigning permissions in the sign_in method like so:

# session_controller.ex

conn
|> AuditAction.record_action
|> put_flash(:info, "Logged in.")
|> Guardian.Plug.sign_in(user, :token, perms: Authentication.perms_for(user))
|> redirect(to: user_path(conn, :index))

Authentication.perms_for/1 seems to be returning permisssions formatted correctly. For example:

%{client:      [:show, :insert],
  client_type: [:show, :insert]}

However, when I try to check permissions, it looks like Guardian is consistently assigning 0 to everything. For example:

# Output from `Authentication.perms_for(Guardian.Plug.current_resource(conn))`
%{client: [:show], client_type: [:show], event_type: [:insert, :update, :show],
  group: [:show], item: [:insert, :update, :show], item_type: [:update, :show],
  location: [:show], location_type: [:show], user: [:show]}

# Output from `Guardian.Plug.claims(conn)`
{:ok,
 %{"aud" => "token", "exp" => 1448828739, "iat" => 1446236739,
   "iss" => "Apollo", "jti" => "078330cb-4f97-492a-9cb1-6529e9ca2c9e",
   "pem" => %{"client" => 0, "client_type" => 0, "event_type" => 0,
     "group" => 0, "item" => 0, "item_type" => 0, "location" => 0,
     "location_type" => 0, "user" => 0}, "sub" => "User:922796382408934407"}}

In iex if I run Guardian.Permissions.available, the result is [].

I'm not sure if I am providing enough information to assist, so if not I'd be more than happy to provide more. It sounds similar to #47 however I am using version 0.6.3.

Thanks for the help!

Logout method for API

I login to api via Guardian.encode_and_sign(xxx). I cannot use Guardian.Plug.sign_out(conn) to logout from api. It said "cannot fetch_session". and method Guardian.revoke! do nothing with authentication data in header

IO.puts

I think it's a code smell for a library to do 'IO.puts' on errors, the errors should be reflected only in the function's output.

My opinion only so if you agree I could make the fix myself and submit a PR.

Guardian.Plug.EnsureNotAuthenticated

Hi,
I'm coding a login/sign up views that I have guarantee that the user are not Authenticated, if it is, I have to do something(handler).

Could I code this plug and submit a pull request?

git tag releases

Hey,
Would it be possible to git-tag the version bumps in the future?

I'm happy to do the grunt work and find the previous commit ids if backward tags can be done by you? (Guessing it requires force push of master, so not repo friendly to do!) but the offer is there!

Strategy Authentication

Could you elaborate on what your thoughts are around how the Strategies would work?

I've tried to work it out, but the {:ok, resource} syntax seems to make it verbose while handling the response. An alternative would just have the user run the authenticator on their own and add the Guardian.sign_in, with the added bonus of being able to have a spot to easily reject the authentication (deactivated account, flood control, etc).

defmodule Guardian.Authenticator do
  use Behaviour

  @doc "Runs an authentication strategy and returns the resource if successful"
  defcallback authenticate(conn :: Plug.Conn.t, params :: term) :: { :ok, resource :: term } | { :error, String.t }
end
defmodule Guardian.Strategy do
  @spec execute(Plug.Conn.t, term, term) :: Plug.Conn.t | {:error, String.t}
  def execute(conn, params, authenticator) do
    case authenticator.authenticate(conn, params) do
      {:ok, resource} -> Guardian.sign_in(conn, resource)
      {:error, message} -> {:error, message}
    end
  end
end
def create(conn, params = %{}) do
  case Guardian.Strategy.execute(conn, params, AuithenticatorModule) do
    conn -> conn
      |> redirect(to: user_path(conn, :index))
    {:error, message} -> conn
      |> put_flash(:error, message)
      |> redirect(to: "/")
  end
end

Proposal Guardian Refresh Tokens

A common use case for many api's that are consumed by mobile app's is to get a new token without re-authenticating to avoid contentiously prompt users with login screens. This is most commonly done with refresh tokens. Would it be an idea to make a project similar to GuardianDb to implement this behavior?

Permissions aren't working

I've been playing around with permissions and haven't been able to get them working... I've set them in my config file:

permissions: %{
    admin: [
      :users
    ]
  }

and then am assigning them on sign in like this:

Guardian.Plug.sign_in(user, :token, perms: %{ admin: [:users]}) 

In my controller I am then using:

plug Guardian.Plug.EnsurePermissions, on_failure: { Blog.UserController, :unauthenticated }, admin: [:users]

The on_failure callback gets called every time I try to access the controller. In order to figure out what was going on I started playing around with the Guardian.Permissions API. When I call:

Guardian.Permissions.to_value([:users]) 

it returns 0. I assume there is something wrong as the docs say that an integer from 1-31 should be assigned.

In addition, when I inspect the claims the "pem" key looks like this:

"pem" => %{"admin" => 0}

Any ideas as to what is going on?

Allow quick sign in for acceptance tests

When I'm working in Ruby, I really enjoy how Clearance's Backdoor Middleware allows me to skip the sign in process when writing browser-level acceptance tests. When in my test environment, I can append the as query string parameter when I make a request, and I'm automatically signed in as that user. (Here's how I'd make the request using Hound).

navigate_to "/?as=#{user.id}"

What would you think about guardian having a middleware that allows developers to be able to skip the sign in process during acceptance tests? To enable this, I was thinking they might add the following to their web/router.ex file.

pipeline :browser_session do
  if Mix.env == :test do
    plug Guardian.Plug.Backdoor
  end

  plug Guardian.Plug.VerifySession
  plug Guardian.Plug.LoadResource
end

I'd be happy to work on this and I'm still thinking over the implementation details, but I wanted to run it by you before I start.

Less dependencies, more fun

Current usage of Calendar dependency is a huge overkill, especially for compilation, releases.

It might be replaced with:

{mgsec, sec, _usec} = :os.timestamp
mgsec * 1_000_000 + sec

Discussion: Guardian.Plug namespace

I could be totally wrong, but I'm a tiny bit on the fence with the semantics of the Plug namespace.

Since Plug is generally concerned with a "connection", should the Guardian.Plug namespace be Guardian.Conn instead?

I realize that the Guardian.Plug namespace indicates that these modules provide the Plug contract (behaviour), just wondering if it's necessary?

Thoughts?

Allow setting the secret on tokens

On encode_and_sign allow setting a secret so that people can use a custom secret per token for signing. This would be considered 'advanced' since you'd need to provide an identifier to lookup the secret along with the token (maybe)

Expand JWT acronym

The README refers to "JWT" in a number of places, but never explains what it is. I'm an old fart web developer and it took me quite a bit of googling to figure out that this meant JSON Web Tokens. Expanding that in the README would help a lot with readability.

Storing Guardian permissions in the User record?

Hi, I'm new to Elixir/Phoenix/Guardian, but I've gotten an API up and running with Guardian configured. Presently, I'm hard-coding permissions for all my users like so:

{ :ok, jwt, full_claims } = Guardian.encode_and_sign(user, :token, %{
  name: user.name,
  perms: %{ admin: [:dashboard] }
})

But I was wondering how I might save the permissions bit on my user record, so that different users could have different permissions (and thus get different tokens).

How might I do this?

Guardian.Permissions.all?

I'm a bit confused by all? I'm assuming it means all permissions are verified, but in the below example a non existent value checks true.

assuming the below config from your example in the README

config :guardian, Guardian,
  issuer: "Aclpho",
  ttl: { 30, :days },
  verify_issuer: true,
  secret_key: "xxxxxxxxxxxxxxxxxx123411",
  serializer: Aclpho

config :guardian, Guardian,
       permissions: %{
         default: [:read, :write],
         admin: [:dashboard, :reconcile]
       }

the test channel

defmodule Aclpho.RC do
  require Logger
  use Phoenix.Channel
  use Guardian.Channel

  def join(_room, %{ claims: claims, resource: resource }, socket) do
    Logger.warn "claims: " <> inspect claims
    case Guardian.verify(resource) do
      { :ok, claims } ->
        Logger.warn "verified claims: " <> inspect claims
        can_read? = Guardian.Permissions.from_claims(claims, :default)
          |> Guardian.Permissions.any?([:read], :default)
        can_write? = Guardian.Permissions.from_claims(claims, :default)
          |> Guardian.Permissions.any?([:write], :default)
        can_foo? = Guardian.Permissions.from_claims(claims, :default)
          |> Guardian.Permissions.all?([:foo], :default)
          #|> Guardian.Permissions.any?([:foo], :default)
        Logger.warn "permissions were: " <> inspect {can_read?,can_write?,can_foo?}

        { :ok, %{ message: "Joined #{inspect clams.sub}" }, socket }
      { :error, reason } ->
        Logger.warn "sad face :("
        { :error,  :authentication_failed}
    end
  end

uncommenting the any? line prints the expected {true, true,false} tuple. Why does aall? return true for a permission that does not exist?

Question/Feature Request:

Is there a way to keep session upon app restart? Or if not, is it possible to hook up redis or something to handle this?

Guardian.Plug.EnsureAuthenticated on failure using wrong controller

Hi there,

Quite new to elixir and phoenix so apologies if this isn't a bug but a mistake in my code, I had a look at the code and couldn't see what was wrong with it to be able contribute a fix or confirm if it is a bug in Guardian or I'm just doing something wrong.

Basically when I use

plug Guardian.Plug.EnsureAuthenticated, on_failure: { SessionController, :new }

In a controller and the on_failure triggers rather than send me to the :new action of the SessionController it seems like it's trying to send me to the :new action of the current controller. I've attached a screenshot of the error and the code the code I'm getting this error with is here: controller where EnsureAuthenticated is being called, https://github.com/oscarduignan/phoenix-time-tracker/blob/master/web/controllers/user_controller.ex#L7; and the action it's trying to use for on_failure, https://github.com/oscarduignan/phoenix-time-tracker/blob/master/web/controllers/session_controller.ex#L6-L8.

ensureauthenticationonfailure

no token on view ?

Hi, i´m sure its my error but i need more info to use guardian
here is my index page.

image

in the console i see the token being inspected

image

however in my index view
i have <%= inspect @conn %> and the token is not there.

if i change the return of index above to
render conn, "index.html" , token: Guardian.Plug.current_token(conn)
i see the token but is set to nill

Can you help a litle more ?

regards
António

Why does EnsurePermissions require key?

I'm developing with Guardian right now, and I want a part of a website to be available to both default user and admin
Because of that I distributed the permissions like that

permisions: %{
  default: [
    :see_page
  ],
  admin: [
    :see_page,
    :do_admin_stuff
   ]
}

But right now if I want to check the permissions using plug I'm unable to check the permission by name
because

plug EnsurePermissions, [handler: __MODULE__, default: [:see_page]]

Which still doesn't allow the admin to see the page, because it check under a key instead of checking the permission itself
Did I get something wrong or is the permissions model quite unintuitive?

Can't create token by encode_and_sign from user resource.

Like in title, can't create token, it seems that it is Guardian or base64url fault, I need to work around it asap.

iex:

user = Repo.get(User, 1)

%Myapp.User{__meta__: #Ecto.Schema.Metadata<:loaded>,
 crypted_password: "$2b$12$CV.lhxwuCT9gfLxek1fC0.uLKNlJnh/ookv7M84UgGO07xzq3V6Zu",
 email: "[email protected]", id: 5,
 inserted_at: #Ecto.DateTime<2015-11-24T18:47:31Z>, password: nil,
 updated_at: #Ecto.DateTime<2015-11-24T18:47:31Z>, username: nil}

{ :ok, jwt, full_claims } = Guardian.encode_and_sign(user, :api)
** (FunctionClauseError) no function clause matching in :base64url.encode/1
    src/base64url.erl:22: :base64url.encode(nil)
    lib/guardian.ex:231: Guardian.jose_jwk/0
    lib/guardian.ex:234: Guardian.encode_claims/1
    lib/guardian.ex:68: Guardian.encode_and_sign/3

mix.lock

%{"base64url": {:hex, :base64url, "0.0.1"},
  "comeonin": {:hex, :comeonin, "1.6.0"},
  "cowboy": {:hex, :cowboy, "1.0.4"},
  "cowlib": {:hex, :cowlib, "1.0.2"},
  "decimal": {:hex, :decimal, "1.1.0"},
  "ecto": {:hex, :ecto, "1.0.6"},
  "fs": {:hex, :fs, "0.9.2"},
  "guardian": {:hex, :guardian, "0.7.1"},
  "jose": {:hex, :jose, "1.4.1"},
  "phoenix": {:hex, :phoenix, "1.0.3"},
  "phoenix_ecto": {:hex, :phoenix_ecto, "1.2.0"},
  "phoenix_html": {:hex, :phoenix_html, "2.2.0"},
  "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.0.1"},
  "plug": {:hex, :plug, "1.0.2"},
  "poison": {:hex, :poison, "1.5.0"},
  "poolboy": {:hex, :poolboy, "1.5.1"},
  "postgrex": {:hex, :postgrex, "0.9.1"},
  "ranch": {:hex, :ranch, "1.2.0"},
  "uuid": {:hex, :uuid, "1.1.1"}}

Poison 2.x support

Has anyone tried Guardian with the new Poison 2.x versions? Any issues?

I've just put poison 2.1 in as an override and haven't seen any issues yet (but it's very early in the day).

How can I authenticate a user to bypass Guardian.Plug.EnsureAuthenticated in controller's test?

Hi!

I am using guardian to authenticate users to access private actions in my phoenix application and everything is working fine.

The problem is that I can't make my controllers tests to pass. All requests are falling on the plug Guardian.Plug.EnsureAuthenticated, rendering the login page instead of executing the action normally.

I have already tried the following in my controller test:

  setup do
    user = Repo.insert(%User{})
    conn = conn()
            |> sign_conn()
            |> Guardian.Plug.sign_in(user, :token, perms: %{ default: Guardian.Permissions.max })
    {:ok, conn: conn}
  end

The sign_conn I got from plug's tests and it is defined in my ConnCase exactly like it is in the link.

I did that because just calling the Guardian.Plug.sign_in in the conn() was raising errors related to fetch_session

So, am I on the right path? If not, what is the best way to bypass this authentication on controller tests?

My best

Problems with Guardian.JWT Joken config Modules decode function (keys: :atoms!)

I had problems verifying tokens from the auth headers, I always got an {:error, "argument error"} in my assigns which happened during JSON decoding of the claims.
Weirdly enough the problem seemed only to appear if I hadn't created a token before.

After looking deeper, I discovered, that it is not the recommended way for the Poison decoder configuration to use atoms as keys, read here:
https://github.com/devinus/poison#parser

Note that keys: :atoms! reuses existing atoms, i.e. if :name was not allocated before the call, you will encounter an argument error message.

You can use the keys: :atoms variant to make sure all atoms are created as needed. However, unless you absolutely know what you're doing, do not do it. Atoms are not garbage-collected, see Erlang Efficiency Guide for more info:

Atoms are not garbage-collected. Once an atom is created, it will never be removed. The emulator will terminate if the limit for the number of atoms (1048576 by default) is reached.

The Guardian.JWT module however uses atoms as keys:

  @doc false
  def decode(binary), do: JSON.decode!(binary, keys: :atoms!)

As I understand, this creates weird problems, where you have to anticipate and instatiate all atoms that you might need for the to-be-decoded JSON text before trying to decode it. Apart from that you could run out of atoms.
I will also open a ticket for the Joken guys, as they have a similar example for a config module in their README.

encode_and_sign without hooks?

It would be nice from a testing perspective to be able to generate a throwaway token for testing, especially when using GuardianDB.

If I've missed this utility somewhere I apologize. If not, I'd definitely be up for submitting a PR.

Write guides on API and Browser Auth with Guardian/Phoenix.

This issue will be closed once I write the guides. It's mostly here as a placeholder to remind myself what I need to do.

The idea is prose + code, a walkthrough that explains both how to use Guardian and why a user should consider it.

Make Generators

Hi,
if you agree I think I can help you to make some generators like device has

jwt working when passed as param, but not from header, though both look identical

This is probably an Elixir string thing, but here goes:

I'm moving from posting a "jwt" param to putting it in the "Authorization" header.

However, I get this weird behavior:

  def a_controller_action(conn, %{ "jwt" => p_jwt} ) do

    h_jwt = get_req_header( conn, "authorization" )
    Logger.error "The jwt from the header looks like this ..."
    Logger.debug h_jwt
    Logger.error "Here's the one from params:"
    Logger.debug p_jwt
    Logger.error "They look the same, but: are those the same?"
    Logger.debug p_jwt == h_jwt
    Logger.error "Son of a gun!"

    case Guardian.decode_and_verify(p_jwt) do
      ...
      # p_jwt works, h_jwt doesn't

The output I get is:

[error] The jwt from the header looks like this ...
[debug] eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJVc2VyOjIzIiwicGVtIjp7fSwianRpIjoiODg4YTBjMjQtZDljMy00YWFmLTkyYTgtNmZiOWE1ZWU0ZTRjIiwiaXNzIjoiRmxhc2hiYW5nIiwiaWF0IjoxNDQ2NzYyNzYzLCJleHAiOjE0NDkzNTQ3NjMsImF1ZCI6InRva2VuIn0.pTVplbARneTmC8-wnAvcajn4Xmc3O4zhs8cF_7gXlXk
[error] Here's the one from params:
[debug] eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJVc2VyOjIzIiwicGVtIjp7fSwianRpIjoiODg4YTBjMjQtZDljMy00YWFmLTkyYTgtNmZiOWE1ZWU0ZTRjIiwiaXNzIjoiRmxhc2hiYW5nIiwiaWF0IjoxNDQ2NzYyNzYzLCJleHAiOjE0NDkzNTQ3NjMsImF1ZCI6InRva2VuIn0.pTVplbARneTmC8-wnAvcajn4Xmc3O4zhs8cF_7gXlXk
[error] They look the same, but: are those the same?
[debug] false
[error] Son of a gun!

Which is funny! They look exactly the same from here.

I'm not sure that this is a Guardian issue, but the thing is, Guardian can decode and use the JWT from params, but when trying to decode the one from headers, I get:

    (stdlib) binary.erl:244: :binary.split/3
    lib/joken/token.ex:102: Joken.Token.verify_signature/3
    lib/joken/token.ex:69: Joken.Token.decode/3
    lib/guardian.ex:127: Guardian.decode_and_verify/2
    (...) web/controllers/user_controller.ex:27: _.UserController.current_user/2
    (...) web/controllers/user_controller.ex:1: _.UserController.action/2
    (...) web/controllers/user_controller.ex:1: _.UserController.phoenix_controller_pipeline/2
    (...) lib/phoenix/router.ex:255: Flashbang.Router.dispatch/2

with no more detail, no lines before/after etc.

Any ideas what could cause two strings that print the same to be different to Elixir? Or why the value extracted from params would be a different binary from the one taken from headers?

What's the best way to get and use a user JWT using API approach?

I followed the guardian phoenix example, and I'm a little confused on how to get a token for a user so they can start making API requests.

I tried copying and pasting the token I got from the browser session, into a API requests Authorization header, but it's failing on EnsureAuthenticated matching on { :error, :no_session }.

This might sound dumb, but..
Will I need to create a user session before calling an API endpoint?

I would like to do something like POST: http://example.com/api/v1/login and pass in an email and password. Then it would return a JWT to use for future API requests.

Anyway, I would like to see your approach on this, because I feel like the example app didnt go into much detail about making API requests with a JWT.

Thank you for any help.

Renaming some things

Hi folks.

There's a bit of confusion around the names of things in Guardian. I'm not surprised because I'm pretty terrible at naming things.

The things that have currently been discussed are:

Functions

Guardian.mint

This function generates a JWT, and calls hooks that incorporate behaviour. Mint does not seem to be a common word for this (it fits! but just not common)

Suggestions so far:
Guardian.encode_and_sign

Guardian.verify

In order to be symmetrical with the replacement for mint, the following has been suggested
Guardian.decode_and_verify

Modules

EnsureSession

This is just flat out poorly named. It implies that you need to be running a session (which is not true).

An alternative might be
EnsureTokenValidity

VerifyAuthorization

This module fetches a token from the Authorization header, but could be named better.

Open for suggestions.

Recompile on permissions update?

Hi,

forgive the somewhat unrelated question, but as a newcomer to Elixir, I just pulled my hair out for way too long because my permissions weren't working ... until I realized that mix actually compiles the Map in config.exs directly into the @perms attribute, and that said compilation isn't automatically re-triggered when my configuration changes. In other words, I have to delete _build/guardian after updating my permissions or else my app won't work.

If this should happen automatically, then something must be missing in my setup, and I'd be grateful for a pointer to a working example.

Cache login

Hello.

The title of this issue is a bit misleading because I don't know how to describe my question in 2-3 words.

I have a bit weird question. I'm building a mobile web app. My app as any other web app can be added to Home Screen in iOS using Safari. The problem is that if you close such 'standalone' app or just send it to background, the next time you open it you will have to login again because, as I understand, cookies are deleted when you send to background or close 'standalone' web app. Similar issue on StackOverflow http://stackoverflow.com/questions/3813599/iphone-bookmark-to-homescreen-removes-cookies-and-session

So I'd like to cache 'something' on client side using localStorage to use this 'something' to be logged in again when I open my 'standalone' web app, so I don't need to submit emai/password every single time when I open my app.
So the question is what should I cache/store on the client side and what should I send to the backend to be logged in?

My current hackity workaround is the following: once a user authenticates using email/password, backend sends Guardian token in the response, client side stores a token in localStorage. Then on the login page javascript checks if there is a token in the cache and sends it to an endpoint(i.e. api/session/exists) with the following plugs in the pipeline:

plug :fetch_session
plug Guardian.Plug.VerifyHeader
plug Guardian.Plug.LoadResource

On the backend side I do the following:

case Guardian.Plug.current_resource(conn) do
  nil -> send_resp(conn, 401, "Unauthenticated")
  user -> Guardian.Plug.sign_in(conn, user) |> send_resp(200, "")
end

In other words I authenticate a user using API approach(jwt token) to make this user authenticated in browser. This is really weird mixture of API and browser authentication and I believe there is a better approach. Also I'm not sure if such approach is secure.
Maybe it's possible somehow to communicate a token in the URL(not in header)?

Could you please help me?

Thank you so much!

Handling Errors in LoadResource

Hi y'all,

Just came across some unexpected functionality:

defmodule MyApp.TokenSerializer do
  @behaviour Guardian.Serializer
  alias MyApp.Repo
  alias MyApp.User

  ...

  def from_token(id) do
    case user = Repo.get(User, id) do
      %User{} ->
        {:ok, user}
      nil ->
        {:error, "Unknown resource"}
    end
  end
end

My naive impression was that that returning an error here would cause Guardian to abort the request in some fashion. However, the error is swallowed up here.

I'll likely implement my own plug to verify that the current_resource is not nil for now, but I thought it might be nice if Guardian provided something similar. Two ideas:

  • Plug.LoadResource could call the unauthenticated method on the handler when the serializer returns an error
  • A new plug like (Plug.EnsureResource?) that calls the unauthenticated method on the handler when the serializer returns an error

I'm happy to take a stab at a PR if you like either idea.

Question about using conn.assigns vs conn.private

I'm still pretty new to Elixir/Phoenix, so please forgive me if this a noob question. Guardian looks like a great authentication library, but I have one question...

I've been looking through the codebase and see a lot of guardian's internal state is stored on conn.assigns instead of using conn.private. It looks like guardian is currently adding keys like :guardian_default_claims and :guardian_default_jwt to the assigns hash, which I think makes them available in phoenix templates by using@guardian_default_claims and @guardian_default_jwt, respectively. I was thinking that the assigns hash was only for things the developer wanted to access in their template using the @variable_name syntax. I'm curious about what the advantage is of putting these keys in conn.assigns versus storing them in conn.private and exposing a helper function to access them if they really need to be accessed.

Any insight on this would be greatly appreciated as I continue to learn Phoenix.

Unable to get value from conn.assigns using Guardian.Plug.claims

I don't think checking the value in conn.assigns dict returns a tuple. all the documentation i can find points now to returning just the value or nil. Since Guardian.Plug.claims is checking for this tuple, https://github.com/hassox/guardian/blob/master/lib/guardian/plug.ex#L161, it's always failing for me with the :no_session error case. This breaks me checking claims and the various plugs like EnsureAuthenticated that use it. I'm using Plug v1.0.2 and I noticed the test suite is set up for v1.0.0 but i can't see the difference in return values for it between the versions. Is there something I'm missing?

Question: How can I authenticate a user via api and session simultaneously?

I am trying to authenticate a user via api, but I'd also like to make sure they remain authenticated should they refresh the page.

Guardian.Plug.sign_in seems to fit the bill for storing user authentication in a cookie, but the cookie doesn't get set when I auth via my api.

This does work:

    conn
    |> Guardian.Plug.sign_in(user)
    |> redirect(to: root_path(conn, :index))

Using these pipelines in this order:

      pipeline :browser do
         plug :accepts, ["html"]
         plug :fetch_session
         plug :fetch_flash
         plug :protect_from_forgery
         plug :put_secure_browser_headers
       end

       pipeline :browser_session do
         plug Guardian.Plug.VerifySession
         plug Guardian.Plug.LoadResource
       end

This does not:

    conn
    |> Guardian.Plug.sign_in(user)
    |> render("session.json")

Using these pipelines in this order:

      pipeline :api do
         plug :accepts, ["json"]
         plug Guardian.Plug.VerifyHeader
         plug Guardian.Plug.LoadResource
      end

      pipeline :api_session do
         plug :fetch_session
         plug :fetch_flash
         plug :put_secure_browser_headers
         plug Guardian.Plug.VerifySession
      end

I'm out of guesses as to why one works and the other doesn't as the behavior of redirect and render seem to be essentially the same.

Is there something I'm missing?

signature with other algorithms than HSxxx

Hey,

I try to use Guardian to sign a JWT with RSA plublic/private key.

I see that your implementation don't allow other signature algorithms than HMAC SHA-xxx (HSxxx). The problem comes from jose_jwk/1 that hardcode an jwk HSxxx.

I would like to offer the possibility to use any algorithm available with JOSE. Should I keep a backward compatibility? If so, have you any idea how to do this properly?

I will propose a pull request later in the day or next week but already does not hesitate to offer me your comments.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.