Fix white screen on token expire (#5271)

While using middleware (executed pre-graphql) for graphql endpoint, we
need to swallow exception and return errors with a 200. Otherwise it's
not a valid graphql response
This commit is contained in:
Charles Bochet 2024-05-03 15:35:49 +02:00 committed by GitHub
parent 2a0c74ab0f
commit 1d9cd234ea
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 77 additions and 52 deletions

View File

@ -21,8 +21,8 @@ import { MetadataGraphQLApiModule } from 'src/engine/api/graphql/metadata-graphq
import { GraphQLConfigModule } from 'src/engine/api/graphql/graphql-config/graphql-config.module';
import { GraphQLConfigService } from 'src/engine/api/graphql/graphql-config/graphql-config.service';
import { EnvironmentService } from 'src/engine/integrations/environment/environment.service';
import { UserWorkspaceMiddleware } from 'src/engine/middlewares/user-workspace.middleware';
import { WorkspaceCacheVersionModule } from 'src/engine/metadata-modules/workspace-cache-version/workspace-cache-version.module';
import { GraphQLHydrateRequestFromTokenMiddleware } from 'src/engine/middlewares/graphql-hydrate-request-from-token.middleware';
import { CoreEngineModule } from './engine/core-modules/core-engine.module';
import { IntegrationsModule } from './engine/integrations/integrations.module';
@ -79,11 +79,11 @@ export class AppModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(UserWorkspaceMiddleware)
.apply(GraphQLHydrateRequestFromTokenMiddleware)
.forRoutes({ path: 'graphql', method: RequestMethod.ALL });
consumer
.apply(UserWorkspaceMiddleware)
.apply(GraphQLHydrateRequestFromTokenMiddleware)
.forRoutes({ path: 'metadata', method: RequestMethod.ALL });
}
}

View File

@ -0,0 +1,74 @@
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
import { TokenService } from 'src/engine/core-modules/auth/services/token.service';
import { JwtData } from 'src/engine/core-modules/auth/types/jwt-data.type';
import { ExceptionHandlerService } from 'src/engine/integrations/exception-handler/exception-handler.service';
import { WorkspaceCacheVersionService } from 'src/engine/metadata-modules/workspace-cache-version/workspace-cache-version.service';
import { handleExceptionAndConvertToGraphQLError } from 'src/engine/utils/global-exception-handler.util';
@Injectable()
export class GraphQLHydrateRequestFromTokenMiddleware
implements NestMiddleware
{
constructor(
private readonly tokenService: TokenService,
private readonly workspaceCacheVersionService: WorkspaceCacheVersionService,
private readonly exceptionHandlerService: ExceptionHandlerService,
) {}
async use(req: Request, res: Response, next: NextFunction) {
const body = req.body;
const excludedOperations = [
'GetClientConfig',
'GetCurrentUser',
'GetWorkspaceFromInviteHash',
'Track',
'CheckUserExists',
'Challenge',
'Verify',
'SignUp',
'RenewToken',
'IntrospectionQuery',
];
if (
!this.tokenService.isTokenPresent(req) &&
(!body?.operationName || excludedOperations.includes(body.operationName))
) {
return next();
}
let data: JwtData;
try {
data = await this.tokenService.validateToken(req);
const cacheVersion = await this.workspaceCacheVersionService.getVersion(
data.workspace.id,
);
req.user = data.user;
req.workspace = data.workspace;
req.cacheVersion = cacheVersion;
} catch (error) {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.write(
JSON.stringify({
errors: [
handleExceptionAndConvertToGraphQLError(
error,
this.exceptionHandlerService,
),
],
}),
);
res.end();
return;
}
next();
}
}

View File

@ -1,49 +0,0 @@
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
import { TokenService } from 'src/engine/core-modules/auth/services/token.service';
import { WorkspaceCacheVersionService } from 'src/engine/metadata-modules/workspace-cache-version/workspace-cache-version.service';
@Injectable()
export class UserWorkspaceMiddleware implements NestMiddleware {
constructor(
private readonly tokenService: TokenService,
private readonly workspaceCacheVersionService: WorkspaceCacheVersionService,
) {}
async use(req: Request, res: Response, next: NextFunction) {
const body = req.body;
const excludedOperations = [
'GetClientConfig',
'GetCurrentUser',
'GetWorkspaceFromInviteHash',
'Track',
'CheckUserExists',
'Challenge',
'Verify',
'SignUp',
'RenewToken',
'IntrospectionQuery',
];
if (
!this.tokenService.isTokenPresent(req) &&
(!body?.operationName || excludedOperations.includes(body.operationName))
) {
return next();
}
const data = await this.tokenService.validateToken(req);
const cacheVersion = await this.workspaceCacheVersionService.getVersion(
data.workspace.id,
);
req.user = data.user;
req.workspace = data.workspace;
req.cacheVersion = cacheVersion;
next();
}
}