首页 > 解决方案 > 无法在 NodeJS + ExpressJS 应用程序中发布

问题描述

我的 NodeJS 应用程序有三种变体。

第一个版本仅使用 Node,并且工作正常。

使用 ExpressJS 的应用程序的第二和第三版本是导致问题的版本。我不得不求助于使用 ExpressJS,因为我将把应用程序部署到 AWS Lambda,并且可能会为此目的使用 ClaudiaJS。

另外,我正在使用 Duo 进行 MFA,它将经过身份验证的用户发送到 Okta,以便可以解锁用户的帐户。

HTML 文件在所有 3 个实例中均未更改。

在第一个简化实例中,应用程序按预期工作:

应用程序.js

let http = require('http')
let url = require('url')
let qs = require('querystring')
let duo_web = require('./duo.js')

const ikey = 'LOREM'
const skey = 'LOREM'
const akey = 'LOREM'
const api_hostname = 'LOREM'
const post_action = ''

const okta = require('@okta/okta-sdk-nodejs');

const client = new okta.Client({
  orgUrl: 'LOREM/',
  token: 'LOREM'    // Obtained from Developer Dashboard
 });


/**
 * Returns templated string with api_hostname and sig_request.
 *
 * @param {string} api_hostname - name of users API Hostname
 * @param {string} sig_request - Signed request returned from Duo's sign_request
 * @param {string} post_action - Name of the post_action url that will be posted
 * to by the IFrame
 */

let IFrame = (api_hostname, sig_request, post_action) => {
  return `<!DOCTYPE html>
  <html>
    <head>
      <title>Duo Authentication Prompt</title>
      <meta name='viewport' content='width=device-width, initial-scale=1'>
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <style>
        body {
            text-align: center;
        }

        iframe {
            width: 100%;
            min-width: 304px;
            max-width: 620px;
            height: 330px;
            border: none;
        }
      </style>
    </head>
    <body>
      <h1>Duo Authentication Prompt</h1>
      <iframe id="duo_iframe"
              title="Two-Factor Authentication"
              data-host= ${api_hostname}
              data-sig-request= ${sig_request}
              data-post-action=${post_action}
              >
      </iframe>
      <script src='https://api.duosecurity.com/frame/hosted/Duo-Web-v2.min.js'></script>
    </body>
  </html>`
}

/**
* Creates the server and listens for any POST/GET requests.
*/

const app = http.createServer((req, res, next) => {
  let base_url = url.parse(req.url).pathname
  let method = req.method
  if (method === 'GET') {
    if (base_url === '/') {
      let query = url.parse(req.url, true).query
      let {username} = query
      if (username) {
        // initializes secondary authentication process
        let sig_request = duo_web.sign_request(ikey, skey, akey, username)
        let duo_frame = IFrame(api_hostname, sig_request, post_action)
        // shows the IFrame
        res.writeHead(200, {'Content-Type': 'text/html'})
        res.end(duo_frame)
      } else {
        res.writeHead(404, {'Content-Type': 'text/html'})
        res.end(`Make sure you add a username:  http://localhost:8080/?username=xxx,\
        and appropriate configuration variables (ikey, skey, etc.). `)
      }
    }
} else if (method === 'POST') {
    if (base_url) {
    let request_body = ''
        req.on('data', data => {
            request_body += data.toString() // convert Buffer to string
        });
        req.on('end', () => {
            let form_data = qs.parse(request_body)
            let sig_response = form_data.sig_response
            // verifies that the signed response is legitimate
            let authenticated_username = duo_web.verify_response(ikey, skey, akey, sig_response)
            if (authenticated_username) {
                client.unlockUser(`${authenticated_username}`)
                .then(user => {
                console.log(user);
                })
            res.end(`${authenticated_username}, You've Been Dual Authenticated !`)
            } else {
                res.status(401).end()
                }
            })
        }
    }
})


module.exports = app

app.local.js

const app = require('./app')
const port = process.env.PORT || 8080
app.listen(port, () => 
  console.log(`Server is listening on port ${port}.`)
)

现在,当我开始使用 ExpressJS 时,我遇到了一些问题,我的代码要么卡在 POST 请求中,我可以在控制台中看到“待定”状态,或者在下面的第三个版本中,我得到了浏览器中的“无法发布”和控制台中的 404。

第二个版本(请求永久挂起):

app2.js

'use strict'
const express = require('express')
var bodyParser = require("body-parser");
const app = express()
let url = require('url')
let qs = require('querystring')
let duo_web = require('./duo.js')
const ikey = 'LOREM'
const skey = 'LOREM'
const akey = 'LOREM'
const api_hostname = 'LOREM'
const post_action = ''
const okta = require('@okta/okta-sdk-nodejs');
const client = new okta.Client({
  orgUrl: 'LOREM',
  token: 'LOREM'    // Obtained from Developer Dashboard
});

//Here we are configuring express to use body-parser as middle-ware.
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(express.static(__dirname + '/public'));
/**
 * Returns templated string with api_hostname and sig_request.
 *
 * @param {string} api_hostname - name of users API Hostname
 * @param {string} sig_request - Signed request returned from Duo's sign_request
 * @param {string} post_action - Name of the post_action url that will be posted
 * to by the IFrame
 */
let IFrame = (api_hostname, sig_request, post_action) => {
  return `<!DOCTYPE html>
  <html>
    <head>
      <title>Duo Authentication Prompt</title>
      <meta name='viewport' content='width=device-width, initial-scale=1'>
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <style>
        body {
            text-align: center;
        }
        iframe {
            width: 100%;
            min-width: 304px;
            max-width: 620px;
            height: 330px;
            border: none;
        }
      </style>
    </head>
    <body>
      <h1>Duo Authentication Prompt</h1>
      <iframe id="duo_iframe"
              title="Two-Factor Authentication"
              data-host= ${api_hostname}
              data-sig-request= ${sig_request}
              data-post-action=${post_action}
              >
      </iframe>
      <script src='https://api.duosecurity.com/frame/hosted/Duo-Web-v2.min.js'></script>
    </body>
  </html>`
}
/**
* Creates the server and listens for any POST/GET requests.
*/

app.get('/', (req, res, next) => {
  let base_url = url.parse(req.url).pathname
  let method = req.method
  if (method === 'GET') {
    if (base_url === '/') {
      let query = url.parse(req.url, true).query
      let {username} = query
      if (username) {
        // initializes secondary authentication process
        let sig_request = duo_web.sign_request(ikey, skey, akey, username)
        let duo_frame = IFrame(api_hostname, sig_request, post_action)
        // shows the IFrame
        res.writeHead(200, {'Content-Type': 'text/html'})
        res.end(duo_frame)
      } else {
        res.writeHead(404, {'Content-Type': 'text/html'})
        res.end(`Make sure you add a username:  http://localhost:8080/?username=xxx,\
        and appropriate configuration variables (ikey, skey, etc.). `)
      }
    }
    next();
  }
})

app.post('/', (req, res, next) => {
  let base_url = url.parse(req.url).pathname
  let method = req.method
  if (method === 'POST') {
    if (base_url) {
      let request_body = ''
      req.on('data', data => {
        request_body += data.body.toString() // convert Buffer to string
      })
      req.on('end', () => {
        let form_data = qs.parse(request_body)
        let sig_response = form_data.sig_response
        // verifies that the signed response is legitimate
        let authenticated_username = duo_web.verify_response(ikey, skey, akey, sig_response)
        if (authenticated_username) {
          client.unlockUser(`${authenticated_username}`)
          .then(user => {
            console.log(user);
          });
          res.end(`${authenticated_username}, You've Been Dual Authenticated !`)
        } else {
          res.status(401).end()
        }
      })
    }
  }
  })

module.exports = app

app2.local.js

const app = require('./app2')
const port = process.env.PORT || 8080
app.listen(port, () => 
  console.log(`Server is listening on port ${port}.`)
)

第三个版本(得到一个不能 POST):

app3.js

'use strict'
var express = require('express');
var bodyParser = require('body-parser');
var path = require('path');
var app = express();
let url = require('url');
let qs = require('querystring');
let duo_web = require('./duo.js');

const ikey = 'LOREM'
const skey = 'LOREM'
const akey = 'LOREM'
const api_hostname = 'LOREM'
const post_action = ''
const okta = require('@okta/okta-sdk-nodejs');
const client = new okta.Client({
  orgUrl: 'LOREM',
  token: 'LOREM'    // Obtained from Developer Dashboard
});

var urlencodedParser = bodyParser.urlencoded({ extended: false });

app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');

app.use(bodyParser.json());
app.use(express.static(path.join(__dirname, 'public')));

/**
 * Returns templated string with api_hostname and sig_request.
 *
 * @param {string} api_hostname - name of users API Hostname
 * @param {string} sig_request - Signed request returned from Duo's sign_request
 * @param {string} post_action - Name of the post_action url that will be posted
 * to by the IFrame
 */
let IFrame = (api_hostname, sig_request, post_action) => {
  return `<!DOCTYPE html>
  <html>
    <head>
      <title>Duo Authentication Prompt</title>
      <meta name='viewport' content='width=device-width, initial-scale=1'>
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <style>
        body {
            text-align: center;
        }
        iframe {
            width: 100%;
            min-width: 304px;
            max-width: 620px;
            height: 330px;
            border: none;
        }
      </style>
    </head>
    <body>
      <h1>Duo Authentication Prompt</h1>
      <iframe id="duo_iframe"
              title="Two-Factor Authentication"
              data-host= ${api_hostname}
              data-sig-request= ${sig_request}
              data-post-action=${post_action}
              >
      </iframe>
      <script src='https://api.duosecurity.com/frame/hosted/Duo-Web-v2.min.js'></script>
    </body>
  </html>`
}
/**
* Creates the server and listens for any POST/GET requests.
*/

app.get('/', (req, res, next) => {
  let base_url = url.parse(req.url).pathname
  let method = req.method
  if (method === 'GET') {
    if (base_url === '/') {
      let query = url.parse(req.url, true).query
      let {username} = query
      if (username) {
        // initializes secondary authentication process
        let sig_request = duo_web.sign_request(ikey, skey, akey, username)
        let duo_frame = IFrame(api_hostname, sig_request, post_action)
        // shows the IFrame
        res.writeHead(200, {'Content-Type': 'text/html'})
        res.end(duo_frame)
        next();
      } else {
        res.writeHead(404, {'Content-Type': 'text/html'})
        res.end(`Make sure you add a username:  http://localhost:8080/?username=xxx,\
        and appropriate configuration variables (ikey, skey, etc.). `)
      }
    }
    next();
  }
})

app.post('/', urlencodedParser, (req, res, next) => {
  let base_url = url.parse(req.url).pathname
  let method = req.method
  if (method === 'POST') {
    if (base_url === '/') {
      let request_body = '';
      req.on('data', (chunk) => {
        request_body.push(chunk); 
      }).on('end', () => {
        request_body = Buffer.concat(request_body).toString();
        let form_data = qs.parse(request_body)
        let sig_response = form_data.sig_response
        // verifies that the signed response is legitimate
        let authenticated_username = duo_web.verify_response(ikey, skey, akey, sig_response)
        if (authenticated_username) {
          client.unlockUser(`${authenticated_username}`)
          .then(user => {
            console.log(user);
          });
          res.end(`${authenticated_username}, You've Been Dual Authenticated !`)
        } else {
          res.status(401).end()
        }
      })
    }
  }
    next();
  })

module.exports = app

app3.local.js

const app = require('./app3')
const port = process.env.PORT || 8080
app.listen(port, () => 
  console.log(`Server is listening on port ${port}.`)
)

在第二个和第三个实例中,应用程序永远不会在 POST 请求中到达此代码块:

req.on('data', (chunk) => {
        request_body.push(chunk); 

我查看了各种资源,并尝试了许多不同的解决方案。请帮忙!

标签: node.jsexpressaws-lambda

解决方案


Ok so with the help of a colleague I was able to resolve this. I was going about trying to access the data in the request in the wrong way.

This works, and is much simpler:

app.post('/', urlencodedParser, (req, res, next) => {
  let base_url = url.parse(req.url).pathname
  let method = req.method
  let sig_response = req.body.sig_response
  console.log(req.body.sig_response)
        // verifies that the signed response is legitimate
        let authenticated_username = duo_web.verify_response(ikey, skey, akey, sig_response)
        if (authenticated_username) {
          client.unlockUser(`${authenticated_username}`)
          .then(user => {
            console.log(user);
          });
          res.end(`${authenticated_username}, You've Been Dual Authenticated !`)
        } else {
          res.status(401).end()
        }
  })

Hope this helps others!


推荐阅读