From e971a6e39c9c10c4cfa127529a89b284a22403c2 Mon Sep 17 00:00:00 2001 From: Alexander O'Neill Date: Fri, 15 Sep 2023 15:11:18 -0300 Subject: [PATCH] Issue #31: Port @kylehuynh205 work to pass auth token to Cantaloupe. --- islandora_mirador.module | 5 ++ islandora_mirador.routing.yml | 6 +++ js/mirador_viewer.js | 49 +++++++++++++++++- js/service_worker.js | 21 ++++++++ src/Controller/ServiceWorkerController.php | 58 ++++++++++++++++++++++ 5 files changed, 138 insertions(+), 1 deletion(-) create mode 100755 js/service_worker.js create mode 100644 src/Controller/ServiceWorkerController.php diff --git a/islandora_mirador.module b/islandora_mirador.module index 298c87a..4b9f88b 100644 --- a/islandora_mirador.module +++ b/islandora_mirador.module @@ -32,6 +32,11 @@ function islandora_mirador_theme() { function template_preprocess_mirador(&$variables) { $variables['mirador_view_id'] = Html::getUniqueId($variables['mirador_view_id']); + if (!empty(\Drupal::hasService('jwt.authentication.jwt'))) { + $variables['#attached']['drupalSettings']['token'] = \Drupal::service('jwt.authentication.jwt')->generateToken(); + } + + /** * @var \Drupal\islandora_mirador\IslandoraMiradorPluginManager */ diff --git a/islandora_mirador.routing.yml b/islandora_mirador.routing.yml index 9d27632..e07167d 100644 --- a/islandora_mirador.routing.yml +++ b/islandora_mirador.routing.yml @@ -5,3 +5,9 @@ islandora_mirador.miradorconfig: _title: 'Mirador Settings' requirements: _permission: 'administer site configuration' +islandora_mirador.service_worker: + path: '/islandora_mirador_service_worker' + defaults: + _controller: '\Drupal\islandora_mirador\Controller\ServiceWorkerController::serve' + requirements: + _permission: 'access content' diff --git a/js/mirador_viewer.js b/js/mirador_viewer.js index 2d30fc4..6219797 100644 --- a/js/mirador_viewer.js +++ b/js/mirador_viewer.js @@ -14,10 +14,57 @@ attach: function (context, settings) { Object.entries(settings.mirador.viewers).forEach(entry => { const [base, values] = entry; - once('mirador-viewer', base, context).forEach(() => + once('mirador-viewer', base, context, settings).forEach(() => { + if (settings.token !== undefined) { + values["resourceHeaders"] = { + 'Authorization': 'Bearer '+ settings.token, + 'token': settings.token + }; + values["requestPipeline"] = [ + (url, options) => ({ ...options, headers: { + "Accept": 'application/ld+json;profile="http://iiif.io/api/presentation/3/context.json"', + 'Authorization': 'Bearer '+ settings.token, + 'token': settings.token + }}) + ]; + values["osdConfig"] = { + "loadTilesWithAjax": true, + "ajaxHeaders": { + 'Authorization': 'Bearer '+ settings.token, + 'token': settings.token + } + }; + values["requests"] = { + preprocessors: [ // Functions that receive HTTP requests and manipulate them (e.g. to add headers) + // rewrite all info.json requests to add the text/json request header + (url, options) => (url.match('info.json') && { ...options, headers: { + 'Authorization': 'Bearer '+ settings.token, + 'token': settings.token + }}) + ], + }; + + } Mirador.viewer(values, window.miradorPlugins || {}) + } ); }); + if (settings.token !== undefined) { + if ('serviceWorker' in navigator) { + // The Mirador viewer uses img tags for thumbnails so thumbnail image requests + // do not have authorization or token headers. Attach them using a service worker. + window.addEventListener('load', () => { + navigator.serviceWorker + .register('/islandora_mirador_service_worker?token=' + settings.token, { scope: '/' }) + .then(registration => { + console.log('ServiceWorker registration successful with scope: ', registration.scope); + }) + .catch(err => { + console.log('ServiceWorker registration failed: ', err); + }); + }); + } + } }, detach: function (context, settings) { Object.entries(settings.mirador.viewers).forEach(entry => { diff --git a/js/service_worker.js b/js/service_worker.js new file mode 100755 index 0000000..3299cb0 --- /dev/null +++ b/js/service_worker.js @@ -0,0 +1,21 @@ +self.addEventListener('activate', function (event) { + console.log('Service Worker: claiming control...'); + return self.clients.claim(); +}); + +self.addEventListener('fetch', function (event) { + if (event.request.destination === "image" && new URL(event.request.url).pathname.startsWith('/cantaloupe/iiif/') && new URL(location).searchParams.has('token')) { + console.log('Service Worker: fetching...'); + var token = new URL(location).searchParams.get('token'); + event.respondWith( + fetch(event.request, { + headers: { + 'Authorization': 'Bearer ' + token, + 'token': token + }, + mode: "cors", + credentials: "include" + }) + ); + } +}); diff --git a/src/Controller/ServiceWorkerController.php b/src/Controller/ServiceWorkerController.php new file mode 100644 index 0000000..fb677b6 --- /dev/null +++ b/src/Controller/ServiceWorkerController.php @@ -0,0 +1,58 @@ +extensionPathResolver = $extension_path_resolver; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('extension.path.resolver') + ); + } + + /** + * Adds headers to the HTTP response. + */ + public function serve(Request $request) { + $file_str = $this->extensionPathResolver->getPath('module', 'islandora_mirador') . '/js/service_worker.js'; + if (file_exists($file_str)) { + $response = new BinaryFileResponse($file_str, 200); + $response->headers->set('Content-Type', 'application/javascript'); + // Allow same origin service worker. + $response->headers->set('Service-Worker-Allowed', '/'); + return $response; + } + throw new NotFoundHttpException(); + } + +}