How to prevent re-render when using sockets on React? - javascript

I have this problem with my code, it likes to re-render and reconnect to the server multiple times, and I'd like to have it so it'll connect when they log in, meaning that it would have to use my "useAuth()" function which uses a different context.
Is there any way to prevent the re-render so it doesn't reconnect multiple times and only connects once?
This is my code:
import React, { useEffect } from "react";
import { useAuth } from "./AuthContext";
import { io } from "socket.io-client";
const SocketContext = React.createContext({});
export const SocketProvider = ({ children }) => {
const { isAuthenticated, user } = useAuth();
useEffect(() => {
if (isAuthenticated) {
const socket = io("ws://localhost:3002", {
reconnectionDelayMax: 10000,
auth: {
token: user.socketAuth,
},
});
socket.on("connect_error", (err) => {
if (err instanceof Error) {
console.log(err.message);
console.log(err.data);
}
});
}
}, []);
return <SocketContext.Provider value="">{children}</SocketContext.Provider>;
};

Never mind, I fixed the issue, it was working how it was supposed to be. It was the server that wasn't accepting the connection and it just kept logging the ID of the socket and not actually letting it connect.
io.use(async (socket, next) => {
const user = await User.findOne({ socketAuth: socket.handshake.auth.token });
if (!user) {
const err = new Error("Unauthorized");
err.data = { message: "Unauthorized, please try again later." };
next(err);
}
next(); // New line added to allow connection
});

Related

cognitoUser.refreshSession keeps refreshing user login

Im trying to refresh the jwt token with cognitoUser.refreshSession, everytime I log in it simply refreshes the login page and asks me to sign in again
I have written up some code from various examples I have found but still struggle to get it going. If anyone has any suggestions I would welcome them:
Code as follows:
import axios from 'axios';
import { Auth } from 'aws-amplify';
const httpClient = axios.create(
{ baseURL: process.env.VUE_APP_API_BASE_URL }
);
const refreshToken = async function() {
try {
const cognitoUser = await Auth.currentAuthenticatedUser();
const currentSession = await Auth.currentSession();
cognitoUser.refreshSession(currentSession.refreshToken, (err, session) => {
console.log('session', err, session);
return Auth.user.signInUserSession.idToken.jwtToken
});
} catch (e) {
console.log('Unable to refresh Token', e);
}
}
httpClient.interceptors.request.use(
config => {
const idToken = refreshToken()
config.headers.Authorization = idToken;
return config
}, error => Promise.reject(error))
export default httpClient;

Where to set Sentry's setUser in Next.js app?

I have been trying to set user data into Sentry's scope globally, so every time there's an error or event, user info is passed to it.
My app is built in Next.js, so naturally I added the config as it is in Sentry's documentation for Next.js.
I haven't got the idea on where to add the Sentry.setUser({id: user.Id}) method in order for it to set the user globally.
So far I have added it to the Sentry's _error.js file, inside the getInitialProps method:
import NextErrorComponent from 'next/error';
import * as Sentry from '#sentry/nextjs';
import { getUser } from '../lib/session';
const MyError = ({ statusCode, hasGetInitialPropsRun, err }) => {
if (!hasGetInitialPropsRun && err) {
Sentry.captureException(err);
}
return <NextErrorComponent statusCode={statusCode} />;
};
MyError.getInitialProps = async (context) => {
const errorInitialProps = await NextErrorComponent.getInitialProps(context);
const { req, res, err, asPath } = context;
errorInitialProps.hasGetInitialPropsRun = true;
const user = await getUser(req, res);
// Set user information
if (user) {
console.log('Setting user');
Sentry.setUser({ id: user.Id });
}
else {
console.log('Removing user');
Sentry.configureScope(scope => scope.setUser(null));
}
if (res?.statusCode === 404) {
return errorInitialProps;
}
if (err) {
Sentry.captureException(err);
await Sentry.flush(2000);
return errorInitialProps;
}
Sentry.captureException(
new Error(`_error.js getInitialProps missing data at path: ${asPath}`),
);
await Sentry.flush(2000);
return errorInitialProps;
};
export default MyError;
But when trying to log errors, the user info doesn't show in Sentry, only the default user ip:
I have also tried setting the user after successful login, and still nothing..
Help is appreciated!!
Not sure if this is the right way, but the above solutions didn't work for me. So I tried calling setUser inside _app.tsx.
import { useEffect } from "react";
import { setUser } from "#sentry/nextjs";
import { UserProvider, useUser } from "#auth0/nextjs-auth0";
import type { AppProps } from "next/app";
function SentryUserManager() {
const { user } = useUser();
useEffect(() => {
if (user) {
setUser({
email: user.email ?? undefined,
username: user.name ?? undefined,
});
} else {
setUser(null);
}
}, [user]);
return null;
}
export default function MyApp({ Component, pageProps }: AppProps) {
return (
<UserProvider>
<Component {...pageProps} />
<SentryUserManager />
</UserProvider>
);
}
Still not sure why this worked for me and the other solutions didn't, but figured it was worth sharing.
I would suggest using the callback handler to set your Sentry user context.
import { handleAuth, handleLogin, handleCallback } from "#auth0/nextjs-auth0";
import * as Sentry from "#sentry/nextjs";
import { NextApiHandler } from "next";
const afterCallback = (_req, _res, session, _state) => {
Sentry.setUser({
id: session.user.sub,
email: session.user.email,
username: session.user.nickname,
name: session.user.name,
avatar: session.user.picture,
});
return session;
};
const handler: NextApiHandler = handleAuth({
async login(req, res) {
await handleLogin(req, res, {
returnTo: "/dashboard",
});
},
async callback(req, res) {
try {
await handleCallback(req, res, { afterCallback });
} catch (error) {
res.status(error.status || 500).end(error.message);
}
},
});
export default Sentry.withSentry(handler);
You can set the user in Sentry right after successful login
const handleLogin = {
try {
const res = await axios.post("/login", {"john#example.com", "password"})
if (res && res?.data) {
// Do other stuff
Sentry.setUser({ email: "john#example.com" });
}
}
}
Additionaly you can clear the user while logging out
const handleLogout = {
// Do othe stuff
Sentry.configureScope(scope => scope.setUser(null));
}

next.js & next-auth When I send http request in getServerSideProps, getSession returns null in secured API Route

I am trying to secure the API Route and this API route is called in the Client and Server-side on different pages.
On the test page, it returns 401 error.
On the test2 page, it returns the content well.
I guess it doesn't pass session when I send the http request in the getServerSideProps.
My question is, how do I secure the API routes used on the client and server-side?
/pages/test
import React from 'react';
import axios from 'axios';
import { getSession } from 'next-auth/react';
const Test = (props) => {
return <div>test</div>;
};
export const getServerSideProps = async (context) => {
// it returns session data
const session = await getSession(context);
// it returns error
const res = await axios.get('/api/secret');
return {
props: {
session,
secret: res.data,
},
};
};
export default Test;
/pages/test2
import React, { useEffect } from 'react';
import axios from 'axios';
import { useSession, getSession } from 'next-auth/react';
const Test = (props) => {
const { data: session } = useSession();
useEffect(() => {
const fetchData = async () => {
const res = await axios.get('/api/secret');
console.log(res.data);
};
fetchData();
}, [session]);
return <div>test</div>;
};
export default Test;
/pages/api/secret
import { getSession } from 'next-auth/react';
const handler = (req, res) => {
const { method } = req;
switch (method) {
case 'GET':
return getSomething(req, res);
default:
return res.status(405).json('Method not allowed');
}
};
const getSomething = async (req, res) => {
const session = await getSession({ req });
console.log(session);
if (session) {
res.send({
content: 'Welcome to the secret page',
});
} else {
res.status(401).send({
err: 'You need to be signed in.',
});
}
};
export default handler;
I found a solution.
export const getServerSideProps = async (ctx) => {
const session = await getSession(ctx);
const headers = ctx.req.headers;
if (session) {
const data = (
await axios.get(`${process.env.NEXTAUTH_URL}/api/secret`, {
headers: { Cookie: headers.cookie },
})
return {
props: {
data,
},
};
} else {
return {
redirect: {
destination: '/login',
permanent: false,
},
};
}
};
/pages/api/secret
import { getSession } from 'next-auth/react';
const handler = async (req, res) => {
const { method } = req;
switch (method) {
case 'GET':
return await getSomething(req, res);
default:
return res.status(405).json('Method not allowed');
}
};
const getSomething = async (req, res) => {
const session = await getSession({ req });
// console.log(session);
if (session) {
res.send({
content: 'Welcome to the secret page',
});
} else {
res.status(401).send({
err: 'You need to be signed in.',
});
}
};
export default handler;
There is a specific method to handle request from serverSideProps, better than using useSession (which is meant for client requests)
https://next-auth.js.org/tutorials/securing-pages-and-api-routes#server-side
Best use unstable_getServerSession as mentioned in the documentation example
await unstable_getServerSession(req, res, authOptions)
with the authOptions coming as an export from your [...nextauth].js

How to do e2e test in nestjs using mock data

I have an api in controller of nestjs.
First it checks wheter there is a center info or no.
After that, it makes a client.
#Post('/client')
public async createClient(
#Body() createClientDto: CreateClientDto,
#Req() req,
#Res() res,
) {
const { centerId } = createClientDto;
let center = null;
try {
center = await this.centersReadService.findCenter(centerId);
} catch (err) {
throw new HttpException(
{
errorLog: err.message,
},
500,
);
}
if (!center) {
throw new HttpException(
{
popup: 'There is no center"
},
500,
);
}
await this.clientCreateService.createClient(createClientDto)
return res.json({})
}
I made e2e test about this api.
It runs on 'docker-compose', so this app-test is running on localhost:20000.
I hope that it dosen't run new app with new port.
In this test, I want to get 'center=null' by findCenter method regardless of 'centerId' of 'createClientDto'.
Is is possible that some methods return fixed value using e2e test?
import { INestApplication } from '#nestjs/common';
import * as request from 'supertest';
import { t_CreateClientDto } from './mock-data';
describe('Masters Clients Post (e2e)', () => {
let app: INestApplication;
const testUrl = 'http://localhost:20000';
it('can't create a client since there is no center.', () => {
return request(testUrl)
.post('/users/v1/masters/client')
.send(t_CreateClientDto)
.expect('Content-Type', /json/)
.expect((res) => {
expect(res.body.popup).toBe('There is no center');
})
.expect(500);
});
});

NuxtServerInit sets Vuex auth state after reload

I'm setting a basic authentication on a Nuxt project with JWT token and cookies to be parsed by nuxtServerInit function.
On login with email/password, works as intended, setUser mutation is triggered and the appropriate user object is stored in state.auth.user.
On reload, nuxtServerInit will get the jwt token from req.headers.cookies, call the GET method and identify user.Works like a charm.
Problem starts when I hit the /logout endpoint. state.auth.user is set to false and Im effectively logged out... but If I refresh, I'm logged in again with the previous user data. Even if my cookies are properly empty (on below code, both user and cookie are undefined after logout and refresh, as expected)
So I really don't get why is my state.auth.user is back to its initial value...
store/index.js
import Vuex from "vuex";
import auth from "./modules/auth";
import axios from "~/plugins/axios";
const cookieparser = process.server ? require("cookieparser") : undefined;
const END_POINT = "api/users";
const createStore = () => {
return new Vuex.Store({
actions: {
async nuxtServerInit({ commit, dispatch}, { req }) {
let cookie = null;
console.log(req.headers.cookie)
if (req.headers.cookie) {
const parsed = cookieparser.parse(req.headers.cookie);
try {
cookie = JSON.parse(parsed.auth);
console.log("cookie", cookie)
const {accessToken} = cookie
const config = {
headers: {
Authorization: `Bearer ${accessToken}`
}
}
const response = await axios.get(`${END_POINT}/current`, config)
const user = response.data
console.log("user nuxt server init", user)
await commit('setUser', user)
} catch (err) {
// No valid cookie found
console.log(err);
}
}
}
},
modules: {
auth
}
});
};
export default createStore;
modules/auth.js
import axios from "~/plugins/axios";
const Cookie = process.client ? require("js-cookie") : undefined;
const END_POINT = "api/users";
export default {
state: {
user: null,
errors: {}
},
getters: {
isAuth: state => !!state.user
},
actions: {
login({ commit }, payload) {
axios
.post(`${END_POINT}/login`, payload)
.then(({ data }) => {
const { user, accessToken } = data;
const auth = { accessToken };
Cookie.set("auth", auth);
commit("setUser", user);
})
.catch(e => {
const error = e;
console.log(e);
commit("setError", error);
});
},
logout({ commit }) {
axios
.post(`${END_POINT}/logout`)
.then(({ data }) => {
Cookie.remove("auth");
commit("setUser", false);
})
.catch(e => console.log(e));
},
},
mutations: {
setUser(state, user) {
state.user = user;
},
setError(state, errors) {
state.errors = errors;
}
}
};
The way I logout my user is by creating a mutation called clearToken and commit to it in the action :
State :
token: null,
Mutations :
clearToken(state) {
state.token = null
},
Actions :
logout(context) {
context.commit('clearToken')
Cookie.remove('token')
}
This way, you token state revert back to null.

Categories

Resources