Skip to content

Commit

Permalink
feat(lib): enable flashable data in the sessions
Browse files Browse the repository at this point in the history
Flash data is data that can only be read once. If a piece of data is
read, it will be deleted from the cookie at the end of the execution
chain.
  • Loading branch information
mrsimonemms committed Jan 27, 2024
1 parent cb000ce commit 9d6ebef
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 17 deletions.
2 changes: 2 additions & 0 deletions example/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,15 @@ const app = express();
app
.use(CookieSession.express(config.session))
.get('/', (req, res) => {
// If done using "flash", this will delete the "date" session, but not "date2"
const { date } = req.session;

res.json({ getter: { date } });
})
.get('/set', (req, res) => {
const date = new Date();
req.session.date = date;
req.session.data2 = date;

res.json({ setter: { date } });
})
Expand Down
54 changes: 37 additions & 17 deletions src/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,22 @@ export class CookieSession {
};
}

private getDataItem(
target: Record<string, unknown>,
prop: string,
receiver: Record<string, unknown>,
): any {
debug(`Retrieving data: ${prop}`);

const value = target[prop];
if (this.opts.flash) {
debug(`Deleting flash data after reading: ${prop}`);
delete receiver[prop];
}

return value;
}

private validate(): void {
if (!this.opts.secret || this.opts.secret.length < 16) {
throw new Error('Secret must be at least 16 characters long');
Expand Down Expand Up @@ -102,26 +118,30 @@ export class CookieSession {
const encData = this.cookies.get(this.opts.name, {
signed: this.opts.cookie.signed,
});
if (!encData) {
if (encData) {
// Decrypt the data
debug('Decrypting cookie data');
const strData = await this.decryptData(encData);
if (strData) {
debug('Parsing data to JSON');
const cookieData = JSON.parse(strData);
if (cookieData) {
// Load the data
debug('Data successfully decrypted - loading');
this.data = { ...cookieData };
}
} else {
debug('No data found after decryption');
}
} else {
debug('No data in cookie');
return;
}

// Decrypt the data
debug('Decrypting cookie data');
const strData = await this.decryptData(encData);
if (!strData) {
debug('No data found after decryption');
return;
}

debug('Parsing data to JSON');
const cookieData = JSON.parse(strData);
if (cookieData) {
// Load the data
debug('Data successfully decrypted - loading');
this.data = { ...cookieData };
}
// Wrap the data in a Proxy, so we can intercept the getters
this.data = new Proxy<Record<string, unknown>>(this.data, {
get: (target, prop: string, receiver: Record<string, unknown>) =>
this.getDataItem(target, prop, receiver),
});
}

async saveCookieData(): Promise<void> {
Expand Down

0 comments on commit 9d6ebef

Please # to comment.