diff --git a/src/shared/states.guard.ts b/src/shared/states.guard.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fb8a18fbb51b5f265d567ba5c62e2039a6d83e1a
--- /dev/null
+++ b/src/shared/states.guard.ts
@@ -0,0 +1,46 @@
+import {
+  Injectable,
+  CanActivate,
+  ExecutionContext,
+  HttpException,
+  HttpStatus,
+} from '@nestjs/common';
+import { Reflector } from '@nestjs/core';
+import { InjectRepository } from '@nestjs/typeorm';
+import { Repository } from 'typeorm';
+
+import { GameEntity } from '../game/game.entity';
+
+@Injectable()
+export class StatesGuard implements CanActivate {
+  constructor(
+    private readonly reflector: Reflector,
+    @InjectRepository(GameEntity)
+    private gameRepository: Repository<GameEntity>,
+  ) {}
+
+  async canActivate(context: ExecutionContext): Promise<boolean> {
+    // get game states that are allowed access, identified by @GameStates('state') decorators in controllers
+    const states = this.reflector.get<string[]>('states', context.getHandler());
+    console.log(states);
+    if (!states) {
+      return true;
+    }
+    const request = context.switchToHttp().getRequest();
+    const gameId = request.params.id;
+    const gameRef = await this.gameRepository.findOne({
+      id: gameId,
+    });
+    // check that the gameState matches the criteria
+    if (gameRef && states.includes(gameRef.state)) {
+      return true;
+    } else {
+      throw new HttpException(
+        `Game is set to ${
+          gameRef.state
+        }, operation only valid in states ${states.join(', ')}`,
+        HttpStatus.BAD_REQUEST,
+      );
+    }
+  }
+}