How to implement persistent session in SPA with Phoenix as API

I’m looking for a way to implement a persistent session of the authenticated user using React that runs on different server than Phoenix.

My initial idea would be making a JWT based authentication, after successful authenticated the server replies with a JWT signed token. The client then will store this token in a localstorage then in a custom PrivateRoute component (a component where it will check for a existing token in the localstorage then redirects to the protected route if exist and redirect to login page if it does not exist). But I think this would not be secure and I’m asking if there are ways to have persistent session of user.

Thank you.

What does mean different server then Phoenix?

What technology stack besides React?

I mean Phoenix backend and React frontend will run on different ports

You will need to use cors https://hexdocs.pm/corsica/Corsica.html or other cors library to allow the server and the front-end to communicate between them.

I’m aware that in order to request from another port I need to configure cors but that’s not what I’m asking.

The check should not be done on the client, but on the server…

This is my flow for SPA. get a Phoenix Token (not a JWT!) when successful authentication. Then store in localstorage.

Then, add this token in the header for each requests.

The token is decoded server side, and guarantee your identity.

There is no session with api.

1 Like

With this flow I always perform a request like /me endpoint whether the request has the token in its header on the client side to know that it does not need to go back again to the login page?

I have two pipelines…

  pipeline :api do
    plug :accepts, ["json"]
  end

  pipeline :api_auth do
    plug(AppApiWeb.Plugs.VerifyHeader, realm: "Bearer")
  end

  scope "/api/v1", AppApiWeb do
    pipe_through :api

    post("/registration", RegistrationController, :create)
    post("/authentication", AuthenticationController, :create)

    # Secure API
    pipe_through(:api_auth)
...

I wrote a plug to check if token is present in the request and extract it if needed. This allows to define which route is protected, or not.

Protected route requires this token to be decoded, and ensure who You are.

Thanks I have understood now the way for the server to check if the user is authenticated or not. Is there for a user let say maintain a authenticated user even though a web app is open in another tab or refreshes the current tab?

Refresh is ok, because client has the token in localstorage, but identity is shared for tabs of the same browser. You can have two identities when opening tabs in different browsers.

Correct me if I’m wrong with your suggestion.

  1. User logins via React SPA app
  2. If credential is valid, the server will respond with a Phoenix Token
  3. Then the client receives the token, stores it in a localStorage then add this token in request header
  4. The server will now know if the user is authenticated or not by checking the header

To maintain a persistent authenticated user in the app, I will check if there a token stored in the localStorage if not then redirect the user to the login page.

Yes, that’s it :slight_smile:

Thanks for it. Anyway is there any cons for storing to token in the localStorage?

Some prefers to persists it in cookies instead. And storage is different when using mobile (eg. React Native).

But I use localstorage…

1 Like

I’ll decide on storing it in localStorage for now. Thanks for the help.

It’s generally a bad idea to store sensitive data in LocalStorage. If you use the same domain then I would let the backend set a httpOnly cookie, otherwise I would only keep the token in memory, and use something like OAuth2 auth code flow with PKCE.

1 Like

Thanks, good link to know :slight_smile:

Some prefer to store it in cookie since it can be protected there from unauthorized access by JS libraries.

Why do you prefer Phoenix Token over JWT?

Because it is lighter :slight_smile:

1 Like