-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Description
Summary
hapi v12.0.0 is a small release focused on removing the framework dependency on the qs module. qs is a URL query string parser with special support for representing complex structures in a simple key=value format. Normally, the query a[b]=1 will parse into the object { "a[b]": "1" }, however when passed through the qs module it results in { a: { b: "1" } }. This release removes this feature and reverts it back to simple query string parsing support as well as similar functionality when parsing form-encoded payloads and multipart form submissions (field names). Additional changes include supporting more complex authentication scopes and a minor change to server.inject().
- Upgrade time: low - no time to a couple of hours for most users
- Complexity: low - requires a few small code changes for those using the query format
- Risk: low - low risk of side effects and few changes to keep track of overall
- Dependencies: low- changes a single API call
Sponsor
The v12.0.0 major release is sponsored by Sideway.
Breaking Changes
- Removed support for the processing of special form-encoded key=value pairs in query strings, form-encoded payloads, and multipart field names.
- Removes the connection
query.qsconfiguration option. - Removes the route
payload.qsconfiguration option. - Removes the
parserOptionsargument from therequest.setUrl()method. - Authentication scope strings cannot begin with
!or+as those prefix characters now have a special meaning. request.route(the route public interface)settings.authchanged to movescopeandentityinside a newaccessarray.- When using
server.inject(), any HTTP trailers are no longer included inres.headersbut instead are provided underres.trailersto be consistent with node. - Removed
request.sessionandrequest.auth.sessionplaceholders (was set tonullbefore).
New Features
- Allow passing a pre-processed URL object (from node's URL
parse()) torequest.setUrl(). - Support new required and forbidden authentication scopes.
- Allow specifying multiple scope/entity sets for a single endpoint.
- New
pendingGenerateTimeoutcache option for reducing calls to the generate method while another is already pending. - Return a
Promisewhen acallbackis not provided. - Expose CORS origin match status in
request.info.cors.isOriginMatch. - Expose entire
request.authobject in validation context.
Bug fixes
- Allow setting dynamic scope in
server.auth.default() - Prevent
request.raw.res.end()from being called twice.
Updated dependencies
- subtext from v3.0.1 to v4.0.0
- catbox from v7.0.0 to v7.1.0
- shot from v2.0.1 to v3.0.0
- mime-db from v1.19.0 to v1.20.0
- moment from v2.10.6 to v2.11.0
- isemail from v2.0.0 to v2.1.0
- joi from v7.0.0 to v7.1.0
- hoek from v3.0.0 to v3.0.4
- boom from v3.0.0 to v3.1.1
- accept from v2.0.0 to v2.1.0
- statehood from v3.0.0 to v3.1.0
Migration Checklist
qs
You don't have to change anything if you are not using the special qs format in:
- Incoming requests URL query string (the part between the
?and the#). - Form-encoded payloads (typically from web forms using the POST method).
- Multipart payloads field names (typically from web forms using the POST method with file upload).
There are a few easy ways of identifying if you are expecting this special format:
- you have field names in web forms which include the
[]characters (as well as.if you enable that custom feature of qs). - your hapi configuration is setting either of the two
qsconfiguration options or you pass a third argument torequest.setUrl(). - you have route payload or query validation rules which include nested objects (e.g. object with sub objects like
{ a: { b: 1 } }). If this is set on a payload rule, check if you expect form-encoded submissions to that endpoint along with normal JSON.
Checklist:
- Figure out if you are using the qs features removed in this release.
- If you are not, test the release as usual but there are no further steps.
- If you are using qs, follow these instructions:
Check out the new hapi-qs plugin which incorporates the code below into a simple plugin form. If you prefer to use custom code, apply the code snippets below.
If you want to parse qs formatted query strings, add this to your server code:
const Url = require('url');
const Qs = require('qs');
const onRequest = function (request, reply) {
const uri = request.raw.req.url;
const parsed = Url.parse(uri, false);
parsed.query = Qs.parse(parsed.query);
request.setUrl(parsed);
return reply.continue();
};
server.ext('onRequest', onRequest);If you previously used the connection.query.qs configuration option, pass that setting to the Qs.parse() method above.
If you want to parse qs formatted field names in payloads, add this to your server code:
const Qs = require('qs');
const onPostAuth = function (request, reply) {
if (typeof request.payload === 'object' &&
!Buffer.isBuffer(request.payload)) {
request.payload = Qs.parse(request.payload);
}
return reply.continue();
};
server.ext('onPostAuth', onPostAuth);If you previously used the route.payload.qs configuration option, pass that setting to the Qs.parse() method above.
Note that these code examples are based on simple setups and may require adjustments for your environment.
Authentication scope and entity changes
While the existing route authentication configuration is still supported (for the time being), the scope and entity keys moved from config.auth to config.auth.access. For example:
server.route({
method: 'GET',
path: '/',
handler: handlerFunc,
config: {
auth: {
mode: 'required',
strategy: 'default',
entity: 'user',
scope: ['account']
}
}
}Should now be expressed as:
server.route({
method: 'GET',
path: '/',
handler: handlerFunc,
config: {
auth: {
mode: 'required',
strategy: 'default',
access: {
entity: 'user',
scope: ['account']
}
}
}
}The access config can now take an array of objects, each providing a different combination of entity and scopes. This is useful if you want to require a different scope for application accounts from user accounts.
The scope config now supports two special prefix characters: + for required scope strings and ! for forbidden scope strings. For example, the scope ['!a', '+b', 'c', 'd'] means the incoming request credentials' scope must not include 'a', must include 'b', and must include one of 'c' or 'd'.
Checklist:
- Look for
scopeconfigs in your code and make sure you do not use+or!in your strings. - Move
entityandscopeincludeaccessto prepare for future changes (nor required at this point). - If you access the the route public interface via
request.route,server.handler(),server.lookup(),server.match(), orserver.on('route'), and you access thescopeorentitysettings, look for them underaccess.scopeandaccess.entity.
Misc
- If your application uses HTTP trailers and you expect to find those headers in
res.headerswhen usingserver.inject(), userres.trailersinstead. The hapi-auth-hawk module uses trailers but this change only affects the module tests. - If you are testing
request.sessionorrequest.auth.sessionfor equality tonull, test forundefinedinstead as they are no longer initialized tonull. This is only likely if you wrote your own session manager (instead of using hapi-auth-cookie or yar.
