Merge pull request #13 from hoppscotch/hotfix/subscriptions

Hotfix: fix subscription cookie issue
This commit is contained in:
Balu Babu
2023-02-08 16:46:49 +05:30
committed by GitHub
10 changed files with 33 additions and 241 deletions

View File

@@ -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

View File

@@ -2,6 +2,8 @@ version: '3.0'
services:
local:
build: .
env_file:
- .env
command: [ "pnpm", "run", "start:dev" ]
environment:
- PRODUCTION=false

View File

@@ -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;

View File

@@ -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"

View File

@@ -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,

View File

@@ -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 <AuthTokens>{
access_token,
refresh_token,
};
};

View File

@@ -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);
}

View File

@@ -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;
},
);

View File

@@ -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;
}
}

View File

@@ -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();