diff --git a/api/.env.defaults b/api/.env.defaults
index ad9987672442b84904a71d8526b7e050267b3924..0320f1402f9ae2e2f1cfa4380a4445073470dece 100644
--- a/api/.env.defaults
+++ b/api/.env.defaults
@@ -1,3 +1,13 @@
+# ENGINE
 ENGINE_TYPE=exareme
 ENGINE_BASE_URL=http://127.0.0.1:8080/services/
+
+# SERVER
 GATEWAY_PORT=8081
+
+# AUTHENTICATION
+AUTH_SKIP=false
+AUTH_JWT_SECRET=SecretForDevPurposeOnly
+AUTH_JWT_TOKEN_EXPIRES_IN=2d
+AUTH_COOKIE_SAME_SITE=strict
+AUTH_COOKIE_SECURE=true
\ No newline at end of file
diff --git a/api/package-lock.json b/api/package-lock.json
index 3d4c786544e197a15f2560fff6c55d4c3107e612..8eef2433f6b4fe6ff3de3b3ffa3bfe2939bb1d0a 100644
--- a/api/package-lock.json
+++ b/api/package-lock.json
@@ -15,13 +15,19 @@
         "@nestjs/config": "^1.0.1",
         "@nestjs/core": "^8.0.0",
         "@nestjs/graphql": "^10.0.6",
+        "@nestjs/jwt": "^8.0.0",
+        "@nestjs/passport": "^8.2.1",
         "@nestjs/platform-express": "^8.0.0",
         "@nestjs/typeorm": "^8.0.2",
         "apollo-server-express": "^3.6.3",
         "axios": "^0.21.1",
+        "cookie-parser": "^1.4.6",
         "graphql": "^15.5.3",
         "graphql-type-json": "^0.3.2",
         "jsonata": "^1.8.5",
+        "passport": "^0.5.2",
+        "passport-jwt": "^4.0.0",
+        "passport-local": "^1.0.0",
         "reflect-metadata": "^0.1.13",
         "rimraf": "^3.0.2",
         "rxjs": "^7.2.0",
@@ -29,6 +35,7 @@
       },
       "devDependencies": {
         "@eclass/semantic-release-docker": "^3.0.0",
+        "@jest-mock/express": "^1.4.5",
         "@nestjs/cli": "^8.0.0",
         "@nestjs/schematics": "^8.0.0",
         "@nestjs/testing": "^8.2.2",
@@ -36,9 +43,12 @@
         "@semantic-release/changelog": "^6.0.1",
         "@semantic-release/git": "^10.0.1",
         "@semantic-release/gitlab": "^7.0.4",
+        "@types/cookie-parser": "^1.4.2",
         "@types/express": "^4.17.13",
         "@types/jest": "^27.0.1",
         "@types/node": "^16.0.0",
+        "@types/passport-jwt": "^3.0.6",
+        "@types/passport-local": "^1.0.34",
         "@types/supertest": "^2.0.11",
         "@typescript-eslint/eslint-plugin": "^4.28.2",
         "@typescript-eslint/parser": "^4.28.2",
@@ -47,6 +57,7 @@
         "eslint-plugin-prettier": "^3.4.0",
         "husky": "^7.0.2",
         "jest": "^27.0.6",
+        "jest-mock": "^27.5.1",
         "prettier": "^2.3.2",
         "supertest": "^6.1.3",
         "ts-jest": "^27.0.3",
@@ -1064,6 +1075,12 @@
         "node": ">=8"
       }
     },
+    "node_modules/@jest-mock/express": {
+      "version": "1.4.5",
+      "resolved": "https://registry.npmjs.org/@jest-mock/express/-/express-1.4.5.tgz",
+      "integrity": "sha512-bERM1jnutyH7VMahdaOHAKy7lgX47zJ7+RTz2eMz0wlCttd9CkhsKFEyoWmJBSz/ow0nVj3lCuRqLem4QDYFkQ==",
+      "dev": true
+    },
     "node_modules/@jest/console": {
       "version": "27.5.1",
       "resolved": "https://registry.npmjs.org/@jest/console/-/console-27.5.1.tgz",
@@ -1819,6 +1836,18 @@
         }
       }
     },
+    "node_modules/@nestjs/jwt": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/@nestjs/jwt/-/jwt-8.0.0.tgz",
+      "integrity": "sha512-fz2LQgYY2zmuD8S+8UE215anwKyXlnB/1FwJQLVR47clNfMeFMK8WCxmn6xdPhF5JKuV1crO6FVabb1qWzDxqQ==",
+      "dependencies": {
+        "@types/jsonwebtoken": "8.5.4",
+        "jsonwebtoken": "8.5.1"
+      },
+      "peerDependencies": {
+        "@nestjs/common": "^6.0.0 || ^7.0.0 || ^8.0.0"
+      }
+    },
     "node_modules/@nestjs/mapped-types": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-1.0.1.tgz",
@@ -1838,6 +1867,15 @@
         }
       }
     },
+    "node_modules/@nestjs/passport": {
+      "version": "8.2.1",
+      "resolved": "https://registry.npmjs.org/@nestjs/passport/-/passport-8.2.1.tgz",
+      "integrity": "sha512-HXEKMLX1x865+lsJB4srwKHBciDNAhWY1Ha+xbxYRbk7J5leGDoHJAmeqe+Wb3NDn5nkboggLV87t0q2mbYc8w==",
+      "peerDependencies": {
+        "@nestjs/common": "^6.0.0 || ^7.0.0 || ^8.0.0",
+        "passport": "^0.4.0 || ^0.5.0"
+      }
+    },
     "node_modules/@nestjs/platform-express": {
       "version": "8.4.0",
       "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-8.4.0.tgz",
@@ -2561,6 +2599,15 @@
         "@types/node": "*"
       }
     },
+    "node_modules/@types/cookie-parser": {
+      "version": "1.4.2",
+      "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.2.tgz",
+      "integrity": "sha512-uwcY8m6SDQqciHsqcKDGbo10GdasYsPCYkH3hVegj9qAah6pX5HivOnOuI3WYmyQMnOATV39zv/Ybs0bC/6iVg==",
+      "dev": true,
+      "dependencies": {
+        "@types/express": "*"
+      }
+    },
     "node_modules/@types/cookiejar": {
       "version": "2.1.2",
       "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.2.tgz",
@@ -2680,6 +2727,14 @@
       "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=",
       "dev": true
     },
+    "node_modules/@types/jsonwebtoken": {
+      "version": "8.5.4",
+      "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.4.tgz",
+      "integrity": "sha512-4L8msWK31oXwdtC81RmRBAULd0ShnAHjBuKT9MRQpjP0piNrZdXyTRcKY9/UIfhGeKIT4PvF5amOOUbbT/9Wpg==",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
     "node_modules/@types/keyv": {
       "version": "3.1.3",
       "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.3.tgz",
@@ -2722,6 +2777,47 @@
       "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==",
       "dev": true
     },
+    "node_modules/@types/passport": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.7.tgz",
+      "integrity": "sha512-JtswU8N3kxBYgo+n9of7C97YQBT+AYPP2aBfNGTzABqPAZnK/WOAaKfh3XesUYMZRrXFuoPc2Hv0/G/nQFveHw==",
+      "dev": true,
+      "dependencies": {
+        "@types/express": "*"
+      }
+    },
+    "node_modules/@types/passport-jwt": {
+      "version": "3.0.6",
+      "resolved": "https://registry.npmjs.org/@types/passport-jwt/-/passport-jwt-3.0.6.tgz",
+      "integrity": "sha512-cmAAMIRTaEwpqxlrZyiEY9kdibk94gP5KTF8AT1Ra4rWNZYHNMreqhKUEeC5WJtuN5SJZjPQmV+XO2P5PlnvNQ==",
+      "dev": true,
+      "dependencies": {
+        "@types/express": "*",
+        "@types/jsonwebtoken": "*",
+        "@types/passport-strategy": "*"
+      }
+    },
+    "node_modules/@types/passport-local": {
+      "version": "1.0.34",
+      "resolved": "https://registry.npmjs.org/@types/passport-local/-/passport-local-1.0.34.tgz",
+      "integrity": "sha512-PSc07UdYx+jhadySxxIYWuv6sAnY5e+gesn/5lkPKfBeGuIYn9OPR+AAEDq73VRUh6NBTpvE/iPE62rzZUslog==",
+      "dev": true,
+      "dependencies": {
+        "@types/express": "*",
+        "@types/passport": "*",
+        "@types/passport-strategy": "*"
+      }
+    },
+    "node_modules/@types/passport-strategy": {
+      "version": "0.2.35",
+      "resolved": "https://registry.npmjs.org/@types/passport-strategy/-/passport-strategy-0.2.35.tgz",
+      "integrity": "sha512-o5D19Jy2XPFoX2rKApykY15et3Apgax00RRLf0RUotPDUsYrQa7x4howLYr9El2mlUApHmCMv5CZ1IXqKFQ2+g==",
+      "dev": true,
+      "dependencies": {
+        "@types/express": "*",
+        "@types/passport": "*"
+      }
+    },
     "node_modules/@types/prettier": {
       "version": "2.4.4",
       "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.4.4.tgz",
@@ -3897,6 +3993,11 @@
         "ieee754": "^1.1.13"
       }
     },
+    "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.2",
       "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
@@ -4542,6 +4643,26 @@
         "node": ">= 0.6"
       }
     },
+    "node_modules/cookie-parser": {
+      "version": "1.4.6",
+      "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz",
+      "integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==",
+      "dependencies": {
+        "cookie": "0.4.1",
+        "cookie-signature": "1.0.6"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/cookie-parser/node_modules/cookie": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz",
+      "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
     "node_modules/cookie-signature": {
       "version": "1.0.6",
       "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
@@ -5094,6 +5215,14 @@
         "safe-buffer": "~5.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",
@@ -8312,6 +8441,54 @@
         "node": "*"
       }
     },
+    "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/semver": {
+      "version": "5.7.1",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+      "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+      "bin": {
+        "semver": "bin/semver"
+      }
+    },
+    "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": "4.1.1",
       "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.1.1.tgz",
@@ -8449,23 +8626,41 @@
       "integrity": "sha1-ZHYsSGGAglGKw99Mz11YhtriA0c=",
       "dev": true
     },
+    "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.ismatch": {
       "version": "4.4.0",
       "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz",
       "integrity": "sha1-dWy1FQyjum8RCFp4hJZF8Yj4Xzc=",
       "dev": true
     },
+    "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=",
-      "dev": true
+      "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=",
-      "dev": true
+      "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE="
     },
     "node_modules/lodash.memoize": {
       "version": "4.1.2",
@@ -8484,6 +8679,11 @@
       "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz",
       "integrity": "sha1-brGa5aHuHdnfC5aeZs4Lf6MLXmA="
     },
+    "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",
@@ -11875,6 +12075,50 @@
         "node": ">= 0.8"
       }
     },
+    "node_modules/passport": {
+      "version": "0.5.2",
+      "resolved": "https://registry.npmjs.org/passport/-/passport-0.5.2.tgz",
+      "integrity": "sha512-w9n/Ot5I7orGD4y+7V3EFJCQEznE5RxHamUxcqLT2QoJY0f2JdN8GyHonYFvN0Vz+L6lUJfVhrk2aZz2LbuREw==",
+      "dependencies": {
+        "passport-strategy": "1.x.x",
+        "pause": "0.0.1"
+      },
+      "engines": {
+        "node": ">= 0.4.0"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/jaredhanson"
+      }
+    },
+    "node_modules/passport-jwt": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/passport-jwt/-/passport-jwt-4.0.0.tgz",
+      "integrity": "sha512-BwC0n2GP/1hMVjR4QpnvqA61TxenUMlmfNjYNgK0ZAs0HK4SOQkHcSv4L328blNTLtHq7DbmvyNJiH+bn6C5Mg==",
+      "dependencies": {
+        "jsonwebtoken": "^8.2.0",
+        "passport-strategy": "^1.0.0"
+      }
+    },
+    "node_modules/passport-local": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz",
+      "integrity": "sha1-H+YyaMkudWBmJkN+O5BmYsFbpu4=",
+      "dependencies": {
+        "passport-strategy": "1.x.x"
+      },
+      "engines": {
+        "node": ">= 0.4.0"
+      }
+    },
+    "node_modules/passport-strategy": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz",
+      "integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ=",
+      "engines": {
+        "node": ">= 0.4.0"
+      }
+    },
     "node_modules/path-exists": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@@ -11921,6 +12165,11 @@
         "node": ">=8"
       }
     },
+    "node_modules/pause": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz",
+      "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10="
+    },
     "node_modules/picocolors": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
@@ -15925,6 +16174,12 @@
       "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==",
       "dev": true
     },
+    "@jest-mock/express": {
+      "version": "1.4.5",
+      "resolved": "https://registry.npmjs.org/@jest-mock/express/-/express-1.4.5.tgz",
+      "integrity": "sha512-bERM1jnutyH7VMahdaOHAKy7lgX47zJ7+RTz2eMz0wlCttd9CkhsKFEyoWmJBSz/ow0nVj3lCuRqLem4QDYFkQ==",
+      "dev": true
+    },
     "@jest/console": {
       "version": "27.5.1",
       "resolved": "https://registry.npmjs.org/@jest/console/-/console-27.5.1.tgz",
@@ -16463,12 +16718,27 @@
         "ws": "8.5.0"
       }
     },
+    "@nestjs/jwt": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/@nestjs/jwt/-/jwt-8.0.0.tgz",
+      "integrity": "sha512-fz2LQgYY2zmuD8S+8UE215anwKyXlnB/1FwJQLVR47clNfMeFMK8WCxmn6xdPhF5JKuV1crO6FVabb1qWzDxqQ==",
+      "requires": {
+        "@types/jsonwebtoken": "8.5.4",
+        "jsonwebtoken": "8.5.1"
+      }
+    },
     "@nestjs/mapped-types": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-1.0.1.tgz",
       "integrity": "sha512-NFvofzSinp00j5rzUd4tf+xi9od6383iY0JP7o0Bnu1fuItAUkWBgc4EKuIQ3D+c2QI3i9pG1kDWAeY27EMGtg==",
       "requires": {}
     },
+    "@nestjs/passport": {
+      "version": "8.2.1",
+      "resolved": "https://registry.npmjs.org/@nestjs/passport/-/passport-8.2.1.tgz",
+      "integrity": "sha512-HXEKMLX1x865+lsJB4srwKHBciDNAhWY1Ha+xbxYRbk7J5leGDoHJAmeqe+Wb3NDn5nkboggLV87t0q2mbYc8w==",
+      "requires": {}
+    },
     "@nestjs/platform-express": {
       "version": "8.4.0",
       "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-8.4.0.tgz",
@@ -17066,6 +17336,15 @@
         "@types/node": "*"
       }
     },
+    "@types/cookie-parser": {
+      "version": "1.4.2",
+      "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.2.tgz",
+      "integrity": "sha512-uwcY8m6SDQqciHsqcKDGbo10GdasYsPCYkH3hVegj9qAah6pX5HivOnOuI3WYmyQMnOATV39zv/Ybs0bC/6iVg==",
+      "dev": true,
+      "requires": {
+        "@types/express": "*"
+      }
+    },
     "@types/cookiejar": {
       "version": "2.1.2",
       "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.2.tgz",
@@ -17185,6 +17464,14 @@
       "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=",
       "dev": true
     },
+    "@types/jsonwebtoken": {
+      "version": "8.5.4",
+      "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.4.tgz",
+      "integrity": "sha512-4L8msWK31oXwdtC81RmRBAULd0ShnAHjBuKT9MRQpjP0piNrZdXyTRcKY9/UIfhGeKIT4PvF5amOOUbbT/9Wpg==",
+      "requires": {
+        "@types/node": "*"
+      }
+    },
     "@types/keyv": {
       "version": "3.1.3",
       "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.3.tgz",
@@ -17227,6 +17514,47 @@
       "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==",
       "dev": true
     },
+    "@types/passport": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.7.tgz",
+      "integrity": "sha512-JtswU8N3kxBYgo+n9of7C97YQBT+AYPP2aBfNGTzABqPAZnK/WOAaKfh3XesUYMZRrXFuoPc2Hv0/G/nQFveHw==",
+      "dev": true,
+      "requires": {
+        "@types/express": "*"
+      }
+    },
+    "@types/passport-jwt": {
+      "version": "3.0.6",
+      "resolved": "https://registry.npmjs.org/@types/passport-jwt/-/passport-jwt-3.0.6.tgz",
+      "integrity": "sha512-cmAAMIRTaEwpqxlrZyiEY9kdibk94gP5KTF8AT1Ra4rWNZYHNMreqhKUEeC5WJtuN5SJZjPQmV+XO2P5PlnvNQ==",
+      "dev": true,
+      "requires": {
+        "@types/express": "*",
+        "@types/jsonwebtoken": "*",
+        "@types/passport-strategy": "*"
+      }
+    },
+    "@types/passport-local": {
+      "version": "1.0.34",
+      "resolved": "https://registry.npmjs.org/@types/passport-local/-/passport-local-1.0.34.tgz",
+      "integrity": "sha512-PSc07UdYx+jhadySxxIYWuv6sAnY5e+gesn/5lkPKfBeGuIYn9OPR+AAEDq73VRUh6NBTpvE/iPE62rzZUslog==",
+      "dev": true,
+      "requires": {
+        "@types/express": "*",
+        "@types/passport": "*",
+        "@types/passport-strategy": "*"
+      }
+    },
+    "@types/passport-strategy": {
+      "version": "0.2.35",
+      "resolved": "https://registry.npmjs.org/@types/passport-strategy/-/passport-strategy-0.2.35.tgz",
+      "integrity": "sha512-o5D19Jy2XPFoX2rKApykY15et3Apgax00RRLf0RUotPDUsYrQa7x4howLYr9El2mlUApHmCMv5CZ1IXqKFQ2+g==",
+      "dev": true,
+      "requires": {
+        "@types/express": "*",
+        "@types/passport": "*"
+      }
+    },
     "@types/prettier": {
       "version": "2.4.4",
       "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.4.4.tgz",
@@ -18141,6 +18469,11 @@
         "ieee754": "^1.1.13"
       }
     },
+    "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.2",
       "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
@@ -18652,6 +18985,22 @@
       "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
       "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA=="
     },
+    "cookie-parser": {
+      "version": "1.4.6",
+      "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz",
+      "integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==",
+      "requires": {
+        "cookie": "0.4.1",
+        "cookie-signature": "1.0.6"
+      },
+      "dependencies": {
+        "cookie": {
+          "version": "0.4.1",
+          "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz",
+          "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA=="
+        }
+      }
+    },
     "cookie-signature": {
       "version": "1.0.6",
       "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
@@ -19095,6 +19444,14 @@
         }
       }
     },
+    "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",
@@ -21513,6 +21870,49 @@
         "through": ">=2.2.7 <3"
       }
     },
+    "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": {
+        "semver": {
+          "version": "5.7.1",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+          "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
+        }
+      }
+    },
+    "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": "4.1.1",
       "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.1.1.tgz",
@@ -21625,23 +22025,41 @@
       "integrity": "sha1-ZHYsSGGAglGKw99Mz11YhtriA0c=",
       "dev": true
     },
+    "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.ismatch": {
       "version": "4.4.0",
       "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz",
       "integrity": "sha1-dWy1FQyjum8RCFp4hJZF8Yj4Xzc=",
       "dev": true
     },
+    "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=",
-      "dev": true
+      "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=",
-      "dev": true
+      "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE="
     },
     "lodash.memoize": {
       "version": "4.1.2",
@@ -21660,6 +22078,11 @@
       "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz",
       "integrity": "sha1-brGa5aHuHdnfC5aeZs4Lf6MLXmA="
     },
+    "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",
@@ -24084,6 +24507,37 @@
       "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
       "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
     },
+    "passport": {
+      "version": "0.5.2",
+      "resolved": "https://registry.npmjs.org/passport/-/passport-0.5.2.tgz",
+      "integrity": "sha512-w9n/Ot5I7orGD4y+7V3EFJCQEznE5RxHamUxcqLT2QoJY0f2JdN8GyHonYFvN0Vz+L6lUJfVhrk2aZz2LbuREw==",
+      "requires": {
+        "passport-strategy": "1.x.x",
+        "pause": "0.0.1"
+      }
+    },
+    "passport-jwt": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/passport-jwt/-/passport-jwt-4.0.0.tgz",
+      "integrity": "sha512-BwC0n2GP/1hMVjR4QpnvqA61TxenUMlmfNjYNgK0ZAs0HK4SOQkHcSv4L328blNTLtHq7DbmvyNJiH+bn6C5Mg==",
+      "requires": {
+        "jsonwebtoken": "^8.2.0",
+        "passport-strategy": "^1.0.0"
+      }
+    },
+    "passport-local": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz",
+      "integrity": "sha1-H+YyaMkudWBmJkN+O5BmYsFbpu4=",
+      "requires": {
+        "passport-strategy": "1.x.x"
+      }
+    },
+    "passport-strategy": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz",
+      "integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ="
+    },
     "path-exists": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@@ -24118,6 +24572,11 @@
       "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
       "dev": true
     },
+    "pause": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz",
+      "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10="
+    },
     "picocolors": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
diff --git a/api/package.json b/api/package.json
index 510c73eab121cf232606d6e7c976e44af667b4c5..ac9d8e2879f8235ecbf9d47acfb6cb7e6eea8d12 100644
--- a/api/package.json
+++ b/api/package.json
@@ -29,13 +29,19 @@
     "@nestjs/config": "^1.0.1",
     "@nestjs/core": "^8.0.0",
     "@nestjs/graphql": "^10.0.6",
+    "@nestjs/jwt": "^8.0.0",
+    "@nestjs/passport": "^8.2.1",
     "@nestjs/platform-express": "^8.0.0",
     "@nestjs/typeorm": "^8.0.2",
     "apollo-server-express": "^3.6.3",
     "axios": "^0.21.1",
+    "cookie-parser": "^1.4.6",
     "graphql": "^15.5.3",
     "graphql-type-json": "^0.3.2",
     "jsonata": "^1.8.5",
+    "passport": "^0.5.2",
+    "passport-jwt": "^4.0.0",
+    "passport-local": "^1.0.0",
     "reflect-metadata": "^0.1.13",
     "rimraf": "^3.0.2",
     "rxjs": "^7.2.0",
@@ -43,6 +49,7 @@
   },
   "devDependencies": {
     "@eclass/semantic-release-docker": "^3.0.0",
+    "@jest-mock/express": "^1.4.5",
     "@nestjs/cli": "^8.0.0",
     "@nestjs/schematics": "^8.0.0",
     "@nestjs/testing": "^8.2.2",
@@ -50,9 +57,12 @@
     "@semantic-release/changelog": "^6.0.1",
     "@semantic-release/git": "^10.0.1",
     "@semantic-release/gitlab": "^7.0.4",
+    "@types/cookie-parser": "^1.4.2",
     "@types/express": "^4.17.13",
     "@types/jest": "^27.0.1",
     "@types/node": "^16.0.0",
+    "@types/passport-jwt": "^3.0.6",
+    "@types/passport-local": "^1.0.34",
     "@types/supertest": "^2.0.11",
     "@typescript-eslint/eslint-plugin": "^4.28.2",
     "@typescript-eslint/parser": "^4.28.2",
@@ -61,6 +71,7 @@
     "eslint-plugin-prettier": "^3.4.0",
     "husky": "^7.0.2",
     "jest": "^27.0.6",
+    "jest-mock": "^27.5.1",
     "prettier": "^2.3.2",
     "supertest": "^6.1.3",
     "ts-jest": "^27.0.3",
diff --git a/api/src/auth/auth-constants.ts b/api/src/auth/auth-constants.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c904d4554a98dc825e4d2e7d6183fbf7590cebcc
--- /dev/null
+++ b/api/src/auth/auth-constants.ts
@@ -0,0 +1,11 @@
+export const authConstants = {
+  JWTSecret: 'AUTH_JWT_SECRET',
+  skipAuth: 'AUTH_SKIP',
+  expiresIn: 'AUTH_JWT_TOKEN_EXPIRES_IN',
+  cookie: {
+    name: 'jwt-gateway',
+    sameSite: 'AUTH_COOKIE_SAME_SITE',
+    secure: 'AUTH_COOKIE_SECURE',
+    httpOnly: 'AUTH_COOKIE_HTTPONLY',
+  },
+};
diff --git a/api/src/auth/auth.module.ts b/api/src/auth/auth.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3a5381e281ff0715c1f08a862b4090f3f133c3f0
--- /dev/null
+++ b/api/src/auth/auth.module.ts
@@ -0,0 +1,37 @@
+import { Module } from '@nestjs/common';
+import { ConfigModule, ConfigService } from '@nestjs/config';
+import { JwtModule } from '@nestjs/jwt';
+import { PassportModule } from '@nestjs/passport';
+import { authConstants } from './auth-constants';
+import { AuthResolver } from './auth.resolver';
+import { AuthService } from './auth.service';
+import { JwtBearerStrategy } from './strategies/jwt-bearer.strategy';
+import { JwtCookiesStrategy } from './strategies/jwt-cookies.strategy';
+import { LocalStrategy } from './strategies/local.strategy';
+
+@Module({
+  imports: [
+    PassportModule.register({
+      session: false,
+    }),
+    JwtModule.registerAsync({
+      imports: [ConfigModule],
+      useFactory: async (configService: ConfigService) => ({
+        secret: configService.get<string>(authConstants.JWTSecret),
+        signOptions: {
+          expiresIn: configService.get<string>(authConstants.expiresIn),
+        },
+      }),
+      inject: [ConfigService],
+    }),
+  ],
+  providers: [
+    AuthService,
+    LocalStrategy,
+    JwtBearerStrategy,
+    JwtCookiesStrategy,
+    AuthResolver,
+  ],
+  exports: [AuthService],
+})
+export class AuthModule {}
diff --git a/api/src/auth/auth.resolver.spec.ts b/api/src/auth/auth.resolver.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..cc74383f2ad204a9b0e6a8c4503456e361081193
--- /dev/null
+++ b/api/src/auth/auth.resolver.spec.ts
@@ -0,0 +1,79 @@
+import { getMockRes } from '@jest-mock/express';
+import { Test, TestingModule } from '@nestjs/testing';
+import { MockFunctionMetadata, ModuleMocker } from 'jest-mock';
+import LocalService from '../engine/connectors/local/main.connector';
+import { ENGINE_SERVICE } from '../engine/engine.constants';
+import { authConstants } from './auth-constants';
+import { AuthResolver } from './auth.resolver';
+import { AuthService } from './auth.service';
+import { User } from './models/user.model';
+
+const moduleMocker = new ModuleMocker(global);
+
+describe('AuthResolver', () => {
+  let resolver: AuthResolver;
+  const { res } = getMockRes();
+  const mockCookie = jest.fn();
+  const mockClearCookie = jest.fn();
+
+  res.cookie = mockCookie;
+  res.clearCookie = mockClearCookie;
+
+  const user: User = {
+    id: 'testing',
+    username: 'testing',
+  };
+
+  const authData = {
+    accessToken: 'DummyToken',
+  };
+
+  const credentials = {
+    username: 'guest',
+    password: 'guest123',
+  };
+
+  beforeEach(async () => {
+    const module: TestingModule = await Test.createTestingModule({
+      providers: [
+        {
+          provide: ENGINE_SERVICE,
+          useClass: LocalService,
+        },
+        AuthResolver,
+      ],
+    })
+      .useMocker((token) => {
+        if (token === AuthService) {
+          return {
+            login: jest.fn().mockResolvedValue(authData),
+          };
+        }
+        if (typeof token === 'function') {
+          const mockMetadata = moduleMocker.getMetadata(
+            token,
+          ) as MockFunctionMetadata<any, any>;
+          const Mock = moduleMocker.generateFromMetadata(mockMetadata);
+          return new Mock();
+        }
+      })
+      .compile();
+
+    resolver = module.get<AuthResolver>(AuthResolver);
+  });
+
+  it('login', async () => {
+    const data = await resolver.login(res, user, credentials);
+
+    expect(mockCookie.mock.calls[0][0]).toBe(authConstants.cookie.name);
+    expect(mockCookie.mock.calls[0][1]).toBe(authData.accessToken);
+    expect(data.accessToken).toBe(authData.accessToken);
+    expect(data.user).toBe(user);
+  });
+
+  it('logout', () => {
+    resolver.logout(res, user);
+
+    expect(mockClearCookie.mock.calls[0][0]).toBe(authConstants.cookie.name);
+  });
+});
diff --git a/api/src/auth/auth.resolver.ts b/api/src/auth/auth.resolver.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6a5f5698a787dea72e3836b3ebc197aef7fea8cf
--- /dev/null
+++ b/api/src/auth/auth.resolver.ts
@@ -0,0 +1,80 @@
+import {
+  Inject,
+  InternalServerErrorException,
+  Logger,
+  UseGuards,
+} from '@nestjs/common';
+import { ConfigService } from '@nestjs/config';
+import { Args, Mutation, Resolver } from '@nestjs/graphql';
+import { Response } from 'express';
+import { parseToBoolean } from '../common/interfaces/utilities.interface';
+import { ENGINE_SERVICE } from '../engine/engine.constants';
+import { IEngineService } from '../engine/engine.interfaces';
+import { authConstants } from './auth-constants';
+import { AuthService } from './auth.service';
+import { GQLResponse } from './decorators/gql-request.decoractor';
+import { CurrentUser } from './decorators/user.decorator';
+import { JwtAuthGuard } from './guards/jwt-auth.guard';
+import { LocalAuthGuard } from './guards/local-auth.guard';
+import { AuthenticationInput } from './inputs/authentication.input';
+import { User } from './models/user.model';
+import { AuthenticationOutput } from './outputs/authentication.output';
+
+//Custom defined type because Pick<CookieOptions, 'sameSite'> does not work
+type SameSiteType = boolean | 'lax' | 'strict' | 'none' | undefined;
+
+@Resolver()
+export class AuthResolver {
+  private readonly logger = new Logger(AuthResolver.name);
+
+  constructor(
+    @Inject(ENGINE_SERVICE) private readonly engineService: IEngineService,
+    private readonly authService: AuthService,
+    private readonly configService: ConfigService,
+  ) {}
+
+  @Mutation(() => AuthenticationOutput)
+  @UseGuards(LocalAuthGuard)
+  async login(
+    @GQLResponse() res: Response,
+    @CurrentUser() user: User,
+    @Args('variables') inputs: AuthenticationInput,
+  ): Promise<AuthenticationOutput> {
+    this.logger.verbose(`${inputs.username} logged in`);
+
+    const data = await this.authService.login(user);
+    if (!data)
+      throw new InternalServerErrorException(
+        `Error during the authentication process`,
+      );
+
+    res.cookie(authConstants.cookie.name, data.accessToken, {
+      httpOnly: parseToBoolean(
+        this.configService.get(authConstants.cookie.httpOnly, 'true'),
+      ),
+      sameSite: this.configService.get<SameSiteType>(
+        authConstants.cookie.sameSite,
+        'strict',
+      ),
+      secure: parseToBoolean(
+        this.configService.get(authConstants.cookie.secure, 'true'),
+      ),
+    });
+
+    return {
+      user,
+      accessToken: data.accessToken,
+    };
+  }
+
+  @Mutation(() => Boolean)
+  @UseGuards(JwtAuthGuard)
+  logout(@GQLResponse() res: Response, @CurrentUser() user: User): boolean {
+    this.logger.verbose(`${user.username} logged out`);
+
+    res.clearCookie(authConstants.cookie.name);
+    this.engineService.logout?.();
+
+    return true;
+  }
+}
diff --git a/api/src/auth/auth.service.spec.ts b/api/src/auth/auth.service.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9122005451083e42a0cd056b161c4d4897f82252
--- /dev/null
+++ b/api/src/auth/auth.service.spec.ts
@@ -0,0 +1,69 @@
+import { JwtService } from '@nestjs/jwt';
+import { Test, TestingModule } from '@nestjs/testing';
+import { MockFunctionMetadata, ModuleMocker } from 'jest-mock';
+import LocalService from '../engine/connectors/local/main.connector';
+import {
+  ENGINE_MODULE_OPTIONS,
+  ENGINE_SERVICE,
+} from '../engine/engine.constants';
+import { AuthService } from './auth.service';
+import { User } from './models/user.model';
+
+const moduleMocker = new ModuleMocker(global);
+
+describe('AuthService', () => {
+  let service: AuthService;
+  const user: User = {
+    id: 'dummy',
+    username: 'dummy64',
+  };
+  const jwtToken = 'JWTDummyToken';
+
+  beforeEach(async () => {
+    const module: TestingModule = await Test.createTestingModule({
+      providers: [
+        {
+          provide: ENGINE_SERVICE,
+          useClass: LocalService,
+        },
+        {
+          provide: ENGINE_MODULE_OPTIONS,
+          useValue: {
+            type: 'local',
+            baseurl: 'test',
+          },
+        },
+        AuthService,
+      ],
+    })
+      .useMocker((token) => {
+        if (token === JwtService) {
+          return {
+            sign: jest.fn().mockReturnValue(jwtToken),
+          };
+        }
+        if (typeof token === 'function') {
+          const mockMetadata = moduleMocker.getMetadata(
+            token,
+          ) as MockFunctionMetadata<any, any>;
+          const Mock = moduleMocker.generateFromMetadata(mockMetadata);
+          return new Mock();
+        }
+      })
+      .compile();
+
+    service = module.get<AuthService>(AuthService);
+  });
+
+  it('login', () => {
+    const data = service.login(user);
+
+    expect(data.accessToken).toBe(jwtToken);
+  });
+
+  it('validateUser', async () => {
+    const data = await service.validateUser('guest', 'password123');
+
+    expect(!!data).toBeTruthy();
+  });
+});
diff --git a/api/src/auth/auth.service.ts b/api/src/auth/auth.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e992fa760c238d0d475115e87cf52e5e07a0d5f3
--- /dev/null
+++ b/api/src/auth/auth.service.ts
@@ -0,0 +1,26 @@
+import { Inject, Injectable, NotImplementedException } from '@nestjs/common';
+import { JwtService } from '@nestjs/jwt';
+import { ENGINE_SERVICE } from '../engine/engine.constants';
+import { IEngineService } from '../engine/engine.interfaces';
+import { User } from './models/user.model';
+import { AuthenticationOutput } from './outputs/authentication.output';
+
+@Injectable()
+export class AuthService {
+  constructor(
+    @Inject(ENGINE_SERVICE) private readonly engineService: IEngineService,
+    private jwtService: JwtService,
+  ) {}
+
+  async validateUser(username: string, password: string): Promise<User> {
+    if (!this.engineService.login) throw new NotImplementedException();
+    return await this.engineService.login?.(username, password);
+  }
+
+  async login(user: User): Promise<Pick<AuthenticationOutput, 'accessToken'>> {
+    const payload = { username: user.username, sub: user };
+    return Promise.resolve({
+      accessToken: this.jwtService.sign(payload),
+    });
+  }
+}
diff --git a/api/src/auth/decorators/gql-request.decoractor.ts b/api/src/auth/decorators/gql-request.decoractor.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4de3409deea7848aa33e7a66d11a842b029ac5a9
--- /dev/null
+++ b/api/src/auth/decorators/gql-request.decoractor.ts
@@ -0,0 +1,14 @@
+import { createParamDecorator, ExecutionContext } from '@nestjs/common';
+import { GqlExecutionContext } from '@nestjs/graphql';
+import { Response } from 'express';
+
+/**
+ * Access to graphQL request through the context
+ * @returns request from graphql
+ */
+export const GQLResponse = createParamDecorator(
+  (data: unknown, context: ExecutionContext): Response => {
+    const ctx = GqlExecutionContext.create(context);
+    return ctx.getContext().res;
+  },
+);
diff --git a/api/src/auth/decorators/user.decorator.ts b/api/src/auth/decorators/user.decorator.ts
new file mode 100644
index 0000000000000000000000000000000000000000..cbb566332c2d214cb3d33ec1b9ff7c458f9992e3
--- /dev/null
+++ b/api/src/auth/decorators/user.decorator.ts
@@ -0,0 +1,14 @@
+import { createParamDecorator, ExecutionContext } from '@nestjs/common';
+import { GqlExecutionContext } from '@nestjs/graphql';
+import { User } from '../models/user.model';
+
+/**
+ * Retrieve the current user within the graphQL request
+ * @returns instance of User or undefined
+ */
+export const CurrentUser = createParamDecorator(
+  (data: unknown, context: ExecutionContext): User | undefined => {
+    const ctx = GqlExecutionContext.create(context);
+    return ctx.getContext().req.user;
+  },
+);
diff --git a/api/src/auth/guards/jwt-auth.guard.ts b/api/src/auth/guards/jwt-auth.guard.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4afaca585a6117aca640b98ef5c641c7cbde9837
--- /dev/null
+++ b/api/src/auth/guards/jwt-auth.guard.ts
@@ -0,0 +1,35 @@
+import { ExecutionContext, Injectable } from '@nestjs/common';
+import { ConfigService } from '@nestjs/config';
+import { GqlExecutionContext } from '@nestjs/graphql';
+import { AuthGuard } from '@nestjs/passport';
+import { Observable } from 'rxjs';
+import { parseToBoolean } from '../../common/interfaces/utilities.interface';
+import { authConstants } from '../auth-constants';
+
+@Injectable()
+export class JwtAuthGuard extends AuthGuard(['jwt-cookies', 'jwt-bearer']) {
+  constructor(private readonly configService: ConfigService) {
+    super();
+  }
+
+  getRequest(context: ExecutionContext) {
+    const ctx = GqlExecutionContext.create(context);
+    const gqlReq = ctx.getContext().req;
+
+    return gqlReq ?? ctx.switchToHttp().getRequest();
+  }
+
+  canActivate(
+    context: ExecutionContext,
+  ): boolean | Promise<boolean> | Observable<boolean> {
+    const skipAuth = parseToBoolean(
+      this.configService.get(authConstants.skipAuth, 'false'),
+    );
+
+    if (skipAuth) {
+      return true;
+    }
+
+    return super.canActivate(context);
+  }
+}
diff --git a/api/src/auth/guards/local-auth.guard.ts b/api/src/auth/guards/local-auth.guard.ts
new file mode 100644
index 0000000000000000000000000000000000000000..77782dcac3a3bc9950b5b2a0f8224c940f16c003
--- /dev/null
+++ b/api/src/auth/guards/local-auth.guard.ts
@@ -0,0 +1,18 @@
+import { ExecutionContext, Injectable } from '@nestjs/common';
+import { GqlExecutionContext } from '@nestjs/graphql';
+import { AuthGuard } from '@nestjs/passport';
+
+@Injectable()
+export class LocalAuthGuard extends AuthGuard('local') {
+  //override to handle graphql specific use case
+  getRequest(context: ExecutionContext) {
+    const ctx = GqlExecutionContext.create(context);
+    const gqlReq = ctx.getContext().req;
+    if (gqlReq) {
+      const { variables } = ctx.getArgs();
+      gqlReq.body = variables;
+      return gqlReq;
+    }
+    return context.switchToHttp().getRequest();
+  }
+}
diff --git a/api/src/auth/inputs/authentication.input.ts b/api/src/auth/inputs/authentication.input.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ba31ea5e97bfde9fa9bd96bcf98d9921610ab479
--- /dev/null
+++ b/api/src/auth/inputs/authentication.input.ts
@@ -0,0 +1,10 @@
+import { Field, InputType } from '@nestjs/graphql';
+
+@InputType()
+export class AuthenticationInput {
+  @Field()
+  username: string;
+
+  @Field()
+  password: string;
+}
diff --git a/api/src/auth/models/user.model.ts b/api/src/auth/models/user.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2352d59008cc9bf68feefd7b954108cf161730ed
--- /dev/null
+++ b/api/src/auth/models/user.model.ts
@@ -0,0 +1,21 @@
+import { Field, ObjectType } from '@nestjs/graphql';
+
+@ObjectType()
+export class User {
+  @Field()
+  id: string;
+
+  @Field()
+  username: string;
+
+  @Field({ nullable: true })
+  fullname?: string;
+
+  @Field({ nullable: true })
+  email?: string;
+
+  @Field({ nullable: true })
+  agreeNDA?: boolean;
+
+  extraFields?: Record<string, any>;
+}
diff --git a/api/src/auth/outputs/authentication.output.ts b/api/src/auth/outputs/authentication.output.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7f4590c2ddbfb3e1a7a4cb69e8127b3cfc07c3c0
--- /dev/null
+++ b/api/src/auth/outputs/authentication.output.ts
@@ -0,0 +1,11 @@
+import { Field, ObjectType } from '@nestjs/graphql';
+import { User } from '../models/user.model';
+
+@ObjectType()
+export class AuthenticationOutput {
+  @Field(() => User)
+  user: User;
+
+  @Field()
+  accessToken: string;
+}
diff --git a/api/src/auth/strategies/jwt-bearer.strategy.ts b/api/src/auth/strategies/jwt-bearer.strategy.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9439c87cf54accd4684455c1ce66df564d3af2ac
--- /dev/null
+++ b/api/src/auth/strategies/jwt-bearer.strategy.ts
@@ -0,0 +1,23 @@
+import { Injectable } from '@nestjs/common';
+import { ConfigService } from '@nestjs/config';
+import { PassportStrategy } from '@nestjs/passport';
+import { ExtractJwt, Strategy } from 'passport-jwt';
+import { authConstants } from '../auth-constants';
+
+@Injectable()
+export class JwtBearerStrategy extends PassportStrategy(
+  Strategy,
+  'jwt-bearer',
+) {
+  constructor(private readonly configService: ConfigService) {
+    super({
+      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
+      ignoreExpiration: false,
+      secretOrKey: configService.get<string>(authConstants.JWTSecret),
+    });
+  }
+
+  async validate(payload: any) {
+    return payload.sub;
+  }
+}
diff --git a/api/src/auth/strategies/jwt-cookies.strategy.ts b/api/src/auth/strategies/jwt-cookies.strategy.ts
new file mode 100644
index 0000000000000000000000000000000000000000..29ca031c34660149bb3a90e30ebed97432f7e9ca
--- /dev/null
+++ b/api/src/auth/strategies/jwt-cookies.strategy.ts
@@ -0,0 +1,32 @@
+import { Injectable } from '@nestjs/common';
+import { ConfigService } from '@nestjs/config';
+import { PassportStrategy } from '@nestjs/passport';
+import { Request } from 'express';
+import { Strategy } from 'passport-jwt';
+import { authConstants } from '../auth-constants';
+
+@Injectable()
+export class JwtCookiesStrategy extends PassportStrategy(
+  Strategy,
+  'jwt-cookies',
+) {
+  constructor(private readonly configService: ConfigService) {
+    super({
+      jwtFromRequest: JwtCookiesStrategy.extractFromCookie,
+      ignoreExpiration: false,
+      secretOrKey: configService.get<string>(authConstants.JWTSecret),
+    });
+  }
+
+  static extractFromCookie = function (req: Request) {
+    let token = null;
+    if (req && req.cookies) {
+      token = req.cookies[authConstants.cookie.name];
+    }
+    return token;
+  };
+
+  async validate(payload: any) {
+    return payload.sub;
+  }
+}
diff --git a/api/src/auth/strategies/local.strategy.ts b/api/src/auth/strategies/local.strategy.ts
new file mode 100644
index 0000000000000000000000000000000000000000..166e9efb0d75dcb31a56ab1c4e09ceb9c8dc846a
--- /dev/null
+++ b/api/src/auth/strategies/local.strategy.ts
@@ -0,0 +1,29 @@
+import { Strategy } from 'passport-local';
+import { PassportStrategy } from '@nestjs/passport';
+import { Injectable, UnauthorizedException } from '@nestjs/common';
+import { AuthService } from '../auth.service';
+import { User } from '../models/user.model';
+import { ContextIdFactory, ModuleRef } from '@nestjs/core';
+
+@Injectable()
+export class LocalStrategy extends PassportStrategy(Strategy, 'local') {
+  constructor(private moduleRef: ModuleRef) {
+    super({
+      passReqToCallback: true,
+    });
+  }
+
+  async validate(
+    request: Request,
+    username: string,
+    password: string,
+  ): Promise<User> {
+    const contextId = ContextIdFactory.getByRequest(request);
+    const authService = await this.moduleRef.resolve(AuthService, contextId);
+    const user = await authService.validateUser(username, password);
+    if (!user) {
+      throw new UnauthorizedException();
+    }
+    return user;
+  }
+}
diff --git a/api/src/common/interfaces/utilities.interface.ts b/api/src/common/interfaces/utilities.interface.ts
index db02ef99e3f4c67f65f320f1d0bfe1a2a6b832f5..762534ac111d76f505e55c787d771ff6eb164d13 100644
--- a/api/src/common/interfaces/utilities.interface.ts
+++ b/api/src/common/interfaces/utilities.interface.ts
@@ -16,3 +16,16 @@ export enum MIME_TYPES {
   HTML = 'text/html',
   TEXT = 'text/plain',
 }
+
+/**
+ * Utility method to convert string value to boolean
+ * @param value string value to be converted
+ * @returns true if string value equals to 'true', false otherwise
+ */
+export const parseToBoolean = (value: string): boolean => {
+  try {
+    return value.toLowerCase() == 'true';
+  } catch {
+    return false;
+  }
+};
diff --git a/api/src/common/utilities.ts b/api/src/common/utilities.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c109df5e5b8c5506844a4e43a7b1d7612440a135
--- /dev/null
+++ b/api/src/common/utilities.ts
@@ -0,0 +1,21 @@
+import {
+  HttpException,
+  InternalServerErrorException,
+  NotFoundException,
+  RequestTimeoutException,
+  UnauthorizedException,
+} from '@nestjs/common';
+import axios from 'axios';
+
+export const errorAxiosHandler = (e: any) => {
+  if (!axios.isAxiosError(e)) throw new InternalServerErrorException(e);
+
+  if (e.response.status === 401) throw new UnauthorizedException();
+  if (e.response.status === 404) throw new NotFoundException();
+  if (e.response.status === 408) throw new RequestTimeoutException();
+  if (e.response.status === 500) throw new InternalServerErrorException();
+
+  if (e.response) throw new HttpException(e.response.data, e.response.status);
+
+  throw new InternalServerErrorException('Unknown error');
+};
diff --git a/api/src/engine/connectors/datashield/main.connector.ts b/api/src/engine/connectors/datashield/main.connector.ts
index 81f7bf6bb6ecba86cba15b2350d460e57dadf933..2e4f3be2ebce9f21319426a07cda073005fcd4ec 100644
--- a/api/src/engine/connectors/datashield/main.connector.ts
+++ b/api/src/engine/connectors/datashield/main.connector.ts
@@ -1,9 +1,11 @@
 import { HttpService } from '@nestjs/axios';
-import { Inject, Logger } from '@nestjs/common';
+import { Inject, Logger, NotImplementedException } from '@nestjs/common';
 import { REQUEST } from '@nestjs/core';
 import { Request } from 'express';
-import { firstValueFrom, Observable } from 'rxjs';
+import { catchError, firstValueFrom, Observable } from 'rxjs';
+import { User } from 'src/auth/models/user.model';
 import { MIME_TYPES } from 'src/common/interfaces/utilities.interface';
+import { errorAxiosHandler } from 'src/common/utilities';
 import { ENGINE_MODULE_OPTIONS } from 'src/engine/engine.constants';
 import {
   IConfiguration,
@@ -43,22 +45,50 @@ export default class DataShieldService implements IEngineService {
     return {};
   }
 
-  logout(): void {
-    throw new Error('Method not implemented.');
+  async login(username: string, password: string): Promise<User> {
+    const loginPath = this.options.baseurl + 'login';
+
+    const user: User = {
+      id: username,
+      username,
+      extraFields: {
+        sid: '',
+      },
+    };
+
+    const loginData = await firstValueFrom(
+      this.httpService
+        .get(loginPath, {
+          auth: { username, password },
+        })
+        .pipe(catchError((e) => errorAxiosHandler(e))),
+    );
+
+    const cookies = (loginData.headers['set-cookie'] as string[]) ?? [];
+    if (loginData.headers && loginData.headers['set-cookie']) {
+      cookies.forEach((cookie) => {
+        const [key, value] = cookie.split(/={1}/);
+        if (key === 'sid') {
+          user.extraFields.sid = value;
+        }
+      });
+    }
+
+    return user;
   }
 
   getAlgorithms(): Algorithm[] | Promise<Algorithm[]> {
-    throw new Error('Method not implemented.');
+    throw new NotImplementedException();
   }
 
-  async getHistogram(variable: string): Promise<RawResult> {
+  async getHistogram(variable: string, cookie?: string): Promise<RawResult> {
     const path =
       this.options.baseurl + `histogram?var=${variable}&type=combine`;
 
     const response = await firstValueFrom(
       this.httpService.get(path, {
         headers: {
-          cookie: this.req['req'].headers['cookie'],
+          cookie,
         },
       }),
     );
@@ -87,13 +117,16 @@ export default class DataShieldService implements IEngineService {
     };
   }
 
-  async getDescriptiveStats(variable: string): Promise<TableResult> {
+  async getDescriptiveStats(
+    variable: string,
+    cookie?: string,
+  ): Promise<TableResult> {
     const path = this.options.baseurl + `quantiles?var=${variable}&type=split`;
 
     const response = await firstValueFrom(
       this.httpService.get(path, {
         headers: {
-          cookie: this.req['req'].headers['cookie'],
+          cookie,
         },
       }),
     );
@@ -111,6 +144,10 @@ export default class DataShieldService implements IEngineService {
     data: ExperimentCreateInput,
     isTransient: boolean,
   ): Promise<Experiment> {
+    const user = this.req.user as User;
+    const cookie = [`sid=${user.extraFields['sid']}`, `user=${user.id}`].join(
+      ';',
+    );
     const expResult: Experiment = {
       id: `${data.algorithm.id}-${Date.now()}`,
       variables: data.variables,
@@ -126,7 +163,7 @@ export default class DataShieldService implements IEngineService {
       case 'MULTIPLE_HISTOGRAMS': {
         expResult.results = await Promise.all<RawResult>(
           data.variables.map(
-            async (variable) => await this.getHistogram(variable),
+            async (variable) => await this.getHistogram(variable, cookie),
           ),
         );
         break;
@@ -134,7 +171,8 @@ export default class DataShieldService implements IEngineService {
       case 'DESCRIPTIVE_STATS': {
         expResult.results = await Promise.all<TableResult>(
           [...data.variables, ...data.coVariables].map(
-            async (variable) => await this.getDescriptiveStats(variable),
+            async (variable) =>
+              await this.getDescriptiveStats(variable, cookie),
           ),
         );
         break;
@@ -157,40 +195,23 @@ export default class DataShieldService implements IEngineService {
   }
 
   getExperiment(id: string): Experiment | Promise<Experiment> {
-    throw new Error('Method not implemented.');
+    throw new NotImplementedException();
   }
 
   removeExperiment(id: string): PartialExperiment | Promise<PartialExperiment> {
-    throw new Error('Method not implemented.');
+    throw new NotImplementedException();
   }
 
   editExperient(
     id: string,
     expriment: ExperimentEditInput,
   ): Experiment | Promise<Experiment> {
-    throw new Error('Method not implemented.');
+    throw new NotImplementedException();
   }
 
   async getDomains(): Promise<Domain[]> {
-    const loginPath = this.options.baseurl + 'login';
-
-    const loginData = await firstValueFrom(
-      this.httpService.get(loginPath, {
-        auth: { username: 'guest', password: 'guest123' },
-      }),
-    );
-
-    const cookies = (loginData.headers['set-cookie'] as string[]) ?? [];
-    if (loginData.headers && loginData.headers['set-cookie']) {
-      cookies.forEach((cookie) => {
-        const [key, value] = cookie.split(/={1}/);
-        this.req.res.cookie(key, value, {
-          httpOnly: true,
-          //sameSite: 'none',
-        });
-      });
-    }
-
+    const user = this.req.user as User;
+    const cookies = [`sid=${user.extraFields['sid']}`, `user=${user.id}`];
     const path = this.options.baseurl + 'getvars';
 
     const response = await firstValueFrom(
@@ -216,27 +237,27 @@ export default class DataShieldService implements IEngineService {
   }
 
   editActiveUser(): Observable<string> {
-    throw new Error('Method not implemented.');
+    throw new NotImplementedException();
   }
 
   getExperimentREST(): Observable<string> {
-    throw new Error('Method not implemented.');
+    throw new NotImplementedException();
   }
 
   deleteExperiment(): Observable<string> {
-    throw new Error('Method not implemented.');
+    throw new NotImplementedException();
   }
 
   editExperimentREST(): Observable<string> {
-    throw new Error('Method not implemented.');
+    throw new NotImplementedException();
   }
 
   startExperimentTransient(): Observable<string> {
-    throw new Error('Method not implemented.');
+    throw new NotImplementedException();
   }
 
   startExperiment(): Observable<string> {
-    throw new Error('Method not implemented.');
+    throw new NotImplementedException();
   }
 
   getExperiments(): string {
diff --git a/api/src/engine/connectors/local/main.connector.ts b/api/src/engine/connectors/local/main.connector.ts
index 003b3c8cc2927a84dda52f8efe97b7d44edb8ab8..af5cd6b66443af0f60c8d0db97e596f3b2810fa5 100644
--- a/api/src/engine/connectors/local/main.connector.ts
+++ b/api/src/engine/connectors/local/main.connector.ts
@@ -9,10 +9,14 @@ import {
 import { ListExperiments } from 'src/engine/models/experiment/list-experiments.model';
 import { ExperimentEditInput } from 'src/engine/models/experiment/input/experiment-edit.input';
 import { Algorithm } from 'src/engine/models/experiment/algorithm.model';
+import { User } from 'src/auth/models/user.model';
 
 export default class LocalService implements IEngineService {
-  logout(): void {
-    throw new Error('Method not implemented.');
+  login(): User | Promise<User> {
+    return {
+      id: '1',
+      username: 'LocalServiceUser',
+    };
   }
 
   getAlgorithms(): Algorithm[] | Promise<Algorithm[]> {
diff --git a/api/src/engine/engine.controller.ts b/api/src/engine/engine.controller.ts
index 6338a6b073b79e1a11c64ce895944b6268c50548..ffd77ead399601950d98d82433a5967deec7f71a 100644
--- a/api/src/engine/engine.controller.ts
+++ b/api/src/engine/engine.controller.ts
@@ -1,18 +1,5 @@
-import {
-  Controller,
-  Get,
-  Inject,
-  NotFoundException,
-  Param,
-  Post,
-  Req,
-  Res,
-  UseInterceptors,
-} from '@nestjs/common';
-import { Request, Response } from 'express';
-import { join } from 'path/posix';
+import { Controller, Get, Inject, Post, UseInterceptors } from '@nestjs/common';
 import { Observable } from 'rxjs';
-import { AssetsService } from './assets.service';
 import { ENGINE_SERVICE } from './engine.constants';
 import { IEngineService } from './engine.interfaces';
 import { ErrorsInterceptor } from './interceptors/errors.interceptor';
@@ -22,35 +9,8 @@ import { ErrorsInterceptor } from './interceptors/errors.interceptor';
 export class EngineController {
   constructor(
     @Inject(ENGINE_SERVICE) private readonly engineService: IEngineService,
-    private readonly assetsService: AssetsService,
   ) {}
 
-  @Get('assets/:name')
-  getFile(
-    @Req() request: Request,
-    @Res() response: Response,
-    @Param('name') filename: string,
-  ) {
-    if (filename.endsWith('.md')) {
-      const baseurl =
-        request.protocol +
-        '://' +
-        join(request.get('host'), process.env.BASE_URL_CONTEXT ?? '', 'assets'); // not full url, should consider "/services"
-      const text = this.assetsService.getMarkdown(filename, baseurl);
-      response.setHeader('Content-Type', 'text/markdown');
-      return response.send(text);
-    }
-
-    const filepath = this.assetsService.getAssetFile(filename);
-
-    // Test if the file exist, if not send 404
-    if (filepath) {
-      return response.sendFile(filepath);
-    } else {
-      throw new NotFoundException();
-    }
-  }
-
   @Get('/algorithms')
   getAlgorithms(): Observable<string> | string {
     return this.engineService.getAlgorithmsREST();
diff --git a/api/src/engine/engine.interfaces.ts b/api/src/engine/engine.interfaces.ts
index 69da9f65a819f323731ac0a75f59a39811c58be3..281ae1f3f5e51ba1c42ec28b91ede0f6637c668a 100644
--- a/api/src/engine/engine.interfaces.ts
+++ b/api/src/engine/engine.interfaces.ts
@@ -1,4 +1,5 @@
 import { Observable } from 'rxjs';
+import { User } from 'src/auth/models/user.model';
 import { Configuration } from './models/configuration.model';
 import { Domain } from './models/domain.model';
 import { Algorithm } from './models/experiment/algorithm.model';
@@ -69,7 +70,18 @@ export interface IEngineService {
 
   editActiveUser(): Observable<string> | string;
 
-  logout(): void;
+  logout?(): void;
+
+  /**
+   * Method that login a user with username and password
+   * @param username
+   * @param password
+   * @returns User object or empty if user not found
+   */
+  login?(
+    username: string,
+    password: string,
+  ): Promise<User | undefined> | User | undefined;
 
   getPassthrough?(suffix: string): Observable<string> | string;
 }
diff --git a/api/src/engine/engine.module.ts b/api/src/engine/engine.module.ts
index 8831eb2aeb9f62e9469de310502d7d2740e6926d..3f5cf47b16136f4485649ad6ef81b96ed2bc32e5 100644
--- a/api/src/engine/engine.module.ts
+++ b/api/src/engine/engine.module.ts
@@ -1,11 +1,8 @@
-import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
 import { HttpModule, HttpService } from '@nestjs/axios';
 import { DynamicModule, Global, Logger, Module } from '@nestjs/common';
 import { REQUEST } from '@nestjs/core';
-import { GraphQLModule } from '@nestjs/graphql';
 import { Request } from 'express';
-import { join } from 'path';
-import { AssetsService } from './assets.service';
+import { IncomingMessage } from 'http';
 import { ENGINE_MODULE_OPTIONS, ENGINE_SERVICE } from './engine.constants';
 import { EngineController } from './engine.controller';
 import { IEngineOptions, IEngineService } from './engine.interfaces';
@@ -16,64 +13,52 @@ import { EngineResolver } from './engine.resolver';
 export class EngineModule {
   private static readonly logger = new Logger(EngineModule.name);
 
-  static async forRootAsync(options: IEngineOptions): Promise<DynamicModule> {
+  static forRoot(options?: Partial<IEngineOptions>): DynamicModule {
     const optionsProvider = {
       provide: ENGINE_MODULE_OPTIONS,
-      useValue: options,
+      useValue: {
+        type: process.env.ENGINE_TYPE,
+        baseurl: process.env.ENGINE_BASE_URL,
+        ...(options ?? {}),
+      },
     };
 
     const engineProvider = {
       provide: ENGINE_SERVICE,
       useFactory: async (httpService: HttpService, req: Request) => {
-        return await this.createEngineConnection(options, httpService, req);
+        return await this.createEngineConnection(
+          optionsProvider.useValue,
+          httpService,
+          req,
+        );
       },
       inject: [HttpService, REQUEST],
     };
 
     return {
       module: EngineModule,
-      imports: [
-        HttpModule,
-        GraphQLModule.forRoot<ApolloDriverConfig>({
-          driver: ApolloDriver,
-          autoSchemaFile: join(process.cwd(), 'src/schema.gql'),
-          context: ({ req, res }) => ({ req, res }),
-          cors: {
-            credentials: true,
-            origin: [
-              /http:\/\/localhost($|:\d*)/,
-              /http:\/\/127.0.0.1($|:\d*)/,
-            ],
-          },
-        }),
-      ],
-      providers: [
-        optionsProvider,
-        engineProvider,
-        EngineResolver,
-        AssetsService,
-      ],
+      imports: [HttpModule],
+      providers: [optionsProvider, engineProvider, EngineResolver],
       controllers: [EngineController],
       exports: [optionsProvider, engineProvider],
     };
   }
 
   private static async createEngineConnection(
-    options: IEngineOptions,
+    opt: IEngineOptions,
     httpService: HttpService,
     req: Request,
   ): Promise<IEngineService> {
     try {
-      const service = await import(
-        `./connectors/${options.type}/main.connector`
-      );
-      const engine = new service.default(options, httpService, req);
+      const service = await import(`./connectors/${opt.type}/main.connector`);
+      const gqlRequest = req && req['req']; // graphql headers exception
+      const request =
+        gqlRequest && gqlRequest instanceof IncomingMessage ? gqlRequest : req;
+      const engine = new service.default(opt, httpService, request);
 
       return engine;
     } catch (e) {
-      this.logger.error(
-        `There is a problem with the connector '${options.type}'`,
-      );
+      this.logger.error(`There is a problem with the connector '${opt.type}'`);
       this.logger.verbose(e);
     }
   }
diff --git a/api/src/engine/engine.resolver.ts b/api/src/engine/engine.resolver.ts
index a67967c8d1db1c13b9cfcf2aa64ae12a19661d96..ac8845c7e04e77ddb05f56cebf1b1e02ad316fdd 100644
--- a/api/src/engine/engine.resolver.ts
+++ b/api/src/engine/engine.resolver.ts
@@ -1,8 +1,9 @@
-import { Inject, UseInterceptors } from '@nestjs/common';
+import { Inject, UseGuards } from '@nestjs/common';
 import { Args, Mutation, Query, Resolver } from '@nestjs/graphql';
+import { JwtAuthGuard } from 'src/auth/guards/jwt-auth.guard';
+import { Md5 } from 'ts-md5';
 import { ENGINE_MODULE_OPTIONS, ENGINE_SERVICE } from './engine.constants';
 import { IEngineOptions, IEngineService } from './engine.interfaces';
-import { ErrorsInterceptor } from './interceptors/errors.interceptor';
 import { Configuration } from './models/configuration.model';
 import { Domain } from './models/domain.model';
 import { Algorithm } from './models/experiment/algorithm.model';
@@ -13,9 +14,8 @@ import {
 import { ExperimentCreateInput } from './models/experiment/input/experiment-create.input';
 import { ExperimentEditInput } from './models/experiment/input/experiment-edit.input';
 import { ListExperiments } from './models/experiment/list-experiments.model';
-import { Md5 } from 'ts-md5';
 
-@UseInterceptors(ErrorsInterceptor)
+@UseGuards(JwtAuthGuard)
 @Resolver()
 export class EngineResolver {
   constructor(
diff --git a/api/src/engine/interceptors/errors.interceptor.ts b/api/src/engine/interceptors/errors.interceptor.ts
index 528e62ae1b525d3f8618070a20c924cc0f140e7d..47e61fc3aab815fa820b1180b655a65529723dc0 100644
--- a/api/src/engine/interceptors/errors.interceptor.ts
+++ b/api/src/engine/interceptors/errors.interceptor.ts
@@ -1,4 +1,3 @@
-import { HttpService } from '@nestjs/axios';
 import {
   CallHandler,
   HttpException,
@@ -17,7 +16,6 @@ export class ErrorsInterceptor implements NestInterceptor {
   private readonly logger: Logger;
 
   constructor(
-    private httpService: HttpService,
     @Inject(ENGINE_MODULE_OPTIONS) private readonly options: IEngineOptions,
   ) {
     // Logger name is the engine name
diff --git a/api/src/files/files.controller.spec.ts b/api/src/files/files.controller.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4ff16a6ce0d67ecfb2e16e7b3ded0df966e7878e
--- /dev/null
+++ b/api/src/files/files.controller.spec.ts
@@ -0,0 +1,42 @@
+import { Test, TestingModule } from '@nestjs/testing';
+import { ENGINE_MODULE_OPTIONS } from '../engine/engine.constants';
+import { FilesController } from './files.controller';
+import { FilesService } from './files.service';
+
+describe('FilesController', () => {
+  let service: FilesService;
+
+  beforeEach(async () => {
+    const module: TestingModule = await Test.createTestingModule({
+      controllers: [FilesController],
+      providers: [
+        {
+          provide: ENGINE_MODULE_OPTIONS,
+          useValue: {
+            type: 'test',
+            baseurl: 'test',
+          },
+        },
+        FilesService,
+      ],
+    }).compile();
+
+    service = module.get<FilesService>(FilesService);
+  });
+
+  it('getAssetFile', () => {
+    const filePathEmpty = service.getAssetFile('FILE_THAT_DOES_NOT_EXIST.txt');
+    const filePath = service.getAssetFile('tos.md');
+    const fileWithLFI = service.getAssetFile('../../../.env');
+
+    expect(filePathEmpty).toBeUndefined();
+    expect(fileWithLFI).toBeUndefined();
+    expect(filePath).toEqual(expect.anything());
+  });
+
+  it('markdown', () => {
+    const fileContent = service.getMarkdown('login.md', 'http://localtest');
+    expect(!!fileContent).toBeTruthy();
+    expect(fileContent.includes('http://localtest')).toBeTruthy();
+  });
+});
diff --git a/api/src/files/files.controller.ts b/api/src/files/files.controller.ts
new file mode 100644
index 0000000000000000000000000000000000000000..91f3f8549eaf05822ed51d456b29dc03e49a5a99
--- /dev/null
+++ b/api/src/files/files.controller.ts
@@ -0,0 +1,42 @@
+import {
+  Controller,
+  Get,
+  NotFoundException,
+  Param,
+  Req,
+  Res,
+} from '@nestjs/common';
+import { Request, Response } from 'express';
+import { join } from 'path/posix';
+import { FilesService } from './files.service';
+
+@Controller()
+export class FilesController {
+  constructor(private readonly filesService: FilesService) {}
+
+  @Get('assets/:name')
+  getFile(
+    @Req() request: Request,
+    @Res() response: Response,
+    @Param('name') filename: string,
+  ) {
+    if (filename.endsWith('.md')) {
+      const baseurl =
+        request.protocol +
+        '://' +
+        join(request.get('host'), process.env.BASE_URL_CONTEXT ?? '', 'assets'); // not full url, should consider "/services"
+      const text = this.filesService.getMarkdown(filename, baseurl);
+      response.setHeader('Content-Type', 'text/markdown');
+      return response.send(text);
+    }
+
+    const filepath = this.filesService.getAssetFile(filename);
+
+    // Test if the file exist, if not send 404
+    if (filepath) {
+      return response.sendFile(filepath);
+    } else {
+      throw new NotFoundException();
+    }
+  }
+}
diff --git a/api/src/files/files.module.ts b/api/src/files/files.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..bcfb777b965a099ff7337d7b716797a5b8fe368c
--- /dev/null
+++ b/api/src/files/files.module.ts
@@ -0,0 +1,9 @@
+import { Module } from '@nestjs/common';
+import { FilesController } from './files.controller';
+import { FilesService } from './files.service';
+
+@Module({
+  controllers: [FilesController],
+  providers: [FilesService],
+})
+export class FilesModule {}
diff --git a/api/src/engine/assets.service.ts b/api/src/files/files.service.ts
similarity index 90%
rename from api/src/engine/assets.service.ts
rename to api/src/files/files.service.ts
index 4abde93c41659838fd369e8cdd4bbf195970b220..685ab86da64b8f1ecf1ead4f11a4e8fe5701ff5d 100644
--- a/api/src/engine/assets.service.ts
+++ b/api/src/files/files.service.ts
@@ -1,11 +1,11 @@
 import { Inject, Injectable } from '@nestjs/common';
 import * as fs from 'fs';
 import { join } from 'path/posix';
-import { ENGINE_MODULE_OPTIONS } from './engine.constants';
-import { IEngineOptions } from './engine.interfaces';
+import { ENGINE_MODULE_OPTIONS } from '../engine/engine.constants';
+import { IEngineOptions } from '../engine/engine.interfaces';
 
 @Injectable()
-export class AssetsService {
+export class FilesService {
   constructor(
     @Inject(ENGINE_MODULE_OPTIONS)
     private readonly engineOptions: IEngineOptions,
diff --git a/api/src/main.ts b/api/src/main.ts
index 97c880c6c04e645b925fe1262997de18282174fa..e33633323795e658f72a120ce67e44dec64a1e1b 100644
--- a/api/src/main.ts
+++ b/api/src/main.ts
@@ -1,7 +1,7 @@
 import { NestFactory } from '@nestjs/core';
 import { NestExpressApplication } from '@nestjs/platform-express';
-import { join } from 'path/posix';
 import { AppModule } from './main/app.module';
+import * as cookieParser from 'cookie-parser';
 
 const CORS_URL = process.env.CORS_URL ?? process.env.ENGINE_BASE_URL;
 
@@ -17,6 +17,8 @@ async function bootstrap() {
     },
   });
 
+  app.use(cookieParser());
+
   await app.listen(process.env.GATEWAY_PORT);
 }
 bootstrap();
diff --git a/api/src/main/app.module.ts b/api/src/main/app.module.ts
index 7638ce2f6f28ce28f57f57974833ba8207669bba..dc204f2c50b100bccff9430051c040538c50da4e 100644
--- a/api/src/main/app.module.ts
+++ b/api/src/main/app.module.ts
@@ -1,6 +1,11 @@
+import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
 import { Module } from '@nestjs/common';
 import { ConfigModule } from '@nestjs/config';
+import { GraphQLModule } from '@nestjs/graphql';
+import { join } from 'path';
+import { AuthModule } from 'src/auth/auth.module';
 import { EngineModule } from 'src/engine/engine.module';
+import { FilesModule } from 'src/files/files.module';
 import { AppController } from './app.controller';
 import { AppService } from './app.service';
 
@@ -10,10 +15,21 @@ import { AppService } from './app.service';
       isGlobal: true,
       envFilePath: ['.env', '.env.defaults'],
     }),
-    EngineModule.forRootAsync({
+    GraphQLModule.forRoot<ApolloDriverConfig>({
+      driver: ApolloDriver,
+      autoSchemaFile: join(process.cwd(), 'src/schema.gql'),
+      context: ({ req, res }) => ({ req, res }),
+      cors: {
+        credentials: true,
+        origin: [/http:\/\/localhost($|:\d*)/, /http:\/\/127.0.0.1($|:\d*)/],
+      },
+    }),
+    EngineModule.forRoot({
       type: process.env.ENGINE_TYPE,
       baseurl: process.env.ENGINE_BASE_URL,
     }),
+    AuthModule,
+    FilesModule,
   ],
   controllers: [AppController],
   providers: [AppService],
diff --git a/api/src/schema.gql b/api/src/schema.gql
index 9fb8aadc4ad75433fbaa7215ecde5afe71110e58..0804f393996f71a257f74595bc84f138262bc2f4 100644
--- a/api/src/schema.gql
+++ b/api/src/schema.gql
@@ -2,6 +2,19 @@
 # THIS FILE WAS AUTOMATICALLY GENERATED (DO NOT MODIFY)
 # ------------------------------------------------------
 
+type User {
+  id: String!
+  username: String!
+  fullname: String
+  email: String
+  agreeNDA: Boolean
+}
+
+type AuthenticationOutput {
+  user: User!
+  accessToken: String!
+}
+
 type Configuration {
   connectorId: String!
   galaxy: Boolean
@@ -224,6 +237,8 @@ type Mutation {
   createExperiment(data: ExperimentCreateInput!, isTransient: Boolean = false): Experiment!
   editExperiment(id: String!, data: ExperimentEditInput!): Experiment!
   removeExperiment(id: String!): PartialExperiment!
+  login(variables: AuthenticationInput!): AuthenticationOutput!
+  logout: Boolean!
 }
 
 input ExperimentCreateInput {
@@ -265,3 +280,8 @@ input ExperimentEditInput {
   shared: Boolean
   viewed: Boolean
 }
+
+input AuthenticationInput {
+  username: String!
+  password: String!
+}