diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3d352420ac80f93a9101b025cceec9e3014b8fb1..683abfea1ec8db4ce91c6eb5fb7df95375fd2c5f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,8 +12,21 @@
 
 **💥 Breaking:**
 **⚠️ Deprecation:**
+
+### 🔨 Internal / Developers
+- No modifications / Keep major and minors versions in sync with all parts of the project
 -->
 
+## 3.0.0 (?)
+
+### ✨ Feature
+- Login to Dojo app via Gitlab OAuth
+- User access type management
+    - **student**: default access type for new users
+    - **teachingStaff**: student access + possibility of creating assignment
+    - **admin**: teachingStaff access + access to user access management
+
+
 ## 2.2.0 (2023-10-16)
 
 ### ✨ Feature
diff --git a/ExpressAPI/.env.vault b/ExpressAPI/.env.vault
index 7e45ae4669f2d45eb5ff0b09d06bfda38a329afb..0fddeea52298fa783288af4b6e389fc290d51817 100644
--- a/ExpressAPI/.env.vault
+++ b/ExpressAPI/.env.vault
@@ -4,8 +4,8 @@
 #/--------------------------------------------------/
 
 # development
-DOTENV_VAULT_DEVELOPMENT="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"
+DOTENV_VAULT_DEVELOPMENT="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"
 
 # production
-DOTENV_VAULT_PRODUCTION="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"
+DOTENV_VAULT_PRODUCTION="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"
 
diff --git a/ExpressAPI/package-lock.json b/ExpressAPI/package-lock.json
index 8f963c7eac7601ac218372bff36cd4a480d1dc0e..820606b932de494ee9fa913b08af03bbe352ebfa 100644
--- a/ExpressAPI/package-lock.json
+++ b/ExpressAPI/package-lock.json
@@ -1,18 +1,17 @@
 {
     "name": "dojo_backend_api",
-    "version": "2.2.0",
+    "version": "3.0.0",
     "lockfileVersion": 3,
     "requires": true,
     "packages": {
         "": {
             "name": "dojo_backend_api",
-            "version": "2.2.0",
+            "version": "3.0.0",
             "license": "AGPLv3",
             "dependencies": {
                 "@prisma/client": "^5.1.1",
                 "ajv": "^8.12.0",
                 "axios": "^1.4.0",
-                "bcryptjs": "^2.4.3",
                 "compression": "^1.7.4",
                 "cors": "^2.8.5",
                 "dotenv": "^16.3.1",
@@ -36,7 +35,6 @@
                 "winston": "^3.8.2"
             },
             "devDependencies": {
-                "@types/bcryptjs": "^2.4.2",
                 "@types/compression": "^1.7.2",
                 "@types/cors": "^2.8.13",
                 "@types/express": "^4.17.17",
@@ -58,9 +56,9 @@
             }
         },
         "node_modules/@colors/colors": {
-            "version": "1.5.0",
-            "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz",
-            "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==",
+            "version": "1.6.0",
+            "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz",
+            "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==",
             "engines": {
                 "node": ">=0.1.90"
             }
@@ -530,21 +528,21 @@
             "dev": true
         },
         "node_modules/@oclif/screen": {
-            "version": "3.0.7",
-            "resolved": "https://registry.npmjs.org/@oclif/screen/-/screen-3.0.7.tgz",
-            "integrity": "sha512-jQBPHcMh5rcIPKdqA6xlzioLOmkaVnjg2MVyjMzBKV8hDhLWNSiZqx7NAWXpP70v2LFvGdVoV8BSbK9iID3eHg==",
+            "version": "3.0.8",
+            "resolved": "https://registry.npmjs.org/@oclif/screen/-/screen-3.0.8.tgz",
+            "integrity": "sha512-yx6KAqlt3TAHBduS2fMQtJDL2ufIHnDRArrJEOoTTuizxqmjLT+psGYOHpmMl3gvQpFJ11Hs76guUUktzAF9Bg==",
             "dev": true,
             "engines": {
                 "node": ">=12.0.0"
             }
         },
         "node_modules/@prisma/client": {
-            "version": "5.3.1",
-            "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.3.1.tgz",
-            "integrity": "sha512-ArOKjHwdFZIe1cGU56oIfy7wRuTn0FfZjGuU/AjgEBOQh+4rDkB6nF+AGHP8KaVpkBIiHGPQh3IpwQ3xDMdO0Q==",
+            "version": "5.4.2",
+            "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.4.2.tgz",
+            "integrity": "sha512-2xsPaz4EaMKj1WS9iW6MlPhmbqtBsXAOeVttSePp8vTFTtvzh2hZbDgswwBdSCgPzmmwF+tLB259QzggvCmJqA==",
             "hasInstallScript": true,
             "dependencies": {
-                "@prisma/engines-version": "5.3.1-2.61e140623197a131c2a6189271ffee05a7aa9a59"
+                "@prisma/engines-version": "5.4.1-2.ac9d7041ed77bcc8a8dbd2ab6616b39013829574"
             },
             "engines": {
                 "node": ">=16.13"
@@ -559,16 +557,16 @@
             }
         },
         "node_modules/@prisma/engines": {
-            "version": "5.3.1",
-            "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.3.1.tgz",
-            "integrity": "sha512-6QkILNyfeeN67BNEPEtkgh3Xo2tm6D7V+UhrkBbRHqKw9CTaz/vvTP/ROwYSP/3JT2MtIutZm/EnhxUiuOPVDA==",
+            "version": "5.4.2",
+            "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.4.2.tgz",
+            "integrity": "sha512-fqeucJ3LH0e1eyFdT0zRx+oETLancu5+n4lhiYECyEz6H2RDskPJHJYHkVc0LhkU4Uv7fuEnppKU3nVKNzMh8g==",
             "devOptional": true,
             "hasInstallScript": true
         },
         "node_modules/@prisma/engines-version": {
-            "version": "5.3.1-2.61e140623197a131c2a6189271ffee05a7aa9a59",
-            "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.3.1-2.61e140623197a131c2a6189271ffee05a7aa9a59.tgz",
-            "integrity": "sha512-y5qbUi3ql2Xg7XraqcXEdMHh0MocBfnBzDn5GbV1xk23S3Mq8MGs+VjacTNiBh3dtEdUERCrUUG7Z3QaJ+h79w=="
+            "version": "5.4.1-2.ac9d7041ed77bcc8a8dbd2ab6616b39013829574",
+            "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.4.1-2.ac9d7041ed77bcc8a8dbd2ab6616b39013829574.tgz",
+            "integrity": "sha512-wvupDL4AA1vf4TQNANg7kR7y98ITqPsk6aacfBxZKtrJKRIsWjURHkZCGcQliHdqCiW/hGreO6d6ZuSv9MhdAA=="
         },
         "node_modules/@tsconfig/node10": {
             "version": "1.0.9",
@@ -594,16 +592,10 @@
             "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
             "dev": true
         },
-        "node_modules/@types/bcryptjs": {
-            "version": "2.4.4",
-            "resolved": "https://registry.npmjs.org/@types/bcryptjs/-/bcryptjs-2.4.4.tgz",
-            "integrity": "sha512-9wlJI7k5gRyJEC4yrV7DubzNQFTPiykYxUA6lBtsk5NlOfW9oWLJ1HdIA4YtE+6C3i3mTpDQQEosJ2rVZfBWnw==",
-            "dev": true
-        },
         "node_modules/@types/body-parser": {
-            "version": "1.19.3",
-            "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.3.tgz",
-            "integrity": "sha512-oyl4jvAfTGX9Bt6Or4H9ni1Z447/tQuxnZsytsCaExKlmJiU8sFgnIBRzJUpKwB5eWn9HuBYlUlVA74q/yN0eQ==",
+            "version": "1.19.4",
+            "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.4.tgz",
+            "integrity": "sha512-N7UDG0/xiPQa2D/XrVJXjkWbpqHCd2sBaB32ggRF2l83RhPfamgKGF8gwwqyksS95qUS5ZYF9aF+lLPRlwI2UA==",
             "dev": true,
             "dependencies": {
                 "@types/connect": "*",
@@ -611,45 +603,45 @@
             }
         },
         "node_modules/@types/cli-progress": {
-            "version": "3.11.3",
-            "resolved": "https://registry.npmjs.org/@types/cli-progress/-/cli-progress-3.11.3.tgz",
-            "integrity": "sha512-/+C9xAdVtc+g5yHHkGBThgAA8rYpi5B+2ve3wLtybYj0JHEBs57ivR4x/zGfSsplRnV+psE91Nfin1soNKqz5Q==",
+            "version": "3.11.4",
+            "resolved": "https://registry.npmjs.org/@types/cli-progress/-/cli-progress-3.11.4.tgz",
+            "integrity": "sha512-yufTxeeNCZuEIxx2uebK8lpSAsJM4lvzakm/VxzYhDtqhXCzwH9jpn7nPCxzrROuEbLATqhFq4MIPoG0tlrsvw==",
             "dev": true,
             "dependencies": {
                 "@types/node": "*"
             }
         },
         "node_modules/@types/compression": {
-            "version": "1.7.3",
-            "resolved": "https://registry.npmjs.org/@types/compression/-/compression-1.7.3.tgz",
-            "integrity": "sha512-rKquEGjebqizyHNMOpaE/4FdYR5VQiWFeesqYfvJU0seSEyB4625UGhNOO/qIkH10S3wftiV7oefc8WdLZ/gCQ==",
+            "version": "1.7.4",
+            "resolved": "https://registry.npmjs.org/@types/compression/-/compression-1.7.4.tgz",
+            "integrity": "sha512-sdFVnQJRkQBX83ydsLCBm4A39p45y0QkxdAR689yOtAFNbbS9Acrp86RZWJj6BHRXyZH9tX4t1dU7XDiGdY3nA==",
             "dev": true,
             "dependencies": {
                 "@types/express": "*"
             }
         },
         "node_modules/@types/connect": {
-            "version": "3.4.36",
-            "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.36.tgz",
-            "integrity": "sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==",
+            "version": "3.4.37",
+            "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.37.tgz",
+            "integrity": "sha512-zBUSRqkfZ59OcwXon4HVxhx5oWCJmc0OtBTK05M+p0dYjgN6iTwIL2T/WbsQZrEsdnwaF9cWQ+azOnpPvIqY3Q==",
             "dev": true,
             "dependencies": {
                 "@types/node": "*"
             }
         },
         "node_modules/@types/cors": {
-            "version": "2.8.14",
-            "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.14.tgz",
-            "integrity": "sha512-RXHUvNWYICtbP6s18PnOCaqToK8y14DnLd75c6HfyKf228dxy7pHNOQkxPtvXKp/hINFMDjbYzsj63nnpPMSRQ==",
+            "version": "2.8.15",
+            "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.15.tgz",
+            "integrity": "sha512-n91JxbNLD8eQIuXDIChAN1tCKNWCEgpceU9b7ZMbFA+P+Q4yIeh80jizFLEvolRPc1ES0VdwFlGv+kJTSirogw==",
             "dev": true,
             "dependencies": {
                 "@types/node": "*"
             }
         },
         "node_modules/@types/express": {
-            "version": "4.17.18",
-            "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.18.tgz",
-            "integrity": "sha512-Sxv8BSLLgsBYmcnGdGjjEjqET2U+AKAdCRODmMiq02FgjwuV75Ut85DRpvFjyw/Mk0vgUOliGRU0UUmuuZHByQ==",
+            "version": "4.17.20",
+            "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.20.tgz",
+            "integrity": "sha512-rOaqlkgEvOW495xErXMsmyX3WKBInbhG5eqojXYi3cGUaLoRDlXa5d52fkfWZT963AZ3v2eZ4MbKE6WpDAGVsw==",
             "dev": true,
             "dependencies": {
                 "@types/body-parser": "*",
@@ -659,9 +651,9 @@
             }
         },
         "node_modules/@types/express-serve-static-core": {
-            "version": "4.17.37",
-            "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.37.tgz",
-            "integrity": "sha512-ZohaCYTgGFcOP7u6aJOhY9uIZQgZ2vxC2yWoArY+FeDXlqeH66ZVBjgvg+RLVAS/DWNq4Ap9ZXu1+SUQiiWYMg==",
+            "version": "4.17.38",
+            "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.38.tgz",
+            "integrity": "sha512-hXOtc0tuDHZPFwwhuBJXPbjemWtXnJjbvuuyNH2Y5Z6in+iXc63c4eXYDc7GGGqHy+iwYqAJMdaItqdnbcBKmg==",
             "dev": true,
             "dependencies": {
                 "@types/node": "*",
@@ -671,78 +663,81 @@
             }
         },
         "node_modules/@types/http-errors": {
-            "version": "2.0.2",
-            "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.2.tgz",
-            "integrity": "sha512-lPG6KlZs88gef6aD85z3HNkztpj7w2R7HmR3gygjfXCQmsLloWNARFkMuzKiiY8FGdh1XDpgBdrSf4aKDiA7Kg==",
+            "version": "2.0.3",
+            "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.3.tgz",
+            "integrity": "sha512-pP0P/9BnCj1OVvQR2lF41EkDG/lWWnDyA203b/4Fmi2eTyORnBtcDoKDwjWQthELrBvWkMOrvSOnZ8OVlW6tXA==",
             "dev": true
         },
         "node_modules/@types/jsonwebtoken": {
-            "version": "9.0.3",
-            "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz",
-            "integrity": "sha512-b0jGiOgHtZ2jqdPgPnP6WLCXZk1T8p06A/vPGzUvxpFGgKMbjXJDjC5m52ErqBnIuWZFgGoIJyRdeG5AyreJjA==",
+            "version": "9.0.4",
+            "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.4.tgz",
+            "integrity": "sha512-8UYapdmR0QlxgvJmyE8lP7guxD0UGVMfknsdtCFZh4ovShdBl3iOI4zdvqBHrB/IS+xUj3PSx73Qkey1fhWz+g==",
             "dev": true,
             "dependencies": {
                 "@types/node": "*"
             }
         },
         "node_modules/@types/mime": {
-            "version": "1.3.3",
-            "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.3.tgz",
-            "integrity": "sha512-Ys+/St+2VF4+xuY6+kDIXGxbNRO0mesVg0bbxEfB97Od1Vjpjx9KD1qxs64Gcb3CWPirk9Xe+PT4YiiHQ9T+eg==",
+            "version": "1.3.4",
+            "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.4.tgz",
+            "integrity": "sha512-1Gjee59G25MrQGk8bsNvC6fxNiRgUlGn2wlhGf95a59DrprnnHk80FIMMFG9XHMdrfsuA119ht06QPDXA1Z7tw==",
             "dev": true
         },
         "node_modules/@types/morgan": {
-            "version": "1.9.6",
-            "resolved": "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.6.tgz",
-            "integrity": "sha512-xfKogz5WcKww2DAiVT9zxMgrqQt+Shq8tDVeLT+otoj6dJnkRkyJxMF51mHtUc3JCPKGk5x1EBU0buuGpfftlQ==",
+            "version": "1.9.7",
+            "resolved": "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.7.tgz",
+            "integrity": "sha512-4sJFBUBrIZkP5EvMm1L6VCXp3SQe8dnXqlVpe1jsmTjS1JQVmSjnpMNs8DosQd6omBi/K7BSKJ6z/Mc3ki0K9g==",
             "dev": true,
             "dependencies": {
                 "@types/node": "*"
             }
         },
         "node_modules/@types/multer": {
-            "version": "1.4.8",
-            "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.8.tgz",
-            "integrity": "sha512-VMZOW6mnmMMhA5m3fsCdXBwFwC+a+27/8gctNMuQC4f7UtWcF79KAFGoIfKZ4iqrElgWIa3j5vhMJDp0iikQ1g==",
+            "version": "1.4.9",
+            "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.9.tgz",
+            "integrity": "sha512-9NSvPJ2E8bNTc8XtJq1Cimx2Wrn2Ah48F15B2Du/hM8a8CHLhVbJMlF3ZCqhvMdht7Sa+YdP0aKP7N4fxDcrrg==",
             "dev": true,
             "dependencies": {
                 "@types/express": "*"
             }
         },
         "node_modules/@types/node": {
-            "version": "20.8.2",
-            "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.2.tgz",
-            "integrity": "sha512-Vvycsc9FQdwhxE3y3DzeIxuEJbWGDsnrxvMADzTDF/lcdR9/K+AQIeAghTQsHtotg/q0j3WEOYS/jQgSdWue3w==",
-            "dev": true
+            "version": "20.8.7",
+            "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.7.tgz",
+            "integrity": "sha512-21TKHHh3eUHIi2MloeptJWALuCu5H7HQTdTrWIFReA8ad+aggoX+lRes3ex7/FtpC+sVUpFMQ+QTfYr74mruiQ==",
+            "dev": true,
+            "dependencies": {
+                "undici-types": "~5.25.1"
+            }
         },
         "node_modules/@types/parse-link-header": {
-            "version": "2.0.1",
-            "resolved": "https://registry.npmjs.org/@types/parse-link-header/-/parse-link-header-2.0.1.tgz",
-            "integrity": "sha512-BrKNSrRTqn3UkMXvdVtr/znJch0PMBpEvEP8oBkxDx7eEGntuFLI+WpA5HGsNHK4SlqyhaMa+Ks0ViwyixQB5w==",
+            "version": "2.0.2",
+            "resolved": "https://registry.npmjs.org/@types/parse-link-header/-/parse-link-header-2.0.2.tgz",
+            "integrity": "sha512-RKU5SIF0oyM2ZI0ubw66FkM/0RJUv/r84I7vJcXkcICcfeOpd1WXfpcqkFJPaWli5z3YdxMsfWojyU5uofT6sA==",
             "dev": true
         },
         "node_modules/@types/qs": {
-            "version": "6.9.8",
-            "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.8.tgz",
-            "integrity": "sha512-u95svzDlTysU5xecFNTgfFG5RUWu1A9P0VzgpcIiGZA9iraHOdSzcxMxQ55DyeRaGCSxQi7LxXDI4rzq/MYfdg==",
+            "version": "6.9.9",
+            "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.9.tgz",
+            "integrity": "sha512-wYLxw35euwqGvTDx6zfY1vokBFnsK0HNrzc6xNHchxfO2hpuRg74GbkEW7e3sSmPvj0TjCDT1VCa6OtHXnubsg==",
             "dev": true
         },
         "node_modules/@types/range-parser": {
-            "version": "1.2.5",
-            "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.5.tgz",
-            "integrity": "sha512-xrO9OoVPqFuYyR/loIHjnbvvyRZREYKLjxV4+dY6v3FQR3stQ9ZxIGkaclF7YhI9hfjpuTbu14hZEy94qKLtOA==",
+            "version": "1.2.6",
+            "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.6.tgz",
+            "integrity": "sha512-+0autS93xyXizIYiyL02FCY8N+KkKPhILhcUSA276HxzreZ16kl+cmwvV2qAM/PuCCwPXzOXOWhiPcw20uSFcA==",
             "dev": true
         },
         "node_modules/@types/semver": {
-            "version": "7.5.3",
-            "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.3.tgz",
-            "integrity": "sha512-OxepLK9EuNEIPxWNME+C6WwbRAOOI2o2BaQEGzz5Lu2e4Z5eDnEo+/aVEDMIXywoJitJ7xWd641wrGLZdtwRyw==",
+            "version": "7.5.4",
+            "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.4.tgz",
+            "integrity": "sha512-MMzuxN3GdFwskAnb6fz0orFvhfqi752yjaXylr0Rp4oDg5H0Zn1IuyRhDVvYOwAXoJirx2xuS16I3WjxnAIHiQ==",
             "dev": true
         },
         "node_modules/@types/send": {
-            "version": "0.17.2",
-            "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.2.tgz",
-            "integrity": "sha512-aAG6yRf6r0wQ29bkS+x97BIs64ZLxeE/ARwyS6wrldMm3C1MdKwCcnnEwMC1slI8wuxJOpiUH9MioC0A0i+GJw==",
+            "version": "0.17.3",
+            "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.3.tgz",
+            "integrity": "sha512-/7fKxvKUoETxjFUsuFlPB9YndePpxxRAOfGC/yJdc9kTjTeP5kRCTzfnE8kPUKCeyiyIZu0YQ76s50hCedI1ug==",
             "dev": true,
             "dependencies": {
                 "@types/mime": "^1",
@@ -750,9 +745,9 @@
             }
         },
         "node_modules/@types/serve-static": {
-            "version": "1.15.3",
-            "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.3.tgz",
-            "integrity": "sha512-yVRvFsEMrv7s0lGhzrggJjNOSmZCdgCjw9xWrPr/kNNLp6FaDfMC1KaYl3TSJ0c58bECwNBMoQrZJ8hA8E1eFg==",
+            "version": "1.15.4",
+            "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.4.tgz",
+            "integrity": "sha512-aqqNfs1XTF0HDrFdlY//+SGUxmdSUbjeRXb5iaZc3x0/vMbYmdw9qvOgHWOyyLFxSSRnUuP5+724zBgfw8/WAw==",
             "dev": true,
             "dependencies": {
                 "@types/http-errors": "*",
@@ -770,14 +765,14 @@
             }
         },
         "node_modules/@types/triple-beam": {
-            "version": "1.3.3",
-            "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.3.tgz",
-            "integrity": "sha512-6tOUG+nVHn0cJbVp25JFayS5UE6+xlbcNF9Lo9mU7U0zk3zeUShZied4YEQZjy1JBF043FSkdXw8YkUJuVtB5g=="
+            "version": "1.3.4",
+            "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.4.tgz",
+            "integrity": "sha512-HlJjF3wxV4R2VQkFpKe0YqJLilYNgtRtsqqZtby7RkVsSs+i+vbyzjtUwpFEdUCKcrGzCiEJE7F/0mKjh0sunA=="
         },
         "node_modules/@types/uuid": {
-            "version": "9.0.4",
-            "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.4.tgz",
-            "integrity": "sha512-zAuJWQflfx6dYJM62vna+Sn5aeSWhh3OB+wfUEACNcqUSc0AGc5JKl+ycL1vrH7frGTXhJchYjE1Hak8L819dA==",
+            "version": "9.0.6",
+            "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.6.tgz",
+            "integrity": "sha512-BT2Krtx4xaO6iwzwMFUYvWBWkV2pr37zD68Vmp1CDV196MzczBRxuEpD6Pr395HAgebC/co7hOphs53r8V7jew==",
             "dev": true
         },
         "node_modules/abbrev": {
@@ -1006,11 +1001,6 @@
                 "node": ">= 0.8"
             }
         },
-        "node_modules/bcryptjs": {
-            "version": "2.4.3",
-            "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz",
-            "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ=="
-        },
         "node_modules/bignumber.js": {
             "version": "9.0.0",
             "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz",
@@ -2117,9 +2107,12 @@
             }
         },
         "node_modules/function-bind": {
-            "version": "1.1.1",
-            "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
-            "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
+            "version": "1.1.2",
+            "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+            "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
         },
         "node_modules/genversion": {
             "version": "3.1.1",
@@ -2875,16 +2868,19 @@
             }
         },
         "node_modules/logform": {
-            "version": "2.5.1",
-            "resolved": "https://registry.npmjs.org/logform/-/logform-2.5.1.tgz",
-            "integrity": "sha512-9FyqAm9o9NKKfiAKfZoYo9bGXXuwMkxQiQttkT4YjjVtQVIQtK6LmVtlxmCaFswo6N4AfEkHqZTV0taDtPotNg==",
+            "version": "2.6.0",
+            "resolved": "https://registry.npmjs.org/logform/-/logform-2.6.0.tgz",
+            "integrity": "sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ==",
             "dependencies": {
-                "@colors/colors": "1.5.0",
+                "@colors/colors": "1.6.0",
                 "@types/triple-beam": "^1.3.2",
                 "fecha": "^4.2.0",
                 "ms": "^2.1.1",
                 "safe-stable-stringify": "^2.3.1",
                 "triple-beam": "^1.3.0"
+            },
+            "engines": {
+                "node": ">= 12.0.0"
             }
         },
         "node_modules/logform/node_modules/ms": {
@@ -3128,9 +3124,9 @@
             }
         },
         "node_modules/node": {
-            "version": "20.7.0",
-            "resolved": "https://registry.npmjs.org/node/-/node-20.7.0.tgz",
-            "integrity": "sha512-GiKqtgSALW8+W7Zi9T2AI9aME8hJg+1EESH6O7Xmk4k1gJfBKOolpy9gdg8vCyR8dMeJlp5GQZOYnvxsu8v5TA==",
+            "version": "20.8.1",
+            "resolved": "https://registry.npmjs.org/node/-/node-20.8.1.tgz",
+            "integrity": "sha512-gG+nhijBgjTNjgPB4BxKaBKnFS49bngOuxw1/jVh4vlscg/6K+00n+2Q4LEnklTZJXFUQM+AqyGdfvh9h1JknA==",
             "hasInstallScript": true,
             "dependencies": {
                 "node-bin-setup": "^1.0.0"
@@ -3236,9 +3232,9 @@
             }
         },
         "node_modules/npm": {
-            "version": "9.8.1",
-            "resolved": "https://registry.npmjs.org/npm/-/npm-9.8.1.tgz",
-            "integrity": "sha512-AfDvThQzsIXhYgk9zhbk5R+lh811lKkLAeQMMhSypf1BM7zUafeIIBzMzespeuVEJ0+LvY36oRQYf7IKLzU3rw==",
+            "version": "9.9.0",
+            "resolved": "https://registry.npmjs.org/npm/-/npm-9.9.0.tgz",
+            "integrity": "sha512-wkd7sjz4KmdmddYQcd0aTP73P1cEuPlekeulz4jTDeMVx/Zo5XZ5KQ1z3eUzV3Q/WZpEO0NJXTrD5FNFe6fhCA==",
             "bundleDependencies": [
                 "@isaacs/string-locale-compare",
                 "@npmcli/arborist",
@@ -3283,6 +3279,7 @@
                 "ms",
                 "node-gyp",
                 "nopt",
+                "normalize-package-data",
                 "npm-audit-report",
                 "npm-install-checks",
                 "npm-package-arg",
@@ -3299,6 +3296,7 @@
                 "read",
                 "semver",
                 "sigstore",
+                "spdx-expression-parse",
                 "ssri",
                 "supports-color",
                 "tar",
@@ -3312,8 +3310,8 @@
             "dev": true,
             "dependencies": {
                 "@isaacs/string-locale-compare": "^1.1.0",
-                "@npmcli/arborist": "^6.3.0",
-                "@npmcli/config": "^6.2.1",
+                "@npmcli/arborist": "^6.5.0",
+                "@npmcli/config": "^6.4.0",
                 "@npmcli/fs": "^3.1.0",
                 "@npmcli/map-workspaces": "^3.0.4",
                 "@npmcli/package-json": "^4.0.1",
@@ -3337,12 +3335,12 @@
                 "is-cidr": "^4.0.2",
                 "json-parse-even-better-errors": "^3.0.0",
                 "libnpmaccess": "^7.0.2",
-                "libnpmdiff": "^5.0.19",
-                "libnpmexec": "^6.0.3",
-                "libnpmfund": "^4.0.19",
+                "libnpmdiff": "^5.0.20",
+                "libnpmexec": "^6.0.4",
+                "libnpmfund": "^4.2.1",
                 "libnpmhook": "^9.0.3",
                 "libnpmorg": "^5.0.4",
-                "libnpmpack": "^5.0.19",
+                "libnpmpack": "^5.0.20",
                 "libnpmpublish": "^7.5.0",
                 "libnpmsearch": "^6.0.2",
                 "libnpmteam": "^5.0.3",
@@ -3354,10 +3352,11 @@
                 "ms": "^2.1.2",
                 "node-gyp": "^9.4.0",
                 "nopt": "^7.2.0",
+                "normalize-package-data": "^5.0.0",
                 "npm-audit-report": "^5.0.0",
-                "npm-install-checks": "^6.1.1",
+                "npm-install-checks": "^6.2.0",
                 "npm-package-arg": "^10.1.0",
-                "npm-pick-manifest": "^8.0.1",
+                "npm-pick-manifest": "^8.0.2",
                 "npm-profile": "^7.0.1",
                 "npm-registry-fetch": "^14.0.5",
                 "npm-user-validate": "^2.0.0",
@@ -3369,7 +3368,8 @@
                 "qrcode-terminal": "^0.12.0",
                 "read": "^2.1.0",
                 "semver": "^7.5.4",
-                "sigstore": "^1.7.0",
+                "sigstore": "^1.9.0",
+                "spdx-expression-parse": "^3.0.1",
                 "ssri": "^10.0.4",
                 "supports-color": "^9.4.0",
                 "tar": "^6.1.15",
@@ -3472,7 +3472,7 @@
             "license": "ISC"
         },
         "node_modules/npm/node_modules/@npmcli/arborist": {
-            "version": "6.3.0",
+            "version": "6.5.0",
             "dev": true,
             "inBundle": true,
             "license": "ISC",
@@ -3495,7 +3495,7 @@
                 "json-stringify-nice": "^1.1.4",
                 "minimatch": "^9.0.0",
                 "nopt": "^7.0.0",
-                "npm-install-checks": "^6.0.0",
+                "npm-install-checks": "^6.2.0",
                 "npm-package-arg": "^10.1.0",
                 "npm-pick-manifest": "^8.0.1",
                 "npm-registry-fetch": "^14.0.3",
@@ -3519,7 +3519,7 @@
             }
         },
         "node_modules/npm/node_modules/@npmcli/config": {
-            "version": "6.2.1",
+            "version": "6.4.0",
             "dev": true,
             "inBundle": true,
             "license": "ISC",
@@ -3712,22 +3712,48 @@
                 "node": ">=14"
             }
         },
+        "node_modules/npm/node_modules/@sigstore/bundle": {
+            "version": "1.1.0",
+            "dev": true,
+            "inBundle": true,
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@sigstore/protobuf-specs": "^0.2.0"
+            },
+            "engines": {
+                "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+            }
+        },
         "node_modules/npm/node_modules/@sigstore/protobuf-specs": {
-            "version": "0.1.0",
+            "version": "0.2.1",
+            "dev": true,
+            "inBundle": true,
+            "license": "Apache-2.0",
+            "engines": {
+                "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+            }
+        },
+        "node_modules/npm/node_modules/@sigstore/sign": {
+            "version": "1.0.0",
             "dev": true,
             "inBundle": true,
             "license": "Apache-2.0",
+            "dependencies": {
+                "@sigstore/bundle": "^1.1.0",
+                "@sigstore/protobuf-specs": "^0.2.0",
+                "make-fetch-happen": "^11.0.1"
+            },
             "engines": {
                 "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
             }
         },
         "node_modules/npm/node_modules/@sigstore/tuf": {
-            "version": "1.0.2",
+            "version": "1.0.3",
             "dev": true,
             "inBundle": true,
             "license": "Apache-2.0",
             "dependencies": {
-                "@sigstore/protobuf-specs": "^0.1.0",
+                "@sigstore/protobuf-specs": "^0.2.0",
                 "tuf-js": "^1.1.7"
             },
             "engines": {
@@ -4716,12 +4742,12 @@
             }
         },
         "node_modules/npm/node_modules/libnpmdiff": {
-            "version": "5.0.19",
+            "version": "5.0.20",
             "dev": true,
             "inBundle": true,
             "license": "ISC",
             "dependencies": {
-                "@npmcli/arborist": "^6.3.0",
+                "@npmcli/arborist": "^6.5.0",
                 "@npmcli/disparity-colors": "^3.0.0",
                 "@npmcli/installed-package-contents": "^2.0.2",
                 "binary-extensions": "^2.2.0",
@@ -4736,12 +4762,12 @@
             }
         },
         "node_modules/npm/node_modules/libnpmexec": {
-            "version": "6.0.3",
+            "version": "6.0.4",
             "dev": true,
             "inBundle": true,
             "license": "ISC",
             "dependencies": {
-                "@npmcli/arborist": "^6.3.0",
+                "@npmcli/arborist": "^6.5.0",
                 "@npmcli/run-script": "^6.0.0",
                 "ci-info": "^3.7.1",
                 "npm-package-arg": "^10.1.0",
@@ -4758,12 +4784,12 @@
             }
         },
         "node_modules/npm/node_modules/libnpmfund": {
-            "version": "4.0.19",
+            "version": "4.2.1",
             "dev": true,
             "inBundle": true,
             "license": "ISC",
             "dependencies": {
-                "@npmcli/arborist": "^6.3.0"
+                "@npmcli/arborist": "^6.5.0"
             },
             "engines": {
                 "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
@@ -4796,12 +4822,12 @@
             }
         },
         "node_modules/npm/node_modules/libnpmpack": {
-            "version": "5.0.19",
+            "version": "5.0.20",
             "dev": true,
             "inBundle": true,
             "license": "ISC",
             "dependencies": {
-                "@npmcli/arborist": "^6.3.0",
+                "@npmcli/arborist": "^6.5.0",
                 "@npmcli/run-script": "^6.0.0",
                 "npm-package-arg": "^10.1.0",
                 "pacote": "^15.0.8"
@@ -5347,7 +5373,7 @@
             }
         },
         "node_modules/npm/node_modules/npm-install-checks": {
-            "version": "6.1.1",
+            "version": "6.2.0",
             "dev": true,
             "inBundle": true,
             "license": "BSD-2-Clause",
@@ -5395,7 +5421,7 @@
             }
         },
         "node_modules/npm/node_modules/npm-pick-manifest": {
-            "version": "8.0.1",
+            "version": "8.0.2",
             "dev": true,
             "inBundle": true,
             "license": "ISC",
@@ -5889,13 +5915,15 @@
             }
         },
         "node_modules/npm/node_modules/sigstore": {
-            "version": "1.7.0",
+            "version": "1.9.0",
             "dev": true,
             "inBundle": true,
             "license": "Apache-2.0",
             "dependencies": {
-                "@sigstore/protobuf-specs": "^0.1.0",
-                "@sigstore/tuf": "^1.0.1",
+                "@sigstore/bundle": "^1.1.0",
+                "@sigstore/protobuf-specs": "^0.2.0",
+                "@sigstore/sign": "^1.0.0",
+                "@sigstore/tuf": "^1.0.3",
                 "make-fetch-happen": "^11.0.1"
             },
             "bin": {
@@ -6360,9 +6388,9 @@
             }
         },
         "node_modules/object-inspect": {
-            "version": "1.12.3",
-            "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
-            "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==",
+            "version": "1.13.0",
+            "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.0.tgz",
+            "integrity": "sha512-HQ4J+ic8hKrgIt3mqk6cVOVrW2ozL4KdvHlqpBv9vDYWx9ysAgENAdvy4FoGF+KFdhR7nQTNm5J0ctAeOwn+3g==",
             "funding": {
                 "url": "https://github.com/sponsors/ljharb"
             }
@@ -6562,13 +6590,13 @@
             }
         },
         "node_modules/prisma": {
-            "version": "5.3.1",
-            "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.3.1.tgz",
-            "integrity": "sha512-Wp2msQIlMPHe+5k5Od6xnsI/WNG7UJGgFUJgqv/ygc7kOECZapcSz/iU4NIEzISs3H1W9sFLjAPbg/gOqqtB7A==",
+            "version": "5.4.2",
+            "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.4.2.tgz",
+            "integrity": "sha512-GDMZwZy7mysB2oXU+angQqJ90iaPFdD0rHaZNkn+dio5NRkGLmMqmXs31//tg/qXT3iB0cTQwnGGQNuirhSTZg==",
             "devOptional": true,
             "hasInstallScript": true,
             "dependencies": {
-                "@prisma/engines": "5.3.1"
+                "@prisma/engines": "5.4.2"
             },
             "bin": {
                 "prisma": "build/index.js"
@@ -6747,9 +6775,9 @@
             }
         },
         "node_modules/resolve": {
-            "version": "1.22.6",
-            "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.6.tgz",
-            "integrity": "sha512-njhxM7mV12JfufShqGy3Rz8j11RPdLy4xi15UurGJeoHLfJpVXKdh3ueuOqbYUcDZnffr6X739JBo5LzyahEsw==",
+            "version": "1.22.8",
+            "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
+            "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
             "dependencies": {
                 "is-core-module": "^2.13.0",
                 "path-parse": "^1.0.7",
@@ -7376,6 +7404,12 @@
             "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==",
             "dev": true
         },
+        "node_modules/undici-types": {
+            "version": "5.25.3",
+            "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.25.3.tgz",
+            "integrity": "sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA==",
+            "dev": true
+        },
         "node_modules/universalify": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
@@ -7485,11 +7519,11 @@
             }
         },
         "node_modules/winston": {
-            "version": "3.10.0",
-            "resolved": "https://registry.npmjs.org/winston/-/winston-3.10.0.tgz",
-            "integrity": "sha512-nT6SIDaE9B7ZRO0u3UvdrimG0HkB7dSTAgInQnNR2SOPJ4bvq5q79+pXLftKmP52lJGW15+H5MCK0nM9D3KB/g==",
+            "version": "3.11.0",
+            "resolved": "https://registry.npmjs.org/winston/-/winston-3.11.0.tgz",
+            "integrity": "sha512-L3yR6/MzZAOl0DsysUXHVjOwv8mKZ71TrA/41EIduGpOOV5LQVodqN+QdQ6BS6PJ/RdIshZhq84P/fStEZkk7g==",
             "dependencies": {
-                "@colors/colors": "1.5.0",
+                "@colors/colors": "^1.6.0",
                 "@dabh/diagnostics": "^2.0.2",
                 "async": "^3.2.3",
                 "is-stream": "^2.0.0",
@@ -7506,16 +7540,16 @@
             }
         },
         "node_modules/winston-transport": {
-            "version": "4.5.0",
-            "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.5.0.tgz",
-            "integrity": "sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q==",
+            "version": "4.6.0",
+            "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.6.0.tgz",
+            "integrity": "sha512-wbBA9PbPAHxKiygo7ub7BYRiKxms0tpfU2ljtWzb3SjRjv5yl6Ozuy/TkXf00HTAt+Uylo3gSkNwzc4ME0wiIg==",
             "dependencies": {
                 "logform": "^2.3.2",
                 "readable-stream": "^3.6.0",
                 "triple-beam": "^1.3.0"
             },
             "engines": {
-                "node": ">= 6.4.0"
+                "node": ">= 12.0.0"
             }
         },
         "node_modules/winston-transport/node_modules/readable-stream": {
diff --git a/ExpressAPI/package.json b/ExpressAPI/package.json
index c251fe54ccd93983fb524e22c7c20c6ff99e0388..ea2f9c786f6ce4702db6f53ed8b9031f116f203b 100644
--- a/ExpressAPI/package.json
+++ b/ExpressAPI/package.json
@@ -1,7 +1,7 @@
 {
     "name"           : "dojo_backend_api",
     "description"    : "Backend API of the Dojo project",
-    "version"        : "2.2.0",
+    "version"        : "3.0.0",
     "license"        : "AGPLv3",
     "author"         : "Michaël Minelli <dojo@minelli.me>",
     "main"           : "dist/src/app.js",
@@ -24,7 +24,6 @@
         "@prisma/client"   : "^5.1.1",
         "ajv"              : "^8.12.0",
         "axios"            : "^1.4.0",
-        "bcryptjs"         : "^2.4.3",
         "compression"      : "^1.7.4",
         "cors"             : "^2.8.5",
         "dotenv"           : "^16.3.1",
@@ -48,7 +47,6 @@
         "winston"          : "^3.8.2"
     },
     "devDependencies": {
-        "@types/bcryptjs"         : "^2.4.2",
         "@types/compression"      : "^1.7.2",
         "@types/cors"             : "^2.8.13",
         "@types/express"          : "^4.17.17",
diff --git a/ExpressAPI/prisma/migrations/20231019130319_user_login_gitlab_migration/migration.sql b/ExpressAPI/prisma/migrations/20231019130319_user_login_gitlab_migration/migration.sql
new file mode 100644
index 0000000000000000000000000000000000000000..55d6884fff7db5497d31d0d2b9c2c5c4a8b087f2
--- /dev/null
+++ b/ExpressAPI/prisma/migrations/20231019130319_user_login_gitlab_migration/migration.sql
@@ -0,0 +1,20 @@
+/*
+  Warnings:
+
+  - You are about to drop the column `firstname` on the `User` table. All the data in the column will be lost.
+  - You are about to drop the column `lastname` on the `User` table. All the data in the column will be lost.
+  - You are about to drop the column `password` on the `User` table. All the data in the column will be lost.
+  - A unique constraint covering the columns `[gitlabUsername]` on the table `User` will be added. If there are existing duplicate values, this will fail.
+  - Added the required column `gitlabUsername` to the `User` table without a default value. This is not possible if the table is not empty.
+
+*/
+-- AlterTable
+ALTER TABLE `User` DROP COLUMN `firstname`,
+    DROP COLUMN `lastname`,
+    DROP COLUMN `password`,
+    ADD COLUMN `gitlabLastInfo` JSON NOT NULL,
+    ADD COLUMN `gitlabUsername` VARCHAR(191) NOT NULL,
+    ADD COLUMN `name` VARCHAR(191) NULL;
+
+-- CreateIndex
+CREATE UNIQUE INDEX `User_gitlabUsername_key` ON `User`(`gitlabUsername`);
diff --git a/ExpressAPI/prisma/migrations/20231019131336_add_user_roles/migration.sql b/ExpressAPI/prisma/migrations/20231019131336_add_user_roles/migration.sql
new file mode 100644
index 0000000000000000000000000000000000000000..bb6981f18bd28efc0ed6e750544a3fc594205bd4
--- /dev/null
+++ b/ExpressAPI/prisma/migrations/20231019131336_add_user_roles/migration.sql
@@ -0,0 +1,8 @@
+/*
+  Warnings:
+
+  - You are about to alter the column `role` on the `User` table. The data in that column could be lost. The data in that column will be cast from `VarChar(191)` to `Enum(EnumId(0))`.
+
+*/
+-- AlterTable
+ALTER TABLE `User` MODIFY `role` ENUM('STUDENT', 'TEACHING_STAFF', 'ADMIN') NULL DEFAULT 'STUDENT';
diff --git a/ExpressAPI/prisma/migrations/20231019133101_user_move_gitlab_id_to_user_id/migration.sql b/ExpressAPI/prisma/migrations/20231019133101_user_move_gitlab_id_to_user_id/migration.sql
new file mode 100644
index 0000000000000000000000000000000000000000..384000e267b9d1d4973b90b8d7a2d2f197748910
--- /dev/null
+++ b/ExpressAPI/prisma/migrations/20231019133101_user_move_gitlab_id_to_user_id/migration.sql
@@ -0,0 +1,12 @@
+/*
+  Warnings:
+
+  - You are about to drop the column `gitlabId` on the `User` table. All the data in the column will be lost.
+
+*/
+-- DropIndex
+DROP INDEX `User_gitlabId_key` ON `User`;
+
+-- AlterTable
+ALTER TABLE `User` DROP COLUMN `gitlabId`,
+    MODIFY `id` INTEGER NOT NULL;
diff --git a/ExpressAPI/prisma/migrations/20231019141626_user_store_refresh_token/migration.sql b/ExpressAPI/prisma/migrations/20231019141626_user_store_refresh_token/migration.sql
new file mode 100644
index 0000000000000000000000000000000000000000..86d1c29b4175daca6a1c5bcf506987a61154a955
--- /dev/null
+++ b/ExpressAPI/prisma/migrations/20231019141626_user_store_refresh_token/migration.sql
@@ -0,0 +1,2 @@
+-- AlterTable
+ALTER TABLE `User` ADD COLUMN `gitlabLastRefreshToken` VARCHAR(191) NULL;
diff --git a/ExpressAPI/prisma/migrations/20231020204832_user_remove_refresh_token/migration.sql b/ExpressAPI/prisma/migrations/20231020204832_user_remove_refresh_token/migration.sql
new file mode 100644
index 0000000000000000000000000000000000000000..46fda322d4927352cf931a964ead45bba72d2045
--- /dev/null
+++ b/ExpressAPI/prisma/migrations/20231020204832_user_remove_refresh_token/migration.sql
@@ -0,0 +1,8 @@
+/*
+  Warnings:
+
+  - You are about to drop the column `gitlabLastRefreshToken` on the `User` table. All the data in the column will be lost.
+
+*/
+-- AlterTable
+ALTER TABLE `User` DROP COLUMN `gitlabLastRefreshToken`;
diff --git a/ExpressAPI/prisma/schema.prisma b/ExpressAPI/prisma/schema.prisma
index df6640eb8f98e4e2c1802ac29ac7c194f3800b28..a68226e0868e475d54557f330fa35699e2f34bf7 100644
--- a/ExpressAPI/prisma/schema.prisma
+++ b/ExpressAPI/prisma/schema.prisma
@@ -7,15 +7,20 @@ datasource db {
     url      = env("DATABASE_URL")
 }
 
+enum UserRole {
+    STUDENT
+    TEACHING_STAFF
+    ADMIN
+}
+
 model User {
-    id        Int     @id @default(autoincrement())
-    firstname String
-    lastname  String?
-    mail      String? @unique
-    password  String?
-    gitlabId  Int     @unique
-    role      String?
-    deleted   Boolean @default(false)
+    id             Int       @id /// The user's id is the same as their gitlab id
+    name           String?
+    mail           String?   @unique
+    role           UserRole? @default(STUDENT)
+    gitlabUsername String    @unique
+    gitlabLastInfo Json      @default("{}") @db.Json
+    deleted        Boolean   @default(false)
 
     assignments Assignment[]
     exercises   Exercise[]
diff --git a/ExpressAPI/prisma/seed.ts b/ExpressAPI/prisma/seed.ts
index 603712461e4588d80a27b4b282f32922ae0e230b..ec4b605c97444027c3ce74f6cde381da9b6d7f1e 100644
--- a/ExpressAPI/prisma/seed.ts
+++ b/ExpressAPI/prisma/seed.ts
@@ -1,25 +1,20 @@
-require('dotenv').config(); // ATTENTION : This line MUST be the first of this file
-require('../src/shared/helpers/TypeScriptExtensions'); // ATTENTION : This line MUST be the second of this file
+require('../src/InitialImports'); // ATTENTION : These lines MUST be the first of this file
 
-import Config      from '../src/config/Config';
-import logger      from '../src/shared/logging/WinstonLogger';
-import * as bcrypt from 'bcryptjs';
-import db          from '../src/helpers/DatabaseHelper';
+import { UserRole } from '@prisma/client';
+import logger       from '../src/shared/logging/WinstonLogger';
+import db           from '../src/helpers/DatabaseHelper';
 
 
 async function main() {
     await db.user.upsert({
-                             where : { gitlabId: 142 },
+                             where : { id: 142 },
                              update: {},
                              create: {
-                                 id       : 1,
-                                 firstname: 'Michaël',
-                                 lastname : 'Minelli',
-                                 mail     : 'michael@minelli.me',
-                                 password : bcrypt.hashSync('123456', Config.userPasswordSaltRounds),
-                                 gitlabId : 142,
-                                 role     : 'colsci',
-                                 deleted  : false
+                                 id            : 142,
+                                 gitlabUsername: 'michael.minelli',
+                                 gitlabLastInfo: {},
+                                 role          : UserRole.ADMIN,
+                                 deleted       : false
                              }
                          });
 }
diff --git a/ExpressAPI/src/InitialImports.ts b/ExpressAPI/src/InitialImports.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1ba7814dd1acfaf666c7878f6fb0446b82b7b4c4
--- /dev/null
+++ b/ExpressAPI/src/InitialImports.ts
@@ -0,0 +1,13 @@
+import path from 'node:path';
+
+
+if ( process.env.NODE_ENV && process.env.NODE_ENV === 'production' ) {
+    const myEnv = require('dotenv').config();
+    require('dotenv-expand').expand(myEnv);
+} else {
+    require('dotenv').config({ path: path.join(__dirname, '../.env.keys') });
+    const myEnv = require('dotenv').config({ DOTENV_KEY: process.env.DOTENV_KEY_DEVELOPMENT });
+    require('dotenv-expand').expand(myEnv);
+}
+
+require('./shared/helpers/TypeScriptExtensions'); // ATTENTION : This line MUST be after the dotenv.config() calls
diff --git a/ExpressAPI/src/app.ts b/ExpressAPI/src/app.ts
index bfbc0494636c237af8dae3185fc2670eb0c51b67..9317f3869137d6789fd35a2dc18fb7c313d48f2b 100644
--- a/ExpressAPI/src/app.ts
+++ b/ExpressAPI/src/app.ts
@@ -1,17 +1,4 @@
-// Read from the .env file
-// ATTENTION : This lines MUST be the first of this file (except for the path import)
-const path = require('node:path');
-
-if ( process.env.NODE_ENV && process.env.NODE_ENV === 'production' ) {
-    const myEnv = require('dotenv').config();
-    require('dotenv-expand').expand(myEnv);
-} else {
-    require('dotenv').config({ path: path.join(__dirname, '../.env.keys') });
-    const myEnv = require('dotenv').config({ DOTENV_KEY: process.env.DOTENV_KEY_DEVELOPMENT });
-    require('dotenv-expand').expand(myEnv);
-}
-
-require('./shared/helpers/TypeScriptExtensions'); // ATTENTION : This line MUST be after the dotenv.config() calls
+require('./InitialImports'); // ATTENTION : These lines MUST be the first of this file
 
 import WorkerRole     from './process/WorkerRole';
 import ClusterManager from './process/ClusterManager';
diff --git a/ExpressAPI/src/config/Config.ts b/ExpressAPI/src/config/Config.ts
index 574f9b2ea0e987c896a5075d4ac6dac7f7dbac55..beab7cc8a1cc89ed8e148766c2e1207d62a1ec8d 100644
--- a/ExpressAPI/src/config/Config.ts
+++ b/ExpressAPI/src/config/Config.ts
@@ -5,7 +5,9 @@ import { Exercise }     from '../types/DatabaseTypes';
 import JSON5            from 'json5';
 
 
-type ConfigGitlabBadge = { link: string, imageUrl: string }
+type ConfigGitlabBadge = {
+    link: string, imageUrl: string
+}
 
 
 class Config {
@@ -13,31 +15,48 @@ class Config {
         port: number
     };
 
-    public readonly requestClientValidation: { version: { [client: string]: string } }; // { version: { CLIENT: CONDITION } }
+    public readonly requestClientValidation: {
+        version: {
+            [client: string]: string
+        }
+    }; // { version: { CLIENT: CONDITION } }
 
     public readonly jwtConfig: {
         secret: string; expiresIn: number;
     };
 
-    public readonly permissions: {
-        teachingStaff: Array<string>;
+    public readonly login: {
+        gitlab: {
+            client: {
+                secret: string
+            }
+        }
     };
 
     public readonly gitlab: {
-        urls: Array<string>; account: { id: number; username: string; token: string; }; group: { root: number; templates: number; assignments: number; exercises: number; }, badges: { pipeline: ConfigGitlabBadge }
+        urls: Array<string>; repository: {
+            timeoutAfterCreation: number;
+        }; account: {
+            id: number; username: string; token: string;
+        }; group: {
+            root: number; templates: number; assignments: number; exercises: number;
+        }, badges: {
+            pipeline: ConfigGitlabBadge
+        }
     };
 
     public readonly assignment: {
-        default: { description: string; initReadme: boolean; sharedRunnersEnabled: boolean; visibility: string; wikiEnabled: boolean; template: string }; baseFiles: Array<string>; filename: string
+        default: {
+            description: string; initReadme: boolean; sharedRunnersEnabled: boolean; visibility: string; wikiEnabled: boolean; template: string
+        }; baseFiles: Array<string>; filename: string
     };
 
     public readonly exercise: {
-        maxSameName: number; resultsFolder: string, pipelineResultsFolder: string; default: { description: string; visibility: string; };
+        maxSameName: number; resultsFolder: string, pipelineResultsFolder: string; default: {
+            description: string; visibility: string;
+        };
     };
 
-    public readonly userPasswordLength: number;
-    public readonly userPasswordSaltRounds: number;
-
     constructor() {
         this.api = {
             port: Number(process.env.API_PORT || 30992)
@@ -46,60 +65,43 @@ class Config {
         this.requestClientValidation = JSON5.parse(process.env.REQUEST_CLIENT_VALIDATION || '{"version": {}}');
 
         this.jwtConfig = {
-            secret   : process.env.JWT_SECRET_KEY || '',
-            expiresIn: Number(process.env.SESSION_TIMEOUT || 0)
+            secret: process.env.JWT_SECRET_KEY || '', expiresIn: Number(process.env.SESSION_TIMEOUT || 0)
         };
 
-        this.permissions = {
-            teachingStaff: JSON5.parse(process.env.ROLES_WITH_TEACHING_STAFF_PERMISSIONS || '[]')
+        this.login = {
+            gitlab: {
+                client: {
+                    secret: process.env.LOGIN_GITLAB_CLIENT_SECRET || ''
+                }
+            }
         };
 
         this.gitlab = {
-            urls   : JSON5.parse(process.env.GITLAB_URLS || '[]'),
-            account: {
-                id      : Number(process.env.GITLAB_DOJO_ACCOUNT_ID || 0),
-                username: process.env.GITLAB_DOJO_ACCOUNT_USERNAME || '',
-                token   : process.env.GITLAB_DOJO_ACCOUNT_TOKEN || ''
-            },
-            group  : {
-                root       : Number(process.env.GITLAB_GROUP_ROOT_ID || 0),
-                templates  : Number(process.env.GITLAB_GROUP_TEMPLATES_ID || 0),
-                assignments: Number(process.env.GITLAB_GROUP_ASSIGNMENTS_ID || 0),
-                exercises  : Number(process.env.GITLAB_GROUP_EXERCISES_ID || 0)
-            },
-            badges : {
+            urls         : JSON5.parse(process.env.GITLAB_URLS || '[]'), account: {
+                id: Number(process.env.GITLAB_DOJO_ACCOUNT_ID || 0), username: process.env.GITLAB_DOJO_ACCOUNT_USERNAME || '', token: process.env.GITLAB_DOJO_ACCOUNT_TOKEN || ''
+            }, repository: {
+                timeoutAfterCreation: Number(process.env.GITLAB_REPOSITORY_CREATION_TIMEOUT || 5000)
+            }, group     : {
+                root: Number(process.env.GITLAB_GROUP_ROOT_ID || 0), templates: Number(process.env.GITLAB_GROUP_TEMPLATES_ID || 0), assignments: Number(process.env.GITLAB_GROUP_ASSIGNMENTS_ID || 0), exercises: Number(process.env.GITLAB_GROUP_EXERCISES_ID || 0)
+            }, badges    : {
                 pipeline: {
-                    link    : process.env.GITLAB_BADGE_PIPELINE_LINK || '',
-                    imageUrl: process.env.GITLAB_BADGE_PIPELINE_IMAGE_URL || ''
+                    link: process.env.GITLAB_BADGE_PIPELINE_LINK || '', imageUrl: process.env.GITLAB_BADGE_PIPELINE_IMAGE_URL || ''
                 }
             }
         };
 
         this.assignment = {
-            default  : {
-                description         : process.env.ASSIGNMENT_DEFAULT_DESCRIPTION?.convertWithEnvVars() ?? '',
-                initReadme          : process.env.ASSIGNMENT_DEFAULT_INIT_README?.toBoolean() ?? false,
-                sharedRunnersEnabled: process.env.ASSIGNMENT_DEFAULT_SHARED_RUNNERS_ENABLED?.toBoolean() ?? true,
-                visibility          : process.env.ASSIGNMENT_DEFAULT_VISIBILITY || GitlabVisibility.PRIVATE,
-                wikiEnabled         : process.env.ASSIGNMENT_DEFAULT_WIKI_ENABLED?.toBoolean() ?? false,
-                template            : process.env.ASSIGNMENT_DEFAULT_TEMPLATE?.replace('{{USERNAME}}', this.gitlab.account.username).replace('{{TOKEN}}', this.gitlab.account.token) ?? ''
-            },
-            baseFiles: JSON5.parse(process.env.ASSIGNMENT_BASE_FILES || '[]'),
-            filename : process.env.ASSIGNMENT_FILENAME || ''
+            default     : {
+                description: process.env.ASSIGNMENT_DEFAULT_DESCRIPTION?.convertWithEnvVars() ?? '', initReadme: process.env.ASSIGNMENT_DEFAULT_INIT_README?.toBoolean() ?? false, sharedRunnersEnabled: process.env.ASSIGNMENT_DEFAULT_SHARED_RUNNERS_ENABLED?.toBoolean() ?? true, visibility: process.env.ASSIGNMENT_DEFAULT_VISIBILITY || GitlabVisibility.PRIVATE, wikiEnabled: process.env.ASSIGNMENT_DEFAULT_WIKI_ENABLED?.toBoolean() ?? false, template: process.env.ASSIGNMENT_DEFAULT_TEMPLATE?.replace('{{USERNAME}}', this.gitlab.account.username).replace('{{TOKEN}}', this.gitlab.account.token) ?? ''
+            }, baseFiles: JSON5.parse(process.env.ASSIGNMENT_BASE_FILES || '[]'), filename: process.env.ASSIGNMENT_FILENAME || ''
         };
 
         this.exercise = {
-            maxSameName          : Number(process.env.EXERCISE_MAX_SAME_NAME || 0),
-            resultsFolder        : process.env.EXERCISE_RESULTS_FOLDER?.convertWithEnvVars() ?? '',
-            pipelineResultsFolder: process.env.EXERCISE_PIPELINE_RESULTS_FOLDER ?? '', //Do not use convertWithEnvVars() because it is used in the exercise creation and muste be interpreted at exercise runtime
-            default              : {
-                description: process.env.EXERCISE_DEFAULT_DESCRIPTION?.convertWithEnvVars() ?? '',
-                visibility : process.env.EXERCISE_DEFAULT_VISIBILITY || GitlabVisibility.PRIVATE
+            maxSameName: Number(process.env.EXERCISE_MAX_SAME_NAME || 0), resultsFolder: process.env.EXERCISE_RESULTS_FOLDER?.convertWithEnvVars() ?? '', pipelineResultsFolder: process.env.EXERCISE_PIPELINE_RESULTS_FOLDER ?? '', //Do not use convertWithEnvVars() because it is used in the exercise creation and muste be interpreted at exercise runtime
+            default    : {
+                description: process.env.EXERCISE_DEFAULT_DESCRIPTION?.convertWithEnvVars() ?? '', visibility: process.env.EXERCISE_DEFAULT_VISIBILITY || GitlabVisibility.PRIVATE
             }
         };
-
-        this.userPasswordLength = Number(process.env.USER_PASSWORD_LENGTH || 0);
-        this.userPasswordSaltRounds = Number(process.env.USER_PASSWORD_SALT_ROUNDS || 10);
     }
 
     public getResultsFolder(exercise: Exercise): string {
diff --git a/ExpressAPI/src/controllers/Session.ts b/ExpressAPI/src/controllers/Session.ts
index 77549d8630df98d91b1d4b1e5ef5a7851f91621e..49e044eab67d18a2108e54ef54c1d4e59b6896bb 100644
--- a/ExpressAPI/src/controllers/Session.ts
+++ b/ExpressAPI/src/controllers/Session.ts
@@ -16,7 +16,6 @@ class Session {
     }
 
     set profile(newProfile: User) {
-        delete newProfile.password;
         this._profile = newProfile;
     }
 
diff --git a/ExpressAPI/src/helpers/GlobalHelper.ts b/ExpressAPI/src/helpers/GlobalHelper.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c97328d8e5cbc2d99b7339da2a94219e4ef01618
--- /dev/null
+++ b/ExpressAPI/src/helpers/GlobalHelper.ts
@@ -0,0 +1,33 @@
+import express          from 'express';
+import GitlabRepository from '../shared/types/Gitlab/GitlabRepository';
+import logger           from '../shared/logging/WinstonLogger';
+import GitlabManager    from '../managers/GitlabManager';
+import { AxiosError }   from 'axios';
+import { StatusCodes }  from 'http-status-codes';
+import DojoStatusCode   from '../shared/types/Dojo/DojoStatusCode';
+
+
+class GlobalHelper {
+    async repositoryCreationError(message: string, error: any, req: express.Request, res: express.Response, gitlabError: DojoStatusCode, internalError: DojoStatusCode, repositoryToRemove?: GitlabRepository): Promise<void> {
+        logger.error(message);
+        logger.error(error);
+
+        try {
+            if ( repositoryToRemove ) {
+                await GitlabManager.deleteRepository(repositoryToRemove.id);
+            }
+        } catch ( error ) {
+            logger.error('Repository deletion error');
+            logger.error(error);
+        }
+
+        if ( error instanceof AxiosError ) {
+            return req.session.sendResponse(res, StatusCodes.INTERNAL_SERVER_ERROR, {}, `Unknown gitlab error: ${ message }`, gitlabError);
+        }
+
+        return req.session.sendResponse(res, StatusCodes.INTERNAL_SERVER_ERROR, {}, `Unknown error: ${ message }`, internalError);
+    };
+}
+
+
+export default new GlobalHelper();
\ No newline at end of file
diff --git a/ExpressAPI/src/helpers/Prisma/Extensions/UserResultExtension.ts b/ExpressAPI/src/helpers/Prisma/Extensions/UserResultExtension.ts
index 9315569e912f7acb2ef0084bf926e90c0d56d9a0..9ece43dcc31b2dd9fde697a33948f6d72fc15247 100644
--- a/ExpressAPI/src/helpers/Prisma/Extensions/UserResultExtension.ts
+++ b/ExpressAPI/src/helpers/Prisma/Extensions/UserResultExtension.ts
@@ -1,8 +1,7 @@
-import { Prisma }    from '@prisma/client';
-import Config        from '../../../config/Config';
-import LazyVal       from '../../../shared/helpers/LazyVal';
-import GitlabUser    from '../../../shared/types/Gitlab/GitlabUser';
-import GitlabManager from '../../../managers/GitlabManager';
+import { Prisma, UserRole } from '@prisma/client';
+import LazyVal              from '../../../shared/helpers/LazyVal';
+import GitlabUser           from '../../../shared/types/Gitlab/GitlabUser';
+import GitlabManager        from '../../../managers/GitlabManager';
 
 
 export default Prisma.defineExtension(client => {
@@ -14,16 +13,21 @@ export default Prisma.defineExtension(client => {
                                                role: true
                                            },
                                            compute(user) {
-                                               return Config.permissions.teachingStaff.includes(user.role!);
+                                               return user.role == UserRole.TEACHING_STAFF || user.role == UserRole.ADMIN;
                                            }
                                        },
-                                       gitlabProfile  : {
+                                       isAdmin        : {
                                            needs: {
-                                               gitlabId: true
+                                               role: true
                                            },
+                                           compute(user) {
+                                               return user.role == UserRole.ADMIN;
+                                           }
+                                       },
+                                       gitlabProfile  : {
                                            compute(user) {
                                                return new LazyVal<GitlabUser | undefined>(() => {
-                                                   return GitlabManager.getUserById(user.gitlabId);
+                                                   return GitlabManager.getUserById(user.id);
                                                });
                                            }
                                        }
diff --git a/ExpressAPI/src/managers/GitlabManager.ts b/ExpressAPI/src/managers/GitlabManager.ts
index fca3977ce07f14eada038511f9106ccf539fba0a..0c93da87efa87ca244f7a11d21d831ccb7c1bc16 100644
--- a/ExpressAPI/src/managers/GitlabManager.ts
+++ b/ExpressAPI/src/managers/GitlabManager.ts
@@ -12,6 +12,7 @@ import GitlabFile        from '../shared/types/Gitlab/GitlabFile';
 import express           from 'express';
 import GitlabRoute       from '../shared/types/Gitlab/GitlabRoute';
 import SharedConfig      from '../shared/config/SharedConfig';
+import GitlabProfile     from '../shared/types/Gitlab/GitlabProfile';
 
 
 class GitlabManager {
@@ -19,6 +20,20 @@ class GitlabManager {
         return `${ SharedConfig.gitlab.apiURL }${ route }`;
     }
 
+    public async getUserProfile(token: string): Promise<GitlabProfile | undefined> {
+        try {
+            return (await axios.get<GitlabProfile>(this.getApiUrl(GitlabRoute.PROFILE_GET), {
+                headers: {
+                    DojoOverrideAuthorization: true,
+                    DojoAuthorizationHeader  : 'Authorization',
+                    DojoAuthorizationValue   : `Bearer ${ token }`
+                }
+            })).data;
+        } catch ( e ) { }
+
+        return undefined;
+    }
+
     public async getUserById(id: number): Promise<GitlabUser | undefined> {
         try {
             const params: any = {};
@@ -69,6 +84,10 @@ class GitlabManager {
         return response.data;
     }
 
+    async deleteRepository(repoId: number): Promise<void> {
+        return await axios.delete(this.getApiUrl(GitlabRoute.REPOSITORY_DELETE).replace('{{id}}', String(repoId)));
+    }
+
     async forkRepository(forkId: number, name: string, path: string, description: string, visibility: string, namespace: number): Promise<GitlabRepository> {
         const response = await axios.post<GitlabRepository>(this.getApiUrl(GitlabRoute.REPOSITORY_FORK).replace('{{id}}', String(forkId)), {
             name        : name,
@@ -142,7 +161,7 @@ class GitlabManager {
         };
         members.forEach(member => {
             if ( member.access_level >= GitlabAccessLevel.REPORTER ) {
-                if ( member.id === req.session.profile.gitlabId ) {
+                if ( member.id === req.session.profile.id ) {
                     isUsersAtLeastReporter.user = true;
                 } else if ( member.id === Config.gitlab.account.id ) {
                     isUsersAtLeastReporter.dojo = true;
@@ -203,8 +222,10 @@ class GitlabManager {
         return response.data;
     }
 
-    async createFile(repoId: number, filePath: string, fileBase64: string, commitMessage: string, branch: string = 'main', authorName: string = 'Dojo', authorMail: string | undefined = undefined) {
-        await axios.post(this.getApiUrl(GitlabRoute.REPOSITORY_FILE).replace('{{id}}', String(repoId)).replace('{{filePath}}', encodeURIComponent(filePath)), {
+    private async createUpdateFile(create: boolean, repoId: number, filePath: string, fileBase64: string, commitMessage: string, branch: string = 'main', authorName: string = 'Dojo', authorMail: string | undefined = undefined) {
+        const axiosFunction = create ? axios.post : axios.put;
+
+        await axiosFunction(this.getApiUrl(GitlabRoute.REPOSITORY_FILE).replace('{{id}}', String(repoId)).replace('{{filePath}}', encodeURIComponent(filePath)), {
             encoding      : 'base64',
             branch        : branch,
             commit_message: commitMessage,
@@ -213,6 +234,14 @@ class GitlabManager {
             author_email  : authorMail
         });
     }
+
+    async createFile(repoId: number, filePath: string, fileBase64: string, commitMessage: string, branch: string = 'main', authorName: string = 'Dojo', authorMail: string | undefined = undefined) {
+        return this.createUpdateFile(true, repoId, filePath, fileBase64, commitMessage, branch, authorName, authorMail);
+    }
+
+    async updateFile(repoId: number, filePath: string, fileBase64: string, commitMessage: string, branch: string = 'main', authorName: string = 'Dojo', authorMail: string | undefined = undefined) {
+        return this.createUpdateFile(false, repoId, filePath, fileBase64, commitMessage, branch, authorName, authorMail);
+    }
 }
 
 
diff --git a/ExpressAPI/src/managers/HttpManager.ts b/ExpressAPI/src/managers/HttpManager.ts
index 6d04c68f0b0536bfa9bead9a6e78978bf5946c9b..075e72717a56923fe0d962b72ac14d11fad0d9e8 100644
--- a/ExpressAPI/src/managers/HttpManager.ts
+++ b/ExpressAPI/src/managers/HttpManager.ts
@@ -18,7 +18,17 @@ class HttpManager {
             }
 
             if ( config.url && config.url.indexOf(SharedConfig.gitlab.apiURL) !== -1 ) {
-                config.headers['PRIVATE-TOKEN'] = Config.gitlab.account.token;
+                if ( !config.headers.DojoOverrideAuthorization ) {
+                    config.headers['PRIVATE-TOKEN'] = Config.gitlab.account.token;
+                }
+            }
+
+            if ( config.headers.DojoOverrideAuthorization && 'DojoAuthorizationHeader' in config.headers && 'DojoAuthorizationValue' in config.headers ) {
+                config.headers[config.headers.DojoAuthorizationHeader] = config.headers.DojoAuthorizationValue;
+
+                delete config.headers.DojoOverrideAuthorization;
+                delete config.headers.DojoAuthorizationHeader;
+                delete config.headers.DojoAuthorizationValue;
             }
 
             return config;
diff --git a/ExpressAPI/src/managers/UserManager.ts b/ExpressAPI/src/managers/UserManager.ts
index 615cbcb37cb5c5b6ec06731c50a1acd391260956..b69705e1e6b5c65c29efe2a0590b27b7a51c48b2 100644
--- a/ExpressAPI/src/managers/UserManager.ts
+++ b/ExpressAPI/src/managers/UserManager.ts
@@ -1,7 +1,8 @@
-import GitlabUser from '../shared/types/Gitlab/GitlabUser';
-import { Prisma } from '@prisma/client';
-import db         from '../helpers/DatabaseHelper';
-import { User }   from '../types/DatabaseTypes';
+import GitlabUser    from '../shared/types/Gitlab/GitlabUser';
+import { Prisma }    from '@prisma/client';
+import db            from '../helpers/DatabaseHelper';
+import GitlabProfile from '../shared/types/Gitlab/GitlabProfile';
+import { User }      from '../types/DatabaseTypes';
 
 
 class UserManager {
@@ -23,23 +24,36 @@ class UserManager {
                                         }) as unknown as User ?? undefined;
     }
 
-    async getByGitlabId(gitlabId: number, returnIdIfUndefined: boolean = true, include: Prisma.UserInclude | undefined = undefined): Promise<User | number | undefined> {
-        return await db.user.findUnique({
-                                            where  : {
-                                                gitlabId: gitlabId
-                                            },
-                                            include: include
-                                        }) as unknown as User ?? (returnIdIfUndefined ? gitlabId : undefined);
+    async getUpdateFromGitlabProfile(gitlabProfile: GitlabProfile, refreshToken: string): Promise<User> {
+        await db.user.upsert({
+                                 where : {
+                                     id: gitlabProfile.id
+                                 },
+                                 update: {
+                                     mail          : gitlabProfile.email,
+                                     gitlabLastInfo: gitlabProfile
+                                 },
+                                 create: {
+                                     id            : gitlabProfile.id,
+                                     name          : gitlabProfile.name,
+                                     mail          : gitlabProfile.email,
+                                     gitlabUsername: gitlabProfile.username,
+                                     gitlabLastInfo: gitlabProfile,
+                                     deleted       : false
+                                 }
+                             });
+
+        return (await this.getById(gitlabProfile.id))!;
     }
 
     async getFromGitlabUser(gitlabUser: GitlabUser, createIfNotExist: boolean = false, include: Prisma.UserInclude | undefined = undefined): Promise<User | number | undefined> {
-        let user = await this.getByGitlabId(gitlabUser.id, true, include);
+        let user = await this.getById(gitlabUser.id, include) ?? gitlabUser.id;
 
         if ( typeof user === 'number' && createIfNotExist ) {
             user = (await db.user.create({
                                              data: {
-                                                 firstname: gitlabUser.name,
-                                                 gitlabId : gitlabUser.id
+                                                 id            : gitlabUser.id,
+                                                 gitlabUsername: gitlabUser.name
                                              }
                                          })).id;
         }
diff --git a/ExpressAPI/src/routes/AssignmentRoutes.ts b/ExpressAPI/src/routes/AssignmentRoutes.ts
index 0e8926263d792b77a20c60ca36b1c64134a5f3d5..e4eb82bfa416c6fcfb1062b84883551c9b821988 100644
--- a/ExpressAPI/src/routes/AssignmentRoutes.ts
+++ b/ExpressAPI/src/routes/AssignmentRoutes.ts
@@ -23,6 +23,8 @@ import GitlabVisibility               from '../shared/types/Gitlab/GitlabVisibil
 import fs                             from 'fs';
 import path                           from 'path';
 import SharedAssignmentHelper         from '../shared/helpers/Dojo/SharedAssignmentHelper';
+import GlobalHelper                   from '../helpers/GlobalHelper';
+import DojoStatusCode                 from '../shared/types/Dojo/DojoStatusCode';
 
 
 class AssignmentRoutes implements RoutesManager {
@@ -86,11 +88,10 @@ class AssignmentRoutes implements RoutesManager {
         let repository: GitlabRepository;
         try {
             repository = await GitlabManager.createRepository(params.name, Config.assignment.default.description.replace('{{ASSIGNMENT_NAME}}', params.name), Config.assignment.default.visibility, Config.assignment.default.initReadme, Config.gitlab.group.assignments, Config.assignment.default.sharedRunnersEnabled, Config.assignment.default.wikiEnabled, params.template);
-
-            await GitlabManager.protectBranch(repository.id, '*', true, GitlabAccessLevel.DEVELOPER, GitlabAccessLevel.DEVELOPER, GitlabAccessLevel.OWNER);
-
-            await GitlabManager.addRepositoryBadge(repository.id, Config.gitlab.badges.pipeline.link, Config.gitlab.badges.pipeline.imageUrl, 'Pipeline Status');
         } catch ( error ) {
+            logger.error('Repo creation error');
+            logger.error(error);
+
             if ( error instanceof AxiosError ) {
                 if ( error.response?.data.message.name && error.response.data.message.name == 'has already been taken' ) {
                     return res.status(StatusCodes.CONFLICT).send();
@@ -99,27 +100,32 @@ class AssignmentRoutes implements RoutesManager {
                 return res.status(error.response?.status ?? HttpStatusCode.InternalServerError).send();
             }
 
-            logger.error(error);
             return res.status(StatusCodes.INTERNAL_SERVER_ERROR).send();
         }
 
+        await new Promise(resolve => setTimeout(resolve, Config.gitlab.repository.timeoutAfterCreation));
+
         try {
-            await GitlabManager.createFile(repository.id, '.gitlab-ci.yml', fs.readFileSync(path.join(__dirname, '../../assets/assignment_gitlab_ci.yml'), 'base64'), 'Add .gitlab-ci.yml (DO NOT MODIFY THIS FILE)');
-        } catch ( error ) {
-            logger.error(error);
+            await GitlabManager.protectBranch(repository.id, '*', true, GitlabAccessLevel.DEVELOPER, GitlabAccessLevel.DEVELOPER, GitlabAccessLevel.OWNER);
 
-            if ( error instanceof AxiosError ) {
-                return res.status(error.response?.status ?? HttpStatusCode.InternalServerError).send();
-            }
+            await GitlabManager.addRepositoryBadge(repository.id, Config.gitlab.badges.pipeline.link, Config.gitlab.badges.pipeline.imageUrl, 'Pipeline Status');
+        } catch ( error ) {
+            return GlobalHelper.repositoryCreationError('Repo params error', error, req, res, DojoStatusCode.ASSIGNMENT_CREATION_GITLAB_ERROR, DojoStatusCode.ASSIGNMENT_CREATION_INTERNAL_ERROR, repository);
+        }
 
-            return res.status(StatusCodes.INTERNAL_SERVER_ERROR).send();
+        try {
+            await GitlabManager.createFile(repository.id, '.gitlab-ci.yml', fs.readFileSync(path.join(__dirname, '../../assets/assignment_gitlab_ci.yml'), 'base64'), 'Add .gitlab-ci.yml (DO NOT MODIFY THIS FILE)');
+        } catch ( error ) {
+            return GlobalHelper.repositoryCreationError('CI file error', error, req, res, DojoStatusCode.ASSIGNMENT_CREATION_GITLAB_ERROR, DojoStatusCode.ASSIGNMENT_CREATION_INTERNAL_ERROR, repository);
         }
 
         try {
             await Promise.all(params.members.map(member => member.id).map(async (memberId: number): Promise<GitlabMember | false> => {
                 try {
                     return await GitlabManager.addRepositoryMember(repository.id, memberId, GitlabAccessLevel.DEVELOPER);
-                } catch ( e ) {
+                } catch ( error ) {
+                    logger.error('Add member error');
+                    logger.error(error);
                     return false;
                 }
             }));
@@ -136,11 +142,11 @@ class AssignmentRoutes implements RoutesManager {
                                                                                   connectOrCreate: [ ...params.members.map(gitlabUser => {
                                                                                       return {
                                                                                           create: {
-                                                                                              gitlabId : gitlabUser.id,
-                                                                                              firstname: gitlabUser.name
+                                                                                              id            : gitlabUser.id,
+                                                                                              gitlabUsername: gitlabUser.name
                                                                                           },
                                                                                           where : {
-                                                                                              gitlabId: gitlabUser.id
+                                                                                              id: gitlabUser.id
                                                                                           }
                                                                                       };
                                                                                   }) ]
@@ -150,12 +156,7 @@ class AssignmentRoutes implements RoutesManager {
 
             return req.session.sendResponse(res, StatusCodes.OK, assignment);
         } catch ( error ) {
-            if ( error instanceof AxiosError ) {
-                return res.status(error.response?.status ?? HttpStatusCode.InternalServerError).send();
-            }
-
-            logger.error(error);
-            return res.status(StatusCodes.INTERNAL_SERVER_ERROR).send();
+            return GlobalHelper.repositoryCreationError('DB error', error, req, res, DojoStatusCode.ASSIGNMENT_CREATION_GITLAB_ERROR, DojoStatusCode.ASSIGNMENT_CREATION_INTERNAL_ERROR, repository);
         }
     }
 
diff --git a/ExpressAPI/src/routes/ExerciseRoutes.ts b/ExpressAPI/src/routes/ExerciseRoutes.ts
index e773d8c2f08c1484a830a28edb962e4031d9de73..8bc2e8a43832d6d59e8fb253d6d857fad24de290 100644
--- a/ExpressAPI/src/routes/ExerciseRoutes.ts
+++ b/ExpressAPI/src/routes/ExerciseRoutes.ts
@@ -1,32 +1,34 @@
-import { Express }                    from 'express-serve-static-core';
-import express                        from 'express';
-import * as ExpressValidator          from 'express-validator';
-import { StatusCodes }                from 'http-status-codes';
-import RoutesManager                  from '../express/RoutesManager';
-import ParamsValidatorMiddleware      from '../middlewares/ParamsValidatorMiddleware';
-import SecurityMiddleware             from '../middlewares/SecurityMiddleware';
-import GitlabUser                     from '../shared/types/Gitlab/GitlabUser';
-import GitlabManager                  from '../managers/GitlabManager';
-import Config                         from '../config/Config';
-import GitlabRepository               from '../shared/types/Gitlab/GitlabRepository';
-import { AxiosError, HttpStatusCode } from 'axios';
-import logger                         from '../shared/logging/WinstonLogger';
-import DojoValidators                 from '../helpers/DojoValidators';
-import { v4 as uuidv4 }               from 'uuid';
-import GitlabMember                   from '../shared/types/Gitlab/GitlabMember';
-import GitlabAccessLevel              from '../shared/types/Gitlab/GitlabAccessLevel';
-import { Prisma }                     from '@prisma/client';
-import { Assignment, Exercise }       from '../types/DatabaseTypes';
-import db                             from '../helpers/DatabaseHelper';
-import SecurityCheckType              from '../types/SecurityCheckType';
-import GitlabTreeFile                 from '../shared/types/Gitlab/GitlabTreeFile';
-import GitlabFile                     from '../shared/types/Gitlab/GitlabFile';
-import GitlabTreeFileType             from '../shared/types/Gitlab/GitlabTreeFileType';
-import JSON5                          from 'json5';
-import fs                             from 'fs';
-import path                           from 'path';
-import AssignmentFile                 from '../shared/types/Dojo/AssignmentFile';
-import ExerciseResultsFile            from '../shared/types/Dojo/ExerciseResultsFile';
+import { Express }               from 'express-serve-static-core';
+import express                   from 'express';
+import * as ExpressValidator     from 'express-validator';
+import { StatusCodes }           from 'http-status-codes';
+import RoutesManager             from '../express/RoutesManager';
+import ParamsValidatorMiddleware from '../middlewares/ParamsValidatorMiddleware';
+import SecurityMiddleware        from '../middlewares/SecurityMiddleware';
+import GitlabUser                from '../shared/types/Gitlab/GitlabUser';
+import GitlabManager             from '../managers/GitlabManager';
+import Config                    from '../config/Config';
+import GitlabRepository          from '../shared/types/Gitlab/GitlabRepository';
+import { AxiosError }            from 'axios';
+import logger                    from '../shared/logging/WinstonLogger';
+import DojoValidators            from '../helpers/DojoValidators';
+import { v4 as uuidv4 }          from 'uuid';
+import GitlabMember              from '../shared/types/Gitlab/GitlabMember';
+import GitlabAccessLevel         from '../shared/types/Gitlab/GitlabAccessLevel';
+import { Prisma }                from '@prisma/client';
+import { Assignment, Exercise }  from '../types/DatabaseTypes';
+import db                        from '../helpers/DatabaseHelper';
+import SecurityCheckType         from '../types/SecurityCheckType';
+import GitlabTreeFile            from '../shared/types/Gitlab/GitlabTreeFile';
+import GitlabFile                from '../shared/types/Gitlab/GitlabFile';
+import GitlabTreeFileType        from '../shared/types/Gitlab/GitlabTreeFileType';
+import JSON5                     from 'json5';
+import fs                        from 'fs';
+import path                      from 'path';
+import AssignmentFile            from '../shared/types/Dojo/AssignmentFile';
+import ExerciseResultsFile       from '../shared/types/Dojo/ExerciseResultsFile';
+import DojoStatusCode            from '../shared/types/Dojo/DojoStatusCode';
+import GlobalHelper              from '../helpers/GlobalHelper';
 
 
 class ExerciseRoutes implements RoutesManager {
@@ -95,50 +97,55 @@ class ExerciseRoutes implements RoutesManager {
         do {
             try {
                 repository = await GitlabManager.forkRepository((assignment.gitlabCreationInfo as unknown as GitlabRepository).id, this.getExerciseName(assignment, params.members, suffix), this.getExercisePath(req.boundParams.assignment!, exerciseId), Config.exercise.default.description.replace('{{ASSIGNMENT_NAME}}', assignment.name), Config.exercise.default.visibility, Config.gitlab.group.exercises);
-
-                await GitlabManager.protectBranch(repository.id, '*', false, GitlabAccessLevel.DEVELOPER, GitlabAccessLevel.DEVELOPER, GitlabAccessLevel.OWNER);
-
-                await GitlabManager.addRepositoryVariable(repository.id, 'DOJO_EXERCISE_ID', exerciseId, false, true);
-                await GitlabManager.addRepositoryVariable(repository.id, 'DOJO_SECRET', secret, false, true);
-                await GitlabManager.addRepositoryVariable(repository.id, 'DOJO_RESULTS_FOLDER', Config.exercise.pipelineResultsFolder, false, false);
-
-                await GitlabManager.addRepositoryBadge(repository.id, Config.gitlab.badges.pipeline.link, Config.gitlab.badges.pipeline.imageUrl, 'Pipeline Status');
-
                 break;
             } catch ( error ) {
+                logger.error('Repo creation error');
+                logger.error(error);
+
                 if ( error instanceof AxiosError ) {
                     if ( error.response?.data.message.name && error.response.data.message.name == 'has already been taken' ) {
                         suffix++;
                     } else {
-                        return res.status(error.response?.status ?? HttpStatusCode.InternalServerError).send();
+                        return req.session.sendResponse(res, StatusCodes.INTERNAL_SERVER_ERROR, {}, 'Unknown gitlab error while forking repository', DojoStatusCode.EXERCISE_CREATION_GITLAB_ERROR);
                     }
                 } else {
-                    return res.status(StatusCodes.INTERNAL_SERVER_ERROR).send();
+                    return req.session.sendResponse(res, StatusCodes.INTERNAL_SERVER_ERROR, {}, 'Unknown error while forking repository', DojoStatusCode.EXERCISE_CREATION_INTERNAL_ERROR);
                 }
             }
         } while ( suffix < Config.exercise.maxSameName );
 
         if ( suffix >= Config.exercise.maxSameName ) {
+            logger.error('Max exercise with same name reached');
             return res.status(StatusCodes.INSUFFICIENT_SPACE_ON_RESOURCE).send();
         }
 
+        await new Promise(resolve => setTimeout(resolve, Config.gitlab.repository.timeoutAfterCreation));
+
         try {
-            await GitlabManager.createFile(repository.id, '.gitlab-ci.yml', fs.readFileSync(path.join(__dirname, '../../assets/exercise_gitlab_ci.yml'), 'base64'), 'Add .gitlab-ci.yml (DO NOT MODIFY THIS FILE)');
-        } catch ( error ) {
-            logger.error(error);
+            await GitlabManager.protectBranch(repository.id, '*', false, GitlabAccessLevel.DEVELOPER, GitlabAccessLevel.DEVELOPER, GitlabAccessLevel.OWNER);
 
-            if ( error instanceof AxiosError ) {
-                return res.status(error.response?.status ?? HttpStatusCode.InternalServerError).send();
-            }
+            await GitlabManager.addRepositoryVariable(repository.id, 'DOJO_EXERCISE_ID', exerciseId, false, true);
+            await GitlabManager.addRepositoryVariable(repository.id, 'DOJO_SECRET', secret, false, true);
+            await GitlabManager.addRepositoryVariable(repository.id, 'DOJO_RESULTS_FOLDER', Config.exercise.pipelineResultsFolder, false, false);
 
-            return res.status(StatusCodes.INTERNAL_SERVER_ERROR).send();
+            await GitlabManager.addRepositoryBadge(repository.id, Config.gitlab.badges.pipeline.link, Config.gitlab.badges.pipeline.imageUrl, 'Pipeline Status');
+        } catch ( error ) {
+            return GlobalHelper.repositoryCreationError('Repo params error', error, req, res, DojoStatusCode.EXERCISE_CREATION_GITLAB_ERROR, DojoStatusCode.EXERCISE_CREATION_INTERNAL_ERROR, repository);
+        }
+
+        try {
+            await GitlabManager.updateFile(repository.id, '.gitlab-ci.yml', fs.readFileSync(path.join(__dirname, '../../assets/exercise_gitlab_ci.yml'), 'base64'), 'Add .gitlab-ci.yml (DO NOT MODIFY THIS FILE)');
+        } catch ( error ) {
+            return GlobalHelper.repositoryCreationError('CI file update error', error, req, res, DojoStatusCode.EXERCISE_CREATION_GITLAB_ERROR, DojoStatusCode.EXERCISE_CREATION_INTERNAL_ERROR, repository);
         }
 
         try {
-            await Promise.all([ ...new Set([ ...assignment.staff.map(user => user.gitlabId), ...params.members.map(member => member.id) ]) ].map(async (memberId: number): Promise<GitlabMember | false> => {
+            await Promise.all([ ...new Set([ ...assignment.staff.map(user => user.id), ...params.members.map(member => member.id) ]) ].map(async (memberId: number): Promise<GitlabMember | false> => {
                 try {
                     return await GitlabManager.addRepositoryMember(repository.id, memberId, GitlabAccessLevel.DEVELOPER);
-                } catch ( e ) {
+                } catch ( error ) {
+                    logger.error('Add member error');
+                    logger.error(error);
                     return false;
                 }
             }));
@@ -158,11 +165,11 @@ class ExerciseRoutes implements RoutesManager {
                                                                             connectOrCreate: [ ...params.members.map(gitlabUser => {
                                                                                 return {
                                                                                     create: {
-                                                                                        gitlabId : gitlabUser.id,
-                                                                                        firstname: gitlabUser.name
+                                                                                        id            : gitlabUser.id,
+                                                                                        gitlabUsername: gitlabUser.name
                                                                                     },
                                                                                     where : {
-                                                                                        gitlabId: gitlabUser.id
+                                                                                        id: gitlabUser.id
                                                                                     }
                                                                                 };
                                                                             }) ]
@@ -172,13 +179,7 @@ class ExerciseRoutes implements RoutesManager {
 
             return req.session.sendResponse(res, StatusCodes.OK, exercise);
         } catch ( error ) {
-            logger.error(error);
-
-            if ( error instanceof AxiosError ) {
-                return res.status(error.response?.status ?? HttpStatusCode.InternalServerError).send();
-            }
-
-            return res.status(StatusCodes.INTERNAL_SERVER_ERROR).send();
+            return GlobalHelper.repositoryCreationError('DB error', error, req, res, DojoStatusCode.EXERCISE_CREATION_GITLAB_ERROR, DojoStatusCode.EXERCISE_CREATION_INTERNAL_ERROR, repository);
         }
     }
 
diff --git a/ExpressAPI/src/routes/SessionRoutes.ts b/ExpressAPI/src/routes/SessionRoutes.ts
index eb329d88b7ccfe6f026a755a7f213bee0ddf1cb7..0479205b47fa69630d954e3c6e08aa703f716043 100644
--- a/ExpressAPI/src/routes/SessionRoutes.ts
+++ b/ExpressAPI/src/routes/SessionRoutes.ts
@@ -2,21 +2,30 @@ import { Express }               from 'express-serve-static-core';
 import express                   from 'express';
 import * as ExpressValidator     from 'express-validator';
 import { StatusCodes }           from 'http-status-codes';
-import * as bcrypt               from 'bcryptjs';
 import RoutesManager             from '../express/RoutesManager';
 import ParamsValidatorMiddleware from '../middlewares/ParamsValidatorMiddleware';
-import UserManager               from '../managers/UserManager';
 import SecurityMiddleware        from '../middlewares/SecurityMiddleware';
-import { User }                  from '../types/DatabaseTypes';
+import GitlabManager             from '../managers/GitlabManager';
+import UserManager               from '../managers/UserManager';
+import DojoStatusCode            from '../shared/types/Dojo/DojoStatusCode';
+import SharedGitlabManager       from '../shared/managers/SharedGitlabManager';
+import Config                    from '../config/Config';
 
 
 class SessionRoutes implements RoutesManager {
     private readonly loginValidator: ExpressValidator.Schema = {
-        user    : {
+        accessToken : {
             trim    : true,
             notEmpty: true
         },
-        password: {
+        refreshToken: {
+            trim    : true,
+            notEmpty: true
+        }
+    };
+
+    private readonly refreshTokensValidator: ExpressValidator.Schema = {
+        refreshToken: {
             trim    : true,
             notEmpty: true
         }
@@ -24,26 +33,43 @@ class SessionRoutes implements RoutesManager {
 
     registerOnBackend(backend: Express) {
         backend.post('/login', ParamsValidatorMiddleware.validate(this.loginValidator), this.login);
+        backend.post('/refresh_tokens', ParamsValidatorMiddleware.validate(this.refreshTokensValidator), this.refreshTokens);
         backend.get('/test_session', SecurityMiddleware.check(true), (req: express.Request, res: express.Response) => req.session.sendResponse(res, StatusCodes.OK));
     }
 
     private async login(req: express.Request, res: express.Response) {
-        const params: {
-            user: string, password: string
-        } = req.body;
+        try {
+            const params: {
+                accessToken: string, refreshToken: string
+            } = req.body;
 
-        const user: User | undefined = await UserManager.getByMail(params.user);
+            const gitlabUser = await GitlabManager.getUserProfile(params.accessToken);
 
-        if ( user ) {
-            if ( bcrypt.compareSync(params.password, user.password ?? '') ) {
-                req.session.profile = user;
+            if ( gitlabUser ) {
+                req.session.profile = await UserManager.getUpdateFromGitlabProfile(gitlabUser, params.refreshToken);
 
                 req.session.sendResponse(res, StatusCodes.OK);
                 return;
+            } else {
+                req.session.sendResponse(res, StatusCodes.NOT_FOUND);
             }
+        } catch ( error ) {
+            req.session.sendResponse(res, StatusCodes.INTERNAL_SERVER_ERROR, {}, 'Unknown error while logging in', DojoStatusCode.LOGIN_FAILED);
         }
+    }
+
+    private async refreshTokens(req: express.Request, res: express.Response) {
+        try {
+            const params: {
+                refreshToken: string
+            } = req.body;
 
-        req.session.sendResponse(res, StatusCodes.NOT_FOUND);
+            const gitlabTokens = await SharedGitlabManager.getTokens(params.refreshToken, true, Config.login.gitlab.client.secret);
+
+            req.session.sendResponse(res, StatusCodes.OK, gitlabTokens);
+        } catch ( error ) {
+            req.session.sendResponse(res, StatusCodes.INTERNAL_SERVER_ERROR, {}, 'Unknown error while refresh tokens', DojoStatusCode.REFRESH_TOKENS_FAILED);
+        }
     }
 }
 
diff --git a/ExpressAPI/src/shared b/ExpressAPI/src/shared
index efe1bf313f57d1826faf935c183d37a0835f8c2d..4a5eb68209ae9204b6d4cc8020bd62cf6a5be989 160000
--- a/ExpressAPI/src/shared
+++ b/ExpressAPI/src/shared
@@ -1 +1 @@
-Subproject commit efe1bf313f57d1826faf935c183d37a0835f8c2d
+Subproject commit 4a5eb68209ae9204b6d4cc8020bd62cf6a5be989
diff --git a/ExpressAPI/src/types/DatabaseTypes.ts b/ExpressAPI/src/types/DatabaseTypes.ts
index bdb25f8c14088d4dcc8524f309a52d5380556a1c..632b05be5eda9eebf30a8c392e5a4020292dd10d 100644
--- a/ExpressAPI/src/types/DatabaseTypes.ts
+++ b/ExpressAPI/src/types/DatabaseTypes.ts
@@ -26,9 +26,9 @@ const resultBase = Prisma.validator<Prisma.ResultDefaultArgs>()({
                                                                 });
 
 
-export type User = Omit<Prisma.UserGetPayload<typeof userBase>, 'password'> & {
-    password?: string
+export type User = Prisma.UserGetPayload<typeof userBase> & {
     isTeachingStaff: boolean
+    isAdmin: boolean
     gitlabProfile: LazyVal<GitlabUser>
 }
 export type Assignment = Prisma.AssignmentGetPayload<typeof assignmentBase>