aaronrenner / phx_gen_auth

An authentication system generator for Phoenix 1.5 applications.
772 stars 55 forks source link

Is phx_gen_auth suitable for an API for a Javascript SPA? #106

Closed ajstrand closed 3 years ago

ajstrand commented 3 years ago

Hi, First off, thanks for the great work on this project. It's made auth very simple for a project I'm working on.

I'm very new to phoenix, working on an example app. I've generated the basic auth files into my phoenix app and this library works great for basic auth tasks(like creating users).

However, I've got Preact app that's running most of the business logic in my app and I'm trying to access the "current_user" object in one of my controllers to send some data to the frontend single page app. Is there a way that I can pass around the current user object to different controllers in my app, so I could send data back to my SPA?

Currently it seems like only phoenix HTML templates can access the current user since they are server side templates.

Would my application be more suitable for an auth generator like https://github.com/riverrun/phauxth?

aaronrenner commented 3 years ago

Phx.gen.auth is absolutely suitable for an API for a Javascript SPA, but it might take a little customizing. It's set up to do session authentication out of the box, so if you're just starting out it would be simplest to use phx.gen.auth's HTML forms for authentication and then render your SPA once the user is authenticated.

If you generated your API using phx.gen.json, you'll have to open up your Router and update your :api pipeline like this:

  pipeline :api do
    plug :accepts, ["json"]
    plug :fetch_session
    plug :fetch_flash # Only needed if you pipe through [:require_authenticated_user] below
    plug :fetch_current_user
  end

and the put your API endpoints in a scope that pipes through the :api pipeline and optionally :require_authenticated_user

  scope "/", MyAppWeb do
    pipe_through [:api, :require_authenticated_user]

    resources "/posts", PostController, except: [:new, :edit]
  end

Then in your controller, you should be able to access the current user using conn.assigns.current_user.

I'm not sure if this is how your app set up, but hopefully this should get something working for you. If you wanted to take this code to production, this may not be the approach you want to take, but the generated code is able to be modified as you need. If you did end up doing session-based auth, you'd want to follow security best practices like adding CSRF protection, etc. Please let me know if this didn't answer your question.

ajstrand commented 3 years ago

Hi, so I followed a similar approach to what you outlined and I was able to pass back the current user to my SPA.

I had a follow up question that I'm not sure if the documentation answered.

Is there a way I could pass the user token/session cookie securely to my SPA so I would be able make a POST request with some data?

I didnt see a CSRF token in my browser at all, so I resorted to grabbing the cookie in a controller and passing that back to the frontend like so(the function is contrived, but it's just an example of what I did in as controller)

def some_method(conn, _opts) do
key = conn.cookies["_app_key"]
      # other logic
      json(conn, %{permissions: data, cookie: key})
end

then in my fetch request in the browser i was just planning on setting my config like this:

const config = {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "x_csrf_token: "the_token_i_grabbed_from_the_backend"
    },
    body: some data,
  };

would my approach be ok for a demo app that's not going to a prod environment?

josevalim commented 3 years ago

@ajstrand the CSRF token is in a meta tag: https://github.com/phoenixframework/phoenix/blob/ea10baae82a9b849b9b4b4c1977dcfc344662863/installer/templates/phx_live/templates/layout/root.html.leex#L7

You can pick it up like this: https://github.com/phoenixframework/phoenix/blob/ea10baae82a9b849b9b4b4c1977dcfc344662863/installer/templates/phx_assets/app.js#L20-L21

ajstrand commented 3 years ago

@josevalim thanks so much for answering that question! I dont think my default phoenix application had a root.html.leex file since I'm not using Live View. So that might have been why I couldnt find the csrf meta tag. I'll go ahead and close this issue now.

Edit: just confirmed that the csrf token worked to post some data using the steps provided.

If either of you think documentation for using phx.gen.auth as an api to an SPA could be helpful let me know and I could create another issue for discussion. I'd be willing to write some docs if that would be helpful.