diff --git a/packages/hoppscotch-backend/.env.example b/packages/hoppscotch-backend/.env.example index f376047db..726cb8bc7 100644 --- a/packages/hoppscotch-backend/.env.example +++ b/packages/hoppscotch-backend/.env.example @@ -6,7 +6,6 @@ POSTMARK_SERVER_TOKEN=************************************************" POSTMARK_SENDER_EMAIL=************************************************" # Auth Tokens Config -SIGNED_COOKIE_SECRET='add some secret here' JWT_SECRET='add some secret here' TOKEN_SALT_COMPLEXITY=10 MAGIC_LINK_TOKEN_VALIDITY=3 diff --git a/packages/hoppscotch-backend/docker-compose.yml b/packages/hoppscotch-backend/docker-compose.yml index a27bd2c7d..f9f606083 100644 --- a/packages/hoppscotch-backend/docker-compose.yml +++ b/packages/hoppscotch-backend/docker-compose.yml @@ -2,6 +2,8 @@ version: '3.0' services: local: build: . + env_file: + - .env command: [ "pnpm", "run", "start:dev" ] environment: - PRODUCTION=false diff --git a/packages/hoppscotch-backend/prisma/migrations/20230201061003_init/migration.sql b/packages/hoppscotch-backend/prisma/migrations/20230201061003_init/migration.sql deleted file mode 100644 index 1f80820f0..000000000 --- a/packages/hoppscotch-backend/prisma/migrations/20230201061003_init/migration.sql +++ /dev/null @@ -1,209 +0,0 @@ --- CreateEnum -CREATE TYPE "ReqType" AS ENUM ('REST', 'GQL'); - --- CreateEnum -CREATE TYPE "TeamMemberRole" AS ENUM ('OWNER', 'VIEWER', 'EDITOR'); - --- CreateTable -CREATE TABLE "Team" ( - "id" TEXT NOT NULL, - "name" TEXT NOT NULL, - - CONSTRAINT "Team_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "TeamMember" ( - "id" TEXT NOT NULL, - "role" "TeamMemberRole" NOT NULL, - "userUid" TEXT NOT NULL, - "teamID" TEXT NOT NULL, - - CONSTRAINT "TeamMember_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "TeamInvitation" ( - "id" TEXT NOT NULL, - "teamID" TEXT NOT NULL, - "creatorUid" TEXT NOT NULL, - "inviteeEmail" TEXT NOT NULL, - "inviteeRole" "TeamMemberRole" NOT NULL, - - CONSTRAINT "TeamInvitation_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "TeamCollection" ( - "id" TEXT NOT NULL, - "parentID" TEXT, - "teamID" TEXT NOT NULL, - "title" TEXT NOT NULL, - - CONSTRAINT "TeamCollection_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "TeamRequest" ( - "id" TEXT NOT NULL, - "collectionID" TEXT NOT NULL, - "teamID" TEXT NOT NULL, - "title" TEXT NOT NULL, - "request" JSONB NOT NULL, - - CONSTRAINT "TeamRequest_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "Shortcode" ( - "id" TEXT NOT NULL, - "request" JSONB NOT NULL, - "creatorUid" TEXT, - "createdOn" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "Shortcode_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "TeamEnvironment" ( - "id" TEXT NOT NULL, - "teamID" TEXT NOT NULL, - "name" TEXT NOT NULL, - "variables" JSONB NOT NULL, - - CONSTRAINT "TeamEnvironment_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "User" ( - "uid" TEXT NOT NULL, - "displayName" TEXT, - "email" TEXT, - "photoURL" TEXT, - "isAdmin" BOOLEAN NOT NULL DEFAULT false, - "refreshToken" TEXT, - "currentRESTSession" JSONB, - "currentGQLSession" JSONB, - "createdOn" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "User_pkey" PRIMARY KEY ("uid") -); - --- CreateTable -CREATE TABLE "Account" ( - "id" TEXT NOT NULL, - "userId" TEXT NOT NULL, - "provider" TEXT NOT NULL, - "providerAccountId" TEXT NOT NULL, - "providerRefreshToken" TEXT, - "providerAccessToken" TEXT, - "providerScope" TEXT, - "loggedIn" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "Account_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "VerificationToken" ( - "deviceIdentifier" TEXT NOT NULL, - "token" TEXT NOT NULL, - "userUid" TEXT NOT NULL, - "expiresOn" TIMESTAMP(3) NOT NULL -); - --- CreateTable -CREATE TABLE "UserSettings" ( - "id" TEXT NOT NULL, - "userUid" TEXT NOT NULL, - "properties" JSONB NOT NULL, - "updatedOn" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "UserSettings_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "UserHistory" ( - "id" TEXT NOT NULL, - "userUid" TEXT NOT NULL, - "reqType" "ReqType" NOT NULL, - "request" JSONB NOT NULL, - "responseMetadata" JSONB NOT NULL, - "isStarred" BOOLEAN NOT NULL, - "executedOn" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "UserHistory_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "UserEnvironment" ( - "id" TEXT NOT NULL, - "userUid" TEXT NOT NULL, - "name" TEXT, - "variables" JSONB NOT NULL, - "isGlobal" BOOLEAN NOT NULL, - - CONSTRAINT "UserEnvironment_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE UNIQUE INDEX "TeamMember_teamID_userUid_key" ON "TeamMember"("teamID", "userUid"); - --- CreateIndex -CREATE INDEX "TeamInvitation_teamID_idx" ON "TeamInvitation"("teamID"); - --- CreateIndex -CREATE UNIQUE INDEX "TeamInvitation_teamID_inviteeEmail_key" ON "TeamInvitation"("teamID", "inviteeEmail"); - --- CreateIndex -CREATE UNIQUE INDEX "Shortcode_id_creatorUid_key" ON "Shortcode"("id", "creatorUid"); - --- CreateIndex -CREATE UNIQUE INDEX "User_email_key" ON "User"("email"); - --- CreateIndex -CREATE UNIQUE INDEX "Account_provider_providerAccountId_key" ON "Account"("provider", "providerAccountId"); - --- CreateIndex -CREATE UNIQUE INDEX "VerificationToken_token_key" ON "VerificationToken"("token"); - --- CreateIndex -CREATE UNIQUE INDEX "VerificationToken_deviceIdentifier_token_key" ON "VerificationToken"("deviceIdentifier", "token"); - --- CreateIndex -CREATE UNIQUE INDEX "UserSettings_userUid_key" ON "UserSettings"("userUid"); - --- AddForeignKey -ALTER TABLE "TeamMember" ADD CONSTRAINT "TeamMember_teamID_fkey" FOREIGN KEY ("teamID") REFERENCES "Team"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "TeamInvitation" ADD CONSTRAINT "TeamInvitation_teamID_fkey" FOREIGN KEY ("teamID") REFERENCES "Team"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "TeamCollection" ADD CONSTRAINT "TeamCollection_parentID_fkey" FOREIGN KEY ("parentID") REFERENCES "TeamCollection"("id") ON DELETE SET NULL ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "TeamCollection" ADD CONSTRAINT "TeamCollection_teamID_fkey" FOREIGN KEY ("teamID") REFERENCES "Team"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "TeamRequest" ADD CONSTRAINT "TeamRequest_collectionID_fkey" FOREIGN KEY ("collectionID") REFERENCES "TeamCollection"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "TeamRequest" ADD CONSTRAINT "TeamRequest_teamID_fkey" FOREIGN KEY ("teamID") REFERENCES "Team"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "TeamEnvironment" ADD CONSTRAINT "TeamEnvironment_teamID_fkey" FOREIGN KEY ("teamID") REFERENCES "Team"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "Account" ADD CONSTRAINT "Account_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("uid") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "VerificationToken" ADD CONSTRAINT "VerificationToken_userUid_fkey" FOREIGN KEY ("userUid") REFERENCES "User"("uid") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "UserSettings" ADD CONSTRAINT "UserSettings_userUid_fkey" FOREIGN KEY ("userUid") REFERENCES "User"("uid") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "UserHistory" ADD CONSTRAINT "UserHistory_userUid_fkey" FOREIGN KEY ("userUid") REFERENCES "User"("uid") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "UserEnvironment" ADD CONSTRAINT "UserEnvironment_userUid_fkey" FOREIGN KEY ("userUid") REFERENCES "User"("uid") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/packages/hoppscotch-backend/prisma/migrations/migration_lock.toml b/packages/hoppscotch-backend/prisma/migrations/migration_lock.toml deleted file mode 100644 index fbffa92c2..000000000 --- a/packages/hoppscotch-backend/prisma/migrations/migration_lock.toml +++ /dev/null @@ -1,3 +0,0 @@ -# Please do not edit this file manually -# It should be added in your version-control system (i.e. Git) -provider = "postgresql" \ No newline at end of file diff --git a/packages/hoppscotch-backend/src/app.module.ts b/packages/hoppscotch-backend/src/app.module.ts index cbb9cd49c..d77e995db 100644 --- a/packages/hoppscotch-backend/src/app.module.ts +++ b/packages/hoppscotch-backend/src/app.module.ts @@ -7,6 +7,7 @@ import { AuthModule } from './auth/auth.module'; import { UserSettingsModule } from './user-settings/user-settings.module'; import { UserEnvironmentsModule } from './user-environment/user-environments.module'; import { UserHistoryModule } from './user-history/user-history.module'; +import { subscriptionContextCookieParser } from './auth/helper'; @Module({ imports: [ @@ -22,33 +23,21 @@ import { UserHistoryModule } from './user-history/user-history.module'; subscriptions: { 'subscriptions-transport-ws': { path: '/graphql', - onConnect: (connectionParams: any) => { + onConnect: (_, websocket) => { + const cookies = subscriptionContextCookieParser( + websocket.upgradeReq.headers.cookie, + ); return { - reqHeaders: Object.fromEntries( - Object.entries(connectionParams).map(([k, v]) => [ - k.toLowerCase(), - v, - ]), - ), + headers: { ...websocket?.upgradeReq?.headers, cookies }, }; }, }, }, - context: async ({ req, connection }) => { - if (req) { - return { reqHeaders: req.headers }; - } else { - return { - // Lowercase the keys - reqHeaders: Object.fromEntries( - Object.entries(connection.context).map(([k, v]) => [ - k.toLowerCase(), - v, - ]), - ), - }; - } - }, + context: ({ req, res, connection }) => ({ + req, + res, + connection, + }), driver: ApolloDriver, }), UserModule, diff --git a/packages/hoppscotch-backend/src/auth/helper.ts b/packages/hoppscotch-backend/src/auth/helper.ts index 43c3ab94d..a2a9371de 100644 --- a/packages/hoppscotch-backend/src/auth/helper.ts +++ b/packages/hoppscotch-backend/src/auth/helper.ts @@ -41,16 +41,28 @@ export const authCookieHandler = ( secure: true, sameSite: 'lax', maxAge: accessTokenValidity, - signed: true, }); res.cookie('refresh_token', authTokens.refresh_token, { httpOnly: true, secure: true, sameSite: 'lax', maxAge: refreshTokenValidity, - signed: true, }); if (redirect) { res.status(HttpStatus.OK).redirect(process.env.REDIRECT_URL); } else res.status(HttpStatus.OK).send(); }; + +/** + * Decode the cookie header from incoming websocket connects and returns a auth token pair + * @param rawCookies cookies from the websocket connection + * @returns AuthTokens for JWT strategy to use + */ +export const subscriptionContextCookieParser = (rawCookies: string) => { + const access_token = rawCookies.split(';')[0].split('=')[1]; + const refresh_token = rawCookies.split(';')[1].split('=')[1]; + return { + access_token, + refresh_token, + }; +}; diff --git a/packages/hoppscotch-backend/src/auth/strategies/jwt.strategy.ts b/packages/hoppscotch-backend/src/auth/strategies/jwt.strategy.ts index 82c2ca735..d589e82c5 100644 --- a/packages/hoppscotch-backend/src/auth/strategies/jwt.strategy.ts +++ b/packages/hoppscotch-backend/src/auth/strategies/jwt.strategy.ts @@ -22,7 +22,7 @@ export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') { super({ jwtFromRequest: ExtractJwt.fromExtractors([ (request: Request) => { - const ATCookie = request.signedCookies['access_token']; + const ATCookie = request.cookies['access_token']; if (!ATCookie) { throw new ForbiddenException(COOKIES_NOT_FOUND); } diff --git a/packages/hoppscotch-backend/src/decorators/gql-user.decorator.ts b/packages/hoppscotch-backend/src/decorators/gql-user.decorator.ts index 83d71e56b..71042e069 100644 --- a/packages/hoppscotch-backend/src/decorators/gql-user.decorator.ts +++ b/packages/hoppscotch-backend/src/decorators/gql-user.decorator.ts @@ -4,6 +4,7 @@ import { GqlExecutionContext } from '@nestjs/graphql'; export const GqlUser = createParamDecorator( (data: unknown, context: ExecutionContext) => { const ctx = GqlExecutionContext.create(context); - return ctx.getContext().req.user; + const { req, headers } = ctx.getContext(); + return headers ? headers.user : req.user; }, ); diff --git a/packages/hoppscotch-backend/src/guards/gql-auth.guard.ts b/packages/hoppscotch-backend/src/guards/gql-auth.guard.ts index 3ae7ade72..6d9952995 100644 --- a/packages/hoppscotch-backend/src/guards/gql-auth.guard.ts +++ b/packages/hoppscotch-backend/src/guards/gql-auth.guard.ts @@ -6,6 +6,7 @@ import { AuthGuard } from '@nestjs/passport'; export class GqlAuthGuard extends AuthGuard('jwt') { getRequest(context: ExecutionContext) { const ctx = GqlExecutionContext.create(context); - return ctx.getContext().req; + const { req, headers } = ctx.getContext(); + return headers ? headers : req; } } diff --git a/packages/hoppscotch-backend/src/main.ts b/packages/hoppscotch-backend/src/main.ts index 6a3f84071..31f8e737a 100644 --- a/packages/hoppscotch-backend/src/main.ts +++ b/packages/hoppscotch-backend/src/main.ts @@ -35,7 +35,7 @@ async function bootstrap() { app.enableVersioning({ type: VersioningType.URI, }); - app.use(cookieParser(process.env.SIGNED_COOKIE_SECRET)); + app.use(cookieParser()); await app.listen(process.env.PORT || 3170); } bootstrap();