diff --git a/client/src/app/auth/auth.actions.ts b/client/src/app/auth/auth.actions.ts
index 4103bcebd10660ab2d67a814181a2300af67872a..e661950bc51ec035d596e7ffa4c36e4b32a96d04 100644
--- a/client/src/app/auth/auth.actions.ts
+++ b/client/src/app/auth/auth.actions.ts
@@ -14,6 +14,7 @@ import { UserProfile } from './user-profile.model';
 export const login = createAction('[Auth] Login');
 export const logout = createAction('[Auth] Logout');
 export const authSuccess = createAction('[Auth] Auth Success');
+export const authRefreshSuccess = createAction('[Auth] Auth Refresh Success');
 export const loadUserProfileSuccess = createAction('[Auth] Load User Profile Success', props<{ userProfile: UserProfile }>());
 export const loadUserRoleSuccess = createAction('[Auth] Load User Roles Success', props<{ userRoles: string[] }>());
 export const loadTokenSuccess = createAction('[Auth] Load Token Success', props<{ token: string }>());
diff --git a/client/src/app/auth/auth.effects.ts b/client/src/app/auth/auth.effects.ts
index a663b0a069bce19419f982be0a4ce012ac52ff08..d063947da7551c674cc504b300992724de67da7f 100644
--- a/client/src/app/auth/auth.effects.ts
+++ b/client/src/app/auth/auth.effects.ts
@@ -60,6 +60,19 @@ export class AuthEffects {
         )
     );
 
+    authRefreshSuccess$ = createEffect(() =>
+        this.actions$.pipe(
+            ofType(authActions.authRefreshSuccess),
+            switchMap(() => from(this.keycloak.getToken())
+                .pipe(
+                    switchMap(token => [
+                        authActions.loadTokenSuccess({ token })
+                    ])
+                )
+            )
+        )
+    );
+
     openEditProfile$ =  createEffect(() =>
         this.actions$.pipe(
             ofType(authActions.openEditProfile),
diff --git a/client/src/app/auth/init.keycloak.ts b/client/src/app/auth/init.keycloak.ts
index a15e68f1179781fb93e1a25d10e3c9a35e3e8ed8..d488a979c2829b954546d7bdd3ff2e9d974961f1 100644
--- a/client/src/app/auth/init.keycloak.ts
+++ b/client/src/app/auth/init.keycloak.ts
@@ -24,6 +24,9 @@ export function initializeKeycloak(keycloak: KeycloakService, store: Store<{ }>,
         if (event.type === KeycloakEventType.OnAuthSuccess) {
             store.dispatch(keycloakActions.authSuccess());
         }
+        if (event.type === KeycloakEventType.OnAuthRefreshSuccess) {
+            store.dispatch(keycloakActions.authRefreshSuccess());
+        }
         if (event.type === KeycloakEventType.OnAuthRefreshError) {
             store.dispatch(keycloakActions.login());
         }
diff --git a/client/src/app/instance/search/components/result/datatable-tab.component.html b/client/src/app/instance/search/components/result/datatable-tab.component.html
index f854be3243fe7e38b59814ca08d52593e70da7ca..f9f6b3aaa6a57531613c217cf6a1ebf3501bc5be 100644
--- a/client/src/app/instance/search/components/result/datatable-tab.component.html
+++ b/client/src/app/instance/search/components/result/datatable-tab.component.html
@@ -19,7 +19,6 @@
                 [dataIsLoading]="dataIsLoading"
                 [dataIsLoaded]="dataIsLoaded"
                 [selectedData]="selectedData"
-                [token]="token"
                 (retrieveData)="retrieveData.emit($event)"
                 (addSelectedData)="addSelectedData.emit($event)"
                 (deleteSelectedData)="deleteSelectedData.emit($event)">
diff --git a/client/src/app/instance/search/components/result/datatable-tab.component.ts b/client/src/app/instance/search/components/result/datatable-tab.component.ts
index 51d18381cc91d321ea591568700f04fafcd0f0e9..a276dec48ebe3974e8494c21d4cda2602d64794b 100644
--- a/client/src/app/instance/search/components/result/datatable-tab.component.ts
+++ b/client/src/app/instance/search/components/result/datatable-tab.component.ts
@@ -33,7 +33,6 @@ export class DatatableTabComponent {
     @Input() dataIsLoading: boolean;
     @Input() dataIsLoaded: boolean;
     @Input() selectedData: any[];
-    @Input() token: string;
     @Output() retrieveData: EventEmitter<Pagination> = new EventEmitter();
     @Output() addSelectedData: EventEmitter<number | string> = new EventEmitter();
     @Output() deleteSelectedData: EventEmitter<number | string> = new EventEmitter();
diff --git a/client/src/app/instance/search/components/result/download.component.html b/client/src/app/instance/search/components/result/download.component.html
index 4f9d94496d025ae285d6bd9a7c30ad0e1db42fe8..bca9d41fafe78bb124dfef68a6541f3d811666f4 100644
--- a/client/src/app/instance/search/components/result/download.component.html
+++ b/client/src/app/instance/search/components/result/download.component.html
@@ -18,15 +18,15 @@
                     <p>Download results just here:</p>
                 </div>
                 <div class="col">
-                    <a *ngIf="getConfigDownloadResultFormat('download_csv')" [href]="getUrl('csv')" class="btn btn-outline-primary" title="Download results in CSV format">
+                    <a *ngIf="getConfigDownloadResultFormat('download_csv')" (click)="click($event, getUrl('csv'), 'csv')" class="btn btn-outline-primary" title="Download results in CSV format">
                         <i class="fas fa-file-csv"></i> CSV
                     </a>
                     &nbsp;
-                    <a *ngIf="getConfigDownloadResultFormat('download_ascii')" [href]="getUrl('ascii')" target="_blank" class="btn btn-outline-primary" title="Download results in ASCII format">
+                    <a *ngIf="getConfigDownloadResultFormat('download_ascii')" (click)="click($event, getUrl('ascii'), 'txt')" class="btn btn-outline-primary" title="Download results in ASCII format">
                         <i class="fas fa-file"></i> ASCII
                     </a>
                     &nbsp;
-                    <a *ngIf="getConfigDownloadResultFormat('download_vo')" [href]="getUrl('votable')" target="_blank" class="btn btn-outline-primary" title="Download results in VO format">
+                    <a *ngIf="getConfigDownloadResultFormat('download_vo')" (click)="click($event, getUrl('votable'), 'xml')" class="btn btn-outline-primary" title="Download results in VO format">
                         <i class="fas fa-file"></i> VOtable
                     </a>
                     &nbsp;
@@ -41,7 +41,7 @@
                     <p>Download archive files just here:</p>
                 </div>
                 <div class="col">
-                    <a [href]="getUrlArchive()" class="btn btn-outline-primary" title="Download an archive with all files">
+                    <a (click)="click($event, getUrlArchive(), 'zip')" class="btn btn-outline-primary" title="Download an archive with all files">
                         <i class="fas fa-archive"></i> Files archive
                     </a>
                 </div>
diff --git a/client/src/app/instance/search/components/result/download.component.ts b/client/src/app/instance/search/components/result/download.component.ts
index 10607d8459b2b922a870567a800cd031fd3d4578..b0498b150edd0e4fb3b223875692fd6063aac658 100644
--- a/client/src/app/instance/search/components/result/download.component.ts
+++ b/client/src/app/instance/search/components/result/download.component.ts
@@ -8,6 +8,7 @@
  */
 
 import { Component, EventEmitter, Input, Output } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
 
 import { Criterion, ConeSearch, criterionToString } from '../../../store/models';
 import { Dataset } from 'src/app/metamodel/models';
@@ -34,7 +35,7 @@ export class DownloadComponent {
     @Input() sampRegistered: boolean;
     @Output() broadcast: EventEmitter<string> = new EventEmitter();
 
-    constructor(private appConfig: AppConfigService) { }
+    constructor(private appConfig: AppConfigService, private http: HttpClient) { }
 
     isDownloadActivated(): boolean {
         const dataset = this.datasetList.find(dataset => dataset.name === this.datasetSelected);
@@ -91,4 +92,17 @@ export class DownloadComponent {
     broadcastVotable(): void {
         this.broadcast.emit(this.getUrl('votable'));
     }
+
+    click(event, href, extension) {
+        event.preventDefault();
+
+        this.http.get(href, {responseType: "blob"}).subscribe(
+            data => {
+                let downloadLink = document.createElement('a');
+                downloadLink.href = window.URL.createObjectURL(data);
+                downloadLink.setAttribute('download', `${this.datasetSelected}.${extension}`);
+                downloadLink.click();
+            }
+        );
+    }
 }
diff --git a/client/src/app/instance/search/containers/result.component.html b/client/src/app/instance/search/containers/result.component.html
index e57e2aa6ea83613a6fb4737817d6f3ae629f705e..052cf4ed64375604dfdd2b3e57adeb0509ff5205 100644
--- a/client/src/app/instance/search/containers/result.component.html
+++ b/client/src/app/instance/search/containers/result.component.html
@@ -82,7 +82,6 @@
                 [dataIsLoading]="dataIsLoading | async"
                 [dataIsLoaded]="dataIsLoaded | async"
                 [selectedData]="selectedData | async"
-                [token]="token | async"
                 (retrieveData)="retrieveData($event)"
                 (addSelectedData)="addSearchData($event)"
                 (deleteSelectedData)="deleteSearchData($event)">
diff --git a/client/src/app/instance/search/containers/result.component.ts b/client/src/app/instance/search/containers/result.component.ts
index 16a1278464bba34e97cd60d30acc9d3457784daf..9254082a3f03e5b3da3a26c1bd75a1062d1af818 100644
--- a/client/src/app/instance/search/containers/result.component.ts
+++ b/client/src/app/instance/search/containers/result.component.ts
@@ -43,7 +43,6 @@ export class ResultComponent extends AbstractSearchComponent {
     public dataIsLoaded: Observable<boolean>;
     public selectedData: Observable<any>;
     public sampRegistered: Observable<boolean>;
-    public token: Observable<string>;
 
     private pristineSubscription: Subscription;
 
@@ -58,7 +57,6 @@ export class ResultComponent extends AbstractSearchComponent {
         this.dataIsLoaded = this.store.select(searchSelector.selectDataIsLoaded);
         this.selectedData = this.store.select(searchSelector.selectSelectedData);
         this.sampRegistered = this.store.select(sampSelector.selectRegistered);
-        this.token = this.store.select(authSelector.selectToken);
     }
 
     ngOnInit() {
diff --git a/client/src/app/instance/shared-search/components/datatable/datatable.component.html b/client/src/app/instance/shared-search/components/datatable/datatable.component.html
index c2509821e3052c7aad63f360ee18a7350fbfb1ff..ecb7f5726a7055b7592d8fc099ca53845d04bb19 100644
--- a/client/src/app/instance/shared-search/components/datatable/datatable.component.html
+++ b/client/src/app/instance/shared-search/components/datatable/datatable.component.html
@@ -62,7 +62,6 @@
                                 [value]="datum[attribute.label]"
                                 [datasetName]="dataset.name"
                                 [datasetPublic]="dataset.public"
-                                [token]="token"
                                 [config]="getRendererConfig(attribute)">
                             </app-download-renderer>
                         </div>
diff --git a/client/src/app/instance/shared-search/components/datatable/datatable.component.ts b/client/src/app/instance/shared-search/components/datatable/datatable.component.ts
index 91b185a51efc3a7407999a5d22a06bd6d6a884a5..08038733980a47f874e9665701321a37fb373204 100644
--- a/client/src/app/instance/shared-search/components/datatable/datatable.component.ts
+++ b/client/src/app/instance/shared-search/components/datatable/datatable.component.ts
@@ -34,7 +34,6 @@ export class DatatableComponent implements OnInit {
     @Input() dataIsLoading: boolean;
     @Input() dataIsLoaded: boolean;
     @Input() selectedData: any[] = [];
-    @Input() token: string;
     @Output() retrieveData: EventEmitter<Pagination> = new EventEmitter();
     @Output() addSelectedData: EventEmitter<number | string> = new EventEmitter();
     @Output() deleteSelectedData: EventEmitter<number | string> = new EventEmitter();
diff --git a/client/src/app/instance/shared-search/components/datatable/renderer/download-renderer.component.html b/client/src/app/instance/shared-search/components/datatable/renderer/download-renderer.component.html
index aef5f0ea9282018d805f76a7cd788e1921333b61..da86483575a96e40ec8973a9697a489c60ca7ae6 100644
--- a/client/src/app/instance/shared-search/components/datatable/renderer/download-renderer.component.html
+++ b/client/src/app/instance/shared-search/components/datatable/renderer/download-renderer.component.html
@@ -1,4 +1,4 @@
-<a [href]="getHref()" [ngClass]="{'btn btn-outline-primary btn-sm': (config.display=='text-button' || config.display=='icon-button' || config.display=='icon-text-btn')}">
+<a (click)="click($event)" [ngClass]="{'btn btn-outline-primary btn-sm': (config.display=='text-button' || config.display=='icon-button' || config.display=='icon-text-btn')}">
     <span *ngIf="config.display === 'icon-button' || config.display === 'icon-text-btn'" class="{{config.icon}}"></span>
     <span *ngIf="config.display === 'icon-text-btn'">&nbsp;</span>
     <span *ngIf="config.display !== 'icon-button'">{{ getText() }}</span>
diff --git a/client/src/app/instance/shared-search/components/datatable/renderer/download-renderer.component.ts b/client/src/app/instance/shared-search/components/datatable/renderer/download-renderer.component.ts
index 40b49a0b16daa0f7c6014b8a854d6117d0f6dca4..56475b8e0d2615e254096a12f551caaa5338b4b9 100644
--- a/client/src/app/instance/shared-search/components/datatable/renderer/download-renderer.component.ts
+++ b/client/src/app/instance/shared-search/components/datatable/renderer/download-renderer.component.ts
@@ -8,6 +8,7 @@
  */
 
 import { Component, Input, ChangeDetectionStrategy } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
 
 import { DownloadRendererConfig } from 'src/app/metamodel/models/renderers/download-renderer-config.model';
 import { getHost } from 'src/app/shared/utils';
@@ -26,10 +27,9 @@ export class DownloadRendererComponent {
     @Input() value: string;
     @Input() datasetName: string;
     @Input() datasetPublic: boolean;
-    @Input() token: string;
     @Input() config: DownloadRendererConfig;
 
-    constructor(private appConfig: AppConfigService) { }
+    constructor(private appConfig: AppConfigService, private http: HttpClient) { }
 
     /**
      * Returns link href.
@@ -37,11 +37,7 @@ export class DownloadRendererComponent {
      * @return string
      */
     getHref(): string {
-        let href = getHost(this.appConfig.apiUrl) + '/download-file/' + this.datasetName + '/' + this.value;
-        if (!this.datasetPublic && this.token) {
-            href += '?token=' + this.token;
-        }
-        return href;
+        return getHost(this.appConfig.apiUrl) + '/download-file/' + this.datasetName + '/' + this.value;
     }
 
     /**
@@ -52,4 +48,20 @@ export class DownloadRendererComponent {
     getText(): string {
         return this.config.text.replace('$value', this.value.toString());
     }
+
+    click(event) {
+        event.preventDefault();
+
+        const href = this.getHref();
+        this.http.get(href, {responseType: "blob"}).subscribe(
+            data => {
+                const filename = href.substring(href.lastIndexOf('/') + 1);
+
+                let downloadLink = document.createElement('a');
+                downloadLink.href = window.URL.createObjectURL(data);
+                downloadLink.setAttribute('download', filename);
+                downloadLink.click();
+            }
+        );
+    }
 }
diff --git a/conf-dev/public_key b/conf-dev/public_key
index 6d9fd24a85f3dbb188e0bbc86e620415f641b46c..3c3ba0e8be160eac8c137f66f4bfb969137c8c04 100644
--- a/conf-dev/public_key
+++ b/conf-dev/public_key
@@ -1,3 +1,3 @@
 -----BEGIN PUBLIC KEY-----
-MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiXxjO2Z+Mnc/L4fcY7oLIvSQXrkADSUQVnAXqfcMzfuNH/clLBMmXIHoPoIOYImIbXFMNaBAFLMWqURSaz2LDEQy5dIfllU9e3kIAV0mJeafjaN3QdxKX8TcJAnrTeQ7soooeZzen7kfPWxfjAaiEtbxo5h2xW4qup+VvADQg15C3dwBS5VV2Lc41z4prQubQNs2WT4IOVfYYhtpp+R/3IuCAFS6qfmAuBUIu8gSq/VpCqJ5Sm/YdmBZDwhyBRYOKJJlkqlNFdGPnHAkBDOL5LeU54cXCBMxPEeomtu22Kv1PIF4w2kfwUie9Qq06yWsTDwyrSAjvQ3xXHAcuAqPRQIDAQAB
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1fucBQK34tl8Gx/fefATnpWqW5PVlwMYcJAqgWvmtwNm9ZW/S5HNZZfjW1S9BOJLfudCM83WHrAwGixgHKI310YXg+6BI9qn2Gnge1GC3JtKZx6UdcxZFAYmlhY0QGL5QxGR58DkEj6l/FDrwAHyVkC5sLqDMiYsqO7CA1uJLtF8yUrCyHvI4TR+kk5ZSM94/osg6eGxGSYA89u+qhG5tz5YCFgiRUNxuAEucsz8XiEfNVAz5kdYgsR4+LtmqECfczpwcJrAu7yDxs3rQPjBqFdGqEehALHX9vq71VEYe5Id2P7ddik3byHa0a21Q0RuVhMYwGrLiMLJCXqxE1YMuwIDAQAB
 -----END PUBLIC KEY-----
diff --git a/docker-compose.yml b/docker-compose.yml
index ee412b76725a54d72cfad75ec8636d000a5842f9..482db6739fd0955000d4d0dc26d19b1500fbcb39 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -34,7 +34,7 @@ services:
             SSO_AUTH_URL: "http://localhost:8180/auth"
             SSO_REALM: "anis"
             SSO_CLIENT_ID: "anis-client"
-            TOKEN_ENABLED: 0
+            TOKEN_ENABLED: 1
             TOKEN_PUBLIC_KEY_FILE: /mnt/public_key
             TOKEN_ADMIN_ROLE: anis_admin
         ports: