├── .eslintignore ├── .eslintrc ├── .github └── workflows │ ├── nodejs.yml │ ├── pkg.pr.new.yml │ └── release.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── __snapshots__ └── session.test.ts.js ├── package.json ├── src ├── app.ts ├── app │ ├── extend │ │ └── application.ts │ └── middleware │ │ └── session.ts ├── config │ └── config.default.ts ├── index.ts ├── types.ts └── typings │ └── index.d.ts ├── test ├── app │ └── middleware │ │ └── session.test.ts └── fixtures │ ├── chips │ ├── app │ │ ├── controller │ │ │ └── home.js │ │ └── router.js │ ├── config │ │ └── config.default.js │ └── package.json │ ├── cookie-session │ ├── app │ │ ├── controller │ │ │ └── home.js │ │ └── router.js │ ├── config │ │ └── config.default.js │ └── package.json │ ├── httponly-false-session │ ├── app │ │ ├── controller │ │ │ └── home.js │ │ └── router.js │ ├── config │ │ └── config.default.js │ └── package.json │ ├── logValue-false-session │ ├── app │ │ ├── controller │ │ │ └── home.js │ │ └── router.js │ ├── config │ │ └── config.default.js │ └── package.json │ ├── memory-session-generator │ ├── app.js │ ├── app │ │ ├── controller │ │ │ └── home.js │ │ └── router.js │ ├── config │ │ └── config.default.js │ └── package.json │ ├── memory-session │ ├── app.js │ ├── app │ │ ├── controller │ │ │ └── home.js │ │ └── router.js │ ├── config │ │ └── config.default.js │ └── package.json │ ├── redis-session │ ├── app.js │ ├── app │ │ ├── controller │ │ │ └── home.js │ │ └── router.js │ ├── config │ │ ├── config.default.js │ │ └── plugin.js │ └── package.json │ ├── samesite-none-session │ ├── app │ │ ├── controller │ │ │ └── home.js │ │ └── router.js │ ├── config │ │ └── config.default.js │ └── package.json │ └── session-maxage-session │ ├── app │ ├── controller │ │ └── home.js │ └── router.js │ ├── config │ └── config.default.js │ └── package.json └── tsconfig.json /.eslintignore: -------------------------------------------------------------------------------- 1 | test/fixtures 2 | coverage 3 | __snapshots__ 4 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "eslint-config-egg/typescript", 4 | "eslint-config-egg/lib/rules/enforce-node-prefix" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /.github/workflows/nodejs.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | Job: 11 | name: Node.js 12 | uses: node-modules/github-actions/.github/workflows/node-test-mysql.yml@master 13 | with: 14 | version: '18.19.0, 18, 20, 22' 15 | secrets: 16 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 17 | -------------------------------------------------------------------------------- /.github/workflows/pkg.pr.new.yml: -------------------------------------------------------------------------------- 1 | name: Publish Any Commit 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | build: 6 | runs-on: ubuntu-latest 7 | 8 | steps: 9 | - name: Checkout code 10 | uses: actions/checkout@v4 11 | 12 | - run: corepack enable 13 | - uses: actions/setup-node@v4 14 | with: 15 | node-version: 20 16 | 17 | - name: Install dependencies 18 | run: npm install 19 | 20 | - name: Build 21 | run: npm run prepublishOnly --if-present 22 | 23 | - run: npx pkg-pr-new publish 24 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | 7 | jobs: 8 | release: 9 | name: Node.js 10 | uses: eggjs/github-actions/.github/workflows/node-release.yml@master 11 | secrets: 12 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 13 | GIT_TOKEN: ${{ secrets.GIT_TOKEN }} 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | logs/ 2 | npm-debug.log 3 | node_modules/ 4 | coverage/ 5 | test/fixtures/**/run 6 | .DS_Store 7 | .tshy* 8 | .eslintcache 9 | dist 10 | package-lock.json 11 | .package-lock.json 12 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [4.0.1](https://github.com/eggjs/session/compare/v4.0.0...v4.0.1) (2025-01-21) 4 | 5 | 6 | ### Bug Fixes 7 | 8 | * should export sessionStore ([#21](https://github.com/eggjs/session/issues/21)) ([30e2b25](https://github.com/eggjs/session/commit/30e2b25864edf180695e42a90a8a8bbd9399d63a)) 9 | 10 | ## [4.0.0](https://github.com/eggjs/session/compare/v3.3.0...v4.0.0) (2025-01-19) 11 | 12 | 13 | ### ⚠ BREAKING CHANGES 14 | 15 | * drop Node.js < 18.19.0 support 16 | 17 | part of https://github.com/eggjs/egg/issues/3644 18 | 19 | https://github.com/eggjs/egg/issues/5257 20 | 21 | 23 | ## Summary by CodeRabbit 24 | 25 | ## Release Notes 26 | 27 | - **Package Upgrade** 28 | - Renamed package from `egg-session` to `@eggjs/session` 29 | - Updated Node.js compatibility to version 18.19.0+ 30 | 31 | - **New Features** 32 | - Enhanced session configuration with improved type safety 33 | - Added support for more granular session management 34 | - Improved logging and security configurations 35 | 36 | - **Breaking Changes** 37 | - Dropped support for Node.js versions below 18.19.0 38 | - Migrated from generator functions to async/await syntax 39 | - Updated session middleware and configuration structure 40 | 41 | - **Performance** 42 | - Updated dependencies, including `koa-session` to version 7.0.2 43 | - Optimized session store handling 44 | 45 | - **Security** 46 | - Strengthened default session configurations 47 | - Added warnings for potential security risks in session settings 48 | 49 | 50 | ### Features 51 | 52 | * support cjs and esm both by tshy ([#20](https://github.com/eggjs/session/issues/20)) ([b1a96e5](https://github.com/eggjs/session/commit/b1a96e5c254dadf6664499e7018246898751db2a)) 53 | 54 | 3.3.0 / 2021-03-23 55 | ================== 56 | 57 | **features** 58 | * [[`fb47f1b`](http://github.com/eggjs/egg-session/commit/fb47f1b5dd5037def631066a95f36e9c2488e5f3)] - feat: as default not to log the session val (#17) (clchenliang <>) 59 | 60 | **others** 61 | * [[`a21e6fe`](http://github.com/eggjs/egg-session/commit/a21e6fe89b5228a4fc9609e775f0909c2bb465ee)] - test: add test case for session maxAge (#16) (Yiyu He <>) 62 | 63 | 3.2.0 / 2020-05-12 64 | ================== 65 | 66 | **features** 67 | * [[`39629ab`](http://github.com/eggjs/egg-session/commit/39629abe1c22ee963f80ab69c18a94c3a3f81cd6)] - feat: warn if httpOnly set to false (#13) (Yiyu He <>) 68 | 69 | **others** 70 | * [[`c0d3cdc`](http://github.com/eggjs/egg-session/commit/c0d3cdc23b9138cecb9d30f8e523bdd593e009fb)] - deps: koa-session@6 (#15) (Yiyu He <>) 71 | * [[`75c8ee6`](http://github.com/eggjs/egg-session/commit/75c8ee6c4143362edced399d66c11834bc00ae5f)] - test: add sameSite=none test case (#14) (fengmk2 <>) 72 | * [[`c9865a7`](http://github.com/eggjs/egg-session/commit/c9865a7e05db773a1a37c296f2170e6ffa899761)] - chore: update travis (TZ | 天猪 <>) 73 | * [[`3168fb7`](http://github.com/eggjs/egg-session/commit/3168fb78877dbbc91c4d2df1ed762f9f5684f52d)] - doc: fix miss backticks (#12) (supperchong <<2267805901@qq.com>>) 74 | 75 | 3.1.0 / 2018-01-09 76 | ================== 77 | 78 | **features** 79 | * [[`e34fd6e`](http://github.com/eggjs/egg-session/commit/e34fd6e43e9ce933e5a6cb013b37af5f2f959768)] - feat: listen session events and warn (#11) (Yiyu He <>) 80 | 81 | **others** 82 | * [[`08fd920`](http://github.com/eggjs/egg-session/commit/08fd920cd85cb1c528b74f00e9d2605fbd2e0c86)] - docs: update readme to async function (dead-horse <>) 83 | 84 | 3.0.0 / 2017-11-09 85 | ================== 86 | 87 | **others** 88 | * [[`38e901b`](http://github.com/eggjs/egg-session/commit/38e901ba06373647074530acdaa72f01d33551a7)] - refactor: upgrade koa-session, support egg2. [BREAKING CHANGE] (#10) (Yiyu He <>) 89 | 90 | 2.1.1 / 2017-06-04 91 | ================== 92 | 93 | * docs: fix License url (#9) 94 | 95 | 2.1.0 / 2017-03-02 96 | ================== 97 | 98 | * feat: support set sessionStore to null and get sessionStore (#8) 99 | 100 | 2.0.0 / 2017-02-28 101 | ================== 102 | 103 | * feat: upgrade koa-session@4, support external session store (#7) 104 | * feat: remove cookie length check (#6) 105 | * chore: upgrade deps and fix test (#5) 106 | 107 | 1.1.0 / 2016-11-17 108 | ================== 109 | 110 | * feat: check response cookie's length (#4) 111 | 112 | 1.0.0 / 2016-11-03 113 | ================== 114 | 115 | * chore: update deps and test on node v7 (#3) 116 | * test: fix appveyor yml (#2) 117 | * test: add testcase (#1) 118 | 119 | 0.0.2 / 2016-07-14 120 | ================== 121 | 122 | * fix: package.json 123 | 124 | 0.0.1 / 2016-07-14 125 | ================== 126 | 127 | * feat: init 128 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Alibaba Group Holding Limited and other contributors. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # @eggjs/session 2 | 3 | [![NPM version][npm-image]][npm-url] 4 | [![Node.js CI](https://github.com/eggjs/session/actions/workflows/nodejs.yml/badge.svg)](https://github.com/eggjs/session/actions/workflows/nodejs.yml) 5 | [![Test coverage][codecov-image]][codecov-url] 6 | [![Known Vulnerabilities][snyk-image]][snyk-url] 7 | [![npm download][download-image]][download-url] 8 | [![Node.js Version](https://img.shields.io/node/v/@eggjs/session.svg?style=flat)](https://nodejs.org/en/download/) 9 | [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](https://makeapullrequest.com) 10 | ![CodeRabbit Pull Request Reviews](https://img.shields.io/coderabbit/prs/github/eggjs/session) 11 | 12 | [npm-image]: https://img.shields.io/npm/v/@eggjs/session.svg?style=flat-square 13 | [npm-url]: https://npmjs.org/package/@eggjs/session 14 | [codecov-image]: https://codecov.io/gh/eggjs/session/branch/master/graph/badge.svg 15 | [codecov-url]: https://codecov.io/gh/eggjs/session 16 | [snyk-image]: https://snyk.io/test/npm/@eggjs/session/badge.svg?style=flat-square 17 | [snyk-url]: https://snyk.io/test/npm/@eggjs/session 18 | [download-image]: https://img.shields.io/npm/dm/@eggjs/session.svg?style=flat-square 19 | [download-url]: https://npmjs.org/package/@eggjs/session 20 | 21 | Session plugin for egg, based on [koa-session](https://github.com/koajs/session). 22 | 23 | ## Install 24 | 25 | ```bash 26 | npm i @eggjs/session 27 | ``` 28 | 29 | ## Usage 30 | 31 | egg-session is a built-in plugin in egg and enabled by default. 32 | 33 | ```js 34 | // {app_root}/config/plugin.js 35 | exports.session = true; // enable by default 36 | ``` 37 | 38 | ### External Store 39 | 40 | egg-session support external store, you can store your sessions in redis, memcached or other databases. 41 | 42 | For example, if you want to store session in redis, you must: 43 | 44 | 1. Dependent [@eggjs/redis](https://github.com/eggjs/redis) 45 | 46 | ```bash 47 | npm i --save @eggjs/redis 48 | ``` 49 | 50 | 2. Import `@eggjs/redis` as a plugin and set the configuration 51 | 52 | ```js 53 | // config/plugin.js 54 | exports.redis = { 55 | enable: true, 56 | package: '@eggjs/redis', 57 | }; 58 | ``` 59 | 60 | ```js 61 | // config/config.default.js 62 | exports.redis = { 63 | // your redis configurations 64 | }; 65 | ``` 66 | 67 | 3. Implement a session store with redis 68 | 69 | ```js 70 | // app.js 71 | 72 | module.exports = app => { 73 | // set redis session store 74 | // session store must have 3 methods 75 | // define sessionStore in `app.js` so you can access `app.redis` 76 | app.sessionStore = { 77 | async get(key) { 78 | const res = await app.redis.get(key); 79 | if (!res) return null; 80 | return JSON.parse(res); 81 | }, 82 | 83 | async set(key, value, maxAge) { 84 | // maxAge not present means session cookies 85 | // we can't exactly know the maxAge and just set an appropriate value like one day 86 | if (!maxAge) maxAge = 24 * 60 * 60 * 1000; 87 | value = JSON.stringify(value); 88 | await app.redis.set(key, value, 'PX', maxAge); 89 | }, 90 | 91 | async destroy(key) { 92 | await app.redis.del(key); 93 | }, 94 | }; 95 | 96 | // session store can be a session store class 97 | // app.sessionStore = class Store { 98 | // constructor(app) { 99 | // this.app = app; 100 | // } 101 | // async get() {} 102 | // async set() {} 103 | // async destroy() {} 104 | // }; 105 | }; 106 | ``` 107 | 108 | Once you use external session store, session is strong dependent on your external store, you can't access session if your external store is down. **Use external session stores only if necessary, avoid use session as a cache, keep session lean and stored by cookie!** 109 | 110 | ## Configuration 111 | 112 | Support all configurations in [koa-session](https://github.com/koajs/session). 113 | 114 | * logValue 115 | 116 | ```bash 117 | Support not to print the session value when session event trigger log. Default to be true. 118 | ``` 119 | 120 | [View the default configurations](https://github.com/eggjs/egg-session/blob/master/config/config.default.js) 121 | 122 | ## Questions & Suggestions 123 | 124 | Please open an issue [here](https://github.com/eggjs/egg/issues). 125 | 126 | ## License 127 | 128 | [MIT](LICENSE) 129 | 130 | ## Contributors 131 | 132 | [![Contributors](https://contrib.rocks/image?repo=eggjs/session)](https://github.com/eggjs/session/graphs/contributors) 133 | 134 | Made with [contributors-img](https://contrib.rocks). 135 | -------------------------------------------------------------------------------- /__snapshots__/session.test.ts.js: -------------------------------------------------------------------------------- 1 | exports['test/app/middlewares/session.test.js sessionStore should keep session config stable 1'] = { 2 | "key": "EGG_SESS", 3 | "maxAge": 86400000, 4 | "autoCommit": true, 5 | "overwrite": true, 6 | "httpOnly": true, 7 | "signed": true, 8 | "rolling": false, 9 | "renew": false, 10 | "logValue": true, 11 | "store": {} 12 | } 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@eggjs/session", 3 | "version": "4.0.1", 4 | "publishConfig": { 5 | "access": "public" 6 | }, 7 | "description": "session plugin for egg", 8 | "eggPlugin": { 9 | "name": "session", 10 | "exports": { 11 | "import": "./dist/esm", 12 | "require": "./dist/commonjs", 13 | "typescript": "./src" 14 | } 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "git@github.com:eggjs/session.git" 19 | }, 20 | "author": "dead_horse", 21 | "keywords": [ 22 | "egg", 23 | "egg-plugin", 24 | "eggPlugin", 25 | "session", 26 | "cookie" 27 | ], 28 | "engines": { 29 | "node": ">= 18.19.0" 30 | }, 31 | "dependencies": { 32 | "@eggjs/core": "^6.3.1", 33 | "koa-session": "^7.0.2", 34 | "zod": "^3.24.1" 35 | }, 36 | "devDependencies": { 37 | "@arethetypeswrong/cli": "^0.17.1", 38 | "@eggjs/bin": "7", 39 | "@eggjs/mock": "^6.0.5", 40 | "@eggjs/redis": "^3.0.0", 41 | "@eggjs/supertest": "^8.2.0", 42 | "@eggjs/tsconfig": "1", 43 | "@types/mocha": "10", 44 | "@types/node": "22", 45 | "egg": "^4.0.3", 46 | "eslint": "8", 47 | "eslint-config-egg": "14", 48 | "rimraf": "6", 49 | "snap-shot-it": "^7.9.10", 50 | "tshy": "3", 51 | "tshy-after": "1", 52 | "typescript": "5" 53 | }, 54 | "scripts": { 55 | "lint": "eslint --cache src test --ext .ts", 56 | "pretest": "npm run clean && npm run lint -- --fix", 57 | "test": "egg-bin test", 58 | "test:snapshot:update": "SNAPSHOT_UPDATE=1 egg-bin test", 59 | "preci": "npm run clean && npm run lint", 60 | "ci": "egg-bin cov", 61 | "postci": "npm run prepublishOnly && npm run clean", 62 | "clean": "rimraf dist", 63 | "prepublishOnly": "tshy && tshy-after && attw --pack" 64 | }, 65 | "type": "module", 66 | "tshy": { 67 | "exports": { 68 | ".": "./src/index.ts", 69 | "./package.json": "./package.json" 70 | } 71 | }, 72 | "exports": { 73 | ".": { 74 | "import": { 75 | "types": "./dist/esm/index.d.ts", 76 | "default": "./dist/esm/index.js" 77 | }, 78 | "require": { 79 | "types": "./dist/commonjs/index.d.ts", 80 | "default": "./dist/commonjs/index.js" 81 | } 82 | }, 83 | "./package.json": "./package.json" 84 | }, 85 | "files": [ 86 | "dist", 87 | "src" 88 | ], 89 | "types": "./dist/commonjs/index.d.ts", 90 | "main": "./dist/commonjs/index.js", 91 | "module": "./dist/esm/index.js" 92 | } 93 | -------------------------------------------------------------------------------- /src/app.ts: -------------------------------------------------------------------------------- 1 | import type { ILifecycleBoot, EggCore } from '@eggjs/core'; 2 | import { SessionConfig } from './config/config.default.js'; 3 | 4 | export default class AppBoot implements ILifecycleBoot { 5 | private readonly app; 6 | 7 | constructor(app: EggCore) { 8 | this.app = app; 9 | } 10 | 11 | configWillLoad() { 12 | const app = this.app; 13 | SessionConfig.parse(app.config.session); 14 | 15 | if (!app.config.session.httpOnly) { 16 | app.coreLogger.warn('[@eggjs/session]: please set `config.session.httpOnly` to true. It is very dangerous if session can read by client JavaScript.'); 17 | } 18 | app.config.coreMiddleware.push('session'); 19 | // listen on session's events 20 | app.on('session:missed', ({ ctx, key }) => { 21 | ctx.coreLogger.warn('[session][missed] key(%s)', key); 22 | }); 23 | app.on('session:expired', ({ ctx, key, value }) => { 24 | ctx.coreLogger.warn('[session][expired] key(%s) value(%j)', key, app.config.session.logValue ? value : ''); 25 | }); 26 | app.on('session:invalid', ({ ctx, key, value }) => { 27 | ctx.coreLogger.warn('[session][invalid] key(%s) value(%j)', key, app.config.session.logValue ? value : ''); 28 | }); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/app/extend/application.ts: -------------------------------------------------------------------------------- 1 | import assert from 'node:assert'; 2 | import { EggCore } from '@eggjs/core'; 3 | import type { SessionConfig } from '../../config/config.default.js'; 4 | 5 | export type SessionStore = Required['store']; 6 | 7 | export type SessionStoreOrAppSessionStoreClass = SessionStore | { 8 | new(app: Application): SessionStore; 9 | }; 10 | 11 | export default class Application extends EggCore { 12 | /** 13 | * set session external store 14 | * 15 | * ```js 16 | * app.sessionStore = { 17 | * get(key): Promise, 18 | * set(key, data): Promise, 19 | * destroy(key): Promise, 20 | * }; 21 | * 22 | * app.sessionStore = class SessionStore { 23 | * constructor(app) { 24 | * } 25 | * get(key): Promise, 26 | * set(key, data): Promise, 27 | * destroy(key): Promise, 28 | * } 29 | * ``` 30 | * @param {Class|Object} store session store class or instance 31 | */ 32 | set sessionStore(store: SessionStoreOrAppSessionStoreClass | null | undefined) { 33 | if (this.config.session.store && this.config.env !== 'unittest') { 34 | this.coreLogger.warn('[@eggjs/session] sessionStore already exists and will be overwrite'); 35 | } 36 | 37 | // support this.sessionStore = null to disable external store 38 | if (!store) { 39 | this.config.session.store = undefined; 40 | this.coreLogger.info('[@eggjs/session] sessionStore is disabled'); 41 | return; 42 | } 43 | 44 | if (typeof store === 'function') { 45 | store = new store(this); 46 | } 47 | assert(typeof store.get === 'function', 'store.get must be function'); 48 | assert(typeof store.set === 'function', 'store.set must be function'); 49 | assert(typeof store.destroy === 'function', 'store.destroy must be function'); 50 | this.config.session.store = store; 51 | } 52 | 53 | /** 54 | * get sessionStore instance 55 | */ 56 | get sessionStore(): SessionStore | undefined { 57 | return this.config.session.store; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/app/middleware/session.ts: -------------------------------------------------------------------------------- 1 | import { createSession } from 'koa-session'; 2 | 3 | export default createSession; 4 | -------------------------------------------------------------------------------- /src/config/config.default.ts: -------------------------------------------------------------------------------- 1 | import z from 'zod'; 2 | import { SessionOptions } from 'koa-session'; 3 | 4 | export const SessionConfig = SessionOptions.extend({ 5 | logValue: z.boolean().default(true), 6 | }); 7 | 8 | export type SessionConfig = z.infer; 9 | 10 | export default { 11 | session: SessionConfig.parse({ 12 | maxAge: 24 * 3600 * 1000, // ms, one day 13 | key: 'EGG_SESS', 14 | httpOnly: true, 15 | encrypt: true, 16 | }), 17 | }; 18 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import './types.js'; 2 | 3 | export * from './config/config.default.js'; 4 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | import type { SessionConfig } from './config/config.default.js'; 2 | import type { SessionStoreOrAppSessionStoreClass, SessionStore } from './app/extend/application.js'; 3 | 4 | declare module '@eggjs/core' { 5 | // add EggAppConfig overrides types 6 | interface EggAppConfig { 7 | session: SessionConfig; 8 | } 9 | 10 | interface EggCore { 11 | // add EggCore instance property 12 | set sessionStore(store: SessionStoreOrAppSessionStoreClass | null | undefined); 13 | get sessionStore(): SessionStore | undefined; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/typings/index.d.ts: -------------------------------------------------------------------------------- 1 | // make sure to import egg typings and let typescript know about it 2 | // @see https://github.com/whxaxes/blog/issues/11 3 | // and https://www.typescriptlang.org/docs/handbook/declaration-merging.html 4 | import 'egg'; 5 | -------------------------------------------------------------------------------- /test/app/middleware/session.test.ts: -------------------------------------------------------------------------------- 1 | import assert from 'node:assert'; 2 | import { scheduler } from 'node:timers/promises'; 3 | import { request, TestAgent } from '@eggjs/supertest'; 4 | import { mm, MockApplication } from '@eggjs/mock'; 5 | import snapshot from 'snap-shot-it'; 6 | 7 | describe('test/app/middlewares/session.test.js', () => { 8 | let app: MockApplication; 9 | let agent: TestAgent; 10 | afterEach(mm.restore); 11 | 12 | describe('sessionStore', () => { 13 | before(() => { 14 | app = mm.app({ baseDir: 'memory-session' }); 15 | return app.ready(); 16 | }); 17 | beforeEach(() => { 18 | agent = new TestAgent(app.callback()); 19 | }); 20 | after(() => app.close()); 21 | 22 | it('should keep session config stable', () => { 23 | snapshot(app.config.session); 24 | }); 25 | 26 | it('should get sessionStore', async () => { 27 | mm.empty(app.sessionStore, 'set'); 28 | await agent 29 | .get('/set?foo=bar') 30 | .expect(200) 31 | .expect({ foo: 'bar' }) 32 | .expect('set-cookie', /EGG_SESS=.*?;/); 33 | 34 | await agent.get('/get') 35 | .expect(200) 36 | .expect({}); 37 | }); 38 | 39 | it('should session store can be change', async () => { 40 | mm(app.config, 'env', 'local'); 41 | 42 | await agent 43 | .get('/set?foo=bar') 44 | .expect(200) 45 | .expect({ foo: 'bar' }) 46 | .expect(res => { 47 | const cookie = res.get('Set-Cookie')!.join('|'); 48 | assert(!cookie.includes('; samesite=none;')); 49 | }) 50 | .expect('set-cookie', /EGG_SESS=.*?;/); 51 | 52 | await agent.get('/get') 53 | .expect(200) 54 | .expect({ foo: 'bar' }); 55 | 56 | app.sessionStore = null; 57 | 58 | await agent.get('/get') 59 | .expect(200) 60 | .expect({}); 61 | }); 62 | }); 63 | 64 | describe('httpOnly', () => { 65 | it('should warn when httponly false', async () => { 66 | app = mm.app({ baseDir: 'httponly-false-session' }); 67 | await app.ready(); 68 | app.expectLog('[@eggjs/session]: please set `config.session.httpOnly` to true. It is very dangerous if session can read by client JavaScript.', 'coreLogger'); 69 | await app.close(); 70 | }); 71 | }); 72 | 73 | describe('sameSite', () => { 74 | before(() => { 75 | app = mm.app({ baseDir: 'samesite-none-session' }); 76 | return app.ready(); 77 | }); 78 | beforeEach(() => { 79 | agent = new TestAgent(app.callback()); 80 | }); 81 | after(() => app.close()); 82 | 83 | it('should work with sameSite=none', async () => { 84 | await agent 85 | .get('/set?foo=bar') 86 | .set('user-agent', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36') 87 | .set('x-forwarded-proto', 'https') 88 | .expect(200) 89 | .expect({ foo: 'bar' }) 90 | .expect(res => { 91 | const cookie = res.get('Set-Cookie')!.join('|'); 92 | assert(cookie.includes('; samesite=none;')); 93 | }); 94 | }); 95 | }); 96 | 97 | describe('chips', () => { 98 | before(() => { 99 | app = mm.app({ baseDir: 'chips' }); 100 | return app.ready(); 101 | }); 102 | beforeEach(() => { 103 | agent = new TestAgent(app.callback()); 104 | }); 105 | after(() => app.close()); 106 | 107 | it('should work with chips', async () => { 108 | await agent 109 | .get('/set?foo=bar') 110 | .set('user-agent', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.4044.138 Safari/537.36') 111 | .set('x-forwarded-proto', 'https') 112 | .expect(200) 113 | .expect({ foo: 'bar' }) 114 | .expect(res => { 115 | const cookies = res.headers['set-cookie']; 116 | console.log(cookies); 117 | }); 118 | }); 119 | }); 120 | 121 | describe('logValue', () => { 122 | before(() => { 123 | app = mm.app({ baseDir: 'logValue-false-session' }); 124 | return app.ready(); 125 | }); 126 | beforeEach(() => { 127 | agent = new TestAgent(app.callback()); 128 | app.mockLog(); 129 | }); 130 | after(() => app.close()); 131 | 132 | it('when logValue is true, should log the session value', async () => { 133 | let cookie = ''; 134 | app.mockLog(); 135 | mm(app.config.session, 'logValue', true); 136 | 137 | await agent 138 | .get('/maxAge?maxAge=100') 139 | .expect(200) 140 | .expect(res => { 141 | cookie = res.get('Set-Cookie')!.join(';'); 142 | }); 143 | 144 | await scheduler.wait(200); 145 | 146 | await request(app.callback()) 147 | .get('/get') 148 | .set('cookie', cookie) 149 | .expect(200) 150 | .expect({}); 151 | app.notExpectLog('[session][expired] key(undefined) value("")', 'coreLogger'); 152 | }); 153 | 154 | it('when logValue is false, should not log the session value', async () => { 155 | mm(app.config.session, 'logValue', false); 156 | app.mockLog(); 157 | let cookie = ''; 158 | 159 | await agent 160 | .get('/maxAge?maxAge=100') 161 | .expect(200) 162 | .expect(res => { 163 | cookie = res.get('Set-Cookie')!.join(';'); 164 | }); 165 | 166 | await scheduler.wait(200); 167 | 168 | await request(app.callback()) 169 | .get('/get') 170 | .set('cookie', cookie) 171 | .expect(200) 172 | .expect({}); 173 | 174 | await scheduler.wait(1000); 175 | 176 | app.expectLog('[session][expired] key(undefined) value("")', 'coreLogger'); 177 | }); 178 | 179 | it('when logValue is false, valid false, should not log the session value', async () => { 180 | mm(app.config.session, 'logValue', false); 181 | mm(app.config.session, 'valid', () => false); 182 | app.mockLog(); 183 | 184 | await agent 185 | .get('/set?foo=bar') 186 | .expect(200) 187 | .expect({ foo: 'bar' }); 188 | 189 | await agent 190 | .get('/get'); 191 | 192 | await scheduler.wait(1000); 193 | 194 | app.expectLog('[session][invalid] key(undefined) value("")', 'coreLogger'); 195 | }); 196 | }); 197 | 198 | describe('session maxage', () => { 199 | before(() => { 200 | app = mm.app({ baseDir: 'session-maxage-session' }); 201 | return app.ready(); 202 | }); 203 | beforeEach(() => { 204 | agent = new TestAgent(app.callback()); 205 | }); 206 | after(() => app.close()); 207 | 208 | it('should work with maxage=ession', async () => { 209 | await agent 210 | .get('/set?foo=bar') 211 | .set('user-agent', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36') 212 | .set('x-forwarded-proto', 'https') 213 | .expect(200) 214 | .expect({ foo: 'bar' }) 215 | .expect(res => { 216 | const cookie = res.get('Set-Cookie')!.join('|'); 217 | assert(!cookie.includes('expires')); 218 | assert(!cookie.includes('max-age')); 219 | }); 220 | }); 221 | 222 | it('should ctx.session.maxAge=session work', async () => { 223 | await agent 224 | .get('/maxAge?maxAge=session') 225 | .expect(200) 226 | .expect(res => { 227 | const cookie = res.get('Set-Cookie')!.join(';'); 228 | assert(cookie.match(/EGG_SESS=.*?;/)); 229 | assert(!cookie.includes('expires')); 230 | assert(!cookie.includes('max-age')); 231 | }); 232 | }); 233 | }); 234 | 235 | [ 236 | 'cookie-session', 237 | 'memory-session', 238 | 'memory-session-generator', 239 | 'redis-session', 240 | ].forEach(name => { 241 | describe(name, () => { 242 | before(() => { 243 | app = mm.app({ 244 | baseDir: name, 245 | cache: false, 246 | }); 247 | return app.ready(); 248 | }); 249 | beforeEach(() => { 250 | agent = new TestAgent(app.callback()); 251 | }); 252 | after(() => app.close()); 253 | 254 | if (name === 'redis-session') { 255 | it('should get with ', async () => { 256 | await agent 257 | .get('/set?foo=bar') 258 | .expect(200) 259 | .expect({ foo: 'bar' }) 260 | .expect('set-cookie', /EGG_SESS=.*?;/); 261 | 262 | mm.empty(app.redis, 'get'); 263 | 264 | await agent 265 | .get('/get') 266 | .expect(200) 267 | .expect({}); 268 | }); 269 | } 270 | 271 | it('should get empty session and do not set cookie when session not populated', async () => { 272 | await agent 273 | .get('/get') 274 | .expect(200) 275 | .expect({}) 276 | .expect(res => { 277 | assert(!res.get('Set-Cookie')!.join('').match(/EGG_SESS/)); 278 | }); 279 | }); 280 | 281 | it('should ctx.session= change the session', async () => { 282 | await agent 283 | .get('/set?foo=bar') 284 | .expect(200) 285 | .expect({ foo: 'bar' }) 286 | .expect('set-cookie', /EGG_SESS=.*?;/); 287 | }); 288 | 289 | it('should ctx.session.key= change the session', async () => { 290 | await agent 291 | .get('/set?key=foo&foo=bar') 292 | .expect(200) 293 | .expect({ key: 'foo', foo: 'bar' }) 294 | .expect('set-cookie', /EGG_SESS=.*?;/); 295 | 296 | await agent 297 | .get('/setKey?key=bar') 298 | .expect(200) 299 | .expect({ key: 'bar', foo: 'bar' }) 300 | .expect('set-cookie', /EGG_SESS=.*?;/); 301 | }); 302 | 303 | it('should ctx.session=null remove the session', async () => { 304 | await agent 305 | .get('/set?key=foo&foo=bar') 306 | .expect(200) 307 | .expect({ key: 'foo', foo: 'bar' }) 308 | .expect('set-cookie', /EGG_SESS=.*?;/); 309 | 310 | await agent 311 | .get('/remove') 312 | .expect(204) 313 | .expect('set-cookie', /EGG_SESS=;/); 314 | 315 | await agent 316 | .get('/get') 317 | .expect(200) 318 | .expect({}); 319 | }); 320 | 321 | it('should ctx.session.maxAge= change maxAge', async () => { 322 | await agent 323 | .get('/set?key=foo&foo=bar') 324 | .expect(200) 325 | .expect({ key: 'foo', foo: 'bar' }) 326 | .expect('set-cookie', /EGG_SESS=.*?;/); 327 | 328 | let cookie = ''; 329 | 330 | await agent 331 | .get('/maxAge?maxAge=100') 332 | .expect(200) 333 | .expect({ key: 'foo', foo: 'bar' }) 334 | .expect(res => { 335 | cookie = res.get('Set-Cookie')!.join(';'); 336 | assert(cookie.match(/EGG_SESS=.*?;/)); 337 | assert(cookie.match(/expires=/)); 338 | assert(cookie.match(/max-age=/)); 339 | }); 340 | 341 | await scheduler.wait(200); 342 | 343 | await agent 344 | .get('/get') 345 | .expect(200) 346 | .expect({}); 347 | 348 | await request(app.callback()) 349 | .get('/get') 350 | .set('cookie', cookie) 351 | .expect(200) 352 | .expect({}); 353 | }); 354 | }); 355 | }); 356 | }); 357 | -------------------------------------------------------------------------------- /test/fixtures/chips/app/controller/home.js: -------------------------------------------------------------------------------- 1 | exports.get = async ctx => { 2 | ctx.body = ctx.session; 3 | }; 4 | 5 | exports.set = async ctx => { 6 | ctx.session = ctx.query; 7 | ctx.body = ctx.session; 8 | }; 9 | -------------------------------------------------------------------------------- /test/fixtures/chips/app/router.js: -------------------------------------------------------------------------------- 1 | module.exports = app => { 2 | app.get('/get', 'home.get'); 3 | app.get('/set', 'home.set'); 4 | }; 5 | -------------------------------------------------------------------------------- /test/fixtures/chips/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.keys = 'keys'; 4 | exports.session = { 5 | partitioned: true, 6 | removeUnpartitioned: true, 7 | }; 8 | 9 | exports.proxy = true; 10 | -------------------------------------------------------------------------------- /test/fixtures/chips/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chips-session" 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/cookie-session/app/controller/home.js: -------------------------------------------------------------------------------- 1 | exports.get = async function(ctx) { 2 | ctx.body = ctx.session; 3 | }; 4 | 5 | exports.set = async function(ctx) { 6 | ctx.session = ctx.query; 7 | ctx.body = ctx.session; 8 | }; 9 | 10 | exports.setKey = async function(ctx) { 11 | ctx.session.key = ctx.query.key; 12 | ctx.body = ctx.session; 13 | }; 14 | 15 | exports.remove = async function(ctx) { 16 | ctx.session = null; 17 | ctx.body = ctx.session; 18 | }; 19 | 20 | exports.maxAge = async function(ctx) { 21 | ctx.session.maxAge = Number(ctx.query.maxAge); 22 | ctx.body = ctx.session; 23 | }; 24 | -------------------------------------------------------------------------------- /test/fixtures/cookie-session/app/router.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(app) { 4 | app.get('/get', 'home.get'); 5 | app.get('/set', 'home.set'); 6 | app.get('/setKey', 'home.setKey'); 7 | app.get('/remove', 'home.remove'); 8 | app.get('/maxAge', 'home.maxAge'); 9 | }; 10 | -------------------------------------------------------------------------------- /test/fixtures/cookie-session/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.keys = 'keys'; 4 | -------------------------------------------------------------------------------- /test/fixtures/cookie-session/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cookie-session" 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/httponly-false-session/app/controller/home.js: -------------------------------------------------------------------------------- 1 | exports.get = async function(ctx) { 2 | ctx.body = ctx.session; 3 | }; 4 | 5 | exports.set = async function(ctx) { 6 | ctx.session = ctx.query; 7 | ctx.body = ctx.session; 8 | }; 9 | 10 | exports.setKey = async function(ctx) { 11 | ctx.session.key = ctx.query.key; 12 | ctx.body = ctx.session; 13 | }; 14 | 15 | exports.remove = async function(ctx) { 16 | ctx.session = null; 17 | ctx.body = ctx.session; 18 | }; 19 | 20 | exports.maxAge = async function(ctx) { 21 | ctx.session.maxAge = Number(ctx.query.maxAge); 22 | ctx.body = ctx.session; 23 | }; 24 | -------------------------------------------------------------------------------- /test/fixtures/httponly-false-session/app/router.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(app) { 4 | app.get('/get', 'home.get'); 5 | app.get('/set', 'home.set'); 6 | app.get('/setKey', 'home.setKey'); 7 | app.get('/remove', 'home.remove'); 8 | app.get('/maxAge', 'home.maxAge'); 9 | }; 10 | -------------------------------------------------------------------------------- /test/fixtures/httponly-false-session/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.keys = 'keys'; 4 | exports.session = { 5 | httpOnly: false, 6 | }; 7 | -------------------------------------------------------------------------------- /test/fixtures/httponly-false-session/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "httponly-false-session" 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/logValue-false-session/app/controller/home.js: -------------------------------------------------------------------------------- 1 | exports.get = async function(ctx) { 2 | ctx.body = ctx.session; 3 | }; 4 | 5 | exports.set = async function(ctx) { 6 | ctx.session = ctx.query; 7 | ctx.body = ctx.session; 8 | }; 9 | 10 | exports.setKey = async function(ctx) { 11 | ctx.session.key = ctx.query.key; 12 | ctx.body = ctx.session; 13 | }; 14 | 15 | exports.remove = async function(ctx) { 16 | ctx.session = null; 17 | ctx.body = ctx.session; 18 | }; 19 | 20 | exports.maxAge = async function(ctx) { 21 | ctx.session.maxAge = Number(ctx.query.maxAge); 22 | ctx.body = ctx.session; 23 | }; 24 | -------------------------------------------------------------------------------- /test/fixtures/logValue-false-session/app/router.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(app) { 4 | app.get('/get', 'home.get'); 5 | app.get('/set', 'home.set'); 6 | app.get('/setKey', 'home.setKey'); 7 | app.get('/remove', 'home.remove'); 8 | app.get('/maxAge', 'home.maxAge'); 9 | }; 10 | -------------------------------------------------------------------------------- /test/fixtures/logValue-false-session/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.keys = 'keys'; 4 | exports.session = { 5 | logValue: false, 6 | }; 7 | -------------------------------------------------------------------------------- /test/fixtures/logValue-false-session/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "logvalue-false-session" 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/memory-session-generator/app.js: -------------------------------------------------------------------------------- 1 | const sessions = {}; 2 | 3 | module.exports = app => { 4 | app.sessionStore = { 5 | async get(key) { 6 | return sessions[key]; 7 | }, 8 | 9 | async set(key, value) { 10 | sessions[key] = value; 11 | }, 12 | 13 | async destroy(key) { 14 | sessions[key] = undefined; 15 | }, 16 | }; 17 | }; 18 | -------------------------------------------------------------------------------- /test/fixtures/memory-session-generator/app/controller/home.js: -------------------------------------------------------------------------------- 1 | exports.get = async function(ctx) { 2 | ctx.body = ctx.session; 3 | }; 4 | 5 | exports.set = async function(ctx) { 6 | ctx.session = ctx.query; 7 | ctx.body = ctx.session; 8 | }; 9 | 10 | exports.setKey = async function(ctx) { 11 | ctx.session.key = ctx.query.key; 12 | ctx.body = ctx.session; 13 | }; 14 | 15 | exports.remove = async function(ctx) { 16 | ctx.session = null; 17 | ctx.body = ctx.session; 18 | }; 19 | 20 | exports.maxAge = async function(ctx) { 21 | ctx.session.maxAge = Number(ctx.query.maxAge); 22 | ctx.body = ctx.session; 23 | }; 24 | -------------------------------------------------------------------------------- /test/fixtures/memory-session-generator/app/router.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(app) { 4 | app.get('/get', 'home.get'); 5 | app.get('/set', 'home.set'); 6 | app.get('/setKey', 'home.setKey'); 7 | app.get('/remove', 'home.remove'); 8 | app.get('/maxAge', 'home.maxAge'); 9 | }; 10 | -------------------------------------------------------------------------------- /test/fixtures/memory-session-generator/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.keys = 'keys'; 4 | -------------------------------------------------------------------------------- /test/fixtures/memory-session-generator/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "memory-session-generator" 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/memory-session/app.js: -------------------------------------------------------------------------------- 1 | const sessions = {}; 2 | 3 | module.exports = class AppBootHook { 4 | constructor(app) { 5 | this.app = app; 6 | } 7 | 8 | async willReady() { 9 | this.app.sessionStore = { 10 | async get(key) { 11 | return sessions[key]; 12 | }, 13 | 14 | async set(key, value) { 15 | sessions[key] = value; 16 | }, 17 | 18 | async destroy(key) { 19 | sessions[key] = undefined; 20 | }, 21 | }; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /test/fixtures/memory-session/app/controller/home.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.get = async ctx => { 4 | ctx.body = ctx.session; 5 | }; 6 | 7 | exports.set = async ctx => { 8 | ctx.session = ctx.query; 9 | ctx.body = ctx.session; 10 | }; 11 | 12 | exports.setKey = async ctx => { 13 | ctx.session.key = ctx.query.key; 14 | ctx.body = ctx.session; 15 | }; 16 | 17 | exports.remove = async ctx => { 18 | ctx.session = null; 19 | ctx.body = ctx.session; 20 | }; 21 | 22 | exports.maxAge = async ctx => { 23 | ctx.session.maxAge = Number(ctx.query.maxAge); 24 | ctx.body = ctx.session; 25 | }; 26 | -------------------------------------------------------------------------------- /test/fixtures/memory-session/app/router.js: -------------------------------------------------------------------------------- 1 | module.exports = function(app) { 2 | app.get('/get', 'home.get'); 3 | app.get('/set', 'home.set'); 4 | app.get('/setKey', 'home.setKey'); 5 | app.get('/remove', 'home.remove'); 6 | app.get('/maxAge', 'home.maxAge'); 7 | }; 8 | -------------------------------------------------------------------------------- /test/fixtures/memory-session/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.keys = 'keys'; 4 | -------------------------------------------------------------------------------- /test/fixtures/memory-session/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "memory-session" 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/redis-session/app.js: -------------------------------------------------------------------------------- 1 | module.exports = app => { 2 | // set redis session store 3 | app.sessionStore = class Store { 4 | constructor(app) { 5 | this.app = app; 6 | } 7 | async get(key) { 8 | const res = await this.app.redis.get(key); 9 | if (!res) return null; 10 | return JSON.parse(res); 11 | } 12 | 13 | async set(key, value, maxAge) { 14 | value = JSON.stringify(value); 15 | await this.app.redis.set(key, value, 'PX', maxAge); 16 | } 17 | 18 | async destroy(key) { 19 | await this.app.redis.del(key); 20 | } 21 | }; 22 | }; 23 | -------------------------------------------------------------------------------- /test/fixtures/redis-session/app/controller/home.js: -------------------------------------------------------------------------------- 1 | exports.get = async function(ctx) { 2 | ctx.body = ctx.session; 3 | }; 4 | 5 | exports.set = async function(ctx) { 6 | ctx.session = ctx.query; 7 | ctx.body = ctx.session; 8 | }; 9 | 10 | exports.setKey = async function(ctx) { 11 | ctx.session.key = ctx.query.key; 12 | ctx.body = ctx.session; 13 | }; 14 | 15 | exports.remove = async function(ctx) { 16 | ctx.session = null; 17 | ctx.body = ctx.session; 18 | }; 19 | 20 | exports.maxAge = async function(ctx) { 21 | ctx.session.maxAge = Number(ctx.query.maxAge); 22 | ctx.body = ctx.session; 23 | }; 24 | -------------------------------------------------------------------------------- /test/fixtures/redis-session/app/router.js: -------------------------------------------------------------------------------- 1 | module.exports = function(app) { 2 | app.get('/get', 'home.get'); 3 | app.get('/set', 'home.set'); 4 | app.get('/setKey', 'home.setKey'); 5 | app.get('/remove', 'home.remove'); 6 | app.get('/maxAge', 'home.maxAge'); 7 | }; 8 | -------------------------------------------------------------------------------- /test/fixtures/redis-session/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.keys = 'keys'; 4 | 5 | exports.redis = { 6 | client: { 7 | host: '127.0.0.1', 8 | port: 6379, 9 | db: '0', 10 | password: '', 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /test/fixtures/redis-session/config/plugin.js: -------------------------------------------------------------------------------- 1 | exports.redis = { 2 | enable: true, 3 | package: '@eggjs/redis', 4 | }; 5 | -------------------------------------------------------------------------------- /test/fixtures/redis-session/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "redis-session" 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/samesite-none-session/app/controller/home.js: -------------------------------------------------------------------------------- 1 | exports.get = async function(ctx) { 2 | ctx.body = ctx.session; 3 | }; 4 | 5 | exports.set = async function(ctx) { 6 | ctx.session = ctx.query; 7 | ctx.body = ctx.session; 8 | }; 9 | -------------------------------------------------------------------------------- /test/fixtures/samesite-none-session/app/router.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(app) { 4 | app.get('/get', 'home.get'); 5 | app.get('/set', 'home.set'); 6 | }; 7 | -------------------------------------------------------------------------------- /test/fixtures/samesite-none-session/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.keys = 'keys'; 4 | exports.session = { 5 | sameSite: 'none', 6 | }; 7 | 8 | exports.proxy = true; 9 | -------------------------------------------------------------------------------- /test/fixtures/samesite-none-session/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "samesite-none-session" 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/session-maxage-session/app/controller/home.js: -------------------------------------------------------------------------------- 1 | exports.get = async function(ctx) { 2 | ctx.body = ctx.session; 3 | }; 4 | 5 | exports.set = async function(ctx) { 6 | ctx.session = ctx.query; 7 | ctx.body = ctx.session; 8 | }; 9 | 10 | exports.setKey = async function(ctx) { 11 | ctx.session.key = ctx.query.key; 12 | ctx.body = ctx.session; 13 | }; 14 | 15 | exports.remove = async function(ctx) { 16 | ctx.session = null; 17 | ctx.body = ctx.session; 18 | }; 19 | 20 | exports.maxAge = async function(ctx) { 21 | ctx.session.maxAge = ctx.query.maxAge; 22 | ctx.body = ctx.session; 23 | }; 24 | -------------------------------------------------------------------------------- /test/fixtures/session-maxage-session/app/router.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(app) { 4 | app.get('/get', 'home.get'); 5 | app.get('/set', 'home.set'); 6 | app.get('/setKey', 'home.setKey'); 7 | app.get('/remove', 'home.remove'); 8 | app.get('/maxAge', 'home.maxAge'); 9 | }; 10 | -------------------------------------------------------------------------------- /test/fixtures/session-maxage-session/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.keys = 'keys'; 4 | exports.session = { 5 | maxAge: 'session', 6 | }; 7 | -------------------------------------------------------------------------------- /test/fixtures/session-maxage-session/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "httponly-false-session" 3 | } 4 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@eggjs/tsconfig", 3 | "compilerOptions": { 4 | "strict": true, 5 | "noImplicitAny": true, 6 | "target": "ES2022", 7 | "module": "NodeNext", 8 | "moduleResolution": "NodeNext" 9 | } 10 | } 11 | --------------------------------------------------------------------------------