2016-05-10 2 views
5

У меня есть обработка промежуточного программного обеспечения Express с моего приложения на стороне клиента для последующего запроса отдельного сервера API, использующего токены OAuth2, я также использую express-session для хранения этих жетонов.Обновление свойства пользовательского значения req.session, похоже, недостаточно быстро

В моем промежуточном программном обеспечении, которое делает исходящий запрос, я добавил обработку, чтобы справляться с случаями, когда истекает срок доступа к токену доступа (сервер API отправляет обратно 403) и делает запрос на обновление токенов, после чего он выдаст тот же исходный исходящий запрос на сервер API, поэтому клиент не знает об этом. Новые извлеченные токены затем сохраняются обратно в хранилище сеансов через express-session для использования в последующих запросах. Токены также используются для установки заголовка токена носителя-носителя, как вы увидите ниже.

Вот части моего кода экспресс, который заключается в:

routes.controller.js

//Currently handling GET API requests from client 
module.exports.fetch = function(req, res) { 
    var options = helpers.buildAPIRequestOptions(req); 
    helpers.performOutgoingRequest(req, res, options); 
}; 

helpers.js

module.exports.buildAPIRequestOptions = function(req, url) { 
    var options = {}; 
    options.method = req.method; 
    options.uri = 'http://someurl.com' + req.path; 
    options.qs = req.query; 
    options.headers = { 
    'Authorization': 'Bearer ' + req.session.accessToken 
    }; 
    return options; 
}; 

module.exports.performOutgoingRequest = function(req, res, options) { 
    request(options, function(err, response, body){ 
    if(response.statusCode === 401){ 
     console.log(chalk.red('\n--- 401 RESPONSE RECEIVED TRY REFRESHING TOKENS ---')); 
     //Note the third param to call below is a callback and is invoked when calling next() in the refreshToken middleware 
     authController.refreshToken(req, res, function(){ 
     console.log(chalk.green('\n--- RETRYING ORIGINAL REQUEST WITH UPDATED ACCESS TOKEN ---')); 
     //Re-use original request options, but making sure we update the Authorization header beforehand 
     options.headers.Authorization = 'Bearer ' + req.session.accessToken; 
     retryOutgoingRequest(res, options); 
     }); 
    } else { 
     res.status(response.statusCode).send(body); 
    } 
    }); 
}; 

function retryOutgoingRequest(res, options) { 
    request(options, function(err, response, body){ 
    if(err) { 
     console.log(err); 
    } 
    res.status(response.statusCode).send(body); 
    }); 
}; 

auth.controller.js

module.exports.refreshToken = function(req, res, next) { 
    var formData = { 
     grant_type: 'refresh_token', 
     refresh_token: req.session.refreshToken 
    }, 
    headers = { 
     'Authorization' : 'Basic ' + consts.CLIENT_KEY_SECRET_BASE64 
    }; 
    request.post({url:consts.ACCESS_TOKEN_REQUEST_URL, form:formData, headers: headers, rejectUnauthorized: false}, function(err, response, body){ 
    var responseBody = JSON.parse(body); 
    if (response.statusCode === 200) { 
     req.session.accessToken = responseBody.access_token; 
     req.session.refreshToken = responseBody.refresh_token; 
     next(); 
    } else { 
     console.log(chalk.yellow('A problem occurred refreshing tokens, sending 401 HTTP response back to client...')); 
     res.status(401).send(); 
    } 
    }); 
}; 

Для большей части выше работает просто отлично

Когда в первый пользователь LOG, некоторые дополнительная информация Профиль пользователя скачиваются с сервера API, прежде чем они будут приняты к главной странице приложения.

Некоторые страницы в приложении также извлекают данные на загрузку страницы и поэтому подвергаются проверкам токена доступа.

Во время обычного использования, когда пользователь входит в систему и начинает щелкать по страницам, я вижу, что токены выгружаются и сохраняются в хранилище сеансов через express-session по мере их истечения. Новый токен доступа правильно используется для последующих запросов в соответствии с написанным мной промежуточным программным обеспечением.

У меня теперь есть сценарий, когда мое промежуточное ПО не работает.

Так что скажите, что я на странице, которая загружает данные на загрузку страницы, позволяет сказать ее страницу заказов. Если я подожду до тех пор, пока не истечет время истечения установленного времени токена на сервере API, а затем обновит браузер, клиентское приложение сначала сделает запрос на информацию пользователя, а при успешном запросе запросит данные заказов, необходимые для страницы (используя AngularJS обещает)

В моем экспресс-приложении пользовательский запрос информации получает 403 сервера API, и поэтому токены обновляются с помощью моего промежуточного программного обеспечения выше, и обновляется req.session.accessToken, которое я вижу через консольное ведение журнала в моем серверном приложении. Но следующий выбор данных для заказов заканчивается использованием ранее установленного токена доступа, и это вызывает дополнительную несанкционированную ошибку с сервера API, поскольку запрос выполняется с недопустимым токеном.

Если я обновляю браузер снова, информация пользователя и заказы пользователя извлекаются с использованием обновленного токена из предыдущего потока промежуточного программного обеспечения.

Так что я не уверен, что здесь происходит, мне интересно, не проблема ли с проблемой req.session не сохраняться в хранилище сеансов вовремя для следующего запроса?

У кого-нибудь есть идеи, что здесь происходит?

Благодаря

Update 1

В соответствии с просьбой в комментариях, вот заголовки запроса и ответа для двух запросов делаются.

первый запрос (который использует обновленный маркер на стороне сервера)

Заголовки запросов

GET /api/userinfo HTTP/1.1 
Host: localhost:5000 
Connection: keep-alive 
Accept: application/json, text/plain, */* 
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.94 Safari/537.36 
Referer: https://localhost:5000/ 
Accept-Encoding: gzip, deflate, sdch 
Accept-Language: en-GB,en-US;q=0.8,en;q=0.6 
Cookie: interact.sid=s%3A0NDG_bn67NeGQAYl1wP1-TmM19ExavFm.Zjv65e9BtSyNBuo%2FDxZEk2Np0963frVur4zHyYw3y5I 

Response Headers

HTTP/1.1 200 OK 
X-Content-Type-Options: nosniff 
X-Frame-Options: SAMEORIGIN 
Strict-Transport-Security: max-age=86400 
X-Download-Options: noopen 
X-XSS-Protection: 1; mode=block 
Content-Type: text/html; charset=utf-8 
Content-Length: 364 
ETag: W/"16c-4AIbpZmTm3I+Yl+SbZdirw" 
set-cookie: interact.sid=s%3A0NDG_bn67NeGQAYl1wP1-TmM19ExavFm.Zjv65e9BtSyNBuo%2FDxZEk2Np0963frVur4zHyYw3y5I; Path=/; Expires=Fri, 13 May 2016 11:54:56 GMT; HttpOnly; Secure 
Date: Fri, 13 May 2016 11:24:56 GMT 
Connection: keep-alive 

второй запрос (который использует старый маркер на стороне сервера)

Заголовки запросов

GET /api/customers HTTP/1.1 
Host: localhost:5000 
Connection: keep-alive 
Accept: application/json, text/plain, */* 
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.94 Safari/537.36 
Referer: https://localhost:5000/ 
Accept-Encoding: gzip, deflate, sdch 
Accept-Language: en-GB,en-US;q=0.8,en;q=0.6 
Cookie: interact.sid=s%3A0NDG_bn67NeGQAYl1wP1-TmM19ExavFm.Zjv65e9BtSyNBuo%2FDxZEk2Np0963frVur4zHyYw3y5I 

Response Headers

HTTP/1.1 401 Unauthorized 
X-Content-Type-Options: nosniff 
X-Frame-Options: SAMEORIGIN 
Strict-Transport-Security: max-age=86400 
X-Download-Options: noopen 
X-XSS-Protection: 1; mode=block 
set-cookie: interact.sid=s%3A0NDG_bn67NeGQAYl1wP1-TmM19ExavFm.Zjv65e9BtSyNBuo%2FDxZEk2Np0963frVur4zHyYw3y5I; Path=/; Expires=Fri, 13 May 2016 11:54:56 GMT; HttpOnly; Secure 
Date: Fri, 13 May 2016 11:24:56 GMT 
Connection: keep-alive 
Content-Length: 0 

Update 2

Я должен также упомянуть, я использую connect-mongo для моего хранилища сеансов, я попытался использовать хранилище памяти по умолчанию, но такое же поведение существует.

ответ

3

это звучит как клиентская сторона состояния гонки, если вы выполняете 2 запроса (чтобы проверить auth - а затем получить данные) является второй (получить данные), вложенной в успех первых звонков? или вы вызываете оба одновременно линейно?

моя мысль:

клиент - отправляет данные пользователя запрос (SessionID 1) - обработка сервера

клиент - получает Информация порядок запроса (SessionID 1) - обработка сервера

сервер - отвечает пользователь Информация - 403 - клиент обновляет идентификационный номер сессии

сервер - отвечает данные заказа - 403

действительно то, что вы хотите я s:

клиента - посылает информацию пользователя запрос (сессионные 1) - обработка сервера

сервер - получает данные пользователя запрос (403) - обновления клиента идентификатор сеанса

клиента - получает информацию ЗАПРОСА (сессия 2) - сервер обработки

сервер - respondes заказать данные - фактические результаты

+0

Привет андрей, нет, как вызовы не делаются от клиента одновременно. Я использую обещания angularjs, а второй запрос на вызов на стороне клиента не получается, пока пользовательский запрос запроса пользователя не будет разрешен. Я добавил несколько обширных протоколов на стороне сервера и вижу, как входящие запросы от клиента обрабатываются один за другим, при этом токены обновляются между ними. Хотелось бы, чтобы это было так же просто, как то, что вы предложили! Спасибо – mindparse

+0

, тогда я также могу предложить следующее сообщение: http://stackoverflow.com/questions/13090177/updating-cookie-session-in-express-not-registering-with-browser - установить флаг качения в true, чтобы заставить сеанс для обновления по каждому запросу (для обеспечения его кеширования) –

+0

Спасибо, но у меня уже есть 'roll: true' set – mindparse

Смежные вопросы