HBE-164 refactor: subscriptions auth cookie fix (#26)

* chore: added error handling to cookie extraction logic for subscriptions

* chore: removed migration file from prisma directory
This commit is contained in:
Balu Babu
2023-02-27 19:32:33 +05:30
committed by GitHub
parent 292ed87201
commit 2ba05a46ee
5 changed files with 6421 additions and 19 deletions

View File

@@ -33,6 +33,7 @@
"apollo-server-plugin-base": "^3.7.1", "apollo-server-plugin-base": "^3.7.1",
"argon2": "^0.30.3", "argon2": "^0.30.3",
"bcrypt": "^5.1.0", "bcrypt": "^5.1.0",
"cookie": "^0.5.0",
"cookie-parser": "^1.4.6", "cookie-parser": "^1.4.6",
"express": "^4.17.1", "express": "^4.17.1",
"fp-ts": "^2.13.1", "fp-ts": "^2.13.1",
@@ -62,10 +63,11 @@
"@relmify/jest-fp-ts": "^2.0.2", "@relmify/jest-fp-ts": "^2.0.2",
"@types/argon2": "^0.15.0", "@types/argon2": "^0.15.0",
"@types/bcrypt": "^5.0.0", "@types/bcrypt": "^5.0.0",
"@types/cookie": "^0.5.1",
"@types/cookie-parser": "^1.4.3", "@types/cookie-parser": "^1.4.3",
"@types/express": "^4.17.14", "@types/express": "^4.17.14",
"@types/luxon": "^3.2.0",
"@types/jest": "^29.4.0", "@types/jest": "^29.4.0",
"@types/luxon": "^3.2.0",
"@types/node": "^18.11.10", "@types/node": "^18.11.10",
"@types/passport-github2": "^1.2.5", "@types/passport-github2": "^1.2.5",
"@types/passport-google-oauth20": "^2.0.11", "@types/passport-google-oauth20": "^2.0.11",
@@ -96,7 +98,7 @@
"ts" "ts"
], ],
"setupFilesAfterEnv": [ "setupFilesAfterEnv": [
"@relmify/jest-fp-ts" "../jest.setup.js"
], ],
"preset": "ts-jest", "preset": "ts-jest",
"clearMocks": true, "clearMocks": true,
@@ -106,7 +108,6 @@
"rootDir": "src", "rootDir": "src",
"moduleNameMapper": { "moduleNameMapper": {
"^src/(.*)$": "<rootDir>/$1" "^src/(.*)$": "<rootDir>/$1"
}, }
"setupFilesAfterEnv": ["../jest.setup.js"]
} }
} }

6381
packages/hoppscotch-backend/pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
import { Module } from '@nestjs/common'; import { ForbiddenException, HttpException, Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql'; import { GraphQLModule } from '@nestjs/graphql';
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo'; import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
import { UserModule } from './user/user.module'; import { UserModule } from './user/user.module';
@@ -14,6 +14,7 @@ import { TeamCollectionModule } from './team-collection/team-collection.module';
import { TeamRequestModule } from './team-request/team-request.module'; import { TeamRequestModule } from './team-request/team-request.module';
import { TeamInvitationModule } from './team-invitation/team-invitation.module'; import { TeamInvitationModule } from './team-invitation/team-invitation.module';
import { ShortcodeModule } from './shortcode/shortcode.module'; import { ShortcodeModule } from './shortcode/shortcode.module';
import { COOKIES_NOT_FOUND } from './errors';
@Module({ @Module({
imports: [ imports: [
@@ -30,12 +31,19 @@ import { ShortcodeModule } from './shortcode/shortcode.module';
'subscriptions-transport-ws': { 'subscriptions-transport-ws': {
path: '/graphql', path: '/graphql',
onConnect: (_, websocket) => { onConnect: (_, websocket) => {
const cookies = subscriptionContextCookieParser( try {
websocket.upgradeReq.headers.cookie, const cookies = subscriptionContextCookieParser(
); websocket.upgradeReq.headers.cookie,
return { );
headers: { ...websocket?.upgradeReq?.headers, cookies },
}; return {
headers: { ...websocket?.upgradeReq?.headers, cookies },
};
} catch (error) {
throw new HttpException(COOKIES_NOT_FOUND, 400, {
cause: new Error(COOKIES_NOT_FOUND),
});
}
}, },
}, },
}, },

View File

@@ -1,8 +1,10 @@
import { HttpException, HttpStatus } from '@nestjs/common'; import { ForbiddenException, HttpException, HttpStatus } from '@nestjs/common';
import { DateTime } from 'luxon'; import { DateTime } from 'luxon';
import { AuthError } from 'src/types/AuthError'; import { AuthError } from 'src/types/AuthError';
import { AuthTokens } from 'src/types/AuthTokens'; import { AuthTokens } from 'src/types/AuthTokens';
import { Response } from 'express'; import { Response } from 'express';
import * as cookie from 'cookie';
import { COOKIES_NOT_FOUND } from 'src/errors';
enum AuthTokenType { enum AuthTokenType {
ACCESS_TOKEN = 'access_token', ACCESS_TOKEN = 'access_token',
@@ -64,14 +66,19 @@ export const authCookieHandler = (
* @returns AuthTokens for JWT strategy to use * @returns AuthTokens for JWT strategy to use
*/ */
export const subscriptionContextCookieParser = (rawCookies: string) => { export const subscriptionContextCookieParser = (rawCookies: string) => {
const cookieMap = new Map<string, string>(); const cookies = cookie.parse(rawCookies);
rawCookies.split(';').forEach((cookie) => {
const [key, value] = cookie.split('='); if (
cookieMap.set(key, value); !cookies[AuthTokenType.ACCESS_TOKEN] &&
}); !cookies[AuthTokenType.REFRESH_TOKEN]
) {
throw new HttpException(COOKIES_NOT_FOUND, 400, {
cause: new Error(COOKIES_NOT_FOUND),
});
}
return <AuthTokens>{ return <AuthTokens>{
access_token: cookieMap.get(AuthTokenType.ACCESS_TOKEN), access_token: cookies[AuthTokenType.ACCESS_TOKEN],
refresh_token: cookieMap.get(AuthTokenType.REFRESH_TOKEN), refresh_token: cookies[AuthTokenType.REFRESH_TOKEN],
}; };
}; };

View File

@@ -19,3 +19,8 @@ export type AuthTokens = {
access_token: string; access_token: string;
refresh_token: string; refresh_token: string;
}; };
export enum AuthTokenType {
ACCESS_TOKEN = 'access_token',
REFRESH_TOKEN = 'refresh_token',
}