Práctica: Passport y LocalStrategy

Descripción

Objetivo:

  • El servidor proveído por el plugin (sea iaaso heroku) deberá autenticar al lector del libro usando LocalStrategy.
    • El uso de la LocalStrategy para autenticación debería ser opcional
    • Se supone que se guarda la información sobre los usuarios {login, name, password (encrypted)} que pueden acceder al libro en un fichero JSON.
    • Añada una ruta al servidor /login/password para que el usuario pueda cambiar la password. En esta ruta se despliega una vista con un formulario que permite al lector cambiar la clave
    • En el caso de Heroku la volatilidad de la máquina virtual hace que esta solución tenga problemas. Se pueden hacer los cambios permanentes asegurandose que los cambios son añadidos y empujados al repo de Heroku o mas general, guardándo el fichero de claves en algún servicio privado externo (dropbox, google-drive, github, ...) al que se accede vía la correspondiente API

Ejemplos

Ejemplos

Express 4.x app using Passport for authentication with username and password

Ejemplo de uso de inquirer

'use strict';
var inquirer = require('inquirer');

var questions = [
  {
    type: 'input',
    name: 'first_name',
    message: 'What\'s your first name'
  },
  {
    type: 'input',
    name: 'last_name',
    message: 'What\'s your last name',
    default: function () {
      return 'Doe';
    }
  },
  {
    type: 'input',
    name: 'phone',
    message: 'What\'s your phone number',
    validate: function (value) {
      var pass = value.match(/^\d+$/i);
      if (pass) {
        return true;
      }

      return 'Please enter a valid phone number';
    }
  }
];

inquirer.prompt(questions).then(function (answers) {
  console.log(JSON.stringify(answers, null, '  '));
});

Ejemplo de ejecución:

[~/local/src/javascript/learning/use-credential(master)]$ node input.js 
? What's your first name Casiano
? What's your last name Doe
? What's your phone number 111222
{
  "first_name": "Casiano",
  "last_name": "Doe",
  "phone": "111222"
}

Ejemplo de uso de credential e inquirer

var inquirer = require('inquirer');
var credential = require('credential'),
  pw = credential(),
  pass = 'chuchu';

pw.hash(pass, function (err, hash) {
  if (err) { throw err; }
  console.log('Store the password hash:\n', hash);
  var questions = [{ message: "Enter your password", type: 'password', name: 'password'}];
  inquirer.prompt(questions).then(function (userInput) {
    console.log(userInput);
    var userPass = userInput.password;
    console.log(userPass);
    pw.verify(hash, userPass, function (err, isValid) {
      var msg;
      if (err) { throw err; }
      msg = isValid ? 'Passwords match!' : 'Wrong password.';
      console.log(msg);
    });
  });
});

El resultado de una ejecución es parecido a este:

[~/local/src/javascript/learning/use-credential(master)]$ node hash.js 
Store the password hash:
{
  "hash":"lFPjexO2wJKgliHvoGC4hTdrb6PbwZfKskhY9DP0GA2sHS8BfkZel0JlQ4YNNKvykDox7Bwwpvdx6Pxic84L6Oby",
  "salt":"gQ+LdSLI5Qxx9owI/ulxd/Qn5uiBfkEof1UzUCWCT8FJCA7LsCZSzn4fMxB/Lb+grqRBwPA24tE5MtKyw49PnfPo",
  "keyLength":66,
  "hashMethod":"pbkdf2",
  "iterations":340230
 }
? Enter your password ******
{ password: 'chuchu' }
chuchu
Passwords match!

Ejemplo de uso de bcrypt-nodejs

Creemos un programa como este:

[~/local/src/javascript/learning/use-credential(master)]$ cat use-bcrypt.js
// Synchronous

var bcrypt = require("bcrypt-nodejs");

/*
  * hashSync(data, salt)
    - data - [REQUIRED] - the data to be encrypted.
    - salt - [REQUIRED] - the salt to be used in encryption.
*/
var hash = bcrypt.hashSync("bacon");

/*
  * compareSync(data, encrypted)
    - data - [REQUIRED] - data to compare.
    - encrypted - [REQUIRED] - data to be compared to.
*/
var ra = bcrypt.compareSync("bacon", hash); // true
console.log(ra);
var wa = bcrypt.compareSync("veggies", hash); // false
console.log(wa);

// Asynchronous

/*
  * hash(data, salt, progress, cb)
    - data - [REQUIRED] - the data to be encrypted.
    - salt - [REQUIRED] - the salt to be used to hash the password.
    - progress - a callback to be called during the hash calculation to signify progress
    - callback - [REQUIRED] - a callback to be fired once the data has been encrypted.
    - error - First parameter to the callback detailing any errors.
    - result - Second parameter to the callback providing the encrypted form.
*/
bcrypt.hash("bacon", null, null, function(err, hash) {
  /*
    * compare(data, encrypted, cb)
      - data - [REQUIRED] - data to compare.
      - encrypted - [REQUIRED] - data to be compared to.
      - callback - [REQUIRED] - a callback to be fired once the data has been compared.
      - error - First parameter to the callback detailing any errors.
      - result - Second parameter to the callback providing whether the data and encrypted forms match [true | false].
  */
  bcrypt.compare("bacon", hash, function(err, res) {
    console.log(res); // res == true
  });

  bcrypt.compare("veggies", hash, function(err, res) {
    console.log(res); // res = false
  });
});

Ejecución:

[~/local/src/javascript/learning/use-credential(master)]$ node use-bcrypt.js 
true
false
true
false

Referencias

OAuth y Passport

Hashing para guardar las claves

Lecturas desde STDIN

Sistemas de Almacenamiento en la Nube

Dropbox
    var Dropbox = require('dropbox');
    var fs = require('fs');
    var prompt = require('prompt');

    prompt.start();

    prompt.get({
      properties: {
        accessToken: {
          description: 'Please enter an API V2 access token'
        },
        sharedLink: {
          description: 'Please enter a shared link to a file'
        }
      }
    }, function (error, result) {
      var dbx = new Dropbox({ accessToken: result.accessToken });
      dbx.sharingGetSharedLinkFile({ url: result.sharedLink })
        .then(function (data) {
          fs.writeFile(data.name, data.fileBinary, 'binary', function (err) {
            if (err) { throw err; }
            console.log('File: ' + data.name + ' saved.');
          });
        })
        .catch(function (err) {
          throw err;
        });
    });
  1. upload.js

     var Dropbox = require('dropbox');
     var fs = require('fs');
     var path = require('path');
     var prompt = require('prompt');
    
     prompt.start();
    
     prompt.get({
       properties: {
         accessToken: {
           description: 'Please enter an API V2 access token'
         }
       }
     }, function (error, result) {
       var dbx = new Dropbox({ accessToken: result.accessToken });
    
       fs.readFile(path.join(__dirname, '/basic.js'), 'utf8', function (err, contents) {
         if (err) {
           console.log('Error: ', err);
         }
    
         // This uploads basic.js to the root of your dropbox
         dbx.filesUpload({ path: '/basic.js', contents: contents })
           .then(function (response) {
             console.log(response);
           })
           .catch(function (err) {
             console.log(err);
           });
       });
     });
    
  2. Ejemplos en el browser To run the examples in your development environment:

    1. Get a token: click the "Get Token" button on the top right and copy the token
    2. Clone this repo
    3. Run npm install
    4. From the root of your repository, start the development server with npm start.
    5. Point your browser to http://0.0.0.0:8080/
    6. Este otro módulo node-dropbox: A simple Dropbox API client for node.js no parece muy actualizado
Google