Comments (5)
I was traveling these last weeks, but finally I'm able to answer this thoroughly! Hopefully this won't be too late of a response 😄
You've probably figured out a lot of this yourself by digging in the source code, but just to make it clear for everyone, this is where the callback is used: https://github.com/danschultzer/pow/blob/master/lib/pow/phoenix/controllers/controller.ex#L74-L92
@doc """
Handles the controller action call.
If a `:controller_callbacks` module has been set in the configuration,
then `before_process` and `before_respond` will be called on this module
on all actions.
"""
@spec action(atom(), Conn.t(), map()) :: Conn.t()
def action(controller, %{private: private} = conn, params) do
action = private.phoenix_action
config = Plug.fetch_config(conn)
callbacks = Config.get(config, :controller_callbacks)
conn
|> maybe_callback(callbacks, :before_process, controller, action, config)
|> process_action(controller, action, params)
|> maybe_callback(callbacks, :before_respond, controller, action, config)
|> respond_action(controller, action)
end
I've been thinking if there's a good way of making Pow nearly plug n' play for JSON response. You could to some degree make it work with controller callbacks as you suggest, but there are redirects that happens in plug methods too so I think the whole flow in Pow has to be thought through. I would love any suggestions for this.
But let's go ahead with how to achieve JSON responses with Pow in its current state. In an API, you wouldn't want to deal with redirects, so we'll set up a custom controller that uses the underlying plug methods directly (the plug methods can be seen in the process methods in the SessionController):
defmodule MyAppWeb.SessionController do
use MyAppWeb, :controller
alias Pow.Plug
def create(conn, %{"user" => user_params}) do
conn
|> Plug.authenticate_user(user_params)
|> case do
{:ok, conn} -> # authenticated, conn has been set with session
{:error, _conn} -> # error occurred, conn remains unchanged
end
end
def delete(conn, params) do
conn
|> Plug.clear_authenticated_user()
|> case do
{:ok, conn} -> # session in conn has been removed
end
end
end
And then I would just replicate the logic in extensions (in this case with email confirmation):
alias PowEmailConfirmation.Phoenix.ControllerCallbacks, as: PowEmailConfirmationControllerCallbacks
def create(conn, %{"user" => user_params}) do
conn
|> Plug.authenticate_user(user_params)
|> verify_confirmed()
end
defp verify_confirmed({:error, conn}), do: # error occurred, conn remains unchanged
defp verify_confirmed({:ok, conn}) do
user = Plug.current_user(conn)
if confirmed?(user) do
# user is confirmed and conn can be used
else
PowEmailConfirmationControllerCallbacks.send_confirmation_email(user, conn)
{:ok, conn} = Plug.clear_authenticated_user(conn)
# conn session has been reset and error can be returned
end
end
defp confirmed?(%{email_confirmed_at: nil, email_confirmation_token: token}) when not is_nil(token), do: false
defp confirmed?(_user), do: true
As the above highlights, I think there's room for making it easier working with extensions. All suggestions are welcome! 😄
from pow.
A quick thought! If redirects isn't a problem, then you could actually just switch out all responses in a custom controller callbacks using before_response
. The only thing you've to do is to return {:halt, conn}
to prevent the response method from being called in the controller.
I think there's just one place where emails are sent out as part of the response, but it's pretty easily handled. All the extension controller callbacks only redirects, e.g. email confirmation just redirects to pow_session_path(conn, :new)
with and :error
flash
Errors won't be clear this way though. I think the best way is to refactor the flow in Pow to make it trivial for developers to handle pow errors in the connection.
from pow.
This is great, thanks! Perfect timing actually. I had gone down the path of using a custom controller previously but I'm going to play around with implementing it using before_response
too and see where I end up. Curious to see how clear/unclear the errors will be because simply switching out the responses sounds nice and clean in and of itself.
I'll keep an eye out for ways to improve the flow to better handle errors and potentially avoid redirecting too 😄
from pow.
Cool! Custom controllers are probably the better approach, but here's an example of how the controller callbacks could look like:
defmodule MyAppWeb.PowControllerCallbacks do
alias Pow.Extension.Phoenix.ControllerCallbacks
def before_process(controller, action, results, config),
do: ControllerCallbacks.before_process(controller, action, results, config)
def before_response(controller, action, results, config) do
controller
|> ControllerCallbacks.before_response(action, results, config)
|> handle_response(controller, action)
end
# Custom response
defp handle_response({:ok, user, conn}, Pow.Phoenix.RegistrationController, :create) do
conn = Phoenix.Controller.json(conn, %{id: user.id})
{:halt, conn}
end
# All other responses should just be handled by default in Pow
defp handle_response(result, _controller, _action), do: result
end
As you can see, it'll call extension controller callbacks before the custom response.
Remember to add it to your config:
config :my_app, :pow,
controller_callbacks: MyAppWeb.PowControllerCallbacks
from pow.
Here's some more information: #57 (comment)
from pow.
Related Issues (20)
- Replacing Phoenix.Token with JWT-based for signing and verifiying of tokens HOT 1
- Unable to logout user in Pow API HOT 2
- Feature Request: Apple Passkeys HOT 1
- Pow.Plug.authenticate_user/1 performance HOT 2
- Reset password - test fails issue HOT 1
- Upgrading to Phoenix 1.7 HOT 5
- Create account without email/password using some unique ID?
- Phoenix 1.7 compilers warning HOT 1
- Maintain case when storing emails HOT 2
- Permissions issues with mnesia backend for Pow HOT 16
- Improve mix tasks
- Crash @missing_field? in Schema.ex HOT 1
- Inject values on registration HOT 2
- users_context seems to be ignored HOT 1
- Upgrading POW from 1.6 to 1.7 HOT 12
- Changeset errors into view HOT 1
- POW & LiveView - best way to implement `assigns.current_user` ? HOT 7
- Elixir 1.15 deprecation warnings HOT 1
- Persistence of stale session ids in MnesiaCache HOT 10
- Phoenix 1.6 App upgraded to 1.7 warning about layout conflicts. HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from pow.