Categories
Express JavaScript Nodejs

Add Basic Authentication to an Express App

To add basic auth capabilities to an Express app, we can use the express-basic-auth package.

With it, we can check for a given username and password in the URL in our protected routes.

In this article, we’ll take a look at how to use it.

How to Install

express-basic-auth is available as a Node package, we can install it by running:

npm install express-basic-auth

Basic Usage

We can use it as follows:

const express = require('express');  
const basicAuth = require('express-basic-auth')  
const app = express();
app.use(basicAuth({  
    users: { 'admin': 'supersecret' }  
}))

app.get('/', (req, res) => {  
  res.send('authorized');  
});

app.listen(3000, () => console.log('server started'));

In the code above, we included the basicAuth middleware from express-basic-auth from in our app.

Then we can call app.use with an object that has the users property with the usernames as the keys and the corresponding passwords as the values.

Then if we make a GET request to a URL like:

https://admin:supersecret@ThistleDarkExponents--five-nine.repl.co

Then we’ll get the authorized response back since the username and password matches what we listed in the object in our app.

Otherwise, we’ll get a 401 response and a configurable body, which is empty by default.

Custom Authorization

We can also pass in our own authorizer function to check for credentials that we want.

We shouldn’t use standard string comparison with == or === when comparing user input with sercet credentials since it’ll make our app vulenrable to timing attacks.

Timing attacks are a side-channel attack where the attacker tries to compromise a system by analyzing the time taken to run cryptographic algorithms.

We should use the safeCompare method provided by the package instead.

Also, we should use bitwise logic operators instead of standard ones for the same reason.

For example, we can use the following:

const express = require('express');  
const basicAuth = require('express-basic-auth')  
const app = express();
app.use(basicAuth({  
  authorizer: (username, password) => {  
    const userMatches = basicAuth.safeCompare(username, 'admin')  
    const passwordMatches = basicAuth.safeCompare(password, 'supersecret')  
    return userMatches & passwordMatches  
  }  
}))

app.get('/', (req, res) => {  
  res.send('authorized');  
});

app.listen(3000, () => console.log('server started'));

In the code above, we have a function that calls safeCompare to match the username and password respectively and then use the AND bitwise operator to combine the 2 to make sure that both are true .

Async Authorization

We can set the authorizeAsync option to true and set a function with a cb parameter for the callback as the authorizer function to check credentials asynchronously:

const express = require('express');  
const basicAuth = require('express-basic-auth')  
const app = express();
app.use(basicAuth({  
  authorizer: (username, password, cb) => {  
    const userMatches = basicAuth.safeCompare(username, 'admin')  
    const passwordMatches = basicAuth.safeCompare(password, 'supersecret')  
    if (userMatches & passwordMatches)  
      return cb(null, true)  
    else  
      return cb(null, false)  
  },  
  authorizeAsync: true,  
}))

app.get('/', (req, res) => {  
  res.send('authorized');  
});

app.listen(3000, () => console.log('server started'));

We return the cb callback call instead of returning the result directly in our authorizer function.

Unauthorized Response Body

We can set the unauthorizedResponse property to a function that returns a response that we want to show when a credential check failed.

For example, we can write:

const express = require('express');  
const basicAuth = require('express-basic-auth')  
const app = express();  
app.use(basicAuth({  
  users: { 'admin': 'supersecret' },  
  unauthorizedResponse: (req) => {  
    return `unauthorized. ip: ${req.ip}`  
  }  
}))  
app.get('/', (req, res) => {  
  res.send('authorized');  
});  
app.listen(3000, () => console.log('server started'));

to return something like:

unauthorized. ip: ::ffff:172.18.0.1

when basic authentication fails.

The function we set unauthorizedResponse to takes the Express request object, so we can use any properties from there in the function.

Challenge

We can make browsers show a popup so that users can enter credentials for authentication by add a challenge: true option to the object.

In addition, we set the realm to identify the system to authenticate against and can be used to save credentials of the challenge by passing a static or a function that gets passed the request object and is expected to return the challenge:

const express = require('express');  
const basicAuth = require('express-basic-auth')  
const app = express();app.use(basicAuth({  
  users: { 'admin': 'supersecret' },  
  challenge: true,  
  realm: 'foo',  
}))

app.get('/', (req, res) => {  
  res.send('authorized');  
});

app.listen(3000, () => console.log('server started'));

With the code above, we should get a dialog box to enter the username and password when we load the / page.

Conclusion

Adding basic auth to an Express app is easy with the express-basic-auth package.

It lets us add a list of valid credentials or use a function to validate credentials.

We can also let users enter the username and password and display custom content when the user is unauthorized.

Categories
Express JavaScript

Compress Express.js Responses with the Compression Middleware

Compressing our responses is a great way to speed up our Express app’s performance.

To do this, we can use the compression middleware.

In this article, we’ll look at the options available to us for compressing data and how to send a compressed response with it.

Adding the Middleware

We can install the package by running:

npm install compression

Then we can include it with:

const compression = require('compression');

Options

The compression middleware takes an options parameter, which is an object that can have the following properties:

chunkSize

The chunk size of the compressed output. Default is 16384 bytes or zlib.Z_DEFAULT_CHUNK , where zlib is the zlib Node package.

filter

A function that decides what kind of responses should be compressed. The function is called as filter(req, res) and should return true if the kind of response should be compressed and false if it shouldn’t.

The default function uses the compressible module to check if the res.getHeader('Content-Type') is compressible.

level

The compression level to apply The higher the level of compression, the longer it’ll take to complete.

The values range from 0 (no compression) to 9 (highest level of compression). -1 can be used to mean the default compression level, which is equivalent to level 6.

memLevel

This sets how much memory should be allocated for compression and is an integer ranging from 1 for the minimum level to 9 for the maximum.

Default is zlib.Z_DEFAULT_MEMLEVEL or 8.

strategy

This sets the compression algorithm. The value only differs in the compression ratio and doesn’t affect the content of the compressed output.

The following are available:

  • zlib.Z_DEFAULT_STRATEGY — used for normal data
  • zlib.Z_FILTERED — used for data produced by a filter. Filtered data consists mostly of small values with a random distribution. This algorithm is tuned to compressed these kinds of data better. This algorithm forces more Huffman coding.
  • zlib.Z_FIXED — use to prevent the use of dynamic Huffman codes
  • zlib.Z_HUFFMAN_ONLY — force Huffman encoding only
  • zlib.Z_RULE — limit match distances to one, This gives a compression ratio as good as Huffman only, but gives better compression for PNG data

threshold

The byte threshold for response body size before compression is considered for the response. The default 1kb . The value can be the number of bytes or any string applied by the bytes module.

windowBits

The default value is zlib.Z_DEFAULT_WINDOWBITS or 15.

.filter

The default filter function, which we can use to construct a custom filter function that extends the default action.

res.flush

A res.flush method is added to force partially compressed response to be flushed to the client.

Example

For example, we can use it as follows:

const express = require('express');  
const bodyParser = require('body-parser');  
const compression = require('compression');
const app = express();  
const shouldCompress = (req, res) => {  
  if (req.headers['x-no-compression']) {  
    return false  
  }  
  return compression.filter(req, res);  
}

app.use(bodyParser.json());  
app.use(compression({  
  filter: shouldCompress,  
  level: 7,  
}));

app.get('/', (req, res) => {  
  const foo = 'foo';  
  res.send(foo.repeat(3000));  
});

app.listen(3000);

In the code above, we add the compression middleware and specify some options.

We specified that we compress our responses when the x-no-compression header isn’t present.

Also, we changed the compression level.

With the x-no-compression header present, our response is 9KB. After compression is enabled by removing the x-no-compression header, the response is only 402 bytes.

As we can see, the difference is huge. This is because the text is repeated so it can just keep one part of it and then repeat it instead of storing the whole string.

Server-Sent Events

We can compress responses with server-sent events. To do this, we compress the content after a window of the output has been buffered.

Once we have that, then we can send the data to the client.

We can use the res.flush() method to send whatever is present to the client.

For example, we can write:

const express = require('express');  
const bodyParser = require('body-parser');  
const compression = require('compression');
const app = express();  
const shouldCompress = (req, res) => {  
  if (req.headers['x-no-compression']) {  
    return false  
  }  
  return compression.filter(req, res);  
}

app.use(bodyParser.json());  
app.use(compression({  
  filter: shouldCompress,  
  level: 7,  
}));

app.get('/', (req, res) => {  
  res.setHeader('Content-Type', 'text/event-stream');  
  res.setHeader('Cache-Control', 'no-cache');  
  const timer = setInterval(() => {  
    res.write('foo');  
    res.flush()  
  }, 2000) res.on('close', () => {  
    clearInterval(timer)  
  })  
});

app.listen(3000);

To send a text event stream to the client, then we should see foo on the screen added every 2 seconds.

Conclusion

The compression middleware is useful for compressing regular responses and server-sent event output.

We can set options like compression level, chunk size, etc.

Also, if we want to compress server-side events, we should call res.flush to send what’s what already buffered to the client compressed.

Categories
Express JavaScript Nodejs

Excluding Routes from Calling Express Middleware with Express-Unless

To conditionally skip a middleware when a condition is met, we can use the express-unless package.

In this article, we’ll take a look at how to use it.

Installation

We can install it by running:

npm i express-unless --save

Usage

After installing it, we can assign the unless function exposed by the express-unless and assign it to the unless property of our middleware functions.

For example, we can write:

const express = require('express');  
const unless = require('express-unless');  
   
const static = express.static(__dirname + '/public');  
static.unless = unless;const app = express();  
app.use(static.unless({ method: 'OPTIONS' }));  
app.listen(3000, () => console.log('server started'));

In the code above, we set the unless property of static to the unless function of the express-unless package.

Then we write:

app.use(static.unless({ method: 'OPTIONS' }));

to exclude the static route from running on OPTIONS requests.

If we’re writing our own middleware, we can assign the unless property of our middleware function as follows:

const express = require('express');  
const unless = require('express-unless');  
   
const static = express.static(__dirname + '/public');
const logHostname = (req, res, next) =>{  
  console.log(req.hostname);  
  next();  
}  
logHostname.unless = unless;const app = express();  
app.use(logHostname.unless({ method: 'OPTIONS' }));  
app.listen(3000, () => console.log('server started'));

In the code above, we defined the logHostman middleware to log the hostname. Then we set the unless property of the function to the unless function exposed in the express-unless package.

Options

In the object that we pass into unless , we can pass in the following options:

  • method — this can be a string or an array of strings. If the request method matches then the middleware won’t run.
  • path — this can be a string, regex or an array of string or regex. It can also be an array of object which is URL and method key-value pairs. If the request path or path and method match, then the middleware won’t run.
  • ext — this is a string or array of strings. If the path ends with those extensions then a middleware won’t run.
  • custom — a function that accepts req and returns a boolean. If the function returns true for the given request, then the middleware won’t run.
  • useOriginalUrl — this should be a boolean. Defaults to true . If it’s false , the path will match against req.url instead of req.originalUrl .

Advanced Examples

We can exclude calling the express.static middleware when we have the jpg , html , css or .js extensions as follows:

const express = require('express');  
const unless = require('express-unless');  
const url = require('url');  
const static = express.static(__dirname + '/public');
static.unless = unless;
const app = express();  
app.use(static.unless((req) => {  
  const ext = url.parse(req.originalUrl).pathname.substr(-4);  
  return !['.jpg', '.html', '.css', '.js'].includes(ext);  
}));  
app.listen(3000, () => console.log('server started'));

In the code above, we check the extensions of the req.originalUrl property to see if they the request have the extensions listed in the array and skip the static middleware if they’re there.

We can also mix paths and request methods together as follows:

const express = require('express');  
const unless = require('express-unless');
const static = express.static(__dirname + '/public');  
const logHostname = (req, res, next) => {  
  console.log(req.hostname);  
  next();  
}  
logHostname.unless = unless;  
const app = express();  
app.use(logHostname.unless({  
  path: [  
    '/index.html',  
    { url: '/', methods: ['OPTIONS', 'PUT'] }  
  ]  
}));  
app.listen(3000, () => console.log('server started'));

Then we stop the logHostname middleware from running when we go to index.html or / with the PUT or OPTION request methods.

Conclusion

We can stop middleware from running on certain routes or request methods by using the express-unless package.

To use it, we just set the unless function exposed from the package and set it to the unless property of our middleware function.

Then we can exclude routes by using various conditions by checking the URL and/or request method.

Categories
Express JavaScript

Guide to the Express Response Object

The Express response object lets us send a response to the client.

Various kinds of responses like strings, JSON, and files can be sent. Also, we can send various headers and status code to the client in addition to the body.

In this article, we’ll look at how to set headers and send status codes with other items, including the set method for setting headers, status for setting response status code, type for setting the Content-Type header value, and the vary method for setting the Vary header value.

Methods

res.set(field [, value])

We can use the set method to set response headers before sending a response.

For example, we can use it as follows:

const express = require('express');  
const path = require('path');  
const app = express()  
app.use(express.json())  
app.use(express.urlencoded({ extended: true }))
app.get('/', (req, res, next) => {  
  res.set({  
    'Content-Type': 'text/plain',  
    'Content-Length': '100'  
  })  
    .send();  
})  
app.listen(3000);

Then when we make a request to / , we get that the Content-Length header is set to 100 and the Content-Type is set to text/plain .

Note that we have to call send to send the response.

We can verify this with an HTTP client like Postman.

res.status(code)

We can call the status method to add a status code before sending the response.

For example, we can use it as follows:

const express = require('express');  
const path = require('path');  
const app = express()  
app.use(express.json())  
app.use(express.urlencoded({ extended: true }))
app.get('/', (req, res, next) => {  
  res.status(400).send('Bad Request');  
})
app.listen(3000);

Then we get Bad Request displayed and the 400 response code.

res.type(type)

The type method sets the Content-Type HTTP header to the MIME type determined by the mime.lookup() method from the node-mime package for the specified type.

If type contains the / character, then it sets the Content-Type to type .

This method doesn’t send the response.

For example, we can use it as follows:

const express = require('express');  
const path = require('path');  
const app = express()  
app.use(express.json())  
app.use(express.urlencoded({ extended: true }))
app.get('/', (req, res) => {  
  res.type('.html').send();  
})  
app.listen(3000);

Then we get that the Content-Type response header has the value text/html; charset=utf-8 .

If we have:

const express = require('express');  
const path = require('path');  
const app = express()  
app.use(express.json())  
app.use(express.urlencoded({ extended: true }))
app.get('/', (req, res) => {  
  res.type('.png').send();  
})  
app.listen(3000);

Then we get image/png instead of text/html for the value of Content-Type .

res.vary(field)

The vary method adds the field to the Vary response header if it’s not already there.

For example, we can use it as follows:

const express = require('express');  
const path = require('path');  
const app = express()  
app.use(express.json())  
app.use(express.urlencoded({ extended: true }))
app.get('/', (req, res) => {  
  res.vary('User-Agent').send();  
})  
app.listen(3000);

Then we have the Vary response header set to User-Agent .

The Vary header lets us to content negotiation, which is a mechanism for serving different kinds of data with the same URL.

The user agent can specify what’s best for the server.

The Vary header indicates which headers it’s used for content negotiation.

Since we have the Vary header set to User-Agent , our app uses the User-Agent header to serve different kinds of content according to the user agent of the browser.

Conclusion

The set method lets us set headers before sending our response. It takes an object with the response headers that we want to send.

status method is for setting a response status code before sending it. We can chain it with the response body.

The type method is for setting the Content-Type header value. The returned MIME-type is determined by the mime.lookup() method from node-mime .

The vary method for setting the Vary header value. The Vary header is used for content negotiation, which is serving different content according to the field specified by the Vary header’s value with the same URL.

Categories
Express JavaScript Nodejs

Verifying JSON Web Tokens with Express-JWT

With the use of single-page apps and API-only back end, JSON web tokens (JWTs) have become a popular way of adding authentication capabilities to our apps.

In this article, we’ll look at how to verify JSON web tokens with Express-JWT.

Installation

express-jwt is available as a Node package. To install it, we run:

npm install express-jwt

Usage

We can use the jwt function that’s included with the express-jwt package to verify a token.

jwt is a middleware function, so we don’t have to create our own middleware to verify the token.

For example, we can use the jwt function as follows:

const express = require('express');  
const jsonwebtoken = require('jsonwebtoken');  
const jwt = require('express-jwt');  
const app = express();
const secret = 'secret';
app.post('/auth', (req, res) => {  
  const token = jsonwebtoken.sign({ foo: 'bar' }, secret);  
  res.send(token);  
})
app.get('/protected', jwt({ secret }), (req, res) => {  
  res.send('protected');  
})
app.listen(3000, () => console.log('server started'));

In the code above, we called jsonwebtoken.sign to issue a token in the auth route.

Then we can call the protected route by putting Bearer and then our token in the Authorization request header.

An example Authorization header would be:

Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJpYXQiOjE1Nzk0NzczMDd9.QW7FOvyJQ36dir0199nJTv07VhlNo9\_cItGTkdyJeK8

If the token isn’t valid or isn’t present, then we would get an error.

We can also check other fields like audience or issuer as follows:

const express = require('express');  
const jsonwebtoken = require('jsonwebtoken');  
const jwt = require('express-jwt');  
const app = express();
const secret = 'secret';  
const audience = 'http://myapi/protected';  
const issuer = 'http://issuer';
app.post('/auth', (req, res) => {  
  const token = jsonwebtoken.sign({ foo: 'bar' }, secret, { audience, issuer });  
  res.send(token);  
})
app.get('/protected', jwt({ secret, audience, issuer }), (req, res) => {  
  res.send('protected');  
})
app.listen(3000, () => console.log('server started'));

In the code above, we added audience and issuer to the object in the 3rd argument of the sign method call, which holds the options that we want to add to the token issued.

Then in the protected route, we added the jwt middleware with the audience and issuer in the options object that we passed into jwt .

If the token has a secret , audience , and issuer that match what the code has, then the protected route returns the protected response. Otherwise, we get an error.

We can verify a token that’s generated with a Base64 encoded secret as follows:

const express = require('express');  
const jsonwebtoken = require('jsonwebtoken');  
const jwt = require('express-jwt');  
const app = express();
const secret = 'secret';
app.post('/auth', (req, res) => {  
  const token = jsonwebtoken.sign({ foo: 'bar' }, new Buffer(secret, 'base64'));  
  res.send(token);  
})
app.get('/protected', jwt({ secret: new Buffer(secret, 'base64') }), (req, res) => {  
  res.send('protected');  
})
app.listen(3000, () => console.log('server started'));

In the code above, we have the new Buffer(secret, ‘base64’) passed to the second argument of the sign method, which generates a token from a Base64 encoded secret.

Then in the protected route handler, we can call the jwt middleware function with the same secret to verify the token.

To verify a token generated with an RSA private key, we can write the following:

const express = require('express');  
const jsonwebtoken = require('jsonwebtoken');  
const jwt = require('express-jwt');  
const fs = require('fs');  
const app = express();
const publicKey = fs.readFileSync('public.pub');
app.post('/auth', (req, res) => {  
  const privateKey = fs.readFileSync('private.key');  
  const token = jsonwebtoken.sign({ foo: 'bar' }, privateKey, { algorithm: 'RS256' });  
  res.send(token);  
})
app.get('/protected', jwt({ secret: publicKey }), (req, res) => {  
  res.send('protected');  
})
app.listen(3000, () => console.log('server started'));

The token is generated in the auth route by reading the private key from a file on the file system and then using that to sign the token with the sign method.

Then we can use the public key that corresponds to the private key that was used to generate the token to verify the token by writing:

jwt({ secret: publicKey })

To access the decoded token, we can use the req.user property as follows:

const express = require('express');  
const jsonwebtoken = require('jsonwebtoken');  
const jwt = require('express-jwt');  
const app = express();  
const secret = 'secret';app.post('/auth', (req, res) => {  
  const token = jsonwebtoken.sign({ foo: 'bar' }, secret);  
  res.send(token);  
})app.get('/protected', jwt({ secret }), (req, res) => {  
  res.send(req.user);  
})app.listen(3000, () => console.log('server started'));

In the protected route, we returned req.user as the response. Then we should get something like:

{  
    "foo": "bar",  
    "iat": 1579478314  
}

in the response content.

We can change what property the decoded token is attached to by setting the requestProperty property as follows:

app.get('/protected', jwt({ secret, requestProperty: 'auth' }), (req, res) => {  
  res.send(req.auth);  
})

Then we get the same response as the previous example.

Conclusion

We can use the express-jwt middleware to verify our JSON web tokens.

It takes a secret and other token options like audience and issuer and then sets the decoded token to the Express request object if verification is successful.

If verification isn’t successful, then an error is returned.

It supports both symmetric and asymmetric crypto algorithms.