If a LiveView needs to refresh a JWT, how can you "push" the new tokens back up to the client without a full page refresh?

A lot has been posted and written about auth, JWTs and LiveView but there’s still one part that we’re struggling to understand. If you have to refresh a token while the user is visiting your LiveView page, how do you send the newly refreshed token back to the client?

We’re using Auth0 and JWTs to authenticate users. Auth0 calls back to our Phoenix app, we do the token handshake and are left with an id_token, access_token and refresh_token which we store in connection’s session-cookie.

We follow the practice of “double authentication” in our LiveViews by first authenticating the HTTP request in a Plug, and then again in the mount call.

The act of authenticating involves checking for the presence of the appropriate tokens and checking that they have not expired. Tokens are passed from the connection to the LiveView in mount.

Our LiveView page is like a dashboard / monitoring page, so it’s reasonable to assume the user might leave it open and running for a long time. Periodically, the user might perform an action on that page which is handled like any LiveView action would be with the addition that we, again, validate the user’s token to ensure it hasn’t expired and that the appropriate permissions are present.

But here’s the problem: If a token has expired, then we want to silently refresh it for the user. We’re able to do so because we have the refresh_token. However, after refreshing the token with Auth0, we now have new tokens (and a new refresh_token) but no way to “push” those new tokens back up to the client such that they are saved to cookies for the next HTTP request.

Our understanding is that we need to perform a full page refresh to update the cookie, which kind of defeats the purpose of short-lived tokens that can silently be refreshed.

We’re trying to get away from using a “session_id” in our cookie and then needing a database lookup to fetch the JWTs.

Just curious if there’s something we’ve overlooked in LiveView that would allow us to silently refresh these tokens without having to reload the LiveView page.

Not sure to understand how you use these tokens and what you intend to achieve.

In OIDC:

  • the ID token is used to open a session on the RP (your site) from the OP (OpenID Provider - Auth0 in your case). Open a session generally means setting a local cookie on your site. Note that there’s no correlation between the tokens you received and your session lifetime
  • the access token is meant to access external third-party API, and the refresh token to get new access tokens

Are you accessing external API ? If not, why do you need these tokens at all? Storing refresh tokens in cookies seems insecure IMO.

I have the feeling that what you want to do is to synchronize the session at Auth0 with your app, that is kind of Auth0-initiated logout. If so, there’s no easy response as there are at least 3 OpenID standards for that - all being unsatisfying in subtle ways :smiley: But you might not need it.

Hey, did you think of sending fetch request to a auth endpoint and set cookies by returning set-cookie http header as folks in react.js do?
In Phoenix you can basically create an endpoints that sets cookies. As I know browser should automatically update cookies

Generally the LiveView is authenticated on mount (as you mentioned) and is then treated as privileged with the user’s credentials. If you have permissions stored in your database then you can check against those with the user’s database id (since you have already authenticated the user).

Note that it is very important to kill all of the user’s LiveView sockets on logout if you’re using this method, which phx.gen.auth does here. Note that this is template code for generating the auth, hence all the EEx - here is the line as it looks in a real app.

More info about this in the docs here:

https://hexdocs.pm/phoenix_live_view/security-model.html#disconnecting-all-instances-of-a-live-user

If you actually need to set a cookie on the client, you could always use JS (can’t set an HttpOnly cookie, though, obviously). Pushing an event from the server should work, see the docs:

https://hexdocs.pm/phoenix_live_view/js-interop.html#handling-server-pushed-events

The id_token returned from Auth0 represents an authenticated user while the access_token represents an authorization / permissions. When returned from Auth0, you could encrypt and store those in your own database under the usual “sessions” table, then create a session_id that is persisted in the user’s cookies. That requires you maintain users and sessions within your own database. (Common, to be sure, but perhaps not always necessary.)

Alternatively, the id_token and access_token can be stored on the client, which is how many SPAs do it. Auth0 has several pages that go through different ways of implementing that.

In the application we’re exploring with Phoenix, we’re content (for now) in “outsourcing” user management to Auth0, so comparable service. We just need to know who’s logged in (id_token) and what they’re allowed to do (access_token).

Part of validating these tokens is checking their expiration date. If they are expired, and we have a refresh_token, then we can silently refresh the either token and send the new tokens back to the client on the next HTTP request with little to no user action or involvement.

However a LiveView page might be open on the screen for a long time. Perhaps long enough that the token expires and we want to silently refresh that token and send it back to the client to store as a secure cookie. It doesn’t look like we can do that “silently” because we need to refresh the page to trigger an HTTP request and get a connection we can store a cookie in.

If you had a banking app, you might see that after some period of inactivity you just get kicked out and redirected to the bank’s login page. That’s fine for that use case, but for something like a dashboard or monitoring app, we want to silently refresh the tokens and only redirect the user back to the login page if the token refresh fails.

You mean that the server should push a message to the client, through LiveView’s socket, and when the client receives that message, it then makes a fetch request back to the server requesting the new tokens and, upon receipt, stores them locally in a secure cookie?

Hadn’t really thought of that but it’s an interesting idea to explore if it’s technically possible.

If we store an opaque session_id in the user’s cookies, then we can easily handle the authentication and authorization of a user throughout their session by storing all relevant information in a database table.

But we’re store JWTs in the user’s cookies and those JWTs include enough information to authenticate and authorize a user. The problem is that they can expire, and when they do they can be silently refreshed but then need to be stored back in the user’s cookies.

In a LiveView we might authenticate the user in mount, but not perform authorization until an action is initiated by the user. That action might take place after the token has expired but can still be silently refreshed. The newly refreshed token needs to be sent back to the client to be stored in a secure cookie, but a LiveView can’t update a connection’s session (and thus the cookie stored on the client) without a page reload, from what we can tell.

If the user navigates away to another page, which triggers a new HTTP request, then this entire “problem” is a non-issue. But if they try to perform an action on a LiveView page, where we don’t want to refresh the entire page, then it does seem difficult to implement.

Yup, you absolutely right :slight_smile:
Hope it will help)

Hi! To resolve this issue, we are using Process.put/get methods:
https://hexdocs.pm/elixir/Process.html#put/2
https://hexdocs.pm/elixir/Process.html#get/2
And we group live_views with live_session:
https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.Router.html#live_session/3