Cookie sharing between server/client in NextJs - javascript

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;

Related

passing function from server to client component Next 13

According to the Next.js docs for the app directory:
"Whenever possible, we recommend fetching data inside Server Components. Server Components always fetch data on the server."
This is great because I am hitting an external API (where I cannot change the CORS policy - Allow-Origins...).
I have a page (server component) with a form (client component), and I am trying to hit the API using the server component.
Home page
import Form from './Form';
export default function Home() {
handleSubmit = () => {...submit logic}
return <Form onSubmit={handleSubmit} />
}
Form Component
'use client'
export default function Form({ onSubmit }) {
return <form onSubmit={handleSubmit}>...</form>
}
When I try to pass the handleSubmit function to the client component, I get this error:
Functions cannot be passed directly to Client Components because they're not serializable.
I'm not sure what that means.
Is there a way to pass functions to client components?
I need to fetch from a server component because the API has CORS policy. Is it possible to do what I am trying to accomplish?
Do you have any error.js in your directory?
Add use client at the top of the file.
See reference here:
https://github.com/vercel/next.js/issues/42408
https://beta.nextjs.org/docs/rendering/server-and-client-components
Hope this helps.
I need to fetch from a server component because the API has a CORS policy. Is it possible to do what I'm trying to accomplish?
The CORS policy applies to any incoming request (whether the request comes from a front or server component doesn't matter).
What is the HTTP method you are going to use in your function? If it's POST, PUT or DELETE, you don't absolutely need to do it in a server component (the purpose of a server component is to optimize SEO by sending the data already loaded - it is called server-side rendering).
If this is a GET, what are you trying to accomplish? Dynamically load data based on what the user has chosen to fill in?
If so, I think it's easier to do this on the client side. Otherwise you don't need a form.

How to modularize Javascript authentication code in MFE context in a VueJS

I'm working on a large,single-page VueJS application that's been broken down into several micro front-ends, and I'm currently implementing the authentication for communication with the orchestration layer and it's stumping me. The application is organized as a container application that bootstraps and mounts several other VueJS applications underneath it using single-spa
We use Azure-AD and the MSAL library for authentication and to refresh the Bearer token before each API request, and currently the application I'm working had previously stored the AD client info itself and created the authenticationContext on initialization, something like
export class Authentiation {
authenticationContext;
refreshToken;
constructor() {
this.authenticationContext = new msal.PublicClientApplication(msalConfig)
// code to initialize the
}
async acquireToken() {
// code which refreshes the token
}
}
Now, the initial authentication code has been moved into the container application since it will authenticate the user first and only then mount the apps once the user is inside. Each application, though, needs to be able to refresh the Bearer token whenever it communicates with its OL layer. Currently, it's just running the initialization again and pulling the AuthenticationContext out of local storage using the supplied keys
The goal is to modify it so that a the authentication context from the container is passed down to whatever application is being mounted up, without the need to store the AD client and context information in the app itself
What's required is that every repository within the application would have an API object injected into it, and would use this API when making requests, and the API would contain the authentication context for being able to refresh the Bearer token on each call, and this is where I'm stumped
I had thought about creating an Authentication Singleton that the API could use when being injected into a repository, but since the authenticationContext is unknown at the time, it feels weird to set it after the fact, ie something like
let authContext = new Authentication()
// setup single-spa bootstrap code
// this function is called when the container application bootstraps this Vue application
export const bootstrap = (props) =>
vueLifecycles.bootstrap(props).then(async () => {
authContext.setContext(props.authentication)
})
Then when the API gets intialized, it would have an authContext that would invoke authContext.acquireToken() to get a Bearer token to set for each refresh. But, something feels incorrect about doing this way and I'm not 100% if it's the way to go.
To summarize, applications that were once handling authentication on their own are now being passed the authentication context as a prop during load, and I'm confused about how to implement this to properly set this context as part of the API
Another team had actually stored the acquireToken function in the a Vuex store but that, to me, does not seem appropriate since it ties this code specifically to Vue

Is CASL React library safe for authorization?

I came CASL JavaScript library, which restricts what resources a given client is allowed to access.
My question is whether it can be used for role based access in a React app in a secure way?
And whether user can temper with the permission and gain unauthorized access if used only in front end to display/hide components as shown in following react code?
import React, { useContext } from 'react';
import { AbilityContext } from './Can'
export default () => {
const createTodo = () => { /* logic to show new todo form */ };
const ability = useContext(AbilityContext);
return (
<div>
{ability.can('create', 'Todo') &&
<button onClick={createTodo}>Create Todo</button>}
</div>
);
}
Reference: https://casl.js.org/v5/en/package/casl-react
Tbh, users always can gain access from the frontend side by modifying some javascript code and that is why you must handle the authorization from the backend
about your question for CASL, it only checks if you have the ability to see this page or button or do specific actions ... so the place where you save user abilities is your responsibility, not CASL responsibility
Any code for the client (especially browsers) is publicly available to the user/guest and it can be easily tempered with. Any view/front-end library/framework is used to make user-interface dynamic has to be used only for making it dynamic, not for adding security measures or critical logic. Just like your client code can communicate with an API, any other client may also communicate with it as well (If not, the client code can easily be tempered).
CASL library for React is used only to make the UI dynamic, to be able to hide unnecessary functionality. It has zero effect on securing the application. Anyone who inspects the code can see the "hidden" UI and with changing a few variables, they can access any functionality. So no, CASL or similar libraries cannot make your application secure, it may even give you the false sense of security.
You should secure your application on the API level. Anything unnecessary should be hidden from the currently authenticated user or non-authenticated user (guests). As long as your API endpoints are secure, the fact of anyone can temper with the client code does not create any security risk (as long as security risks like XSS, CSRF are eliminated and the client code does not give much information about the intricacies of the critical logic at the API level).
Libraries like CASL should be only used to improve the user-interface, thus improving the user-experience. If not used, let's say the admin dashboard is visible to any user, but they wouldn't be able to see any data or do any action because the API endpoint won't allow them (authorization on the API level); that would create a confusion in user as they may think this functionality is necessary for them to use your application but somehow there is a problem, or it may signal that their account/data may not be safe as well.

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

Access LocalStorage in Middleware - NuxtJs

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

Categories

Resources