GithubHelp home page GithubHelp logo

hassox / phoenix_guardian Goto Github PK

View Code? Open in Web Editor NEW
300.0 13.0 70.0 169 KB

A demo application showing usage of guardian and ueberauth

License: MIT License

JavaScript 6.99% Elixir 77.85% CSS 0.05% HTML 15.11%

phoenix_guardian's Introduction

PhoenixGuardian

Build Status

Provides an example implementation for authentication in Phoenix applications using Überauth and Guardian.

The main user model is User which has many Authorizations. This is done so that for any given user they can have authorizations from different sources. Github, Slack, Email/Password etc.

Once we have the user in the system - Guardian steps in to provide request implementation.

The two systems, Überauth and Guardian, are independent and are used together in this application for demonstration purposes.

To start your Phoenix app:

  1. Install dependencies with mix deps.get && npm install
  2. Create and migrate your database with mix ecto.create && mix ecto.migrate
  3. Start Phoenix endpoint with mix phoenix.server
  4. Export secrets for your Github and Slack application in your environment * GITHUB_CLIENT_ID * GITHUB_CLIENT_SECRET * SLACK_CLIENT_ID * SLACK_CLIENT_SECRET

Now you can visit localhost:4000 from your browser.

To visit the admin site, after you've created a user you should flip the "is_admin" boolean on the users table in the database.

Then visit localhost:4000/admin/users

What to look for

The site is broken into two broad parts

  1. Normal section - optionally logged in
  2. Admin section - requires login of a user who is an admin

License

Copyright (c) 2015 Daniel Neighman

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

phoenix_guardian's People

Contributors

aforward avatar diogoneves avatar stevedomin avatar terakilobyte avatar ylankgz 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

phoenix_guardian's Issues

Current Auth Status Inflexible

So, I'm finding this is getting more and more problematic as time goes on. Currently, if I want to use the permissions of a user, I have to create a parser for each user type. Consider the following:

  def load(conn, params, current_user, {:ok, %{"pem" => %{"sys" => perms}}} = claims) do
    do_load(conn, params, current_user, perms)
  end
  def load(conn, params, current_user, {:ok, %{"pem" => %{"admin" => perms}}} = claims) do
    do_load(conn, params, current_user, perms)
  end
  def load(conn, params, current_user, {:ok, %{"pem" => %{"user" => perms}}} = claims) do
    do_load(conn, params, current_user, perms)
  end

Here, I have to create all three function signatures, because it's not possible to do:

  def load(conn, params, current_user, {:ok, %{"pem" => %{type => perms}}} = claims) when type in ["sys", "admin", "user"] do

Any thoughts on an efficient way to do this?

Thanks

Compilation error on file web/router.ex

Hi, I've got a error when try to install phoenix_guardian

** (CompileError) web/router.ex: internal error in v3_core;
crash reason: {case_clause,
{'EXIT',
{badarg,
[{erl_anno,anno_info,[-1],[{file,"erl_anno.erl"},{line,360}]},
{v3_core,record_anno,2,[{file,"v3_core.erl"},{line,2410}]},
{v3_core,expr,2,[{file,"v3_core.erl"},{line,539}]},
{v3_core,safe,2,[{file,"v3_core.erl"},{line,1593}]},
{v3_core,expr,2,[{file,"v3_core.erl"},{line,528}]},
{v3_core,safe,2,[{file,"v3_core.erl"},{line,1593}]},
{v3_core,'-safe_list/2-anonymous-0-',2,
[{file,"v3_core.erl"},{line,1608}]},
{lists,foldr,3,[{file,"lists.erl"},{line,1276}]},
{v3_core,expr,2,[{file,"v3_core.erl"},{line,538}]},
{v3_core,safe,2,[{file,"v3_core.erl"},{line,1593}]},
{v3_core,'-safe_list/2-anonymous-0-',2,
[{file,"v3_core.erl"},{line,1608}]},
{lists,foldr,3,[{file,"lists.erl"},{line,1276}]},
{v3_core,expr,2,[{file,"v3_core.erl"},{line,538}]},
{v3_core,safe,2,[{file,"v3_core.erl"},{line,1593}]},
{v3_core,expr,2,[{file,"v3_core.erl"},{line,528}]},
{v3_core,safe,2,[{file,"v3_core.erl"},{line,1593}]},
{v3_core,'-safe_list/2-anonymous-0-',2,
[{file,"v3_core.erl"},{line,1608}]},
{lists,foldr,3,[{file,"lists.erl"},{line,1276}]},
{v3_core,expr,2,[{file,"v3_core.erl"},{line,652}]},
{v3_core,exprs,2,[{file,"v3_core.erl"},{line,512}]}]}}}

in function compile:'-select_passes/2-anonymous-2-'/2 (compile.erl, line 530)
in call from compile:'-internal_comp/4-anonymous-1-'/2 (compile.erl, line 315)
in call from compile:fold_comp/3 (compile.erl, line 341)
in call from compile:internal_comp/4 (compile.erl, line 325)
in call from compile:'-do_compile/2-anonymous-0-'/2 (compile.erl, line 175)
(stdlib) lists.erl:1338: :lists.foreach/2
(phoenix) expanding macro: Phoenix.Router.before_compile/1
web/router.ex:1: PhoenixGuardian.Router (module)
(elixir) lib/kernel/parallel_compiler.ex:116: anonymous fn/4 in Kernel.ParallelCompiler.spawn_compilers/1

Any Ideas?

Better to add helpers.ex to web.ex inside def controller?

Hi there,

Not sure if it would be better to add helpers.ex to the controller section of web.ex?

What do you think?

I'm going through and writing up instructions on integrating this example into a new project - I'm pretty sure they must exist somewhere but I am learning as I go. I'll make my notes/issues available. How is best?

Incorrect error if user exists on sign up form

If the user already exists, but tries to sign up again using the original email but a different password, rather than prompt the user that the email already exists, the error returned is password_does_not_match. This is because technically users can log in via the sign up form.

Are you planning to update this to Guardian 1.0?

Would really like to see an example of how to migrate to Guardian 1.0, 'cause I find the official upgrade guide a bit unclear on several fronts.

For example, your example projects defines several pipelines in the router, while the Guardian 1.0 guides recommends writing your own pipelines in separate modules...

mix ecto.create error on Windows 10

Hi
when I ran:
...
mix ecto.create

I got this error:

== Compilation error on file web/router.ex ==
** (CompileError) web/router.ex: internal error in v3_core;
crash reason: {case_clause,
{'EXIT',
{badarg,
[{erl_anno,anno_info,[-1],[{file,"erl_anno.erl"},{line,360}]},
{v3_core,record_anno,2,[{file,"v3_core.erl"},{line,2410}]},
{v3_core,expr,2,[{file,"v3_core.erl"},{line,539}]},
{v3_core,safe,2,[{file,"v3_core.erl"},{line,1593}]},
{v3_core,expr,2,[{file,"v3_core.erl"},{line,528}]},
{v3_core,safe,2,[{file,"v3_core.erl"},{line,1593}]},
{v3_core,'-safe_list/2-anonymous-0-',2,
[{file,"v3_core.erl"},{line,1608}]},
{lists,foldr,3,[{file,"lists.erl"},{line,1276}]},
{v3_core,expr,2,[{file,"v3_core.erl"},{line,538}]},
{v3_core,safe,2,[{file,"v3_core.erl"},{line,1593}]},
{v3_core,'-safe_list/2-anonymous-0-',2,
[{file,"v3_core.erl"},{line,1608}]},
{lists,foldr,3,[{file,"lists.erl"},{line,1276}]},
{v3_core,expr,2,[{file,"v3_core.erl"},{line,538}]},
{v3_core,safe,2,[{file,"v3_core.erl"},{line,1593}]},
{v3_core,expr,2,[{file,"v3_core.erl"},{line,528}]},
{v3_core,safe,2,[{file,"v3_core.erl"},{line,1593}]},
{v3_core,'-safe_list/2-anonymous-0-',2,
[{file,"v3_core.erl"},{line,1608}]},
{lists,foldr,3,[{file,"lists.erl"},{line,1276}]},
{v3_core,expr,2,[{file,"v3_core.erl"},{line,652}]},
{v3_core,exprs,2,[{file,"v3_core.erl"},{line,512}]}]}}}

in function compile:'-select_passes/2-anonymous-2-'/2 (compile.erl, line 530)
in call from compile:'-internal_comp/4-anonymous-1-'/2 (compile.erl, line 315)
in call from compile:fold_comp/3 (compile.erl, line 341)
in call from compile:internal_comp/4 (compile.erl, line 325)
in call from compile:'-do_compile/2-anonymous-0-'/2 (compile.erl, line 175)
(stdlib) lists.erl:1338: :lists.foreach/2
(phoenix) expanding macro: Phoenix.Router.before_compile/1
web/router.ex:1: PhoenixGuardian.Router (module)
(elixir) lib/kernel/parallel_compiler.ex:116: anonymous fn/4 in Kernel.ParallelCompiler.spawn_compilers/1


How can I debug it? or Fix it?
Thanks

Users are able to register with identity and impersonate oauth accounts

It seems like a bug, to reproduce.

  1. Authenticate with google -> creates user record with email address from google and authorization
  2. Logout
  3. Create a new identity account with username/password with the same email address -> creates new authorization for the existing user.
  4. New user has got access to the account originated from external providers.

Add support for password resets

I'd be interested to see what you suggest for password resets, since uberauth/guardian don't seem to have something built in. Or do you recommend using sentinel which seems to provide some of this functionality out of the box. Thanks @hassox!

What happens when identity users change their email addresses?

When using identity login Ueberauth stores the email address in the authorizations schema. The provider is set to identity and the uid to the email address. What happens a user changes his or her email address? The uid column in authorizations isn't updated. This is problematic. While it's not hard to solve, I'm not convinced this is a good way to go about this. @hassox what do you think?

Duplicate user in the database when signing-up

I used your example code in my project and everything is working except that when I create a new user it creates two equal users in the database. Do you have an idea where the bug is (or is it me that changed something because you example is working properly with you?).

Walkthrough?

Hi guys,

Like many other newbies I am new to Elixir and Phoenix(Evaluating to Migrate from Meteor/Node.js).
It would really help to have a walkthrough video or blog post to figure what are all the things going on there and why.

Cheers!

Read permissions from config file on run instead of compilation

Guardian.Permissions module reads the permissions from the config file during compilation time instead of runtime.
Because of that changing the config.exs file has no effect post deps compilation, and do change config you need to clear the _deps folder, which shouldn't be the desired behaviour

Unable to complile

Installed latest Phoenix etc per the docs on Mac El Capitan. Cloned this repo. Followed instructions and I get :

== Compilation error on file lib/comeonin/bcrypt.ex ==
** (MatchError) no match of right hand side value: {:error, :on_load_failure}
    (stdlib) erl_eval.erl:669: :erl_eval.do_apply/6


16:31:11.166 [error] Process #PID<0.301.0> raised an exception
** (MatchError) no match of right hand side value: {:error, {:load_failed, 'Failed to load NIF library: \'dlopen(/Users/livz/dev/code/play/old/phoenix_guardian/_build/dev/lib/comeonin/priv/bcrypt_nif.so, 2): image not found\''}}
    lib/comeonin/bcrypt.ex:28: Comeonin.Bcrypt.init/0
    (kernel) code_server.erl:1674: anonymous fn/1 in :code_server.handle_on_load/4
could not compile dependency :comeonin, "mix compile" failed. You can recompile this dependency with "mix deps.compile comeonin", update it with "mix deps.update comeonin" or clean it with "mix deps.clean comeonin"

16:31:11.168 [warn]  The on_load function for module Elixir.Comeonin.Bcrypt returned {{:badmatch,
  {:error,
   {:load_failed,
    'Failed to load NIF library: \'dlopen(/Users/livz/dev/code/play/old/phoenix_guardian/_build/dev/lib/comeonin/priv/bcrypt_nif.so, 2): image not found\''}}},
 [{Comeonin.Bcrypt, :init, 0, [file: 'lib/comeonin/bcrypt.ex', line: 28]},
  {:code_server, :"-handle_on_load/4-fun-0-", 1,
   [file: 'code_server.erl', line: 1674]}]}

The tried mix deps.compile comeonin and mix phoenix.server:

== Compilation error on file lib/phoenix/code_reloader.ex ==
** (CompileError) lib/phoenix/code_reloader.ex:61: function full_path/1 undefined
    (stdlib) lists.erl:1337: :lists.foreach/2
    (stdlib) erl_eval.erl:669: :erl_eval.do_apply/6

Following any further instructions give the same exception.

I tried getting the unmerged pull but had issues with that as well. Any ideas?

Question: Why keep the password and password confirmation on the Authorization model?

I was just going through the code and had a question about this line:

password: password_from_auth(auth),

Could you explain why it's beneficial to set the password and password_confirmation on the Authorization model at all? AFAICT from the code, it seems like the generated token would be enough for authorization purposes, and this just leaves the password hanging around in memory, which could potentially make it vulnerable to buffer overflow attacks.

You've clearly added it here for a specific purpose though so I'm assuming I'm missing something - I just can't seem to find a reference to these fields being used on an authorization struct anywhere.

Thanks!

Citex not found

19:03:43.347 [info]  == Running PhoenixGuardian.Repo.Migrations.CreateUser.change/0 forward

19:03:43.351 [info]  execute "CREATE EXTENSION citext;"
** (Postgrex.Error) ERROR (undefined_file): could not open extension control file  "/usr/share/postgresql/9.3/extension/citext.control"

Does this repo depend on citext? Which version of psql is required? 9.5? http://www.postgresql.org/docs/current/static/citext.html

Lets close this soon 👍

Simulate callback for JSON API requests

I need to forgo the request/callback process in order to login and register using JSON requests. However, there doesn't seem to be a way to populate the Auth object. Is there a general flow for achieving this?

Thanks,
Lee

Empty login/register form submission throws an error

[error] #PID<0.524.0> running PhoenixGuardian.Endpoint terminated
Server: localhost:4000 (http)
Request: POST /auth/identity/callback
** (exit) an exception was raised:
    ** (ArgumentError) nil given for :uid. Comparison with nil is forbidden as it is unsafe. Instead write a query with is_nil/1, for example: is_nil(s.uid)
        (ecto) lib/ecto/query/builder/filter.ex:107: Ecto.Query.Builder.Filter.runtime!/6
        (ecto) lib/ecto/query/builder/filter.ex:98: Ecto.Query.Builder.Filter.runtime!/2
        (ecto) lib/ecto/repo/queryable.ex:304: Ecto.Repo.Queryable.query_for_get_by/3
        (ecto) lib/ecto/repo/queryable.ex:52: Ecto.Repo.Queryable.get_by/5
        (phoenix_guardian) web/auth/user_from_auth.ex:117: PhoenixGuardian.UserFromAuth.auth_and_validate/2
        (phoenix_guardian) web/auth/user_from_auth.ex:7: PhoenixGuardian.UserFromAuth.get_or_insert/3
        (phoenix_guardian) web/controllers/auth_controller.ex:24: PhoenixGuardian.AuthController.callback/4
        (phoenix_guardian) web/controllers/auth_controller.ex:1: PhoenixGuardian.AuthController.action/2
        (phoenix_guardian) web/controllers/auth_controller.ex:1: PhoenixGuardian.AuthController.phoenix_controller_pipeline/2
        (phoenix_guardian) lib/phoenix/router.ex:261: PhoenixGuardian.Router.dispatch/2
        (phoenix_guardian) web/router.ex:1: PhoenixGuardian.Router.do_call/2
        (phoenix_guardian) lib/phoenix_guardian/endpoint.ex:1: PhoenixGuardian.Endpoint.phoenix_pipeline/1
        (phoenix_guardian) lib/plug/debugger.ex:122: PhoenixGuardian.Endpoint."call (overridable 3)"/2
        (phoenix_guardian) lib/phoenix/endpoint/render_errors.ex:34: PhoenixGuardian.Endpoint.call/2
        (plug) lib/plug/adapters/cowboy/handler.ex:15: Plug.Adapters.Cowboy.Handler.upgrade/4
        (cowboy) src/cowboy_protocol.erl:442: :cowboy_protocol.execute/4

CSRF authentication with Plug 0.13

The CSRF authentication does not seem to work with plug 0.13. I don't have time right now to investigate more, but most probably it's related to this commit elixir-plug/plug@06579e7. I decided to open the issue in this repository because I'm not sure if it's a bug in guardian or it's related to the implementation in the app.

Btw guardian looks awesome so far, thank you @hassox!

Refresh Token

When should I refresh a token? How can I tell if a token has expired? I want to avoid asking the user to click the "Authorize" button more than once. I plan to add a plug to a pipeline for all the APIs. Or should I call UserFromAuth.find_or_create_user(auth, current_user, Repo) every time in all API controller actions?

#_=_ is appended in URL after Facebook OAuth

https://developers.facebook.com/blog/post/552/

Temporary solution is adding the following but you can still see the #_=_ while the page is loading.

window.onload = function(e){
  if (window.location.hash == '#_=_') {
    window.location.hash = ''; // for older browsers, leaves a # behind
    history.pushState('', document.title, window.location.pathname); // nice and clean
    e.preventDefault(); // no page reload
  }
}

Compilation error on file web/views/admin/user_view.ex: badarg

Beginning phoenix user here. Using newest Elixir and Phoenix, I tried mix deps.update --all butstill got the error:

== Compilation error on file web/views/admin/user_view.ex ==
** (CompileError) web/views/admin/user_view.ex: internal error in lint_module;
crash reason: badarg

  in function  erl_anno:anno_info/1
     called as erl_anno:anno_info(-1)
  in call from erl_lint:loc/2 (erl_lint.erl, line 640)
  in call from erl_lint:icrt_export/4 (erl_lint.erl, line 3094)
  in call from erl_lint:icrt_clauses/4 (erl_lint.erl, line 3076)
  in call from erl_lint:expr/3 (erl_lint.erl, line 2168)
  in call from erl_lint:'-expr_list/3-fun-0-'/3 (erl_lint.erl, line 2330)
  in call from lists:foldl/3 (lists.erl, line 1263)
  in call from erl_lint:exprs/3 (erl_lint.erl, line 2100)
  in call from erl_lint:exprs/3 (erl_lint.erl, line 2101)
  in call from erl_lint:'-expr_list/3-fun-0-'/3 (erl_lint.erl, line 2330)
  in call from lists:foldl/3 (lists.erl, line 1263)
  in call from erl_lint:expr/3 (erl_lint.erl, line 2306)
  in call from erl_lint:exprs/3 (erl_lint.erl, line 2100)
  in call from erl_lint:'-expr_list/3-fun-0-'/3 (erl_lint.erl, line 2330)
  in call from lists:foldl/3 (lists.erl, line 1263)
  in call from erl_lint:expr/3 (erl_lint.erl, line 2306)
  in call from erl_lint:exprs/3 (erl_lint.erl, line 2100)
  in call from erl_lint:'-expr_list/3-fun-0-'/3 (erl_lint.erl, line 2330)
  in call from lists:foldl/3 (lists.erl, line 1263)
  in call from erl_lint:expr/3 (erl_lint.erl, line 2306)
    (stdlib) lists.erl:1338: :lists.foreach/2
    (stdlib) erl_eval.erl:670: :erl_eval.do_apply/6
    (elixir) lib/kernel/parallel_compiler.ex:117: anonymous fn/4 in Kernel.ParallelCompiler.spawn_compilers/1

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.