Экспресс не устанавливает cookie

У меня проблема с настройкой файлов cookie через express. Я использую Este.js dev stack и я пытаюсь установить cookie в API auth /login route. Вот код, который я использую в /api/v1/auth/login route

res.cookie('token', jwt.token, {expires: new Date(Date.now() + 9999999)});
res.status(200).send({user, token: jwt.token});

В src/server/main.js я зарегистрировал cookie-parser как первое промежуточное программное обеспечение

app.use(cookieParser());

Заголовок ответа для маршрута /api/v1/auth/login содержит

Set-Cookie:token=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJ.. 

но cookie не сохраняется в браузере (document.cookie пуст, а также Resources - Cookies вкладка Resources - Cookies в инструментах develepoers пуста) :(

EDIT: Я обнаружил, что когда я вызываю это в /api/v1/auth/login (без вызова res.send или res.json)

res.cookie('token', jwt.token, {expires: new Date(Date.now() + 9999999), httpOnly: false}); next();

то cookie установлен. И заголовок ответа имеет X-Powered-By:Este.js... это устанавливает esteMiddleware в esteMiddleware frontend esteMiddleware part.

Когда я использую res.send

res.cookie('token', jwt.token, {expires: new Date(Date.now() + 9999999), httpOnly: false}).send({user, token: jwt.token});'
next();

то я получаю ошибку Can't set headers after they are sent. потому что используется метод send, поэтому frontend render throw this error.

Но я должен отправить данные из API, так как я могу справиться с этим?

Может кто-нибудь мне помочь? Благодарю!

Ответы

Ответ 1

Я была такая же проблема. Ответ сервера поставляется с набором файлов cookie:

Set-Cookie:my_cookie=HelloWorld; Path=/; Expires=Wed, 15 Mar 2017 15:59:59 GMT 

Но cookie не был сохранен браузером.

Вот как я это решил.

Я использую fetch в клиентском коде. Если вы не укажете credentials: 'include' в параметрах fetch, файлы cookie не отправляются на сервер и не сохраняются браузером, хотя ответ сервера устанавливает куки.

Пример:

var headers = new Headers();
headers.append('Content-Type', 'application/json');
headers.append('Accept', 'application/json');

return fetch('/your/server_endpoint', {
    method: 'POST',
    mode: 'same-origin',
    redirect: 'follow',
    credentials: 'include', // Don't forget to specify this if you need cookies
    headers: headers,
    body: JSON.stringify({
        first_name: 'John',
        last_name: 'Doe'
    })
})

Надеюсь, это поможет кому-то.

Ответ 2

я работаю с выражением 4 и узлом 7.4 и угловым, у меня была та же проблема, я помогаю этому:
a) серверная сторона: в файле app.js я даю заголовки всем ответам вроде:

 app.use(function(req, res, next) {  
      res.header('Access-Control-Allow-Origin', req.headers.origin);
      res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
      next();
 });  

это должно быть до всех маршрутизаторов.
Я видел много добавленных заголовков:

res.header("Access-Control-Allow-Headers","*");
res.header('Access-Control-Allow-Credentials', true);
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');

но мне это не нужно,

б) когда вы определяете cookie, вы добавляете httpOnly: false, например:

 res.cookie( key, value,{ maxAge: 1000 * 60 * 10, httpOnly: false });

c) клиентская сторона: в send ajax вам нужно добавить: "withCredentials: true", например:

$http({
     method: 'POST',
     url: 'url, 
     withCredentials: true,
     data : {}
   }).then(function(response){
        // code  
   }, function (response) {
         // code 
   });

удачи.

Ответ 3

Есть несколько вопросов:

  • cookie, который явно не задан с помощью httpOnly: false не будет доступен через document.cookie в браузере. Он по-прежнему будет отправляться с HTTP-запросами, и если вы проверите средства разработки браузеров, вы, скорее всего, найдете там cookie (в Chrome их можно найти на вкладке "Ресурсы" инструментов dev);
  • next() который вы вызываете, должен использоваться только в том случае, если вы хотите отложить отправку ответа на какую-либо другую часть вашего приложения, что, судя по вашему коду, - это не то, что вы хотите.

Итак, мне кажется, что это должно решить ваши проблемы:

res.cookie('token', jwt.token, {
  expires  : new Date(Date.now() + 9999999),
  httpOnly : false
});
res.status(200).send({ user, token: jwt.token });

В качестве побочного примечания: существует причина, по которой httpOnly не соответствует true (чтобы не допустить, чтобы злонамеренные скрипты XSS httpOnly к httpOnly сеансов и тому подобное). Если у вас нет оснований для доступа к файлу cookie через JS на стороне клиента, не устанавливайте его в false.

Ответ 4

Дважды проверьте размер вашего куки.

Для меня способ, которым я генерировал токен аутентификации для хранения в моем cookie файле, вызывал увеличение размера cookie файла с последующими попытками входа в систему, в результате чего браузер не устанавливал cookie файл из-за его слишком большого размера.

Шпаргалка размера печенья браузера

Ответ 5

app.post('/api/user/login',(req,res)=>{

    User.findOne({'email':req.body.email},(err,user)=>{
        if(!user) res.json({message: 'Auth failed, user not found'})
        
        user.comparePassword(req.body.password,(err,isMatch)=>{
            if(err) throw err;
            if(!isMatch) return res.status(400).json({
                message:'Wrong password'
            });
            user.generateToken((err,user)=>{
                if(err) return res.status(400).send(err);
                res.cookie('auth',user.token).send('ok')
            })
        }) 
    })
});

Ответ 6

Одна из главных особенностей - правильно настроить заголовок.

Для nginx:

add-header Access-Control-Allow-Origin '' domain.com ';

add_header 'Access-Control-Allow-Credentials' 'true';

Добавьте это на свой веб-сервер.

Затем создайте файл cookie следующим образом:

"cookie": {
        "secure": true, 
        "path": "/", 
        "httpOnly": true, 
        "hostOnly": true, 
        "sameSite": false, 
        "domain" : "domain.com"
    }

Лучший способ получить куки файл из экспресса - использовать cookie-parser.

Ответ 7

Файл cookie не может быть установлен, если клиент и сервер находятся в разных доменах. Разные субдомены возможны, но не разные домены и не разные порты.

Если вы используете Angular в качестве внешнего интерфейса, вы можете просто отправить все запросы в тот же домен, что и ваше приложение Angular (таким образом, приложение отправляет все запросы API к себе), и вставить/api/в каждый URL-адрес HTTP-запроса HTTP - обычно это настраивается в вашей среде..ts файл:

export const environment = {
  production: false,
  httpPhp: 'http://localhost:4200/api'
}

Тогда все HTTP-запросы будут использовать environment.httpPhp + '/rest/of/path'

Затем вы можете прокси эти запросы, создав proxy.conf.json следующим образом:

{
  "/api/*": {
    "target": "http://localhost:5200",
    "secure": false,
    "changeOrigin": true,
    "pathRewrite": {
      "^/api": ""
    }
  }
}

Затем добавьте это в ng serve:

ng serve -o --proxy-config proxy.conf.json

Затем перезапустите ваше приложение, и все должно работать, при условии, что ваш сервер фактически использует Set-Cookie в заголовках ответа HTTP. (Обратите внимание, что в домене diff вы даже не увидите заголовок ответа Set-Cookie, даже если сервер настроен правильно).