New: Update `webauthnsample` to use ES6

This commit is contained in:
Manish Kumar Khedawat 2019-01-18 02:06:46 +05:30 коммит произвёл Antón Molleda
Родитель eb7db7daa4
Коммит fbe28c87a3
6 изменённых файлов: 66 добавлений и 62 удалений

16
app.js
Просмотреть файл

@ -1,8 +1,8 @@
var express = require("express");
var app = express();
var fido = require('./fido.js');
var bodyParser = require('body-parser');
var enforce = require('express-sslify');
const express = require("express");
const app = express();
const fido = require('./fido.js');
const bodyParser = require('body-parser');
const enforce = require('express-sslify');
if (process.env.ENFORCE_SSL_HEROKU === "true") {
app.use(enforce.HTTPS({ trustProtoHeader: true }));
@ -16,7 +16,7 @@ app.use(bodyParser.urlencoded({ extended: true }));
app.get('/challenge', async (req, res) => {
try {
var challenge = await fido.getChallenge();
const challenge = await fido.getChallenge();
res.json({
result: challenge
});
@ -29,7 +29,7 @@ app.get('/challenge', async (req, res) => {
app.put('/credentials', async (req, res) => {
try {
var credential = await fido.makeCredential(req.body);
const credential = await fido.makeCredential(req.body);
res.json({
result: credential
});
@ -42,7 +42,7 @@ app.put('/credentials', async (req, res) => {
app.put('/assertion', async (req, res) => {
try {
var credential = await fido.verifyAssertion(req.body);
const credential = await fido.verifyAssertion(req.body);
res.json({
result: credential
});

94
fido.js
Просмотреть файл

@ -1,18 +1,18 @@
var base64url = require('base64url');
var cbor = require('cbor');
var uuid = require('uuid-parse');
var jwkToPem = require('jwk-to-pem');
var jwt = require('jsonwebtoken');
var crypto = require('crypto');
var url = require('url');
const base64url = require('base64url');
const cbor = require('cbor');
const uuid = require('uuid-parse');
const jwkToPem = require('jwk-to-pem');
const jwt = require('jsonwebtoken');
const crypto = require('crypto');
const url = require('url');
var storage = require('./storage.js');
const storage = require('./storage.js');
var hostname = process.env.HOSTNAME || "localhost";
var jwt_secret = process.env.JWT_SECRET || "defaultsecret";
const hostname = process.env.HOSTNAME || "localhost";
const jwt_secret = process.env.JWT_SECRET || "defaultsecret";
var fido = {};
const fido = {};
/**
* Gets an opaque challenge for the client.
@ -43,7 +43,7 @@ fido.makeCredential = async (attestation) => {
//Step 1-2: Let C be the parsed the client data claimed as collected during
//the credential creation
var C;
let C;
try {
C = JSON.parse(attestation.clientDataJSON);
} catch (e) {
@ -53,17 +53,17 @@ fido.makeCredential = async (attestation) => {
//Step 3-6: Verify client data
validateClientData(C, "webauthn.create");
//Step 7: Compute the hash of response.clientDataJSON using SHA-256.
var clientDataHash = sha256(attestation.clientDataJSON);
const clientDataHash = sha256(attestation.clientDataJSON);
//Step 8: Perform CBOR decoding on the attestationObject
var attestationObject;
let attestationObject;
try {
attestationObject = cbor.decodeFirstSync(Buffer.from(attestation.attestationObject, 'base64'));
} catch (e) {
throw new Error("attestationObject could not be decoded");
}
//Step 8.1: Parse authData data inside the attestationObject
var authenticatorData = parseAuthenticatorData(attestationObject.authData);
const authenticatorData = parseAuthenticatorData(attestationObject.authData);
//Step 8.2: authenticatorData should contain attestedCredentialData
if (!authenticatorData.attestedCredentialData)
throw new Exception("Did not see AD flag in authenticatorData");
@ -84,10 +84,10 @@ fido.makeCredential = async (attestation) => {
throw new Error("User Verified bit was not set.");
}
//Steps 12-19 are skipped because this is a sample app.
//Steps 12-19 are skipped because this is a sample app.
//Store the credential
var credential = await storage.Credentials.create({
const credential = await storage.Credentials.create({
id: authenticatorData.attestedCredentialData.credentialId.toString('base64'),
publicKeyJwk: authenticatorData.attestedCredentialData.publicKeyJwk,
signCount: authenticatorData.signCount
@ -107,26 +107,26 @@ fido.verifyAssertion = async (assertion) => {
// Step 1 and 2 are skipped because this is a sample app
// Step 3: Using credentials id attribute look up the corresponding
// Step 3: Using credentials id attribute look up the corresponding
// credential public key.
var credential = await storage.Credentials.findOne({
let credential = await storage.Credentials.findOne({
id: assertion.id
});
if (!credential) {
throw new Error("Could not find credential with that ID");
}
var publicKey = credential.publicKeyJwk;
const publicKey = credential.publicKeyJwk;
if (!publicKey)
throw new Error("Could not read stored credential public key");
// Step 4: Let cData, authData and sig denote the value of credentials
// Step 4: Let cData, authData and sig denote the value of credentials
// response's clientDataJSON, authenticatorData, and signature respectively
var cData = assertion.clientDataJSON;
var authData = Buffer.from(assertion.authenticatorData, 'base64');
var sig = Buffer.from(assertion.signature, 'base64');
const cData = assertion.clientDataJSON;
const authData = Buffer.from(assertion.authenticatorData, 'base64');
const sig = Buffer.from(assertion.signature, 'base64');
// Step 5 and 6: Let C be the decoded client data claimed by the signature.
var C;
let C;
try {
C = JSON.parse(cData);
} catch (e) {
@ -136,7 +136,7 @@ fido.verifyAssertion = async (assertion) => {
validateClientData(C, "webauthn.get");
//Parse authenticator data used for the next few steps
var authenticatorData = parseAuthenticatorData(authData);
const authenticatorData = parseAuthenticatorData(authData);
//Step 11: Verify that the rpIdHash in authData is the SHA-256 hash of the
//RP ID expected by the Relying Party.
@ -154,7 +154,7 @@ fido.verifyAssertion = async (assertion) => {
throw new Error("User Verified bit was not set.");
}
//Step 14: Verify that the values of the client extension outputs in
//Step 14: Verify that the values of the client extension outputs in
//clientExtensionResults and the authenticator extension outputs in the
//extensions in authData are as expected
if (authenticatorData.extensionData) {
@ -162,15 +162,14 @@ fido.verifyAssertion = async (assertion) => {
throw new Error("Received unexpected extension data");
}
//Step 15: Let hash be the result of computing a hash over the cData using
//Step 15: Let hash be the result of computing a hash over the cData using
//SHA-256.
var hash = sha256(cData);
const hash = sha256(cData);
//Step 16: Using the credential public key looked up in step 3, verify
//Step 16: Using the credential public key looked up in step 3, verify
//that sig is a valid signature over the binary concatenation of authData
//and hash.
var publicKey = credential.publicKeyJwk;
var verify = (publicKey.kty === "RSA") ? crypto.createVerify('RSA-SHA256') : crypto.createVerify('sha256');
const verify = (publicKey.kty === "RSA") ? crypto.createVerify('RSA-SHA256') : crypto.createVerify('sha256');
verify.update(authData);
verify.update(hash);
if (!verify.verify(jwkToPem(publicKey), sig))
@ -210,21 +209,21 @@ fido.verifyAssertion = async (assertion) => {
* @property {string} credentialId
* @property {number} credentialIdLength
*/
function parseAuthenticatorData(authData) {
const parseAuthenticatorData = authData => {
try {
var authenticatorData = {};
const authenticatorData = {};
authenticatorData.rpIdHash = authData.slice(0, 32);
authenticatorData.flags = authData[32];
authenticatorData.signCount = (authData[33] << 24) | (authData[34] << 16) | (authData[35] << 8) | (authData[36]);
if (authenticatorData.flags & 64) {
var attestedCredentialData = {};
const attestedCredentialData = {};
attestedCredentialData.aaguid = uuid.unparse(authData.slice(37, 53)).toUpperCase();
attestedCredentialData.credentialIdLength = (authData[53] << 8) | authData[54];
attestedCredentialData.credentialId = authData.slice(55, 55 + attestedCredentialData.credentialIdLength);
//Public key is the first CBOR element of the remaining buffer
var publicKeyCoseBuffer = authData.slice(55 + attestedCredentialData.credentialIdLength, authData.length);
const publicKeyCoseBuffer = authData.slice(55 + attestedCredentialData.credentialIdLength, authData.length);
//convert public key to JWK for storage
attestedCredentialData.publicKeyJwk = coseToJwk(publicKeyCoseBuffer);
@ -235,10 +234,10 @@ function parseAuthenticatorData(authData) {
if (authenticatorData.flags & 128) {
//has extension data
var extensionDataCbor;
let extensionDataCbor;
if (authenticatorData.attestedCredentialData) {
//if we have attesttestedCredentialData, then extension data is
//if we have attesttestedCredentialData, then extension data is
//the second element
extensionDataCbor = cbor.decodeAllSync(authData.slice(55 + authenticatorData.attestedCredentialData.credentialIdLength, authData.length));
extensionDataCbor = extensionDataCbor[1];
@ -261,11 +260,11 @@ function parseAuthenticatorData(authData) {
* @param {any} clientData JSON parsed client data object received from client
* @param {string} type Operation type: webauthn.create or webauthn.get
*/
function validateClientData(clientData, type) {
const validateClientData = (clientData, type) => {
if (clientData.type !== type)
throw new Error("collectedClientData type was expected to be " + type);
var origin;
let origin;
try {
origin = url.parse(clientData.origin);
} catch (e) {
@ -278,8 +277,7 @@ function validateClientData(clientData, type) {
if (hostname !== "localhost" && origin.protocol !== "https:")
throw new Error("Invalid origin in collectedClientData. Expected HTTPS protocol.");
var decodedChallenge;
let decodedChallenge;
try {
decodedChallenge = jwt.verify(base64url.decode(clientData.challenge), jwt_secret);
} catch (err) {
@ -292,10 +290,10 @@ function validateClientData(clientData, type) {
* @param {Buffer} cose Buffer containing COSE key data
* @returns {any} JWK object
*/
function coseToJwk(cose) {
const coseToJwk = cose => {
try {
var publicKeyJwk = {};
var publicKeyCbor = cbor.decodeFirstSync(cose);
let publicKeyJwk = {};
const publicKeyCbor = cbor.decodeFirstSync(cose);
if (publicKeyCbor.get(3) == -7) {
publicKeyJwk = {
@ -322,11 +320,11 @@ function coseToJwk(cose) {
/**
* Evaluates the sha256 hash of a buffer
* @param {Buffer} data
* @param {Buffer} data
* @returns sha256 of the input data
*/
function sha256(data) {
var hash = crypto.createHash('sha256');
const sha256 = data => {
const hash = crypto.createHash('sha256');
hash.update(data);
return hash.digest();
}

Просмотреть файл

@ -4,7 +4,8 @@
"description": "WebAuthn Sample App",
"main": "app.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node app.js"
},
"author": "Ibrahim Damlaj <idamlaj@gmail.com> (https://github.com/MicrosoftEdge/webauthnsample)",
"license": "MIT",

Просмотреть файл

@ -1,11 +1,11 @@
.btn {
width: 240px;
margin-right:10px;
margin-right: 10px;
margin-bottom: 10px;
}
.spinner {
margin-right:10px;
margin-right: 10px;
}
.hidden {
@ -15,3 +15,8 @@
.errorText {
color: red;
}
.wordWrap {
word-break: break-all;
word-wrap: break-word;
}

Просмотреть файл

@ -80,7 +80,7 @@
<br>
<br>
<br>
<div class="alert alert-primary hidden" role="alert" id="status">
<div class="alert alert-primary hidden wordWrap" role="alert" id="status">
Successfully received assertion
</div>
</div>

Просмотреть файл

@ -1,9 +1,9 @@
const mongoose = require('mongoose');
var mongodb_url = process.env.MONGODB_URL || 'mongodb://localhost/fido';
const mongodb_url = process.env.MONGODB_URL || 'mongodb://localhost/fido';
mongoose.connect(mongodb_url);
var storage = {};
const storage = {};
storage.Credentials = mongoose.model('Credential', new mongoose.Schema({
id: {type: String, index: true},