Giter VIP home page Giter VIP logo

guardian's Introduction

Überauth

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

An Elixir Authentication System for Plug-based Web Applications

Ueberauth is a two-phase authentication framework that provides a clear API - allowing for many strategies to be created and shared within the community. It is heavily inspired by Omniauth. You could call it a port but it is significantly different in operation - but almost the same concept. Huge hat tip to Intridea.

Ueberauth provides only the initial authentication challenge, (initial OAuth flow, collecting the information from a login form, etc). It does not authenticate each request, that's up to your application. You could issue a token or put the result into a session for your applications needs. Libraries like Guardian can help you with that aspect of authentication.

The two phases are request and callback. These phases are implemented by Strategies.

Strategies

Strategies are plugs that decorate or intercept requests (or both).

Strategies implement the two phases and then may allow the request to flow through to your downstream plugs. Implementing the request and callback phases is optional depending on the strategies requirements. If a strategy does not redirect, the request will be decorated with Ueberauth information and allowed to carry on through the pipeline.

See the full list of the strategies on the Wiki.

Request Phase

The request phase is where you request information about the user. This could be a redirect to an OAuth2 authorization url or a form for collecting username and password. The request phase is concerned with only the collection of information. When a request comes in on the request phase url the relevant strategy will receive the handle_request! call.

In some cases (default) the application using Ueberauth is responsible for implementing the request phase. That is, you should set up a route to receive the request phase and provide a form etc. In some cases, like OAuth, the request phase is used to redirect your user to a 3rd party site to fulfill the request.

For example, an OAuth strategy for GitHub will receive the request phase url and stop the request, redirecting you to GitHub’s OAuth challenge url with some query parameters. Once you complete the GitHub OAuth flow, the user will be redirected back to the host site to the callback URL.

Another example is simple email/password authentication. A request is made by the client to the request phase path and the host application displays a form. The strategy will likely not do anything with the incoming handle_request! request and simply pass through to the application. Once the form is completed, the POST should go to the callback url where it is handled (passwords checked, users created / authenticated).

Callback Phase

The callback phase is where the fun happens. Once a successful request phase has been completed, the request phase provider (OAuth provider or host site, etc) should call the callback URL. The strategy will intercept the request via the handle_callback!. If successful, it should prepare the connection so the Ueberauth.Auth struct can be created, or set errors to indicate a failure.

See Ueberauth.Strategy for more information on constructing the Ueberauth.Auth struct.

Looking for an example? Take a look ueberauth/ueberauth_example.

Setup

Add the dependency

# mix.exs

defp deps do
  # Add the dependency
  [{:ueberauth, "~> 0.10"}]
end

Fetch the dependencies

mix deps.get

Configuring providers

In your configuration file (config/config.exs) provide a list of the providers you intend to use. For example:

config :ueberauth, Ueberauth,
  providers: [
    facebook: { Ueberauth.Strategy.Facebook, [ opt1: "value", opts2: "value" ] },
    github: { Ueberauth.Strategy.Github, [ opt1: "value", opts2: "value" ] }
  ]

This will define two providers for you. The general structure of the providers value is:

config :ueberauth, Ueberauth,
  providers: [
    <provider name>: { <Strategy Module>, [ <strategy options> ] }
  ]

We use the configuration options for defining these to allow for dependency injection in different environments. The provider name will be used to construct request and response paths (by default) but will also be returned in the Ueberauth.Auth struct as the provider field.

Once you've setup your providers, in your router you need to configure the plug to run. The plug should run before your application routes.

In phoenix, plug this module in your controller:

defmodule MyApp.AuthController do
  use MyApp.Web, :controller
  plug Ueberauth
  ...
end

Its URL matching is done via pattern matching rather than explicit runtime checks so your strategies will only fire for relevant requests.

Now that you have this, your strategies will intercept relevant requests for each strategy for both request and callback phases. The default urls are (for our Facebook & GitHub example)

# Request phase paths
/auth/facebook
/auth/github

# Callback phase paths
/auth/facebook/callback
/auth/github/callback

Customizing Paths

These paths can be configured on a per strategy basis by setting options on the provider.

Note: These paths are absolute

config :ueberauth, Ueberauth,
  base_path: "/login", # default is "/auth"
  providers: [
    identity: {Ueberauth.Strategies.Identity, [request_path: "/login/identity",
                                               callback_path: "/login/identity/callback"]}
  ]

Customizing JSON Serializer

Your JSON serializer can be configured depending on what you have installed in your application. Defaults to Jason.

config :ueberauth, Ueberauth,
  json_library: Poison # default is Jason

HTTP Methods

By default, all callback URLs are only available via the "GET" method. You can override this via options to your strategy.

providers: [
  identity: {Ueberauth.Strategies.Identity, [callback_methods: ["POST"]]}
]

Strategy Options

All options that are passed into your strategy are available at runtime to modify the behaviour of the strategy.

Copyright and License

Copyright (c) 2015 Sonny Scroggin

Released under the MIT License, which can be found in the repository in LICENSE.

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

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?

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.

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},

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.

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

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!

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?

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.

Make Generators

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

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.

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

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!

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

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?

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.

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

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

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)

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

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?

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.

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.

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?

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?

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.

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?

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?

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?

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.

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?

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

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.

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?

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!

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.

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.

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.