diff --git a/client/src/actions/presentation.ts b/client/src/actions/presentation.ts
index 223b802dc1d26f8ac30a1b7e6c2b6ef5495b0ddc..90b728c54aa18a483ed6ab000f718c407b46abfd 100644
--- a/client/src/actions/presentation.ts
+++ b/client/src/actions/presentation.ts
@@ -5,11 +5,11 @@ This file handles actions for the presentation redux state
 import axios from 'axios'
 import { Slide } from '../interfaces/ApiModels'
 import { Timer } from '../interfaces/Timer'
-import store, { AppDispatch } from './../store'
+import store, { AppDispatch, RootState } from './../store'
 import Types from './types'
 
 // Save competition in presentation state from input id
-export const getPresentationCompetition = (id: string) => async (dispatch: AppDispatch) => {
+export const getPresentationCompetition = (id: string) => async (dispatch: AppDispatch, getState: () => RootState) => {
   await axios
     .get(`/api/competitions/${id}`)
     .then((res) => {
@@ -17,6 +17,9 @@ export const getPresentationCompetition = (id: string) => async (dispatch: AppDi
         type: Types.SET_PRESENTATION_COMPETITION,
         payload: res.data,
       })
+      if (getState().presentation.slide.id === -1 && res.data.slides[0]) {
+        setCurrentSlideByOrder(0)(dispatch)
+      }
     })
     .catch((err) => {
       console.log(err)
diff --git a/client/src/interfaces/ApiModels.ts b/client/src/interfaces/ApiModels.ts
index faecc98f43bdaee26b871616c84cca7aac155913..a6fa68a0e9b58dcc63ad67d29884bbf3e781a64a 100644
--- a/client/src/interfaces/ApiModels.ts
+++ b/client/src/interfaces/ApiModels.ts
@@ -54,7 +54,6 @@ export interface Team extends NameID {
 
 export interface Question extends NameID {
   slide_id: number
-  title: string
   total_score: number
   type_id: number
 }
diff --git a/client/src/pages/presentationEditor/PresentationEditorPage.tsx b/client/src/pages/presentationEditor/PresentationEditorPage.tsx
index 47a746f2943a29a065ccca408658c2eaa00006c1..65720dc9a416264e58938a03fc96246e5a0975ab 100644
--- a/client/src/pages/presentationEditor/PresentationEditorPage.tsx
+++ b/client/src/pages/presentationEditor/PresentationEditorPage.tsx
@@ -6,10 +6,6 @@ import Drawer from '@material-ui/core/Drawer'
 import ListItemText from '@material-ui/core/ListItemText'
 import { createStyles, makeStyles, Theme, withStyles } from '@material-ui/core/styles'
 import AddOutlinedIcon from '@material-ui/icons/AddOutlined'
-import BuildOutlinedIcon from '@material-ui/icons/BuildOutlined'
-import CreateOutlinedIcon from '@material-ui/icons/CreateOutlined'
-import DnsOutlinedIcon from '@material-ui/icons/DnsOutlined'
-import InfoOutlinedIcon from '@material-ui/icons/InfoOutlined'
 import axios from 'axios'
 import React, { useEffect, useState } from 'react'
 import { Link, useParams } from 'react-router-dom'
@@ -18,6 +14,7 @@ import { getEditorCompetition, setEditorSlideId, setEditorViewId } from '../../a
 import { getTypes } from '../../actions/typesAction'
 import { useAppDispatch, useAppSelector } from '../../hooks'
 import { RichSlide } from '../../interfaces/ApiRichModels'
+import { renderSlideIcon } from '../../utils/renderSlideIcon'
 import { RemoveMenuItem } from '../admin/styledComp'
 import { Content, InnerContent } from '../views/styled'
 import SettingsPanel from './components/SettingsPanel'
@@ -141,21 +138,6 @@ const PresentationEditorPage: React.FC = () => {
     setContextState(initialState)
   }
 
-  const renderSlideIcon = (slide: RichSlide) => {
-    if (slide.questions && slide.questions[0] && slide.questions[0].type_id) {
-      switch (slide.questions[0].type_id) {
-        case 1:
-          return <CreateOutlinedIcon /> // text question
-        case 2:
-          return <BuildOutlinedIcon /> // practical qustion
-        case 3:
-          return <DnsOutlinedIcon /> // multiple choice question
-      }
-    } else {
-      return <InfoOutlinedIcon /> // information slide
-    }
-  }
-
   const GreenCheckbox = withStyles({
     root: {
       color: '#FFFFFF',
diff --git a/client/src/pages/presentationEditor/components/SlideDisplay.tsx b/client/src/pages/presentationEditor/components/SlideDisplay.tsx
index b2ca9233e253dd5963ec53fbd8534398632409ab..42096d254b4746fa260863935a23328a6be80ef4 100644
--- a/client/src/pages/presentationEditor/components/SlideDisplay.tsx
+++ b/client/src/pages/presentationEditor/components/SlideDisplay.tsx
@@ -15,7 +15,7 @@ const SlideDisplay = ({ variant, activeViewTypeId }: SlideDisplayProps) => {
   const components = useAppSelector((state) => {
     if (variant === 'editor')
       return state.editor.competition.slides.find((slide) => slide.id === state.editor.activeSlideId)?.components
-    return state.presentation.competition.slides.find((slide) => slide.id === state.presentation.slide.id)?.components
+    return state.presentation.competition.slides.find((slide) => slide.id === state.presentation.slide?.id)?.components
   })
   const dispatch = useAppDispatch()
   const editorPaperRef = useRef<HTMLDivElement>(null)
diff --git a/client/src/pages/views/JudgeViewPage.test.tsx b/client/src/pages/views/JudgeViewPage.test.tsx
index 29de4d12e22fcaac0f6af990bc504077a977b470..2e15f0904705726d364672b87af478ddaf922fbb 100644
--- a/client/src/pages/views/JudgeViewPage.test.tsx
+++ b/client/src/pages/views/JudgeViewPage.test.tsx
@@ -9,7 +9,7 @@ import JudgeViewPage from './JudgeViewPage'
 it('renders judge view page', () => {
   const compRes: any = {
     data: {
-      slides: [{ id: 0, title: '' }],
+      slides: [{ id: 0, title: '', questions: [{ id: 0 }] }],
     },
   }
   const teamsRes: any = {
diff --git a/client/src/pages/views/JudgeViewPage.tsx b/client/src/pages/views/JudgeViewPage.tsx
index f72c052db601d0c346cf8937f58114d173662841..677a080d04ab34e8232120daae018b6d1a2b29c4 100644
--- a/client/src/pages/views/JudgeViewPage.tsx
+++ b/client/src/pages/views/JudgeViewPage.tsx
@@ -1,4 +1,4 @@
-import { Divider, List, ListItemText, Typography } from '@material-ui/core'
+import { Card, Divider, List, ListItem, ListItemText, Paper, 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'
@@ -18,8 +18,13 @@ import {
   JudgeToolbar,
   LeftDrawer,
   RightDrawer,
+  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
@@ -50,6 +55,7 @@ const JudgeViewPage = ({ competitionId, code }: JudgeViewPageProps) => {
   const activeViewTypeId = viewTypes.find((viewType) => viewType.name === 'Judge')?.id
   const teams = useAppSelector((state) => state.presentation.competition.teams)
   const slides = useAppSelector((state) => state.presentation.competition.slides)
+  const currentQuestion = slides[activeSlideIndex]?.questions[0]
   const handleSelectSlide = (index: number) => {
     setActiveSlideIndex(index)
     dispatch(setCurrentSlide(slides[index]))
@@ -88,8 +94,8 @@ const JudgeViewPage = ({ competitionId, code }: JudgeViewPageProps) => {
               button
               key={slide.id}
             >
-              <Typography variant="h6">Slide ID: {slide.id} </Typography>
-              <ListItemText primary={slide.title} />
+              {renderSlideIcon(slide)}
+              <ListItemText primary={`Sida ${slide.order + 1}`} />
             </SlideListItem>
           ))}
         </List>
@@ -103,7 +109,13 @@ const JudgeViewPage = ({ competitionId, code }: JudgeViewPageProps) => {
         anchor="right"
       >
         <div className={classes.toolbar} />
-        <List>
+        {currentQuestion && (
+          <ScoreHeaderPaper $rightDrawerWidth={rightDrawerWidth} elevation={4}>
+            <Typography variant="h4">{`${currentQuestion.name} (${currentQuestion.total_score}p)`}</Typography>
+          </ScoreHeaderPaper>
+        )}
+        <ScoreHeaderPadding />
+        <List style={{ overflowY: 'scroll', overflowX: 'hidden' }}>
           {teams &&
             teams.map((answer, index) => (
               <div key={answer.name}>
@@ -112,8 +124,10 @@ const JudgeViewPage = ({ competitionId, code }: JudgeViewPageProps) => {
               </div>
             ))}
         </List>
+        <ScoreFooterPadding />
+        <JudgeScoringInstructions question={currentQuestion} />
       </RightDrawer>
-      <div style={{ height: 64 }} />
+      <div className={classes.toolbar} />
       <Content leftDrawerWidth={leftDrawerWidth} rightDrawerWidth={rightDrawerWidth}>
         <InnerContent>
           {activeViewTypeId && <SlideDisplay variant="presentation" activeViewTypeId={activeViewTypeId} />}
diff --git a/client/src/pages/views/components/JudgeScoreDisplay.tsx b/client/src/pages/views/components/JudgeScoreDisplay.tsx
index 6308e39b0576a81967068eae4365d43e6ca9befc..2745e12970f3d5a20d2057adbc76ecc0ce2e6d6f 100644
--- a/client/src/pages/views/components/JudgeScoreDisplay.tsx
+++ b/client/src/pages/views/components/JudgeScoreDisplay.tsx
@@ -1,15 +1,35 @@
 import { Box, Typography } from '@material-ui/core'
+import axios from 'axios'
 import React from 'react'
-import { useAppSelector } from '../../../hooks'
+import { getPresentationCompetition } from '../../../actions/presentation'
+import { useAppDispatch, useAppSelector } from '../../../hooks'
 import { AnswerContainer, ScoreDisplayContainer, ScoreDisplayHeader, ScoreInput } from './styled'
 
 type ScoreDisplayProps = {
   teamIndex: number
 }
-const questionMaxScore = 5
 
 const JudgeScoreDisplay = ({ teamIndex }: ScoreDisplayProps) => {
+  const dispatch = useAppDispatch()
   const currentTeam = useAppSelector((state) => state.presentation.competition.teams[teamIndex])
+  const currentCompetititonId = useAppSelector((state) => state.presentation.competition.id)
+  const activeQuestion = useAppSelector(
+    (state) =>
+      state.presentation.competition.slides.find((slide) => slide.id === state.presentation.slide?.id)?.questions[0]
+  )
+  const scores = currentTeam.question_answers.map((questionAnswer) => questionAnswer.score)
+  const questionMaxScore = activeQuestion?.total_score
+  const activeAnswer = currentTeam.question_answers.find(
+    (questionAnswer) => questionAnswer.question_id === activeQuestion?.id
+  )
+  const handleEditScore = async (newScore: number, answerId: number) => {
+    await axios
+      .put(`/api/competitions/${currentCompetititonId}/teams/${currentTeam.id}/answers/${answerId}`, {
+        score: newScore,
+      })
+      .then(() => dispatch(getPresentationCompetition(currentCompetititonId.toString())))
+  }
+
   return (
     <ScoreDisplayContainer>
       <ScoreDisplayHeader>
@@ -17,21 +37,25 @@ const JudgeScoreDisplay = ({ teamIndex }: ScoreDisplayProps) => {
           <Box fontWeight="fontWeightBold">{currentTeam.name}</Box>
         </Typography>
 
-        <ScoreInput
-          label="Poäng"
-          defaultValue={0}
-          inputProps={{ style: { fontSize: 20 } }}
-          InputProps={{ disableUnderline: true, inputProps: { min: 0, max: questionMaxScore } }}
-          type="number"
-        ></ScoreInput>
+        {activeAnswer && (
+          <ScoreInput
+            label="Poäng"
+            defaultValue={activeAnswer?.score}
+            inputProps={{ style: { fontSize: 20 } }}
+            InputProps={{ disableUnderline: true, inputProps: { min: 0, max: questionMaxScore } }}
+            type="number"
+            onChange={(event) => handleEditScore(+event.target.value, activeAnswer.id)}
+          />
+        )}
       </ScoreDisplayHeader>
-      <Typography variant="h6">Alla poäng: 2 0 0 0 0 0 0 0 0</Typography>
-      <Typography variant="h6">Total poäng: 9</Typography>
-      <AnswerContainer>
-        <Typography variant="body1">
-          Svar: blablablablablablablablablabla blablablablabla blablablablabla blablablablablablablablablabla{' '}
-        </Typography>
-      </AnswerContainer>
+      <Typography variant="h6">Alla poäng: [ {scores.map((score) => `${score} `)}]</Typography>
+      <Typography variant="h6">Total poäng: {scores.reduce((a, b) => a + b, 0)}</Typography>
+      {activeAnswer && (
+        <AnswerContainer>
+          <Typography variant="body1">{activeAnswer.answer}</Typography>
+        </AnswerContainer>
+      )}
+      {!activeAnswer && <Typography variant="body1">Inget svar</Typography>}
     </ScoreDisplayContainer>
   )
 }
diff --git a/client/src/pages/views/components/JudgeScoringInstructions.tsx b/client/src/pages/views/components/JudgeScoringInstructions.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..3cc803194a9754bea8a9b23b3f2b073d6547c05c
--- /dev/null
+++ b/client/src/pages/views/components/JudgeScoringInstructions.tsx
@@ -0,0 +1,28 @@
+import { Box, Card, Typography } from '@material-ui/core'
+import axios from 'axios'
+import React from 'react'
+import { getPresentationCompetition } from '../../../actions/presentation'
+import { useAppDispatch, useAppSelector } from '../../../hooks'
+import { RichQuestion } from '../../../interfaces/ApiRichModels'
+import {
+  AnswerContainer,
+  JudgeScoringInstructionsContainer,
+  ScoreDisplayContainer,
+  ScoreDisplayHeader,
+  ScoreInput,
+} from './styled'
+
+type JudgeScoringInstructionsProps = {
+  question: RichQuestion
+}
+
+const JudgeScoringInstructions = ({ question }: JudgeScoringInstructionsProps) => {
+  return (
+    <JudgeScoringInstructionsContainer elevation={3}>
+      <Typography variant="h4">Rättningsinstruktioner</Typography>
+      <Typography variant="body1">Såhär rättar du denhär frågan</Typography>
+    </JudgeScoringInstructionsContainer>
+  )
+}
+
+export default JudgeScoringInstructions
diff --git a/client/src/pages/views/components/PresentationComponent.tsx b/client/src/pages/views/components/PresentationComponent.tsx
index cb95576f9e908d170eee33d78b7f92a285e61be1..a41f7912469256a6e946522790f4f8203f8da60f 100644
--- a/client/src/pages/views/components/PresentationComponent.tsx
+++ b/client/src/pages/views/components/PresentationComponent.tsx
@@ -37,6 +37,7 @@ const PresentationComponent = ({ component, width, height, scale }: Presentation
       minWidth={75 * scale}
       minHeight={75 * scale}
       disableDragging={true}
+      enableResizing={false}
       bounds="parent"
       //Multiply by scale to show components correctly for current screen size
       size={{ width: component.w * scale, height: component.h * scale }}
diff --git a/client/src/pages/views/components/styled.tsx b/client/src/pages/views/components/styled.tsx
index b522b20d3548b4f11864373d5a91088424c45a58..fef186f792dafe8ee259faa693cf6707975d06ba 100644
--- a/client/src/pages/views/components/styled.tsx
+++ b/client/src/pages/views/components/styled.tsx
@@ -1,4 +1,4 @@
-import { TextField } from '@material-ui/core'
+import { Card, Paper, TextField } from '@material-ui/core'
 import styled from 'styled-components'
 
 export const SlideContainer = styled.div`
@@ -32,3 +32,13 @@ export const AnswerContainer = styled.div`
   display: flex;
   flex-wrap: wrap;
 `
+
+export const JudgeScoringInstructionsContainer = styled(Paper)`
+  position: absolute;
+  bottom: 0;
+  height: 250px;
+  width: 100%;
+  display: flex;
+  align-items: center;
+  flex-direction: column;
+`
diff --git a/client/src/pages/views/styled.tsx b/client/src/pages/views/styled.tsx
index 17d09581501876a9f44e389c031e2f64f04fda82..8f63892ade7928842c12d4dc73df988a505f4cea 100644
--- a/client/src/pages/views/styled.tsx
+++ b/client/src/pages/views/styled.tsx
@@ -1,4 +1,4 @@
-import { AppBar, Button, Drawer, Toolbar, Typography } from '@material-ui/core'
+import { AppBar, Button, Card, Drawer, Paper, Toolbar, Typography } from '@material-ui/core'
 import styled from 'styled-components'
 
 export const JudgeAppBar = styled(AppBar)`
@@ -146,3 +146,26 @@ export const PresenterInnerContent = styled.div`
 export const ParticipantContainer = styled.div`
   max-width: calc((100vh / 9) * 16);
 `
+
+interface ScoreHeaderPaperProps {
+  $rightDrawerWidth: number
+}
+
+export const ScoreHeaderPaper = styled(Card)<ScoreHeaderPaperProps>`
+  position: absolute;
+  top: 66px;
+  width: ${(props) => (props ? props.$rightDrawerWidth : 0)}px;
+  height: 71px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  z-index: 10;
+`
+
+export const ScoreHeaderPadding = styled.div`
+  min-height: 71px;
+`
+
+export const ScoreFooterPadding = styled.div`
+  min-height: 250px;
+`
diff --git a/client/src/reducers/presentationReducer.test.ts b/client/src/reducers/presentationReducer.test.ts
index ee08e0685125f095274474430008f4ad4eaa357e..202d9406853628a32c77b9e42e1bcc502299889d 100644
--- a/client/src/reducers/presentationReducer.test.ts
+++ b/client/src/reducers/presentationReducer.test.ts
@@ -6,7 +6,8 @@ import presentationReducer from './presentationReducer'
 const initialState = {
   competition: {
     name: '',
-    id: 0,
+    id: 1,
+    background_image: undefined,
     city_id: 0,
     slides: [],
     year: 0,
@@ -14,7 +15,8 @@ const initialState = {
   },
   slide: {
     competition_id: 0,
-    id: 0,
+    background_image: undefined,
+    id: -1,
     order: 0,
     timer: 0,
     title: '',
@@ -48,7 +50,7 @@ it('should handle SET_PRESENTATION_COMPETITION', () => {
     })
   ).toEqual({
     competition: testCompetition,
-    slide: testCompetition.slides[0],
+    slide: initialState.slide,
     code: initialState.code,
     timer: initialState.timer,
   })
diff --git a/client/src/reducers/presentationReducer.ts b/client/src/reducers/presentationReducer.ts
index b657f5a463b339a4fbfc8309f79157ed78320f9e..b0c4791e9ab1f0ef05c931caa1fc4e9fe8268917 100644
--- a/client/src/reducers/presentationReducer.ts
+++ b/client/src/reducers/presentationReducer.ts
@@ -14,7 +14,7 @@ interface PresentationState {
 const initialState: PresentationState = {
   competition: {
     name: '',
-    id: 0,
+    id: 1,
     city_id: 0,
     slides: [],
     year: 0,
@@ -23,7 +23,7 @@ const initialState: PresentationState = {
   },
   slide: {
     competition_id: 0,
-    id: 0,
+    id: -1,
     order: 0,
     timer: 0,
     title: '',
@@ -41,7 +41,6 @@ export default function (state = initialState, action: AnyAction) {
     case Types.SET_PRESENTATION_COMPETITION:
       return {
         ...state,
-        slide: action.payload.slides[0] as Slide,
         competition: action.payload as RichCompetition,
       }
     case Types.SET_PRESENTATION_CODE:
diff --git a/client/src/utils/renderSlideIcon.tsx b/client/src/utils/renderSlideIcon.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..ba1eafcb8052469dd8b627b95d2eee518bfd5fe8
--- /dev/null
+++ b/client/src/utils/renderSlideIcon.tsx
@@ -0,0 +1,21 @@
+import { RichSlide } from '../interfaces/ApiRichModels'
+import React from 'react'
+import BuildOutlinedIcon from '@material-ui/icons/BuildOutlined'
+import CreateOutlinedIcon from '@material-ui/icons/CreateOutlined'
+import DnsOutlinedIcon from '@material-ui/icons/DnsOutlined'
+import InfoOutlinedIcon from '@material-ui/icons/InfoOutlined'
+
+export const renderSlideIcon = (slide: RichSlide) => {
+  if (slide.questions && slide.questions[0] && slide.questions[0].type_id) {
+    switch (slide.questions[0].type_id) {
+      case 1:
+        return <CreateOutlinedIcon /> // text question
+      case 2:
+        return <BuildOutlinedIcon /> // practical qustion
+      case 3:
+        return <DnsOutlinedIcon /> // multiple choice question
+    }
+  } else {
+    return <InfoOutlinedIcon /> // information slide
+  }
+}
diff --git a/server/app/apis/__init__.py b/server/app/apis/__init__.py
index 5eab3f829ae81e17ecc269d98f568eeacc45a68c..e5ec3d2ca1e1d9de6d4c5cca5ec1e3fba40f33ee 100644
--- a/server/app/apis/__init__.py
+++ b/server/app/apis/__init__.py
@@ -13,24 +13,62 @@ def validate_editor(db_item, *views):
         abort(http_codes.UNAUTHORIZED)
 
 
-def check_jwt(editor=False, *views):
-    def wrapper(fn):
-        @wraps(fn)
-        def decorator(*args, **kwargs):
+def _is_allowed(allowed, actual):
+    return actual and "*" in allowed or actual in allowed
+
+
+def _has_access(in_claim, in_route):
+    in_route = int(in_route) if in_route else None
+    return not in_route or in_claim and in_claim == in_route
+
+
+def protect_route(allowed_roles=None, allowed_views=None):
+    def wrapper(func):
+        def inner(*args, **kwargs):
             verify_jwt_in_request()
             claims = get_jwt_claims()
+
+            # Authorize request if roles has access to the route #
+
+            nonlocal allowed_roles
+            allowed_roles = allowed_roles or []
             role = claims.get("role")
+            if _is_allowed(allowed_roles, role):
+                return func(*args, **kwargs)
+
+            # Authorize request if view has access and is trying to access the
+            # competition its in. Also check team if client is a team.
+            # Allow request if route doesn't belong to any competition.
+
+            nonlocal allowed_views
+            allowed_views = allowed_views or []
             view = claims.get("view")
-            if role == "Admin":
-                return fn(*args, **kwargs)
-            elif editor and role == "Editor":
-                return fn(*args, **kwargs)
-            elif view in views:
-                return fn(*args, **kwargs)
-            else:
-                abort(http_codes.UNAUTHORIZED)
-
-        return decorator
+            if not _is_allowed(allowed_views, view):
+                abort(
+                    http_codes.UNAUTHORIZED,
+                    f"Client with view '{view}' is not allowed to access route with allowed views {allowed_views}.",
+                )
+
+            claim_competition_id = claims.get("competition_id")
+            route_competition_id = kwargs.get("competition_id")
+            if not _has_access(claim_competition_id, route_competition_id):
+                abort(
+                    http_codes.UNAUTHORIZED,
+                    f"Client in competition '{claim_competition_id}' is not allowed to access competition '{route_competition_id}'.",
+                )
+
+            if view == "Team":
+                claim_team_id = claims.get("team_id")
+                route_team_id = kwargs.get("team_id")
+                if not _has_access(claim_team_id, route_team_id):
+                    abort(
+                        http_codes.UNAUTHORIZED,
+                        f"Client in team '{claim_team_id}' is not allowed to access team '{route_team_id}'.",
+                    )
+
+            return func(*args, **kwargs)
+
+        return inner
 
     return wrapper
 
diff --git a/server/app/apis/alternatives.py b/server/app/apis/alternatives.py
index 2adad553ddb34bbd43e1405223c7c45962d66e19..0ce74d53b0a742ea31689d826f898e1b12f33b41 100644
--- a/server/app/apis/alternatives.py
+++ b/server/app/apis/alternatives.py
@@ -1,6 +1,6 @@
 import app.core.http_codes as codes
 import app.database.controller as dbc
-from app.apis import check_jwt, item_response, list_response
+from app.apis import item_response, list_response, protect_route
 from app.core.dto import QuestionAlternativeDTO
 from flask_restx import Resource
 from flask_restx import reqparse
@@ -17,12 +17,12 @@ question_alternative_parser.add_argument("value", type=int, default=None, locati
 @api.route("")
 @api.param("competition_id, slide_id, question_id")
 class QuestionAlternativeList(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"], allowed_views=["*"])
     def get(self, competition_id, slide_id, question_id):
         items = dbc.get.question_alternative_list(competition_id, slide_id, question_id)
         return list_response(list_schema.dump(items))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def post(self, competition_id, slide_id, question_id):
         args = question_alternative_parser.parse_args(strict=True)
         item = dbc.add.question_alternative(**args, question_id=question_id)
@@ -32,19 +32,19 @@ class QuestionAlternativeList(Resource):
 @api.route("/<alternative_id>")
 @api.param("competition_id, slide_id, question_id, alternative_id")
 class QuestionAlternatives(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"], allowed_views=["*"])
     def get(self, competition_id, slide_id, question_id, alternative_id):
         items = dbc.get.question_alternative(competition_id, slide_id, question_id, alternative_id)
         return item_response(schema.dump(items))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def put(self, competition_id, slide_id, question_id, alternative_id):
         args = question_alternative_parser.parse_args(strict=True)
         item = dbc.get.question_alternative(competition_id, slide_id, question_id, alternative_id)
         item = dbc.edit.default(item, **args)
         return item_response(schema.dump(item))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def delete(self, competition_id, slide_id, question_id, alternative_id):
         item = dbc.get.question_alternative(competition_id, slide_id, question_id, alternative_id)
         dbc.delete.default(item)
diff --git a/server/app/apis/answers.py b/server/app/apis/answers.py
index e3e225b9d0fbdb1076a03d4bd12f9fd018f25b90..8f72d3bde8610c255083df9deefc69b89a68560a 100644
--- a/server/app/apis/answers.py
+++ b/server/app/apis/answers.py
@@ -1,6 +1,6 @@
 import app.core.http_codes as codes
 import app.database.controller as dbc
-from app.apis import check_jwt, item_response, list_response
+from app.apis import item_response, list_response, protect_route
 from app.core.dto import QuestionAnswerDTO
 from flask_restx import Resource
 from flask_restx import reqparse
@@ -22,12 +22,12 @@ question_answer_edit_parser.add_argument("score", type=int, default=None, locati
 @api.route("")
 @api.param("competition_id, team_id")
 class QuestionAnswerList(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"], allowed_views=["*"])
     def get(self, competition_id, team_id):
         items = dbc.get.question_answer_list(competition_id, team_id)
         return list_response(list_schema.dump(items))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"], allowed_views=["*"])
     def post(self, competition_id, team_id):
         args = question_answer_parser.parse_args(strict=True)
         item = dbc.add.question_answer(**args, team_id=team_id)
@@ -37,12 +37,12 @@ class QuestionAnswerList(Resource):
 @api.route("/<answer_id>")
 @api.param("competition_id, team_id, answer_id")
 class QuestionAnswers(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"], allowed_views=["*"])
     def get(self, competition_id, team_id, answer_id):
         item = dbc.get.question_answer(competition_id, team_id, answer_id)
         return item_response(schema.dump(item))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"], allowed_views=["*"])
     def put(self, competition_id, team_id, answer_id):
         args = question_answer_edit_parser.parse_args(strict=True)
         item = dbc.get.question_answer(competition_id, team_id, answer_id)
diff --git a/server/app/apis/auth.py b/server/app/apis/auth.py
index 37da9e98d4b5970cd82b47dd54ad940cc53b6736..9c9ed24a876a9b130c385194bb8cc322bef65c9d 100644
--- a/server/app/apis/auth.py
+++ b/server/app/apis/auth.py
@@ -1,6 +1,6 @@
 import app.core.http_codes as codes
 import app.database.controller as dbc
-from app.apis import check_jwt, item_response, text_response
+from app.apis import item_response, protect_route, text_response
 from app.core.codes import verify_code
 from app.core.dto import AuthDTO, CodeDTO
 from flask_jwt_extended import (
@@ -12,6 +12,8 @@ from flask_jwt_extended import (
 )
 from flask_restx import Resource
 from flask_restx import inputs, reqparse
+from datetime import timedelta
+from app.core import sockets
 
 api = AuthDTO.api
 schema = AuthDTO.schema
@@ -33,9 +35,13 @@ def get_user_claims(item_user):
     return {"role": item_user.role.name, "city_id": item_user.city_id}
 
 
+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}
+
+
 @api.route("/signup")
 class AuthSignup(Resource):
-    @check_jwt(editor=False)
+    @protect_route(allowed_roles=["Admin"])
     def post(self):
         args = create_user_parser.parse_args(strict=True)
         email = args.get("email")
@@ -50,7 +56,7 @@ class AuthSignup(Resource):
 @api.route("/delete/<ID>")
 @api.param("ID")
 class AuthDelete(Resource):
-    @check_jwt(editor=False)
+    @protect_route(allowed_roles=["Admin"])
     def delete(self, ID):
         item_user = dbc.get.user(ID)
 
@@ -86,24 +92,38 @@ class AuthLoginCode(Resource):
         code = args["code"]
 
         if not verify_code(code):
-            api.abort(codes.BAD_REQUEST, "Invalid code")
+            api.abort(codes.UNAUTHORIZED, "Invalid code")
 
         item_code = dbc.get.code_by_code(code)
-        return item_response(CodeDTO.schema.dump(item_code))
+
+        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)
+        )
+
+        response = {
+            "competition_id": item_code.competition_id,
+            "view_type_id": item_code.view_type_id,
+            "team_id": item_code.team_id,
+            "access_token": access_token,
+        }
+        return response
 
 
 @api.route("/logout")
 class AuthLogout(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"], allowed_views=["*"])
     def post(self):
         jti = get_raw_jwt()["jti"]
         dbc.add.blacklist(jti)
-        return text_response("User logout")
+        return text_response("Logout")
 
 
 @api.route("/refresh")
 class AuthRefresh(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     @jwt_refresh_token_required
     def post(self):
         old_jti = get_raw_jwt()["jti"]
diff --git a/server/app/apis/codes.py b/server/app/apis/codes.py
index 8a4105d24a77f07b04da1e1e2ed477746ea7a932..d07e17435aed31417254acfd83ed9315f1477ba3 100644
--- a/server/app/apis/codes.py
+++ b/server/app/apis/codes.py
@@ -1,5 +1,5 @@
 import app.database.controller as dbc
-from app.apis import check_jwt, item_response, list_response
+from app.apis import item_response, list_response, protect_route
 from app.core import http_codes as codes
 from app.core.dto import CodeDTO
 from app.database.models import Code
@@ -13,7 +13,7 @@ list_schema = CodeDTO.list_schema
 @api.route("")
 @api.param("competition_id")
 class CodesList(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def get(self, competition_id):
         items = dbc.get.code_list(competition_id)
         return list_response(list_schema.dump(items), len(items))
@@ -22,7 +22,7 @@ class CodesList(Resource):
 @api.route("/<code_id>")
 @api.param("competition_id, code_id")
 class CodesById(Resource):
-    @check_jwt(editor=False)
+    @protect_route(allowed_roles=["*"])
     def put(self, competition_id, code_id):
         item = dbc.get.one(Code, code_id)
         item.code = dbc.utils.generate_unique_code()
diff --git a/server/app/apis/competitions.py b/server/app/apis/competitions.py
index fded52ec97df7d72acaf531eb5f0033c25227669..d1fd95722f026cb3aa67e9bb8354a6823af9bbee 100644
--- a/server/app/apis/competitions.py
+++ b/server/app/apis/competitions.py
@@ -1,7 +1,7 @@
 import time
 
 import app.database.controller as dbc
-from app.apis import check_jwt, item_response, list_response
+from app.apis import item_response, list_response, protect_route
 from app.core.dto import CompetitionDTO
 from app.database.models import Competition
 from flask_restx import Resource
@@ -29,7 +29,7 @@ competition_search_parser.add_argument("city_id", type=int, default=None, locati
 
 @api.route("")
 class CompetitionsList(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def post(self):
         args = competition_parser.parse_args(strict=True)
 
@@ -44,20 +44,21 @@ class CompetitionsList(Resource):
 @api.route("/<competition_id>")
 @api.param("competition_id")
 class Competitions(Resource):
+    @protect_route(allowed_roles=["*"], allowed_views=["*"])
     def get(self, competition_id):
         item = dbc.get.competition(competition_id)
 
         return item_response(rich_schema.dump(item))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def put(self, competition_id):
         args = competition_edit_parser.parse_args(strict=True)
         item = dbc.get.one(Competition, competition_id)
-        item = dbc.edit.default(item, **args)
+        item = dbc.edit.competition(item, **args)
 
         return item_response(schema.dump(item))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def delete(self, competition_id):
         item = dbc.get.one(Competition, competition_id)
         dbc.delete.competition(item)
@@ -67,7 +68,7 @@ class Competitions(Resource):
 
 @api.route("/search")
 class CompetitionSearch(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def get(self):
         args = competition_search_parser.parse_args(strict=True)
         items, total = dbc.search.competition(**args)
@@ -77,7 +78,7 @@ class CompetitionSearch(Resource):
 @api.route("/<competition_id>/copy")
 @api.param("competition_id")
 class SlidesOrder(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def post(self, competition_id):
         item_competition = dbc.get.competition(competition_id)
 
diff --git a/server/app/apis/components.py b/server/app/apis/components.py
index 8f2531513a54fd6392983c7bd0b0657c3d4c6455..43018695e959f4ff00fa4377f2f3600e8a1cd494 100644
--- a/server/app/apis/components.py
+++ b/server/app/apis/components.py
@@ -1,6 +1,6 @@
 import app.core.http_codes as codes
 import app.database.controller as dbc
-from app.apis import check_jwt, item_response, list_response
+from app.apis import item_response, list_response, protect_route
 from app.core.dto import ComponentDTO
 from flask_restx import Resource
 from flask_restx import reqparse
@@ -28,12 +28,12 @@ component_create_parser.add_argument("view_type_id", type=int, required=True, lo
 @api.route("/<component_id>")
 @api.param("competition_id, slide_id, component_id")
 class ComponentByID(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"], allowed_views=["*"])
     def get(self, competition_id, slide_id, component_id):
         item = dbc.get.component(competition_id, slide_id, component_id)
         return item_response(schema.dump(item))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def put(self, competition_id, slide_id, component_id):
         args = component_edit_parser.parse_args(strict=True)
         item = dbc.get.component(competition_id, slide_id, component_id)
@@ -41,7 +41,7 @@ class ComponentByID(Resource):
         item = dbc.edit.default(item, **args_without_none)
         return item_response(schema.dump(item))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def delete(self, competition_id, slide_id, component_id):
         item = dbc.get.component(competition_id, slide_id, component_id)
         dbc.delete.component(item)
@@ -51,12 +51,12 @@ class ComponentByID(Resource):
 @api.route("")
 @api.param("competition_id, slide_id")
 class ComponentList(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"], allowed_views=["*"])
     def get(self, competition_id, slide_id):
         items = dbc.get.component_list(competition_id, slide_id)
         return list_response(list_schema.dump(items))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def post(self, competition_id, slide_id):
         args = component_create_parser.parse_args()
         item = dbc.add.component(slide_id=slide_id, **args)
diff --git a/server/app/apis/media.py b/server/app/apis/media.py
index 5d89550ac19aaff78643a34ae0a3059c011aed23..c7de8c4d4c1cbd3f482d386e654a9bfd0063370b 100644
--- a/server/app/apis/media.py
+++ b/server/app/apis/media.py
@@ -1,6 +1,6 @@
 import app.core.http_codes as codes
 import app.database.controller as dbc
-from app.apis import check_jwt, item_response, list_response
+from app.apis import item_response, list_response, protect_route
 from app.core.dto import MediaDTO
 from app.core.parsers import search_parser
 from app.database.models import Media
@@ -22,13 +22,13 @@ media_parser_search.add_argument("filename", type=str, default=None, location="a
 
 @api.route("/images")
 class ImageList(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def get(self):
         args = media_parser_search.parse_args(strict=True)
         items, total = dbc.search.image(**args)
         return list_response(list_schema.dump(items), total)
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def post(self):
         if "image" not in request.files:
             api.abort(codes.BAD_REQUEST, "Missing image in request.files")
@@ -48,12 +48,12 @@ class ImageList(Resource):
 @api.route("/images/<ID>")
 @api.param("ID")
 class ImageList(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"], allowed_views=["*"])
     def get(self, ID):
         item = dbc.get.one(Media, ID)
         return item_response(schema.dump(item))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def delete(self, ID):
         item = dbc.get.one(Media, ID)
         try:
diff --git a/server/app/apis/misc.py b/server/app/apis/misc.py
index ab52362d46d026d8483c52f5c4d8384f32de755d..20a84e4c17c138b3a94ea6e3902e67154036cabc 100644
--- a/server/app/apis/misc.py
+++ b/server/app/apis/misc.py
@@ -1,5 +1,5 @@
 import app.database.controller as dbc
-from app.apis import check_jwt, list_response
+from app.apis import list_response, protect_route
 from app.core import http_codes
 from app.core.dto import MiscDTO
 from app.database.models import City, Competition, ComponentType, MediaType, QuestionType, Role, User, ViewType
@@ -23,6 +23,7 @@ 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))
@@ -34,7 +35,7 @@ class TypesList(Resource):
 
 @api.route("/roles")
 class RoleList(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def get(self):
         items = dbc.get.all(Role)
         return list_response(role_schema.dump(items))
@@ -42,12 +43,12 @@ class RoleList(Resource):
 
 @api.route("/cities")
 class CitiesList(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def get(self):
         items = dbc.get.all(City)
         return list_response(city_schema.dump(items))
 
-    @check_jwt(editor=False)
+    @protect_route(allowed_roles=["Admin"])
     def post(self):
         args = name_parser.parse_args(strict=True)
         dbc.add.city(args["name"])
@@ -58,7 +59,7 @@ class CitiesList(Resource):
 @api.route("/cities/<ID>")
 @api.param("ID")
 class Cities(Resource):
-    @check_jwt(editor=False)
+    @protect_route(allowed_roles=["Admin"])
     def put(self, ID):
         item = dbc.get.one(City, ID)
         args = name_parser.parse_args(strict=True)
@@ -67,7 +68,7 @@ class Cities(Resource):
         items = dbc.get.all(City)
         return list_response(city_schema.dump(items))
 
-    @check_jwt(editor=False)
+    @protect_route(allowed_roles=["Admin"])
     def delete(self, ID):
         item = dbc.get.one(City, ID)
         dbc.delete.default(item)
@@ -77,7 +78,7 @@ class Cities(Resource):
 
 @api.route("/statistics")
 class Statistics(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def get(self):
         user_count = User.query.count()
         competition_count = Competition.query.count()
diff --git a/server/app/apis/questions.py b/server/app/apis/questions.py
index aaefafc40608d14bf3a4e8a2a3b7fdfbda559f2c..de40a310db5f11a1b0c03cb20e1546f74682af70 100644
--- a/server/app/apis/questions.py
+++ b/server/app/apis/questions.py
@@ -1,6 +1,6 @@
 import app.core.http_codes as codes
 import app.database.controller as dbc
-from app.apis import check_jwt, item_response, list_response
+from app.apis import item_response, list_response, protect_route
 from app.core.dto import QuestionDTO
 from flask_restx import Resource
 from flask_restx import reqparse
@@ -18,7 +18,7 @@ question_parser.add_argument("type_id", type=int, default=None, location="json")
 @api.route("/questions")
 @api.param("competition_id")
 class QuestionList(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def get(self, competition_id):
         items = dbc.get.question_list_for_competition(competition_id)
         return list_response(list_schema.dump(items))
@@ -27,12 +27,12 @@ class QuestionList(Resource):
 @api.route("/slides/<slide_id>/questions")
 @api.param("competition_id, slide_id")
 class QuestionListForSlide(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def get(self, competition_id, slide_id):
         items = dbc.get.question_list(competition_id, slide_id)
         return list_response(list_schema.dump(items))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def post(self, competition_id, slide_id):
         args = question_parser.parse_args(strict=True)
         item = dbc.add.question(slide_id=slide_id, **args)
@@ -42,12 +42,12 @@ class QuestionListForSlide(Resource):
 @api.route("/slides/<slide_id>/questions/<question_id>")
 @api.param("competition_id, slide_id, question_id")
 class QuestionById(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def get(self, competition_id, slide_id, question_id):
         item_question = dbc.get.question(competition_id, slide_id, question_id)
         return item_response(schema.dump(item_question))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def put(self, competition_id, slide_id, question_id):
         args = question_parser.parse_args(strict=True)
 
@@ -56,7 +56,7 @@ class QuestionById(Resource):
 
         return item_response(schema.dump(item_question))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def delete(self, competition_id, slide_id, question_id):
         item_question = dbc.get.question(competition_id, slide_id, question_id)
         dbc.delete.question(item_question)
diff --git a/server/app/apis/slides.py b/server/app/apis/slides.py
index da29633a794cdb1262d2c307b8c80a94c794830e..52553796260493507bcc47ef32f8977f241a2a88 100644
--- a/server/app/apis/slides.py
+++ b/server/app/apis/slides.py
@@ -1,6 +1,6 @@
 import app.core.http_codes as codes
 import app.database.controller as dbc
-from app.apis import check_jwt, item_response, list_response
+from app.apis import item_response, list_response, protect_route
 from app.core.dto import SlideDTO
 from flask_restx import Resource
 from flask_restx import reqparse
@@ -19,12 +19,12 @@ slide_parser.add_argument("background_image_id", default=None, type=int, locatio
 @api.route("")
 @api.param("competition_id")
 class SlidesList(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def get(self, competition_id):
         items = dbc.get.slide_list(competition_id)
         return list_response(list_schema.dump(items))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def post(self, competition_id):
         item_slide = dbc.add.slide(competition_id)
         return item_response(schema.dump(item_slide))
@@ -33,21 +33,21 @@ class SlidesList(Resource):
 @api.route("/<slide_id>")
 @api.param("competition_id,slide_id")
 class Slides(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def get(self, competition_id, slide_id):
         item_slide = dbc.get.slide(competition_id, slide_id)
         return item_response(schema.dump(item_slide))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def put(self, competition_id, slide_id):
         args = slide_parser.parse_args(strict=True)
 
         item_slide = dbc.get.slide(competition_id, slide_id)
-        item_slide = dbc.edit.default(item_slide, **args)
+        item_slide = dbc.edit.slide(item_slide, **args)
 
         return item_response(schema.dump(item_slide))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def delete(self, competition_id, slide_id):
         item_slide = dbc.get.slide(competition_id, slide_id)
 
@@ -58,7 +58,7 @@ class Slides(Resource):
 @api.route("/<slide_id>/order")
 @api.param("competition_id,slide_id")
 class SlideOrder(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def put(self, competition_id, slide_id):
         args = slide_parser.parse_args(strict=True)
         order = args.get("order")
@@ -87,7 +87,7 @@ class SlideOrder(Resource):
 @api.route("/<slide_id>/copy")
 @api.param("competition_id,slide_id")
 class SlideCopy(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def post(self, competition_id, slide_id):
         item_slide = dbc.get.slide(competition_id, slide_id)
 
diff --git a/server/app/apis/teams.py b/server/app/apis/teams.py
index 3fcb276c959c8880be30713bf08869d6b30b8dbb..c951ae53e6b7839d1457e60ccce60ea398ee617d 100644
--- a/server/app/apis/teams.py
+++ b/server/app/apis/teams.py
@@ -1,6 +1,6 @@
 import app.core.http_codes as codes
 import app.database.controller as dbc
-from app.apis import check_jwt, item_response, list_response
+from app.apis import item_response, list_response, protect_route
 from app.core.dto import TeamDTO
 from flask_restx import Resource, reqparse
 from flask_restx import reqparse
@@ -16,12 +16,12 @@ team_parser.add_argument("name", type=str, location="json")
 @api.route("")
 @api.param("competition_id")
 class TeamsList(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def get(self, competition_id):
         items = dbc.get.team_list(competition_id)
         return list_response(list_schema.dump(items))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def post(self, competition_id):
         args = team_parser.parse_args(strict=True)
         item_team = dbc.add.team(args["name"], competition_id)
@@ -31,19 +31,19 @@ class TeamsList(Resource):
 @api.route("/<team_id>")
 @api.param("competition_id,team_id")
 class Teams(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def get(self, competition_id, team_id):
         item = dbc.get.team(competition_id, team_id)
         return item_response(schema.dump(item))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def delete(self, competition_id, team_id):
         item_team = dbc.get.team(competition_id, team_id)
 
         dbc.delete.team(item_team)
         return {}, codes.NO_CONTENT
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def put(self, competition_id, team_id):
         args = team_parser.parse_args(strict=True)
         name = args.get("name")
diff --git a/server/app/apis/users.py b/server/app/apis/users.py
index a5b2d58b01f9ce8931fd29cba3af9d1d8b825cc0..50470db724ebf3648a2ff3a206a87b53650417ee 100644
--- a/server/app/apis/users.py
+++ b/server/app/apis/users.py
@@ -1,6 +1,6 @@
 import app.core.http_codes as codes
 import app.database.controller as dbc
-from app.apis import check_jwt, item_response, list_response
+from app.apis import item_response, list_response, protect_route
 from app.core.dto import UserDTO
 from flask_jwt_extended import get_jwt_identity
 from flask_restx import Resource
@@ -40,12 +40,12 @@ def _edit_user(item_user, args):
 
 @api.route("")
 class UsersList(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def get(self):
         item = dbc.get.user(get_jwt_identity())
         return item_response(schema.dump(item))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def put(self):
         args = user_parser.parse_args(strict=True)
         item = dbc.get.user(get_jwt_identity())
@@ -56,12 +56,12 @@ class UsersList(Resource):
 @api.route("/<ID>")
 @api.param("ID")
 class Users(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def get(self, ID):
         item = dbc.get.user(ID)
         return item_response(schema.dump(item))
 
-    @check_jwt(editor=False)
+    @protect_route(allowed_roles=["Admin"])
     def put(self, ID):
         args = user_parser.parse_args(strict=True)
         item = dbc.get.user(ID)
@@ -71,7 +71,7 @@ class Users(Resource):
 
 @api.route("/search")
 class UserSearch(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def get(self):
         args = user_search_parser.parse_args(strict=True)
         items, total = dbc.search.user(**args)
diff --git a/server/app/database/controller/add.py b/server/app/database/controller/add.py
index f7b0edbb17ac91ea9ef3786182ff58614b43cd45..50321626d0164fb01ef61d11e642bb679e729ead 100644
--- a/server/app/database/controller/add.py
+++ b/server/app/database/controller/add.py
@@ -151,9 +151,13 @@ def competition(name, year, city_id):
 
     # Add code for Judge view
     code(2, item_competition.id)
+
     # Add code for Audience view
     code(3, item_competition.id)
 
+    # Add code for Operator view
+    code(4, item_competition.id)
+
     item_competition = utils.refresh(item_competition)
     return item_competition
 
diff --git a/server/app/database/controller/edit.py b/server/app/database/controller/edit.py
index b1f5bc91e045e43c5f69618a685a39f86c7b081d..94bc6008a1f5b8173df1ddbba7d6745f7c6ea5f0 100644
--- a/server/app/database/controller/edit.py
+++ b/server/app/database/controller/edit.py
@@ -51,3 +51,17 @@ def default(item, **kwargs):
     db.session.commit()
     db.session.refresh(item)
     return item
+
+
+def competition(item, **kwargs):
+    if kwargs["background_image_id"] == -1:
+        item.background_image_id = None
+        del kwargs["background_image_id"]
+    return default(item, **kwargs)
+
+
+def slide(item, **kwargs):
+    if kwargs["background_image_id"] == -1:
+        item.background_image_id = None
+        del kwargs["background_image_id"]
+    return default(item, **kwargs)
\ No newline at end of file
diff --git a/server/app/database/models.py b/server/app/database/models.py
index c84677b2d04fc7ce2554548d67fb3d8a21d5ccf2..c56aa3f0051ba6033e7376b0912cbf4f45928252 100644
--- a/server/app/database/models.py
+++ b/server/app/database/models.py
@@ -231,6 +231,8 @@ class Code(db.Model):
     competition_id = db.Column(db.Integer, db.ForeignKey("competition.id"), nullable=False)
     team_id = db.Column(db.Integer, db.ForeignKey("team.id"), nullable=True)
 
+    view_type = db.relationship("ViewType", uselist=False)
+
     def __init__(self, code, view_type_id, competition_id=None, team_id=None):
         self.code = code
         self.view_type_id = view_type_id
@@ -241,7 +243,6 @@ class Code(db.Model):
 class ViewType(db.Model):
     id = db.Column(db.Integer, primary_key=True)
     name = db.Column(db.String(STRING_SIZE), unique=True)
-    codes = db.relationship("Code", backref="view_type")
 
     def __init__(self, name):
         self.name = name
diff --git a/server/tests/test_app.py b/server/tests/test_app.py
index 199df387e878fdbbde759a80e6aa3d9ea486ea7d..d59428a6380b74abe8853c6c09c4df0bafc031e0 100644
--- a/server/tests/test_app.py
+++ b/server/tests/test_app.py
@@ -3,7 +3,9 @@ This file tests the api function calls.
 """
 
 import app.core.http_codes as codes
+from app.database.controller.add import competition
 from app.database.models import Slide
+from app.core import sockets
 
 from tests import app, client, db
 from tests.test_helpers import add_default_values, change_order_test, delete, get, post, put
@@ -342,7 +344,7 @@ def test_question_api(client):
     slide_order = 1
     response, body = get(client, f"/api/competitions/{CID}/questions", headers=headers)
     assert response.status_code == codes.OK
-    assert body["count"] == 1
+    assert body["count"] == 2
 
     # Get questions from another competition that should have some questions
     CID = 3
@@ -385,3 +387,67 @@ def test_question_api(client):
     response, _ = delete(client, f"/api/competitions/{CID}/slides/{NEW_slide_order}/questions/{QID}", headers=headers)
     assert response.status_code == codes.NOT_FOUND
     """
+
+
+def test_authorization(client):
+    add_default_values()
+
+    # Fake that competition 1 is active
+    sockets.presentations[1] = {}
+
+    #### TEAM ####
+    # Login in with team code
+    response, body = post(client, "/api/auth/login/code", {"code": "111111"})
+    assert response.status_code == codes.OK
+    headers = {"Authorization": "Bearer " + body["access_token"]}
+
+    competition_id = body["competition_id"]
+    team_id = body["team_id"]
+
+    # Get competition team is in
+    response, body = get(client, f"/api/competitions/{competition_id}", headers=headers)
+    assert response.status_code == codes.OK
+
+    # Try to delete competition team is in
+    response, body = delete(client, f"/api/competitions/{competition_id}", headers=headers)
+    assert response.status_code == codes.UNAUTHORIZED
+
+    # Try to get a different competition
+    response, body = get(client, f"/api/competitions/{competition_id+1}", headers=headers)
+    assert response.status_code == codes.UNAUTHORIZED
+
+    # Get own answers
+    response, body = get(client, f"/api/competitions/{competition_id}/teams/{team_id}/answers", headers=headers)
+    assert response.status_code == codes.OK
+
+    # Try to get another teams answers
+    response, body = get(client, f"/api/competitions/{competition_id}/teams/{team_id+1}/answers", headers=headers)
+    assert response.status_code == codes.UNAUTHORIZED
+
+    #### JUDGE ####
+    # Login in with judge code
+    response, body = post(client, "/api/auth/login/code", {"code": "222222"})
+    assert response.status_code == codes.OK
+    headers = {"Authorization": "Bearer " + body["access_token"]}
+
+    competition_id = body["competition_id"]
+
+    # Get competition judge is in
+    response, body = get(client, f"/api/competitions/{competition_id}", headers=headers)
+    assert response.status_code == codes.OK
+
+    # Try to delete competition judge is in
+    response, body = delete(client, f"/api/competitions/{competition_id}", headers=headers)
+    assert response.status_code == codes.UNAUTHORIZED
+
+    # Try to get a different competition
+    response, body = get(client, f"/api/competitions/{competition_id+1}", headers=headers)
+    assert response.status_code == codes.UNAUTHORIZED
+
+    # Get team answers
+    response, body = get(client, f"/api/competitions/{competition_id}/teams/{team_id}/answers", headers=headers)
+    assert response.status_code == codes.OK
+
+    # Also get antoher teams answers
+    response, body = get(client, f"/api/competitions/{competition_id}/teams/{team_id+1}/answers", headers=headers)
+    assert response.status_code == codes.OK
\ No newline at end of file
diff --git a/server/tests/test_helpers.py b/server/tests/test_helpers.py
index 9603fa59d5e8b11872367dd1a73c83e334e600f1..f55aa68251a5a3b9ea73411c959939acc93f1bc6 100644
--- a/server/tests/test_helpers.py
+++ b/server/tests/test_helpers.py
@@ -3,14 +3,14 @@ import json
 import app.core.http_codes as codes
 import app.database.controller as dbc
 from app.core import db
-from app.database.models import City, Role
+from app.database.models import City, Code, Role
 
 
 def add_default_values():
     media_types = ["Image", "Video"]
     question_types = ["Boolean", "Multiple", "Text"]
     component_types = ["Text", "Image"]
-    view_types = ["Team", "Judge", "Audience"]
+    view_types = ["Team", "Judge", "Audience", "Operator"]
 
     roles = ["Admin", "Editor"]
     cities = ["Linköping", "Testköping"]
@@ -40,6 +40,20 @@ def add_default_values():
 
     # Add competitions
     item_competition = dbc.add.competition("Tom tävling", 2012, item_city.id)
+
+    item_question = dbc.add.question("hej", 5, 1, item_competition.slides[0].id)
+
+    item_team1 = dbc.add.team("Hej lag 3", item_competition.id)
+    item_team2 = dbc.add.team("Hej lag 4", item_competition.id)
+
+    db.session.add(Code("111111", 1, item_competition.id, item_team1.id))  # Team
+    db.session.add(Code("222222", 2, item_competition.id))  # Judge
+
+    dbc.add.QuestionAnswer("hej", 5, item_question.id, item_team1)
+    dbc.add.QuestionAnswer("då", 5, item_question.id, item_team2)
+
+    db.session.commit()
+
     for j in range(2):
         item_comp = dbc.add.competition(f"Tävling {j}", 2012, item_city.id)
         # Add two more slides to competition