diff --git a/.vscode/settings.json b/.vscode/settings.json
index ffbd77a7309c46b530b1097e83ca3690b43fb5f9..d064f2c0c8cbe0db00a1acb24bb9188e1f375c5c 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -5,7 +5,7 @@
   "editor.tabCompletion": "on",
   "editor.codeActionsOnSave": {
     "source.fixAll.eslint": true,
-    "source.organizeImports": false
+    "source.organizeImports": true
   },
   //python
   "python.venvPath": "${workspaceFolder}\\server",
diff --git a/client/src/Main.tsx b/client/src/Main.tsx
index b1f0675d7129f755eb70ff9506c4c145a9b5a83c..5cd27cc3d83e96bda772e2e9b07282903ed069f5 100644
--- a/client/src/Main.tsx
+++ b/client/src/Main.tsx
@@ -7,8 +7,8 @@ import LoginPage from './pages/login/LoginPage'
 import PresentationEditorPage from './pages/presentationEditor/PresentationEditorPage'
 import AudienceViewPage from './pages/views/AudienceViewPage'
 import JudgeViewPage from './pages/views/JudgeViewPage'
-import TeamViewPage from './pages/views/TeamViewPage'
 import OperatorViewPage from './pages/views/OperatorViewPage'
+import TeamViewPage from './pages/views/TeamViewPage'
 import ViewSelectPage from './pages/views/ViewSelectPage'
 import SecureRoute from './utils/SecureRoute'
 
@@ -20,14 +20,38 @@ const Main: React.FC = () => {
   return (
     <BrowserRouter>
       <Switch>
-        <SecureRoute login exact path="/" component={LoginPage} />
-        <SecureRoute path="/admin" component={AdminPage} />
-        <SecureRoute path="/editor/competition-id=:competitionId" component={PresentationEditorPage} />
+        <SecureRoute authLevel="login" exact path="/" component={LoginPage} />
+        <SecureRoute authLevel="admin" path="/admin" component={AdminPage} />
+        <SecureRoute
+          authLevel="admin"
+          path="/editor/competition-id=:competitionId"
+          component={PresentationEditorPage}
+        />
         <Route exact path="/:code" component={ViewSelectPage} />
-        <Route exact path="/team/id=:id&code=:code" component={TeamViewPage} />
-        <SecureRoute exact path="/operator/id=:id&code=:code" component={OperatorViewPage} />
-        <Route exact path="/judge/id=:id&code=:code" component={JudgeViewPage} />
-        <Route exact path="/audience/id=:id&code=:code" component={AudienceViewPage} />
+        <SecureRoute
+          authLevel="competition"
+          exact
+          path="/team/competition-id=:competitionId"
+          component={TeamViewPage}
+        />
+        <SecureRoute
+          authLevel="competition"
+          exact
+          path="/operator/competition-id=:competitionId"
+          component={OperatorViewPage}
+        />
+        <SecureRoute
+          authLevel="competition"
+          exact
+          path="/judge/competition-id=:competitionId"
+          component={JudgeViewPage}
+        />
+        <SecureRoute
+          authLevel="competition"
+          exact
+          path="/audience/competition-id=:competitionId"
+          component={AudienceViewPage}
+        />
       </Switch>
     </BrowserRouter>
   )
diff --git a/client/src/actions/competitionLogin.ts b/client/src/actions/competitionLogin.ts
index 093abdb8dfb638752f9968815fd8bb2bc2159930..448eec249f360acbadeb6161abac6412daccb32b 100644
--- a/client/src/actions/competitionLogin.ts
+++ b/client/src/actions/competitionLogin.ts
@@ -5,19 +5,29 @@ This file handles actions for the competitionLogin redux state
 import axios from 'axios'
 import { History } from 'history'
 import { AppDispatch } from '../store'
-import { AccountLoginModel } from './../interfaces/FormModels'
 import Types from './types'
 
 // Action creator to attempt to login with competition code
-export const loginCompetition = (code: string, history: History) => async (dispatch: AppDispatch) => {
+export const loginCompetition = (code: string, history: History, redirect: boolean) => async (
+  dispatch: AppDispatch
+) => {
   dispatch({ type: Types.LOADING_COMPETITION_LOGIN })
   await axios
     .post('/api/auth/login/code', { code })
     .then((res) => {
-      console.log(code, res.data[0])
+      const token = `Bearer ${res.data.access_token}`
+      localStorage.setItem('competitionToken', token) //setting token to local storage
+      axios.defaults.headers.common['Authorization'] = token //setting authorize token to header in axios
       dispatch({ type: Types.CLEAR_COMPETITION_LOGIN_ERRORS }) // no error
-      // history.push('/admin') //redirecting to admin page after login success
-      if (res.data && res.data[0] && res.data[0].view_type_id) {
+      dispatch({
+        type: Types.SET_COMPETITION_LOGIN_DATA,
+        payload: {
+          competition_id: res.data.competition_id,
+          team_id: res.data.team_id,
+          view: res.data.view,
+        },
+      })
+      if (redirect && res.data && res.data.view_type_id) {
         history.push(`/${code}`)
       }
     })
@@ -26,3 +36,15 @@ export const loginCompetition = (code: string, history: History) => async (dispa
       console.log(err)
     })
 }
+
+// Log out from competition and remove jwt token from local storage and axios
+export const logoutCompetition = () => async (dispatch: AppDispatch) => {
+  localStorage.removeItem('competitionToken')
+  await axios.post('/api/auth/logout').then(() => {
+    delete axios.defaults.headers.common['Authorization']
+    dispatch({
+      type: Types.SET_COMPETITION_LOGIN_UNAUTHENTICATED,
+    })
+    window.location.href = '/' //redirect to login page
+  })
+}
diff --git a/client/src/actions/types.ts b/client/src/actions/types.ts
index 2e9fc776f4c1828427df8424ef93d9588f20afea..b422f95423539cc2f2ea0bf4e7ecdc58f78bdbc8 100644
--- a/client/src/actions/types.ts
+++ b/client/src/actions/types.ts
@@ -14,6 +14,9 @@ export default {
   SET_SEARCH_USERS_TOTAL_COUNT: 'SET_SEARCH_USERS_TOTAL_COUNT',
   SET_ERRORS: 'SET_ERRORS',
   CLEAR_ERRORS: 'CLEAR_ERRORS',
+  SET_COMPETITION_LOGIN_DATA: 'SET_COMPETITION_LOGIN_DATA',
+  SET_COMPETITION_LOGIN_AUTHENTICATED: 'SET_COMPETITION_LOGIN_AUTHENTICATED',
+  SET_COMPETITION_LOGIN_UNAUTHENTICATED: 'SET_COMPETITION_LOGIN_UNAUTHENTICATED',
   SET_COMPETITION_LOGIN_ERRORS: 'SET_COMPETITION_LOGIN_ERRORS',
   CLEAR_COMPETITION_LOGIN_ERRORS: 'CLEAR_COMPETITION_LOGIN_ERRORS',
   SET_UNAUTHENTICATED: 'SET_UNAUTHENTICATED',
diff --git a/client/src/interfaces/ViewParams.ts b/client/src/interfaces/ViewParams.ts
index e9aa6a5c5f81a6bf852f8caa30443a793b0dddd7..b8114216500b3295050a0b62f6188fa065fcbfdb 100644
--- a/client/src/interfaces/ViewParams.ts
+++ b/client/src/interfaces/ViewParams.ts
@@ -1,4 +1,3 @@
 export interface ViewParams {
-  id: string
-  code: string
+  competitionId: string
 }
diff --git a/client/src/pages/admin/AdminPage.tsx b/client/src/pages/admin/AdminPage.tsx
index a8b1746aa8ee4e7564a6f13dc074b11082002747..1b543083da7fbfb1e5c1a48d44a40a2d7fdcc17e 100644
--- a/client/src/pages/admin/AdminPage.tsx
+++ b/client/src/pages/admin/AdminPage.tsx
@@ -16,7 +16,6 @@ import ExitToAppIcon from '@material-ui/icons/ExitToApp'
 import LocationCityIcon from '@material-ui/icons/LocationCity'
 import PeopleIcon from '@material-ui/icons/People'
 import SettingsOverscanIcon from '@material-ui/icons/SettingsOverscan'
-import axios from 'axios'
 import React, { useEffect } from 'react'
 import { Link, Route, Switch, useRouteMatch } from 'react-router-dom'
 import { getCities } from '../../actions/cities'
diff --git a/client/src/pages/login/components/CompetitionLogin.tsx b/client/src/pages/login/components/CompetitionLogin.tsx
index d89cafcf8216197de081d65550116e2e06b56f22..8dbbee33b6962c5bcd21f346ee594b224719a9a7 100644
--- a/client/src/pages/login/components/CompetitionLogin.tsx
+++ b/client/src/pages/login/components/CompetitionLogin.tsx
@@ -1,9 +1,8 @@
 import { Button, TextField, Typography } from '@material-ui/core'
 import { Alert, AlertTitle } from '@material-ui/lab'
-import axios from 'axios'
-import { Formik, FormikHelpers } from 'formik'
+import { Formik } from 'formik'
+import React from 'react'
 import { useHistory } from 'react-router-dom'
-import React, { useEffect, useState } from 'react'
 import * as Yup from 'yup'
 import { loginCompetition } from '../../../actions/competitionLogin'
 import { useAppDispatch, useAppSelector } from '../../../hooks'
@@ -37,8 +36,9 @@ const CompetitionLogin: React.FC = () => {
     model: { code: '' },
   }
   const handleCompetitionSubmit = async (values: CompetitionLoginFormModel) => {
-    dispatch(loginCompetition(values.model.code, history))
+    dispatch(loginCompetition(values.model.code, history, true))
   }
+  
   return (
     <Formik
       initialValues={competitionInitialValues}
diff --git a/client/src/pages/presentationEditor/PresentationEditorPage.tsx b/client/src/pages/presentationEditor/PresentationEditorPage.tsx
index 6d9bc11905e9cca28526bd27ceb0921648ef5ed7..886f8d19a24ea6f8d7469b1481a5a11def854469 100644
--- a/client/src/pages/presentationEditor/PresentationEditorPage.tsx
+++ b/client/src/pages/presentationEditor/PresentationEditorPage.tsx
@@ -1,4 +1,4 @@
-import { Button, CircularProgress, Divider, Menu, MenuItem, Typography } from '@material-ui/core'
+import { Button, CircularProgress, Divider, Menu, MenuItem } from '@material-ui/core'
 import CssBaseline from '@material-ui/core/CssBaseline'
 import ListItemText from '@material-ui/core/ListItemText'
 import AddOutlinedIcon from '@material-ui/icons/AddOutlined'
@@ -17,21 +17,21 @@ import SlideDisplay from './components/SlideDisplay'
 import {
   AppBarEditor,
   CenteredSpinnerContainer,
+  CompetitionName,
+  FillLeftContainer,
+  FillRightContainer,
   HomeIcon,
   LeftDrawer,
-  RightDrawer,
+  PositionBottom,
   PresentationEditorContainer,
+  RightDrawer,
+  RightPanelScroll,
   SlideList,
   SlideListItem,
   ToolBarContainer,
+  ToolbarMargin,
   ViewButton,
   ViewButtonGroup,
-  ToolbarMargin,
-  FillLeftContainer,
-  PositionBottom,
-  FillRightContainer,
-  CompetitionName,
-  RightPanelScroll,
 } from './styled'
 
 const initialState = {
@@ -113,7 +113,7 @@ const PresentationEditorPage: React.FC = () => {
   return (
     <PresentationEditorContainer>
       <CssBaseline />
-      <AppBarEditor leftDrawerWidth={leftDrawerWidth} rightDrawerWidth={rightDrawerWidth} position="fixed">
+      <AppBarEditor $leftDrawerWidth={leftDrawerWidth} $rightDrawerWidth={rightDrawerWidth} position="fixed">
         <ToolBarContainer>
           <Button component={Link} to="/admin/tävlingshanterare" style={{ padding: 0 }}>
             <HomeIcon src="/t8.png" />
@@ -142,8 +142,8 @@ const PresentationEditorPage: React.FC = () => {
           </ViewButtonGroup>
         </ToolBarContainer>
       </AppBarEditor>
-      <LeftDrawer leftDrawerWidth={leftDrawerWidth} rightDrawerWidth={undefined} variant="permanent" anchor="left">
-        <FillLeftContainer leftDrawerWidth={leftDrawerWidth} rightDrawerWidth={undefined}>
+      <LeftDrawer $leftDrawerWidth={leftDrawerWidth} $rightDrawerWidth={undefined} variant="permanent" anchor="left">
+        <FillLeftContainer $leftDrawerWidth={leftDrawerWidth} $rightDrawerWidth={undefined}>
           <ToolbarMargin />
           <SlideList>
             {competition.slides &&
@@ -166,13 +166,13 @@ const PresentationEditorPage: React.FC = () => {
             <SlideListItem divider button onClick={() => createNewSlide()}>
               <ListItemText primary="Ny sida" />
               <AddOutlinedIcon />
-            </SlideListItem>
+              </SlideListItem>
           </PositionBottom>
         </FillLeftContainer>
       </LeftDrawer>
       <ToolbarMargin />
-      <RightDrawer leftDrawerWidth={undefined} rightDrawerWidth={rightDrawerWidth} variant="permanent" anchor="right">
-        <FillRightContainer leftDrawerWidth={undefined} rightDrawerWidth={rightDrawerWidth}>
+      <RightDrawer $leftDrawerWidth={undefined} $rightDrawerWidth={rightDrawerWidth} variant="permanent" anchor="right">
+        <FillRightContainer $leftDrawerWidth={undefined} $rightDrawerWidth={rightDrawerWidth}>
           <RightPanelScroll>
             {!competitionLoading ? (
               <SettingsPanel />
diff --git a/client/src/pages/presentationEditor/components/RndComponent.tsx b/client/src/pages/presentationEditor/components/RndComponent.tsx
index 77ed5de337f5ed620e77994cea2e512166d7b7d9..8f2324c93a36d8e246043a4b5adeaec990f88dae 100644
--- a/client/src/pages/presentationEditor/components/RndComponent.tsx
+++ b/client/src/pages/presentationEditor/components/RndComponent.tsx
@@ -1,15 +1,13 @@
-import { Button, Card, IconButton, Tooltip, Typography } from '@material-ui/core'
+import { Card, IconButton, Tooltip } from '@material-ui/core'
 import axios from 'axios'
 import React, { useEffect, useState } from 'react'
 import { Rnd } from 'react-rnd'
 import { ComponentTypes } from '../../../enum/ComponentTypes'
 import { useAppSelector } from '../../../hooks'
-import { Component, ImageComponent, QuestionAlternativeComponent, TextComponent } from '../../../interfaces/ApiModels'
+import { Component, ImageComponent, TextComponent } from '../../../interfaces/ApiModels'
 import { Position, Size } from '../../../interfaces/Components'
-import CheckboxComponent from './CheckboxComponent'
 import ImageComponentDisplay from './ImageComponentDisplay'
 import { HoverContainer } from './styled'
-import FormatAlignCenterIcon from '@material-ui/icons/FormatAlignCenter'
 import TextComponentDisplay from './TextComponentDisplay'
 
 type RndComponentProps = {
@@ -42,13 +40,11 @@ const RndComponent = ({ component, width, height, scale }: RndComponentProps) =>
     })
   }
   const handleCenterHorizontal = () => {
-    console.log(width, currentSize.w)
     const centerX = width / (2 * scale) - currentSize.w / 2
     setCurrentPos({ x: centerX, y: currentPos.y })
     handleUpdatePos({ x: centerX, y: currentPos.y })
   }
   const handleCenterVertical = () => {
-    console.log(height, currentSize.h)
     const centerY = height / (2 * scale) - currentSize.h / 2
     setCurrentPos({ x: currentPos.x, y: centerY })
     handleUpdatePos({ x: currentPos.x, y: centerY })
diff --git a/client/src/pages/presentationEditor/components/TextComponentEdit.tsx b/client/src/pages/presentationEditor/components/TextComponentEdit.tsx
index f6cda5760ec8aff5bce211a8caf7d2435a8d5c61..04ddd6daa7bb015b3e48b905b3f17f3594c8ba98 100644
--- a/client/src/pages/presentationEditor/components/TextComponentEdit.tsx
+++ b/client/src/pages/presentationEditor/components/TextComponentEdit.tsx
@@ -36,7 +36,6 @@ const TextComponentEdit = ({ component }: ImageComponentProps) => {
     //Only updates 250ms after last input was made to not spam
     setTimerHandle(
       window.setTimeout(async () => {
-        console.log('Content was updated on server. id: ', component.id)
         await axios.put(`/api/competitions/${competitionId}/slides/${activeSlideId}/components/${component.id}`, {
           text: newText,
         })
diff --git a/client/src/pages/presentationEditor/components/slideSettingsComponents/Instructions.tsx b/client/src/pages/presentationEditor/components/slideSettingsComponents/Instructions.tsx
index 09279daeefa890c0fffa21b92bf3bc41b880c017..48917fcc01967b72e121b506fc6e4e05ac883c40 100644
--- a/client/src/pages/presentationEditor/components/slideSettingsComponents/Instructions.tsx
+++ b/client/src/pages/presentationEditor/components/slideSettingsComponents/Instructions.tsx
@@ -1,4 +1,4 @@
-import { ListItem, ListItemText, TextField, withStyles } from '@material-ui/core'
+import { ListItem, ListItemText, TextField } from '@material-ui/core'
 import axios from 'axios'
 import React from 'react'
 import { getEditorCompetition } from '../../../../actions/editor'
@@ -23,7 +23,6 @@ const Instructions = ({ activeSlide, competitionId }: InstructionsProps) => {
     //Only updates 250ms after last input was made to not spam
     setTimerHandle(
       window.setTimeout(async () => {
-        console.log('Content was updated on server. id: ', activeSlide.questions[0].id)
         if (activeSlide && activeSlide.questions[0]) {
           await axios
             // TODO: Implement instructions field in question and add put API
diff --git a/client/src/pages/presentationEditor/components/slideSettingsComponents/QuestionSettings.tsx b/client/src/pages/presentationEditor/components/slideSettingsComponents/QuestionSettings.tsx
index 2bc10b3588133245d54767dbbc7d8381e4134b59..e917afbd19f1aac2d7256c1f5913d8a63f323ee1 100644
--- a/client/src/pages/presentationEditor/components/slideSettingsComponents/QuestionSettings.tsx
+++ b/client/src/pages/presentationEditor/components/slideSettingsComponents/QuestionSettings.tsx
@@ -18,7 +18,6 @@ const QuestionSettings = ({ activeSlide, competitionId }: QuestionSettingsProps)
     updateTitle: boolean,
     event: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>
   ) => {
-    console.log('Content was updated on server. id: ', activeSlide.questions[0].id)
     if (activeSlide && activeSlide.questions[0]) {
       if (updateTitle) {
         await axios
diff --git a/client/src/pages/presentationEditor/styled.tsx b/client/src/pages/presentationEditor/styled.tsx
index c02054090f9b597fc32f1cacc03d145f3b77d9fe..4f830687898bd87fbd7c637dfd19706324fe909e 100644
--- a/client/src/pages/presentationEditor/styled.tsx
+++ b/client/src/pages/presentationEditor/styled.tsx
@@ -6,8 +6,8 @@ interface ViewButtonProps {
 }
 
 interface DrawerSizeProps {
-  leftDrawerWidth: number | undefined
-  rightDrawerWidth: number | undefined
+  $leftDrawerWidth: number | undefined
+  $rightDrawerWidth: number | undefined
 }
 
 const AppBarHeight = 64
@@ -66,22 +66,22 @@ export const HomeIcon = styled.img`
 `
 
 export const LeftDrawer = styled(Drawer)<DrawerSizeProps>`
-  width: ${(props) => (props ? props.leftDrawerWidth : 0)}px;
+  width: ${(props) => (props ? props.$leftDrawerWidth : 0)}px;
   flex-shrink: 0;
   position: relative;
   z-index: 1;
 `
 
 export const RightDrawer = styled(Drawer)<DrawerSizeProps>`
-  width: ${(props) => (props ? props.rightDrawerWidth : 0)}px;
+  width: ${(props) => (props ? props.$rightDrawerWidth : 0)}px;
   flex-shrink: 0;
 `
 
 export const AppBarEditor = styled(AppBar)<DrawerSizeProps>`
-  width: calc(100% - ${(props) => (props ? props.rightDrawerWidth : 0)}px);
+  width: calc(100% - ${(props) => (props ? props.$rightDrawerWidth : 0)}px);
   left: 0;
-  margin-left: leftDrawerWidth;
-  margin-right: rightDrawerWidth;
+  margin-left: $leftDrawerWidth;
+  margin-right: $rightDrawerWidth;
 `
 
 // Necessary for content to be below app bar
@@ -90,13 +90,13 @@ export const ToolbarMargin = styled.div`
 `
 
 export const FillLeftContainer = styled.div<DrawerSizeProps>`
-  width: ${(props) => (props ? props.leftDrawerWidth : 0)}px;
+  width: ${(props) => (props ? props.$leftDrawerWidth : 0)}px;
   height: calc(100% - ${SlideListHeight}px);
   overflow: hidden;
 `
 
 export const FillRightContainer = styled.div<DrawerSizeProps>`
-  width: ${(props) => (props ? props.rightDrawerWidth : 0)}px;
+  width: ${(props) => (props ? props.$rightDrawerWidth : 0)}px;
   height: 100%;
   overflow-y: auto;
   background: #e9e9e9;
diff --git a/client/src/pages/views/AudienceViewPage.test.tsx b/client/src/pages/views/AudienceViewPage.test.tsx
index d00d4277e8b3a5021088b02a13947fb41f28b9f1..be17ab7428591823718ce2a5451ab594c517c54d 100644
--- a/client/src/pages/views/AudienceViewPage.test.tsx
+++ b/client/src/pages/views/AudienceViewPage.test.tsx
@@ -1,13 +1,23 @@
 import { render } from '@testing-library/react'
+import mockedAxios from 'axios'
 import React from 'react'
 import { Provider } from 'react-redux'
+import { BrowserRouter } from 'react-router-dom'
 import store from '../../store'
 import AudienceViewPage from './AudienceViewPage'
 
 it('renders audience view page', () => {
+  const typeRes: any = {
+    data: { id: 5, slides: [{ id: 2 }] },
+  }
+  ;(mockedAxios.get as jest.Mock).mockImplementation(() => {
+    return Promise.resolve(typeRes)
+  })
   render(
-    <Provider store={store}>
-      <AudienceViewPage />
-    </Provider>
+    <BrowserRouter>
+      <Provider store={store}>
+        <AudienceViewPage />
+      </Provider>
+    </BrowserRouter>
   )
 })
diff --git a/client/src/pages/views/AudienceViewPage.tsx b/client/src/pages/views/AudienceViewPage.tsx
index d03f3367499b9820ad35d646feedda3821928dcc..48b92c4686c762536c629e1c33f5bb33ade9cc59 100644
--- a/client/src/pages/views/AudienceViewPage.tsx
+++ b/client/src/pages/views/AudienceViewPage.tsx
@@ -1,13 +1,34 @@
 import { Typography } from '@material-ui/core'
-import React from 'react'
-import { useAppSelector } from '../../hooks'
+import React, { useEffect } from 'react'
+import { useParams } from 'react-router-dom'
+import { getPresentationCompetition } from '../../actions/presentation'
+import { useAppDispatch, useAppSelector } from '../../hooks'
+import { ViewParams } from '../../interfaces/ViewParams'
+import { socketConnect, socketJoinPresentation } from '../../sockets'
 import SlideDisplay from '../presentationEditor/components/SlideDisplay'
+import { PresentationBackground, PresentationContainer } from './styled'
 
 const AudienceViewPage: React.FC = () => {
+  const { competitionId }: ViewParams = useParams()
+  const code = useAppSelector((state) => state.presentation.code)
+  const dispatch = useAppDispatch()
   const viewTypes = useAppSelector((state) => state.types.viewTypes)
   const activeViewTypeId = viewTypes.find((viewType) => viewType.name === 'Audience')?.id
+  useEffect(() => {
+    dispatch(getPresentationCompetition(competitionId))
+    if (code && code !== '') {
+      socketConnect()
+      socketJoinPresentation()
+    }
+  }, [])
   if (activeViewTypeId) {
-    return <SlideDisplay variant="presentation" activeViewTypeId={activeViewTypeId} />
+    return (
+      <PresentationBackground>
+        <PresentationContainer>
+          <SlideDisplay variant="presentation" activeViewTypeId={activeViewTypeId} />
+        </PresentationContainer>
+      </PresentationBackground>
+    )
   }
   return <Typography>Error: Åskådarvyn kunde inte laddas</Typography>
 }
diff --git a/client/src/pages/views/JudgeViewPage.tsx b/client/src/pages/views/JudgeViewPage.tsx
index 677a080d04ab34e8232120daae018b6d1a2b29c4..81f70edbc86c72409f2df6ac178d35ac10b8bc72 100644
--- a/client/src/pages/views/JudgeViewPage.tsx
+++ b/client/src/pages/views/JudgeViewPage.tsx
@@ -1,14 +1,16 @@
-import { Card, Divider, List, ListItem, ListItemText, Paper, Typography } from '@material-ui/core'
+import { Divider, List, ListItemText, Typography } from '@material-ui/core'
 import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'
 import React, { useEffect, useState } from 'react'
-import { getPresentationCompetition, setCurrentSlide, setPresentationCode } from '../../actions/presentation'
+import { useHistory, useParams } from 'react-router-dom'
+import { getPresentationCompetition, setCurrentSlide } from '../../actions/presentation'
 import { useAppDispatch, useAppSelector } from '../../hooks'
 import { ViewParams } from '../../interfaces/ViewParams'
-import { socket_connect } from '../../sockets'
+import { socketConnect } from '../../sockets'
+import { renderSlideIcon } from '../../utils/renderSlideIcon'
+import SlideDisplay from '../presentationEditor/components/SlideDisplay'
 import { SlideListItem } from '../presentationEditor/styled'
 import JudgeScoreDisplay from './components/JudgeScoreDisplay'
-import PresentationComponent from './components/PresentationComponent'
-import { useHistory } from 'react-router-dom'
+import JudgeScoringInstructions from './components/JudgeScoringInstructions'
 import {
   Content,
   InnerContent,
@@ -18,13 +20,10 @@ import {
   JudgeToolbar,
   LeftDrawer,
   RightDrawer,
+  ScoreFooterPadding,
   ScoreHeaderPadding,
   ScoreHeaderPaper,
-  ScoreFooterPadding,
 } from './styled'
-import SlideDisplay from '../presentationEditor/components/SlideDisplay'
-import JudgeScoringInstructions from './components/JudgeScoringInstructions'
-import { renderSlideIcon } from '../../utils/renderSlideIcon'
 
 const leftDrawerWidth = 150
 const rightDrawerWidth = 700
@@ -40,32 +39,25 @@ const useStyles = makeStyles((theme: Theme) =>
     toolbar: theme.mixins.toolbar,
   })
 )
-type JudgeViewPageProps = {
-  //Prop to distinguish between editor and active competition
-  competitionId: number
-  code: string
-}
 
-const JudgeViewPage = ({ competitionId, code }: JudgeViewPageProps) => {
+const JudgeViewPage: React.FC = () => {
   const classes = useStyles()
   const history = useHistory()
   const dispatch = useAppDispatch()
   const [activeSlideIndex, setActiveSlideIndex] = useState<number>(0)
   const viewTypes = useAppSelector((state) => state.types.viewTypes)
-  const activeViewTypeId = viewTypes.find((viewType) => viewType.name === 'Judge')?.id
+  const activeViewTypeId = viewTypes.find((viewType) => viewType.name === 'Team')?.id
   const teams = useAppSelector((state) => state.presentation.competition.teams)
   const slides = useAppSelector((state) => state.presentation.competition.slides)
   const currentQuestion = slides[activeSlideIndex]?.questions[0]
+  const { competitionId }: ViewParams = useParams()
   const handleSelectSlide = (index: number) => {
     setActiveSlideIndex(index)
     dispatch(setCurrentSlide(slides[index]))
   }
   useEffect(() => {
-    socket_connect()
-    dispatch(getPresentationCompetition(competitionId.toString()))
-    dispatch(setPresentationCode(code))
-    //hides the url so people can't sneak peak
-    history.push('judge')
+    socketConnect()
+    dispatch(getPresentationCompetition(competitionId))
   }, [])
 
   return (
diff --git a/client/src/pages/views/OperatorViewPage.test.tsx b/client/src/pages/views/OperatorViewPage.test.tsx
index e658fe3b386899b9ab2972243c5d4ceeefc6dec4..3259fcfcf07f7f54a725e01b2ea6fa8b69a89b21 100644
--- a/client/src/pages/views/OperatorViewPage.test.tsx
+++ b/client/src/pages/views/OperatorViewPage.test.tsx
@@ -1,43 +1,46 @@
 import { render } from '@testing-library/react'
 import mockedAxios from 'axios'
 import React from 'react'
+import { act } from 'react-dom/test-utils'
 import { Provider } from 'react-redux'
 import { BrowserRouter } from 'react-router-dom'
 import store from '../../store'
 import OperatorViewPage from './OperatorViewPage'
 
-it('renders presenter view page', () => {
-  const compRes: any = {
-    data: {
-      slides: [{ id: 0, title: '' }],
-    },
-  }
-  const teamsRes: any = {
-    data: {
-      items: [
-        {
-          id: 1,
-          name: 'team1',
-        },
-        {
-          id: 2,
-          name: 'team2',
-        },
-      ],
-      count: 2,
-      total_count: 3,
-    },
-  }
+it('renders operator view page', async () => {
+  await act(async () => {
+    const compRes: any = {
+      data: {
+        slides: [{ id: 0, title: '' }],
+      },
+    }
+    const teamsRes: any = {
+      data: {
+        items: [
+          {
+            id: 1,
+            name: 'team1',
+          },
+          {
+            id: 2,
+            name: 'team2',
+          },
+        ],
+        count: 2,
+        total_count: 3,
+      },
+    }
 
-  ;(mockedAxios.get as jest.Mock).mockImplementation((path: string, params?: any) => {
-    if (path.endsWith('/teams')) return Promise.resolve(teamsRes)
-    else return Promise.resolve(compRes)
+    ;(mockedAxios.get as jest.Mock).mockImplementation((path: string, params?: any) => {
+      if (path.endsWith('/teams')) return Promise.resolve(teamsRes)
+      else return Promise.resolve(compRes)
+    })
+    render(
+      <BrowserRouter>
+        <Provider store={store}>
+          <OperatorViewPage />
+        </Provider>
+      </BrowserRouter>
+    )
   })
-  render(
-    <BrowserRouter>
-      <Provider store={store}>
-        <OperatorViewPage />
-      </Provider>
-    </BrowserRouter>
-  )
 })
diff --git a/client/src/pages/views/OperatorViewPage.tsx b/client/src/pages/views/OperatorViewPage.tsx
index 546323e1e5e69b93c1633da7f4426ad016f896f7..e92dc1b9b470977c563b8bc6b955af7da984b6ea 100644
--- a/client/src/pages/views/OperatorViewPage.tsx
+++ b/client/src/pages/views/OperatorViewPage.tsx
@@ -15,45 +15,42 @@ import {
   Theme,
   Tooltip,
   Typography,
-  useMediaQuery,
-  useTheme,
 } from '@material-ui/core'
 import AssignmentIcon from '@material-ui/icons/Assignment'
-import FileCopyIcon from '@material-ui/icons/FileCopy'
-import SupervisorAccountIcon from '@material-ui/icons/SupervisorAccount'
 import BackspaceIcon from '@material-ui/icons/Backspace'
 import ChevronLeftIcon from '@material-ui/icons/ChevronLeft'
 import ChevronRightIcon from '@material-ui/icons/ChevronRight'
+import FileCopyIcon from '@material-ui/icons/FileCopy'
+import SupervisorAccountIcon from '@material-ui/icons/SupervisorAccount'
 import TimerIcon from '@material-ui/icons/Timer'
-import React, { useEffect, useState } from 'react'
+import axios from 'axios'
+import React, { useEffect } from 'react'
 import { useHistory, useParams } from 'react-router-dom'
-import { getPresentationCompetition, setPresentationCode } from '../../actions/presentation'
+import { getPresentationCompetition } from '../../actions/presentation'
 import { useAppDispatch, useAppSelector } from '../../hooks'
+import { Team } from '../../interfaces/ApiModels'
 import { ViewParams } from '../../interfaces/ViewParams'
 import {
+  socketConnect,
   socketEndPresentation,
   socketSetSlide,
   socketSetSlideNext,
   socketSetSlidePrev,
   socketStartPresentation,
   socketStartTimer,
-  socket_connect,
 } from '../../sockets'
 import SlideDisplay from '../presentationEditor/components/SlideDisplay'
-import PresentationComponent from './components/PresentationComponent'
 import Timer from './components/Timer'
 import {
   OperatorButton,
   OperatorContainer,
+  OperatorContent,
   OperatorFooter,
   OperatorHeader,
-  OperatorContent,
   OperatorInnerContent,
   SlideCounter,
   ToolBarContainer,
 } from './styled'
-import axios from 'axios'
-import { Team } from '../../interfaces/ApiModels'
 
 /**
  *  Description:
@@ -111,18 +108,17 @@ const OperatorViewPage: React.FC = () => {
   const classes = useStyles()
   //const teams = useAppSelector((state) => state.presentation.competition.teams)
   const [anchorEl, setAnchorEl] = React.useState<HTMLButtonElement | null>(null)
-  const { id, code }: ViewParams = useParams()
+  const { competitionId }: ViewParams = useParams()
   const presentation = useAppSelector((state) => state.presentation)
   const activeId = useAppSelector((state) => state.presentation.competition.id)
   const history = useHistory()
   const dispatch = useAppDispatch()
   const viewTypes = useAppSelector((state) => state.types.viewTypes)
-  const activeViewTypeId = viewTypes.find((viewType) => viewType.name === 'Operator')?.id
+  const activeViewTypeId = viewTypes.find((viewType) => viewType.name === 'Audience')?.id
 
   useEffect(() => {
-    dispatch(getPresentationCompetition(id))
-    dispatch(setPresentationCode(code))
-    socket_connect()
+    dispatch(getPresentationCompetition(competitionId))
+    socketConnect()
     socketSetSlide // Behövs denna?
     handleOpenCodes()
     setTimeout(startCompetition, 1000) // Ghetto, wait for everything to load
@@ -148,7 +144,7 @@ const OperatorViewPage: React.FC = () => {
   const startCompetition = () => {
     socketStartPresentation()
     console.log('started competition for')
-    console.log(id)
+    console.log(competitionId)
   }
 
   const handleVerifyExit = () => {
@@ -177,7 +173,6 @@ const OperatorViewPage: React.FC = () => {
     await axios
       .get(`/api/competitions/${activeId}/codes`)
       .then((response) => {
-        console.log(response.data)
         setCodes(response.data.items)
       })
       .catch(console.log)
@@ -187,7 +182,6 @@ const OperatorViewPage: React.FC = () => {
     await axios
       .get(`/api/competitions/${activeId}/teams`)
       .then((response) => {
-        console.log(response.data.items)
         setTeams(response.data.items)
       })
       .catch((err) => {
@@ -199,7 +193,6 @@ const OperatorViewPage: React.FC = () => {
     await axios
       .get(`/api/competitions/${activeId}`)
       .then((response) => {
-        console.log(response.data.name)
         setCompetitionName(response.data.name)
       })
       .catch((err) => {
@@ -249,28 +242,29 @@ const OperatorViewPage: React.FC = () => {
         </DialogTitle>
         <DialogContent>
           {/* <DialogContentText>Här visas tävlingskoderna till den valda tävlingen.</DialogContentText> */}
-          {codes.map((code) => (
-            <ListItem key={code.id} style={{ display: 'flex' }}>
-              <ListItemText primary={`${getTypeName(code)}: `} />
-              <Typography component="div">
-                <ListItemText style={{ textAlign: 'right', marginLeft: '10px' }}>
-                  <Box fontFamily="Monospace" fontWeight="fontWeightBold">
-                    {code.code}
-                  </Box>
-                </ListItemText>
-              </Typography>
-              <Tooltip title="Kopiera kod" arrow>
-                <Button
-                  margin-right="0px"
-                  onClick={() => {
-                    navigator.clipboard.writeText(code.code)
-                  }}
-                >
-                  <FileCopyIcon fontSize="small" />
-                </Button>
-              </Tooltip>
-            </ListItem>
-          ))}
+          {codes &&
+            codes.map((code) => (
+              <ListItem key={code.id} style={{ display: 'flex' }}>
+                <ListItemText primary={`${getTypeName(code)}: `} />
+                <Typography component="div">
+                  <ListItemText style={{ textAlign: 'right', marginLeft: '10px' }}>
+                    <Box fontFamily="Monospace" fontWeight="fontWeightBold">
+                      {code.code}
+                    </Box>
+                  </ListItemText>
+                </Typography>
+                <Tooltip title="Kopiera kod" arrow>
+                  <Button
+                    margin-right="0px"
+                    onClick={() => {
+                      navigator.clipboard.writeText(code.code)
+                    }}
+                  >
+                    <FileCopyIcon fontSize="small" />
+                  </Button>
+                </Tooltip>
+              </ListItem>
+            ))}
         </DialogContent>
         <DialogActions>
           <Button onClick={handleClose} color="primary">
diff --git a/client/src/pages/views/TeamViewPage.test.tsx b/client/src/pages/views/TeamViewPage.test.tsx
index 10574f7e51df7dabf9f07754e9d8595d1c489559..33f7014df5d9974f141dd65273af3050062766ed 100644
--- a/client/src/pages/views/TeamViewPage.test.tsx
+++ b/client/src/pages/views/TeamViewPage.test.tsx
@@ -1,14 +1,14 @@
 import { render } from '@testing-library/react'
+import mockedAxios from 'axios'
 import React from 'react'
 import { Provider } from 'react-redux'
 import { BrowserRouter } from 'react-router-dom'
 import store from '../../store'
 import TeamViewPage from './TeamViewPage'
-import mockedAxios from 'axios'
 
 it('renders participant view page', () => {
   const res = {
-    data: {},
+    data: { slides: [{ id: 5 }] },
   }
   ;(mockedAxios.get as jest.Mock).mockImplementation(() => {
     return Promise.resolve(res)
diff --git a/client/src/pages/views/TeamViewPage.tsx b/client/src/pages/views/TeamViewPage.tsx
index 32eef28b9f7acd350195571719ea0449aeb19610..13791b3acd7b1ee5688ba3314aa4daa6e04f7272 100644
--- a/client/src/pages/views/TeamViewPage.tsx
+++ b/client/src/pages/views/TeamViewPage.tsx
@@ -1,28 +1,32 @@
 import React, { useEffect } from 'react'
-import PresentationComponent from './components/PresentationComponent'
-import { useHistory } from 'react-router-dom'
+import { useHistory, useParams } from 'react-router-dom'
+import { getPresentationCompetition } from '../../actions/presentation'
+import { useAppDispatch, useAppSelector } from '../../hooks'
+import { ViewParams } from '../../interfaces/ViewParams'
+import { socketConnect, socketJoinPresentation } from '../../sockets'
 import SlideDisplay from '../presentationEditor/components/SlideDisplay'
-import { TeamContainer } from './styled'
-import { socketJoinPresentation, socket_connect } from '../../sockets'
-import { useAppSelector } from '../../hooks'
+import { PresentationBackground, PresentationContainer } from './styled'
 
 const TeamViewPage: React.FC = () => {
   const history = useHistory()
   const code = useAppSelector((state) => state.presentation.code)
   const viewTypes = useAppSelector((state) => state.types.viewTypes)
   const activeViewTypeId = viewTypes.find((viewType) => viewType.name === 'Team')?.id
+  const { competitionId }: ViewParams = useParams()
+  const dispatch = useAppDispatch()
   useEffect(() => {
-    //hides the url so people can't sneak peak
-    history.push('team')
+    dispatch(getPresentationCompetition(competitionId))
     if (code && code !== '') {
-      socket_connect()
+      socketConnect()
       socketJoinPresentation()
     }
   }, [])
   return (
-    <TeamContainer>
-      {activeViewTypeId && <SlideDisplay variant="presentation" activeViewTypeId={activeViewTypeId} />}
-    </TeamContainer>
+    <PresentationBackground>
+      <PresentationContainer>
+        {activeViewTypeId && <SlideDisplay variant="presentation" activeViewTypeId={activeViewTypeId} />}
+      </PresentationContainer>
+    </PresentationBackground>
   )
 }
 
diff --git a/client/src/pages/views/ViewSelectPage.tsx b/client/src/pages/views/ViewSelectPage.tsx
index 845ba21c3edc1e5f499f6aae817e195af4d9bc21..d69dc8b4243d680647802fe9d6f72acbd23618ba 100644
--- a/client/src/pages/views/ViewSelectPage.tsx
+++ b/client/src/pages/views/ViewSelectPage.tsx
@@ -1,69 +1,57 @@
-import Button from '@material-ui/core/Button'
-import React, { useEffect, useState } from 'react'
-import { Link, useRouteMatch } from 'react-router-dom'
-import { ViewSelectButtonGroup, ViewSelectContainer } from './styled'
-import { useParams } from 'react-router-dom'
 import { CircularProgress, Typography } from '@material-ui/core'
-import TeamViewPage from './TeamViewPage'
-import axios from 'axios'
-import OperatorViewPage from './OperatorViewPage'
-import JudgeViewPage from './JudgeViewPage'
-import AudienceViewPage from './AudienceViewPage'
+import React, { useEffect } from 'react'
+import { Redirect, useHistory, useParams } from 'react-router-dom'
+import { loginCompetition } from '../../actions/competitionLogin'
 import { useAppDispatch, useAppSelector } from '../../hooks'
-import { getPresentationCompetition, setPresentationCode } from '../../actions/presentation'
+import { ViewSelectButtonGroup, ViewSelectContainer } from './styled'
 
 interface ViewSelectParams {
   code: string
 }
 const ViewSelectPage: React.FC = () => {
   const dispatch = useAppDispatch()
-  const [loading, setLoading] = useState(true)
-  const [error, setError] = useState(false)
-  const [viewTypeId, setViewTypeId] = useState(undefined)
-  const [competitionId, setCompetitionId] = useState<number | undefined>(undefined)
+  const history = useHistory()
+  const competitionId = useAppSelector((state) => state.competitionLogin.data?.competition_id)
+  const errorMessage = useAppSelector((state) => state.competitionLogin.errors?.message)
+  const loading = useAppSelector((state) => state.competitionLogin.loading)
   const { code }: ViewSelectParams = useParams()
-  const viewType = useAppSelector((state) => state.types.viewTypes.find((viewType) => viewType.id === viewTypeId)?.name)
+  const viewType = useAppSelector((state) => state.competitionLogin.data?.view)
 
-  const renderView = (viewTypeId: number | undefined) => {
+  const renderView = () => {
     //Renders the correct view depending on view type
     if (competitionId) {
       switch (viewType) {
         case 'Team':
-          return <TeamViewPage />
+          return <Redirect to={`/team/competition-id=${competitionId}`} />
         case 'Judge':
-          return <JudgeViewPage code={code} competitionId={competitionId} />
+          return <Redirect to={`/judge/competition-id=${competitionId}`} />
         case 'Audience':
-          return <AudienceViewPage />
+          return <Redirect to={`/audience/competition-id=${competitionId}`} />
+        case 'Operator':
+          return <Redirect to={`/operator/competition-id=${competitionId}`} />
         default:
-          return <Typography>Inkorrekt vy</Typography>
+          return (
+            <ViewSelectContainer>
+              <ViewSelectButtonGroup>
+                <Typography variant="h4">Inkorrekt vy</Typography>
+              </ViewSelectButtonGroup>
+            </ViewSelectContainer>
+          )
       }
     }
   }
-
   useEffect(() => {
-    axios
-      .post('/api/auth/login/code', { code })
-      .then((response) => {
-        setLoading(false)
-        setViewTypeId(response.data.view_type_id)
-        setCompetitionId(response.data.competition_id)
-        dispatch(getPresentationCompetition(response.data.competition_id))
-        dispatch(setPresentationCode(code))
-      })
-      .catch(() => {
-        setLoading(false)
-        setError(true)
-      })
+    dispatch(loginCompetition(code, history, false))
   }, [])
 
   return (
     <>
-      {!loading && renderView(viewTypeId)}
-      {(loading || error) && (
+      {renderView()}
+      {(loading || errorMessage) && (
         <ViewSelectContainer>
           <ViewSelectButtonGroup>
             {loading && <CircularProgress />}
-            {error && <Typography>Något gick fel, dubbelkolla koden och försök igen</Typography>}
+            {errorMessage && <Typography variant="h4">{errorMessage}</Typography>}
           </ViewSelectButtonGroup>
         </ViewSelectContainer>
       )}
diff --git a/client/src/pages/views/components/SocketTest.tsx b/client/src/pages/views/components/SocketTest.tsx
index d99f5b2aa740d9a2690b3aadeae6e1050b92d089..01a0a6f29b51d9a194cb397fdc73c720202e0c1c 100644
--- a/client/src/pages/views/components/SocketTest.tsx
+++ b/client/src/pages/views/components/SocketTest.tsx
@@ -2,13 +2,13 @@ import React, { useEffect } from 'react'
 import { connect } from 'react-redux'
 import { useAppDispatch } from '../../../hooks'
 import {
+  socketConnect,
   socketEndPresentation,
   socketJoinPresentation,
   socketSetSlideNext,
   socketSetSlidePrev,
   socketStartPresentation,
   socketStartTimer,
-  socket_connect,
 } from '../../../sockets'
 
 const mapStateToProps = (state: any) => {
@@ -27,7 +27,7 @@ const SocketTest: React.FC = (props: any) => {
   const dispatch = useAppDispatch()
 
   useEffect(() => {
-    socket_connect()
+    socketConnect()
     // dispatch(getPresentationCompetition('1')) // TODO: Use ID of item_code gotten from auth/login/<code> api call
     // dispatch(getPresentationTeams('1')) // TODO: Use ID of item_code gotten from auth/login/<code> api call
   }, [])
diff --git a/client/src/pages/views/styled.tsx b/client/src/pages/views/styled.tsx
index 9699902727b2352fec07b8781a9d8c43ad20663e..4b01d63ab8df48957ab88619424a089f96f36ebd 100644
--- a/client/src/pages/views/styled.tsx
+++ b/client/src/pages/views/styled.tsx
@@ -1,4 +1,4 @@
-import { AppBar, Button, Card, Drawer, Paper, Toolbar, Typography } from '@material-ui/core'
+import { AppBar, Button, Card, Drawer, Toolbar, Typography } from '@material-ui/core'
 import styled from 'styled-components'
 
 export const JudgeAppBar = styled(AppBar)`
@@ -143,10 +143,20 @@ export const OperatorInnerContent = styled.div`
   max-width: calc(((100vh - 260px) / 9) * 16);
 `
 
-export const TeamContainer = styled.div`
+export const PresentationContainer = styled.div`
+  height: 100%;
+  width: 100%;
   max-width: calc((100vh / 9) * 16);
 `
 
+export const PresentationBackground = styled.div`
+  height: 100%;
+  width: 100%;
+  background-color: rgba(0, 0, 0, 0.08);
+  display: flex;
+  justify-content: center;
+`
+
 interface ScoreHeaderPaperProps {
   $rightDrawerWidth: number
 }
diff --git a/client/src/reducers/competitionLoginReducer.ts b/client/src/reducers/competitionLoginReducer.ts
index 81c426a4a297f138fdfa6350e17a3cd4fc72d3fd..6d425f97209c810cd1646c3f9722dd9ec5666ff8 100644
--- a/client/src/reducers/competitionLoginReducer.ts
+++ b/client/src/reducers/competitionLoginReducer.ts
@@ -1,29 +1,57 @@
 import { AnyAction } from 'redux'
 import Types from '../actions/types'
 
+interface CompetitionLoginData {
+  competition_id: number
+  team_id: number | null
+  view: string
+}
+
 interface UIError {
   message: string
 }
 
-interface UserState {
+interface CompetitionLoginState {
   loading: boolean
   errors: null | UIError
+  authenticated: boolean
+  data: CompetitionLoginData | null
+  initialized: boolean
 }
 
-const initialState: UserState = {
+const initialState: CompetitionLoginState = {
   loading: false,
   errors: null,
+  authenticated: false,
+  data: null,
+  initialized: false,
 }
 
 export default function (state = initialState, action: AnyAction) {
   switch (action.type) {
+    case Types.SET_COMPETITION_LOGIN_DATA:
+      return {
+        ...state,
+        data: action.payload as CompetitionLoginData,
+        authenticated: true,
+        initialized: true,
+      }
+
+    case Types.SET_COMPETITION_LOGIN_AUTHENTICATED:
+      return {
+        ...state,
+        authenticated: true,
+        initialized: true,
+      }
     case Types.SET_COMPETITION_LOGIN_ERRORS:
       return {
+        ...state,
         errors: action.payload as UIError,
         loading: false,
       }
     case Types.CLEAR_COMPETITION_LOGIN_ERRORS:
       return {
+        ...state,
         loading: false,
         errors: null,
       }
diff --git a/client/src/sockets.ts b/client/src/sockets.ts
index 4392021df3c89d3d981096e38e1576d155c89b21..97e0b0e318dacfaab24c37e9f1ef806b7a4c0b18 100644
--- a/client/src/sockets.ts
+++ b/client/src/sockets.ts
@@ -18,9 +18,18 @@ interface SetTimerInterface {
 
 let socket: SocketIOClient.Socket
 
-export const socket_connect = () => {
+export const socketConnect = () => {
   if (!socket) {
-    socket = io('localhost:5000')
+    const token = localStorage.competitionToken
+    socket = io('localhost:5000', {
+      transportOptions: {
+        polling: {
+          extraHeaders: {
+            Authorization: token,
+          },
+        },
+      },
+    })
 
     socket.on('set_slide', (data: SetSlideInterface) => {
       setCurrentSlideByOrder(data.slide_order)(store.dispatch)
diff --git a/client/src/utils/SecureRoute.tsx b/client/src/utils/SecureRoute.tsx
index c8c238dffc4015a887fef707cf40b3eba5d6c6e5..e2885501748c468eb898e833481ae219516c5946 100644
--- a/client/src/utils/SecureRoute.tsx
+++ b/client/src/utils/SecureRoute.tsx
@@ -1,30 +1,45 @@
-import React, { useEffect } from 'react'
+import React from 'react'
 import { Redirect, Route, RouteProps } from 'react-router-dom'
 import { useAppSelector } from '../hooks'
-import { CheckAuthentication } from './checkAuthentication'
+import { CheckAuthenticationAdmin } from './checkAuthenticationAdmin'
+import { CheckAuthenticationCompetition } from './checkAuthenticationCompetition'
 
 interface SecureRouteProps extends RouteProps {
-  login?: boolean
   component: React.ComponentType<any>
   rest?: any
+  authLevel: 'competition' | 'admin' | 'login'
 }
+
 /** Utility component to use for authentication, replace all routes that should be private with secure routes*/
-const SecureRoute: React.FC<SecureRouteProps> = ({ login, component: Component, ...rest }: SecureRouteProps) => {
-  const authenticated = useAppSelector((state) => state.user.authenticated)
+const SecureRoute: React.FC<SecureRouteProps> = ({ component: Component, authLevel, ...rest }: SecureRouteProps) => {
+  const userAuthenticated = useAppSelector((state) => state.user.authenticated)
+  const compAuthenticated = useAppSelector((state) => state.competitionLogin.authenticated)
   const [initialized, setInitialized] = React.useState(false)
-  useEffect(() => {
-    const waitForAuthentication = async () => {
-      await CheckAuthentication()
-      setInitialized(true)
+  const compInitialized = useAppSelector((state) => state.competitionLogin.initialized)
+  React.useEffect(() => {
+    if (authLevel === 'admin' || authLevel === 'login') {
+      CheckAuthenticationAdmin().then(() => setInitialized(true))
+    } else {
+      CheckAuthenticationCompetition().then(() => setInitialized(true))
     }
-    waitForAuthentication()
   }, [])
+
   if (initialized) {
-    if (login)
+    if (authLevel === 'login')
+      return (
+        <Route
+          {...rest}
+          render={(props) => (userAuthenticated ? <Redirect to="/admin" /> : <Component {...props} />)}
+        />
+      )
+    else if (authLevel === 'competition' && compInitialized)
+      return (
+        <Route {...rest} render={(props) => (compAuthenticated ? <Component {...props} /> : <Redirect to="/" />)} />
+      )
+    else
       return (
-        <Route {...rest} render={(props) => (authenticated ? <Redirect to="/admin" /> : <Component {...props} />)} />
+        <Route {...rest} render={(props) => (userAuthenticated ? <Component {...props} /> : <Redirect to="/" />)} />
       )
-    else return <Route {...rest} render={(props) => (authenticated ? <Component {...props} /> : <Redirect to="/" />)} />
   } else return null
 }
 export default SecureRoute
diff --git a/client/src/utils/checkAuthentication.test.ts b/client/src/utils/checkAuthenticationAdmin.test.ts
similarity index 97%
rename from client/src/utils/checkAuthentication.test.ts
rename to client/src/utils/checkAuthenticationAdmin.test.ts
index 6d12e1fc77cab5e2575af85e0b91c22ae29ad731..b2f033859c408c2baf5544f7bf88b2f5942b943e 100644
--- a/client/src/utils/checkAuthentication.test.ts
+++ b/client/src/utils/checkAuthenticationAdmin.test.ts
@@ -1,7 +1,7 @@
 import mockedAxios from 'axios'
 import Types from '../actions/types'
 import store from '../store'
-import { CheckAuthentication } from './checkAuthentication'
+import { CheckAuthenticationAdmin } from './checkAuthenticationAdmin'
 
 it('dispatches correct actions when auth token is ok', async () => {
   const userRes: any = {
@@ -20,7 +20,7 @@ it('dispatches correct actions when auth token is ok', async () => {
   const testToken =
     'Bearer eyJ0eXAiOiJeyJ0eXAiOiJKV1QeyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2MTgzMDQ5MzksImV4cCI6MzI1MTI1MjU3NTcsImF1ZCI6Ind3dy5leGFtcGxlLmNvbSIsInN1YiI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJHaXZlbk5hbWUiOiJKb2hubnkiLCJTdXJuYW1lIjoiUm9ja2V0IiwiRW1haWwiOiJqcm9ja2V0QGV4YW1wbGUuY29tIiwiUm9sZSI6WyJNYW5hZ2VyIiwiUHJvamVjdCBBZG1pbmlzdHJhdG9yIl19.DrOOcCo5jMR-SNERE0uRp_kolQ2HjX8-hHXYEMnIxSceyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2MTgzMDQ5MzksImV4cCI6MzI1MTI1MjU3NTcsImF1ZCI6Ind3dy5leGFtcGxlLmNvbSIsInN1YiI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJHaXZlbk5hbWUiOiJKb2hubnkiLCJTdXJuYW1lIjoiUm9ja2V0IiwiRW1haWwiOiJqcm9ja2V0QGV4YW1wbGUuY29tIiwiUm9sZSI6WyJNYW5hZ2VyIiwiUHJvamVjdCBBZG1pbmlzdHJhdG9yIl19.DrOOcCo5jMR-SNERE0uRp_kolQ2HjX8-hHXYEMnIxSceyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2MTgzMDQ5MzksImV4cCI6MzI1MTI1MjU3NTcsImF1ZCI6Ind3dy5leGFtcGxlLmNvbSIsInN1YiI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJHaXZlbk5hbWUiOiJKb2hubnkiLCJTdXJuYW1lIjoiUm9ja2V0IiwiRW1haWwiOiJqcm9ja2V0QGV4YW1wbGUuY29tIiwiUm9sZSI6WyJNYW5hZ2VyIiwiUHJvamVjdCBBZG1pbmlzdHJhdG9yIl19.DrOOcCo5jMR-SNERE0uRp_kolQ2HjX8-hHXYEMnIxSceyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2MTgzMDQ5MzksImV4cCI6MzI1MTI1MjU3NTcsImF1ZCI6Ind3dy5leGFtcGxlLmNvbSIsInN1YiI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJHaXZlbk5hbWUiOiJKb2hubnkiLCJTdXJuYW1lIjoiUm9ja2V0IiwiRW1haWwiOiJqcm9ja2V0QGV4YW1wbGUuY29tIiwiUm9sZSI6WyJNYW5hZ2VyIiwiUHJvamVjdCBBZG1pbmlzdHJhdG9yIl19.DrOOcCo5jMR-SNERE0uRp_kolQ2HjX8-hHXYEMnIxSciLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2MTgzMDQ5MzksImV4cCI6MzI1MTI1MjU3NTcsImF1ZCI6Ind3dy5leGFtcGxlLmNvbSIsInN1YiI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJHaXZlbk5hbWUiOiJKb2hubnkiLCJTdXJuYW1lIjoiUm9ja2V0IiwiRW1haWwiOiJqcm9ja2V0QGV4YW1wbGUuY29tIiwiUm9sZSI6WyJNYW5hZ2VyIiwiUHJvamVjdCBBZG1pbmlzdHJhdG9yIl19.DrOOcCo5jMR-SNERE0uRp_kolQ2HjX8-hHXYEMnIxScKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2MTgzMDQ5MzksImV4cCI6MzI1MTI1MjU3NTcsImF1ZCI6Ind3dy5leGFtcGxlLmNvbSIsInN1YiI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJHaXZlbk5hbWUiOiJKb2hubnkiLCJTdXJuYW1lIjoiUm9ja2V0IiwiRW1haWwiOiJqcm9ja2V0QGV4YW1wbGUuY29tIiwiUm9sZSI6WyJNYW5hZ2VyIiwiUHJvamVjdCBBZG1pbmlzdHJhdG9yIl19.DrOOcCo5jMR-SNERE0uRp_kolQ2HjX8-hHXYEMnIxSc'
   localStorage.setItem('token', testToken)
-  await CheckAuthentication()
+  await CheckAuthenticationAdmin()
   expect(spy).toBeCalledWith({ type: Types.LOADING_USER })
   expect(spy).toBeCalledWith({ type: Types.SET_AUTHENTICATED })
   expect(spy).toBeCalledWith({ type: Types.SET_USER, payload: userRes.data })
@@ -39,7 +39,7 @@ it('dispatches correct actions when getting user data fails', async () => {
   const testToken =
     'Bearer eyJ0eXAiOiJeyJ0eXAiOiJKV1QeyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2MTgzMDQ5MzksImV4cCI6MzI1MTI1MjU3NTcsImF1ZCI6Ind3dy5leGFtcGxlLmNvbSIsInN1YiI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJHaXZlbk5hbWUiOiJKb2hubnkiLCJTdXJuYW1lIjoiUm9ja2V0IiwiRW1haWwiOiJqcm9ja2V0QGV4YW1wbGUuY29tIiwiUm9sZSI6WyJNYW5hZ2VyIiwiUHJvamVjdCBBZG1pbmlzdHJhdG9yIl19.DrOOcCo5jMR-SNERE0uRp_kolQ2HjX8-hHXYEMnIxSceyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2MTgzMDQ5MzksImV4cCI6MzI1MTI1MjU3NTcsImF1ZCI6Ind3dy5leGFtcGxlLmNvbSIsInN1YiI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJHaXZlbk5hbWUiOiJKb2hubnkiLCJTdXJuYW1lIjoiUm9ja2V0IiwiRW1haWwiOiJqcm9ja2V0QGV4YW1wbGUuY29tIiwiUm9sZSI6WyJNYW5hZ2VyIiwiUHJvamVjdCBBZG1pbmlzdHJhdG9yIl19.DrOOcCo5jMR-SNERE0uRp_kolQ2HjX8-hHXYEMnIxSceyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2MTgzMDQ5MzksImV4cCI6MzI1MTI1MjU3NTcsImF1ZCI6Ind3dy5leGFtcGxlLmNvbSIsInN1YiI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJHaXZlbk5hbWUiOiJKb2hubnkiLCJTdXJuYW1lIjoiUm9ja2V0IiwiRW1haWwiOiJqcm9ja2V0QGV4YW1wbGUuY29tIiwiUm9sZSI6WyJNYW5hZ2VyIiwiUHJvamVjdCBBZG1pbmlzdHJhdG9yIl19.DrOOcCo5jMR-SNERE0uRp_kolQ2HjX8-hHXYEMnIxSceyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2MTgzMDQ5MzksImV4cCI6MzI1MTI1MjU3NTcsImF1ZCI6Ind3dy5leGFtcGxlLmNvbSIsInN1YiI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJHaXZlbk5hbWUiOiJKb2hubnkiLCJTdXJuYW1lIjoiUm9ja2V0IiwiRW1haWwiOiJqcm9ja2V0QGV4YW1wbGUuY29tIiwiUm9sZSI6WyJNYW5hZ2VyIiwiUHJvamVjdCBBZG1pbmlzdHJhdG9yIl19.DrOOcCo5jMR-SNERE0uRp_kolQ2HjX8-hHXYEMnIxSciLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2MTgzMDQ5MzksImV4cCI6MzI1MTI1MjU3NTcsImF1ZCI6Ind3dy5leGFtcGxlLmNvbSIsInN1YiI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJHaXZlbk5hbWUiOiJKb2hubnkiLCJTdXJuYW1lIjoiUm9ja2V0IiwiRW1haWwiOiJqcm9ja2V0QGV4YW1wbGUuY29tIiwiUm9sZSI6WyJNYW5hZ2VyIiwiUHJvamVjdCBBZG1pbmlzdHJhdG9yIl19.DrOOcCo5jMR-SNERE0uRp_kolQ2HjX8-hHXYEMnIxScKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2MTgzMDQ5MzksImV4cCI6MzI1MTI1MjU3NTcsImF1ZCI6Ind3dy5leGFtcGxlLmNvbSIsInN1YiI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJHaXZlbk5hbWUiOiJKb2hubnkiLCJTdXJuYW1lIjoiUm9ja2V0IiwiRW1haWwiOiJqcm9ja2V0QGV4YW1wbGUuY29tIiwiUm9sZSI6WyJNYW5hZ2VyIiwiUHJvamVjdCBBZG1pbmlzdHJhdG9yIl19.DrOOcCo5jMR-SNERE0uRp_kolQ2HjX8-hHXYEMnIxSc'
   localStorage.setItem('token', testToken)
-  await CheckAuthentication()
+  await CheckAuthenticationAdmin()
   expect(spy).toBeCalledWith({ type: Types.LOADING_USER })
   expect(spy).toBeCalledWith({ type: Types.SET_UNAUTHENTICATED })
   expect(spy).toBeCalledTimes(2)
@@ -51,7 +51,7 @@ it('dispatches no actions when no token exists', async () => {
     return Promise.resolve({ data: {} })
   })
   const spy = jest.spyOn(store, 'dispatch')
-  await CheckAuthentication()
+  await CheckAuthenticationAdmin()
   expect(spy).not.toBeCalled()
 })
 
@@ -63,7 +63,7 @@ it('dispatches correct actions when token is expired', async () => {
     'Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2MTgzMDY1MTUsImV4cCI6MTU4Njc3MDUxNSwiYXVkIjoid3d3LmV4YW1wbGUuY29tIiwic3ViIjoianJvY2tldEBleGFtcGxlLmNvbSIsIkdpdmVuTmFtZSI6IkpvaG5ueSIsIlN1cm5hbWUiOiJSb2NrZXQiLCJFbWFpbCI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJSb2xlIjpbIk1hbmFnZXIiLCJQcm9qZWN0IEFkbWluaXN0cmF0b3IiXX0.R5-oWGGumd-YWPoKyziJmVB8SdX6B9SsV6m7novIfgg'
   localStorage.setItem('token', testToken)
   const spy = jest.spyOn(store, 'dispatch')
-  await CheckAuthentication()
+  await CheckAuthenticationAdmin()
   expect(spy).toBeCalledWith({ type: Types.SET_UNAUTHENTICATED })
   expect(spy).toBeCalledTimes(1)
 })
diff --git a/client/src/utils/checkAuthentication.ts b/client/src/utils/checkAuthenticationAdmin.ts
similarity index 94%
rename from client/src/utils/checkAuthentication.ts
rename to client/src/utils/checkAuthenticationAdmin.ts
index 9225aa29858d2f9ea58f8ac118dd2feed01d1cc5..3565f8e3b93bb8ff83114e166618a22f823d335e 100644
--- a/client/src/utils/checkAuthentication.ts
+++ b/client/src/utils/checkAuthenticationAdmin.ts
@@ -8,7 +8,7 @@ const UnAuthorized = async () => {
   await logoutUser()(store.dispatch)
 }
 
-export const CheckAuthentication = async () => {
+export const CheckAuthenticationAdmin = async () => {
   const authToken = localStorage.token
   if (authToken) {
     const decodedToken: any = jwtDecode(authToken)
diff --git a/client/src/utils/checkAuthenticationCompetition.ts b/client/src/utils/checkAuthenticationCompetition.ts
new file mode 100644
index 0000000000000000000000000000000000000000..db0c32360a9928459dd4cac4e65bfb1715a8353f
--- /dev/null
+++ b/client/src/utils/checkAuthenticationCompetition.ts
@@ -0,0 +1,41 @@
+import axios from 'axios'
+import jwtDecode from 'jwt-decode'
+import { logoutCompetition } from '../actions/competitionLogin'
+import { setPresentationCode } from '../actions/presentation'
+import Types from '../actions/types'
+import store from '../store'
+
+const UnAuthorized = async () => {
+  await logoutCompetition()(store.dispatch)
+}
+
+export const CheckAuthenticationCompetition = async () => {
+  const authToken = localStorage.competitionToken
+  if (authToken) {
+    const decodedToken: any = jwtDecode(authToken)
+    if (decodedToken.exp * 1000 >= Date.now()) {
+      axios.defaults.headers.common['Authorization'] = authToken
+      console.log(decodedToken.user_claims)
+      await axios
+        .get('/api/auth/test')
+        .then((res) => {
+          store.dispatch({ type: Types.SET_COMPETITION_LOGIN_AUTHENTICATED })
+          store.dispatch({
+            type: Types.SET_COMPETITION_LOGIN_DATA,
+            payload: {
+              competition_id: decodedToken.user_claims.competition_id,
+              team_id: decodedToken.user_claims.team_id,
+              view: res.data.view,
+            },
+          })
+          setPresentationCode(decodedToken.user_claims.code)(store.dispatch)
+        })
+        .catch((error) => {
+          console.log(error)
+          UnAuthorized()
+        })
+    } else {
+      await UnAuthorized()
+    }
+  }
+}
diff --git a/server/app/apis/auth.py b/server/app/apis/auth.py
index c8c358078c6405e9f157f2e1b1697e4383258c0b..bf9eeefde781f3fcacfd4bdafade8829db24acaa 100644
--- a/server/app/apis/auth.py
+++ b/server/app/apis/auth.py
@@ -1,18 +1,15 @@
-from flask_jwt_extended.utils import get_jti
+from datetime import timedelta
+
 import app.core.http_codes as codes
 import app.database.controller as dbc
 from app.apis import item_response, protect_route, text_response
+from app.core import sockets
 from app.core.codes import verify_code
 from app.core.dto import AuthDTO
-from flask_jwt_extended import (
-    create_access_token,
-    get_raw_jwt,
-)
-from flask_restx import Resource
-from flask_restx import inputs, reqparse
-from datetime import timedelta
-from app.core import sockets
 from app.database.models import Whitelist
+from flask_jwt_extended import create_access_token, get_jti, get_raw_jwt
+from flask_jwt_extended.utils import get_jti
+from flask_restx import Resource, inputs, reqparse
 
 api = AuthDTO.api
 schema = AuthDTO.schema
@@ -35,7 +32,12 @@ def get_user_claims(item_user):
 
 
 def get_code_claims(item_code):
-    return {"view": item_code.view_type.name, "competition_id": item_code.competition_id, "team_id": item_code.team_id}
+    return {
+        "view": item_code.view_type.name,
+        "competition_id": item_code.competition_id,
+        "team_id": item_code.team_id,
+        "code": item_code.code,
+    }
 
 
 @api.route("/test")
@@ -101,8 +103,9 @@ class AuthLoginCode(Resource):
 
         item_code = dbc.get.code_by_code(code)
 
-        if item_code.competition_id not in sockets.presentations:
-            api.abort(codes.UNAUTHORIZED, "Competition not active")
+        if item_code.view_type_id != 4:
+            if item_code.competition_id not in sockets.presentations:
+                api.abort(codes.UNAUTHORIZED, "Competition not active")
 
         access_token = create_access_token(
             item_code.id, user_claims=get_code_claims(item_code), expires_delta=timedelta(hours=8)
@@ -111,7 +114,7 @@ class AuthLoginCode(Resource):
         dbc.add.whitelist(get_jti(access_token), competition_id=item_code.competition_id)
         response = {
             "competition_id": item_code.competition_id,
-            "view_type_id": item_code.view_type_id,
+            "view": item_code.view_type.name,
             "team_id": item_code.team_id,
             "access_token": access_token,
         }
diff --git a/server/app/apis/misc.py b/server/app/apis/misc.py
index 20a84e4c17c138b3a94ea6e3902e67154036cabc..a9069f6dd916af90b764b6703f83bb227fbcc2a4 100644
--- a/server/app/apis/misc.py
+++ b/server/app/apis/misc.py
@@ -23,7 +23,6 @@ name_parser.add_argument("name", type=str, required=True, location="json")
 
 @api.route("/types")
 class TypesList(Resource):
-    @protect_route(allowed_roles=["*"], allowed_views=["*"])
     def get(self):
         result = {}
         result["media_types"] = media_type_schema.dump(dbc.get.all(MediaType))
diff --git a/server/app/core/sockets.py b/server/app/core/sockets.py
index e95304b9fc1c50bb4dc354cd863c9780f975b95f..4422d466b29ab3c20e3614d0c7c52c9d36cc86b9 100644
--- a/server/app/core/sockets.py
+++ b/server/app/core/sockets.py
@@ -3,13 +3,15 @@ Contains all functionality related sockets. That is starting and ending a presen
 joining and leaving a presentation and syncing slides and timer bewteen all clients
 connected to the same presentation.
 """
-
+import logging
 from typing import Dict
+
 from app.core import db
-from app.database.models import Slide, ViewType, Code
+from app.database.models import Code, Slide, ViewType
 from flask.globals import request
+from flask_jwt_extended import verify_jwt_in_request
+from flask_jwt_extended.utils import get_jwt_claims
 from flask_socketio import SocketIO, emit, join_room
-import logging
 
 logger = logging.getLogger(__name__)
 logger.propagate = False
@@ -25,6 +27,34 @@ sio = SocketIO(cors_allowed_origins="http://localhost:3000")
 presentations = {}
 
 
+def _is_allowed(allowed, actual):
+    return actual and "*" in allowed or actual in allowed
+
+
+def protect_route(allowed_views=None):
+    def wrapper(func):
+        def inner(*args, **kwargs):
+            try:
+                verify_jwt_in_request()
+            except:
+                logger.warning("Missing Authorization Header")
+                return
+
+            nonlocal allowed_views
+            allowed_views = allowed_views or []
+            claims = get_jwt_claims()
+            view = claims.get("view")
+            if not _is_allowed(allowed_views, view):
+                logger.warning(f"View '{view}' is not allowed to access route only accessible by '{allowed_views}'")
+                return
+
+            return func(*args, **kwargs)
+
+        return inner
+
+    return wrapper
+
+
 @sio.on("connect")
 def connect() -> None:
     logger.info(f"Client '{request.sid}' connected")
@@ -49,6 +79,7 @@ def disconnect() -> None:
     logger.info(f"Client '{request.sid}' disconnected")
 
 
+@protect_route(allowed_views=["Operator"])
 @sio.on("start_presentation")
 def start_presentation(data: Dict) -> None:
     """
@@ -75,6 +106,7 @@ def start_presentation(data: Dict) -> None:
     logger.info(f"Client '{request.sid}' started competition '{competition_id}'")
 
 
+@protect_route(allowed_views=["Operator"])
 @sio.on("end_presentation")
 def end_presentation(data: Dict) -> None:
     """
@@ -152,6 +184,7 @@ def join_presentation(data: Dict) -> None:
     logger.info(f"Client '{request.sid}' joined competition '{competition_id}'")
 
 
+@protect_route(allowed_views=["Operator"])
 @sio.on("set_slide")
 def set_slide(data: Dict) -> None:
     """
@@ -199,6 +232,7 @@ def set_slide(data: Dict) -> None:
     logger.info(f"Client '{request.sid}' set slide '{slide_order}' in competition '{competition_id}'")
 
 
+@protect_route(allowed_views=["Operator"])
 @sio.on("set_timer")
 def set_timer(data: Dict) -> None:
     """