From 0e16c5f48319a95640730ed5e3b8d90608517a5b Mon Sep 17 00:00:00 2001
From: Jonny Ngo Luong <jonnynl@stud.ntnu.no>
Date: Mon, 22 Feb 2021 01:17:40 +0100
Subject: [PATCH] feat: added jwt token as session and authentication token
 (#9)

---
 .gitignore                                    |   5 +
 client/src/app/app.module.ts                  |   3 +-
 client/src/app/authentication/auth.module.ts  |  12 +
 .../app/authentication/auth.service.spec.ts   |  16 ++
 client/src/app/authentication/auth.service.ts |  50 ++++
 .../user-login-form.component.ts              |  15 +-
 .../user-profile/user-profile.component.html  |   3 +
 .../user-profile/user-profile.component.ts    |  19 +-
 server/package-lock.json                      | 218 ++++++++++++++++++
 server/package.json                           |   3 +
 server/src/config.ts                          |  11 +-
 .../src/controllers/userController/index.ts   |  18 +-
 server/src/index.ts                           |   6 +-
 13 files changed, 368 insertions(+), 11 deletions(-)
 create mode 100644 client/src/app/authentication/auth.module.ts
 create mode 100644 client/src/app/authentication/auth.service.spec.ts
 create mode 100644 client/src/app/authentication/auth.service.ts

diff --git a/.gitignore b/.gitignore
index 42b7fa6..3c05752 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,3 +18,8 @@ yarn-error.log*
 
 .idea
 .vscode
+
+# keys and .env
+jwtRS256.key
+jwtRS256.key.pub
+server/.env
\ No newline at end of file
diff --git a/client/src/app/app.module.ts b/client/src/app/app.module.ts
index 28a13e7..497c6e4 100644
--- a/client/src/app/app.module.ts
+++ b/client/src/app/app.module.ts
@@ -6,6 +6,7 @@ import { AppRoutingModule } from './app-routing.module';
 import { AppComponent } from './app.component';
 import { PostModule } from './posts/post.module';
 import { UserModule } from './users/user.module';
+import { AuthModule } from './authentication/auth.module';
 import { SharedModule } from './shared/shared.module';
 
 
@@ -16,7 +17,7 @@ import { SharedModule } from './shared/shared.module';
   imports: [
     BrowserModule,
     UserModule,
-
+    AuthModule,
     AppRoutingModule,
     PostModule,
     SharedModule,
diff --git a/client/src/app/authentication/auth.module.ts b/client/src/app/authentication/auth.module.ts
new file mode 100644
index 0000000..d2e4fae
--- /dev/null
+++ b/client/src/app/authentication/auth.module.ts
@@ -0,0 +1,12 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+
+
+
+@NgModule({
+  declarations: [],
+  imports: [
+    CommonModule
+  ]
+})
+export class AuthModule { }
diff --git a/client/src/app/authentication/auth.service.spec.ts b/client/src/app/authentication/auth.service.spec.ts
new file mode 100644
index 0000000..f1251ca
--- /dev/null
+++ b/client/src/app/authentication/auth.service.spec.ts
@@ -0,0 +1,16 @@
+import { TestBed } from '@angular/core/testing';
+
+import { AuthService } from './auth.service';
+
+describe('AuthService', () => {
+  let service: AuthService;
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({});
+    service = TestBed.inject(AuthService);
+  });
+
+  it('should be created', () => {
+    expect(service).toBeTruthy();
+  });
+});
diff --git a/client/src/app/authentication/auth.service.ts b/client/src/app/authentication/auth.service.ts
new file mode 100644
index 0000000..626aff1
--- /dev/null
+++ b/client/src/app/authentication/auth.service.ts
@@ -0,0 +1,50 @@
+import { HttpClient, HttpEvent, HttpInterceptor, HttpResponse } from '@angular/common/http';
+import { Injectable } from '@angular/core';
+import { User } from '../models/user.model';
+import { tap, shareReplay } from 'rxjs/operators';
+
+interface IUserLogin {
+  username: string;
+  password: string;
+}
+
+@Injectable({
+  providedIn: 'root'
+})
+export class AuthService {
+  loginUrl = "api/user/login"
+
+  constructor(private http: HttpClient) { }
+
+  login(body: IUserLogin): Promise<string> {
+    return new Promise<string>(
+      (resolve, reject) => {
+        this.login_user(body).subscribe((data: any) => {
+          try {
+            resolve(data.token);
+          } catch (err: any) {
+            reject(err);
+          }
+        },
+        (err: any) => {
+          console.log(err.message);
+          reject(err);
+        });
+      }
+    );
+  }
+  private login_user(body: IUserLogin) {
+    return this.http.post(this.loginUrl, body).pipe(
+        tap(res =>this.setSession(res)),
+        shareReplay());;
+  }
+  private setSession(authResult) {
+    console.log(authResult);
+    localStorage.setItem('token', authResult.token);
+  }
+
+  logout() {
+    localStorage.removeItem("token");
+  }
+
+}
diff --git a/client/src/app/users/user-login-form/user-login-form.component.ts b/client/src/app/users/user-login-form/user-login-form.component.ts
index 24057cb..328d380 100644
--- a/client/src/app/users/user-login-form/user-login-form.component.ts
+++ b/client/src/app/users/user-login-form/user-login-form.component.ts
@@ -2,6 +2,7 @@ import { Component, OnInit } from '@angular/core';
 import { Router } from '@angular/router';
 import { User } from 'src/app/models/user.model';
 import { UserService } from '../user.service';
+import { AuthService } from '../../authentication/auth.service';
 
 @Component({
   selector: 'app-user-login-form',
@@ -14,7 +15,7 @@ export class UserLoginFormComponent implements OnInit {
 
   statusMessage: string = "";
 
-  constructor(private userService: UserService, private router: Router) { }
+  constructor(private userService: UserService, private authService: AuthService, private router: Router) { }
 
   ngOnInit(): void {
   }
@@ -46,13 +47,21 @@ export class UserLoginFormComponent implements OnInit {
         password: this.password,
       };
 
-      // Adds user to database and changes page afterwards
+      // Login the user
+      this.authService.login(request).then(status => {
+        console.log("User login1: " + JSON.stringify(status));
+        this.router.navigateByUrl("/");
+      }).catch(error => {
+        console.log("Error user login: " + error);
+      });
+      /* Old
       this.userService.login(request).then(status => {
-        console.log("User login: " + JSON.stringify(status));
+        console.log("User login2: " + JSON.stringify(status));
         this.router.navigateByUrl("/");
       }).catch(error => {
         console.log("Error adding user: " + error);
       });
+      */
     }
   }
 
diff --git a/client/src/app/users/user-profile/user-profile.component.html b/client/src/app/users/user-profile/user-profile.component.html
index fedcb8b..9b8f350 100644
--- a/client/src/app/users/user-profile/user-profile.component.html
+++ b/client/src/app/users/user-profile/user-profile.component.html
@@ -1 +1,4 @@
 <p>user-profile works!</p>
+<p>Userid: {{user.getUserId}}</p>
+<p>username: {{user.getUsername}}</p>
+<p>email: {{user.getEmail}}</p>
\ No newline at end of file
diff --git a/client/src/app/users/user-profile/user-profile.component.ts b/client/src/app/users/user-profile/user-profile.component.ts
index d7ff774..d818b70 100644
--- a/client/src/app/users/user-profile/user-profile.component.ts
+++ b/client/src/app/users/user-profile/user-profile.component.ts
@@ -1,4 +1,9 @@
 import { Component, OnInit } from '@angular/core';
+import { Router } from '@angular/router';
+import { AuthService } from 'src/app/authentication/auth.service';
+import { User } from 'src/app/models/user.model';
+import { UserService } from '../user.service';
+
 
 @Component({
   selector: 'app-user-profile',
@@ -7,9 +12,21 @@ import { Component, OnInit } from '@angular/core';
 })
 export class UserProfileComponent implements OnInit {
 
-  constructor() { }
+  user: User = new User();
+  constructor(private userService: UserService, private authService: AuthService, private router: Router) { }
 
   ngOnInit(): void {
+    const token = localStorage.getItem('token');
+    if (!token) this.router.navigateByUrl("/");
+    // Get user data from JWT token
+    const user_data = JSON.parse(atob(token.split(".")[1])).data[0];
+    console.log(user_data)
+    // Gets all categories and displays them in dropdown
+    this.userService.getUser(user_data.userId).then(user => {
+      this.user = user;
+    }).catch (error => {
+      console.log("Error getting user: " + error);
+    });
   }
 
 }
diff --git a/server/package-lock.json b/server/package-lock.json
index 4908b6d..95c60c7 100644
--- a/server/package-lock.json
+++ b/server/package-lock.json
@@ -15,14 +15,17 @@
         "@types/supertest": "^2.0.10",
         "body-parser": "^1.19.0",
         "cors": "^2.8.5",
+        "dotenv": "^8.2.0",
         "express": "^4.17.1",
         "jest": "^26.6.3",
+        "jsonwebtoken": "^8.5.1",
         "mysql": "^2.18.1",
         "mysql2": "^2.2.5",
         "supertest": "^6.1.3",
         "ts-jest": "^26.5.1"
       },
       "devDependencies": {
+        "@types/jsonwebtoken": "^8.5.0",
         "nodemon": "^2.0.7",
         "ts-node": "^9.1.1",
         "typescript": "^4.1.3"
@@ -1062,6 +1065,15 @@
         "pretty-format": "^26.0.0"
       }
     },
+    "node_modules/@types/jsonwebtoken": {
+      "version": "8.5.0",
+      "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.0.tgz",
+      "integrity": "sha512-9bVao7LvyorRGZCw0VmH/dr7Og+NdjYSsKAxB43OQoComFbBgsEpoR9JW6+qSq/ogwVBg8GI2MfAlk4SYI4OLg==",
+      "dev": true,
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
     "node_modules/@types/mime": {
       "version": "1.3.2",
       "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
@@ -1697,6 +1709,11 @@
         "node-int64": "^0.4.0"
       }
     },
+    "node_modules/buffer-equal-constant-time": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
+      "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk="
+    },
     "node_modules/buffer-from": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
@@ -2387,6 +2404,14 @@
         "node": ">=8"
       }
     },
+    "node_modules/dotenv": {
+      "version": "8.2.0",
+      "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz",
+      "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==",
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/duplexer3": {
       "version": "0.1.4",
       "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz",
@@ -2402,6 +2427,14 @@
         "safer-buffer": "^2.1.0"
       }
     },
+    "node_modules/ecdsa-sig-formatter": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
+      "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
+      "dependencies": {
+        "safe-buffer": "^5.0.1"
+      }
+    },
     "node_modules/ee-first": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@@ -4904,6 +4937,32 @@
         "node": ">=6"
       }
     },
+    "node_modules/jsonwebtoken": {
+      "version": "8.5.1",
+      "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz",
+      "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==",
+      "dependencies": {
+        "jws": "^3.2.2",
+        "lodash.includes": "^4.3.0",
+        "lodash.isboolean": "^3.0.3",
+        "lodash.isinteger": "^4.0.4",
+        "lodash.isnumber": "^3.0.3",
+        "lodash.isplainobject": "^4.0.6",
+        "lodash.isstring": "^4.0.1",
+        "lodash.once": "^4.0.0",
+        "ms": "^2.1.1",
+        "semver": "^5.6.0"
+      },
+      "engines": {
+        "node": ">=4",
+        "npm": ">=1.4.28"
+      }
+    },
+    "node_modules/jsonwebtoken/node_modules/ms": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
+    },
     "node_modules/jsprim": {
       "version": "1.4.1",
       "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
@@ -4918,6 +4977,25 @@
         "verror": "1.10.0"
       }
     },
+    "node_modules/jwa": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
+      "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
+      "dependencies": {
+        "buffer-equal-constant-time": "1.0.1",
+        "ecdsa-sig-formatter": "1.0.11",
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "node_modules/jws": {
+      "version": "3.2.2",
+      "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
+      "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
+      "dependencies": {
+        "jwa": "^1.4.1",
+        "safe-buffer": "^5.0.1"
+      }
+    },
     "node_modules/keyv": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz",
@@ -4996,6 +5074,41 @@
       "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
       "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
     },
+    "node_modules/lodash.includes": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
+      "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8="
+    },
+    "node_modules/lodash.isboolean": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
+      "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY="
+    },
+    "node_modules/lodash.isinteger": {
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
+      "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M="
+    },
+    "node_modules/lodash.isnumber": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
+      "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w="
+    },
+    "node_modules/lodash.isplainobject": {
+      "version": "4.0.6",
+      "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+      "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs="
+    },
+    "node_modules/lodash.isstring": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
+      "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE="
+    },
+    "node_modules/lodash.once": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
+      "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w="
+    },
     "node_modules/lodash.sortby": {
       "version": "4.7.0",
       "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
@@ -8997,6 +9110,15 @@
         "pretty-format": "^26.0.0"
       }
     },
+    "@types/jsonwebtoken": {
+      "version": "8.5.0",
+      "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.0.tgz",
+      "integrity": "sha512-9bVao7LvyorRGZCw0VmH/dr7Og+NdjYSsKAxB43OQoComFbBgsEpoR9JW6+qSq/ogwVBg8GI2MfAlk4SYI4OLg==",
+      "dev": true,
+      "requires": {
+        "@types/node": "*"
+      }
+    },
     "@types/mime": {
       "version": "1.3.2",
       "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
@@ -9501,6 +9623,11 @@
         "node-int64": "^0.4.0"
       }
     },
+    "buffer-equal-constant-time": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
+      "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk="
+    },
     "buffer-from": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
@@ -10039,6 +10166,11 @@
         "is-obj": "^2.0.0"
       }
     },
+    "dotenv": {
+      "version": "8.2.0",
+      "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz",
+      "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw=="
+    },
     "duplexer3": {
       "version": "0.1.4",
       "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz",
@@ -10054,6 +10186,14 @@
         "safer-buffer": "^2.1.0"
       }
     },
+    "ecdsa-sig-formatter": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
+      "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
+      "requires": {
+        "safe-buffer": "^5.0.1"
+      }
+    },
     "ee-first": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@@ -11928,6 +12068,30 @@
         "minimist": "^1.2.5"
       }
     },
+    "jsonwebtoken": {
+      "version": "8.5.1",
+      "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz",
+      "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==",
+      "requires": {
+        "jws": "^3.2.2",
+        "lodash.includes": "^4.3.0",
+        "lodash.isboolean": "^3.0.3",
+        "lodash.isinteger": "^4.0.4",
+        "lodash.isnumber": "^3.0.3",
+        "lodash.isplainobject": "^4.0.6",
+        "lodash.isstring": "^4.0.1",
+        "lodash.once": "^4.0.0",
+        "ms": "^2.1.1",
+        "semver": "^5.6.0"
+      },
+      "dependencies": {
+        "ms": {
+          "version": "2.1.3",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+          "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
+        }
+      }
+    },
     "jsprim": {
       "version": "1.4.1",
       "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
@@ -11939,6 +12103,25 @@
         "verror": "1.10.0"
       }
     },
+    "jwa": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
+      "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
+      "requires": {
+        "buffer-equal-constant-time": "1.0.1",
+        "ecdsa-sig-formatter": "1.0.11",
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "jws": {
+      "version": "3.2.2",
+      "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
+      "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
+      "requires": {
+        "jwa": "^1.4.1",
+        "safe-buffer": "^5.0.1"
+      }
+    },
     "keyv": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz",
@@ -11999,6 +12182,41 @@
       "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
       "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
     },
+    "lodash.includes": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
+      "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8="
+    },
+    "lodash.isboolean": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
+      "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY="
+    },
+    "lodash.isinteger": {
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
+      "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M="
+    },
+    "lodash.isnumber": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
+      "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w="
+    },
+    "lodash.isplainobject": {
+      "version": "4.0.6",
+      "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+      "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs="
+    },
+    "lodash.isstring": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
+      "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE="
+    },
+    "lodash.once": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
+      "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w="
+    },
     "lodash.sortby": {
       "version": "4.7.0",
       "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
diff --git a/server/package.json b/server/package.json
index ddee435..7b7e5cc 100644
--- a/server/package.json
+++ b/server/package.json
@@ -17,14 +17,17 @@
     "@types/supertest": "^2.0.10",
     "body-parser": "^1.19.0",
     "cors": "^2.8.5",
+    "dotenv": "^8.2.0",
     "express": "^4.17.1",
     "jest": "^26.6.3",
+    "jsonwebtoken": "^8.5.1",
     "mysql": "^2.18.1",
     "mysql2": "^2.2.5",
     "supertest": "^6.1.3",
     "ts-jest": "^26.5.1"
   },
   "devDependencies": {
+    "@types/jsonwebtoken": "^8.5.0",
     "nodemon": "^2.0.7",
     "ts-node": "^9.1.1",
     "typescript": "^4.1.3"
diff --git a/server/src/config.ts b/server/src/config.ts
index 3ca155c..f2dc97f 100644
--- a/server/src/config.ts
+++ b/server/src/config.ts
@@ -1,6 +1,8 @@
+const { config } = require('dotenv');
+config({ path: __dirname+'/../.env'});
 const env = process.env;
 
-const config = {
+export default {
 	db: {
 		host: env.DB_HOST || 'mysql.stud.ntnu.no',
 		user: env.DB_USER || 'jonnynl_tdt4140',
@@ -12,6 +14,9 @@ const config = {
 		debug: false
 	},
 	listPerPage: 10,
+	JWT_KEY : env.JWT_KEY || "",
+	HOST: env.HOST || "localhost",
+    PORT: env.HTTPPORT || 3000,
+    ACCESS_TOKEN_SECRET: env.ACCESS_TOKEN_SECRET,
+    REFRESH_TOKEN_SECRET: env.REFRESH_TOKEN_SECRET,
 };
-
-export default config;
diff --git a/server/src/controllers/userController/index.ts b/server/src/controllers/userController/index.ts
index f45e65b..6f2b01e 100644
--- a/server/src/controllers/userController/index.ts
+++ b/server/src/controllers/userController/index.ts
@@ -2,6 +2,8 @@ import { Response, Request } from "express";
 import query from '../../services/db_query';
 import express from 'express';
 import IUser from '../../models/user';
+import * as jwt from 'jsonwebtoken';
+import config from '../../config';
 
 const router = express.Router();
 /* ============================= CREATE ============================= */
@@ -50,9 +52,23 @@ router.route('/login').post(async (request: Request, response: Response) => {
 	const {username, password} = request.body;
 	try {
 		const input = "SELECT userId, username, email, create_time FROM user WHERE username=? AND password=?;"
-		response.status(200).json(await query(input,[username, password]));
+		const user = await query(input,[username, password]);
+		// Check if an user object is retrieved
+		const userObj = Object.values(JSON.parse(JSON.stringify(user.data)))[0];
+		if (userObj) {
+			const jwt_token = jwt.sign({data: user.data}, config.JWT_KEY.replace(/\\n/gm, '\n'), {
+				algorithm: 'RS256',
+				expiresIn: 3600*24, // 24 hours
+			});
+			response.status(200).json({
+				token: jwt_token,
+			});
+		} else {
+			response.status(403).send("Invalid combination of username and password given!");
+		}
 	} catch (error) {
 		response.status(400).send("Bad Request");
+		console.log(error);
 	}
 });
 
diff --git a/server/src/index.ts b/server/src/index.ts
index d4b4977..77bda9f 100644
--- a/server/src/index.ts
+++ b/server/src/index.ts
@@ -1,8 +1,10 @@
 import app from './app';
+import config from './config';
 
 // REST API config
-const port = 3000;
+const port = config.PORT;
 
 app.listen(port, () => {
-	console.log(`Listening on port ${port}!`)
+	const host = config.HOST;
+	console.log('[*] Server listening at http://%s:%s \n', host, port);
 });
\ No newline at end of file
-- 
GitLab