Coder Social home page Coder Social logo

phx_gen_auth's Introduction

Phx.Gen.Auth

An authentication system generator for Phoenix 1.5 applications.

Note: This project is no longer maintained as mix phx.gen.auth has been merged into Phoenix v1.6.

Overview

The purpose of phx.gen.auth is to generate a pre-built authentication system into a Phoenix 1.5 application that follows both security and elixir best practices. By generating code into the user's application instead of using a library, the user has complete freedom to modify the authentication system so it works best with their app. The following links have more information regarding the motivation and design of the code this generates.

Usage

Generating a Phoenix 1.5 app

phx.gen.auth must be installed into a Phoenix 1.5 application.

Once the installer is installed, a new project can be generated by running

$ mix phx.new my_app

Please note, the --no-ecto and --no-html options are not supported.

Installation

After running mix phx.new, cd into your application's directory (ex. my_app).

Basic installation

  1. Add phx_gen_auth to your list of dependencies in mix.exs

    def deps do
      [
        {:phx_gen_auth, "~> 0.7", only: [:dev], runtime: false},
        ...
      ]
    end
  2. Install and compile the dependencies

    $ mix do deps.get, deps.compile
    

Umbrella installation

  1. cd into your project's web app directory (ex. apps/my_app_web)

    $ cd apps/my_app_web
    
  2. Add phx_gen_auth to your list of dependencies in mix.exs

    def deps do
      [
        {:phx_gen_auth, "~> 0.7", only: [:dev], runtime: false},
        ...
      ]
    end
  3. Install and compile the dependencies

    $ mix do deps.get, deps.compile
    

Running the generator

From the root of your phoenix app (or apps/my_app_web in an umbrella app), you can install the authentication system with the following command

$ mix phx.gen.auth Accounts User users

This creates the templates,views, and controllers on the web namespace, and a new MyApp.Accounts context, in the application namespace.

Verify the database connection details for the development and test environments in config/ so the migrator and tests can run properly. Then run the following to create the database

$ mix ecto.create

Next, let's install the dependencies and migrate the database

$ mix deps.get
$ mix ecto.migrate

Let's run the tests and make sure our new authentication system works as expected.

$ mix test

Finally, let's start our phoenix server and try it out.

$ mix phx.server

Note on apps upgraded from Phoenix 1.4

If you've upgraded your app from Phoenix 1.4, you'll need to make the following update to test/support/conn_case.ex to get mix test to pass:

using do
  quote do
    # Import conveniences for testing with connections
    import Plug.Conn
    import Phoenix.ConnTest
+   import DemoWeb.ConnCase
    alias DemoWeb.Router.Helpers, as: Routes

    # The default endpoint for testing
    @endpoint DemoWeb.Endpoint
  end
end

Changing id types

By default, this generator uses the same type of id fields as the rest of the application. To override this configuration, the generator accepts --binary-id and --no-binary-id flags.

$ mix phx.gen.auth Accounts User users --binary-id

More information about these options are available in the documentation.

Learning more

To learn more about phx.gen.auth, run the following command.

$ mix help phx.gen.auth

You can also look up the mix task in hexdocs.

Upgrading

Since mix phx.gen.auth generates its code directly into your application, upgrading the version of this library will not upgrade your application's current authentication logic.

To see the changes that have been made to the generator output since the version that was used in your application, visit the CHANGELOG and click the [Diff] links for each version. These diffs will show you the changes to make to your application so it can be up to date with the current generator output.

License

Copyright 2020 Dashbit, Aaron Renner

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

phx_gen_auth's People

Contributors

a-maze-d avatar aaronrenner avatar adamtew avatar bhtabor avatar chvanikoff avatar cvkmohan avatar dalarenal avatar goofansu avatar holandes22 avatar ian-gl avatar josevalim avatar jrdnull avatar leggebroten avatar liamwhite avatar lostkobrakai avatar marcdel avatar matthewlehner avatar mikl avatar mveytsman avatar paulstatezny avatar ream88 avatar sgerrand avatar skovsgaard avatar zorn 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

phx_gen_auth's Issues

Mix.Tasks.Phx.Gen.Context.build/2 is undefined

Elixir 1.10.4
Erlang/OTP 23
Phoenix 1.5.4

Installed Phoenix, credo, and dialyxir

Added {:phx_gen_auth, "~> 0.5", only: [:dev], runtime: false} to mix.exs... then ran in to the following exception.

$ mix phx.gen.auth Accounts User users
** (UndefinedFunctionError) function Mix.Tasks.Phx.Gen.Context.build/2 is undefined (module Mix.Tasks.Phx.Gen.Context is not available)
    Mix.Tasks.Phx.Gen.Context.build(["Accounts", "User", "users"], Mix.Tasks.Phx.Gen.Auth)
    lib/mix/tasks/phx.gen.auth.ex:98: Mix.Tasks.Phx.Gen.Auth.run/1
    (mix 1.10.4) lib/mix/task.ex:330: Mix.Task.run_task/3
    (mix 1.10.4) lib/mix/cli.ex:82: Mix.CLI.run_task/2

mix phx.gen.context Accounts User users name:string works fine... so I'm not sure what's going on here...

remember me cookie based on app name

The default name for the session cookie in phoenix is based on the name of the app and it makes it easy for multiple local projects to all run at localhost:4000 without cookies getting mixed up. I'm wondering if it would make sense to do the same for the remember me cookie?

API Authentication

Is API authentication within the scope of this project or is it a non-goal?

Improve user experience on confirmation messages

Those are changes I have applied based on production feedback of the confirmation system. Also, because sometimes e-mail clients may accidentally preview the confirmation link, showing an error message for the cases the user is already logged in can be confusing, so we skip that too.

diff --git a/apps/bytepack_web/lib/bytepack_web/controllers/user_confirmation_controller.ex b/apps/bytepack_web/lib/bytepack_web/controllers/user_confirmation_controller.ex
index 6a89883..ea79aa2 100644
--- a/apps/bytepack_web/lib/bytepack_web/controllers/user_confirmation_controller.ex
+++ b/apps/bytepack_web/lib/bytepack_web/controllers/user_confirmation_controller.ex
@@ -39,9 +39,19 @@ defmodule BytepackWeb.UserConfirmationController do
         |> redirect(to: Routes.user_session_path(conn, :new))
 
       :error ->
-        conn
-        |> put_flash(:error, "Confirmation token is invalid or it has expired.")
-        |> redirect(to: Routes.user_session_path(conn, :new))
+        # If there is a current user and the account was already confirmed,
+        # then odds are that the confirmation link was already visited, either
+        # by some automation or by the user themselves, so we redirect without
+        # a warning message.
+        case conn.assigns do
+          %{current_user: %{confirmed_at: confirmed_at}} when not is_nil(confirmed_at) ->
+            redirect(conn, to: Routes.user_session_path(conn, :index))
+
+          %{} ->
+            conn
+            |> put_flash(:error, "Account confirmation link is invalid or it has expired.")
+            |> redirect(to: Routes.user_session_path(conn, :new))
+        end
     end
   end
 end
diff --git a/apps/bytepack_web/test/bytepack_web/controllers/user_confirmation_controller_test.exs b/apps/bytepack_web/test/bytepack_web/controllers/user_confirmation_controller_test.exs
index 5a92925..1238712 100644
--- a/apps/bytepack_web/test/bytepack_web/controllers/user_confirmation_controller_test.exs
+++ b/apps/bytepack_web/test/bytepack_web/controllers/user_confirmation_controller_test.exs
@@ -69,15 +69,21 @@ defmodule BytepackWeb.UserConfirmationControllerTest do
       refute get_session(conn, :user_token)
       assert Repo.all(Accounts.UserToken) == []
 
+      # When not logged in
       conn = get(conn, Routes.user_confirmation_path(conn, :confirm, token))
       assert redirected_to(conn) == Routes.user_session_path(conn, :new)
-      assert get_flash(conn, :error) =~ "Confirmation token is invalid or it has expired"
+      assert get_flash(conn, :error) =~ "Account confirmation link is invalid or it has expired"
+
+      # When logged in
+      conn = build_conn() |> login_user(user) |> get(Routes.user_confirmation_path(conn, :confirm, token))
+      assert redirected_to(conn) == Routes.dashboard_index_path(conn, :index)
+      refute get_flash(conn, :error)
     end
 
     test "does not confirm email with invalid token", %{conn: conn, user: user} do
       conn = get(conn, Routes.user_confirmation_path(conn, :confirm, "oops"))
       assert redirected_to(conn) == Routes.user_session_path(conn, :new)
-      assert get_flash(conn, :error) =~ "Confirmation token is invalid or it has expired"
+      assert get_flash(conn, :error) =~ "Account confirmation link is invalid or it has expired"
       refute Accounts.get_user!(user.id).confirmed_at
     end
   end

I am submitting them because they are probably worth incorporating here too!

Update terminology usage (e-mail vs email).

When applying this to my own projects I saw two terminology discrepancies.

I feel like e-mail should be email.

https://twitter.com/APStylebook/status/48798366980780033

I also lean toward sign in / sign out instead of log in / log out.

One of the main reasons I do this is to avoid the confusion between a login name and the verb log in. In my observations most system have difficulty with consistency here. Some other observations / usability studies of the topic are collected here:

https://blog.benjamincharity.com/log-in-vs-login-vs-sign-in/

I know these can be well contested topics but if there was agreement I'd be happy to work on a PR.

Even if the project sticks with the current stuff, I think it might be a helpful addition to make a patterns guide explaining why certain decisions were made, for usability reasons or security reasons (like why 12 password characters instead of 8).

Let me know. Love the project!

MatchError at POST /users/register - no match of right hand side value: :ok

Hello, I just setup UserNotifier.ex to work with Sendgrid and now I'm getting this error.

def create(conn, %{"user" => user_params}) do
    case Accounts.register_user(user_params) do
      {:ok, user} ->
        {:ok, _} =
          Accounts.deliver_user_confirmation_instructions(
            user,
            &Routes.user_confirmation_url(conn, :confirm, &1)
          )

        conn
        |> put_flash(:info, "User created successfully.")
        |> UserAuth.log_in_user(user)

      {:error, %Ecto.Changeset{} = changeset} ->
        render(conn, "new.html", changeset: changeset)
    end
end

The error is with the 4th line of the above, {:ok, _} =

User Notifier has been working fine for Password Reset emails.

IO.inspect Email.build()
    |> Email.put_from("[email protected]")
    |> Email.add_to(to)
    |> Email.put_subject(subject)
    |> Email.put_text(body)
    |> Mail.send()

The IO.inspect returns :ok

Losing conn.assigns values

Before I explain my issue I will mention that I'm using {:phoenix, "~> 1.5.2"} and phoenix_live_view.

I'm seeing some strange behavior while trying to integrate this into an existing app. The 'creates account and logs the user in' test is failing for the second request in the user registrations controller. The second request in the 'logs the user in' test is also failing in the user sessions controller.

I noticed that the require_authenticated_user function is checking conn.assigns[:current_user], but login_user doesn't assign it. Where does that assign happen? In the meantime I've added the assign in to login_user.

Here's what I'm seeing for the second request in the user registration test after adding that assign:

IO.inspect(conn.assigns)
conn = get(conn, "/users/settings")
IO.inspect(conn.assigns)

The first inspect has current_user in the map, but the second one doesn't. I'm having trouble tracking down where it could've been removed.

Newlines generated in session token

token = :crypto.strong_rand_bytes(@rand_size)

I've pinpointed the above as causing the following error to emerge when attempting to authorise with a cookie:

(Plug.Conn.InvalidHeaderError) value for header "set-cookie" contains control feed (\r) or newline (\n): <<109, 101, 109, 98, 101, 114, 95, 97, 117, 116, 104, 61, 90, 19, 252, 11, 129, 235, 219, 107, 22, 191, 10, 222, 41, 109, 159, 20, 14, 30, 141, 20, 224, 121, 223, 163, 205, 213, 9, 148, 19, 147, 209, 164, 59, 32, 112, 97, 116, 104, ...>>

Note: 10 is a newline byte.

For my own project, I've amended this to base64 before passing the value along. I'd want to open a discussion for which stage newlines should be prevented from entering the cookie header, or if a fixed dictionary to replace strong_rand_bytes would be preferred.

My updated line reads as

token =
      :crypto.strong_rand_bytes(@rand_size)
      |> Base.encode64(padding: false)

Support --no-html

Is there any plan to add support of --no-html to provide a token based authentication generator?

Invite-Only Applications (No public registration)

Thanks so much for this library! Really great stuff. โค๏ธ

I'm wondering, do you have any specific recommendations for modifying the generated code for an application that doesn't have public registration? That is, an application where you set up "administrators" to start off, and then from that point on the way that users join is by invitation. This would be typical of an application used by a business, with only its employees as users.

Thanks!

User or Credential? Rewrite the Authentication part of the Phoenix Guides

The Phoenix Guides use a slightly different but, when put against what phx_gen_auth produces, utterly confusing naming regarding authentication. In the Phoenix Guides "Credential" is used instead of "User", and they use it as an example how to created a one_to_one relationship:
mix phx.gen.context Accounts Credential credentials email:string:unique user_id:references:users

One or the other will have to yield If you want to merge to Phoenix. Either way the Authentication part of the Phoenix Guides should be rewritten.

Slow recompilation in development

Importing the UserAuth module into the router in development causes very minor changes to the application to end up recompiling the router. This is very slow.

Performance after touch lib/philomena/users.ex:

root@28c98c26a5aa:/srv/philomena# time mix compile
Compiling 2 files (.ex)
Generated philomena app

real	0m6.173s
user	0m6.524s
sys	0m0.888s
root@28c98c26a5aa:/srv/philomena# time mix compile
Compiling 2 files (.ex)

real	0m6.024s
user	0m6.408s
sys	0m0.706s

A small set of changes causes development recompilation performance to return to normal:

--- a/lib/philomena_web/router.ex
+++ b/lib/philomena_web/router.ex
@@ -1,15 +1,13 @@
 defmodule PhilomenaWeb.Router do
   use PhilomenaWeb, :router
 
-  import PhilomenaWeb.UserAuth
-
   pipeline :browser do
     plug :accepts, ["html"]
     plug :fetch_session
     plug :fetch_flash
     plug :protect_from_forgery
     plug :put_secure_browser_headers
-    plug :fetch_current_user
+    plug PhilomenaWeb.FetchUserPlug
     plug PhilomenaWeb.ContentSecurityPolicyPlug
     plug PhilomenaWeb.CurrentFilterPlug
     plug PhilomenaWeb.ImageFilterPlug
@@ -53,6 +51,14 @@ defmodule PhilomenaWeb.Router do
     plug PhilomenaWeb.FilterBannedUsersPlug
   end
 
+  pipeline :require_authenticated_user do
+    plug PhilomenaWeb.RequireUserPlug
+  end
+
+  pipeline :redirect_if_user_is_authenticated do
+    plug PhilomenaWeb.RequireNoUserPlug
+  end
+
   scope "/", PhilomenaWeb do
     pipe_through [:browser, :redirect_if_user_is_authenticated]
 

The added plugs simply call the relevant function in the UserAuth module.

Performance after touch lib/philomena/users.ex:

root@28c98c26a5aa:/srv/philomena# time mix compile
Compiling 1 file (.ex)
Generated philomena app

real	0m1.584s
user	0m1.687s
sys	0m0.599s
root@28c98c26a5aa:/srv/philomena# time mix compile
Compiling 1 file (.ex)

real	0m1.560s
user	0m1.588s
sys	0m0.595s

I've already confirmed that the problem only occurs because the router is recompiled, and recompiling the router alone is quite slow:

root@28c98c26a5aa:/srv/philomena# time mix compile
Compiling 1 file (.ex)

real	0m5.698s
user	0m5.929s
sys	0m0.802s

Can "maybe_hash_password" run the "else" condition?

I've used this in my project, and I noticed that the coverage of tests is not 100%. Checking the code, the part without test is this one, in the else condition:
https://github.com/aaronrenner/phx_gen_auth/blob/v0.2.0/priv/templates/phx.gen.auth/schema.ex#L52

 defp maybe_hash_password(changeset) do
    if password = get_change(changeset, :password) do
      changeset
      |> put_change(:hashed_password, <%= inspect hashing_library.module %>.hash_pwd_salt(password))
      |> delete_change(:password)
    else
      changeset
    end
  end

I've been trying to identify in which cases we can reach that else. It only happens in the registration and password changesets. For both, you need to enter the password, so the if condition will be true (as this will run only if is valid, durint the Repo.insert.

Could this be simplified without the if else, or I'm I missing something? Or in the case there is a way to reach it, could that be added to the tests?

Make the `auth_module` less coupled to controllers

The auth_module is designed to work for controllers only. I was thinking that it is possible to divide it into two modules:

  • One module manages the :user_token in the session and the remember-me cookie (this is then directly usable for SPA/Absinthe apps for example, controllers, etc.);

  • Another module on top of it manages the redirections and the :user_return_to in the session (the controller-specific code).

  • Code can further be extracted, such as fetch_current_user/2 into some Plug directory.

One can say, just modify the code according to the type of app you have. But some have multiple APIs (use of controllers, use of Absinthe, etc.). This then doesn't just require to modify a few lines of code, but to change the design for less coupling.

Just an idea.

Rationale for not hashing all stored tokens

The generator creates two functions along the lines of the following (copying from my app's source code):

  @doc """
  Generates a token that will be stored in a signed place,
  such as session or cookie. As they are signed, those
  tokens do not need to be hashed.
  """
  def build_session_token(user) do
    token = :crypto.strong_rand_bytes(@rand_size)
    {token, %Philomena.Users.UserToken{token: token, context: "session", user_id: user.id}}
  end

  @doc """
  Builds a token with a hashed counter part.

  The non-hashed token is sent to the user email while the
  hashed part is stored in the database, to avoid reconstruction.
  The token is valid for a week as long as users don't change
  their email.
  """
  def build_email_token(user, context) do
    build_hashed_token(user, context, user.email)
  end

  defp build_hashed_token(user, context, sent_to) do
    token = :crypto.strong_rand_bytes(@rand_size)
    hashed_token = :crypto.hash(@hash_algorithm, token)

    {Base.url_encode64(token, padding: false),
     %Philomena.Users.UserToken{
       token: hashed_token,
       context: context,
       sent_to: sent_to,
       user_id: user.id
     }}
  end

I am curious as to why the decision was made not to simply hash the token stored in the database in all cases. There does not appear to be any concrete downside to doing so, as the tokens never need to be reconstructed by the application.

"Confirm email" instead of "confirm user"

Reading the code, we "confirm a user" through email confirmation.
I'm not convinced about the naming; I find it too ambiguous.

Why not talk about "confirm an email". It is more explicit. What does "confirming a user" really means. Furthermore, we might want to to confirm a phone number by SMS some day as well.

Just semantics. (Just in case you agree, let me know if you need help to refactor, it's a lot of changes).

For example confirm_user(token) should be confirm_email(token), confirm_changeset to confirm_email_changeset, etc.

Use `__MODULE__` when possible?

I see in the code that we prefer to generate the full module name when that name references the module we're in. Any reason to not prefer the usage of __MODULE__?

A good example is the xxxToken module and the numerous use of:

<%= inspect schema.module %>Token

The advantage of __MODULE__ is that the module is easier to rename. I don't see disadvantages in its use.

Interested in Typespecs?

Hi there ๐Ÿ‘‹

Thank you for your work on this project. This is a fantastic resource for auth implementation as well as demonstrating Elixir and Phoenix in general.

Are you interested in adding type specifications to the functions created by this project? I realize not everyone uses specs, so totally understand if you'd prefer not to. ๐Ÿ™‚

If so, I'm happy to contribute towards this effort with your guidance.

Needs to work properly with custom table names

Running the following command needs to generate the proper table names:

mix phx.gen.auth Ticketing User user --table ticketing_users

This should generate the following tables

  • ticketing_users
  • ticketing_user_tokens

Currently it generates:

  • ticketing_users
  • user_tokens

Generator injection issue (plug :fetch_current_user)

I just ran the generator in an existing project. Before, my browser pipeline in the router looked like this:

pipeline :browser do
    ...
    plug :put_secure_browser_headers, %{"content-security-policy" => @csp}
end

The generator put the options for put_secure_browser_headers behind fetch_current_user:

pipeline :browser do
    ...
    plug :put_secure_browser_headers
    plug :fetch_current_user, %{"content-security-policy" => @csp}
 end

Delete User function?

I'm looking through the Accounts context and I don't see a function to delete the user.

I noticed the migration for the user_tokens table has cascade deletion on it, which is good, just want to make sure I'm not messing anything up by manually deleting users or writing my own simple function for this.

Simplify layout injection

Suggestion: this code can probably just inject it after the opening <body> tag. It will be wrong but people will have to move it anyway. WDYT?

Clean expired tokens?

Hello, user session tokens are being piled up in the "users_tokens" table. I don't see any code that cleans up expired tokens. There is also no :expired_at field.

An app may have a lot of new sessions every day, this table can lead to very high number of records in a relatively short time.

I do not see reasons why not provide a mechanism to clean this up, as every developer will lead to plus or less the same code doing that.

Use Router helpers in test cases assertions

Hi,
I'd suggest changing generated test cases to assert against Routes.*_path rather than plain string - this way it becomes much easier to change routes like /users/login -> /auth/login.
i.e.:

- assert redirected_to(conn) == "/users/login"
+ assert redirected_to(conn) == Routes.user_session_path(conn, :new)

"log in" by default?

It's a bit pedantic, but I've had to make this change globally to sites before. Perhaps a generator should use "log in" for verb and "login" for noun?

Bug in test.exs injection with windows newlines

Hi, cool project!

I ran this on windows and got Mix.raise(~s[Could not find "use Mix.Config" or "import Config" in #{inspect(file)}]) even though my test.exs has the former. I suppose because it is "use Mix.Config\r\n" in my case which will not match "use Mix.Config\n".

Needs support for binary ids

This generator needs to correctly support the following

  • A phoenix app generated with --binary-id
  • The generator being run with --binary-id

Can we start a CONTRIBUTING.md document?

I've started work on a PR for the previously discussed "email" change. I am currently not able to run the test to verify I am not breaking anything. I feel like I missing the whole picture of what it takes to run the tests for this repo locally.

First had to get over an error about :phx_new compilations by installing a local version of :phx_new per the linked instructions below.

could not compile dependency :phx_new, "mix compile" failed. You can recompile this dependency with "mix deps.compile phx_new", update it with "mix deps.update phx_new" or clean it with "mix deps.clean phx_new"

https://github.com/phoenixframework/phoenix/blob/master/installer/README.md

I am not running into error running the tests like:

error: pathspec '.' did not match any file(s) known to git

And

1) test does not allow generator to be run at the umbrella root (Phx.Gen.Auth.IntegrationTests.DefaultUmbrellaTest)
     test/mix/tasks/phx_gen_auth/integration_tests/default_umbrella_test.exs:33
     ** (MatchError) no match of right hand side value: {"", 1}
     stacktrace:
       (phx_gen_auth 0.4.0) test/support/integration_test_helpers.ex:150: Phx.Gen.Auth.TestSupport.IntegrationTestHelpers.revert_to_clean_phoenix_app/1
       (phx_gen_auth 0.4.0) test/support/integration_test_helpers.ex:35: Phx.Gen.Auth.TestSupport.IntegrationTestHelpers.setup_test_umbrella_app/2
       test/mix/tasks/phx_gen_auth/integration_tests/default_umbrella_test.exs:12: Phx.Gen.Auth.IntegrationTests.DefaultUmbrellaTest.__ex_unit_setup_0/1
       test/mix/tasks/phx_gen_auth/integration_tests/default_umbrella_test.exs:1: Phx.Gen.Auth.IntegrationTests.DefaultUmbrellaTest.__ex_unit__/2

I suspect I may need to running this in a Docker container but I'm still very new to that.

ANYWAYS. If we could work on a document on how to stand up this project for local editing that would be very helpful. Thanks!

user is pluralized everywhere

In many places user is pluralized to users such as:

def login_users(conn, users, params \\ %{}) do token = Accounts.generate_users_session_token(users) users_return_to = get_session(conn, :users_return_to)

There doesn't seem to be a singular instance anywhere, is this intended to be the possessive form as in user's?

I wouldn't normally mind, but I'm trying to attach this to a database field called users and I believe Phoenix is looking for user_id from the users table in some parts to do a one to many association in Ecto and changing it to singular gradually is breaking some things, so I'm wondering if it's the generator that could be modified to make the instances where it's just talking about one user (like logging in) singular?

Question: UUID instead of integer for user id

Hi @aaronrenner thanks for the awesome lib. Auth is so important and I am happy that we now have an open source, community vetted solution that works out of the box (so nicely) with our beloved stack. Another feather on the Phoenix stack's hat.

I was playing around with the generators and realized we are using integers for the id column in user instead of using uuids. I was wondering if adding support to uuids is in your roadmap. If so, could you please give some details about what you'd be looking for? Would a --uuid flag be an option?

My use case is really just having the generator use uuids instead of common integer ids and have the existing code play nicely with that change (there are a few dependencies - tests, db relationships, a couple of mentions in the user_token context).

Thanks again for the awesome work, have a great Sunday.

Generator option for multitenancy via query prefixes

Hi, this is just something to consider. I'm not sure if this is something you think would belong in the generator.

I am currently looking at creating a multi-tenant system using the query prefix as the mechanism for separating the Tenants (https://hexdocs.pm/ecto/multi-tenancy-with-query-prefixes.html and https://github.com/ateliware/triplex). I'm interested in using this generator as the basis for the authentication system, but I found that I would have to make pretty extensive changes to support the "prefix" queries - all the existing queries, tests, etc would need to be modified I think.

Given that this is a generator, I was thinking that it might be possible to have this as a configurable option - i.e. the queries and such would be generated with the prefixes.

For my project I'll likely be going through the changes manually, but this might be useful for future projects / others.

Thanks!

Provide support for Ueberauth

Sorry for the annoying feature request, but I do think that a lot of people would use phx.gen.auth with the hope of providing support for both standard username/password auth flows and external auth providers (Google, GitHub, Apple, etc.). At the moment, ueberauth appears to be a de facto standard, and adding an --ueberauth option to phx.gen.auth would be great. Would be happy to help but wanted to see what you think first.

Typespec issue with user_token schema

This is probably an error on my part but I'm not sure what it could be.

I tried adding typespecs to some of the functions created by the generator and I'm getting a warning from dialyzer that I just can't seem to figure out why it's occurring. I generated the files by using the example in the readme i.e. mix phx.gen.auth Accounts User users and then added the following:

user.ex

  @type t() :: %__MODULE__{
    __meta__: Ecto.Schema.Metadata.t(),
    id: integer() | nil,
    email: String.t() | nil,
    password: String.t() | nil,
    hashed_password: String.t() | nil,
    confirmed_at: NaiveDateTime.t(),

    inserted_at: NaiveDateTime.t(),
    updated_at: NaiveDateTime.t()
  }

user_token.ex

  @type t() :: %__MODULE__{
    __meta__: Ecto.Schema.Metadata.t(),
    id: integer() | nil,
    token: binary() | nil,
    context: String.t() | nil,
    sent_to: String.t() | nil,
    user_id: integer() | nil,
    user: User.t() | Ecto.Association.NotLoaded.t() | nil,
    inserted_at: NaiveDateTime.t()
  }

  # ...

  # warning on this typespec
  @spec build_session_token(User.t()) :: {binary(), t()}
  def build_session_token(user) do
    token = :crypto.strong_rand_bytes(@rand_size)
    {token, %TempAuthExperiment.Accounts.UserToken{token: token, context: "session", user_id: user.id}}
  end

The warning I get is

The @spec for the function does not match the success typing of the function.

Success typing:
@spec build_session_token(atom() | %{:id => _, _ => _}) ::
  {binary(),
   %TempAuthExperiment.Accounts.UserToken{
     :__meta__ => %Ecto.Schema.Metadata{
       :context => nil,
       :prefix => nil,
       :schema => TempAuthExperiment.Accounts.UserToken,
       :source => <<_::96>>,
       :state => :built
     },
     :context => <<_::56>>,
     :id => nil,
     :inserted_at => nil,
     :sent_to => nil,
     :token => binary(),
     :user => %Ecto.Association.NotLoaded{
       :__cardinality__ => :one,
       :__field__ => :user,
       :__owner__ => TempAuthExperiment.Accounts.UserToken
     },
     :user_id => _
  }}

I feel like I barely added any code to get this to mess up. I get it has to do with the user type but I don't see why it's resolving to atom() | %{:id => _, _ => _}

Is it ok that `get_user!/1` is generated but only used by tests?

One of the notes I took while learning this generator was the curious generation of get_user!/1 that is not used in the actual code but only the tests. This leads me to ask two questions:

  1. Should we be generating code in the public Accounts module if it is not currently or intended to be used by other controllers?
  2. If we generate this in the anticipation that a user will extend their project to say provide a users/:id/profile path, should they be using a bang ! function in said controller to get that user? When doing resource lookup on a path like this should we just assume a Phoenix project will catch a Ecto.NoResultsError error and show a 404 page? I am still kind of new to Phoenix and community norms but my initial reaction is that a custom module like Accounts should not throw Ecto errors.

Posting here to start a conversation. Thanks.

Wrong arity listed in descriptions for context tests

get_user_by_email_and_password/1 (should be /2)

def get_<%= schema.singular %>_by_email_and_password(email, password)

describe "get_<%= schema.singular %>_by_email_and_password/1" do


get_user_by_reset_password_token/2 (should be /1)

def get_<%= schema.singular %>_by_reset_password_token(token) do

describe "get_<%= schema.singular %>_by_reset_password_token/2" do


reset_user_password/3 (should be /2)

def reset_<%= schema.singular %>_password(<%= schema.singular %>, attrs) do

describe "reset_<%= schema.singular %>_password/3" do

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.