Access LocalStorage in Middleware - NuxtJs - javascript

Well, I'm starting with nuxt and I have following routes:
/home
/dashboard
/login
I want to protect the /dashboard, but only for users logged in with a token in localStorage.
The simplest way I thought of doing this was by creating a /middleware/auth.js
export default function () {
if (!window.localStorage.getItem('token')) {
window.location = '/login'
}
}
and registering it in the /dashboard/index.vue component.
<script>
export default {
middleware: 'auth',
}
</script>
But I cannot access localStorage within a middleware, because LocalStorage is client-side.
I have already tried to add this same check in the created() dashboard layout, but I cannot return window not set mounted() is too late, it can only check after the page has been fully assembled.
So how can I achieve this?
Note: I do not intend to use any Vuex for this project.

I used cookie-universal-nuxt
On vuex store for login action I set a commit with the token
window.$cookies.set('token', payload, {
path: '/',
})
and access it in middleware as
middleware/auth.js
export default (context) => {
if (!context.app.$cookies.get('token')) {
return context.redirect('/login')
}
}

For anyone not satisfied storing the information in cookies, here's me solution:
I've been having a lot of problems with this and I were not satisfied setting a cookie.
If you are running Nuxt and haven't told it to run in spa mode it will run in universal mode. Nuxt defines universal mode as:
Isomorphic application (server-side rendering + client-side navigation)
The result being that localStorage is not defined serverside and thus throws an error.
The give away for me was that console logging from middleware files and Vuex outputted to terminal and not the console in developer tools in the browser.
The solution for me was to change the mode to spa in the nuxt.config.js which is located at the root.
Please notice that you can still access localStorage, running universal mode, in page files and components because they are not server side.
Middleware files are, in universal mode, run server side, so changing to spa mode makes them run client side and thus allows them access to localStorage.
For more information about Nuxt modes, read these:
https://nuxtjs.org/guide/
https://recurse.me/posts/choosing-a-nuxt-mode.html

You can use this for Local Storage (and many other things) in Nuxt. It will work on client and server. There's no documentation, but README should be sufficient.
https://github.com/nuxt-community/universal-storage-module

Related

Cookie sharing between server/client in NextJs

Let's say you have a standalone server that acts as your data layer and then a Next.js app that's sole purpose is to server-side render the React and occasionally prefetch data from getServerSideProps. Beyond that, most interactions happen between the browser-side client and the standalone server directly (trying to avoid an unnecessary Next.js middleman / cold starts). Now if I'm trying to set an auth token via cookies, is there an elegant way to effectively share that cookie between Next.js and the client, so that the client is authenticated on requests, but also the Next.js getServerSideProps functions?
I'm wondering if I need to have a singular /api/login Next.js serverless function that just acts as a "I'll take the cookie for myself and also pass it right along to you (the client)"
Does this all make sense? In my case the standalone server is built on NestJS. I could use a tool like react-ssr to server side render everything, and that might be a good solution since almost every page has blocking data requirements, but I'm trying to understand my options, and it seems like this is probably a commonly faced issue with frameworks like Next.js.
You can use js-cookie to set cookies directly from the renderer.
Then all you need to do is get your cookie from req.headers.cookie.
import Cookie from "js-cookie";
function Index() {
function setCookie() {
Cookie.set("yourCookieName", "yourCookieValue");
}
return (
<div>
<button onClick={setCookie}>set cookie</button>
</div>
);
};
export async function getServerSideProps({ req }) {
console.log(req.headers.cookie)
return { props: {} }
}
export default Index;

Nuxt - can i run client side code from a middleware in Universal mode?

I'm working on a universal Nuxt app that uses the standard Django session authentication.
I need to restrict some pages in my project to logged in users only, so i decided to use a middleware.
The problem with the middleware is that it will run from server side, so it will always return False to the user even when the user is logged in, since it doesn't send any cookie in the request. This happens only when i refresh page or when i navigate directly to it, not when i navigate from another page to a restricted page, that's because in that case the middleware is executed client side and not server side.
Is there any way i can "force" the following code to run client side instead of server side from the middleware? Or do i have to look for another solution?
export default async function (context) {
axios.defaults.withCredentials = true;
return axios({
method: 'get',
url: 'http://127.0.0.1:8000/checkAuth',
withCredentials: true,
}).then(function (response) {
//Check if user is authenticated - response is always False
}).catch(function (error) {
//Handle error
});
}
I tried to do the same with nuxtServerInit but the outcome is the same. If i run that code in the beforeCreate block from the page it will first load the page and then execute the request, which works but it's quite ugly since the user will see the page for a second before being redirected.
There is another way around this.
First You need to run your middleware code only on the client side.
export default async function (context) {
if(process.client) {
// your middleware code here
}
}
Now you just have to take care of the first load of your application. you can create a plugin that only runs on the client-side and use the middleware code inside the plugin.
for running the plugin only on the client use the mode key word like this.
this is the nuxt.config.js:
plugins: [
{ src: '~/plugins/client-only.js', mode: 'client' }, // only on client side
]

What is client side and server side in Next.js?

Can anyone please explain me the concept of client side and server side in Next.js as they have mentioned in their documentation. What I know is that Next.js works on react which is client side and run in the browser and server side means the api (backend). Any help would be appreciated. Thanks
From Next.js documentation:
This function gets called at build time on server-side. It won't be called on client-side, so you can even do direct database queries. See the "Technical details" section.
export async function getStaticProps() {
const postsDirectory = path.join(process.cwd(), 'posts')
const filenames = await fs.readdir(postsDirectory)
}
I started writing NextJS app few months before, i'll explain as far as i know check whether it would be helpful.
Your understanding on client and server(API) is correct but in case of NextJS there is another client side and server side as NextJS is used for Server-Side Rendering(SSR).
In simple a same page Ex: pages/home.js when loaded with browser hard re-load https://example.com/home is loaded as server side. Pages written under /pages/ folder will be rendered server side on navigation. So the DOM elements of the page will be available in page source(view page source option in browser) which will be used by crawlers too.
You can find the difference by checking whether type of window !== 'undefined', as window represents browser which is client and view page source of browser represents server side rendering.
In Pages also you can check
Create a Next.js project
Have two pages index.js and home.js
In home.js write Home.getInitalProps method which is similar to useEffect or componentDidMount in react component. Here pages cannot contain componentDidMount or useEffect instead all API calls before render has to be done in getInitialProps or other related methods.
Home.getInitialProps = async (context) => {
const { req, query, res, asPath, pathname } = context
if (!req) {
if (typeof window !== 'undefined') {
//its server side request happened on
}
} else {
// its client side call that calls getInitialProps when routing
happened Router.push('/home') from index page or inside components
rendered from pages/index.js
}
}
Let me know if you need some more details, we can explore and figure it out.

How does Express and React Routes work on initial GET request from browser?

I'm new to the react world and to the fullstack world as a whole but I've searched endlessly for an answer to the following and some guidance would be really appreciated.
I'm creating an app using React and Express. It requires authentication so I was planning on using Passport to help. The client side JS uses React Routers to navigate through the website. That's all fine but my issue is with the initial GET request made by the browser.
I'll first describe my specific app requirements and then generalize what I don't understand.
As I said, my application requires OAuth2 authentication. If you try to GET a path on my website and you're not logged in, it should just load the login page. If you are logged in, then load as normal and find your path. Similar to facebook, I'd like the login URL to be the same as the "feed" page. So similar to how facebook.com '/' route is either the login page or your new feed depending on whether you are signed in, I want the same thing.
From what I understand, Passport authenticates on the back end by checking the request header. So I understand that I should have some kind of middleware that says "if user is signed in, continue down the routes otherwise render sign in page" ... How is this done? What would the code look like? My only experience with Express was from an intro class which used res.render to send back an HTML file and pass it through some template engine like handlebars. But I have no idea how it'd work with react routes. Would i still use res.render()? Something else?
Let's say my index.html has the root div to inject the react into. If I had to guess, I'd send back that index.html page with the .js file with the routes and somehow on the backend send back the route I want it to match on my react routes (either the login one or the user requested)??
More generally, I guess I'm just confused how the initial request to a website using react routes is done. 1) How does the server interact with everything to render what I asked for? 2) What would the code look like for that. My only experience with React is from a basic Udemy course that just used "react-scripts start" to render the page.
After spending the entire day Googling this question it led me to SSR which is a rabbit-hole of its own and I'm not even sure if its what I need to help me. Is it?
I'm clearly missing some fundamental knowledge as this is really tripping me up so if you have any resources to learn more just post them. Thanks!
I understand your struggle as I've had to go through it myself when combining front-end with back-end, specifically React and Node. So first things first, we know that the browser/client will always initiate a request to the server, so how does React Router take control of the routes? Well its plain simple actually, all you have to do is return the entire react app from any route from your express server. The code will look something like this:
const express = require('express');
const app = express();
app.get('/*', (req, res, next) => {
// Return React App index.html
});
app.listen(3000);
Once the react app renders on the user browser (don't worry about paths, as react will automatically render according to the URL based on the code you wrote in the client side, it will also take care of authentication vs feed page when it will scan for your local storage, cookies, etc), it will take control of routing, instead of a request going to the express server. But what happens when we request data from our server, well it returns react app on each route so we need to setup an api route to handle any data requests.
app.get('/api/v1/*', (req, res, next) {
// Return some data in json format
});
Hopefully, this gives you insight about what you were looking for.
I think the fundamental gap you're struggling with stems from that lot of those 'intro courses' shove the entire browser client into the application server to get things up and running quickly, as in, the Node server renders the entire React app AND operates as an API...
// Ajax request from React app to: http://example.com/api
app.use('/api/*'),()=> {
res.send({ <!-- some JSON object -->})
})
// User visits in browser: http://example.com/**/*
app.use('/*',()=>{
res.render(<!-- entire React App sent to browser -->)
})
The first request (assuming the user doesn't visit /api/* ) will just send down the React bundle. Further user navigation within the client would generally send XHR requests (or open WebSockets) from the React app to Express routes running on the same node program.
In many situations it makes sense to have these parts of your program separated, as by having react delivered from a completely different location than where it requests data. There's many reasons for this, but optimizing computing resources to their differing demands of CPU, memory, network .etc and manageability of code/deployment are the big reasons for me.
For example...
User visits: http://example.com *
Nginx, Apache, a 'cloud proxy' .etc direct the traffic to a static React bundle, which has no authentication and never makes contact with your Node server.
If the user has Authenticate previously they will have token in local storage (if you're using JWTs for Authentication) and your React app will be configured to always check for these tokens when you first it is initially loaded.
If the user has a token it will send an Ajax request in the background with the token as a Header Bearer and will send back user data, then redirect them to an 'Authenticated page' like the FB feed you mention.
If they don't have a token or the token Authentication fails then React will redirect them to the Login or Registration page
React
React basically high jacks the browser's native 'location' functionality (whats displayed after you domain name). So any events after the initial page load (buttons clicks and such) are handled entirely by React internally and uses those routes to determine what to display or what data to fetch from the API through Ajax (XHR).
If the user performs a hard page reload then that request will go back to the server and it will perform the whole cycle over again
React Router
Allows you to do 2 things simultaneously...
Manipulate the browser Location and History objects.
Use that History and Location information elsewhere by detecting changes and sending off events.
SSR
I've only toyed around with SSR so I can speak to it, but its provides extremely low latency for initial renders, doing it in 1 network request, so you want to use it areas of your program where thats important.
Not sure if this answers you question, but let me know if you would like me to elaborate on anything or provide some more detailed resources.
SSR is a little bit confuses for developer that has less experience, let forget it for now.
It will be more easier for you to assume that frontend JavaScript (React) and backend Javascript (NodeJS) are two separate apps, and they communicate to each other via API.
here the code that show Login component and Feed component depending on whether you are signed in
import React, { Component } from "react";
import axios from "axios";
class Home extends Component {
constructor() {
const accessToken = localStorage.getItem("accessToken");
this.state = {
accessToken,
feeds: []
};
}
componentDidMount() {
if (this.state.accessToken) {
axios(`api/feeds?accessToken=${this.state.accessToken}`).then(({ data }) => {
this.setState({
feeds: data
});
});
}
}
render() {
if (this.state.accessToken) {
return <FeedsComponent feeds={this.state.feeds} />;
}
return <LoginComponent />;
}
}
and this is your backend
const express = require("express");
const app = express();
app.get('/api/feeds', (req, res, ) => {
const feeds = [
{},
{}
]
res.status(200).json(feeds);
});
app.listen(3001);
just keep in mind that they are two separate apps, the can be in two different folder, different server, different port.
Simply point Express to the folder containing your React files or build files.
app.use(express.static(__dirname + '/dist'));
where 'dist' contains the build files
See docs for more details

LocalStorage not showing with build file

I have implemented a small localStorage with react, where I save URI endpoints once the users enters them, and I call them on my componentDidMount function if they exist.
The setup seemed super simple and it totally worked while I was doing npm start on my dev files, however on building my project and hosting it locally using 'serve', I am not able to see my localStorage anymore. Does this have to do something with the build files or the way I'm serving them?
componentDidMount() {
userUri = localStorage.getItem('userUri');
tracesUri = localStorage.getItem('tracesUri');
if (userUri && tracesUri) {
this.setState({
userUri: userUri,
tracesUri: tracesUri
});
}
};
closeModal = () => {
this.setState({
showSettings: false
});
localStorage.setItem('userUri', this.state.userUri);
localStorage.setItem('tracesUri', this.state.tracesUri);
};
If I understand your question correctly, when you run your app locally, you are not able to see the data that was persisted when you ran your app via npm?
Something to keep in mind is that data stored via localStorage is restricted to the current document.origin see MDN docs here.
You need to ensure that you are testing/running locally at the same origin for the same persisted data to be visible in both cases.
You can add this code to your app:
console.log('Origin is:', document.origin);
This will print the origin to console, and then cross check this origin by running the app both via 'npm' and by hosting locally to verify that the origin is the same or different
Access to data stored in the browser such as localStorage and IndexedDB are separated by origin. Each origin gets its own separate storage, and JavaScript in one origin cannot read from or write to the storage belonging to another origin.[ref: link]
So I guess while you serve, you must be using a different port due to which you were not able to access the previous localStorage values.

Categories

Resources