diff --git a/Documentation/Books/Manual/Foxx/Sessions/Storages/JWT.mdpp b/Documentation/Books/Manual/Foxx/Sessions/Storages/JWT.mdpp index af56b058bc..5dc1f108c4 100644 --- a/Documentation/Books/Manual/Foxx/Sessions/Storages/JWT.mdpp +++ b/Documentation/Books/Manual/Foxx/Sessions/Storages/JWT.mdpp @@ -57,4 +57,8 @@ Creates a [Storage](README.md) that can be used in the sessions middleware. If set to `false` the signature will not be verified but still generated (unless using the "none" algorithm). + * **maxExp**: `number` (Default: `Infinity`) + + Largest value that will be accepted in an incoming JWT `exp` (expiration) field. + If a string is passed instead of an options object it will be interpreted as the *secret* option. diff --git a/Documentation/Books/Manual/ReleaseNotes/README.mdpp b/Documentation/Books/Manual/ReleaseNotes/README.mdpp index b120dc3fd0..42f4ac162b 100644 --- a/Documentation/Books/Manual/ReleaseNotes/README.mdpp +++ b/Documentation/Books/Manual/ReleaseNotes/README.mdpp @@ -22,6 +22,7 @@ Incompatible changes Also see [Upgrading](../Administration/Upgrading/README.md) in the Administration chapter. +- [Incompatible changes in 3.2](UpgradingChanges32.md) - [Incompatible changes in 3.1](UpgradingChanges31.md) - [Incompatible changes in 3.0](UpgradingChanges30.md) - [Incompatible changes in 2.8](UpgradingChanges28.md) diff --git a/Documentation/Books/Manual/ReleaseNotes/UpgradingChanges32.mdpp b/Documentation/Books/Manual/ReleaseNotes/UpgradingChanges32.mdpp new file mode 100644 index 0000000000..b92662dda5 --- /dev/null +++ b/Documentation/Books/Manual/ReleaseNotes/UpgradingChanges32.mdpp @@ -0,0 +1,14 @@ +Incompatible changes in ArangoDB 3.2 +==================================== + +It is recommended to check the following list of incompatible changes **before** +upgrading to ArangoDB 3.2, and adjust any client programs if necessary. + +Foxx +---- + +JWT token issued by the built-in [JWT session storage](../Foxx/Sessions/Storages/JWT.md) now correctly specify the `iat` and `exp` values in seconds rather than milliseconds as specified in the JSON Web Token standard. + +This may result in previously expired tokens using milliseconds being incorrectly accepted. For this reason it is recommended to replace the signing `secret` or set the new `maxExp` option to a reasonable value that is smaller than the oldest issued expiration timestamp. + +For example setting `maxExp` to `10**12` would invalidate all incorrectly issued tokens before 9 September 2001 without impairing new tokens until the year 33658 (at which point these tokens are hopefully no longer relevant). diff --git a/Documentation/Books/Manual/SUMMARY.md b/Documentation/Books/Manual/SUMMARY.md index d595dae188..77f187ddc4 100644 --- a/Documentation/Books/Manual/SUMMARY.md +++ b/Documentation/Books/Manual/SUMMARY.md @@ -194,6 +194,7 @@ # * [Release notes](ReleaseNotes/README.md) * [Whats New in 3.2](ReleaseNotes/NewFeatures32.md) + * [Incompatible changes in 3.2](ReleaseNotes/UpgradingChanges32.md) * [Whats New in 3.1](ReleaseNotes/NewFeatures31.md) * [Incompatible changes in 3.1](ReleaseNotes/UpgradingChanges31.md) * [Whats New in 3.0](ReleaseNotes/NewFeatures30.md) diff --git a/js/server/modules/@arangodb/foxx/sessions/storages/jwt.js b/js/server/modules/@arangodb/foxx/sessions/storages/jwt.js index 29559891b0..9634e029f2 100644 --- a/js/server/modules/@arangodb/foxx/sessions/storages/jwt.js +++ b/js/server/modules/@arangodb/foxx/sessions/storages/jwt.js @@ -35,25 +35,26 @@ module.exports = function jwtStorage (cfg) { assert(cfg.algorithm === 'none' || cfg.secret, `Must pass a JWT secret for "${cfg.algorithm}" algorithm`); assert(cfg.algorithm !== 'none' || !cfg.secret, 'Must NOT pass a JWT secret for "none" algorithm'); const algorithm = cfg.algorithm || 'HS512'; - const ttl = (cfg.ttl || 60 * 60) * 1000; + const ttl = (cfg.ttl || 60 * 60); + const maxVal = cfg.maxExp || Infinity; return { fromClient (sid) { const token = crypto.jwtDecode(cfg.secret, sid, cfg.verify === false); - if (token.exp < Date.now()) { + if (Date.now() > token.exp * 1000 || token.exp > maxVal) { return null; } return { uid: token.uid, - created: token.iat, + created: token.iat * 1000, data: token.payload }; }, forClient (session) { const token = { uid: session.uid, - iat: session.created, + iat: Math.floor(session.created / 1000), payload: session.data, - exp: Date.now() + ttl + exp: Math.floor(Date.now() / 1000) + ttl }; return crypto.jwtEncode(cfg.secret, token, algorithm); },