diff --git a/.markdownlintignore b/.markdownlintignore new file mode 100644 index 00000000..6db0d6c1 --- /dev/null +++ b/.markdownlintignore @@ -0,0 +1,2 @@ +node_modules +/servers/fastify/session/node_modules diff --git a/package.json b/package.json index 618e41ab..31b55bba 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "scripts": { "test:unit": "jest --coverage", "test:onlychanged": "jest --onlyChanged --coverage", - "test:markdown": "markdownlint . --ignore ./node_modules", + "test:markdown": "markdownlint . ", "lint": "standard", "test": "npm run lint && npm run test:unit && npm run test:markdown" }, @@ -20,6 +20,11 @@ "standard": "^14.3.3" }, "dependencies": { + "connect-redis": "^5.0.0", + "fastify-cookie": "^4.0.2", + "fastify-session": "^5.0.0", + "is-docker": "^2.1.1", + "redis": "^3.0.2", "yargs": "^15.3.1" }, "standard": { diff --git a/servers/fastify/session/README.md b/servers/fastify/session/README.md new file mode 100644 index 00000000..e8a5759b --- /dev/null +++ b/servers/fastify/session/README.md @@ -0,0 +1,66 @@ +# Fastify Session + +A common use case for a web framework is to provide session storage. Fastify has a [concept of plugins](https://www.fastify.io/docs/latest/Plugins-Guide/) +and the plugin for session is [fastify-session](https://github.com/SerayaEryn/fastify-session). These examples +will demonstrate server side session storage. + +## Simple local http session demonstration using local storage + +[http-session-server](./http-session-server/http-session-server.js) + +The most simple demonstration is an http server using a memory store. A memory store would be a local nodeJS based +storage mechanism. This demonstrates the concept of +session in fastify but is not what you would want in production. You would want to use an https server and +you would want to use a network based storage mechanism. In order to scale your NodeJS based servers you +would need to share the session storage between multiple instances of the same program so that any particular +cookied request would find its own session. + +In this example in addition to requiring fastify there are two additional modules being required. These are + +- [fastify-session](https://www.npmjs.com/package/fastify-session) +- [fastify-cookie](https://www.npmjs.com/package/fastify-cookie) + +The fastify cookie plugin will add support to read and set cookie headers. The fastify session plugin provides session +based storage on the localhost. + +## Simple local https session demonstration using local storage + +[https-session-server](./https-session-server/https-session-server.js) + +Another simple demonstration is the use of https on your local machine using local nodeJS based storage. In order to +start **fastify** as an https server it is necessary to provide certificates. Localhost certificates have been provided +to save the user the difficulty of creating one. This is a command to create certificates. + +```shell script +openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout selfsigned.key -out selfsigned.crt -subj "/CN=localhost" +``` + +The key and cert are created using [openssl](https://www.openssl.org/) and were created on a linux ubuntu system. The +commands are similar for osx. + +## Local https session using redis + +[https-session-server-redis](./https-session-server-redis/https-session-server-redis.js) + +A more realistic example of creating a session on fastify would involve using a network based store. This would allow +multiple instances of fastify to access the session data when a cookie is sent. +In order to run this you either have to have a local [redis](https://redislabs.com/get-started-with-redis/) running or +use docker. If you have [docker](https://www.docker.com/products/docker-desktop) installed then can simple bring the +service up + +```shell script +docker-compose up +``` + +The server will be running on port 3000 and the session will be stored in the redis cache. + +Each time you hit the url the response will tell you how many times you requested this URL. + +```json +{ +"hello": "route requested 6 times in this session" +} +``` + +Note the use of **await** when registering **fastify-redis** as we want the redis client available when we give the +fastify session its options. diff --git a/servers/fastify/session/http-session-server/http-session-server.js b/servers/fastify/session/http-session-server/http-session-server.js new file mode 100644 index 00000000..29085cc8 --- /dev/null +++ b/servers/fastify/session/http-session-server/http-session-server.js @@ -0,0 +1,34 @@ +'use strict' + +/* These examples are based on those from the fastify-session plugin readme */ + +const Fastify = require('fastify') +const fastifySession = require('fastify-session') +const fastifyCookie = require('fastify-cookie') + +const APP_PORT = process.env.PORT || 3000 +const fastify = Fastify({ logger: true }) +fastify.register(fastifyCookie) +const sessionOptions = { + secret: 'a secret with minimum length of 32 characters', + cookieName: 'example-name', + cookie: { + secure: false, + maxAge: 1000 * 60 * 3 + } +} +fastify.register(fastifySession, sessionOptions) + +fastify.get('/', async (request, reply) => { + if (request.session) { + request.session.count = request.session.count ? request.session.count + 1 : 1 + } + return { hello: `route requested ${request.session.count} times in this session` } +}) + +fastify.listen(APP_PORT, function (err) { + if (err) { + fastify.log.error(err) + process.exit(1) + } +}) diff --git a/servers/fastify/session/https-session-server-redis/Dockerfile b/servers/fastify/session/https-session-server-redis/Dockerfile new file mode 100644 index 00000000..beff4495 --- /dev/null +++ b/servers/fastify/session/https-session-server-redis/Dockerfile @@ -0,0 +1,15 @@ +FROM node:12-alpine + +USER node + +RUN mkdir /home/node/code + +WORKDIR /home/node/code + +COPY --chown=node:node package.json package-lock.json ./ + +RUN npm ci + +COPY --chown=node:node . . + +CMD ["node", "https-session-server-redis.js"] diff --git a/servers/fastify/session/https-session-server-redis/certificates/selfsigned.crt b/servers/fastify/session/https-session-server-redis/certificates/selfsigned.crt new file mode 100644 index 00000000..444a6da8 --- /dev/null +++ b/servers/fastify/session/https-session-server-redis/certificates/selfsigned.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDCTCCAfGgAwIBAgIUGSuVcr1tSWFodB3chT1HG3ExSdEwDQYJKoZIhvcNAQEL +BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTIwMDgxNjEzMzAwN1oXDTIxMDgx +NjEzMzAwN1owFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAx+TCGDTi5hULfciEG18Ec6M9aL+CbjQxFVKpLpoE2uKG +kJesSjze489PFV/355ai+afx/FV6yUSFTd5ZiBSFRPWecZ2/QVZdbkrdUjCF4EKN ++z40arZpb5l82R6RXgIfcZkcTjqrfX4TXoWrC5PhmdOtgG/BWCtwDSOBYfCb3uRP +iWyD2+9OLblbjOezuxrOYR6N6R+bIFtacp0Agunyd88RYeuYTTjWMmridf29Nip4 +Tyg6fFmFcJ0BhzyrQQM95eSDoABJv7V001WUiXGI/Y47XdhUxL4J1F5ZEbp3GglP +lj5kzip4hIN4AOdRJOPiRDtJSUdHcBqc1qi1IJcj4QIDAQABo1MwUTAdBgNVHQ4E +FgQU9TlVrEOHqghi+3yxCL2dDOBwH8kwHwYDVR0jBBgwFoAU9TlVrEOHqghi+3yx +CL2dDOBwH8kwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEANDOt +uiMWeQhm8b/aBPlmC6ABDsQRGsZX9j3mlKcdoQmLfXJc6ZtfK+gi54hYQWDcprp+ +Xa2odGLi1kKmn1Sv1kjHhA7sUrSZKV+1mAujDkO9lwygIRoqPqeW9DX4g8mfxWXd +X5zQ5ZaO8gAG1qgdGAut3JU0GLCduDBRJ6UJvzYrVGGY6Uhocp1u6u2drhC8QtJk +x4/piT/JFFzdGTAwpUCsYkElTW5sIEM4Nx1+g37My/KQqajWI38paa2TpMOiI5rU +MtVgL8QAKlly1TA8lmXtfwsod3Y4gnyd6F7Jr4Vzx2OcCPZn0uyz7lQRqbHT3hNU +ieLvVoq75jbffPxewQ== +-----END CERTIFICATE----- diff --git a/servers/fastify/session/https-session-server-redis/certificates/selfsigned.key b/servers/fastify/session/https-session-server-redis/certificates/selfsigned.key new file mode 100644 index 00000000..b0473b89 --- /dev/null +++ b/servers/fastify/session/https-session-server-redis/certificates/selfsigned.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDH5MIYNOLmFQt9 +yIQbXwRzoz1ov4JuNDEVUqkumgTa4oaQl6xKPN7jz08VX/fnlqL5p/H8VXrJRIVN +3lmIFIVE9Z5xnb9BVl1uSt1SMIXgQo37PjRqtmlvmXzZHpFeAh9xmRxOOqt9fhNe +hasLk+GZ062Ab8FYK3ANI4Fh8Jve5E+JbIPb704tuVuM57O7Gs5hHo3pH5sgW1py +nQCC6fJ3zxFh65hNONYyauJ1/b02KnhPKDp8WYVwnQGHPKtBAz3l5IOgAEm/tXTT +VZSJcYj9jjtd2FTEvgnUXlkRuncaCU+WPmTOKniEg3gA51Ek4+JEO0lJR0dwGpzW +qLUglyPhAgMBAAECggEAF/br8hz9CtqBCy5r8CAfF4H9jb5P88hcDhNf5w9d/6Pi +wBj+9dOAYU1sTMK5pNEhbs7cqwTQeKq3VJOQpkjXhWHxAewIjtu8zck56W2Zzz4L +aZCWliiSUWfUWO9aPCwC+wqBIzvTbXMc/VsHG5c6F8gR5/D9/AURJPIZw9Ulyr3c +IhAodL3SbqZUr7WT3ZzHeQ5WsDqq8Y8lOSIgemRO5RAkv3r7FQgUyoWu6P3fQTSd +Eb6ZLQ6xRnxNvKDGea1Bv5DI1Nuni9Cv7XoVDatUukYXMQqYrQWNXS20/OVzJxF2 +tNsCbG0pZcEp0cf2tWTQHyYN/iMWEA63Aox0BGFkVQKBgQDtKq0EncX9zyiGreOD +WV6PSQamN4rfQlzkN77U+WNut3JDwUv5CdavpTt2Qr0g/wLsmovKpMUnwhERyO4X +JBL41/eAOtHeQzwYxE7aoVGFPS4/3QIGV8Bqd0rHgYJHztfa26WJWJcnIpwWBZgY +Oao1xlveI+3zPO++ajw8OA2HywKBgQDXxFzzb1oNM6E9+w+r/kXW1fUM2gln5RgU +Jdc+H/ckiJSGes3e7ci5zek53vgCMiQdmQ6kDON/bnArRg+ol7fCC9sUeuXg1TA4 +cNaeBjf7fQhzb1CQlJp+h38Vy7NgkvPAkgI4PWJihgT7QEsiMn+k3mBHAXkfcbuh +WwJ2mbUVgwKBgBcaQSBh/hdrRpdX+QGigwOSKYOnhW+aF1Jj28MDSBxQ4mCXQ79O +pgsWHWS3u5SrQq2poFRtGId28BK7b/XxHaf/4awsDqWIByKifMvvSvGftBGkhb34 +blXwqOgmRXqZO42mN8nZR2AYjvvWL6qsc1gpqmlJNrSrCu+RiayUCT1hAoGBAKaW +NgnBdC5zKU+4Uh5BwFwhbwRQJyju6QtNOAUAGwk65il6EQ7IWcyS3TnQG31ehyHO +9U3VoaPWeYX/nsFU+gw4qRoD1Q4kqwk4nYr+VCS4IVk2nWYzRaDhLk5+qmyqqMWK +NWqEgjx9KsVtm1S41nJNOto3mfOcFPh8UseM3xHPAoGBAKzHshWR+jmRrVp0XKrz +Yl/Vz1pCIg1bbQk9iip08FqJTpMMd3sr/XS/ULX5jaOeEQcT4JQRr8H84rhunoBu +AMCPCbqLNjpyxxY6zWftYOx4chLECh6/ro/EOOiJnxrQB5AHcHOoHi0mjl+Lux/q +1jbqcR5IfkapWhD2xuyLgVOz +-----END PRIVATE KEY----- diff --git a/servers/fastify/session/https-session-server-redis/docker-compose.yml b/servers/fastify/session/https-session-server-redis/docker-compose.yml new file mode 100644 index 00000000..ca951874 --- /dev/null +++ b/servers/fastify/session/https-session-server-redis/docker-compose.yml @@ -0,0 +1,15 @@ +version: "3" +services: + fastify-session-with-redis: + build: . + ports: + - "3000:3000" + volumes: + - .:/home/node/code + - /home/node/code/node_modules + depends_on: + - redis + redis: + image: redis:6.0.5 + ports: + - "6379:6379" diff --git a/servers/fastify/session/https-session-server-redis/https-session-server-redis.js b/servers/fastify/session/https-session-server-redis/https-session-server-redis.js new file mode 100644 index 00000000..1ce1e165 --- /dev/null +++ b/servers/fastify/session/https-session-server-redis/https-session-server-redis.js @@ -0,0 +1,71 @@ +'use strict' + +const path = require('path') +const Fastify = require('fastify') +const fastifySession = require('fastify-session') +const fastifyCookie = require('fastify-cookie') +const fs = require('fs') +const isDocker = require('is-docker') +const RedisStore = require('connect-redis')(fastifySession); + +const APP_PORT = 3000 + +// the docker compose service is called redis +let host = 'localhost' +if (isDocker()) { + host = 'redis' +} + +const getCertificates = () => { + const cert = fs.readFileSync(path.join(__dirname, './certificates/selfsigned.crt'), 'utf-8') + const key = fs.readFileSync(path.join(__dirname, './certificates/selfsigned.key'), 'utf-8') + return { + cert, + key + } +} +const { key, cert } = getCertificates() + +const fastify = Fastify({ + https: { + key, + cert + }, + logger: true +}) + +const initialization = async () => { + fastify.register(fastifyCookie) + await fastify.register(require('fastify-redis'), { host }) + const { redis } = fastify; + const sessionOptions = { + secret: 'a secret with minimum length of 32 characters', + cookieName: 'example-redis-session', + cookie: { + secure: true, + maxAge: 1000 * 60 * 3 + }, + store: new RedisStore({ client: redis }) + } + fastify.register(fastifySession, sessionOptions) + + fastify.get('/', (request, reply) => { + if (request.session) { + request.session.count = request.session.count ? request.session.count + 1 : 1 + } + return { hello: `route requested ${request.session.count} times in this session` } + }) + + fastify.listen(APP_PORT, '0.0.0.0', (err) => { + if (err) { + fastify.log.error(err) + process.exit(1) + } + }) +} + +initialization().catch((err) => { + if (err) { + fastify.log.error(err) + } +}) diff --git a/servers/fastify/session/https-session-server-redis/package.json b/servers/fastify/session/https-session-server-redis/package.json new file mode 100644 index 00000000..d6583684 --- /dev/null +++ b/servers/fastify/session/https-session-server-redis/package.json @@ -0,0 +1,20 @@ +{ + "name": "https-session-server-redis", + "version": "1.0.0", + "description": "", + "main": "https-session-server-redis.js", + "scripts": { + "start": "node https-session-server-redis.js" + }, + "keywords": [], + "author": "", + "license": "MIT", + "dependencies": { + "connect-redis": "^5.0.0", + "fastify": "^3.2.0", + "fastify-cookie": "^4.0.2", + "fastify-redis": "^4.0.3", + "fastify-session": "^5.0.0", + "is-docker": "^2.1.1" + } +} diff --git a/servers/fastify/session/https-session-server/certificates/selfsigned.crt b/servers/fastify/session/https-session-server/certificates/selfsigned.crt new file mode 100644 index 00000000..444a6da8 --- /dev/null +++ b/servers/fastify/session/https-session-server/certificates/selfsigned.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDCTCCAfGgAwIBAgIUGSuVcr1tSWFodB3chT1HG3ExSdEwDQYJKoZIhvcNAQEL +BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTIwMDgxNjEzMzAwN1oXDTIxMDgx +NjEzMzAwN1owFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAx+TCGDTi5hULfciEG18Ec6M9aL+CbjQxFVKpLpoE2uKG +kJesSjze489PFV/355ai+afx/FV6yUSFTd5ZiBSFRPWecZ2/QVZdbkrdUjCF4EKN ++z40arZpb5l82R6RXgIfcZkcTjqrfX4TXoWrC5PhmdOtgG/BWCtwDSOBYfCb3uRP +iWyD2+9OLblbjOezuxrOYR6N6R+bIFtacp0Agunyd88RYeuYTTjWMmridf29Nip4 +Tyg6fFmFcJ0BhzyrQQM95eSDoABJv7V001WUiXGI/Y47XdhUxL4J1F5ZEbp3GglP +lj5kzip4hIN4AOdRJOPiRDtJSUdHcBqc1qi1IJcj4QIDAQABo1MwUTAdBgNVHQ4E +FgQU9TlVrEOHqghi+3yxCL2dDOBwH8kwHwYDVR0jBBgwFoAU9TlVrEOHqghi+3yx +CL2dDOBwH8kwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEANDOt +uiMWeQhm8b/aBPlmC6ABDsQRGsZX9j3mlKcdoQmLfXJc6ZtfK+gi54hYQWDcprp+ +Xa2odGLi1kKmn1Sv1kjHhA7sUrSZKV+1mAujDkO9lwygIRoqPqeW9DX4g8mfxWXd +X5zQ5ZaO8gAG1qgdGAut3JU0GLCduDBRJ6UJvzYrVGGY6Uhocp1u6u2drhC8QtJk +x4/piT/JFFzdGTAwpUCsYkElTW5sIEM4Nx1+g37My/KQqajWI38paa2TpMOiI5rU +MtVgL8QAKlly1TA8lmXtfwsod3Y4gnyd6F7Jr4Vzx2OcCPZn0uyz7lQRqbHT3hNU +ieLvVoq75jbffPxewQ== +-----END CERTIFICATE----- diff --git a/servers/fastify/session/https-session-server/certificates/selfsigned.key b/servers/fastify/session/https-session-server/certificates/selfsigned.key new file mode 100644 index 00000000..b0473b89 --- /dev/null +++ b/servers/fastify/session/https-session-server/certificates/selfsigned.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDH5MIYNOLmFQt9 +yIQbXwRzoz1ov4JuNDEVUqkumgTa4oaQl6xKPN7jz08VX/fnlqL5p/H8VXrJRIVN +3lmIFIVE9Z5xnb9BVl1uSt1SMIXgQo37PjRqtmlvmXzZHpFeAh9xmRxOOqt9fhNe +hasLk+GZ062Ab8FYK3ANI4Fh8Jve5E+JbIPb704tuVuM57O7Gs5hHo3pH5sgW1py +nQCC6fJ3zxFh65hNONYyauJ1/b02KnhPKDp8WYVwnQGHPKtBAz3l5IOgAEm/tXTT +VZSJcYj9jjtd2FTEvgnUXlkRuncaCU+WPmTOKniEg3gA51Ek4+JEO0lJR0dwGpzW +qLUglyPhAgMBAAECggEAF/br8hz9CtqBCy5r8CAfF4H9jb5P88hcDhNf5w9d/6Pi +wBj+9dOAYU1sTMK5pNEhbs7cqwTQeKq3VJOQpkjXhWHxAewIjtu8zck56W2Zzz4L +aZCWliiSUWfUWO9aPCwC+wqBIzvTbXMc/VsHG5c6F8gR5/D9/AURJPIZw9Ulyr3c +IhAodL3SbqZUr7WT3ZzHeQ5WsDqq8Y8lOSIgemRO5RAkv3r7FQgUyoWu6P3fQTSd +Eb6ZLQ6xRnxNvKDGea1Bv5DI1Nuni9Cv7XoVDatUukYXMQqYrQWNXS20/OVzJxF2 +tNsCbG0pZcEp0cf2tWTQHyYN/iMWEA63Aox0BGFkVQKBgQDtKq0EncX9zyiGreOD +WV6PSQamN4rfQlzkN77U+WNut3JDwUv5CdavpTt2Qr0g/wLsmovKpMUnwhERyO4X +JBL41/eAOtHeQzwYxE7aoVGFPS4/3QIGV8Bqd0rHgYJHztfa26WJWJcnIpwWBZgY +Oao1xlveI+3zPO++ajw8OA2HywKBgQDXxFzzb1oNM6E9+w+r/kXW1fUM2gln5RgU +Jdc+H/ckiJSGes3e7ci5zek53vgCMiQdmQ6kDON/bnArRg+ol7fCC9sUeuXg1TA4 +cNaeBjf7fQhzb1CQlJp+h38Vy7NgkvPAkgI4PWJihgT7QEsiMn+k3mBHAXkfcbuh +WwJ2mbUVgwKBgBcaQSBh/hdrRpdX+QGigwOSKYOnhW+aF1Jj28MDSBxQ4mCXQ79O +pgsWHWS3u5SrQq2poFRtGId28BK7b/XxHaf/4awsDqWIByKifMvvSvGftBGkhb34 +blXwqOgmRXqZO42mN8nZR2AYjvvWL6qsc1gpqmlJNrSrCu+RiayUCT1hAoGBAKaW +NgnBdC5zKU+4Uh5BwFwhbwRQJyju6QtNOAUAGwk65il6EQ7IWcyS3TnQG31ehyHO +9U3VoaPWeYX/nsFU+gw4qRoD1Q4kqwk4nYr+VCS4IVk2nWYzRaDhLk5+qmyqqMWK +NWqEgjx9KsVtm1S41nJNOto3mfOcFPh8UseM3xHPAoGBAKzHshWR+jmRrVp0XKrz +Yl/Vz1pCIg1bbQk9iip08FqJTpMMd3sr/XS/ULX5jaOeEQcT4JQRr8H84rhunoBu +AMCPCbqLNjpyxxY6zWftYOx4chLECh6/ro/EOOiJnxrQB5AHcHOoHi0mjl+Lux/q +1jbqcR5IfkapWhD2xuyLgVOz +-----END PRIVATE KEY----- diff --git a/servers/fastify/session/https-session-server/https-session-server.js b/servers/fastify/session/https-session-server/https-session-server.js new file mode 100644 index 00000000..127ed932 --- /dev/null +++ b/servers/fastify/session/https-session-server/https-session-server.js @@ -0,0 +1,52 @@ +'use strict' + +const path = require('path') +const Fastify = require('fastify') +const fastifySession = require('fastify-session') +const fastifyCookie = require('fastify-cookie') +const fs = require('fs') + +const APP_PORT = process.env.PORT || 3000 +const getCertificates = () => { + const cert = fs.readFileSync(path.join(__dirname, './certificates/selfsigned.crt'), 'utf-8') + const key = fs.readFileSync(path.join(__dirname, './certificates/selfsigned.key'), 'utf-8') + return { + cert, + key + } +} +const { key, cert } = getCertificates() + +const fastify = Fastify({ + logger: true, + https: { + key, + cert + } +}) + +fastify.register(fastifyCookie) + +const sessionOptions = { + secret: 'a secret with minimum length of 32 characters', + cookieName: 'example-using-https-connection', + cookie: { + secure: true, + maxAge: 1000 * 60 * 3 + } +} +fastify.register(fastifySession, sessionOptions) + +fastify.get('/', async (request, reply) => { + if (request.session) { + request.session.count = request.session.count ? request.session.count + 1 : 1 + } + return { hello: `route requested ${request.session.count} times in this session` } +}) + +fastify.listen(APP_PORT, function (err) { + if (err) { + fastify.log.error(err) + process.exit(1) + } +})