req.session with different domains node.js - node.js

I'm building an app using next.js and node.js (express).
The client run on localhost:4000, and the server run on lovalhost:3000.
to communicate between the two domains Iwm using cors().
to authorization i'm using with cookieSession and jwt.
app.use(
cookieSession({
signed: false,
secure: false,
sameSite: false,
})
);
when user login i put on the session a jwt.
req.session = {
jwt:userJwt
};
this time the user needs to be identified by the cookie.
this works nice in postman environment, when a user sign in he is identified, the cookie with his jwt is saved.
but when i try to make the same request via the client unforteantually the cookie is not saved.
the client code:
const signin= () => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const {doRequest, errors} = useRequest({
url: 'http://localhost:4000/api/auth/signin',
method: 'post',
body: {
email,password
},
onSuccess: () => Router.push('/')
});
the useRequest hook:
import axios from 'axios';
import { useState } from 'react';
const useRequest= ({url, method, body, onSuccess}) => {
const [errors, setErrors] = useState(null);
const doRequest =async () => {
try {
setErrors(null);
console.log(url);
const response = await axios[method](url,body);
console.log(response);
if(onSuccess) {
onSuccess(response.data);
}
return response.data;
} catch (error) {
console.log(error);
setErrors(
<div className= "alert alert-danger" >
<h4>Ooops...</h4>
{error.response.data ?
<ul className="my-0">
{error.response.data.errors.map(err => (
<li key = {err.message}> {err.message}</li>
))}
</ul>:{}
}
</div>
);
}
};
return { doRequest,errors }
};
export default useRequest;
Maybe because the diffrent domains the session is not the same?
How can I slove it?
My app
I hope I was clearly.
thank you!
I'm using process.env variables

Related

Cookies doesn't show up in my application ReactJS

Hello i'm trying to code auth for my app i'm using json web token the problem is when i send post request using postman i can see the cookie and access token in headers but in my application i can't see anything in my localstorage&cookies
Here is my code
authContext.js
import axios from "axios";
import { createContext, useEffect, useState } from "react";
export const AuthContext = createContext();
export const AuthContextProvider = ({ children }) => {
const [currentUser, setCurrentUser] = useState(
JSON.parse(localStorage.getItem("user")) || null
);
const login = async (inputs) => {
const res = await axios.post("http://localhost:8800/api/auth/login", inputs, {
withCredentials: true,
});
setCurrentUser(res.data)
};
useEffect(() => {
localStorage.setItem("user", JSON.stringify(currentUser));
console.log(currentUser);
}, [currentUser]);
return (
<AuthContext.Provider value={{ currentUser, login }}>
{children}
</AuthContext.Provider>
);
};
in login.jsx
const [inputs, setInputs] = useState({
username: "",
password: "",
});
const [err, setErr] = useState(null);
const navigate = useNavigate()
const handleChange = (e) => {
setInputs((prev) => ({ ...prev, [e.target.name]: e.target.value }));
};
const login = useContext(AuthContext);
const handleLogin = async (e) => {
e.preventDefault();
try {
await login(inputs);
navigate("/")
} catch (err) {
setErr(err.response.data);
}
};
console.log(err);
console.log(inputs);
I'm trying to solve the problem because i'm trying to create a basic social app i need accessToken to display posts in my feed easily

401 error in axios post request to local server

Context
I'm building a simple web application using the MERN stack for practice. In the app, logged-in users should be able to add a new blog to the site. However, for some reason my axios post request to the backend is failing and I'm receiving a 401 error. I'm using jsonwebtoken to handle the authentication. Submitting a POST request via Insomnia works fine so I don't believe it's an issue with my endpoint. I'm running backend server locally on my machine on port 3003 and have set up a proxy so there's no issues with cors. This works fine as the blogs from the backend are displays on the frontend once a user has logged in.
I've also checked the headers and can confirm that logged-in users have a valid bearer token.
What could be causing the issue?
Frontend
I can't post any images but here's a link to the frontend view:
https://imgur.com/a/DdUlfg9
App.js
import React, { useState, useEffect } from 'react'
import Blog from './components/Blog'
import blogService from './services/blogs'
import loginService from './services/login'
import LoginForm from './components/loginForm'
import BlogForm from './components/blogForm'
const App = () => {
const [blogs, setBlogs] = useState([])
const [username, setUsername] = useState('')
const [password, setPassword] = useState('')
const [user, setUser] = useState(null)
const [errorMessage, setErrorMessage] = useState(null)
const [newBlog, setNewBlog] = useState({
title: '',
author: '',
url: ''
})
useEffect(() => {
blogService.getAll().then(blogs =>
setBlogs( blogs )
)
}, [])
useEffect(() => {
const loggedInUser = window.localStorage.getItem("loggedBlogUser")
if(loggedInUser){
const user = JSON.parse(loggedInUser)
setUser(user)
}
},[])
const handleLogin = async (event) => {
event.preventDefault()
try {
const user = await loginService.login({
username, password
})
window.localStorage.setItem(
'loggedBlogUser', JSON.stringify(user)
)
blogService.setToken(user.token)
setUser(user)
setUsername('')
setPassword('')
} catch (exception){
setErrorMessage('Wrong credentials')
setTimeout(() => {
setErrorMessage(null)
}, 5000)
}
}
const handleLogout = async (event) => {
event.preventDefault()
if(user){
window.localStorage.removeItem("loggedBlogUser")
setUser(null)
}
}
const handleBlogField = (event) => {
event.preventDefault()
const {name, value} = event.target
console.log(newBlog.title)
setNewBlog(prevBlog => ({
...prevBlog,
[name] : value
}))
}
const addBlog = async (event) => {
event.preventDefault()
try {
const blog = await blogService.create(newBlog)
console.log("POST REQUEST: ",newBlog)
console.log('lets geddit')
setBlogs(blogs.concat(blog))
} catch (exception){
setErrorMessage('Uh oh, try again :[')
setTimeout(() => {
setErrorMessage(null)
}, 5000)
}
}
if(user === null){
return(
<>
{errorMessage}
<h2>Log into application</h2>
<LoginForm handleLogin={handleLogin} setUsername={setUsername} setPassword={setPassword} username={username} password={password}/>
</>
)
}
return (
<div>
<h2>blogs</h2>
{user &&
<div>
<h3>{user.username} logged in</h3>
<button onClick={handleLogout}>Logout</button>
</div>
}
<BlogForm handleSubmission={addBlog} newBlog={newBlog} handleBlogField={setNewBlog}/>
{/* <BlogForm addBlog={addBlog} title={newBlog.title} setTitle={setTitle} setAuthor={setAuthor} author={newBlog.author} url={newBlog.url} setUrl={setUrl}/> */}
{blogs.map(blog =>
<Blog key={blog.id} blog={blog} />
)}
</div>
)
}
export default App
Blogs.js
import axios from 'axios'
const baseUrl = '/api/blogs'
let token = null
//let config
const setToken = (newToken) => {
token = `bearer ${newToken}`
}
const getAll = async () => {
const response = await axios.get(baseUrl)
return response.data
}
const create = async (newObject) => {
const config = {
headers: {
Authorization: token
}
}
const response = await axios.post(baseUrl, newObject, config)
console.log(`RESPONSE: ${newObject}`)
return response.data
}
const blogService = {
getAll, setToken, create
}
export default blogService
Have you configured CORS?, in order to accept your localhost requests?

Expressjs: google-auth-libary id-token verification "token used too late number > number..."

I have tried to add google auth login for my reactjs/expressjs web app. On the frontend I am using the react-google-login package:
import React from "react"
import { GoogleLogin } from "react-google-login"
import { useHistory } from "react-router-dom"
const axios = require('axios')
export default function LoginButton() {
let history = useHistory()
const { REACT_APP_GOOGLE_CLIENT_ID, REACT_APP_API_URL } = process.env
const onSuccess = async (response) => {
console.log(response)
const data = { token: response.tokenId }
const res = await axios.post(REACT_APP_API_URL + "/auth/google", data, {
'Content-Type' : 'text/json'
})
.then((res) => {
history.push('/home')
})
.catch((err) => {
console.log("[LOGIN FAILED]")
})
}
const onFailure = (response) => {
console.log(response)
}
return(
<GoogleLogin
clientId={REACT_APP_GOOGLE_CLIENT_ID}
buttonText="Log in with Google"
onSuccess={onSuccess}
onFailure={onFailure}
cookiePolicy={'single_host_origin'}
/>
)
}
From what it seems I am getting the correct data from this button. I am sending the token to the expressjs api. This is where I am having issues. When using google-auth-library to verify the token I am getting the error: "Error: Token used too late, number_1 > number_2". As far as I know the idToken has expired, but isn't it weird considering I sent it as soon as possible from the frontend. Backend code:
const { OAuth2Client } = require('google-auth-library')
require('dotenv').config()
const client = new OAuth2Client(process.env.CLIENT_ID)
const postGoogleLogin = async (req, res) => {
const { token } = req.body
try{
const ticket = await client.verifyIdToken({
idToken: token,
audience: process.env.CLIENT_ID
})
const { name, email, picture } = ticket.getPayload()
res.status(201).json({
name: name,
email: email,
picture: picture
})
}
catch(e){
console.log(e)
}
}
I have tried to verify the token using the endpoint https://oauth2.googleapis.com/tokeninfo?id_token=XYZ123 which says the token is valid, but as far as I know this endpoint should not be used in production
The issue appears to be from the library itself. As long as you're using the same client id on both React and ExpressJs, verifyIdToken should return a success response as long as the token is still valid.
Also, you can make use of https://oauth2.googleapis.com/tokeninfo?id_token=XYZ123 in your production code.
Internally, the library call the same endpoint to verify your token.

How to use JWT token with axios on React Native

I did a REST api using node.js and express, I started trying to build user authentication so I used JWT. Now, I'm trying to build a React Native application, but keep getting Error 401 Access Denied. Here's where I receive the JWT token:
...
import api from '../services/api';
export default function Login({ navigation }) {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
useEffect(() => {
AsyncStorage.getItem('authToken').then(authToken => {
if (authToken) {
navigation.navigate('Dashboard');
}
});
}, []);
async function handleSubmit() {
try {
const response = await api.post('/login', {
email,
password
});
const authToken = response.data;
await AsyncStorage.setItem('authToken', response.data);
if (authToken) navigation.navigate('Dashboard');
} catch (err) {
}
}
...
Here's where I need authentication, but I can't seem find out how to send authToken and get response from the api.
...
import api from '../services/api';
export default function List({ navigation }) {
useEffect(() => {
AsyncStorage.getItem('authToken').then(authToken => {
if (!authToken) {
navigation.navigate('Login');
}
});
}, []);
async function handleSubmit() {
const response = await api.post('/tasks', {
"description": "Finish this shit",
"priority": 3,
"completed": false
});
console.log(response.data);
}
...

Express server posting twice, exactly two minutes apart?

I am trying to build a react chat box using an express server and pusher to listen for a dialogflow bot. It works fine at first, but the bot always responds a second time, repeating itself (but sometimes with a different response) exactly two minutes later to the second.
I have some logging statements in the server code to try and debug it, and have been monitoring the react front-end for network activity. It appears that react is only sending one fetch request, because there is only one network log in the browser. But on the server-side, the request is logged twice. I'm not sure why this is or what I'm doing wrong!
// server.js
require("dotenv").config({ path: "variables.env" });
const express = require("express");
const bodyParser = require("body-parser");
const cors = require("cors");
const processMessage = require("./process-message");
const app = express();
app.use(cors());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.post("/chat", (req, res) => {
const { message } = req.body;
processMessage(message);
console.log(message);
});
app.set("port", process.env.PORT || 5000);
const server = app.listen(app.get("port"), () => {
console.log(`Express running → PORT ${server.address().port}`);
});
//process-message.js
const Dialogflow = require("dialogflow");
const Pusher = require("pusher");
const projectID = "firstchatbox-fakeURL";
const sessionID = "123456";
const languageCode = "en-US";
const config = {
credentials: {
private_key: process.env.DIALOGFLOW_PRIVATE_KEY,
client_email: process.env.DIALOGFLOW_CLIENT_EMAIL
}
};
const pusher = new Pusher({
appId: process.env.PUSHER_APP_ID,
key: process.env.PUSHER_APP_KEY,
secret: process.env.PUSHER_APP_SECRET,
cluster: process.env.PUSHER_APP_CLUSTER,
encrypted: true
});
const sessionClient = new Dialogflow.SessionsClient(config);
const sessionPath = sessionClient.sessionPath(projectID, sessionID);
const processMessage = message => {
const request = {
session: sessionPath,
queryInput: {
text: {
text: message,
languageCode
}
}
};
sessionClient
.detectIntent(request)
.then(responses => {
console.log(responses);
const result = responses[0].queryResult;
return pusher.trigger("bot", "bot-response", {
message: result.fulfillmentText
});
})
.catch(err => {
console.error("ERROR:", err);
});
};
module.exports = processMessage;
// Here is the React front-end code, even though i'm ~60% sure
//the bug is server-side at this point
//App.js
import React, { Component } from "react";
import Pusher from "pusher-js";
import "./App.css";
class App extends Component {
constructor(props) {
super(props);
this.state = {
userMessage: "",
conversation: []
};
}
componentDidMount() {
const pusher = new Pusher("fakepusherappID454564564566", {
cluster: "us3"
});
const channel = pusher.subscribe("bot");
channel.bind("bot-response", data => {
const msg = {
text: data.message,
user: "ai"
};
this.setState({
conversation: [...this.state.conversation, msg]
});
});
}
handleChange = event => {
this.setState({ userMessage: event.target.value });
};
handleSubmit = event => {
event.preventDefault();
if (!this.state.userMessage.trim()) return;
const msg = {
text: this.state.userMessage,
user: "human"
};
this.setState({
conversation: [...this.state.conversation, msg]
});
fetch("http://localhost:5000/chat", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
message: this.state.userMessage
})
})
.then(d => console.log(d))
.catch(e => console.log(e));
this.setState({ userMessage: "" });
};
render() {
const ChatBubble = (text, i, className) => {
return (
<div key={`${className}-${i}`} className={`${className} chat-bubble`}>
<span className="chat-content">{text}</span>
</div>
);
};
const chat = this.state.conversation.map((e, index) =>
ChatBubble(e.text, index, e.user)
);
return (
<div>
<h1>React Chatbot</h1>
<div className="chat-window">
<div className="conversation-view">{chat}</div>
<div className="message-box">
<form onSubmit={this.handleSubmit}>
<input
value={this.state.userMessage}
onInput={this.handleChange}
onChange={this.handleChange}
className="text-input"
type="text"
autoFocus
placeholder="Type your message and hit Enter to send"
/>
</form>
</div>
</div>
</div>
);
}
}
export default App;
It shows occasional console errors:
Source map error: TypeError: NetworkError when attempting to fetch resource.
Resource URL: http://localhost:3000/static/js/0.chunk.js
Source Map URL: 0.chunk.js.map
but I don't think they are relevant?
Exactly two minutes apart sounds like it could be a browser request that timed out and the browser is retrying.
And, now that I look at your app.post() handler, you don't seem to send any sort of response back to the client so that could cause an issue like this.
All http requests received by your server must send some sort of response, even if they just send a 404 or 500 status back. Either do res.sendStatus(200) or res.send("some response");.
You can open the debugger in Chrome and look at the network tab and then submit your form and watch exactly what network traffic happens between client and server.
And, since you tried this and it fixed your problem, I'm posting it as an answer.

Resources