IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

JavaScript Discussion :

Les services worker et l'authentification http


Sujet :

JavaScript

  1. #1
    Futur Membre du Club Avatar de bvrignaud
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mars 2013
    Messages
    17
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Mars 2013
    Messages : 17
    Points : 9
    Points
    9
    Par défaut Les services worker et l'authentification http
    Bonjour à tous,

    Je développe une application web pour tablette/smartphone de type offline first.

    Coté serveur, c'est du php (CodeIgniter), coté client : html5, jquery mobile, ...

    D'un coté, j'arrive bien à faire fonctionner mon application avec les service worker (offline par exemple).
    De l'autre (quand je n'utilise pas les service worker), je me connecte à mon appli avec l'Authentification HTTP (Digest).

    Mon soucis, c'est que je n'arrive pas à concilier les deux.

    Ce que je voudrais, c'est que si mon appli/site à besoin de se connecter au serveur (mise à jour des données par exemple), il devra être connecter au serveur (via l'Authentification HTTP par exemple), mais s'il est en mode offline, alors, les données proviendrons du cache.

    L'un d'entre vous aurait il déjà réussi à faire fonctionner les 2 ensemble ?

    Si vous avez des idées, je suis preneur, car là je suis à cours d'idée.

    Merci

  2. #2
    Rédacteur/Modérateur

    Avatar de SylvainPV
    Profil pro
    Inscrit en
    Novembre 2012
    Messages
    3 375
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2012
    Messages : 3 375
    Points : 9 944
    Points
    9 944
    Par défaut
    Bonsoir,

    Quelle stratégie utilises-tu pour ton service worker ? Tu es parti d'une source existante ou tu as codé le worker from scratch ?
    Le mieux serait que tu montres le code du worker, sans code on ne fait pas de miracles.
    One Web to rule them all

  3. #3
    Futur Membre du Club Avatar de bvrignaud
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mars 2013
    Messages
    17
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Mars 2013
    Messages : 17
    Points : 9
    Points
    9
    Par défaut
    Je me suis principalement basé sur la très bonne vidéo de jake Archibald https://www.udacity.com/course/offli...cations--ud899 ainsi que la doc MDN.

    mon code :
    index.js
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    if (navigator.serviceWorker) {
        navigator.serviceWorker.register('/mon_app/sw.js', { scope: 'mon_app/' }).then(function (reg) {
            // registration work
            console.info('Registration succeeded. Scope is ' + reg.scope);
            if (!navigator.serviceWorker.controller) {
                return;
            }
     
            if (reg.waiting) {
                console.log('Nouveau serviceWorker en attente.')
                //indexController._updateReady(reg.waiting);
                return;
            }
     
            if (reg.installing) {
                //indexController._trackInstalling(reg.installing);
                return;
            }
     
            reg.addEventListener('updatefound', function () {
                _trackInstalling(reg.installing);
            });
        }).catch(function(error) {
            // registration failed
            console.error('Registration failed with ' + error);
        });
     
        // Listen for the controlling service worker changing
        // and reload the page
        // Ensure refresh is only called once.
        // This works around a bug in "force update on reload".
        var refreshing;
        navigator.serviceWorker.addEventListener('controllerchange', function() {
          if (refreshing) return;
          window.location.reload();
          refreshing = true;
        });
     
    } else {
        console.error("serviceWorker non supporté !");
    }
     
     
    function _trackInstalling(worker) {
        worker.addEventListener('statechange', function() {
            if (worker.state == 'installed') {
              worker.postMessage({action: 'skipWaiting'});
            }
        });
    }
    Mon service worker :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
     
    "use strict";
     
    const CACHE_NAME = 'mon_app-v4';
    const CACHE_DATAS = [
        '/mon_app/',
        '/mon_app/index.php',
        '/mon_app/assets/libs/jquery.mobile/jquery.mobile-1.4.5.min.css',
        '/mon_app/assets/libs/jqm-datebox/jqm-datebox-1.4.5.min.css',
        '/mon_app/assets/libs/jquery-2.1.4.min.js',
        '/mon_app/assets/libs/jquery.mobile/jquery.mobile-1.4.5.min.js',
        '/mon_app/assets/libs/jquery.mobile/images/ajax-loader.gif',
        '/mon_app/assets/libs/jqm-datebox/jqm-datebox-1.4.5.core.min.js',
        '/mon_app/assets/libs/jqm-datebox/jqm-datebox-1.4.5.mode.calbox.min.js',
        '/mon_app/assets/libs/jqm-datebox/jquery.mobile.datebox.i18n.fr.utf8.min.js',
        '/mon_app/assets/css/styles.css',
        '/mon_app/assets/js/index.js',
        '/mon_app/assets/js/utils.js',
        '/mon_app/assets/favicon/android-chrome-144x144.png',
        '/mon_app/assets/favicon/android-chrome-192x192.png',
        '/mon_app/assets/favicon/android-chrome-36x36.png',
        '/mon_app/assets/favicon/android-chrome-48x48.png',
        '/mon_app/assets/favicon/android-chrome-72x72.png',
        '/mon_app/assets/favicon/android-chrome-96x96.png',
        '/mon_app/assets/favicon/apple-touch-icon-114x114.png',
        '/mon_app/assets/favicon/apple-touch-icon-120x120.png',
        '/mon_app/assets/favicon/apple-touch-icon-144x144.png',
        '/mon_app/assets/favicon/apple-touch-icon-152x152.png',
        '/mon_app/assets/favicon/apple-touch-icon-180x180.png',
        '/mon_app/assets/favicon/apple-touch-icon-57x57.png',
        '/mon_app/assets/favicon/apple-touch-icon-60x60.png',
        '/mon_app/assets/favicon/apple-touch-icon-72x72.png',
        '/mon_app/assets/favicon/apple-touch-icon-76x76.png',
        '/mon_app/assets/favicon/apple-touch-icon-precomposed.png',
        '/mon_app/assets/favicon/apple-touch-icon.png',
        '/mon_app/assets/favicon/browserconfig.xml',
        '/mon_app/assets/favicon/favicon-16x16.png',
        '/mon_app/assets/favicon/favicon-32x32.png',
        '/mon_app/assets/favicon/favicon-96x96.png',
        '/mon_app/assets/favicon/favicon.ico',
        '/mon_app/assets/favicon/manifest.json',
        '/mon_app/assets/favicon/mstile-144x144.png',
        '/mon_app/assets/favicon/mstile-150x150.png',
        '/mon_app/assets/favicon/mstile-310x150.png',
        '/mon_app/assets/favicon/mstile-310x310.png',
        '/mon_app/assets/favicon/mstile-70x70.png'
    ];
     
     
    // Installation du ServiceWorker
    this.addEventListener('install', function (event) {
      event.waitUntil(
          caches.open(CACHE_NAME).then(function (cache) {
            return cache.addAll(CACHE_DATAS);
          }).then(function () {
            console.log('Cache ok');
          }).catch(function (error) {
            console.error('Problème durant la mise en cache : ' + error);
          })
      );
    });
     
     
    /**
     * Suppression des anciens caches
     */
    self.addEventListener('activate', event => {
        event.waitUntil(
            caches.keys().then(function(cacheNames) {
              return Promise.all(
                cacheNames.filter(function(cacheName) {
                  return cacheName.startsWith('m.zotband') &&
                         cacheName != CACHE_NAME;
                }).map(function(cacheName) {
                  return caches.delete(cacheName);
                })
              );
            })
        );
    });
     
     
    // Retourne les données via le cache ou directement via le réseau
    self.addEventListener('fetch', function(event) {
        event.respondWith(
            caches.match(event.request).then(function(response) {
              return response || fetch(event.request);
            })
        );
    });
     
     
    self.addEventListener('message', function(event) {
      f (event.data.action == 'skipWaiting') {
        self.skipWaiting();
      }
    });
    Coté serveur, pour chaque demande client->serveur, je teste si la personne est connecté, et dans le cas contraire je lui demande un login et un mot de passe :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
     
    <?php
    defined('BASEPATH') OR exit('No direct script access allowed');
     
    require_once APPPATH . 'bean/Utilisateur.php';
     
    /* 
     */
    class Connect
    {
        private $realm = 'Restricted area';
        private $CI;
     
        public function __construct()
        {
            $this->CI =& get_instance();
        }
     
        public function connexion()
        {
            if ($this->isLoggedIn()) {
                return TRUE;
            }
     
            if (empty($this->CI->input->server('PHP_AUTH_DIGEST'))) {
                header('HTTP/1.1 401 Unauthorized');
                header('WWW-Authenticate: Digest realm="'.$this->realm.
                       '",qop="auth",nonce="'.uniqid().'",opaque="'.md5($this->realm).'"');
                die();
            }
     
            // analyze the PHP_AUTH_DIGEST variable
            if (!($data = $this->http_digest_parse($this->CI->input->server('PHP_AUTH_DIGEST')))) {
                $this->login();
            }
     
            $vUser = $this->CI->user_model->getUser($data['username']);
            if (!isset($vUser->password)) {
                $this->login();
            }
     
            // generate the valid response
            $A1 = md5($data['username'] . ':' . $this->realm . ':' . $vUser->password);
            $A2 = md5($this->CI->input->server('REQUEST_METHOD').':'.$data['uri']);
            $valid_response = md5($A1.':'.$data['nonce'].':'.$data['nc'].':'.$data['cnonce'].':'.$data['qop'].':'.$A2);
            if ($data['response'] != $valid_response) {
                $this->login();
            }
     
            // ok, valid username & password
            $this->CI->session->utilisateur = $vUser;
        }
     
        /**
         * function to parse the http auth header
         * @param type $txt
         * @return type
         */
        private function http_digest_parse($txt)
        {
            // protect against missing data
            $needed_parts = array('nonce'=>1, 'nc'=>1, 'cnonce'=>1, 'qop'=>1, 'username'=>1, 'uri'=>1, 'response'=>1);
            $data = array();
            $keys = implode('|', array_keys($needed_parts));
     
            preg_match_all('@(' . $keys . ')=(?:([\'"])([^\2]+?)\2|([^\s,]+))@', $txt, $matches, PREG_SET_ORDER);
     
            foreach ($matches as $m) {
                $data[$m[1]] = $m[3] ? $m[3] : $m[4];
                unset($needed_parts[$m[1]]);
            }
     
            return $needed_parts ? false : $data;
        }
     
        private function login()
        {
            header('HTTP/1.1 401 Unauthorized');
            header('WWW-Authenticate: Digest realm="'.$this->realm.
                   '",qop="auth",nonce="'.uniqid().'",opaque="'.md5($this->realm).'"');
            die();
        }
     
        /**
         * Est ce que l'utilisateur est loggué ?
         * @return boolean - true, si l'utilisateur est logué 
         */
        private function isLoggedIn()
        {
            if ($this->CI->session->userdata('utilisateur')) {
                return true;
            } else {
                return false;
            }
        }
    }
    Dans l'idéal, je voudrais que tout puisse fonctionner en mode déconnecter, mais que s'il à besoin du réseau (maj des données), il demande un login et un mot de passe.

    Merci

  4. #4
    Rédacteur/Modérateur

    Avatar de SylvainPV
    Profil pro
    Inscrit en
    Novembre 2012
    Messages
    3 375
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2012
    Messages : 3 375
    Points : 9 944
    Points
    9 944
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    self.addEventListener('fetch', function(event) {    event.respondWith(
            caches.match(event.request).then(function(response) {
              return response || fetch(event.request);
            })
        );
    });
    C'est une stratégie cache par défaut et network en fallback. A partir du moment où les données sont en cache, le serveur ne sera plus jamais sollicité, du moins pour les entrées renseignées (index.php est-il chargé des mises à jour du cache ?)

    Je pense que tu cherches plus à avoir une stratégie network first, cache en fallback. Du moins pour les appels data serveur.
    One Web to rule them all

  5. #5
    Futur Membre du Club Avatar de bvrignaud
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mars 2013
    Messages
    17
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Mars 2013
    Messages : 17
    Points : 9
    Points
    9
    Par défaut
    Non, je veux plutôt du offline first.
    j'utilise IndexedDb pour toutes mes données.
    L'idée est que le site fonctionne strictement comme une appli, mais que quand il a du réseau, les données (indexedDb) sont maj.
    Et du coup, j'ai besoin d'un module d'authentification.
    Cela fonctionne avec appCache, mais pas avec service-worker.

  6. #6
    Rédacteur/Modérateur

    Avatar de SylvainPV
    Profil pro
    Inscrit en
    Novembre 2012
    Messages
    3 375
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2012
    Messages : 3 375
    Points : 9 944
    Points
    9 944
    Par défaut
    Offline first c'est assez imprécis comme terme, ça ne dit pas si on se contente du cache ou si on requête quand même le réseau et on actualise la vue après réception des données.

    De ce que tu me décris, j'ai l'impression que tu veux une stratégie Cache-Network-race : https://jakearchibald.com/2014/offli...e-network-race

    C'est bien ça ?
    One Web to rule them all

  7. #7
    Futur Membre du Club Avatar de bvrignaud
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mars 2013
    Messages
    17
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Mars 2013
    Messages : 17
    Points : 9
    Points
    9
    Par défaut
    Citation Envoyé par SylvainPV Voir le message
    Offline first c'est assez imprécis comme terme, ça ne dit pas si on se contente du cache ou si on requête quand même le réseau et on actualise la vue après réception des données.

    De ce que tu me décris, j'ai l'impression que tu veux une stratégie Cache-Network-race : https://jakearchibald.com/2014/offli...e-network-race

    C'est bien ça ?
    En fait mon application doit pouvoir fonctionner entièrement en offline.
    Les données html/css/javascripts sont chargé à partir du cache.
    Les données proviennent d'indexedDb.

    Le soucis, c'est que l'accès au serveur doit nécessiter un login et un mot de passe.
    C'est pourquoi j'utilise l'authentification http.

    Par exemple, lors de la première connexion au serveur, il me demande mon login et mon mot de passe, mais je n'arrive pas à concilier ça avec la mise en cache dans mon service worker.
    Idem, lorsque, plus tard, l'utilisateur retourne sur le site, le service worker regarde s'il y a une maj de lui même, c'est là que ça déconne, j'ai un soucis avec authentification.

  8. #8
    Rédacteur/Modérateur

    Avatar de SylvainPV
    Profil pro
    Inscrit en
    Novembre 2012
    Messages
    3 375
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2012
    Messages : 3 375
    Points : 9 944
    Points
    9 944
    Par défaut
    Donc c'est bien du Cache-Network Race qu'il te faut. Le code de Jake dans le lien précédent devrait faire l'affaire.

    Pour l'authentification HTTP, perso je ne l'aurais pas activé sur des ressources pouvant être mises en cache, puisqu'une fois en cache leur sécurité est compromise localement. Donc si on est en network only pour ces ressources, en principe le service worker ne s'interpose pas à l'authentification.

    Si tu veux quand même mettre du cache+network race partout avec de l'authentification HTTP, j'ai trouvé ce ticket qui parle de la faiblesse de la spec sur ce point : https://github.com/slightlyoff/ServiceWorker/issues/119
    One Web to rule them all

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo