Skip to content

Commit

Permalink
Multiple devices reg and preventing duplicate reg
Browse files Browse the repository at this point in the history
  • Loading branch information
dqj1998 committed Aug 3, 2022
1 parent b5ea9ef commit d40bed9
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 47 deletions.
102 changes: 62 additions & 40 deletions main.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,22 +57,20 @@ async function AppController(request, response) {
return
}

var authnOptions = await fido2lib.assertionOptions();
authnOptions.challenge = base64url.encode(uuidv4())//base64url.encode(authnOptions.challenge);
let authnOptions = await fido2lib.assertionOptions(body);
authnOptions.challenge = base64url.encode(uuidv4())

let allowCredentials = [];
for(let authr of database[username].attestation) {
allowCredentials.push({
type: 'public-key',
id: authr.credId,
transports: ['internal', 'usb', 'nfc', 'ble']
transports: ['internal', 'hybrid', 'usb', 'nfc', 'ble']// can be overrided by client
})
}
authnOptions.allowCredentials = allowCredentials;
console.log(authnOptions);

//context.req.session.challenge = authnOptions.challenge;
//context.req.session.username = username;
sessions[authnOptions.challenge] = {
'challenge': authnOptions.challenge,
'username': username
Expand All @@ -88,8 +86,8 @@ async function AppController(request, response) {
const cltdatatxt = String.fromCharCode.apply("", cda)
const clientData = JSON.parse(cltdatatxt)

var attestation = null;
for( var i = 0 ; i < database[sessions[clientData.challenge].username].attestation.length ; i++ ){
let attestation = null;
for( let i = 0 ; i < database[sessions[clientData.challenge].username].attestation.length ; i++ ){
if( database[sessions[clientData.challenge].username].attestation[i].credId == body.id ){
attestation = database[sessions[clientData.challenge].username].attestation[i];
break;
Expand All @@ -104,18 +102,21 @@ async function AppController(request, response) {
response.end(JSON.stringify(rtn));
return
}

var assertionExpectations = {
challenge: sessions[clientData.challenge].challenge,

const cur_session = sessions[clientData.challenge]
delete sessions[clientData.challenge]

let assertionExpectations = {
challenge: cur_session.challenge,
origin: FIDO_ORIGIN,
factor: "either",
publicKey: attestation.publickey,
prevCounter: attestation.counter,
userHandle: database[sessions[clientData.challenge].username].id //null
userHandle: database[cur_session.username].id //null
};

body.rawId = new Uint8Array(base64url.toBuffer(body.rawId)).buffer;
var authnResult = await fido2lib.assertionResult(body, assertionExpectations);
let authnResult = await fido2lib.assertionResult(body, assertionExpectations);
console.log(authnResult);

let rtn='';
Expand All @@ -133,38 +134,50 @@ async function AppController(request, response) {
'message': 'Can not authenticate signature!'
}
}

delete sessions[clientData.challenge]

response.end(JSON.stringify(rtn));
}else if(url.pathname === '/attestation/options'){
const body = await loadJsonBody(request)

let username = body.username;

if(database[username] && database[username].registered) {
response.end(JSON.stringify({
'status': 'err',
'message': `Username ${username} already exists`
}));
return;
let userid;
if(database[username]) {
userid = database[username].id;
}else{
userid = uuidv4();
}

const id = uuidv4();

let registrationOptions = await fido2lib.attestationOptions();
registrationOptions.challenge = base64url.encode(uuidv4())//use challenge as session id
registrationOptions.user.id = id;

//Prevent register same authenticator
if(database[username] && database[username].attestation){
let excludeCredentials = [];
for(let authr of database[username].attestation) {
excludeCredentials.push({
type: 'public-key',
id: authr.credId,
transports: ['internal', 'hybrid', 'usb', 'nfc', 'ble']
})
}
registrationOptions.excludeCredentials = excludeCredentials
}

registrationOptions.user.id = userid;
registrationOptions.user.name = username;
registrationOptions.user.displayName = username;
registrationOptions.user.displayName = body.displayName?body.displayName:username;

console.log(registrationOptions);

database[username] = {
'name': username,
'registered': false,
'id': id,
'attestation': []
};
if(!database[username]){
database[username] = {
'name': username,
'registered': false,
'id': registrationOptions.user.id,
'attestation': []
};
}

sessions[registrationOptions.challenge] = {
'challenge': registrationOptions.challenge,
Expand All @@ -182,27 +195,32 @@ async function AppController(request, response) {
const cltdatatxt = String.fromCharCode.apply("", cda)
const clientData = JSON.parse(cltdatatxt)

const cur_session = sessions[clientData.challenge]
delete sessions[clientData.challenge]

let attestationExpectations = {
challenge: sessions[clientData.challenge].challenge,
challenge: cur_session.challenge,
origin: FIDO_ORIGIN,
factor: "either"
};
body.rawId = new Uint8Array(base64url.toBuffer(body.rawId)).buffer;
let regResult = await fido2lib.attestationResult(body, attestationExpectations);
console.log(regResult);

let credId = base64url.encode(regResult.authnrData.get('credId'));
let counter = regResult.authnrData.get('counter');
database[sessions[clientData.challenge].username].attestation.push({

const credId = base64url.encode(regResult.authnrData.get('credId'));
const aaguid = buf2hex(regResult.authnrData.get('aaguid'))//No required info for reg/auth
const counter = regResult.authnrData.get('counter');
database[cur_session.username].attestation.push({
publickey : regResult.authnrData.get('credentialPublicKeyPem'),
counter : counter,
fmt: regResult.authnrData.get('fmt'),
credId : credId
fmt : regResult.authnrData.get('fmt'),
credId : credId,
aaguid : aaguid
});

let rtn='';
if(regResult.audit.complete) {
database[sessions[clientData.challenge].username].registered = true
database[cur_session.username].registered = true

rtn = {
'status': 'ok',
Expand All @@ -216,8 +234,6 @@ async function AppController(request, response) {
};
}

delete sessions[clientData.challenge]

response.end(JSON.stringify(rtn));
}
}catch(ex){
Expand All @@ -227,6 +243,12 @@ async function AppController(request, response) {

};

function buf2hex(buffer) { // buffer is an ArrayBuffer
return [...new Uint8Array(buffer)]
.map(x => x.toString(16).padStart(2, '0'))
.join('');
}

async function loadJsonBody(request){
const buffers = [];

Expand Down
14 changes: 7 additions & 7 deletions views/fido2.html
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,8 @@
.then((resp) => {
console.log("/attestation/options response: ");
console.log(JSON.stringify(resp, null, 2));
if (resp.status === "err") {
throw "Cannot reg, user may exist!";
if (resp.status === "failed") {
throw resp.message;
}else{
return navigator.credentials.create(makePublicKey(resp));
}
Expand Down Expand Up @@ -128,7 +128,7 @@
if (resp.status === "ok") {
alert("Completed Attestation");
} else {
alert(`Server Error: ${resp.errorMessage}`);
alert(`Server Error: ${resp.message}`);
}
} else {
alert("Svr result error");
Expand All @@ -144,7 +144,7 @@
"username": username,
"userVerification": "preferred",
"authenticatorSelection": {
"authenticatorAttachment": "platform"
"authenticatorAttachment": "cross-platform" //, //"platform",
}
};

Expand All @@ -164,7 +164,7 @@
return {
id: _base64ToArrayBuffer(_fromBase64URL(x.id)),
type: x.type,
transports: x.transports
transports: x.transports // can set like ['internal', 'usb'] to override server settings
}
});

Expand All @@ -180,8 +180,8 @@
console.error("navigator.credentials.get error with code: " + err.code + " and message: " + err.message + " and name: " + err.name);
});
}else{
alert('Auth err, user may does NOT exist.')
throw 'Auth err, user may does NOT exist.'
alert('Auth err, '+resp.message)
throw 'Auth err, '+resp.message
}
})
.then(resp => {
Expand Down

0 comments on commit d40bed9

Please sign in to comment.