Nuxt SSR with phoenix http only cookies authenticate and authorize, is it possible?

Hello everyone,

I have the following stack that i am trying to make it work:

Backend:

  • Phoenix + Absinthe

Frontend

  • Nuxt

For this SSR app that I am working on(for learning purposes),I am trying to set a http only cookie and sent it towards NUXT, but i can’t find a reliable way to make NUXT work with the logic from the backend if the cookie is http only.

Also from the NUXT documentation on auth routes I figured that the default behavior doesn’t help me. Because it needs access to the cookie to read it and make decisions on authorization and authentication permissions.

The documentation wth examples of what i am talking about can be found here https://nuxtjs.org/examples/auth-routes/

So my learning has come to a stop and i would really like to move forward and finish this app with some of you may know is a medium clone(complex blog under MIT).

I am looking for solution to use SSR with http only cookies and couldn’t find anything, so please share any ideas or technologies that may help me to finish this.

Thank you in advance

After a lot of search found this

I am using an approach where I split my JWT into two cookies containing signature.payload and signature . The cookie containing signature is httpOnly and signature.payload can be accessed by the browser. This way I have the safety of httpOnly cookies while getting the user info in the payload and not risking of leaking the full JWT, as they are separated.
I think this is a reasonable and should be implemented. Meanwhile I guess I will have to write a custom solution

Can somone explain what is he referring too, when he says that is splitting the JWT singnature in 2 parts?

Because form the JWT docs I can’t understand it

Signature

To create the signature part you have to take the encoded header, the encoded payload, a secret, the algorithm specified in the header, and sign that.

For example if you want to use the HMAC SHA256 algorithm, the signature will be created in the following way:

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

The signature is used to verify the message wasn’t changed along the way, and, in the case of tokens signed with a private key, it can also verify that the sender of the JWT is who it says it is.

There are two parts to this: Authentication and Authorization.

The first is making sure someone else is who they claim to be. The second is about if someone is allowed to do a certain action.

Authentication is most important if you look at different parties. E.g. your client might need to authenticate for your server to trust it’s inputs. Now the question is does your nuxt app actually require authentication or do you only need to authenticate against a backend server? The nuxt app by itself hardly can do proper authentication, as the whole source is readable on the client so there’s not really a secret to authenticate a user by.

For authorization you could just send the nuxt app a list of permissions once authentication (probably by the server) is done and handle/modify however your app needs to handle it (timeouts or things like that).

This is basically what the person did with their JWT token. Put the part required for authentication in the httpOnly cookie and put the part for permissions in a js readable cookie, which now has nothing to do with authentication anymore, but is only meant for authorization.

2 Likes

Let me try to explain what i understood from your post:

So in the cookie with http only flag will contain only the user_id

and in the jwt will be the user_role?

The cookie will contain whatever you need for your server to maintain a session. In phoenix you can put the user_id on the cookie, because it’s signed and you can be sure it’s not changed if the signing secret is not compromised. But other systems just send e.g. a session ID and store actual data on the server or whatever. The key is that those cookies are to be kept httpOnly. It could also be a JWT stored in the cookie, but it doesn’t need to and imho there’s not much reason for it to be a JWT. For httpOnly cookies there’s no real benefit from a JWT over just Phoenix.Token.

For handing of data (permissions are also just data) to your nuxt app you need to use a transfer method, which is readable by js. Could be a normal cookie, could be an api request or anything the js in the end has access to.

Now you seem to be very cautious about security. With e.g. JWT and public key crypto (not shared secret) you could encode the permissions so the nuxt app can validate that the permissions came from your server, even though the private key is not on the client, only the public key would be. So you can be sure permissions are not altered since the server sent them.

What you cannot do however - without the server - is check if the permissions are current for the user, which is why usually JWTs are set to only be valid for small amounts of times. Your server has no way to actively revoke the permissions the JWT gives within your nuxt app once both are no longer connected.

Now it’s your turn to decide how rigidly you want to enforce authentication/authorization in your nuxt app. If the server is available you can always reauthenticate/ask for permissions of a user, but if that’s not the case you can just work with what you got from the server at earlier times. As mentioned prev. authentication just on the client without a server is not really secure. You need to make the decision what you allow the user to do in case the server is not available and maybe check authentication/authorization again when trying to send data to the server, once it’s available again.

2 Likes

Why not create an endpoint which returns information about the current user (it could be a “me” graphql query) that returns the current user plus roles if they have a valid cookie, or null if there is no user. You can call this endpoint at the top level of your app and pass the user and roles down to the lower level components on the frontend where needed.

Unless you have a very specific need to use JWTs then I think this is a much easier approach to use.

2 Likes

The problem is that when my app goes in pWA mode (offline first ) and i also have vuex that is used for the source of truth all the security patterns don’t match because if i store the jwt or cookie in the store I am vulnerable to all kind of attacks.

Can the me query be used to authorize page routes?

Ex:

me{
username
role
}

Then create a middleware for page authorization?

Because nuxt doesn’t have the route action beforeEnter from vue that would have been helpful here.

So i am getting confused on this part how to handle all this.

Also thank you for your help an explanations so far.

You’re not more attackable offline than you’re online, besides when it comes to sending data to your server. Everything, which your nuxt app loads/reads/displays can be accessed otherwise by people who can do javascript / open the dev tools or have otherwise access into the javascript runtime. Offline or not.

Sending data to the server is only possible when the app is not used offline anyways, so no problem here, let the server handle everything.

The only way for your nuxt app to prevent access to data is not storing it or at least deleting it once a certain timeout or other criteria is met. There’s no magical way to make data only readable by your application, but not someone with access to the runtime it works in. It’s as secure as your server is if you give someone ssh access to root.

2 Likes

So from what you shared with me now, I am better off using jwt with short life span of 5 minutes and make my PWA work even offline and for applications with sensitive data I should use phoenix and minimal JavaScript.

Thanks for the info and patience

You don’t even necessarily need the JWT. It’s just a way for your javascript to validate that a certain chunk for data was not altered since encoded by your server. This might be useful to your application or not.

Also authorization in javascript might still be useful if the risk of someone using e.g. the console to get to data is one you can take. Most often offline data is owned by the person using it, but you still like to not give data away to the random person strolling by their laptop. There are also ways in e.g. kiosk applications to show websites without giving people access to dev tools or alike.

If data is truely highly sensitive though it’s best kept on the server.

3 Likes

@wolfiton Hope you wouldn’t mind some off-topic question :slight_smile:

I’ve been exploring SSR frontend solutions for PWA for some time and I have concerns about them.

From hight point of view the idea is great, you can write SPA as SEO friendly fast-loading website but in details I heard about caveats with that approach.

The first is: You need the Node on the server to handle JS pre-rendering and you need to have a lot more memory to be able to process javascript on server side. Also it takes time to pre-render JS. Does it still able to answer within less than 1ms timeframe?

The second is: How well does it play with parallel requests? How many workers do you have which can work in parallel? As an example what I’m talking about: https://github.com/zeit/next.js/issues/1840.

Thank you.

@achempion

PWA in general

Yes the biggest one is that your security will be a very low priority(JWT in localStoarge), so if you make a site that doesn’t deal with processing cards or payments you should be alright, if not then don’t use it.

Node SSR

I don’t use node for my approach but Phoenix absinthe apollo with graphql and NUXT, that said I think you can generate SSR without node.Haven’t tried it yet though.

But this link should offer some support to my theory

Also i am starting to feel that JavaScript frameworks are over rated and phoenix with html.eex templates plus minimal javascript is the way to go forward. Because losing some fancy animation at route change are nothing compared to data leaks or a lot of other attacks.

I’m writing the frontend for my Phoenix app in Nuxt, it was previously a Vue SPA but now I need SSR.

I was able to set up Nuxt to use httponly cookies with the phoenix app by allowing the frontend domain/host/port with Corsica.

To get the initial state needed for nuxt I set up an endpoint that returns the current user, permissions and settings that the nuxt server calls to fill the store.
I don’t use “permissions” to do authorization in the client but to decide what options to show the user and what pages can ve viewed. Real authorization happens at the server side.

Just make sure the server is properly secured and never trust the client.

security will be a very low priority(JWT in localStoarge)

In such case with localStorage you wouldn’t be able to render server side because you don’t have an access to the local storage.

And from the link at vue forum:

Yes you will need nodejs in your server. Although you still can use Nuxt in spa mode and generate the static content using Nuxt generate command

If you want SSR you need the ability to execute JS on server side hence the need of nodeJS.

Your approach works well if you are only after SSR but I am after PWA(offline first), so my fronted will need to work without back-end when the app has no internet.

If it is true then this trade off is a high price on performance.

Which is not a problem, my app is a PWA as well. Being an offline PWA has nothing to do with using cookies.

If you need user data for offline tasks(such as username, some settings, but nothing sensitive like credentials of course), then store it in, say, localStorage, and fall back to it when you can´t connect to the server, for example. When buildinga PWA you can even setup a worker to cache requests for when the network is unreachable.

SSR with node is quite fast, node is not really that slow, and you would be using it only for the SSR part, which usually happens only once per user session. So even if node is a little bit slow, most of the time it’s not a big tradeof. Subsequent requests will be handled by phoenix since most of them will be queries to the graphql api.

I will have to test this and come back with my findings.

But still something is bothering me if you put in the Vuex store your user info, what are you actually storing there?

Just the

username

email

and no credential info? and you are ignoring completely the auth middleware of nuxt in favor of the backend logic?

Also does the node server for SSR comes bundled with nuxt? or i will have to use express or fastify for this?

Anything required by the nuxt app to render the page: username, avatar, settings(such as the theme styles), and a list of permissions.

I do use the auth middleware, based on the user info in the store. If there’s a User, then there was a successful authetication and the user can view auth restricted pages. I don’t need credentials for that. But the REAL authentication and authorization happens at the backend(which is where the core app is). If the user is not logged in or do not have rights to do some actions, event if the nuxt app thinks it can and shows the user a restricted page, the backend will say NOPE and return an error message.

Always remember that the frontend can be tricked by the user to think that it can do more than what it really can. This is true with or without nuxt.

It comes bundled with nuxt.

1 Like

Thank you @doorgan for the examples and explanations for my questions.