diff --git a/.htaccess b/.htaccess
index fa24a060ae7e5730d1ebd8aaccad939411216459..f782cf9c008a84c36305a8757fde5bc5c007a4ad 100755
--- a/.htaccess
+++ b/.htaccess
@@ -8,5 +8,4 @@
 
 RewriteEngine On
 RewriteRule "\.(css|js|eot|ttf|woff|woff2|svg|ico|png)$" - [L]
-RewriteRule "^simplesaml/" - [L]
-RewriteRule ^ dist/index.html
+RewriteRule "^simplesaml/" - [L]
\ No newline at end of file
diff --git a/bin/scheduler.php b/bin/scheduler.php
index eff7efb5a31446c303b5fd427b58eb259244599f..42f060839872ff6932a615ae55dd7fc02df6cbcf 100644
--- a/bin/scheduler.php
+++ b/bin/scheduler.php
@@ -177,7 +177,7 @@ function sendNotifications(array $args)
         $rows = '';
         $recipientNotificationIds = [];
         foreach ($recipientNotifications as $recipientNotification) {
-            $url  = $args['applicationUrl'] . 'dist/documents/' . $recipientNotification['main_document_id'];
+            $url  = $args['applicationUrl'] . 'dist/#/documents/' . $recipientNotification['main_document_id'];
             $document = DocumentModel::getById(['id' => $recipientNotification['main_document_id'], 'select' => ['title']]);
 
             $row = str_replace('{{object}}', $document['title'], $lang['notificationTemplateRow']);
diff --git a/index.php b/index.php
index 0267c86efab0fb6fba259180788d0e502bec99cc..e7e1bf90926217aeb923c6cc0b8a33db62202367 100644
--- a/index.php
+++ b/index.php
@@ -1,3 +1,3 @@
 <script>
-    window.location.href = 'dist/index.html';
+    window.location.href = 'dist/';
 </script>
diff --git a/lang/en.json b/lang/en.json
index 9524834565f9e35e98bbcef1c36d1c6cb056c9bb..af1fd867d6baa16390a84520a9337f48e0915319 100755
--- a/lang/en.json
+++ b/lang/en.json
@@ -668,6 +668,8 @@
         "cannotAcces": "Unable to access document",
         "metaSignSignatureDisabled": "The metaSignSignature parameter is not activated",
 		"eidas_metasign": "Metasign Stamp",
-		"eidas_metasignUser": "Metasign Stamp"
+		"eidas_metasignUser": "Metasign Stamp",
+		"inputCardReaderUpdateCerts": "Card entry detected, updating certificates...",
+		"outputCardReaderUpdateCerts": "Card output detected, updating certificates..."
     }
 }
\ No newline at end of file
diff --git a/lang/fr.json b/lang/fr.json
index 9f0d16cf13a6378046abd0bdf06d724ba5a905b6..29b28724f647df17acdca9f487c0bb8a01c978d0 100755
--- a/lang/fr.json
+++ b/lang/fr.json
@@ -667,6 +667,8 @@
 		"hardDeleted":"Fichiers supprimés",
 		"metaSignSignatureDisabled": "Le paramètre  metaSignSignature n'est pas activé",
 		"eidas_metasign": "Cachet Metasign",
-		"eidas_metasignUser": "Cachet Metasign"
+		"eidas_metasignUser": "Cachet Metasign",
+		"inputCardReaderUpdateCerts": "Une entrée de carte détectée, actualisation des certificats...",
+		"outputCardReaderUpdateCerts": "Une sortie de carte détectée, actualisation des certificats..."
 	}
 }
diff --git a/package.json b/package.json
index b147c9d5b3dcbf48404b5ad1ebc2eb82bbf34a9c..506ff7112b6374f71309ff348e1568bfb186cdd5 100644
--- a/package.json
+++ b/package.json
@@ -3,9 +3,9 @@
   "version": "21.03.1",
   "description": "Annoter ou Signer vos documents sur votre tablette",
   "homepage": "https://labs.maarch.org/maarch/MaarchParapheur",
-  "engines" : { 
-    "npm" : ">=6.14.5",
-    "node" : ">=14.4.0"
+  "engines": {
+    "npm": ">=6.14.5",
+    "node": ">=14.4.0"
   },
   "scripts": {
     "dep-list": "npm list --depth=0",
@@ -54,7 +54,7 @@
     "@ngrx/store-devtools": "^9.2.1",
     "@ngx-translate/core": "^12.1.2",
     "@ngx-translate/http-loader": "^4.0.0",
-    "@peculiar/fortify-webcomponents": "^0.17.3",
+    "@peculiar/fortify-webcomponents": "^3.1.0",
     "@types/hammerjs": "^2.0.39",
     "@types/jasmine": "^3.7.7",
     "@types/jasminewd2": "^2.0.9",
diff --git a/src/app/document/controllers/DocumentController.php b/src/app/document/controllers/DocumentController.php
index 86a507f557e6b6b0ce19cc92fee6e965a00c1fef..ab29ce646ab3cd6e386d68e79f21722cb10ceb16 100755
--- a/src/app/document/controllers/DocumentController.php
+++ b/src/app/document/controllers/DocumentController.php
@@ -500,7 +500,8 @@ class DocumentController
                     return $response->withStatus(400)->withJson(['errors' => "Body workflow[{$key}] signaturePositions is not an array"]);
                 }
                 foreach ($workflow['signaturePositions'] as $keySP => $signaturePosition) {
-                    if (empty($signaturePosition['positionX']) || empty($signaturePosition['positionY']) || empty($signaturePosition['page'])) {
+                    if (!Validator::each(Validator::between(0, 100, true))->validate([$signaturePosition['positionX'], $signaturePosition['positionY']])
+                        || !Validator::intVal()->positive()->validate($signaturePosition['page'])) {
                         return $response->withStatus(400)->withJson(['errors' => "Body workflow[{$key}][signaturePositions][{$keySP}] is wrong formatted"]);
                     }
                 }
@@ -510,7 +511,8 @@ class DocumentController
                     return $response->withStatus(400)->withJson(['errors' => "Body workflow[{$key}] datePositions is not an array"]);
                 }
                 foreach ($workflow['datePositions'] as $keyDP => $datePosition) {
-                    if (empty($datePosition['positionX']) || empty($datePosition['positionY']) || empty($datePosition['page'])) {
+                    if (!Validator::each(Validator::between(0, 100, true))->validate([$datePosition['positionX'], $datePosition['positionY']])
+                        || !Validator::intVal()->positive()->validate($datePosition['page'])) {
                         return $response->withStatus(400)->withJson(['errors' => "Body workflow[{$key}][datePositions][{$keyDP}] is wrong formatted"]);
                     } elseif (empty($datePosition['color']) || empty($datePosition['font']) || empty($datePosition['format']) || empty($datePosition['width'])) {
                         return $response->withStatus(400)->withJson(['errors' => "Body workflow[{$key}][datePositions][{$keyDP}] is wrong formatted"]);
diff --git a/src/app/email/controllers/EmailController.php b/src/app/email/controllers/EmailController.php
index b4cc19f7e9b6cac8f25bc82c286efd26fa290a7d..ad73e50dde847563278aa23c51e261e508a4dc21 100644
--- a/src/app/email/controllers/EmailController.php
+++ b/src/app/email/controllers/EmailController.php
@@ -263,7 +263,7 @@ class EmailController
     public static function sendNotificationToTypist(array $args)
     {
         $lang = LanguageController::get(['lang' => $args['recipient']['lang']]);
-        $url = ConfigurationModel::getApplicationUrl() . 'dist/search?documentId=' . $args['documentId'];
+        $url = ConfigurationModel::getApplicationUrl() . 'dist/#/search?documentId=' . $args['documentId'];
 
         if ($args['mode'] == 'END') {
             $subject = $lang['notificationEndOfWorkflowSubject'];
@@ -347,7 +347,7 @@ class EmailController
     public static function sendNotificationToUser(array $args)
     {
         $lang = LanguageController::get(['lang' => $args['preferences']['lang']]);
-        $url  = ConfigurationModel::getApplicationUrl() . 'dist/documents/' . $args['documentId'];
+        $url  = ConfigurationModel::getApplicationUrl() . 'dist/#/documents/' . $args['documentId'];
         EmailController::createEmail([
             'userId' => $args['senderId'],
             'data'   => [
diff --git a/src/app/user/controllers/UserController.php b/src/app/user/controllers/UserController.php
index 468e4753442a5eab6f20086ce3e1c21fa2807d1e..368b5419e1d95a148c87ce99c076e8a1427061be 100755
--- a/src/app/user/controllers/UserController.php
+++ b/src/app/user/controllers/UserController.php
@@ -732,7 +732,7 @@ class UserController
         $user['preferences'] = json_decode($user['preferences'], true);
         $lang = LanguageController::get(['lang' => $user['preferences']['lang']]);
 
-        $url = ConfigurationModel::getApplicationUrl() . 'dist/update-password?token=' . $resetToken;
+        $url = ConfigurationModel::getApplicationUrl() . 'dist/#/update-password?token=' . $resetToken;
         EmailController::createEmail([
             'userId' => $user['id'],
             'data'   => [
diff --git a/src/core/controllers/AuthenticationController.php b/src/core/controllers/AuthenticationController.php
index aa044897be97824643448c3a0d4f0e093f42a383..23e725183f9da7e698053cf65617b322e92d1852 100755
--- a/src/core/controllers/AuthenticationController.php
+++ b/src/core/controllers/AuthenticationController.php
@@ -377,7 +377,7 @@ class AuthenticationController
             $user = UserModel::getById(['select' => ['login'], 'id' => $args['userId']]);
             $lang = LanguageController::get(['lang' => 'fr']);
     
-            $url = ConfigurationModel::getApplicationUrl() . 'dist/update-password?token=' . $resetToken;
+            $url = ConfigurationModel::getApplicationUrl() . 'dist/#/update-password?token=' . $resetToken;
             EmailController::createEmail([
                 'userId' => $args['userId'],
                 'data'   => [
diff --git a/src/frontend/app/administration/customization/customization.component.ts b/src/frontend/app/administration/customization/customization.component.ts
index defeeb982c7447832d26deb75e1f20420f111baa..84b8d8cf50054a89193a408f49271748ab15d2d3 100644
--- a/src/frontend/app/administration/customization/customization.component.ts
+++ b/src/frontend/app/administration/customization/customization.component.ts
@@ -109,11 +109,11 @@ export class CustomizationComponent implements OnInit, OnDestroy {
     initMce() {
         const param = {
             selector: '#login_message',
-            base_url: this.functions.getBaseUrl() + '/tinymce/',
+            base_url: `${this.functions.getBaseUrl().split('#')[0]}/tinymce/`,
             height: '200',
             suffix: '.min',
             extended_valid_elements : 'tag,class',
-            content_css: this.functions.getBaseUrl() + '/assets/custom_tinymce.css',
+            content_css: `${this.functions.getBaseUrl().split('#')[0]}/assets/custom_tinymce.css`,
             language: this.translate.instant('lang.langISO').replace('-', '_'),
             language_url: `../node_modules/tinymce-i18n/langs/${this.translate.instant('lang.langISO').replace('-', '_')}.js`,
             menubar: false,
diff --git a/src/frontend/app/administration/otp/otp.component.ts b/src/frontend/app/administration/otp/otp.component.ts
index ddb988e7557b174b93b48d30cd99f267c47cb1f8..7d85a9b32e04081383f5c398a5d9f2385498d217 100644
--- a/src/frontend/app/administration/otp/otp.component.ts
+++ b/src/frontend/app/administration/otp/otp.component.ts
@@ -110,11 +110,11 @@ export class OtpComponent implements OnInit, OnDestroy {
     initMce() {
         const param = {
             selector: '#email_message',
-            base_url: this.functions.getBaseUrl() + '/tinymce/',
+            base_url: `${this.functions.getBaseUrl().split('#')[0]}/tinymce/`,
             height: '200',
             suffix: '.min',
             extended_valid_elements : 'tag,class',
-            content_css: this.functions.getBaseUrl() + '/assets/custom_tinymce.css',
+            content_css: `${this.functions.getBaseUrl().split('#')[0]}/assets/custom_tinymce.css`,
             language: this.translate.instant('lang.langISO').replace('-', '_'),
             language_url: `../node_modules/tinymce-i18n/langs/${this.translate.instant('lang.langISO').replace('-', '_')}.js`,
             menubar: false,
diff --git a/src/frontend/app/app-routing.module.ts b/src/frontend/app/app-routing.module.ts
index b6f8bbc5cf0bfbf21e2a2fdacf6e499b0be6a232..57b02f95ea80012fd80acac5fbe7c80c97e5dfee 100755
--- a/src/frontend/app/app-routing.module.ts
+++ b/src/frontend/app/app-routing.module.ts
@@ -62,7 +62,7 @@ import { NotificationsListComponent } from './administration/notification/notifi
             { path: 'update-password', component: UpdatePasswordComponent },
             { path: 'password-modification', component: PasswordModificationComponent },
             { path: '**', redirectTo: 'login', pathMatch: 'full' },
-        ], { relativeLinkResolution: 'legacy' }),
+        ], { relativeLinkResolution: 'legacy', useHash: true }),
     ],
     exports: [
         RouterModule
diff --git a/src/frontend/app/document/document.component.ts b/src/frontend/app/document/document.component.ts
index 99d0490b9dc56d5ddebadf88ee7f3833a3cea0de..823c33e785a6e27f7bd7c9f839929925a94ef470 100755
--- a/src/frontend/app/document/document.component.ts
+++ b/src/frontend/app/document/document.component.ts
@@ -635,18 +635,7 @@ export class DocumentComponent implements OnInit, OnDestroy {
                 {
                     text: this.getLabel(event),
                     handler: async (data: any) => {
-                        const currentUserWorkflow = this.mainDocument.workflow.filter((line: { current: boolean }) => line.current === true)[0];
-                        const idsToProcess = await this.actionsService.checkGroupMail(this.mainDocument, 'validate');
-                        const res = await this.signatureMethodService.checkAuthenticationAndLaunchAction(currentUserWorkflow, data.paragraph, idsToProcess);
-
-                        if (!this.functionsService.empty(res)) {
-                            const config: MatBottomSheetConfig = {
-                                disableClose: true,
-                                direction: 'ltr'
-                            };
-                            this.bottomSheet.open(SuccessInfoValidBottomSheetComponent, config);
-                            this.localStorage.remove(this.mainDocument.id.toString());
-                        }
+                        await this.processToSign(data);
                     }
                 }
             ]
@@ -655,6 +644,24 @@ export class DocumentComponent implements OnInit, OnDestroy {
         await alert.present();
     }
 
+    async processToSign(data: any) {
+        const currentUserWorkflow = this.mainDocument.workflow.filter((line: { current: boolean }) => line.current === true)[0];
+        const idsToProcess = await this.actionsService.checkGroupMail(this.mainDocument, 'validate');
+        const res = await this.signatureMethodService.checkAuthenticationAndLaunchAction(currentUserWorkflow, data.paragraph, idsToProcess);
+        if (!this.functionsService.empty(res)) {
+            if (res === 'refresh_certs') {
+                this.processToSign(data);
+            } else {
+                const config: MatBottomSheetConfig = {
+                    disableClose: true,
+                    direction: 'ltr'
+                };
+                this.bottomSheet.open(SuccessInfoValidBottomSheetComponent, config);
+                this.localStorage.remove(this.mainDocument.id.toString());
+            }
+        }
+    }
+
     async removeTags() {
         this.signaturesService.currentAction = 0;
 
diff --git a/src/frontend/app/document/visa-workflow/visa-workflow.component.ts b/src/frontend/app/document/visa-workflow/visa-workflow.component.ts
index 21ee72d5939cbed3a12394d0af743914766b4108..db4cd09b78b9a64c00c95575ef4e54f932d91c63 100644
--- a/src/frontend/app/document/visa-workflow/visa-workflow.component.ts
+++ b/src/frontend/app/document/visa-workflow/visa-workflow.component.ts
@@ -22,7 +22,7 @@ import { FunctionsService } from '../../service/functions.service';
 export class VisaWorkflowComponent implements OnInit {
 
     @Input() editMode: boolean = false;
-    @Input() visaWorkflow: any = [];
+    @Input() visaWorkflow: any[] = [];
     @ViewChild(IonReorderGroup) reorderGroup: IonReorderGroup;
 
     loading: boolean = false;
@@ -37,6 +37,8 @@ export class VisaWorkflowComponent implements OnInit {
     hasConnector: boolean = false;
     userDelegated: boolean = false;
 
+    tmpArray: any[] = [];
+
     constructor(
         public http: HttpClient,
         private translate: TranslateService,
@@ -406,20 +408,55 @@ export class VisaWorkflowComponent implements OnInit {
         });
     }
 
-    setPositionsWorkfow(resource: any, positions: any[]) {
-        this.clearPositionsFromResource(resource);
+    setPositionsWorkfow(resources: any[], index: number, positions: any[]) {
+        this.clearPositionsFromResource(resources[index]);
         if (positions.length > 0) {
             Object.keys(positions).forEach(key => {
                 const objPos = {
                     ...positions[key],
-                    mainDocument : resource.mainDocument,
+                    mainDocument : resources[index].mainDocument,
                 };
-                this.visaWorkflow[positions[key].sequence].signaturePositions = this.visaWorkflow[positions[key].sequence].signaturePositions.filter((pos: any) => pos.mainDocument !== resource.mainDocument);
                 this.visaWorkflow[positions[key].sequence].signaturePositions.push(objPos);
+
+                const existingObject: any = this.tmpArray.find((item: any) => item.key === positions[key].sequence && item.page === positions[key].page && item.docIndex === resources.indexOf(resources[index]));
+                if (existingObject !== undefined) {
+                    this.tmpArray[this.tmpArray.indexOf(existingObject)].signaturePositions = positions[key].position;
+                } else {
+                    this.tmpArray.push({
+                        key: positions[key].sequence,
+                        page: positions[key].page,
+                        docIndex: resources.indexOf(resources[index]),
+                        signaturePositions: positions[key].position
+                    });
+                }
+            });
+
+            this.tmpArray.forEach((element: any) => {
+                const existingObject: any = (this.visaWorkflow[element.key].signaturePositions as any[]).find((item: any) => item.page === element.page && item.sequence === element.key && item.docIndex === element.docIndex);
+                if (existingObject !== undefined) {
+                    this.updatePositionsWorkfow(existingObject, element);
+                } else {
+                    (this.visaWorkflow[element.key].signaturePositions as any[]).push(
+                        {
+                            docIndex: element.docIndex,
+                            page: element.page,
+                            mainDocument: true,
+                            sequence: element.key,
+                            position: element.signaturePositions
+                        }
+                    );
+                }
             });
         }
     }
 
+    updatePositionsWorkfow(object: any, data: any) {
+        this.visaWorkflow.forEach((element: any) => {
+            element.signaturePositions[(element.signaturePositions as any[]).indexOf(object)].position = data.signaturePositions;
+        });
+    }
+
+
     clearPositionsFromResource(resource: any) {
         this.visaWorkflow.forEach((user: any) => {
             user.signaturePositions = user.signaturePositions.filter((pos: any) => pos.mainDocument !== resource.mainDocument);
diff --git a/src/frontend/app/indexation/indexation.component.ts b/src/frontend/app/indexation/indexation.component.ts
index 3273ee61fd0d942b82f4a7d2aa6d732b159e9001..83fb369cdbdb3b5102a2e5218c945a00f5902c52 100644
--- a/src/frontend/app/indexation/indexation.component.ts
+++ b/src/frontend/app/indexation/indexation.component.ts
@@ -188,7 +188,7 @@ export class IndexationComponent implements OnInit {
                             load.present();
                             const objTosend = this.formatData(data.note);
                             for (let index = 0; index < objTosend.length; index++) {
-                                await this.saveDocument(objTosend[index], index);
+                                await this.saveDocument(objTosend, index);
                                 if (objTosend[0].workflow[0].userId === this.authService.user.id) {
                                     this.actionsService.setEvent('refreshList');
                                 }
@@ -207,14 +207,19 @@ export class IndexationComponent implements OnInit {
     }
 
     saveDocument(data: any, index: number) {
+        data.forEach((element: any) => {
+            (element.workflow as any[]).forEach((item: any) => {
+                item.signaturePositions = (item.signaturePositions as any[]).filter((el: any) => el.docIndex === data.indexOf(element));
+            });
+        });
         return new Promise((resolve) => {
-            this.http.post('../rest/documents', data).pipe(
+            this.http.post('../rest/documents', data[index]).pipe(
                 tap(() => {
                     this.errors = [];
                 }),
                 finalize(() => resolve(true)),
                 catchError((err: any) => {
-                    this.errors.push(data.title);
+                    this.errors.push(data[index].title);
                     this.notificationService.handleErrors(err);
                     return of(false);
                 })
@@ -273,6 +278,7 @@ export class IndexationComponent implements OnInit {
                     signatureMode: this.authService.getSignatureMode(item.role),
                     signaturePositions: item.signaturePositions !== undefined ? this.formatPositions(item.signaturePositions.filter((pos: any) => pos.mainDocument === file.mainDocument && file.signPos !== undefined)).map((itemFile: any) => ({
                         page: itemFile.page,
+                        docIndex: itemFile.docIndex,
                         positionX: itemFile.position.positionX,
                         positionY: itemFile.position.positionY,
                     })) : []
@@ -360,6 +366,11 @@ export class IndexationComponent implements OnInit {
 
     deleteFile(index: number) {
         this.filesToUpload.splice(index, 1);
+        if (this.filesToUpload.length === 0) {
+            this.appVisaWorkflow.getCurrentWorkflow().forEach((element: any) => {
+                element.signaturePositions = [];
+            });
+        }
     }
 
     async signPos(index: number) {
@@ -378,16 +389,17 @@ export class IndexationComponent implements OnInit {
                 component: SignaturePositionComponent,
                 cssClass: 'custom-alert-fullscreen',
                 componentProps: {
-                    'workflow': this.appVisaWorkflow.getCurrentWorkflow(),
-                    'resource': this.filesToUpload[index],
-                    'pdfContent': 'data:application/pdf;base64,' + this.filesToUpload[index].content,
+                    workflow: this.appVisaWorkflow.getCurrentWorkflow(),
+                    resource: this.filesToUpload[index],
+                    pdfContent: 'data:application/pdf;base64,' + this.filesToUpload[index].content,
+                    docIndex: this.filesToUpload.indexOf(this.filesToUpload[index])
                 }
             });
             await modal.present();
             const { data } = await modal.onWillDismiss();
             if (data !== undefined) {
                 this.filesToUpload[index].signPos = data;
-                this.appVisaWorkflow.setPositionsWorkfow(this.filesToUpload[index], data);
+                this.appVisaWorkflow.setPositionsWorkfow(this.filesToUpload, index, data);
             }
         } else {
             this.notificationService.error('lang.mustSetWorkflowBeforeSignPositions');
diff --git a/src/frontend/app/indexation/signature-position/signature-position.component.ts b/src/frontend/app/indexation/signature-position/signature-position.component.ts
index 8e3bf242a28cf19c48f8840c24764c0326fefbeb..904e94434351c2dcb9c08421f17d0e9f6fc53b7c 100644
--- a/src/frontend/app/indexation/signature-position/signature-position.component.ts
+++ b/src/frontend/app/indexation/signature-position/signature-position.component.ts
@@ -14,6 +14,7 @@ export class SignaturePositionComponent implements OnInit {
     @Input() workflow: any[] = [];
     @Input() resource: any = [];
     @Input() pdfContent: any = null;
+    @Input() docIndex: number = null;
 
     loading: boolean = false;
     dragging: boolean = false;
@@ -61,9 +62,10 @@ export class SignaturePositionComponent implements OnInit {
     initSignPos() {
         this.workflow.forEach((user: any, index: number) => {
             if (user.signaturePositions?.length > 0) {
-                this.signList = this.signList.concat(user.signaturePositions.filter((pos: any) => pos.mainDocument === this.resource.mainDocument).map((pos: any) => ({
+                this.signList = this.signList.concat(user.signaturePositions.filter((pos: any) => pos.mainDocument === this.resource.mainDocument && pos.docIndex === this.docIndex).map((pos: any) => ({
                     ...pos,
-                    sequence : index
+                    sequence : index,
+                    docIndex: this.docIndex,
                 })));
             }
         });
@@ -103,8 +105,14 @@ export class SignaturePositionComponent implements OnInit {
     }
 
     moveSign(event: any, i: number) {
-        const percentx = (event.x * 100) / this.workingAreaWidth;
-        const percenty = (event.y * 100) / this.workingAreaHeight;
+        let percentx = (event.x * 100) / this.workingAreaWidth;
+        let percenty = (event.y * 100) / this.workingAreaHeight;
+
+        percentx = percentx < 0 ? 0 : percentx;
+        percentx = percentx > 100 ? 100 : percentx;
+        percenty = percenty < 0 ? 0 : percenty;
+        percenty = percenty > 100 ? 100 : percenty;
+
         this.signList.filter((item: any) => item.sequence === this.currentUser && item.page === this.currentPage)[0].position.positionX = percentx;
         this.signList.filter((item: any) => item.sequence === this.currentUser && item.page === this.currentPage)[0].position.positionY = percenty;
         this.dragging = false;
@@ -119,6 +127,7 @@ export class SignaturePositionComponent implements OnInit {
             {
                 sequence: this.currentUser,
                 page: this.currentPage,
+                docIndex: this.docIndex,
                 position: {
                     positionX: 75,
                     positionY: 90
diff --git a/src/frontend/app/service/notification.service.ts b/src/frontend/app/service/notification.service.ts
index 54d66710bb057c2f5635204091916504d5ca81c2..a1b0fa175b12839a0d983d9ac111cb0dc5256148 100644
--- a/src/frontend/app/service/notification.service.ts
+++ b/src/frontend/app/service/notification.service.ts
@@ -23,8 +23,22 @@ export class NotificationService {
         toast.present();
     }
 
-    async error(message: string) {
+    async message(message: string) {
         const msg = message.includes('lang.') ? this.translate.instant(message) : message;
+        const toast = await this.toastController.create({
+            cssClass: 'notif-primary',
+            duration: 3000,
+            message: msg,
+            position: 'top'
+        });
+        toast.present();
+    }
+
+    async error(message: any) {
+        if (typeof message === 'object') {
+            message = message.toString();
+        }
+        const msg = message.indexOf('lang.') > -1 ? this.translate.instant(message) : message;
         const toast = await this.toastController.create({
             cssClass: 'notif-error',
             duration: 3000,
diff --git a/src/frontend/app/service/signature-method/signature-method-modal.component.html b/src/frontend/app/service/signature-method/signature-method-modal.component.html
index 5aaa59004357c7a9f66020a379cb0e756ad666bf..52c27d6b6f9d8f80a79b164a9d4b3f6e17baefa7 100644
--- a/src/frontend/app/service/signature-method/signature-method-modal.component.html
+++ b/src/frontend/app/service/signature-method/signature-method-modal.component.html
@@ -1,4 +1,4 @@
 <ion-content>
-    <peculiar-fortify-certificates style="height: 100%;width: 100%;" language="fr" [filters]="filters"
-        (continue)="certificateChosen($event)" (cancel)="cancelSign()" hide-footer></peculiar-fortify-certificates>
+    <peculiar-fortify-certificates *ngIf="!signaturesService.mustRefreshCerts" style="height: 100%;width: 100%;" language="fr" [filters]="filters"
+        (selectionSuccess)="certificateChosen($event)" (selectionCancel)="cancelSign()" hide-footer></peculiar-fortify-certificates>
 </ion-content>
diff --git a/src/frontend/app/service/signature-method/signature-method-modal.component.ts b/src/frontend/app/service/signature-method/signature-method-modal.component.ts
index 1f3f2a70679fdaed3e5fd2bc65d59e694c27c643..3b9c9fd57e82ea840c67b499132dbcf8d33c1f5a 100644
--- a/src/frontend/app/service/signature-method/signature-method-modal.component.ts
+++ b/src/frontend/app/service/signature-method/signature-method-modal.component.ts
@@ -1,4 +1,4 @@
-import { Component, Input, OnInit } from '@angular/core';
+import { Component, Input, OnInit, OnDestroy } from '@angular/core';
 import { LoadingController, ModalController } from '@ionic/angular';
 import { catchError, tap } from 'rxjs/operators';
 import { of } from 'rxjs';
@@ -9,13 +9,15 @@ import { ActionsService } from '../actions.service';
 import { SignaturesContentService } from '../signatures.service';
 import { AuthService } from '../auth.service';
 import { FunctionsService } from '../functions.service';
+import { FortifyAPI } from '@peculiar/fortify-client-core';
+
 
 @Component({
     selector: 'signature-method-modal',
     templateUrl: 'signature-method-modal.component.html',
     styleUrls: ['./signature-method-modal.component.scss']
 })
-export class SignatureMethodModalComponent implements OnInit {
+export class SignatureMethodModalComponent implements OnInit, OnDestroy {
 
     @Input() note: string;
     @Input() signatureMode: string;
@@ -39,7 +41,7 @@ export class SignatureMethodModalComponent implements OnInit {
     certificate: any;
     signatureLength: any = null;
 
-    server: any = null;
+    api: FortifyAPI = null;
 
     constructor(
         public modalController: ModalController,
@@ -64,6 +66,68 @@ export class SignatureMethodModalComponent implements OnInit {
         if (!this.functionsService.empty(signatureModeData.keyUsage)) {
             this.filters.keyUsage.push(signatureModeData.keyUsage);
         }
+
+        await this.processInitFortify();
+    }
+
+    async ngOnDestroy(): Promise<void> {
+        if (!this.functionsService.empty(this.api)) {
+            this.api.finish();
+            this.api = null;
+        }
+    }
+
+    async initFortify() {
+        this.api = new FortifyAPI(null);
+
+        await this.waitFortifyStart();
+
+        const challenge = await this.api.challenge();
+
+        if (challenge) {
+            await this.api.login();
+        }
+
+        if (this.signaturesService.mustRefreshCerts) {
+            await this.updateProvider(null);
+        }
+        this.api.server.cardReader
+            .on('insert', (event: any) => this.updateProvider(event))
+            .on('remove', (event: any) => this.updateProvider(event));
+    }
+
+    async processInitFortify() {
+        if (this.signaturesService.mustRefreshCerts) {
+            this.loadingController.create({
+                message: this.translate.instant('lang.waiting'),
+                spinner: 'dots'
+            }).then(async (load: HTMLIonLoadingElement) => {
+                load.present();
+                await this.initFortify();
+                load.dismiss();
+            });
+        } else {
+            await this.initFortify();
+        }
+    }
+
+    async waitFortifyStart() {
+        let fortifyState: boolean = false;
+        while (!fortifyState) {
+            fortifyState = await this.fortifyStart();
+            await new Promise(resolve => setTimeout(resolve, 1000));
+        }
+    }
+
+    fortifyStart(): Promise<boolean> {
+        return new Promise(async (resolve) => {
+            try {
+                await this.api.start();
+                resolve(true);
+            } catch (error) {
+                resolve(false);
+            }
+        });
     }
 
     async certificateChosen(certData: any) {
@@ -74,9 +138,8 @@ export class SignatureMethodModalComponent implements OnInit {
             load.present();
 
             try {
-                this.server = certData.detail.server;
                 this.checkWebsocketSession();
-                this.provider = await certData.detail.server.getCrypto(certData.detail.providerId);
+                this.provider = await this.api.server.getCrypto(certData.detail.providerId);
                 this.checkWebsocketSession();
                 this.cert = await this.provider.certStorage.getItem(certData.detail.certificateId);
                 this.checkWebsocketSession();
@@ -101,18 +164,40 @@ export class SignatureMethodModalComponent implements OnInit {
                 result = await this.sendAndSign(this.idsToProcess[index]);
             }
             load.dismiss();
+            setTimeout(() => {
+                this.signaturesService.mustRefreshCerts = true;
+            }, 500);
             this.modalController.dismiss(result);
         });
     }
 
     async checkWebsocketSession() {
         // session closed?!
-        while (this.server.client.state !== WebSocket.OPEN) {
-            await this.server.connect();
+        while (this.api.server.state !== WebSocket.OPEN) {
+            await this.api.server.connect();
             await new Promise(resolve => setTimeout(resolve, 150));
         }
     }
 
+    async updateProvider(event: any) {
+        const info = await this.api.server.info();
+        for (const provider of info.providers) {
+            const crypto = await this.api.server.getCrypto(provider.id);
+            await crypto.reset();
+        }
+
+        if (!this.functionsService.empty(event?.action) && event?.action.includes('insert')) {
+            this.notificationService.message('lang.inputCardReaderUpdateCerts');
+            this.modalController.dismiss('refresh_certs');
+        } else if (!this.functionsService.empty(event?.action) && event?.action.includes('remove')) {
+            this.notificationService.message('lang.outputCardReaderUpdateCerts');
+            this.modalController.dismiss('refresh_certs');
+        } else if (this.signaturesService.mustRefreshCerts && event === null) {
+            this.modalController.dismiss('refresh_certs');
+            this.signaturesService.mustRefreshCerts = false;
+        }
+    }
+
     async sendAndSign(idToProcess: number) {
         let allSignaturesComplete: boolean = false;
         let res: any = {};
@@ -199,6 +284,7 @@ export class SignatureMethodModalComponent implements OnInit {
     }
 
     cancelSign() {
+        this.signaturesService.mustRefreshCerts = true;
         this.modalController.dismiss(false);
     }
 
diff --git a/src/frontend/app/service/signatures.service.ts b/src/frontend/app/service/signatures.service.ts
index 15568b71092953dfcbcb731bf92b944e7bc4d4ff..1272d4aac4a4e0e34d7dad3a855c7d933c96cb9b 100755
--- a/src/frontend/app/service/signatures.service.ts
+++ b/src/frontend/app/service/signatures.service.ts
@@ -36,6 +36,8 @@ export class SignaturesContentService {
 
     appSession: any;
 
+    mustRefreshCerts: boolean = true;
+
     private portalHost: DomPortalOutlet;
 
     constructor(
diff --git a/src/frontend/core/global.scss b/src/frontend/core/global.scss
index 3ffcb298dc4e756be6f54e34ce70db2dd69ee0b1..cac6dfc4ce9183658407b2caf57772a1ca3886d4 100644
--- a/src/frontend/core/global.scss
+++ b/src/frontend/core/global.scss
@@ -75,6 +75,17 @@ input:read-only {
     font-weight: bold;
 }
 
+.notif-primary {
+    z-index: 60003;
+    --background: white;
+    --color: var(--ion-color-primary);
+    --border-color: var(--ion-color-primary);
+    --border-width: 1px;
+    --border-style: solid;
+    text-align: center;
+    font-weight: bold;
+}
+
 .fullscreen {
     --width: 100%;
     --height: 100%;
diff --git a/src/frontend/core/index.html b/src/frontend/core/index.html
index d3456fed9f0f30cb429e55fe634d1446459ea5f1..bfbb5d58357c3aaeac8a8135321febe33b673466 100644
--- a/src/frontend/core/index.html
+++ b/src/frontend/core/index.html
@@ -1,21 +1,7 @@
 <!doctype html>
 <html lang="fr">
 <head>
-    <base id="baseHref">
-    <script type="text/javascript">
-        let fullPath = window.location.pathname;
-        let before = fullPath.split('/')[0];
-        let splitedUrl = fullPath.split('/')[1];
-        let after = fullPath.split('/')[2];
-
-        if (typeof splitedUrl === 'undefined' || splitedUrl === '' || splitedUrl === 'dist') {
-            document.getElementById('baseHref').href = '/dist/';
-        } else if (before == '' && (after == 'dist' || after == '')) {
-            document.getElementById('baseHref').href = '/' + splitedUrl + '/dist/';
-        } else {
-            document.getElementById('baseHref').href = '/' + splitedUrl;
-        }
-    </script>
+    <base href=".">
     <meta charset="utf-8">
     <title>Maarch Parapheur</title>
     <meta name="viewport"