diff --git a/deadlock-plugins/deadlock-extension/src/core/api.service.ts b/deadlock-plugins/deadlock-extension/src/core/api.service.ts
index b7321240493e82d6aef594a04a102d062dfdce38..9583bdc3a45b0e4e88a46b21da7a73970490c68d 100644
--- a/deadlock-plugins/deadlock-extension/src/core/api.service.ts
+++ b/deadlock-plugins/deadlock-extension/src/core/api.service.ts
@@ -92,6 +92,8 @@ export default class ApiService {
     } catch (_error: any) {
       if (_error.response && _error.response.data) {
         return Promise.reject(_error.response.data);
+      } else {
+        return await this.onInvalidRefreshToken(originalConfig);
       }
       return Promise.reject(_error);
     }
diff --git a/deadlock-plugins/deadlock-extension/src/core/controller.ts b/deadlock-plugins/deadlock-extension/src/core/controller.ts
index 7697f39772277c24768dfdcc0bfbb1efebcd6e54..9ae124a6b08f87b48e0c1a2c4bbe2b44f985fa45 100644
--- a/deadlock-plugins/deadlock-extension/src/core/controller.ts
+++ b/deadlock-plugins/deadlock-extension/src/core/controller.ts
@@ -1,6 +1,5 @@
 import * as vscode from 'vscode';
 import { KEYCLOAK_DEVICE_AUTH_URL, KEYCLOAK_TOKEN_CREATE_URL, KEYCLOAK_USER_INFO_URL } from '../config';
-
 import { log } from '../recorder/utils';
 import BriefingView from '../view/briefingView';
 import QuickSetupView from '../view/quickSetupView';
@@ -11,8 +10,8 @@ import ApiService from './api.service';
 import SshKeyManager from './sshKeyManager';
 import { GiteaPublicProperties } from '../model/giteaPublicProperties.model';
 import { User } from '../model/user.model';
-import { openEditorInFolder } from './mission/missionOpenInEditor';
 import { Mission } from './mission/mission';
+import { MissionDevContainer } from './mission/missionDevContainer';
 
 export default class Controller {
   public connection: KeycloakOAuth2DeviceFlowConnection;
@@ -39,6 +38,7 @@ export default class Controller {
 
     this.init();
   }
+
   private async init() {
     const that = this;
     vscode.window.registerUriHandler({
@@ -68,6 +68,7 @@ export default class Controller {
 
     this.quickSetupView.isAlreadyConnected = (await this.extensionStore.getAccessToken()) !== undefined;
   }
+
   async chooseMissionWorkdir() {
     const actualMissionWorkDir = this.extensionStore.getMissionWorkdir();
 
@@ -107,6 +108,7 @@ export default class Controller {
     await this.createSshKeyPairIfNotExist();
     this.quickSetupView.isAlreadyConnected = true;
   }
+
   public static openBrowserWithUrl(url: string) {
     vscode.commands.executeCommand(OPEN_URL_IN_BROWSER_COMMAND.cmd, vscode.Uri.parse(url));
   }
@@ -125,22 +127,21 @@ export default class Controller {
       vscode.window.showInformationMessage('Connexion validée');
     }
 
-    const user: User = await this.callApiService.getUser();
-    const giteaPublicProperties: GiteaPublicProperties = await this.callApiService.getGiteaPublicProperties();
-
     const mission = new Mission(
       `${ExtensionStore.getInstance().getMissionWorkdir()}/${missionId}`,
       missionId,
       missionVersion,
-      giteaPublicProperties,
     );
 
+    const user: User = await this.callApiService.getUser();
+    const giteaPublicProperties: GiteaPublicProperties = await this.callApiService.getGiteaPublicProperties();
+
+    const missionDevcontainer = new MissionDevContainer(user, mission, giteaPublicProperties);
+
     vscode.window.showInformationMessage(
       'opening inside folder ' + this.extensionStore.getMissionWorkdir()! + '/' + missionId,
     );
 
-    await mission.setup(user);
-
-    await openEditorInFolder(mission.directory);
+    await missionDevcontainer.open();
   }
 }
diff --git a/deadlock-plugins/deadlock-extension/src/core/mission/mission.ts b/deadlock-plugins/deadlock-extension/src/core/mission/mission.ts
index bd236bec81753342bc7b594fa82239fd9b5783af..641b8112e3ab1f9be44ca2fa593568ae72b5f831 100644
--- a/deadlock-plugins/deadlock-extension/src/core/mission/mission.ts
+++ b/deadlock-plugins/deadlock-extension/src/core/mission/mission.ts
@@ -1,34 +1,11 @@
-import * as fs from 'fs';
-import { GiteaPublicProperties } from '../../model/giteaPublicProperties.model';
-import { User } from '../../model/user.model';
-import { DockerfileSpecific, Base, VSCodespecific, LifecycleScripts } from './devContainer';
-import { createDevContainerFile } from './missionDevContainer';
-import { createUserChallengeJsonFile } from './missionUserChallenge';
-
 export class Mission {
   readonly directory: string;
   readonly id: string;
   readonly version: string;
-  readonly giteaProperties: GiteaPublicProperties;
 
-  constructor(directory: string, id: string, version: string, giteaProperties: GiteaPublicProperties) {
+  constructor(directory: string, id: string, version: string) {
     this.directory = directory;
     this.id = id;
     this.version = version;
-    this.giteaProperties = giteaProperties;
-  }
-
-  public async setup(user: User, options?: Partial<DockerfileSpecific & Base & VSCodespecific & LifecycleScripts>) {
-    await this.createDirectories([
-      `${this.directory}/.config`,
-      `${this.directory}/.devcontainer`,
-      `${this.directory}/mounted`,
-    ]);
-    await createDevContainerFile(this, options);
-    await createUserChallengeJsonFile(user, this, this.giteaProperties);
-  }
-
-  private createDirectories(directoryPaths: string[]) {
-    return Promise.all(directoryPaths.map((directoryPath) => fs.promises.mkdir(directoryPath, { recursive: true })));
   }
 }
diff --git a/deadlock-plugins/deadlock-extension/src/core/mission/missionDevContainer.ts b/deadlock-plugins/deadlock-extension/src/core/mission/missionDevContainer.ts
index aff814655d451ca5e1b61e74d943706d38f06b33..363c60ca47c183f6b150bac48c8f954c42072d0e 100644
--- a/deadlock-plugins/deadlock-extension/src/core/mission/missionDevContainer.ts
+++ b/deadlock-plugins/deadlock-extension/src/core/mission/missionDevContainer.ts
@@ -1,57 +1,111 @@
 import { userSshKeyFolderPath } from '../config';
-import ExtensionStore from '../extensionStore';
 import { Base, DockerfileSpecific, LifecycleScripts, VSCodespecific } from './devContainer';
-import * as fs from 'fs';
+import { existsSync } from 'fs';
+import { mkdir, writeFile } from 'fs/promises';
 import { Mission } from './mission';
+import { User, UserChallengeJson } from '../../model/user.model';
+import { GiteaPublicProperties } from '../../model/giteaPublicProperties.model';
+import { commands, Uri } from 'vscode';
+import assert = require('assert');
 
 const DOCKER_IMAGE_URL = 'registry.takima.io/deadlock/deadlock-challenges';
 
-export function createDevContainerFile(
-  mission: Mission,
-  options?: Partial<DockerfileSpecific & Base & VSCodespecific & LifecycleScripts>,
-) {
-  const hostBaseWorkDir = ExtensionStore.getInstance().getMissionWorkdir() ?? '';
-  const hostMissionDir = `${hostBaseWorkDir}/${mission.id}`;
-  const userMissionConfigPath = `${hostMissionDir}/.config`;
-  const hostMissionDevcontainerDir = `${hostMissionDir}/.devcontainer`;
-  const hostMissionDevcontainerFileDir = `${hostMissionDevcontainerDir}/devcontainer.json`;
-  const hostMissionMountDir = `${hostMissionDir}/mounted`;
-  const dockerImageURL = `${DOCKER_IMAGE_URL}/${mission.id}:${mission.version}`;
-  const remoteUserHomeDir = '/home/deadlock';
-  const remoteMissionDir = `${remoteUserHomeDir}/mission/`;
-  const remoteGiteaWorkDir = `/src`;
-
-  return fs.promises.writeFile(
-    hostMissionDevcontainerFileDir,
-    (() => {
-      const devcontainer: Partial<DockerfileSpecific & Base & VSCodespecific & LifecycleScripts> = {
-        name: `deadlock-${mission.id}`,
-        image: dockerImageURL,
-        extensions: ['Deadlock.deadlock-coding'],
-        remoteUser: 'deadlock',
-        mounts: [
-          `source=${userSshKeyFolderPath},target=/tmp/.ssh,type=bind,consistency=cached,readonly`,
-          `source=${userMissionConfigPath},target=/home/config/,type=bind,consistency=cached,readonly`,
-          'source=/etc/hosts,target=/etc/hosts,type=bind,consistency=cached,readonly',
-        ],
-        userEnvProbe: 'interactiveShell',
-        settings: {
-          'terminal.integrated.defaultProfile.linux': 'bash',
-          'terminal.integrated.profiles.linux': {
-            bash: {
-              path: '/bin/bash',
+export class MissionDevContainer {
+  private isInit = false;
+
+  private readonly dirs = {
+    config: `${this.mission.directory}/.config`,
+    devcontainer: `${this.mission.directory}/.devcontainer`,
+    mounted: `${this.mission.directory}/mounted`,
+  };
+
+  constructor(
+    private readonly user: User,
+    private readonly mission: Mission,
+    private readonly giteaProperties: GiteaPublicProperties,
+  ) {}
+
+  async open() {
+    if (!this.isInit) {
+      await this.init();
+    }
+    assert(existsSync(this.mission.directory));
+    await commands.executeCommand('remote-containers.openFolder', Uri.file(this.mission.directory));
+  }
+  private async init() {
+    if (this.isInit) {
+      throw new Error(`${MissionDevContainer.constructor.name} Already initialized`);
+    }
+
+    this.isInit = true;
+    await this.createDirectories(...Object.values(this.dirs));
+    await this.createDevContainerFile();
+    await this.createUserChallengeJsonFile(this.user, this.giteaProperties);
+  }
+
+  private createUserChallengeJsonFile(user: User, giteaPublicProperties: GiteaPublicProperties) {
+    return writeFile(
+      `${this.dirs.config}/user-challenge.json`,
+      (() => {
+        const userChallengeJson: UserChallengeJson = {
+          giteaHost: giteaPublicProperties.sshHost,
+          giteaSshPort: giteaPublicProperties.sshPort,
+          username: user.id.split('-').join(''),
+          email: `${user.id.split('-').join('')}@deadlock.io`,
+          missionId: this.mission.id,
+          remoteGitUsername: user.id.split('-').join(''),
+          currentUserDetails: user.details,
+          remoteUserDetails: user.details,
+        };
+        return JSON.stringify(userChallengeJson, null, 2);
+      })(),
+    );
+  }
+
+  private createDevContainerFile(options?: Partial<DockerfileSpecific & Base & VSCodespecific & LifecycleScripts>) {
+    const remoteUserHomeDir = '/home/deadlock';
+    const remoteMissionDir = `${remoteUserHomeDir}/mission/`;
+    const remoteGiteaWorkDir = `/src`;
+    const hostMissionDevcontainerFileDir = `${this.dirs.devcontainer}/devcontainer.json`;
+
+    const image = `${DOCKER_IMAGE_URL}/${this.mission.id}:${this.mission.version}`;
+
+    return writeFile(
+      hostMissionDevcontainerFileDir,
+      (() => {
+        const devcontainer: Partial<DockerfileSpecific & Base & VSCodespecific & LifecycleScripts> = {
+          name: `deadlock-${this.mission.id}`,
+          image,
+          extensions: ['Deadlock.deadlock-coding'],
+          remoteUser: 'deadlock',
+          mounts: [
+            `source=${userSshKeyFolderPath},target=/tmp/.ssh,type=bind,consistency=cached,readonly`,
+            `source=${this.dirs.config},target=/home/config/,type=bind,consistency=cached,readonly`,
+            'source=/etc/hosts,target=/etc/hosts,type=bind,consistency=cached,readonly',
+          ],
+          userEnvProbe: 'interactiveShell',
+          settings: {
+            'terminal.integrated.defaultProfile.linux': 'bash',
+            'terminal.integrated.profiles.linux': {
+              bash: {
+                path: '/bin/bash',
+              },
             },
           },
-        },
-        overrideCommand: false,
-        shutdownAction: 'stopContainer',
-        workspaceMount: `source=${hostMissionMountDir},target=${remoteMissionDir},type=bind`,
-        workspaceFolder: `${remoteMissionDir}`,
-        onCreateCommand: `cp -R ${remoteGiteaWorkDir}/. ${remoteMissionDir}`,
-        runArgs: ['--privileged'],
-        ...options,
-      };
-      return JSON.stringify(devcontainer, null, 2);
-    })(),
-  );
+          overrideCommand: false,
+          shutdownAction: 'stopContainer',
+          workspaceMount: `source=${this.dirs.mounted},target=${remoteMissionDir},type=bind`,
+          workspaceFolder: `${remoteMissionDir}`,
+          onCreateCommand: `cp -R ${remoteGiteaWorkDir}/. ${remoteMissionDir}`,
+          runArgs: ['--privileged'],
+          ...options,
+        };
+        return JSON.stringify(devcontainer, null, 2);
+      })(),
+    );
+  }
+
+  private createDirectories(...directoryPaths: string[]) {
+    return Promise.all(directoryPaths.map((directoryPath) => mkdir(directoryPath, { recursive: true })));
+  }
 }
diff --git a/deadlock-plugins/deadlock-extension/src/core/mission/missionOpenInEditor.ts b/deadlock-plugins/deadlock-extension/src/core/mission/missionOpenInEditor.ts
deleted file mode 100644
index 4c3f8367bc3f0fd3cf886830815ff52a43a77d03..0000000000000000000000000000000000000000
--- a/deadlock-plugins/deadlock-extension/src/core/mission/missionOpenInEditor.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import * as fs from 'fs';
-import { error as err, log } from '../../recorder/utils';
-import * as vscode from 'vscode';
-
-export async function openEditorInFolder(folderPath: string) {
-  if (!fs.existsSync(folderPath)) {
-    log('WARN missing path ', folderPath);
-    await fs.promises.mkdir(folderPath, { recursive: true });
-  }
-  await vscode.commands.executeCommand('remote-containers.openFolder', vscode.Uri.file(folderPath));
-}
diff --git a/deadlock-plugins/deadlock-extension/src/core/mission/missionUserChallenge.ts b/deadlock-plugins/deadlock-extension/src/core/mission/missionUserChallenge.ts
deleted file mode 100644
index aa98cfbbec7d13447978c9b9230c6e2d11bbc7da..0000000000000000000000000000000000000000
--- a/deadlock-plugins/deadlock-extension/src/core/mission/missionUserChallenge.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import { User, UserChallengeJson } from '../../model/user.model';
-import * as fs from 'fs';
-import { GiteaPublicProperties } from '../../model/giteaPublicProperties.model';
-import { Mission } from './mission';
-
-export function createUserChallengeJsonFile(
-  user: User,
-  mission: Mission,
-  giteaPublicProperties: GiteaPublicProperties,
-) {
-  return fs.promises.writeFile(
-    `${mission.directory}/.config/user-challenge.json`,
-    (() => {
-      const userChallengeJson: UserChallengeJson = {
-        giteaHost: giteaPublicProperties.sshHost,
-        giteaSshPort: giteaPublicProperties.sshPort,
-        username: user.id.split('-').join(''),
-        email: `${user.id.split('-').join('')}@deadlock.io`,
-        missionId: mission.id,
-        remoteGitUsername: user.id.split('-').join(''),
-        currentUserDetails: user.details,
-        remoteUserDetails: user.details,
-      };
-      return JSON.stringify(userChallengeJson, null, 2);
-    })(),
-  );
-}
diff --git a/deadlock-plugins/deadlock-extension/src/model/user.model.ts b/deadlock-plugins/deadlock-extension/src/model/user.model.ts
index f3e1b03e91b32d58e85f07df898e8f50b554020e..7744bdc8f081d3c950926eb76932c3dc6078225b 100644
--- a/deadlock-plugins/deadlock-extension/src/model/user.model.ts
+++ b/deadlock-plugins/deadlock-extension/src/model/user.model.ts
@@ -1,3 +1,5 @@
+import { GiteaPublicProperties } from './giteaPublicProperties.model';
+
 export interface User {
   id: string;
   details: UserDetails;
@@ -23,3 +25,13 @@ export interface UserChallengeJson {
   currentUserDetails: UserDetails;
   remoteUserDetails: UserDetails;
 }
+
+export interface UserChallengeJson2 {
+  gitea: Pick<GiteaPublicProperties, 'sshPort' | 'sshHost'>;
+  username: string;
+  email: string;
+  missionId: string;
+  remoteGitUsername: string;
+  currentUserDetails: UserDetails;
+  remoteUserDetails: UserDetails;
+}