From 94ad30ab3ab596603b7fbb9cf210c6f4507c3ba2 Mon Sep 17 00:00:00 2001
From: Lansana DIOMANDE <ldiomande@takima.fr>
Date: Thu, 12 May 2022 16:35:22 +0200
Subject: [PATCH] feat: user source code is save automatically

---
 .../deadlock-extension/package-lock.json      | 110 +++++++++++++++---
 .../deadlock-extension/package.json           |   4 +-
 .../deadlock-extension/src/config.prod.ts     |   4 +-
 .../deadlock-extension/src/config.staging.ts  |   9 +-
 .../deadlock-extension/src/config.ts          |   8 +-
 .../src/core/callApi.service.ts               |  96 +++++++++++++++
 .../deadlock-extension/src/core/config.ts     |   3 +-
 .../deadlock-extension/src/core/controller.ts |  43 +++++--
 .../keycloakOAuth2DeviceFlowConnection.ts     |   3 +-
 ...oakOAuth2DeviceFlowConnectionVSCodeImpl.ts |  28 +++++
 .../deadlock-extension/src/core/mission.ts    |  42 ++++++-
 .../src/core/sshKeyManager.ts                 |  22 ++++
 .../deadlock-extension/src/core/userConfig.ts |   8 +-
 .../src/model/giteaPublicProperties.model.ts  |   6 +
 .../src/model/sshKeyPair.model.ts             |   4 +
 .../src/model/user.model.ts                   |  25 ++++
 .../deadlock-extension/src/recorder/index.ts  |   4 +-
 .../src/recorder/services/http-server.ts      |   6 +-
 .../deadlock-extension/src/recorder/utils.ts  |   2 +
 19 files changed, 377 insertions(+), 50 deletions(-)
 create mode 100644 deadlock-plugins/deadlock-extension/src/core/callApi.service.ts
 create mode 100644 deadlock-plugins/deadlock-extension/src/core/keycloakOAuth2DeviceFlowConnectionVSCodeImpl.ts
 create mode 100644 deadlock-plugins/deadlock-extension/src/core/sshKeyManager.ts
 create mode 100644 deadlock-plugins/deadlock-extension/src/model/giteaPublicProperties.model.ts
 create mode 100644 deadlock-plugins/deadlock-extension/src/model/sshKeyPair.model.ts
 create mode 100644 deadlock-plugins/deadlock-extension/src/model/user.model.ts

diff --git a/deadlock-plugins/deadlock-extension/package-lock.json b/deadlock-plugins/deadlock-extension/package-lock.json
index 8be81528..3c270bbc 100644
--- a/deadlock-plugins/deadlock-extension/package-lock.json
+++ b/deadlock-plugins/deadlock-extension/package-lock.json
@@ -1,18 +1,20 @@
 {
   "name": "deadlock-coding",
-  "version": "0.0.2",
+  "version": "0.1.3",
   "lockfileVersion": 2,
   "requires": true,
   "packages": {
     "": {
       "name": "deadlock-coding",
-      "version": "0.0.2",
+      "version": "0.1.3",
       "dependencies": {
         "@vscode/webview-ui-toolkit": "^1.0.0",
         "async": "^3.2.2",
+        "axios": "^0.27.2",
         "crypto-js": "^4.1.1",
         "date-fns": "^2.27.0",
         "inversify": "^6.0.1",
+        "is-docker": "^3.0.0",
         "marked": "^4.0.6",
         "node-fetch": "^3.2.3",
         "reflect-metadata": "^0.1.13",
@@ -744,8 +746,7 @@
     "node_modules/asynckit": {
       "version": "0.4.0",
       "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
-      "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
-      "dev": true
+      "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
     },
     "node_modules/auto-changelog": {
       "version": "2.4.0",
@@ -795,6 +796,28 @@
         }
       }
     },
+    "node_modules/axios": {
+      "version": "0.27.2",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
+      "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
+      "dependencies": {
+        "follow-redirects": "^1.14.9",
+        "form-data": "^4.0.0"
+      }
+    },
+    "node_modules/axios/node_modules/form-data": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+      "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+      "dependencies": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.8",
+        "mime-types": "^2.1.12"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
     "node_modules/azure-devops-node-api": {
       "version": "11.1.0",
       "resolved": "https://registry.npmjs.org/azure-devops-node-api/-/azure-devops-node-api-11.1.0.tgz",
@@ -1142,7 +1165,6 @@
       "version": "1.0.8",
       "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
       "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
-      "dev": true,
       "dependencies": {
         "delayed-stream": "~1.0.0"
       },
@@ -1294,7 +1316,6 @@
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
       "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
-      "dev": true,
       "engines": {
         "node": ">=0.4.0"
       }
@@ -1876,6 +1897,25 @@
       "integrity": "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw==",
       "dev": true
     },
+    "node_modules/follow-redirects": {
+      "version": "1.15.0",
+      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.0.tgz",
+      "integrity": "sha512-aExlJShTV4qOUOL7yF1U5tvLCB0xQuudbf6toyYA0E/acBNw71mvjFTnLaRp50aQaYocMR0a/RMMBIHeZnGyjQ==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://github.com/sponsors/RubenVerborgh"
+        }
+      ],
+      "engines": {
+        "node": ">=4.0"
+      },
+      "peerDependenciesMeta": {
+        "debug": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/form-data": {
       "version": "3.0.1",
       "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
@@ -2297,6 +2337,20 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/is-docker": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz",
+      "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==",
+      "bin": {
+        "is-docker": "cli.js"
+      },
+      "engines": {
+        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/is-extglob": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@@ -2636,7 +2690,6 @@
       "version": "1.51.0",
       "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz",
       "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==",
-      "dev": true,
       "engines": {
         "node": ">= 0.6"
       }
@@ -2645,7 +2698,6 @@
       "version": "2.1.34",
       "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz",
       "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==",
-      "dev": true,
       "dependencies": {
         "mime-db": "1.51.0"
       },
@@ -4906,8 +4958,7 @@
     "asynckit": {
       "version": "0.4.0",
       "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
-      "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
-      "dev": true
+      "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
     },
     "auto-changelog": {
       "version": "2.4.0",
@@ -4939,6 +4990,27 @@
         }
       }
     },
+    "axios": {
+      "version": "0.27.2",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
+      "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
+      "requires": {
+        "follow-redirects": "^1.14.9",
+        "form-data": "^4.0.0"
+      },
+      "dependencies": {
+        "form-data": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+          "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+          "requires": {
+            "asynckit": "^0.4.0",
+            "combined-stream": "^1.0.8",
+            "mime-types": "^2.1.12"
+          }
+        }
+      }
+    },
     "azure-devops-node-api": {
       "version": "11.1.0",
       "resolved": "https://registry.npmjs.org/azure-devops-node-api/-/azure-devops-node-api-11.1.0.tgz",
@@ -5199,7 +5271,6 @@
       "version": "1.0.8",
       "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
       "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
-      "dev": true,
       "requires": {
         "delayed-stream": "~1.0.0"
       }
@@ -5311,8 +5382,7 @@
     "delayed-stream": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
-      "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
-      "dev": true
+      "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
     },
     "delegates": {
       "version": "1.0.0",
@@ -5743,6 +5813,11 @@
       "integrity": "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw==",
       "dev": true
     },
+    "follow-redirects": {
+      "version": "1.15.0",
+      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.0.tgz",
+      "integrity": "sha512-aExlJShTV4qOUOL7yF1U5tvLCB0xQuudbf6toyYA0E/acBNw71mvjFTnLaRp50aQaYocMR0a/RMMBIHeZnGyjQ=="
+    },
     "form-data": {
       "version": "3.0.1",
       "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
@@ -6053,6 +6128,11 @@
         "has": "^1.0.3"
       }
     },
+    "is-docker": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz",
+      "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ=="
+    },
     "is-extglob": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@@ -6319,14 +6399,12 @@
     "mime-db": {
       "version": "1.51.0",
       "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz",
-      "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==",
-      "dev": true
+      "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g=="
     },
     "mime-types": {
       "version": "2.1.34",
       "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz",
       "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==",
-      "dev": true,
       "requires": {
         "mime-db": "1.51.0"
       }
diff --git a/deadlock-plugins/deadlock-extension/package.json b/deadlock-plugins/deadlock-extension/package.json
index 5095eb4f..24441062 100644
--- a/deadlock-plugins/deadlock-extension/package.json
+++ b/deadlock-plugins/deadlock-extension/package.json
@@ -1,7 +1,7 @@
 {
   "name": "deadlock-coding",
   "description": "Deadlock Coding",
-  "version": "0.0.2",
+  "version": "0.1.3",
   "publisher": "Deadlock",
   "icon": "media/logo.png",
   "engines": {
@@ -97,9 +97,11 @@
   "dependencies": {
     "@vscode/webview-ui-toolkit": "^1.0.0",
     "async": "^3.2.2",
+    "axios": "^0.27.2",
     "crypto-js": "^4.1.1",
     "date-fns": "^2.27.0",
     "inversify": "^6.0.1",
+    "is-docker": "^3.0.0",
     "marked": "^4.0.6",
     "node-fetch": "^3.2.3",
     "reflect-metadata": "^0.1.13",
diff --git a/deadlock-plugins/deadlock-extension/src/config.prod.ts b/deadlock-plugins/deadlock-extension/src/config.prod.ts
index c5b24b4b..93648f3f 100644
--- a/deadlock-plugins/deadlock-extension/src/config.prod.ts
+++ b/deadlock-plugins/deadlock-extension/src/config.prod.ts
@@ -4,7 +4,9 @@ export const KEYCLOAK_TOKEN_CREATE_URL = 'https://auth.deadlock.io/auth/realms/D
 export const KEYCLOAK_USER_INFO_URL = 'https://auth.deadlock.io/auth/realms/Deadlock/protocol/openid-connect/userinfo';
 export const REGISTRY_MISSION_URL = 'registry.e-biz.fr/deadlock/deadlock-challenges';
 export const REJECT_UNAUTHORIZED = true;
-export const ENABLE_HTTP_SERVER = false;
 export const ENABLE_AUTOMATIC_SAVE = true;
+export const ENABLE_RECORDER_HTTP_SERVER = false;
 export const RECORDER_HTTP_SERVER_PORT = 8751;
 export const RECORDER_HTTP_SERVER_URL = `http://localhost:${RECORDER_HTTP_SERVER_PORT}`;
+export const API_URL = 'https://api.deadlock.io/';
+export const API_QUERY_REFERER = 'https://takima.deadlock.io';
diff --git a/deadlock-plugins/deadlock-extension/src/config.staging.ts b/deadlock-plugins/deadlock-extension/src/config.staging.ts
index 3c310bb6..4067193b 100644
--- a/deadlock-plugins/deadlock-extension/src/config.staging.ts
+++ b/deadlock-plugins/deadlock-extension/src/config.staging.ts
@@ -6,6 +6,9 @@ export const KEYCLOAK_USER_INFO_URL =
   'https://auth.staging.deadlock.io/auth/realms/Deadlock/protocol/openid-connect/userinfo';
 export const REGISTRY_MISSION_URL = 'registry.e-biz.fr/deadlock/deadlock-challenges';
 export const REJECT_UNAUTHORIZED = true;
-export const ENABLE_HTTP_SERVER = false;
-export const HTTP_SERVER_PORT = 8751;
-export const HTTP_SERVER_URL = `http://localhost:${HTTP_SERVER_PORT}`;
+export const ENABLE_AUTOMATIC_SAVE = true;
+export const ENABLE_RECORDER_HTTP_SERVER = false;
+export const RECORDER_HTTP_SERVER_PORT = 8751;
+export const RECORDER_HTTP_SERVER_URL = `http://localhost:${RECORDER_HTTP_SERVER_PORT}`;
+export const API_URL = 'https://api.staging.deadlock.io/';
+export const API_QUERY_REFERER = 'https://takima.staging.deadlock.io';
diff --git a/deadlock-plugins/deadlock-extension/src/config.ts b/deadlock-plugins/deadlock-extension/src/config.ts
index 99c5026e..850e35d0 100644
--- a/deadlock-plugins/deadlock-extension/src/config.ts
+++ b/deadlock-plugins/deadlock-extension/src/config.ts
@@ -6,7 +6,9 @@ export const KEYCLOAK_USER_INFO_URL =
   'https://auth.dev.deadlock.io/auth/realms/Deadlock/protocol/openid-connect/userinfo';
 export const REGISTRY_MISSION_URL = 'registry.e-biz.fr/deadlock/deadlock-challenges';
 export const REJECT_UNAUTHORIZED = false;
-export const ENABLE_HTTP_SERVER = false;
 export const ENABLE_AUTOMATIC_SAVE = true;
-export const HTTP_SERVER_PORT = 8751;
-export const HTTP_SERVER_URL = `http://localhost:${HTTP_SERVER_PORT}`;
+export const ENABLE_RECORDER_HTTP_SERVER = false;
+export const RECORDER_HTTP_SERVER_PORT = 8751;
+export const RECORDER_HTTP_SERVER_URL = `http://localhost:${RECORDER_HTTP_SERVER_PORT}`;
+export const API_URL = 'https://api.dev.deadlock.io/';
+export const API_QUERY_REFERER = 'https://takima.dev.deadlock.io';
diff --git a/deadlock-plugins/deadlock-extension/src/core/callApi.service.ts b/deadlock-plugins/deadlock-extension/src/core/callApi.service.ts
new file mode 100644
index 00000000..3cefbb85
--- /dev/null
+++ b/deadlock-plugins/deadlock-extension/src/core/callApi.service.ts
@@ -0,0 +1,96 @@
+import { GiteaPublicProperties } from './../customTypings/giteaPublicProperties.model';
+import { SshKeyPair } from './../customTypings/sshKeyPair.model';
+import axios, { AxiosInstance } from 'axios';
+import { API_URL, API_QUERY_REFERER } from '../config';
+import { User } from '../customTypings/user.model';
+import Controller from './controller';
+import ExtensionStore from './extensionStore';
+import KeycloakOAuth2DeviceFlowConnection from './keycloakOAuth2DeviceFlowConnection';
+
+export default class CallApiService {
+  private callApi: AxiosInstance;
+  constructor(private keycloackConnection: KeycloakOAuth2DeviceFlowConnection, private extensionStore: ExtensionStore) {
+    this.callApi = axios.create({
+      baseURL: API_URL,
+      headers: {
+        'Content-Type': 'application/json',
+        referer: API_QUERY_REFERER,
+      },
+    });
+
+    this.initApiInterceptor();
+  }
+
+  initApiInterceptor() {
+    this.callApi.interceptors.request.use(
+      async (config) => {
+        const accessToken = await this.extensionStore.getAccessToken();
+
+        if (accessToken && config.headers) {
+          config.headers['authorization'] = `BEARER ${accessToken}`;
+        }
+        return config;
+      },
+      (error) => {
+        return Promise.reject(error);
+      },
+    );
+
+    this.callApi.interceptors.response.use(
+      (res) => {
+        return res;
+      },
+      async (err) => {
+        const originalConfig = err.config;
+        if (err.response) {
+          // Access Token was expired
+          if (err.response.status === 401) {
+            if (!originalConfig._retry) {
+              originalConfig._retry = true;
+              try {
+                const storedRefreshToken = await this.extensionStore.getRefreshToken();
+                const { accessToken } = await this.keycloackConnection.getToken({
+                  refreshToken: storedRefreshToken,
+                  openLink: Controller.openBrowserWithUrl,
+                });
+
+                this.callApi.defaults.headers.common['authorization'] = `BEARER ${accessToken}`;
+                return this.callApi(originalConfig);
+              } catch (_error: any) {
+                if (_error.response && _error.response.data) {
+                  return Promise.reject(_error.response.data);
+                }
+                return Promise.reject(_error);
+              }
+            } else if (originalConfig._retry) {
+              // IF REFRESH TOKEN NOT WORK, REQUEST NEW CONNECTION IN USER BROWSER
+              try {
+                await this.keycloackConnection.getToken({ openLink: Controller.openBrowserWithUrl });
+                return this.callApi(originalConfig);
+              } catch (_error) {
+                return Promise.reject(_error);
+              }
+            }
+          }
+
+          if (err.response.status === 403 && err.response.data) {
+            return Promise.reject(err.response.data);
+          }
+        }
+        return Promise.reject(err);
+      },
+    );
+  }
+
+  getGiteaPublicProperties(): Promise<GiteaPublicProperties> {
+    return this.callApi.get<GiteaPublicProperties>(`gitea`).then((res) => res.data);
+  }
+
+  getUserSshKey(): Promise<SshKeyPair> {
+    return this.callApi.put<SshKeyPair>(`users/gitea/keypair`).then((res) => res.data);
+  }
+
+  getUser(): Promise<User> {
+    return this.callApi.get<User>(`auth`).then((res) => res.data);
+  }
+}
diff --git a/deadlock-plugins/deadlock-extension/src/core/config.ts b/deadlock-plugins/deadlock-extension/src/core/config.ts
index 7222a295..9cb8eb83 100644
--- a/deadlock-plugins/deadlock-extension/src/core/config.ts
+++ b/deadlock-plugins/deadlock-extension/src/core/config.ts
@@ -1,9 +1,10 @@
 import * as os from 'os';
 import * as path from 'path';
+import isDocker from 'is-docker';
 
 const homeDir = os.homedir();
 // if we are on container, means the directory will depend differently
-const onContainer = homeDir.includes('theia') || homeDir.includes('root') || homeDir.includes('deadlock');
+const onContainer = isDocker();
 
 const deadlockExtensionPath = path.join(homeDir, 'deadlock-extension');
 
diff --git a/deadlock-plugins/deadlock-extension/src/core/controller.ts b/deadlock-plugins/deadlock-extension/src/core/controller.ts
index 633a1753..17339d05 100644
--- a/deadlock-plugins/deadlock-extension/src/core/controller.ts
+++ b/deadlock-plugins/deadlock-extension/src/core/controller.ts
@@ -1,3 +1,5 @@
+import { GiteaPublicProperties } from './../customTypings/giteaPublicProperties.model';
+import { User } from './../customTypings/user.model';
 import * as vscode from 'vscode';
 import {
   KEYCLOAK_DEVICE_AUTH_URL,
@@ -13,25 +15,32 @@ import { CHOOSE_MISSION_WORKDIR_COMMAND, CommandHandler, OPEN_URL_IN_BROWSER_COM
 import ExtensionStore from './extensionStore';
 import KeycloakOAuth2DeviceFlowConnection from './keycloakOAuth2DeviceFlowConnection';
 import Mission from './mission';
-
+import CallApiService from './callApi.service';
+import KeycloakOAuth2DeviceFlowConnectionVSCodeImpl from './keycloakOAuth2DeviceFlowConnectionVSCodeImpl';
+import SshKeyManager from './sshKeyManager';
 export default class Controller {
   public connection: KeycloakOAuth2DeviceFlowConnection;
   private commandHandler: CommandHandler;
   private briefingView: BriefingView;
   private quickSetupView: QuickSetupView;
   private extensionStore: ExtensionStore;
+  private callApiService: CallApiService;
+  private sshKeyManager: SshKeyManager;
 
   constructor(private context: vscode.ExtensionContext) {
     this.extensionStore = ExtensionStore.getInstance(context);
     this.briefingView = new BriefingView();
     this.quickSetupView = new QuickSetupView(context.extensionUri);
     this.commandHandler = new CommandHandler(this);
-    this.connection = new KeycloakOAuth2DeviceFlowConnection(
+    this.connection = new KeycloakOAuth2DeviceFlowConnectionVSCodeImpl(
       KEYCLOAK_DEVICE_AUTH_URL,
       KEYCLOAK_TOKEN_CREATE_URL,
       KEYCLOAK_USER_INFO_URL,
     );
 
+    this.callApiService = new CallApiService(this.connection, this.extensionStore);
+    this.sshKeyManager = new SshKeyManager();
+
     this.init();
   }
   private async init() {
@@ -72,9 +81,18 @@ export default class Controller {
       }
       this.chooseMissionWorkdir();
     } else {
+      await this.insertSshKeyInMissionWorkdir(folderUri[0].path);
       this.extensionStore.setMissionWorkdir(folderUri[0].path);
     }
   }
+
+  public async insertSshKeyInMissionWorkdir(sshKeyFolderPath) {
+    const { publicKey, privateKey } = await this.callApiService.getUserSshKey();
+    this.sshKeyManager.createSshKeyFiles(publicKey, privateKey, `${sshKeyFolderPath}/.ssh`);
+  }
+
+  public async createMissionUserChallengeJson(missionId: string) {}
+
   public async clear() {
     const exensionStorage = ExtensionStore.getInstance();
     await exensionStorage.clear();
@@ -95,6 +113,7 @@ export default class Controller {
   }
 
   public async launchMission(missionId: string | null, missionVersion: string | null) {
+    console.log('LAUCH MISSION : ' + missionId);
     if (missionId && missionVersion) {
       vscode.window.showInformationMessage(`vous lancez la mission ${missionId}`);
       const hadMissionWorkdir = this.extensionStore.getMissionWorkdir() !== undefined;
@@ -111,16 +130,26 @@ export default class Controller {
         vscode.window.showInformationMessage('Déjà connecté: session récupérée');
       }
 
+      console.log('BEFORE QUERY');
+      const user: User = await this.callApiService.getUser();
+      const giteaPublicProperties: GiteaPublicProperties = await this.callApiService.getGiteaPublicProperties();
       // TODO Should I fetch GET api/missions/{missionId} one day instead of passing necessary parameters in vscode xdg-open link ?
-      const mission = new Mission({
-        registryBaseURL: REGISTRY_MISSION_URL,
-        missionId: missionId,
-        missionVersion: missionVersion,
-      });
+      const mission = new Mission(
+        {
+          registryBaseURL: REGISTRY_MISSION_URL,
+          missionId: missionId,
+          missionVersion: missionVersion,
+        },
+        user,
+        giteaPublicProperties,
+      );
       vscode.window.showInformationMessage(
         'opening inside folder ' + this.extensionStore.getMissionWorkdir()! + '/' + missionId,
       );
+      console.log('BEFORE SETUP');
       await mission.setup({});
+      console.log('BEFORE open editor');
+
       await mission.openEditorInFolder();
 
       vscode.commands.executeCommand(OPEN_QUICK_SETUP_COMMAND.cmd);
diff --git a/deadlock-plugins/deadlock-extension/src/core/keycloakOAuth2DeviceFlowConnection.ts b/deadlock-plugins/deadlock-extension/src/core/keycloakOAuth2DeviceFlowConnection.ts
index e0fbb38b..1ec89b2e 100644
--- a/deadlock-plugins/deadlock-extension/src/core/keycloakOAuth2DeviceFlowConnection.ts
+++ b/deadlock-plugins/deadlock-extension/src/core/keycloakOAuth2DeviceFlowConnection.ts
@@ -65,7 +65,7 @@ export default class KeycloakOAuth2DeviceFlowConnection {
     }
   }
 
-  public async getToken(args: { refreshToken?: string; openLink: (link: string) => void }) {
+  public async getToken(args: { refreshToken?: string; openLink?: (link: string) => void }) {
     const { refreshToken, openLink } = args;
     if (!!refreshToken) {
       await this.createUserAuthentication({
@@ -85,6 +85,7 @@ export default class KeycloakOAuth2DeviceFlowConnection {
       await this.registerDevice();
     }
     try {
+      if (!openLink) throw new Error('You neeed to provide a way to open a link for oauth device flow protocol');
       openLink(this.deviceAuthorizationRequestResponse.verification_uri_complete!);
       await this.createUserAuthentication({
         url: this.tokenUrl,
diff --git a/deadlock-plugins/deadlock-extension/src/core/keycloakOAuth2DeviceFlowConnectionVSCodeImpl.ts b/deadlock-plugins/deadlock-extension/src/core/keycloakOAuth2DeviceFlowConnectionVSCodeImpl.ts
new file mode 100644
index 00000000..fabccd3a
--- /dev/null
+++ b/deadlock-plugins/deadlock-extension/src/core/keycloakOAuth2DeviceFlowConnectionVSCodeImpl.ts
@@ -0,0 +1,28 @@
+import ExtensionStore from './extensionStore';
+import KeycloakOAuth2DeviceFlowConnection from './keycloakOAuth2DeviceFlowConnection';
+
+export default class KeycloakOAuth2DeviceFlowConnectionVSCodeImpl extends KeycloakOAuth2DeviceFlowConnection {
+  private extensionStore: ExtensionStore;
+  constructor(deviceUrl: string, tokenUrl: string, userInfoUrl: string) {
+    super(deviceUrl, tokenUrl, userInfoUrl);
+    this.extensionStore = ExtensionStore.getInstance();
+  }
+
+  public async getToken(args: { refreshToken?: string; openLink: (link: string) => void }): Promise<{
+    accessToken: string;
+    refreshToken: string;
+  }> {
+    const existingAccessToken = await this.extensionStore.getAccessToken();
+    const existingRefreshToken = await this.extensionStore.getRefreshToken();
+    if (existingAccessToken!! && existingRefreshToken!! && (await super.tokenIsValid(existingAccessToken!))) {
+      return Promise.resolve({ accessToken: existingAccessToken, refreshToken: existingRefreshToken });
+    }
+    const { refreshToken, accessToken } = await super.getToken({
+      refreshToken: args.refreshToken,
+      openLink: args.openLink,
+    });
+    await this.extensionStore.setAccessToken(accessToken);
+    await this.extensionStore.setRefreshToken(refreshToken);
+    return Promise.resolve({ refreshToken: refreshToken, accessToken: accessToken });
+  }
+}
diff --git a/deadlock-plugins/deadlock-extension/src/core/mission.ts b/deadlock-plugins/deadlock-extension/src/core/mission.ts
index 8a4d7118..d159354d 100644
--- a/deadlock-plugins/deadlock-extension/src/core/mission.ts
+++ b/deadlock-plugins/deadlock-extension/src/core/mission.ts
@@ -1,9 +1,11 @@
+import { GiteaPublicProperties } from './../customTypings/giteaPublicProperties.model';
 import { exec as _exec } from 'child_process';
 import * as fs from 'fs';
 import * as util from 'util';
 import * as vscode from 'vscode';
 import { error as err, log } from '../recorder/utils';
 import ExtensionStore from './extensionStore';
+import { User, UserChallengeJson } from '../customTypings/user.model';
 
 /**
  * {@link https://nodejs.org/api/child_process.html#child_process_child_process_exec_command_options_callback}
@@ -22,16 +24,19 @@ export default class Mission {
   private readonly remoteGiteaWorkDir: string;
   private readonly userConfigPath: string;
   private readonly userSshPath: string;
+  private readonly user: User;
+  private readonly giteaPublicProperties: GiteaPublicProperties;
+
   constructor(
     private params: { registryBaseURL: string; missionId: string; missionVersion: string },
-    userSshPath: string,
+    user: User,
+    giteaPublicProperties: GiteaPublicProperties,
   ) {
     const { registryBaseURL, missionId, missionVersion } = params;
     this.hostBaseWorkDir = ExtensionStore.getInstance().getMissionWorkdir() ?? '';
     this.hostMissionDir = `${this.hostBaseWorkDir}/${missionId}`;
-    // TODO: NEED TO BE MOCK FOR MOMENT : WAITING US#8
-    this.userConfigPath = '/home/lansana/Documents/deadlock/deadlock-theia/config'; //`${this.hostMissionDir}/.userConfig`;
-    this.userSshPath = '/home/lansana/Documents/deadlock/deadlock-theia/ssh'; // userSshPath
+    this.userConfigPath = `${this.hostMissionDir}/.config`;
+    this.userSshPath = `${this.hostBaseWorkDir}/.ssh`;
     this.hostMissionDevcontainerDir = `${this.hostMissionDir}/.devcontainer`;
     this.hostMissionDevcontainerFileDir = `${this.hostMissionDevcontainerDir}/devcontainer.json`;
     this.hostMissionMountDir = `${this.hostMissionDir}/mounted`;
@@ -39,6 +44,8 @@ export default class Mission {
     this.remoteUserHomeDir = '/home/deadlock';
     this.remoteMissionDir = `${this.remoteUserHomeDir}/mission`;
     this.remoteGiteaWorkDir = `/src`;
+    this.user = user;
+    this.giteaPublicProperties = giteaPublicProperties;
   }
 
   static async pullImage(url) {
@@ -50,7 +57,13 @@ export default class Mission {
   public async setup(options?: Partial<DockerfileSpecific & Base & VSCodespecific & LifecycleScripts>) {
     await fs.promises.mkdir(this.hostMissionDevcontainerDir, { recursive: true });
     await fs.promises.mkdir(this.hostMissionMountDir, { recursive: true });
-    await fs.promises.writeFile(
+    await fs.promises.mkdir(this.userConfigPath, { recursive: true });
+    await this.createDevContainerFile(options);
+    await this.createUserChallengeJsonFile();
+  }
+
+  private createDevContainerFile(options?: Partial<DockerfileSpecific & Base & VSCodespecific & LifecycleScripts>) {
+    return fs.promises.writeFile(
       this.hostMissionDevcontainerFileDir,
       (() => {
         const devcontainer: Partial<DockerfileSpecific & Base & VSCodespecific & LifecycleScripts> = {
@@ -90,6 +103,25 @@ export default class Mission {
     );
   }
 
+  private createUserChallengeJsonFile() {
+    return fs.promises.writeFile(
+      `${this.userConfigPath}/user-challenge.json`,
+      (() => {
+        const userChallengeJson: UserChallengeJson = {
+          giteaHost: this.giteaPublicProperties.sshHost,
+          giteaSshPort: this.giteaPublicProperties.sshPort,
+          username: this.user.id.split('-').join(''),
+          email: `${this.user.id.split('-').join('')}@deadlock.io`,
+          missionId: this.params.missionId,
+          remoteGitUsername: this.user.id.split('-').join(''),
+          currentUserDetails: this.user.details,
+          remoteUserDetails: this.user.details,
+        };
+        return JSON.stringify(userChallengeJson, null, 2);
+      })(),
+    );
+  }
+
   public async openEditorInFolder(arbitraryPath?: string) {
     if (arbitraryPath) {
       return vscode.commands.executeCommand('remote-containers.openFolder', vscode.Uri.file(arbitraryPath));
diff --git a/deadlock-plugins/deadlock-extension/src/core/sshKeyManager.ts b/deadlock-plugins/deadlock-extension/src/core/sshKeyManager.ts
new file mode 100644
index 00000000..3ebf3a59
--- /dev/null
+++ b/deadlock-plugins/deadlock-extension/src/core/sshKeyManager.ts
@@ -0,0 +1,22 @@
+import ExtensionStore from './extensionStore';
+import * as fs from 'fs';
+
+export default class SshKeyManager {
+  constructor() {}
+
+  public async createSshKeyFiles(publicKey: string, privateKey: string, sshKeyFolderPath: string) {
+    await this.createSshKeyFolderIfNotExist(sshKeyFolderPath);
+    await fs.promises.writeFile(`${sshKeyFolderPath}/id_rsa.pub`, publicKey);
+
+    await fs.promises.writeFile(`${sshKeyFolderPath}/id_rsa`, privateKey, { mode: 0o600 });
+  }
+  private async createSshKeyFolderIfNotExist(sshKeyFolderPath) {
+    if (!this.isSshKeyFolderExist(sshKeyFolderPath)) {
+      await fs.promises.mkdir(sshKeyFolderPath, { recursive: true });
+    }
+  }
+
+  private isSshKeyFolderExist(sshKeyFolderPath: string) {
+    return fs.existsSync(sshKeyFolderPath);
+  }
+}
diff --git a/deadlock-plugins/deadlock-extension/src/core/userConfig.ts b/deadlock-plugins/deadlock-extension/src/core/userConfig.ts
index da50d104..0b6906cf 100644
--- a/deadlock-plugins/deadlock-extension/src/core/userConfig.ts
+++ b/deadlock-plugins/deadlock-extension/src/core/userConfig.ts
@@ -17,13 +17,7 @@ import { log } from './../recorder/utils';
  */
 
 import { error } from '../recorder/utils';
-
-export interface UserDetails {
-  firstName: string;
-  lastName: string;
-  avatarUrl: string;
-  email: string;
-}
+import { UserDetails } from '../customTypings/user.model';
 
 export default abstract class UserConfig {
   private userConfigJson: any | undefined;
diff --git a/deadlock-plugins/deadlock-extension/src/model/giteaPublicProperties.model.ts b/deadlock-plugins/deadlock-extension/src/model/giteaPublicProperties.model.ts
new file mode 100644
index 00000000..5c10aba3
--- /dev/null
+++ b/deadlock-plugins/deadlock-extension/src/model/giteaPublicProperties.model.ts
@@ -0,0 +1,6 @@
+export interface GiteaPublicProperties {
+  apiHost: string;
+  sshHost: string;
+  port: number;
+  sshPort: number;
+}
diff --git a/deadlock-plugins/deadlock-extension/src/model/sshKeyPair.model.ts b/deadlock-plugins/deadlock-extension/src/model/sshKeyPair.model.ts
new file mode 100644
index 00000000..4e1868d5
--- /dev/null
+++ b/deadlock-plugins/deadlock-extension/src/model/sshKeyPair.model.ts
@@ -0,0 +1,4 @@
+export interface SshKeyPair {
+  privateKey: string;
+  publicKey: string;
+}
diff --git a/deadlock-plugins/deadlock-extension/src/model/user.model.ts b/deadlock-plugins/deadlock-extension/src/model/user.model.ts
new file mode 100644
index 00000000..f3e1b03e
--- /dev/null
+++ b/deadlock-plugins/deadlock-extension/src/model/user.model.ts
@@ -0,0 +1,25 @@
+export interface User {
+  id: string;
+  details: UserDetails;
+}
+
+export interface UserDetails {
+  firstName: string;
+  lastName: string;
+  organization: string;
+  email: string;
+  login: string;
+  roles: string[];
+  avatarUrl: string;
+}
+
+export interface UserChallengeJson {
+  giteaHost: string;
+  giteaSshPort: number;
+  username: string;
+  email: string;
+  missionId: string;
+  remoteGitUsername: string;
+  currentUserDetails: UserDetails;
+  remoteUserDetails: UserDetails;
+}
diff --git a/deadlock-plugins/deadlock-extension/src/recorder/index.ts b/deadlock-plugins/deadlock-extension/src/recorder/index.ts
index 6e10a2b8..b513b15b 100644
--- a/deadlock-plugins/deadlock-extension/src/recorder/index.ts
+++ b/deadlock-plugins/deadlock-extension/src/recorder/index.ts
@@ -7,7 +7,7 @@ import { PROJECT_SRC_PATH, PROJECT_DEADLOCK_DESKTOP_PATH } from '../core/config'
 import { copyProjectSources, clearFilesExceptGit, log, error, renameTempToUserGitFiles } from './utils';
 import UserConfig from '../core/userConfig';
 import HttpServer from './services/http-server';
-import { ENABLE_HTTP_SERVER } from '../config';
+import { ENABLE_RECORDER_HTTP_SERVER } from '../config';
 import FileWatcher from './services/file-watcher';
 import AutomaticSave from './services/automatic-save';
 export default class Recorder {
@@ -63,7 +63,7 @@ export default class Recorder {
       log('Init GitMission');
       gitMission = await new GitMission(userConfig).init();
 
-      if (ENABLE_HTTP_SERVER) new HttpServer(gitMission);
+      if (ENABLE_RECORDER_HTTP_SERVER) new HttpServer(gitMission);
       await this.setupFromRemoteRepo(gitMission);
 
       if (ENABLE_AUTOMATIC_SAVE) new AutomaticSave(PROJECT_DEADLOCK_DESKTOP_PATH, gitMission);
diff --git a/deadlock-plugins/deadlock-extension/src/recorder/services/http-server.ts b/deadlock-plugins/deadlock-extension/src/recorder/services/http-server.ts
index ab7a5d5b..99623646 100644
--- a/deadlock-plugins/deadlock-extension/src/recorder/services/http-server.ts
+++ b/deadlock-plugins/deadlock-extension/src/recorder/services/http-server.ts
@@ -1,7 +1,7 @@
 import GitMission from '../../core/gitMission';
 import { Express } from 'express';
 import { updateRemote, CommitFrom, log } from '../utils';
-import { HTTP_SERVER_PORT } from '../../config';
+import { RECORDER_HTTP_SERVER_PORT } from '../../config';
 const express = require('express');
 export default class HttpServer {
   app: Express;
@@ -30,8 +30,8 @@ export default class HttpServer {
   }
 
   listen() {
-    this.app.listen(HTTP_SERVER_PORT, () => {
-      log(`Http server listen on port ${HTTP_SERVER_PORT}`);
+    this.app.listen(RECORDER_HTTP_SERVER_PORT, () => {
+      log(`Http server listen on port ${RECORDER_HTTP_SERVER_PORT}`);
     });
   }
 }
diff --git a/deadlock-plugins/deadlock-extension/src/recorder/utils.ts b/deadlock-plugins/deadlock-extension/src/recorder/utils.ts
index dcda0382..5b8585bb 100644
--- a/deadlock-plugins/deadlock-extension/src/recorder/utils.ts
+++ b/deadlock-plugins/deadlock-extension/src/recorder/utils.ts
@@ -4,6 +4,8 @@ import { PROJECT_SRC_PATH, PROJECT_DEADLOCK_DESKTOP_PATH } from '../core/config'
 import { format } from 'date-fns';
 import { execSync } from 'child_process';
 import { existsSync, renameSync, copyFileSync, PathLike } from 'fs';
+import KeycloakOAuth2DeviceFlowConnection from '../core/keycloakOAuth2DeviceFlowConnection';
+import ExtensionStore from '../core/extensionStore';
 
 const util = require('util');
 const unlink = util.promisify(require('fs').unlink);
-- 
GitLab