diff --git a/package-lock.json b/package-lock.json
index ee2c710acace40e8b5f781384980fabf0bb108ae..cc152e8216a58d74572e8c18ccc8c5359ccd71d4 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,6 +8,8 @@
       "name": "smartmat-frontend",
       "version": "0.0.0",
       "dependencies": {
+        "@vuelidate/core": "^2.0.2",
+        "@vuelidate/validators": "^2.0.2",
         "element-plus": "^2.3.3",
         "pinia": "^2.0.32",
         "vue": "^3.2.47",
@@ -1438,6 +1440,90 @@
         }
       }
     },
+    "node_modules/@vuelidate/core": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/@vuelidate/core/-/core-2.0.2.tgz",
+      "integrity": "sha512-aG1OZWv6xVws3ljyKy/pyxq1rdZZ2ryj+FEREcC9d4GP4qOvNHHZUl/NQxa0Bck3Ooc0RfXU8vwCA9piRoWy6w==",
+      "dependencies": {
+        "vue-demi": "^0.13.11"
+      },
+      "peerDependencies": {
+        "@vue/composition-api": "^1.0.0-rc.1",
+        "vue": "^2.0.0 || >=3.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@vue/composition-api": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@vuelidate/core/node_modules/vue-demi": {
+      "version": "0.13.11",
+      "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.11.tgz",
+      "integrity": "sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==",
+      "hasInstallScript": true,
+      "bin": {
+        "vue-demi-fix": "bin/vue-demi-fix.js",
+        "vue-demi-switch": "bin/vue-demi-switch.js"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      },
+      "peerDependencies": {
+        "@vue/composition-api": "^1.0.0-rc.1",
+        "vue": "^3.0.0-0 || ^2.6.0"
+      },
+      "peerDependenciesMeta": {
+        "@vue/composition-api": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@vuelidate/validators": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/@vuelidate/validators/-/validators-2.0.2.tgz",
+      "integrity": "sha512-6y6QLoK567XVmaLP3Paf1vkg6K2zO6xax3yTyczy1RnJ4PsLDLLGzP1PFzSpwb16aw4CKduBgI63HvIuctJhQg==",
+      "dependencies": {
+        "vue-demi": "^0.13.11"
+      },
+      "peerDependencies": {
+        "@vue/composition-api": "^1.0.0-rc.1",
+        "vue": "^2.0.0 || >=3.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@vue/composition-api": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@vuelidate/validators/node_modules/vue-demi": {
+      "version": "0.13.11",
+      "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.11.tgz",
+      "integrity": "sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==",
+      "hasInstallScript": true,
+      "bin": {
+        "vue-demi-fix": "bin/vue-demi-fix.js",
+        "vue-demi-switch": "bin/vue-demi-switch.js"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      },
+      "peerDependencies": {
+        "@vue/composition-api": "^1.0.0-rc.1",
+        "vue": "^3.0.0-0 || ^2.6.0"
+      },
+      "peerDependenciesMeta": {
+        "@vue/composition-api": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/@vueuse/core": {
       "version": "9.13.0",
       "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-9.13.0.tgz",
diff --git a/package.json b/package.json
index 7fba60885018228333a821447061540dfd892651..789b6e62a592b64662e0b6e0d4b8fc6f03732ee4 100644
--- a/package.json
+++ b/package.json
@@ -17,6 +17,8 @@
     "format:check": "prettier --check src/ cypress"
   },
   "dependencies": {
+    "@vuelidate/core": "^2.0.2",
+    "@vuelidate/validators": "^2.0.2",
     "element-plus": "^2.3.3",
     "pinia": "^2.0.32",
     "vue": "^3.2.47",
diff --git a/src/App.vue b/src/App.vue
index 68dbc0376c0a46a6bcb6d194f572efafb550fca0..2034b8eabd5a5f3430dbe8dd9e6cae947f2facfc 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -1,10 +1,12 @@
 <script setup lang="ts">
+import { computed, onMounted, ref } from "vue";
+import { Expand } from "@element-plus/icons-vue";
+import { useToggle } from "@vueuse/core";
 import { RouterView } from "vue-router";
+import router from "@/router";
+
 import SideNavBar from "@/components/SideNavBar.vue";
 import TopNavBar from "@/components/TopNavBar.vue";
-import { computed, onMounted, ref } from "vue";
-import { useToggle } from "@vueuse/core";
-import { Expand } from "@element-plus/icons-vue";
 
 const windowSize = ref(window.innerWidth);
 
@@ -19,6 +21,8 @@ const collapsed = computed(() => {
 });
 
 const [drawer, drawerToggle] = useToggle();
+
+const isFullScreen = computed(() => router.currentRoute.value.meta?.fullScreen);
 </script>
 
 <template>
@@ -38,8 +42,8 @@ const [drawer, drawerToggle] = useToggle();
         </el-menu-item>
       </el-menu>
     </div>
-    <el-container style="height: 0">
-      <el-aside width="300px" v-if="!collapsed">
+    <el-container>
+      <el-aside width="300px" v-if="!collapsed && !isFullScreen">
         <SideNavBar class="sidenav" />
       </el-aside>
       <el-drawer v-model="drawer" direction="ltr" size="306px">
diff --git a/src/assets/main.css b/src/assets/main.css
index 10a8bb4d013c264fc68aac1b04bffdbadd9cb439..ab5ff62a0a48937d71342491adf8c1ca83955037 100644
--- a/src/assets/main.css
+++ b/src/assets/main.css
@@ -23,6 +23,6 @@ main {
 }
 @media only screen and (max-width: 768px) {
   main {
-    padding: 1rem !important;
+    padding: 0.5rem !important;
   }
 }
diff --git a/src/components/RegisterComponent.vue b/src/components/RegisterComponent.vue
new file mode 100644
index 0000000000000000000000000000000000000000..fa65ee8813e2e529c36439077c9bd8afc2a67290
--- /dev/null
+++ b/src/components/RegisterComponent.vue
@@ -0,0 +1,131 @@
+<template>
+  <el-card class="container">
+    <h2 class="my-3">Registrer deg</h2>
+
+    <el-form
+      ref="ruleFormRef"
+      label-position="top"
+      :model="newUser"
+      :rules="validationRules"
+      status-icon
+    >
+      <el-form-item label="Email" prop="email">
+        <el-input placeholder="Email" type="text" v-model="email" size="large" />
+      </el-form-item>
+
+      <el-form-item label="Fornavn" prop="firstName">
+        <el-input placeholder="fornavn" type="text" v-model="firstName" size="large" />
+      </el-form-item>
+
+      <el-form-item label="Passord" prop="password">
+        <el-input type="password" v-model="password" placeholder="Password" size="large" />
+      </el-form-item>
+
+      <el-form-item label="Gjenta passord" prop="passwordConfirm">
+        <el-input type="password" v-model="passwordConfirm" placeholder="Password" size="large" />
+      </el-form-item>
+
+      <el-button ref="submitButton" type="primary" size="large" @click="submit"
+        >Registrer deg
+      </el-button>
+    </el-form>
+    <el-alert
+      type="error"
+      show-icon
+      :title="errorMessage"
+      v-if="errorMessage"
+      style="margin-top: 1rem"
+    />
+  </el-card>
+</template>
+<script setup lang="ts">
+import type { CreateUser } from "@/services";
+import { computed, ref } from "vue";
+import type { FormInstance } from "element-plus";
+
+const ruleFormRef = ref<FormInstance>();
+const props = defineProps<{
+  firstName: string;
+  password: string;
+  email: string;
+  errorMessage: string;
+}>();
+
+const emit = defineEmits<{
+  "update:firstName": (firstName: string) => void;
+  "update:password": (password: string) => void;
+  "update:email": (email: string) => void;
+  submit: () => void;
+}>();
+
+const passwordConfirm = ref<string>("");
+
+const validationRules = ref({
+  firstName: [
+    { required: true, message: "Fornavn er påkrevd", trigger: "blur" },
+    { min: 2, message: "Fornavn må være minst 2 tegn", trigger: "blur" },
+  ],
+  email: [
+    { required: true, message: "Email er påkrevd", trigger: "blur" },
+    { type: "email", message: "Email må være en gyldig email", trigger: "blur" },
+  ],
+  password: [
+    { required: true, message: "Passord er påkrevd", trigger: "blur" },
+    { min: 8, message: "Passord må være minst 8 tegn", trigger: "blur" },
+  ],
+  passwordConfirm: [
+    { required: true, message: "Gjenta passordet", trigger: "blur" },
+    { min: 8, message: "Passord må være minst 8 tegn", trigger: "blur" },
+    {
+      validator: (rule, value, callback) => {
+        if (value !== password.value) {
+          callback(new Error("Passordene må være like"));
+        } else {
+          callback();
+        }
+      },
+      trigger: "blur",
+    },
+  ],
+});
+
+const firstName = computed({
+  get: () => props.firstName,
+  set: (value: string) => emit("update:firstName", value),
+});
+
+const password = computed({
+  get: () => props.password,
+  set: (value: string) => emit("update:password", value),
+});
+
+const email = computed({
+  get: () => props.email,
+  set: (value: string) => emit("update:email", value),
+});
+
+const newUser = computed(() => {
+  return {
+    firstName: props.firstName,
+    password: props.password,
+    email: props.email,
+    passwordConfirm: passwordConfirm.value,
+  } as CreateUser;
+});
+
+function submit() {
+  if (!ruleFormRef.value) return;
+  ruleFormRef.value.validate((valid) => {
+    if (valid) {
+      emit("submit");
+    }
+  });
+}
+</script>
+<style scoped>
+.container {
+  width: 90%;
+  margin: 10vh auto;
+  max-width: 500px;
+}
+</style>
diff --git a/src/components/TopNavBar.vue b/src/components/TopNavBar.vue
index 01841189da3b2bcf42d8988ff1f25ec176075491..54e59d27eb6a7ec0d33dcdc6c3f36105d99877d8 100644
--- a/src/components/TopNavBar.vue
+++ b/src/components/TopNavBar.vue
@@ -8,13 +8,13 @@
     >
       <el-menu-item index="0">LOGO</el-menu-item>
       <div class="flex-grow" />
-      <el-menu-item index="1">
+      <el-menu-item index="1" v-if="sessionStore.isAuthenticated">
         <el-icon>
           <User />
         </el-icon>
         <span>Profil</span>
       </el-menu-item>
-      <el-button class="menu-item-button">
+      <el-button class="menu-item-button" v-if="sessionStore.isAuthenticated" @click="logOut">
         <el-icon>
           <TurnOff />
         </el-icon>
@@ -26,11 +26,18 @@
 <script lang="ts" setup>
 import { ref } from "vue";
 import { TurnOff, User } from "@element-plus/icons-vue";
+import { useSessionStore } from "@/stores/session";
 
 const activeIndex = ref("1");
 const handleSelect = (key: string, keyPath: string[]) => {
   console.log(key, keyPath);
 };
+
+const sessionStore = useSessionStore();
+
+function logOut() {
+  sessionStore.logOut();
+}
 </script>
 
 <style scoped>
diff --git a/src/router/index.ts b/src/router/index.ts
index a62fe31f401c78e9503694477fc5e13eddea16d1..86f211f898fa9725aa56dd2692b5a9834d0e6983 100644
--- a/src/router/index.ts
+++ b/src/router/index.ts
@@ -1,7 +1,10 @@
 import { createRouter, createWebHistory } from "vue-router";
 import HomeView from "../views/HomeView.vue";
 import NotFoundView from "../views/NotFoundView.vue";
+import { useSessionStore } from "@/stores/session";
+import { AccountApi } from "@/services/index";
 
+let startup = true;
 const router = createRouter({
   history: createWebHistory(import.meta.env.BASE_URL),
   routes: [
@@ -9,23 +12,68 @@ const router = createRouter({
       path: "/",
       name: "home",
       component: HomeView,
-    },
-    {
-      path: "/:pathMatch(.*)*",
-      name: "not-found",
-      component: NotFoundView,
+      meta: {
+        requiresAuth: true,
+      },
     },
     {
       path: "/shopping-list",
       name: "shopping-list",
-      component: () => import("../views/ShoppingListView.vue"),
+      component: () => import("@/views/ShoppingListView.vue"),
+      meta: {
+        requiresAuth: true,
+      },
     },
     {
       path: "/inventory",
       name: "inventory",
       component: () => import("@/views/InventoryView.vue"),
+      meta: {
+        requiresAuth: true,
+      },
+    },
+    {
+      path: "/login",
+      name: "login",
+      component: () => import("@/views/LoginView.vue"),
+      meta: {
+        fullScreen: true,
+      },
+    },
+    {
+      path: "/register",
+      name: "register",
+      component: () => import("@/views/RegisterView.vue"),
+      meta: {
+        fullScreen: true,
+      },
+    },
+    {
+      path: "/:pathMatch(.*)*",
+      name: "not-found",
+      component: NotFoundView,
     },
   ],
 });
 
+router.beforeEach((to, from, next) => {
+  const sessionStore = useSessionStore();
+  /*
+  const accountApi = new AccountApi();
+  if (startup) {
+    accountApi.getUserById().then((data) => {
+      if (data.status == 200) {
+        sessionStore.authenticate(data.data);
+      }
+    });
+    startup = false;
+  }
+   */
+  if (to.meta.requiresAuth && !sessionStore.isAuthenticated) {
+    next({ name: "login" });
+  } else {
+    next();
+  }
+});
+
 export default router;
diff --git a/src/services/models/create-user.ts b/src/services/models/create-user.ts
index f4a42fe170ad473aa2012d9af5c6fd59f64369c0..010e4e90ae932684e2bb3fa3cf01f1d8472b5386 100644
--- a/src/services/models/create-user.ts
+++ b/src/services/models/create-user.ts
@@ -29,6 +29,8 @@ export interface CreateUser {
    * @memberof CreateUser
    */
   password?: string;
+
+  passwordConfirm?: string;
   /**
    *
    * @type {string}
diff --git a/src/views/LoginView.vue b/src/views/LoginView.vue
new file mode 100644
index 0000000000000000000000000000000000000000..5fcd9fccebd34ff6b592fbcd610a80de519af770
--- /dev/null
+++ b/src/views/LoginView.vue
@@ -0,0 +1,116 @@
+<template>
+  <el-card class="container">
+    <h2>Log in</h2>
+
+    <el-form ref="form" label-position="top" :model="data" class="login-form">
+      <el-form-item label="Email" prop="email">
+        <el-input
+          placeholder="Email"
+          type="text"
+          v-model="data.email"
+          size="large"
+          @input="errorMessage = ''"
+        />
+      </el-form-item>
+
+      <el-form-item label="Password" prop="password">
+        <el-input
+          type="password"
+          v-model="data.password"
+          @input="errorMessage = ''"
+          placeholder="Password"
+          size="large"
+        />
+      </el-form-item>
+
+      <p class="no-account">
+        Har du ikke konto?
+        <el-link type="primary" @click="router.push({ name: 'register' })">Registrer deg! </el-link>
+      </p>
+
+      <el-button ref="submitButton" type="primary" size="large" @click="signIn"
+        >Logg inn
+      </el-button>
+      <el-alert
+        type="error"
+        v-if="errorMessage"
+        show-icon
+        closable
+        style="margin-top: 1rem"
+        :title="errorMessage"
+      >
+      </el-alert>
+    </el-form>
+  </el-card>
+</template>
+
+<script setup lang="ts">
+import { reactive, ref } from "vue";
+
+import type { LoginUser } from "@/services/index";
+import { AccountApi, HouseholdApi } from "@/services/index";
+import { useSessionStore } from "@/stores/session";
+import { showError } from "@/utils/error-utils";
+import router from "@/router";
+import { useHouseholdStore } from "@/stores/household";
+
+// Define APIs
+const accountApi = new AccountApi();
+const householdApi = new HouseholdApi();
+
+// Define stores
+const sessionStore = useSessionStore();
+const householdStore = useHouseholdStore();
+
+// Define refs
+const submitButton = ref<HTMLElement | null>(null);
+
+const data: LoginUser = reactive({
+  email: "",
+  password: "",
+});
+
+const errorMessage = ref<string>("");
+
+// Define callbacks
+function signIn() {
+  accountApi
+    .loginUser(data)
+    .then((response) => response.data)
+    .then((data) => {
+      sessionStore.authenticate(data);
+
+      // Get household the first household
+      householdApi
+        .getHouseholds(data.id!)
+        .then((response) => response.data)
+        .then((households) => {
+          householdStore.setHousehold(households[0]);
+          router.push({ name: "inventory" });
+        });
+    })
+    .catch((error) => {
+      if (error.response.status === 401) {
+        errorMessage.value = "Feil brukernavn eller passord";
+      } else {
+        errorMessage.value = "En uventet feil oppstod";
+      }
+    });
+}
+</script>
+
+<style scoped>
+.container {
+  width: 90%;
+  margin: 10vh auto;
+  max-width: 500px;
+}
+
+.login-form {
+  flex-direction: column;
+}
+
+.no-account {
+  text-align: end;
+}
+</style>
diff --git a/src/views/RegisterView.vue b/src/views/RegisterView.vue
new file mode 100644
index 0000000000000000000000000000000000000000..963d3a70d0f5e765340921f4f1104412a6f56cdf
--- /dev/null
+++ b/src/views/RegisterView.vue
@@ -0,0 +1,49 @@
+<template>
+  <RegisterComponent
+    v-model:email="user.email"
+    v-model:first-name="user.firstName"
+    v-model:password="user.password"
+    @submit="submit"
+    :error-message="errorMessage"
+  >
+  </RegisterComponent>
+</template>
+<script setup lang="ts">
+import { reactive, ref } from "vue";
+import RegisterComponent from "@/components/RegisterComponent.vue";
+import type { CreateUser } from "@/services/index";
+import { AccountApi } from "@/services/index";
+import { useSessionStore } from "@/stores/session";
+import router from "@/router";
+import { showError } from "@/utils/error-utils";
+
+const user = reactive({
+  email: "",
+  password: "",
+  firstName: "",
+} as CreateUser);
+
+const accountApi = new AccountApi();
+const sessionStore = useSessionStore();
+const errorMessage = ref<string>("");
+
+function submit() {
+  console.log("submitting");
+  accountApi
+    .createUser(user)
+    .then((data) => {
+      sessionStore.authenticate(data.data);
+      router.push({ name: "home" });
+    })
+    .catch((error) => {
+      if (error.response.status === 409) {
+        errorMessage.value = "En bruker med denne eposten eksisterer allerede";
+      } else {
+        errorMessage.value = "En uventet feil oppstod";
+      }
+      setTimeout(() => {
+        errorMessage.value = "";
+      }, 5000);
+    });
+}
+</script>