Skip to content

Commit

Permalink
remove seesion from cookie manager
Browse files Browse the repository at this point in the history
extract chunkString to utils

configure jest

add test to cookie manager and utils

upgarde typescript
  • Loading branch information
yuvalotem1 committed Dec 27, 2022
1 parent e0ef961 commit f158248
Show file tree
Hide file tree
Showing 11 changed files with 1,589 additions and 49 deletions.
7 changes: 7 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = {
preset: 'ts-jest',
transform: {
'^.+\\.(ts|tsx)?$': 'ts-jest',
'^.+\\.(js|jsx)$': 'babel-jest',
},
};
13 changes: 9 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,21 @@
"demo": "lerna run dev --stream --scope '@frontegg/demo-saas'",
"demo12": "lerna run dev --stream --scope '@frontegg/demo-saas-next12'",
"build": "lerna run build --stream --scope '@frontegg/nextjs'",
"test": "jest",
"prettier-hook": "prettier --config ./.prettierrc --write .",
"prettier-check-hook": "prettier --config ./.prettierrc --check ."
},
"devDependencies": {
"@rollup/plugin-node-resolve": "^8.0.1",
"@types/history": "^4.7.7",
"@types/jest": "^29.2.4",
"@types/node": "^13.9.1",
"@types/react": "^18.0.26",
"@types/react-dom": "^18.0.9",
"eslint": "^8.30.0",
"eslint-plugin-react": "^7.17.0",
"history": "^4.9.0",
"jest": "^29.3.1",
"lerna": "6.2.0",
"path": "^0.12.7",
"pre-commit": "^1.2.2",
Expand All @@ -31,19 +34,21 @@
"rollup": "^2.15.0",
"rollup-plugin-node-resolve": "^5.2.0",
"rollup-plugin-typescript2": "^0.27.2",
"ts-jest": "^29.0.3",
"tslib": "^2.3.0",
"tslint": "^6.1.0",
"typescript": "^3.9.7",
"typescript": "^4.9.4",
"webpack": "^5.75.0"
},
"nyc": {
"reporter": [
"html"
]
},
"pre-commit": {
"run": "prettier-check-hook"
},
"pre-commit": [
"test",
"prettier-check-hook"
],
"workspaces": [
"packages/*"
]
Expand Down
2 changes: 1 addition & 1 deletion packages/nextjs/src/FronteggMiddleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ export function fronteggMiddleware(req: NextApiRequest, res: NextApiResponse): P
const [session, decodedJwt] = await createSessionFromAccessToken(output);
if (session) {
const sessionCookie = CookieManager.createCookie({
session,
value: session,
expires: new Date(decodedJwt.exp * 1000),
isSecured,
});
Expand Down
38 changes: 13 additions & 25 deletions packages/nextjs/src/common/CookieManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ import { ServerResponse } from 'http';
import { RequestCookie } from 'next/dist/server/web/spec-extension/cookies';
import FronteggConfig from './FronteggConfig';
import { RequestType } from './types';
import { chunkString } from './utils';

type CreateCookieArguments = {
cookieName?: string;
session: string;
value: string;
expires: CookieSerializeOptions['expires'];
isSecured: CookieSerializeOptions['secure'];
cookieDomain?: CookieSerializeOptions['domain'];
Expand All @@ -24,17 +25,6 @@ type RemoveCookiesArguments = {

const COOKIE_MAX_LENGTH = 4096;

function chunkString(str: string, chunkSize: number) {
const numChunks = Math.ceil(str.length / chunkSize);
const chunks: string[] = [];
for (let i = 0; i < numChunks; i++) {
const start = i * chunkSize;
const end = start + chunkSize;
chunks.push(str.substring(start, end < str.length ? end : str.length));
}
return chunks;
}

class CookieManager {
constructor() {}

Expand Down Expand Up @@ -69,7 +59,7 @@ class CookieManager {

createCookie({
cookieName,
session,
value,
expires,
isSecured,
cookieDomain = FronteggConfig.cookieDomain,
Expand All @@ -84,28 +74,26 @@ class CookieManager {
sameSite: isSecured ? ('none' as const) : undefined,
secure: isSecured,
};
const cookieValue = cookie.serialize(cookieName ?? this.getCookieName(1), session, options);
const cookieValue = cookie.serialize(cookieName ?? this.getCookieName(1), value, options);
if (cookieValue.length < COOKIE_MAX_LENGTH) {
return [cookieValue];
}
const sessionChunks = this.splitSessionToChunks(cookieName, session, options);
return this.mapSessionChunksToCookies(cookieName, sessionChunks, options);
const valueChunks = this.splitValueToChunks(cookieName, value, options);
return this.mapValueChunksToCookies(cookieName, valueChunks, options);
}

splitSessionToChunks(cookieName: string | undefined, session: string, options: CookieSerializeOptions): string[] {
splitValueToChunks(cookieName: string | undefined, value: string, options: CookieSerializeOptions): string[] {
const cookieOptionLength = cookie.serialize(this.getCookieName(1, cookieName), '', options).length;
const maxSessionLength = COOKIE_MAX_LENGTH - cookieOptionLength;
return chunkString(session, maxSessionLength);
const maxValueLength = COOKIE_MAX_LENGTH - cookieOptionLength;
return chunkString(value, maxValueLength);
}

mapSessionChunksToCookies = (
mapValueChunksToCookies = (
cookieName: string | undefined,
sessionChunks: string[],
valueChunks: string[],
options: CookieSerializeOptions
): string[] =>
sessionChunks.map((sessionChunk, index) =>
cookie.serialize(this.getCookieName(index + 1, cookieName), sessionChunk, options)
);
valueChunks.map((chunk, index) => cookie.serialize(this.getCookieName(index + 1, cookieName), chunk, options));

getCookieStringFromRequest = (req: RequestType) =>
'credentials' in req ? req.headers.get('cookie') || '' : req.headers.cookie || '';
Expand Down Expand Up @@ -153,7 +141,7 @@ class CookieManager {
res.setHeader('set-cookie', [...existingSetCookie, ...newCookies]);
}
createEmptySingleCookie = (cookieName: string, isSecured: boolean, cookieDomain: string) =>
this.createCookie({ cookieName, session: '', expires: new Date(), isSecured, cookieDomain });
this.createCookie({ cookieName, value: '', expires: new Date(), isSecured, cookieDomain });

createEmptyCookies = (isSecured: boolean, cookieDomain: string, cookiesToRemove: string[]): string[] => {
const allEmptyCookies: string[] = [];
Expand Down
10 changes: 10 additions & 0 deletions packages/nextjs/src/common/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export function chunkString(str: string, chunkSize: number) {
const numChunks = Math.ceil(str.length / chunkSize);
const chunks: string[] = [];
for (let i = 0; i < numChunks; i++) {
const start = i * chunkSize;
const end = start + chunkSize;
chunks.push(str.substring(start, end < str.length ? end : str.length));
}
return chunks;
}
6 changes: 5 additions & 1 deletion packages/nextjs/src/refreshToken.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,11 @@ export async function refreshToken(ctx: NextPageContext): Promise<FronteggNextJS
if (!session) {
return null;
}
const cookieValue = CookieManager.createCookie({ session, expires: new Date(decodedJwt.exp * 1000), isSecured });
const cookieValue = CookieManager.createCookie({
value: session,
expires: new Date(decodedJwt.exp * 1000),
isSecured,
});
if (typeof newSetCookie === 'string') {
newSetCookie = [newSetCookie];
}
Expand Down
102 changes: 102 additions & 0 deletions packages/nextjs/test/src/common/CookieManager.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { CookieManager, FronteggConfig } from '../../../src';
import { smallCookieValue, bigCookieValue, cookieDomain, cookieName } from './const';

const extractValueOutOfCookie = (cookie: string) =>
cookie
.split('; ')
?.find((c) => c.includes(cookieName))
?.split('=')[1] ?? '';

describe('Cookie Manager', () => {
it('getCookieName should return right cookie name', () => {
const cookieNumber = 7;
const cookieName = CookieManager.getCookieName(cookieNumber);
expect(cookieName).toEqual(`${FronteggConfig.cookieName}-${cookieNumber}`);
});

it('createCookie should create cookie properly', () => {
const cookieValue = CookieManager.createCookie({
cookieName,
value: smallCookieValue,
expires: new Date(),
isSecured: true,
cookieDomain,
});

expect(cookieValue.length).toEqual(1);
expect(cookieValue[0]).toContain(`${cookieName}=${smallCookieValue}`);
expect(cookieValue[0]).toContain('HttpOnly');
expect(cookieValue[0]).toContain('Secure');
expect(cookieValue[0]).toContain('SameSite=None');
expect(cookieValue[0]).toContain(`Domain=${cookieDomain}`);
});

it('createCookie with big value should create split cookie properly', () => {
const cookieValue = CookieManager.createCookie({
cookieName,
value: bigCookieValue,
expires: new Date(),
isSecured: true,
cookieDomain,
});
const firstCookieName = CookieManager.getCookieName(1, cookieName);
const secondCookieName = CookieManager.getCookieName(2, cookieName);

const firstCookieValue = extractValueOutOfCookie(cookieValue[0]);
const secondCookieValue = extractValueOutOfCookie(cookieValue[1]);

expect(cookieValue.length).toEqual(2);

expect(cookieValue[0]).toContain(`${firstCookieName}=${firstCookieValue}`);
expect(cookieValue[0]).toContain('HttpOnly');
expect(cookieValue[0]).toContain('Secure');
expect(cookieValue[0]).toContain('SameSite=None');
expect(cookieValue[0]).toContain(`Domain=${cookieDomain}`);

expect(cookieValue[1]).toContain(`${secondCookieName}=${secondCookieValue}`);
expect(cookieValue[1]).toContain('HttpOnly');
expect(cookieValue[1]).toContain('Secure');
expect(cookieValue[1]).toContain('SameSite=None');
expect(cookieValue[1]).toContain(`Domain=${cookieDomain}`);

expect(firstCookieValue + secondCookieValue).toEqual(bigCookieValue);
});

it('createCookie with big value should create split cookie properly', () => {
const cookieValue = CookieManager.createCookie({
cookieName,
value: bigCookieValue,
expires: new Date(),
isSecured: true,
cookieDomain,
});
const firstCookieName = CookieManager.getCookieName(1, cookieName);
const secondCookieName = CookieManager.getCookieName(2, cookieName);

expect(cookieValue.length).toEqual(2);

expect(cookieValue[0]).toContain(firstCookieName);
expect(cookieValue[0]).toContain('HttpOnly');
expect(cookieValue[0]).toContain('Secure');
expect(cookieValue[0]).toContain('SameSite=None');
expect(cookieValue[0]).toContain(`Domain=${cookieDomain}`);

expect(cookieValue[1]).toContain(secondCookieName);
expect(cookieValue[1]).toContain('HttpOnly');
expect(cookieValue[1]).toContain('Secure');
expect(cookieValue[1]).toContain('SameSite=None');
expect(cookieValue[1]).toContain(`Domain=${cookieDomain}`);
});

it('createEmptyCookies should accepts cookie names and create same cookies with empty value and expiration date equals to now', () => {
const emptyCookies = CookieManager.createEmptyCookies(true, cookieDomain, [cookieName]);

expect(emptyCookies.length).toEqual(1);
expect(emptyCookies[0]).toContain(`${cookieName}=;`);
expect(emptyCookies[0]).toContain('HttpOnly');
expect(emptyCookies[0]).toContain('Secure');
expect(emptyCookies[0]).toContain('SameSite=None');
expect(emptyCookies[0]).toContain(`Domain=${cookieDomain}`);
expect(emptyCookies[0]).toContain(`Expires=${new Date().toUTCString()}`);
});
});
5 changes: 5 additions & 0 deletions packages/nextjs/test/src/common/const.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const smallCookieValue = 'value';
export const bigCookieValue =
'vjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjjvjsdhavkljdrbonfoknmokfmnoksrjtnokbsmrtkbmsrobmormbowrjtnojetynjj';
export const cookieDomain = 'domain';
export const cookieName = 'cookieName';
29 changes: 29 additions & 0 deletions packages/nextjs/test/src/common/utils.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { chunkString } from '../../../src/common/utils';

describe('utils', () => {
it('chunkString should return array of strings with the right chunk size', () => {
const chunkSize = 3;
const chunks = chunkString('123456', chunkSize);
expect(chunks.length).toEqual(2);
expect(chunks[0].length).toEqual(chunkSize);
expect(chunks[1].length).toEqual(chunkSize);
});

it('chunkString should return array of one string if chunk size is bigger then string size', () => {
const chunkSize = 9;
const string = '123456';
const chunks = chunkString(string, chunkSize);
expect(chunks.length).toEqual(1);
expect(chunks[0].length).toEqual(string.length);
});

it('chunkString should return array of strings with the right chunk size and also last item if his size is not equal chunk size', () => {
const chunkSize = 3;
const string = '12345678';
const chunks = chunkString(string, chunkSize);
expect(chunks.length).toEqual(3);
expect(chunks[0].length).toEqual(chunkSize);
expect(chunks[1].length).toEqual(chunkSize);
expect(chunks[2].length).not.toEqual(chunkSize);
});
});
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
"strict": true,
"forceConsistentCasingInFileNames": true,
"isolatedModules": true,
"types": []
"types": ["jest"],
}
}

Loading

0 comments on commit f158248

Please sign in to comment.