Is it possible to authenticate entirely in LiveView, without Controller?

A Phoenix application, using Phx.Gen.Auth, has a publicly accessible LiveView (Auth) for handling a signup/login process. It has another authorized-only LiveView (App) for the internals of the application. It it possible for the user to be seamlessly redirected from Auth to App? If so, how would the session cookie be set on the user’s browser?

The normal way of doing this is to use a Phoenix.Controller, since it has access to a full HTTP request/response and we can use renew_session/1 and put_session/3 to ensure the session cookie has been set when the user completes auth and navigates to the LiveView. This is shown in one of the examples in LiveView docs (Security considerations of the LiveView model — Phoenix LiveView v0.17.2), where presumably redirect('/login) is meant to send the user to a Controller-powered login interface.

Theoretically, though, it should be possible to generate a token from within the Auth LiveView, sign it, push it to the client with Phoenix.LiveView.push_event/3 (JavaScript interoperability — Phoenix LiveView v0.17.2), set it as the session cookie, then redirect() to the App LiveView, which finds that token on mount.

Are there any examples of this being done? Any obvious reasons this would be a terrible idea?

1 Like

In order to set a cookie you need a conn object. Liveview has no access to the conn object. So I think there is no way to do that in LV

2 Likes

It looks like PhoenixLiveSession adds a put_session callable on a LiveView socket, though it also adds a pubsub system that this use case doesn’t really require.

And there is an example out there of setting session cookie with hooks.

Yes. Instead of relying on the session, which you have no access to from within the LV, you can put the auth token in Local Storage, and have it back later with a bit of help from javascript.

1 Like

Local storage has different security considerations than (http only) cookies though.

4 Likes

Yes. If you use untrusted 3rd party scripts or is open for XSS attacks in someway, don’t put auth token in local storage. Another downside is you would need one extra round trip to get auth’ed, because you can only present the auth token after being connected in LV.

Those being said, I still think it is better than trying to tamper with the session from a fetch request initiated from the JS. It would have exactly the same security concern as above and you would need to write more code.

1 Like

The answer is: yes, it’s possible to do with server-side sessions.

  • Each unauthenticated request receives a unique session ID in the response Set-Cookie header.
  • On authentication, that session ID is saved in the server’s session store.
  • On subsequent visits, plugs and LiveView mount() callbacks check whether an authenticated session is associated with that session ID.

The method suggested in the question, pushing a newly generated session token to the client and setting it with JS, would expose your users to having their sessions hijacked.

An example of the server-side approach can be found here (Cookie Authentication with Phoenix LiveView).

I would submit, better still would be to just use a controller to authenticate. :grin:

@j4p3 Is there a reason you are trying to avoid using a controller? Or perhaps you are just asking about the capabilities of the technology?

The intention is to present the user with a more instantaneous transition into the app, using LiveView.push_redirect rather than triggering an additional two HTTP requests (one to the auth controller, returning a 302, and another following that 302 into the app).

The user experience can be made to feel more responsive this way, especially on slow connections where a user might spend some time staring at a blank browser window while they load the 302 destination. Performance-wise, though I haven’t benchmarked it, it feels like it would be faster to do everything over the established WS connection rather than two HTTP roundtrips. And it just feels a little awkward, aesthetically, to be redirecting a user from LiveView to a controller only to redirect them back to another LiveView.

That said, handing auth tokens to client JS is definitely a Bad Idea. The conclusion I came to in the answer is that, if you really wanted a direct LiveView → LiveView auth handoff, this is what server-side sessions are for.

1 Like