Skip to content

Commit

Permalink
Add express-session dependency (#1341)
Browse files Browse the repository at this point in the history
  • Loading branch information
awidener3 authored Jan 26, 2024
1 parent fb3c43c commit 9f95906
Show file tree
Hide file tree
Showing 30 changed files with 361 additions and 86 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

## Next version

- Put your changes here...
- Add `express-session` dependency.
- The name of the directory containing secrets and HTTPS keys is now configurable using the `secretsDir` parameter (default value is `secrets`).

## 0.21.16

Expand Down
37 changes: 37 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,9 @@ Roosevelt apps created with the app generator come with the following notable [n
- Default shorthand:
- `npm run c`
- Script is short for: `node ./node_modules/roosevelt/lib/scripts/certsGenerator.js`
- `npm run generate-session-secret`: Generates a secret key for the `express-session` module.
- Script is short for: `node ./node_modules/roosevelt/lib/scripts/sessionSecretGenerator.js`
- `npm run generate-secrets`: Generates self-signed HTTPS certs and a `express-session` secret.
- `npm run audit-config`: Scans current `rooseveltConfig` and `scripts` in `package.json` and warns about any parameters or npm scripts that don't match the current Roosevelt API:
- Default shorthand:
- `npm run a`
Expand Down Expand Up @@ -380,6 +383,12 @@ Resolves to:

- Default: *[String]* The directory where your app's `package.json` is located.

- `secretsDir`: Directory that stores certs, keys, and secrets.

- Default: *[String]* `secrets`.

- Important: Changing this value will require updating `.gitignore`.

- `localhostOnly`: Listen only to requests coming from localhost in production mode. This is useful in environments where it is expected that HTTP requests to your app will be proxied through a more traditional web server like Apache or nginx.

- Default: *[Boolean]* `true`.
Expand Down Expand Up @@ -475,6 +484,33 @@ Resolves to:
- Default: *[Object]*
The default options are specified in the [helmet docs](https://helmetjs.github.io/), with the exception of the upgrade-insecure-requests in the content security policy, which has been removed.

- `expressSession`: Parameter(s) to pass to the [express-session](https://github.com/expressjs/session) module. Can pass in a boolean (`true`/`false`), or supplying an object that allows further configuration using express-session parameters.

- Default: *[Boolean]* `true`

- If `expressSession` is set to `true`, it will use the configuration below:

```json
{
"resave": false,
"saveUninitialized": false,
}
```

- Optional: An object containing parameters that are specified in the [express-session docs](https://github.com/expressjs/session?tab=readme-ov-file#api) (Note: `secret` is generated by Roosevelt).

```json
// using true/false
"expressSession": true

// using express-session parameters
"expressSession:" {
"resave": false,
"saveUninitialized": false,
...other express-session parameters
}
```

- `toobusy`: Parameters to pass to the [node-toobusy](https://github.com/STRML/node-toobusy) module.

- `maxLagPerRequest`: *[Number]* Maximum amount of time (in milliseconds) a given request is allowed to take before being interrupted with a [503 error](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#5xx_Server_errors).
Expand Down Expand Up @@ -1287,6 +1323,7 @@ In addition to exposing a number of variables to Express and providing the MVC i

- Includes the [compression](https://github.com/expressjs/compression) middleware.
- Includes the [cookie-parser](https://github.com/expressjs/cookie-parser) middleware.
- Includes the [express-session](https://github.com/expressjs/session) middleware.
- Includes the [helmet](https://github.com/helmetjs/helmet) middleware.
- Logs HTTP requests to the console using [morgan](https://github.com/expressjs/morgan), specifically `morgan('combined')`.
- Includes the [method-override](https://github.com/expressjs/method-override) middleware.
Expand Down
2 changes: 2 additions & 0 deletions lib/defaults/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@
"requestCert": null,
"rejectUnauthorized": null
},
"expressSession": true,
"secretsDir": "secrets",
"modelsPath": "mvc/models",
"viewsPath": "mvc/views",
"viewEngine": "none",
Expand Down
30 changes: 18 additions & 12 deletions lib/scripts/certsGenerator.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ if (module.parent) {
} else {
certsGenerator()
}
function certsGenerator (appDir) {

function certsGenerator (appDir, keyFolder) {
const selfsigned = require('selfsigned')
const pem = selfsigned.generate(null, {
keySize: 2048, // the size for the private key in bits (default: 1024)
Expand All @@ -20,32 +21,37 @@ function certsGenerator (appDir) {
const fs = require('fs')
const key = pem.private
const cert = pem.cert
const keyFolder = './certs'
const path = require('path')
const sourceParams = require('../sourceParams')

if (!appDir) {
let processEnv
if (fs.existsSync(path.join(process.cwd(), 'node_modules')) === false) {
processEnv = process.cwd()
} else {
processEnv = undefined
}
const processEnv = fs.existsSync(path.join(process.cwd(), 'node_modules'))
? process.cwd()
: undefined

appDir = processEnv
}

if (!keyFolder) {
const params = sourceParams({ appDir: process.cwd() })
keyFolder = params.secretsDir
}

try {
if (!fs.existsSync(keyFolder)) {
fs.mkdirSync(keyFolder)
// make secrets folder if non-existent
if (!fs.existsSync(appDir + '/' + keyFolder)) {
const path = appDir + '/' + keyFolder
fs.mkdirSync(path)
}

fs.writeFileSync('./certs/key.pem', key, err => {
fs.writeFileSync(keyFolder + '/key.pem', key, err => {
if (err) {
console.error(err)
}
// file written successfully
})

fs.writeFileSync('./certs/cert.pem', cert, err => {
fs.writeFileSync(keyFolder + '/cert.pem', cert, err => {
if (err) {
console.error(err)
}
Expand Down
6 changes: 6 additions & 0 deletions lib/scripts/configAuditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,9 @@ function configAudit (appDir) {
case 'modelsPath':
checkTypes(userParam, key, ['string'])
break
case 'secretsDir':
checkTypes(userParam, key, ['string'])
break
case 'formidable':
checkTypes(userParam, key, ['boolean', 'object'])
break
Expand Down Expand Up @@ -252,6 +255,9 @@ function configAudit (appDir) {
case 'helmet':
checkTypes(userParam, key, ['object'])
break
case 'expressSession':
checkTypes(userParam, key, ['boolean', 'object'])
break
case 'bodyParser': {
checkTypes(userParam, key, ['object'])
const bodyParserParam = userParam || {}
Expand Down
44 changes: 44 additions & 0 deletions lib/scripts/sessionSecretGenerator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
if (module.parent) {
module.exports = {
sessionSecretGenerator
}
} else {
sessionSecretGenerator()
}

function sessionSecretGenerator (appDir, keyFolder) {
const crypto = require('crypto')
const fs = require('fs')
const path = require('path')
const sourceParams = require('../sourceParams')

if (!appDir) {
const processEnv = fs.existsSync(path.join(process.cwd(), 'node_modules'))
? process.cwd()
: undefined

appDir = processEnv
}

if (!keyFolder) {
const params = sourceParams({ appDir: process.cwd() })
keyFolder = params.secretsDir
}

try {
// make secrets folder if non-existent
if (!fs.existsSync(appDir + '/' + keyFolder)) {
const path = appDir + '/' + keyFolder
fs.mkdirSync(path)
}

fs.writeFileSync(keyFolder + '/sessionSecret.json', JSON.stringify({
secret: crypto.randomUUID()
}), err => {
if (err) {
console.error(err)
}
// file written successfully
})
} catch {}
}
26 changes: 25 additions & 1 deletion lib/setExpressConfigs.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
// configure specific express options

const fs = require('fs')
require('@colors/colors')

const morgan = require('morgan') // express logger
const express = require('express')
const session = require('express-session')
const helmet = require('helmet')

module.exports = function (app) {
Expand Down Expand Up @@ -45,6 +46,29 @@ module.exports = function (app) {
// set morgan
app.set('morgan', morgan)

// enable express-session
if (params.expressSession) {
const secret = JSON.parse(fs.readFileSync(params.secretsDir + '/sessionSecret.json', 'utf-8')).secret

if (typeof params.expressSession === 'boolean') {
// user wants default config
app.use(session({
// used to sign the session ID cookie
secret,
// setting to true forces the session to be saved to the session store even if session wasn't modified during the request
resave: false,
// setting to true forces an "uninitialized" session to be saved to the store - a session is "uninitialized" when it is new but not modified
saveUninitialized: false
}))
} else {
// user has supplied their own configuration
app.use(session({
...params.expressSession,
secret // generate an express session key
}))
}
}

// set helmet middleware
if (params.mode !== 'development') {
let contentSecurityPolicy = params.helmet.contentSecurityPolicy
Expand Down
8 changes: 7 additions & 1 deletion lib/sourceParams.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ module.exports = (params, app, appSchema) => {
*/
const schema = {
appDir: {
default: params.appDir
default: appDir
},
port: {
envVar: ['HTTP_PORT', 'NODE_PORT'],
Expand Down Expand Up @@ -155,6 +155,9 @@ module.exports = (params, app, appSchema) => {
helmet: {
default: defaults.helmet
},
expressSession: {
default: defaults.expressSession
},
bodyParser: {
urlEncoded: {
default: defaults.bodyParser.urlEncoded
Expand Down Expand Up @@ -214,6 +217,9 @@ module.exports = (params, app, appSchema) => {
default: null
}
},
secretsDir: {
default: defaults.secretsDir
},
modelsPath: {
default: defaults.modelsPath
},
Expand Down
65 changes: 65 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"execa": "8.0.1",
"express": "4.18.2",
"express-html-validator": "0.2.4",
"express-session": "1.17.3",
"formidable": "3.5.1",
"fs-extra": "11.2.0",
"helmet": "7.1.0",
Expand Down
Loading

0 comments on commit 9f95906

Please sign in to comment.