how to i establish socket.io connection only AFTER logging in , REACT + socket.io - reactjs

can i get help with something im stuck on for awhile
im establishing a socket .io connection in my frontend with react context,
but the connection is triggered as soon as my web load, i want it to connect only AFTER i login
im running a jwt auth on my socket connection and what happens is it fails to auth because i dont have a chance to login before it checks it
and then after i log in it wont establish connection untill i manually reload the page...
😦
client:
import React from 'react';
import io from 'socket.io-client'
import { SOCKET_IO_URL } from '../api/config';
const jwt = JSON.parse(localStorage.getItem('profile'))?.token
console.log(jwt)
export const socket = io.connect(SOCKET_IO_URL, {query: {token: jwt}});
export const SocketContext = React.createContext()
server:
io.on('connection', (socket => {
const token = socket.handshake.query.token;
try {
if (!token) throw new Error("TOKEN NOT FOUND")
jwt.verify(token,'secret')
} catch (error) {
console.log('VERIFICATION FAILED')
socket.disconnect()
}
console.log('socket conected', socket.id)
socket.on('request_fetch', () => {
socket.broadcast.emit('response_fetch')
})
}))

You can import the socket / use the socket only in areas where you are logged in.
And lazy import those components
const Dashboard = lazy(() => import('./pages/Dashboard'))
that way the token will always be available when loading those components

According to the docs in the client options, there is an option autoConnect
Client Options
Set this to false as it defaults to true then manually try to connect after you have logged in with socket.connect

Related

SOLVED: Decrypting or unsigning cookies using Express.Request?

I am creating cookies using Express.Response and am having trouble decrypting/unsigning them. Here is how I am creating cookies (this is all happening on the server side so no browser stuff):
In server/index.ts:
import express from 'express'
import Router from 'express-promise-router'
import cookieParser from 'cookie-parser'
import UserRoutes from './routes/UserRoutes'
const app = express() // creating the app
app.use(cookieParser('secret')) // setting the cookie secret
app.use(cors({
origin: true,
credentials: true
})
const router = Router() // creating the router
router.use(UserRoutes) // router using UserRoutes
app.use(router) // app using the router
In UserRoutes:
import Router from 'express-promise-router'
import * as UserController from '../controllers/UserController'
const router = Router()
router.route('/user/login').post(
UserController.loginUser
)
In UserController:
import Express from 'express'
import cookieParser from 'cookie-parser'
import signature from 'cookie-signature'
import { v4 as uuid } from 'uuid'
export const loginUser = async (req: Express.Request, res: Express.Response) => {
// stuff here dealing with username and password - not important
const newToken = uuid() // creating a long random token
console.log(newToken) // a2af2c05-2135-4e9c-9fc0-cc10fb8e5dfb ==> correct cookie value
// creating 'User' cookie with newToken as value
res.cookie('User', newToken, { httpOnly: true, signed: true })
console.log(req.signedCookies.User) // e57583d0-09a4-4a49-98d5-50e966782b8f ==> different because it is 'signed'
// trying decrypting and unsigning the cookie
// this returns the same value as the cookie is not encrypted
console.log(cookieParser.signedCookie(req.signedCookie.User, 'secret')) // e57583d0-09a4-4a49-98d5-50e966782b8f
// this returns 'false', I guess because it is the wrong secret
console.log(signature.unsign(req.signedCookies.User, 'secret')) // false
}
Any idea why the 'User' cookie is not getting decrypted or unsigned? I am using the same secret in app.use(cookieParser('secret')) and when trying to decrypt or unsign.
Thanks for your help
SOLUTION: It turns out that retrieving req.signedCookies.User in the same scope/function in which it was created was causing the problem. I created an authenticate function that gets called before any other route functions, and everything seems to work fine.

Listening to sails socket in react

I'm trying to listen to changes from Sails socket in the front-end using react.
Server listen to changes in a MongoDB Collection and blasts the changes:
// Setting up connection to MongoDB
const RTPEntryDB = sails.getDatastore("RTPEntrydb").manager;
// Switching to the appropriate collection in MongoDB
const profilesCollection = RTPEntryDB.collection("workloads");
// MongoDB Change Stream - Detect changes in DB's collection
const changeStream = profilesCollection.watch();
changeStream.on("change", function(change) {
console.log("new change", change);
sails.sockets.blast('newWorkloads', change);
})
This is working perfectly fine.
But on the front-end, I couldn't listen
import React, { useRef, useReducer, useEffect } from 'react';
import PropTypes from 'prop-types';
import socketIOClient from 'socket.io-client';
import sailsIOClient from 'sails.io.js';
const FetchWorkloads = (props) => {
// Instantiate the socket client (`io`)
const io = sailsIOClient(socketIOClient);
io.sails.url = 'http://localhost:1337/';
io.socket.on('connect', function onConnect(){
console.log('This socket is now connected to the Sails server!');
});
io.socket.on('newWorkloads', (msg) => {
console.log('workloads changed', msg);
});
return (
<>
{/* My Component */}
</>
);
}
FetchWorkloads.propTypes = {
api: PropTypes.object.isRequired
};
export default FetchWorkloads;
I'm getting the error GET http://localhost:1337/__getcookie net::ERR_ABORTED 404 (Not Found). How do I resolve this?
Thanks
I resolved this by defining a custom __getcookie in routes.js as follows:
'GET /__getcookie': (req, res) => {
return res.send('_sailsIoJSConnect();');
},
There was also another issue with the compatibility between sails.io.js and socket.io-client. I was using the latest version of socket.io-client (v4.4) but sails was only compatible with v2.x.

How can one deploy Apollo Server with Create-React-App?

I'm trying to deploy my application to a production environment, but having some trouble wiring it all together.
I've got a create-react-app for my frontend, which is served up by a simple express/serve server. On the backend, I've got NGINX proxying successfully to my API server, which is using Apollo to serve my data. The API is running on port 4000.
The Apollo-Server is as-follows, and works fine:
import { resolve } from "path";
import dotenv from "dotenv";
const envi = process.env.NODE_ENV;
dotenv.config({ path: resolve(__dirname, `../.${envi}.env`) });
import "reflect-metadata";
import { connect } from "./mongodb/connect";
import { buildSchema } from "type-graphql";
import { ApolloServer } from "apollo-server";
import { SenateCommitteeResolver, HouseCommitteeResolver } from "./resolvers";
import { populateDatabase } from "./util";
(async () => {
// Connect to MongoDB
await connect();
console.log(`📊 Databases connected`);
const schema = await buildSchema({
resolvers: [HouseCommitteeResolver, SenateCommitteeResolver],
emitSchemaFile: resolve(__dirname, "schema.gql"),
});
// If development, set database docs
envi === "development" && (await populateDatabase());
// Launch the server!
const server = new ApolloServer({
schema,
playground: true,
});
// Server listens at URL
const { url } = await server.listen(4000);
console.log(`🚀 Server ready, at ${url}`);
})();
I'm trying to connect my express server to the Apollo Server, but that's where I'm running into problems. The application is supposed to connect using Apollo's Client and HTTP Link, because I'm using Apollo Client on the frontend too:
import React, { useEffect } from "react";
import { AppRouter } from "./routers";
import ReactGA from "react-ga";
import { ApolloProvider } from "#apollo/client";
import client from "./graphql/client";
import "./styles/index.scss";
function App(): React.ReactElement {
return (
<ApolloProvider client={client}>
<AppRouter />
</ApolloProvider>
);
}
export default App;
And here's the client file:
import { ApolloClient, InMemoryCache, createHttpLink } from "#apollo/client";
const httpLink = createHttpLink({ uri: process.env.REACT_APP_API as string });
const cache = new InMemoryCache();
const client = new ApolloClient({
link: httpLink,
cache,
connectToDevTools: true,
});
export default client;
However, when the user navigates to the site and the site itself tries to make a request to my backend, I'm getting a CORS error:
Access to fetch at 'https://www.cloture.app/' from origin 'https://cloture.app' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
What's going wrong? How can I connect Apollo's client with my Apollo Server on the backend?
Adding it here, because the suggestion requires some code.
Try adding :
const server = new ApolloServer({
schema,
playground: true,
cors: {
origin: "*" // it will allow any client to access the server, but you can add specific url also
}
});

How to send data from ReactJs to signalR? In Asp.net Mvc Web Api

Im getting a problem when i want to send data from React Component to hub It's Not send...
Note Hub is connected to client But Data Not Send/Recieve
Hub Code
public void SendMessageToAll(string userName, string message)
{
Clients.All.messageReceived(userName, message );
}
React Js Code:
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import { hubConnection } from 'signalr-no-jquery';
class Dashboard extends Component {
constructor(props) {
super(props);
const connection = hubConnection('https://localhost:44332');
const hubProxy = connection.createHubProxy('Chat');
hubProxy.on('Hello', function(name) {
console.log(name);
});
// atempt connection, and handle errors
connection.start()
.done(function(){ console.log('Now connected, connection ID=' + connection.id); })
.fail(function(){ console.log('Could not connect'); });
}
render() {
return (
<div/>
);
}
}
export default Dashboard;
Generally, when using SignalR, we will create the ChatHub.cs, which contains the send/receive method. In the client side, we will declare a proxy to reference the hub and connect the hub, then, we can call the hub's send/receive method to transfer the message. But from your code, I didn't find where you call the "SendMessageToAll()" method, so the message will not send. Please check your code.
Here are some tutorials about using SignalR, you can check them:
Chat Application Using ASP.NET, ReactJS, Web API, SignalR and Gulp
Tutorial: Real-time chat with SignalR 2

How to connect socket.io with a Heroku-deployed React Native app?

I have almost googled my fingers off trying to figure this out. It seems a lot of the existing info on connecting socket.io with React Native is outdated, or maybe I'm just interpreting things wrong?
I've managed to get the client-side connected (I'm getting the client console logs when I connect to my app). It seems to be the server-side that's giving me issues. Why is the data being emitted from the client not showing up as a log in my terminal? None of the related console.logs in my server.js are logging but the App.js console.logs are registering.
Edit: Here is my full App.js file:
import Expo from 'expo';
import React from 'react';
import { Dimensions, StyleSheet, Text, View } from 'react-native';
import store from './src/store';
import { Provider } from 'react-redux';
// window.navigator.useragent = 'react-native'; -> not necessary anymore?
const ROOT_URL = 'https://myherokudomain.herokuapp.com';
const io = require('socket.io-client/dist/socket.io');
const socket = io.connect(ROOT_URL);
socket.on('connect', () => {
console.log('Connected to server');
});
socket.on('example', (data) => {
console.log(data);
socket.emit('my other event', { my: 'data' });
});
socket.on('disconnect', () => {
console.log('Disconnected from server');
});
export default class App extends React.Component {
render() {
// const MainNavigator = my react-navigation system
return (
<Provider store={store}>
<View style={styles.container}>
<MainNavigator />
</View>
</Provider>
);
}
}
Edit: Here is my full server.js file:
const config = require('./config/config');
const { mongoose } = require('./db/mongoose');
const express = require('express');
const cors = require('cors');
const app = express();
const port = process.env.PORT;
// ************ Include and use separate routes file
app.use(require('./routes/routes'));
// ************
//Cross-Origin resource sharing. cors library solves CORS problems.
app.use(cors());
//***********
/* Chat server code*/
// enabled heroku session affinity:
// see https://devcenter.heroku.com/articles/session-affinity
// to enable: heroku features:enable http-session-affinity
// to diable: heroku features:disable http-session-affinity
const socketIO = require('socket.io');
const http = require('http');
const server = http.createServer(app);
const io = socketIO(server, { origin: "*:*" });
//********** */
io.on('connection', (socket) => {
console.log('A client just joined', socket.id);
socket.emit('example', { hello: 'world' });
socket.on('my other event', (data) => {
console.log(data);
});
socket.on('disconnect', () => {
console.log('User was disconnected');
});
});
server.listen(port, (err) => {
console.log(`started on port ${port}`);
});
module.exports = { app };
I am getting the console logs on the client side just fine (for instance, the "connected to server" and "hello: world" stuff is showing up when I open my app on expo. But I am not getting the server-side console logs.
What am I doing wrong - how do I get socket.io fully working with a deployed React-Native app?
I would really appreciate any help at all! I've been stuck on this forever.
I'm assuming all the code works, just not the logging since that's all you're asking about. The problem is Node doesn't output to your browser's console.
If it's deployed on heroku then you should see everything being logged there, otherwise you can use libraries like https://github.com/node-inspector/node-inspector to output to your browser.
You're not getting the server-side console logs because 1.) They're only logging on the server, and 2.) You're not emitting them, if you do actually want to send the data back.

Resources