Authorizing requests based on request body content [http/authorization/request_body] ==================================================================================== Sometimes authentication credentials are passed in the request body rather than through headers or the query string. Using njs, we have full access to the request body contents. **Step 1:** Use the following commands to start your NGINX container with this lab's files: *Notice the SECRET_KEY environment variable* .. code-block:: shell EXAMPLE='http/authorization/request_body' docker run --rm --name njs_example -e SECRET_KEY="foo" -v $(pwd)/conf/$EXAMPLE.conf:/etc/nginx/nginx.conf:ro -v $(pwd)/njs/:/etc/nginx/njs/:ro -p 80:80 -d nginx **Step 2:** Now let's use curl to test our NGINX server: .. code-block:: shell :emphasize-lines: 1,4,7,10,13 curl http://localhost/secure/B No signature curl http://localhost/secure/B?a=1 -H Signature:A Invalid signature: YC5iL6aKDnv7XOjknEeDL+P58iw= curl http://localhost/secure/B?a=1 -H Signature:YC5iL6aKDnv7XOjknEeDL+P58iw= BACKEND:/secure/B curl http://localhost/secure/B -d "a=1" -X POST -H Signature:YC5iL6aKDnv7XOjknEeDL+P58iw= BACKEND:/secure/B docker stop njs_example Code Snippets ~~~~~~~~~~~~~ This config uses `auth_request` to make a request to an "authentication server" before proxying to the upstream server. In this case, the "auth server" is an internal location that calls our njs code. .. code-block:: nginx :linenos: :caption: nginx.conf ... env SECRET_KEY; http { js_path "/etc/nginx/njs/"; js_import main from http/authorization/request_body.js; upstream backend { server 127.0.0.1:8081; } server { listen 80; location /secure/ { js_content main.authorize; } location @app-backend { proxy_pass http://backend; } } server { listen 127.0.0.1:8081; return 200 "BACKEND:$uri\n"; } } The njs code will look for a "Signature" header and compare its contents with a digital signature generated by the crypto library of njs. If the request method is POST, we make sure the content-type is a form and then incorporate the request body (if present) into the digital signature. .. code-block:: js :linenos: :caption: request_body.js function authorize(r) { var signature = r.headersIn.Signature; if (!signature) { r.return(401, "No signature\n"); return; } var h = require('crypto').createHmac('sha1', process.env.SECRET_KEY); h.update(r.uri); switch (r.method) { case 'GET': var args = r.variables.args; h.update(args ? args : ""); break; case 'POST': var body = r.requestBody; if (r.headersIn['Content-Type'] != 'application/x-www-form-urlencoded' || !body.length) { r.return(401, "Unsupported method\n"); } h.update(body); break; default: r.return(401, "Unsupported method\n"); return; } var req_sig = h.digest("base64"); if (req_sig != signature) { r.return(401, `Invalid signature: ${req_sig}\n`); return; } r.internalRedirect('@app-backend'); } export default {authorize}