В приложении узла / express я реализовал аутентификацию с использованием локальной стратегии паспорта и использовал express -сессию.
Используя почтальона, при нажатии localhost:3000/register
, пользователь и документ сеанса создаются в базе данных, как и ожидалось.
Проблема заключается в том, когда я делаю то же самое из реагирующего приложения. Создаются два документа сеанса, и, хотя заголовок ответа имеет свойство setCook ie, в браузере не задается cook ie.
Вот мое приложение узла и мой компонент реакции.
// Node App
const express = require('express');
const session = require('express-session');
const passport = require('passport');
const cors = require('cors');
const crypto = require('crypto');
const mongoose = require('mongoose');
const path = require('path');
const routes = require('./routes');
require('dotenv').config();
mongoose.set('useCreateIndex', true);
const connection = mongoose.createConnection(process.env.MONGODB_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
const MongoStore = require('connect-mongo')(session);
const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
/**
* -------------- SESSION SETUP ----------------
*/
const sessionStore = new MongoStore({
mongooseConnection: connection,
collection: 'sessions',
});
const sessionOptions = {
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
store: sessionStore,
cookie: {
maxAge: 1000 * 60 * 60 * 24, // Equals 1 day (1 day * 24 hr/1 day * 60 min/1 hr * 60 sec/1 min * 1000 ms / 1 sec)
},
};
app.use(session(sessionOptions));
/**
* -------------- PASSPORT AUTHENTICATION ----------------
*/
function validPassword(password, hash, salt) {
var hashVerify = crypto.pbkdf2Sync(password, salt, 10000, 64, 'sha512').toString('hex');
return hash === hashVerify;
}
/**
* Local Strategy
*/
const localStrategyOptions = {
usernameField: 'email',
passwordField: 'password',
};
const localStrategyVerifyCallback = (username, password, done) => {
User.findOne({ 'local.email': username })
.then((user) => {
if (!user) {
return done(null, false);
}
const isValid = validPassword(password, user.local.hash, user.local.salt);
if (isValid) {
return done(null, user);
} else {
return done(null, false);
}
})
.catch((err) => {
done(err);
});
};
passport.use(
new LocalStrategy(localStrategyOptions, localStrategyVerifyCallback)
);
/**
* Serializer and Deserializer
*/
passport.serializeUser((user, done) => {
done(null, user.id);
});
passport.deserializeUser((userId, done) => {
User.findById(userId)
.then((user) => {
done(null, user);
})
.catch((err) => done(err));
});
app.use(passport.initialize());
app.use(passport.session());
/**
* CORS
*/
app.use(cors());
/**
* -------------- ROUTES ----------------
*/
router.post('/register', (req, res, next) => {
const saltHash = genPassword(req.body.password);
const newUser = new User({
local: {
email: req.body.email,
hash: saltHash.hash,
salt: saltHash.salt,
admin: true,
},
verification: {
email: req.body.email,
},
});
newUser
.save()
.then(user => {
res.send({
id: user._id,
email: user.local.email,
});
})
.catch(err => console.log('err: ', err));
});
app.use((req, res, next) => {
const error = new Error('Not found');
error.status = 404;
next(error);
});
app.use((error, req, res, next) => {
res.status(error.status || 500);
res.json({
error: {
message: error.message,
},
});
});
/**
* -------------- APP LISTEN ----------------
*/
app.listen(process.env.PORT, () => {
console.log(`App is running on port ${process.env.PORT}`);
});
import React, { useContext, useState } from "react";
import { Segment, Form, Input, Button } from "semantic-ui-react";
export default function SignupLocalStrategy() {
const email = useFormInput("");
const password = useFormInput("");
const onSubmit = () => {
fetch(`http://localhost:3000/register`, {
method: "post",
headers: { "Content-Type": "application/json" },
mode: "cors",
body: JSON.stringify({
email: email.value,
password: password.value,
}),
})
.then((res) => (res.ok ? res : Promise.reject(res)))
.catch((err) => err);
};
return (
<Segment basic>
<Form onSubmit={onSubmit}>
<Form.Group widths="3">
<Form.Field width={6}>
<Input placeholder="Enter Email" {...email} type="email" required />
</Form.Field>
<Form.Field width={6}>
<Input
placeholder="Enter Password"
{...password}
type="password"
required
/>
</Form.Field>
<Form.Field width={4}>
<Button fluid primary>
New Contact
</Button>
</Form.Field>
</Form.Group>
</Form>
</Segment>
);
}
function useFormInput(initialValue) {
const [value, setValue] = useState(initialValue);
const handleChange = (e) => {
setValue(e.target.value);
};
const handleReset = () => {
setValue("");
};
return {
value,
onChange: handleChange,
onReset: handleReset,
};
}