Comments (11)
Figured it out, but leaving for the record:
Pow.Plug.put_config(plug: Pow.Plug.Session, otp_app: :kyodo_web)
I found this closed issue when trying to wire up login/registration via absinthe
. While this is super helpful, I am running into problems when attempting to use this method. Is there something special about session/registrations, by any chance?
%Plug.Conn{}
|> Pow.Plug.put_config(otp_app: :kyodo_web)
|> IO.inspect()
|> Pow.Plug.authenticate_user(%{"email" => input.email, "password" => input.password})
Results in:
# Pow plug was not found in config. Please use a Pow plug that puts the
:plug in the Pow configuration.
I'm aiming to move all of this functionality into Absinthe and then I'd be into making a pow-absinthe package if things go well... Either way, maybe I can help w/ the guides if there are some good learnings.
UPDATE: In digging through the tests it seems this has to do w/ not properly setting up the session in the conn. I will keep digging but this is starting to get hairy for me :)
from pow.
I got another idea for something that can make the integration cleaner.
First we'll pass the conn to the context:
pipeline :graphql do
plug MyAppWeb.Plugs.AbsintheContext
end
defmodule MyAppWeb.Plugs.AbsintheContext do
import Plug.Conn
def init(default), do: default
def call(conn, _default), do: Absinthe.Plug.put_options(conn, context: %{conn: conn})
end
Then we'll remove render handling in the resolvers as it should only return data:
defmodule GolfWeb.Resolvers.Accounts do
def create_reset_token(_parent, %{email: email_address}, _resolution) do
email_address
|> Context.get_by_email(otp_app: :golf)
|> maybe_generate_token()
end
defp maybe_generate_token(nil) do
# error response
end
defp maybe_generate_token(user) do
uuid = Pow.UUID.generate()
backend = Pow.Config.get(otp_app: :golf, :cache_store_backend, Pow.Store.Backend.EtsCache)
PowResetPassword.Store.ResetTokenCache.put([backend: backend], uuid, user)
{:ok, %{user: user, token: token}}
end
end
And finally handle the mail delivery in the response layer (like controller):
@desc "Resets a password"
field :reset_password, :user_email do
arg(:email, non_null(:string))
resolve(&Resolvers.Accounts.create_reset_token/3)
middleware &send_reset_password_email/2
end
def send_reset_password_email(%{value: %{user: user, token: token}, context: %{conn: conn}} = resolution, _) do
url = Routes.pow_reset_password_reset_password_url(
GolfWeb.Endpoint,
:edit,
token
)
email = PowResetPassword.Phoenix.Mailer.reset_password(conn, user, url)
Pow.Phoenix.Mailer.deliver(conn, email)
%{resolution | value: %{email: user.email}}
end
from pow.
In case this helps someone, I was trying to figure out how to deliver an invitation mail from a LiveComponent, and this issue put me on the right track i.e.:
defp deliver_invitation_email(socket, user, invited_by) do
conn = conn(socket)
token = PowInvitation.Plug.sign_invitation_token(conn, user)
url = Routes.pow_invitation_invitation_url(conn, :show, token)
email = Mailer.invitation(conn, user, invited_by, url)
Pow.Phoenix.Mailer.deliver(conn, email)
end
defp conn(%{endpoint: endpoint} = _socket) do
Conn
|> struct!(%{
private: %{phoenix_endpoint: endpoint},
secret_key_base: endpoint.config(:secret_key_base)
})
|> Pow.Plug.put_config(otp_app: :my_app)
end
from pow.
I just want to note, that it isn't proper to pass conn
into absinthe context and rely on it because GraphQL API is transport agnostic. In my opinion building new conn inside a resolver is the best workaround nowadays.
from pow.
Here is my current approach which seems to be working. I send the email address back regardless of whether the email is sent.
defmodule GolfWeb.Resolvers.Accounts do
alias GolfWeb.Router.Helpers, as: Routes
alias PowResetPassword.Phoenix.Mailer
def create_reset_token(_parent, %{email: email_address}, _resolution) do
config = Application.get_env(:golf, :pow, [])
conn = Pow.Plug.put_config(%Plug.Conn{}, config)
params = %{"email" => email_address}
conn
|> PowResetPassword.Plug.create_reset_token(params)
|> respond_create(email_address)
end
defp respond_create({:ok, %{token: token, user: user}, conn}, email_address) do
url =
Routes.pow_reset_password_reset_password_url(
GolfWeb.Endpoint,
:edit,
token
)
email = Mailer.reset_password(conn, user, url)
Pow.Phoenix.Mailer.deliver(conn, email)
{:ok, %{email: email_address}}
end
defp respond_create({:error, _, _}, email_address) do
{:ok, %{email: email_address}}
end
end
from pow.
The plug modules handles plug connections, so nearly all plug methods will be focused on conn transformations. This also includes using configuration from the connection which could be set in the endpoint rather than env config. With absinthe plug all of that will be bypassed. I've not worked with GraphQL/absinthe before, but I'll take a look at the absinthe and absinthe plug libraries to see if there is a cleaner way of handling this. It would be ideal if the connection could be passed along, but I'll also take a look at the API in Pow and see if it makes sense to separate some of the reset password method logic to make it easier for this type of customization.
But your approach looks great. Instead of loading the app env you could set it to load the :otp_app
config the same way as in your endpoint:
params = %{"email" => email_address}
%Plug.Conn{}
|> Pow.Plug.put_config(otp_app: :golf)
|> PowResetPassword.Plug.create_reset_token(params)
|> respond_create(email_address)
from pow.
Ok, I have given it some thought and your solution is probably the best one. Since this logic is spread over a controller and plug module, I don't think there's much that can be done to make a simpler integration.
I would probably try write some of these bits into smaller methods so refactoring will be easier if there'll be any breaking changes in Pow in the future:
defmodule GolfWeb.Resolvers.Accounts do
alias GolfWeb.Router.Helpers, as: Routes
def create_reset_token(_parent, %{email: email_address}, _resolution) do
conn()
|> PowResetPassword.Plug.create_reset_token(%{email: email_address})
|> maybe_send_email(email_address)
end
defp maybe_send_email({:ok, %{token: token, user: user}, conn}, email_address) do
deliver_email(conn, user, token)
{:ok, %{email: email_address}}
end
defp maybe_send_email({:error, _, _}, email_address) do
{:ok, %{email: email_address}}
end
defp conn(), do: Pow.Plug.put_config(%Plug.Conn{}, otp_app: :golf)
defp deliver_email(conn, user, token) do
url = reset_password_url(token)
email = PowResetPassword.Phoenix.Mailer.reset_password(conn, user, url)
Pow.Phoenix.Mailer.deliver(conn, email)
end
defp reset_password_url(token) do
Routes.pow_reset_password_reset_password_url(
GolfWeb.Endpoint,
:edit,
token
)
end
end
This is all about your preference though, the way you have written it is good as it is 😄
from pow.
This worked for me, though this line
defp conn(), do: Pow.Plug.put_config(%Plug.Conn{}, otp_app: :golf)
is giving me dialyzer errors about success typing and the contract. If I had to guess based on previous dialyzer battles, this is because the Plug.Conn typespec has keys which need a value (like pid), and the argument we are passing is primarily empty
I suppressed dialyzer for that file to make progress
from pow.
@dlobo good call, should probably do something similar to Plug.Adapters.Test.Conn
: https://github.com/elixir-plug/plug/blob/f24af6a5b00dba4d32d44c6f2fb4423f6a5d8e3b/lib/plug/adapters/test/conn.ex#L34-L50
from pow.
In my case, the Pow calls just needed the config object (basically I wanted to delete the existing tokens in case the user updated the roles, so deleting the token in persistent and credentials cache)
so I just sent in the config as: [otp_app: :glific] and did not need the conn object (and hence did not need to get in via absinthe either)
from pow.
I am still getting this error "Pow plug was not found in config. Please use a Pow plug that puts the :plug in the Pow configuration."
config = Application.get_env(:myapp_api, :pow, [])
conn = Pow.Plug.put_config(%Plug.Conn{}, config)
def signin(conn, attrs \ %{}) do
user = pow_authenticate(%{"email" => attrs.email, "password" => attrs.password})
if user != nil && user.is_disabled do
nil
else
{result, connection} = conn
|> Pow.Plug.authenticate_user(%{"email" => attrs.email, "password" => attrs.password})
{result, connection, user}
end
end
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 15
- 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.