Redirect after login using passport js - javascript

I have an aplication where i used react and node js. Also i want to create authentification feature. Register page is working ok, i can get data in node js from fron end. But appears a problem when i try to log in. For log in, in node js, i use passport.js:
app.post('/login', checkNotAuthenticated, passport.authenticate('local', {
successRedirect: '/',
failureRedirect: '/login',
failureFlash: true
}
));
Also i have the Login component in reactjs:
const Login = () => {
const [name, setName] = useState('')
const [password, setPassword] = useState('')
const nameChange = (e) => {
const val = e.target.value;
setName(val)
}
const passwordChange = (e) => {
const val = e.target.value;
setPassword(val)
}
const register = (e) => {
e.preventDefault()
console.log('login: ' + name + password)
const data = {
name: name,
password: password
};
const requestOptions = {
method: 'post',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify( {name:name,password: password})
};
fetch('http://localhost:4000/login', requestOptions)
.then((response) => response.json())
.then((messages) => {console.log(messages);});
}
return (
< div>
<h1>Login</h1>
<form onSubmit={register}>
<input value={name} onChange={nameChange} type="text" placeholder='name' name='name'/>
<input value={password} onChange={passwordChange} type="text" placeholder='password' name='password'/>
<input type="submit" value='Login'/>
</form>
</div>
)
;
};
export default Login;
The question is, how to connect front end with node js? How to check when the user is loged with success?

You can use session, something like redis to store user's information in server-side and set a cookie in your client side.
In this way, you should generate a random key and store user's data in redis with that.
'hashed-key' : {//user info in redis}
On every request to server, your client side app send the hash-key with cookie, Nodejs app will check the redis for existence of that key and if everything is OK, it will process the request.
In client side you create a state for your app: member or visitor.
It is a good document for the whole idea "Managing Node.js - Express Sessions with Redis"
It is "Create basic login forms using create react app module in reactjs"
And also read "All You Need to Know About User Session Security"

If you use Express server with ReactJS, you don't have to use redirect option when call passport.authenticate.
Because each environment is different.
So if you redirect to "/" in Express, it will be redirect app.get("/)
you can try like this
app.post('/login', checkNotAuthenticated, (req, res, next) => {
passport.authenticate('local', (err, user) => {
if(err) console.error(err);
req.login(user, loginErr => {
if(loginErr) {
console.error(err);
}
return res.json(user);
});
})(req, res);
});
Then, request from React !
fetch('http://localhost:4000/login', requestOptions)
.then((response) => response.json())
.then((user) => { console.log(user); }); // <- Now you get a user!
.catch(err => console.error(err)); // <- If failed login
if you want to route whether user logged in
try like this
const Router = () => {
const [user, setUser] = useState(null);
// when login request success, setUser(user);
return user ? <Main /> : <Login />;
}

Related

How to get token from sessionStorage in node before serving react static files

I have created a login page which is a react webpage. The purpose of the page is to login a user. If the user is successfully logged in using a user id and a password, a jwt token is returned by the server to the login page. The current login pages saves the returned jwt to sessionStorage. I have used passport.js with jwt authentication method.
I have an admin page. I want that page to be viewed only if the user is authenticated. This is another react web app which is separate from the one above. I want the react static files for admin area to be served only if a user is authenticated or logged in first.
Now my problem is how do I send the token from session storage on the client side to the server side. Session storage can not be accessed by the server side.
I want to reiterate my login page and admin page are separate react components.
Here is my server side code: -
app.get('/admin', passport.authenticate('jwt', {session: false, failureRedirect: '/login'}),(req, res) => {
//Send admin static files
....
})
My login: -
app.post("/login", (req, res) => {
//Add sanitization function later
const userID = req.body.user;
const password = req.body.password;
//check if user credentials are correct, then return json token
if (verifyCredentials(userID, password)){
let token = generateJSONToken(userID);
res.json({id: userID, token}).status(200);
} else {
res.json({error: "Wrong username or password"})
}
})
My login page: -
const LoginArea = () => {
const loginInput = useRef();
const passwordInput = useRef();
//shows error message
const [errorMessage, setErrorMessage] = useState("");
//Submit login form to the server
const submitLogin = async e => {
e.preventDefault();
const loginResult = await login(loginInput.current.value, passwordInput.current.value);
if (loginResult.data.error){
setErrorMessage(loginResult.data.error);
setTimeout(() => {
setErrorMessage("");
}, 5000);
}
if (loginResult.data.token){
storeTokenOnUserside(loginResult.data.token)
}
}
return (
<div className={styles.container}>
<Box>
{errorMessage}
<FormControl isRequired>
<FormLabel>Login Id:</FormLabel>
<Input ref={loginInput} type="text" placeholder="Login ID"/>
</FormControl>
<FormControl isRequired >
<FormLabel>Password:</FormLabel>
<Input ref={passwordInput} type="password" placeholder="Password" />
</FormControl>
<FormControl isRequired>
<Button type="submit" onClick={(e) => submitLogin(e)}>
Login
</Button>
</FormControl>
</Box>
</div>
);
};
export default LoginArea;

Spotify node web api - logs out immediately after logging in

I am trying to do Spotify Authentication using Client(React) and Server, the logging in works for a second then the page refreshes immediately after logging in and logs the user out. Anyone knows where might be the problem?
Here is my code:
server.js:
require('dotenv').config();
const express = require('express');
const cors = require('cors');
const SpotifyWebApi = require('spotify-web-api-node');
const app = express();
app.use(cors()) // to handle the cross-origin requests
app.use(express.json()); // to parse JSON bodies
const port = process.env.PORT || 8000;
const credentials = {
clientId: process.env.CLIENT_ID,
clientSecret: process.env.CLIENT_SECRET,
redirectUri: process.env.REDIRECT_URI || "http://localhost:3000"
};
app.post('/refresh', (req, res) => {
const refreshToken = req.body.refreshToken;
// console.log("Hii");
let spotifyApi = new spotifyWebApi({
clientId: process.env.CLIENT_ID,
clientSecret: process.env.CLIENT_SECRET,
redirectUri: process.env.REDIRECT_URI,
refreshToken,
});
spotifyApi
.refreshAccessToken()
.then((data) => {
// console.log(data.body);
res.json({
accessToken: data.body.access_token,
expiresIn: data.body.expires_in,
})
})
.catch((err) => {
console.log(err);
res.sendStatus(400);
});
});
app.post('/login', (req,res) => {
// Get the "code" value posted from the client-side and get the user data from the spotify api
let spotifyApi = new spotifyWebApi(credentials)
const code = req.body.code
spotifyApi.authorizationCodeGrant(code).then((data) => {
// Returning the User's Data in the json formate
res.json({
accessToken : data.body.access_token,
refreshToken : data.body.refresh_token,
expiresIn : data.body.expires_in
})
})
.catch((err) => {
console.log(err);
res.sendStatus(400)
})
})
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
})
Client side:
useAuth.js:
import React from 'react';
import { useEffect, useState } from 'react';
import axios from "axios"
export default function useAuth(code) {
const [accessToken, setAccessToken] = useState();
const [refreshToken, setRefreshToken] = useState();
const [expiresIn, setExpiresIn] = useState();
useEffect(() => {
axios
.post("/login", {code})
.then((res) => {
window.history.pushState({}, null, "/");
console.log(res.data);
setAccessToken(res.data.accessToken);
setRefreshToken(res.data.refreshToken);
setExpiresIn(res.data.expiresIn);
})
.catch(() => {
window.location = "/";
});
}, [code]);
useEffect(() => {
if (!refreshToken || !expiresIn) {
return;
}
let interval = setInterval(() => {
axios
.post("/refresh", {refreshToken})
.then((res) => {
setAccessToken(res.data.accessToken);
setExpiresIn(res.data.expiresIn);
})
.catch(() => {
window.location = "/";
});
}, (expiresIn - 60) * 1000);
return () => clearInterval(interval)
}, [refreshToken, expiresIn]);
return accessToken;
}
spotifyConfig.js:
const authEndpoint = "https://accounts.spotify.com/authorize";
const redirectUri = "http://localhost:3000";
const clientId = "ea28d4ba34f34b44b59c640052c6e098";
const scopes = [
"streaming",
"playlist-modify-public",
"ugc-image-upload",
"user-read-email",
"user-read-private",
"user-read-currently-playing",
"user-read-recently-played",
"user-read-playback-state",
"user-modify-playback-state"
];
export const loginUrl = `${authEndpoint}?client_id=${clientId}&response_type=code&redirect_uri=${redirectUri}&scope=${scopes.join(
"%20"
)}&show_dialog=true`;
App.js:
import './App.css';
import Dashboard from './pages/Dashboard';
import Login from './components/Login';
const code = new URLSearchParams(window.location.search).get('code')
function App() {
return (
<div>
{code ? <Dashboard code={code}/> : <Login/>}
</div>
);
}
export default App;
👉🏽 this page appears a second then logs out Dashboard.js:
import React, {useEffect} from 'react';
import useAuth from '../useAuth';
import SpotifyWebApi from "spotify-web-api-node";
const spotifyApi = new SpotifyWebApi({
clientId: "ea28d4ba34f34b44b59c640052c6e098",
});
export default function Dashboard({code}) {
const accessToken = useAuth(code);
useEffect(() => {
if (!accessToken) return;
spotifyApi.setAccessToken(accessToken);
spotifyApi.getMe().then(data => {
console.log(data);
})
}, []);
return (
<div>
This is the home page 🏠
</div>
)
}
Login.js:
import React from 'react';
import { loginUrl } from '../spotifyConfig';
export default function Login() {
return (
<div>
<a href={loginUrl}>
<button>LOGIN WITH SPOTIFY</button>
</a>
<div className="links">
<p>
⚠ When joining or creating a Queue, open Spotify to be able to queue up tracks
</p>
</div>
</div>
)
}
I experienced a similar issue. Here are a few steps that helped me to resolve it and many of the subsequent issues I encountered.
Run your IDE's debugger and set break points for your /login request. Also, check whether your environment variables are getting set as you intend (if running VSCode you can learn how to set this up here). In particular, make sure your credentials
clientId: process.env.CLIENT_ID, clientSecret: process.env.CLIENT_SECRET, redirectUri: process.env.REDIRECT_URI
are correct. If your environment variables are not being loaded you won't be able to create a new SpotifyWebApi instance (check out dotenv file is not loading environment variables). Another easy way to check if this is the problem is to hard code your values temporarily.
Test your server /login endpoint independently before running the client to see whether the endpoint is returning 400 or another error when executing requests to Spotify.
Make sure you keep your devtools console open in your browser so you can identify any failed requests you're making to the Spotify API and consider setting devtools to preserve logs in case the page refreshing is deleting them.
If you are running both your client and server from inside VSCode, try instead running them both in new shells outside of VSCode.
I was having a similar issue, having followed this tutorial, and then this YouTube tutorial.
What I realised was that my App component was being rendered twice, which was causing everything to be called twice, including the login endpoint. I was able to verify this using console.log in the endpoint and seeing if the log appeared twice. As the same Spotify code was being used twice in SpotifyWebApi.authorizationCodeGrant, this was what was causing the error.
I was able to trace the issue to the React.StrictMode being enabled, which must have happened when using the create-react-app command. Verify whether the tags appear in your index.js file. For more information, check this StackOverflow answer: My React Component is rendering twice because of Strict Mode

Accessing Auth0 logged in user from within an API?

I have a React app which is using auth0 along side an express API server.
My question is, I can get the user information in the client side from the Auth0 user object, but i'm not sure how to access it within the api when a secure end point is called.
Sending the information to the api with any requests seems much less secure than using the access token somehow in the backend but i'm unsure how to do it or if it's even possible.
API SERVER
const express = require('express')
const app = express()
const port = 8000
const jwt = require('express-jwt')
const cors = require('cors')
var jwtCheck = jwt({
secret: '',
audience: 'http://localhost:8000',
issuer: 'https://dev-ml4yg9zg.us.auth0.com/',
algorithms: ['HS256']
});
app.use(cors())
app.get('/unprotected',(req,res) =>{
res.send("not secured resource")
})
app.get('/authed', jwtCheck,(req,res) =>{
// GET THE DATA FOR THE LOGGED IN USER WHO MADE THE CALL
res.send("secured resource")
})
app.listen(port, () =>{
console.log(`app listening on port ${port}`)
})
REACT APP
import React,{useEffect, useState} from 'react';
import axios from 'axios'
import {useAuth0} from '#auth0/auth0-react'
function App() {
const [accessToken, setAccessToken] = useState(null)
const [userMetaData, setUserMetadata] = useState(null)
const {
loginWithRedirect,
logout,
user,
isAuthenticated,
isLoading,
getAccessTokenSilently
} = useAuth0()
console.log(user)
const getToken = async () => {
try {
const accessToken = await getAccessTokenSilently({
audience: `http://localhost:8000`,
scope: "read:current_user",
});
setAccessToken(accessToken)
} catch (e) {
console.log(e.message);
}
};
const callProtected = () =>{
axios.get('http://localhost:8000/authed',{
headers:{
Authorization:`Bearer ${accessToken}`
}
}).then(res =>{
console.log(res.data)
}).catch(e =>{
console.log(e)
})
}
const callUnprotected = ()=>{
axios.get('http://localhost:8000/unprotected')
.then(res =>{
console.log(res.data)
}).catch(e =>{
console.log(e)
})
}
return (
<div className="App">
<button onClick={() => loginWithRedirect()}>Login</button>
<button onClick={() => logout({returnTo:window.location.origin})}>Log out</button>
<button onClick={() => getToken()}>Get token</button>
<button onClick={() => callUnprotected()}>Call unprotected resource</button>
<button onClick={() => callProtected()}>Call protected resource</button>
<div>
User : {user?.name}
</div>
</div>
);
}
export default App;
So I wasn't aware of the User Management API
To solve the issue of getting the user data to confirm authentication within my API server I decoded the JWT token server-side which contained the user ID, then I used the ID within a call to the management API to get the full user data.
The auth token for the call to the management API can be generated within the API dashboard on the Auth0 website
app.get('/authed', jwtCheck, async (req,res) =>{
let token = req.headers.authorization.substring(7, req.headers.authorization.length)
// GET THE DATA FOR THE LOGGED IN USER WHO MADE THE CALL
var decoded = jwt_decode(token);
console.log(decoded.sub)
axios.get(`https://************.us.auth0.com/api/v2/users/${decoded.sub}`,{
headers:{
Authorization:`Bearer *`,
}
}).then((res) =>{
console.log(res)
}).catch(e =>{
console.log(e)
})
res.send("secured resource")
})

Node / Express Post request using a function to send data

I am using a function that prevents the default submit of a form and want to use a second function that posts this so i can work / modify the data in the fist function before submitting.
the first function
const Test = document.querySelector('.test')
Test.addEventListener('submit', (e) => {
e.preventDefault()
const username = CreateUser.querySelector('.username').value
const password = CreateUser.querySelector('.password').value
post('/about', { username, password })
})
i found the following the function that submits the Post request. It works fine when the destination is another function without leaving the actual page.
function post (path, data) {
return window.fetch(path, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
}
I use the following routing in my index.js
const express = require('express')
const bodyParser = require('body-parser')
const store = require('./store')
const app = express()
app.use(express.static(__dirname + '/public'))
app.use(bodyParser.json())
var path = require('path')
app.post('/createUser', (req, res) => {
store
.createUser({
username: req.body.username,
password: req.body.password
})
.then(() => res.sendStatus(200))
})
app.get('/about',(req, res) => {
res.sendFile(path.join(__dirname, './public', 'about.html'));
})
app.post('/about',(req, res) => {
res.sendFile(path.join(__dirname, './public', 'about.html'));
})
app.listen(7555, () => {
console.log('Server running on http://localhost:7555')
})
When i make a post to /createUser i works fine and i can insert the data to a mysql table using a function.
I now want to make a post to /about using a function and eventually pass the data.
Why does it not work? I dont get any error.
The about.html, index.html and the js file with my functions are all in the public folder.
thanks for helping
Your route for the post function
app.post('/about',(req, res) => {
res.sendFile(path.join(__dirname, './public', 'about.html'));
})
is just returning the about.html page from your public folder. So there wouldn't be an error, you should just be getting back that HTML after posting to that endpoint with how it is currently configured.
The problem is that you'll only be getting this back as the body of your fetch() request. If you're wanting to see the about.html page, you'll want to actually redirect to http://localhost:7555/about.html. If you want to see the result of your fetch() request, you should be able to see the payload in the Networks tab of your DevTools (or your browser of choice's equivalent).

Next.js with Express Backend

I have a next.js application that runs with an express server. Everything was fine until the fetch. When I send from front-end to back-end post request, it works fine. And also when I try to fetch some data from another server it works fine. But whenever I want to fetch data from my server(same server as next app), it does not fetch. When I go to the page through the client(routes) it fetches everything, when I refresh and try (server side) does not work... When I console log the data inside getInitialProps it shows on the terminal(server). The error occurs only when I try to fetch something from my server.
Here is my server :
const app = next({ dev });
const handle = app.getRequestHandler();
app.prepare().then(() => {
const server = express();
server.use(bodyparser.json());
server.use(cors());
server.use("/api", userRoutes);
server.get("/service/:slug", (req, res) => {
return app.render(req, res, "/service", { slug: req.params.slug });
});
server.get("*", (req, res) => {
return handle(req, res);
});
server.listen(port, err => {
if (err) throw err;
console.log(`> Ready on http://localhost:${port}`);
});
});
And this is service page:
import fetch from "isomorphic-fetch";
import Layout from "../components/layout";
const Service = ({ data }) => {
console.log(data,'ins');
return (
<Layout>
<div style={{ padding: "100px" }} className="componentDiv">
<p>xxx</p>
{/* <p>{json.longDesc}</p>
<img src={json.img} alt="" /> */}
</div>
</Layout>
);
};
Service.getInitialProps = async ({ query }) => {
const response = await fetch(
`http://localhost:3000/api/service/${query.slug}`
);
const jsonv = await response.json();
console.log(jsonv);
return { data: jsonv.data };
};
export default Service;
Update!
I used axios instead isomorphic fetch and problem solved.

Categories