Implementation of a session storage using DynamoDB as an extension of the express-session middleware.
The minimum Node.js version required by this module is 6.x.
The project uses the following stack:
- ES2017
- Babel
- Eslint with AirBnB style
- Jest
- Yarn
- Husky
- Flow
The project was tested with Express.js 4.x.
This store implements the touch method to allow express-session configurations to use resave: false.
yarn add dynamodb-store
or
npm install --save dynamodb-store
AWS SDK is set as optional dependency, so use the --no-optional-dependencies
to avoid shipping it (lambda containers already have it).
Usage within express:
const session = require("express-session");
const DynamoDBStore = require('dynamodb-store');
app.use(session({
store: new DynamoDBStore(options),
...
}));
I've built a boilerplate that shows how to use this store.
{
"table": {
"name": "<NAME OF THE DYNAMO TABLE>",
"hashKey": "<NAME OF THE ID FIELD>",
"hashPrefix": "<PREFIX FOR THE SESSION ID VALUES>",
"readCapacityUnits": 10,
"writeCapacityUnits": 10
},
"dynamoConfig": {
"accessKeyId": "<AWS ACCESS KEY>",
"secretAccessKey": "<AWS ACCESS KEY SECRET>",
"region": "<AWS REGION>",
"endpoint": "<DYNAMO ENDPOINT>"
},
"keepExpired": false,
"touchInterval": 30000,
"ttl": 600000
}
The table
configuration is optional. The missing properties will be replaced by defaults. readCapacityUnits
and writeCapacityUnits
are only used if the table is created by this store.
The dynamoConfig
can be optional if the following environment variables are set: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY and AWS_REGION (which are present on Lambda Functions running on AWS). All properties from AWS.DynamoDB constructor can be informed in this structure.
The keepExpired
property is optional (defaults to false). When set to false informs the store to remove from DynamoDB expired session rows when they are requested. When set to true the store will just ignores the expired rows and leave them in DynamoDB. This property does not guarantee that all expired sessions will be removed from DynamoDB, only the ones that receive requests after they expire.
The touchInterval
property defines how ofter requests should update the time to live of a session. This property is important to avoid unnecessary table writes. By default the interval allows express to touch a same session every 30 seconds. touchInterval
= 0 will cause a touch on every request.
The ttl
property is optional (defaults to 1 day) and represents the server-side controlled time to live of the sessions (in ms). See more below.
The time to live of the sessions can be controlled:
Using cookies with the cookie.maxAge property:
If this property is set, the session cookie will be created with a fixed 'expires' attribute. After the specified time the session cookie will expire and a new session will be created even if the user is still active. To avoid that you need update the 'expires' attribute of the session cookie on every request by setting the rolling session property to true. This way every request will have a set-cookie response with the updated 'expires' attribute.
Using the ttl
property implemented by this store the session time to live will be controlled by the server. The time to live will be refreshed based on the touchInterval
property without the need to update cookies.
If both cookie.maxAge
and ttl
are informed, ttl
takes precedence.
To keep the session table clear of expired sessions, you must setup the Time To Live feature of DynamoDB pointing to the expires
field.
Bear in mind that DynamoDB's TTL cleanup can take up to 48 hours. Although the expired sessions will be ignored by the store, they will still be in the table during this period.
If you have intense traffic on your application and the 48h wait period causes unnecessary storage costs, consider creating a scheduled lambda function that scans few records at a time and clears the expired.
If you really want the store to be responsible for that use this other store that has a reap
mechanism to periodically clear the expired sessions manually.
Setting the keepExpired
property to false also helps with the the housekeeping.
With a local DynamoDB running on port 8000 you just need to run:
yarn run testLocal
If you want to test with a different DynamoDB configuration, edit the variables on .env:
AWS_ACCESS_KEY_ID=dummyKey
AWS_SECRET_ACCESS_KEY=dummySecret
AWS_REGION=local
AWS_DYNAMO_ENDPOINT=http://localhost:8000
DYNAMODB_STORE_DEBUG=true
If you want to run the tests and see the coverage:
yarn test
To enable debug logging of the store, set the environment variable DYNAMODB_STORE_DEBUG to true and all the store method calls will be shown in the console:
Wed Jul 19 2017 23:16:04 GMT 0100 (WEST) - DYNAMODB_STORE: Skipping touch of session 'vn31s3sl3k5fHiHs1saMXNEyb_hEp1KS'
Wed Jul 19 2017 23:16:06 GMT 0100 (WEST) - DYNAMODB_STORE: Session 'vn31s3sl3k5fHiHs1saMXNEyb_hEp1KS' found {"csrfSecret":"ZeYyyZyHv1rADky_hmiYt40e","cookie":{"path":"/","expires":null,"_expires":null,"data":{"path":"/","expires":null,"httpOnly":true,"secure":true,"originalMaxAge":null,"sameSite":true},"maxAge":null,"sameSite":true,"httpOnly":true,"secure":true,"originalMaxAge":null},"updated":1500502536135,"user":{"firstName":"Shaylee","lastName":"Robel","avatar":"https://s3.amazonaws.com/uifaces/faces/twitter/yassiryahya/128.jpg"}}
Wed Jul 19 2017 23:16:06 GMT 0100 (WEST) - DYNAMODB_STORE: Touching session 'vn31s3sl3k5fHiHs1saMXNEyb_hEp1KS'
This project used connect-dynamodb and cassandra-store as reference for implementation/documentation.