Welcome to WebAuthn workshop
Please check that you have obtained FIDO U2F Security Key
Please check that you have installed LATEST Nodejs(v6.x), NPM, Git and Google Chrome.
Authenticating
your Web
Yuriy Ackermann
Sr. Certification Engineer @FIDOAlliance
twitter/github: @herrjemand
Todays plan:
- Learn what is webauthn
- Learn how to webauthn
- Learn how to make
- ...manage
- ...and assert creds.
So... before we start check that:
- You have installed latest NodeJS
- ...and NPM
- Chrome(67)
- Git
- Text editor
Short recap of CredManAPI
- JS API for credentials management
- Official W3C spec
- Basically JS API for autofill
- Read/watch:
- https://www.w3.org/TR/credential-management/
- https://developers.google.com/web/fundamentals/security/credential-management/
- https://pusher.com/sessions/meetup/js-monthly-london/building-a-better-login-with-the-credential-management-api
navigator.credentials
.get({ 'password': true })
.then(credential => {
if (!credential) {
throw new Error('No credentials returned!')
}
let credentials = {
'username': credential.id,
'password': credential.password
}
return fetch('https://example.com/loginEndpoint', {
method: 'POST',
body: JSON.stringify(credentials),
credentials: 'include'
})
})
.then((response) => {
...
})
navigator.credentials.store({
'type': 'password',
'id': 'alice',
'password': 'VeryRandomPassword123456'
})
What is WebAuthn?
- PublicKey extension to credential management API
- An official W3C standard
- A sub-spec of FIDO2 specs
- Basically public keys for authentication in browsers
- Read:
- https://w3c.github.io/webauthn/
- https://webauthn.org/
- http://slides.com/herrjemand/webauthn-isig
MakeCredentials request
var publicKey = {
challenge: new Uint8Array([21,31,105, ..., 55]),
rp: {
name: "ACME Corporation"
},
user: {
id: Uint8Array.from(window.atob("MIIBkzCCATigAwIBAjCCAZMwggE4oAMCAQIwggGTMII="), c=>c.charCodeAt(0)),
name: "alex.p.mueller@example.com",
displayName: "Alex P. Müller"
},
attestation: 'direct',
pubKeyCredParams: [
{
type: "public-key", alg: -7 // "ES256" IANA COSE Algorithms registry
},
{
type: "public-key", alg: -257 // "RS256" IANA COSE Algorithms registry
}
]
}
navigator.credentials.create({ publicKey })
.then((newCredentialInfo) => {
/* Public key credential */
}).catch((error) => {
/* Error */
})
Random 32byte challenge buffer
Friendly RP name
User names and userid buffer
Signature algorithm negotiation
You want attestation object or not
Let's try it our selves:
var randomChallengeBuffer = new Uint8Array(32);
window.crypto.getRandomValues(randomChallengeBuffer);
var base64id = 'MIIBkzCCATigAwIBAjCCAZMwggE4oAMCAQIwggGTMII='
var idBuffer = Uint8Array.from(window.atob(base64id), c=>c.charCodeAt(0))
var publicKey = {
challenge: randomChallengeBuffer,
rp: { name: "FIDO Example Corporation" },
user: {
id: idBuffer,
name: "alice@example.com",
displayName: "Alice von Wunderland"
},
attestation: 'direct',
pubKeyCredParams: [
{ type: 'public-key', alg: -7 }, // ES256
{ type: 'public-key', alg: -257 } // RS256
]
}
navigator.credentials.create({ publicKey })
.then((newCredentialInfo) => {
console.log('SUCCESS', newCredentialInfo)
})
.catch((error) => {
console.log('FAIL', error)
})
Go to https://webauthn.bin.coffee and run in the console
MakeCredentials response
Inner authr id. id == base64url(rawId)
{
"challenge": "HDhbiI5a6F_ndjVmnkCYKM_Mjt5Nv7OQwrYAeI8zX5E",
"hashAlgorithm": "SHA-256",
"origin": "https://webauthn.bin.coffee"
}
Client collected data
Authr assertion
Okay, hands on deck, lets code
- Open terminal
- git clone https://github.com/fido-alliance/webauthn-demo
- cd webauthn-demo
- npm install
- node app.js
- In Google Chrome http://localhost:3000
Request challenge
Process challenge
Return response
What are we doing?
App architecture
- Simple Express
- app.js + config.json - app config
- routes/ - express routers + db
- utils.js - help functions + crypto
- static/ - static frontend
Frontend architecture
- Simple HTML framework + jQuery
- js/password.auth.js - well... you get it
- js/helpers.js - help functions
- js/view.js - some gui help functions
For registration:
- Get username and name(password field is obsolete, lol)
- Send them to the server
- Server responds with challenge
- MakeCredential
- Send response to the server
- Check that server likes it
- PROFIT!
- Remove password section from registration form in index.html
- In "password.auth.js" comment #register handler
- In "webauthn.auth.js" add:
/* Handle for register form submission */
$('#register').submit(function(event) {
event.preventDefault();
let username = this.username.value;
let name = this.name.value;
if(!username || !name) {
alert('Name or username is missing!')
return
}
})
- Then we need to get MakeCred challenge. For that we will have /webauthn/register endpoint
- Adding getMakeCredentialsChallenge fetch function to "webauthn.auth.js"
let getMakeCredentialsChallenge = (formBody) => {
return fetch('/webauthn/register', {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(formBody)
})
.then((response) => response.json())
.then((response) => {
if(response.status !== 'ok')
throw new Error(`Server responed with error. The message is: ${response.message}`);
return response
})
}
POST request
Its a JSON request, and it takes JS object and JSON encodes it
Server responds with JSON
response key "status" can be "ok" or "failed"
- Adding "getMakeCredentialsChallenge" to "#register" form processor in "webauthn.auth.js"
/* Handle for register form submission */
$('#register').submit(function(event) {
event.preventDefault();
let username = this.username.value;
let name = this.name.value;
if(!username || !name) {
alert('Name or username is missing!')
return
}
getMakeCredentialsChallenge({username, name})
.then((response) => {
console.log(response)
})
})
- It receives name and username
- Adds adds them to the DB tagged as not registered
- Generates registration request
- And sends it back to the browser
Now for the /webauthn/register endpoint
- Now lets create /webauthn/register endpoint
- In routes/webauthn.js insert this code
router.post('/register', (request, response) => {
if(!request.body || !request.body.username || !request.body.name) {
response.json({
'status': 'failed',
'message': 'Request missing name or username field!'
})
return
}
let username = request.body.username;
let name = request.body.name;
if(database[username] && database[username].registered) {
response.json({
'status': 'failed',
'message': `Username ${username} already exists`
})
return
}
database[username] = {
'name': name,
'registered': false,
'id': utils.randomBase64URLBuffer(),
'authenticators': []
}
})
Check all field. The body is the request.body
Check that user does not exist or he is not registered
Creating user
Generating user random ID
This is where we store registered authenticators
- To generate makeCredential challenge utils have "generateServerMakeCredRequest" method
let generateServerMakeCredRequest = (username, displayName, id) => {
return {
challenge: randomBase64URLBuffer(32),
rp: {
name: "FIDO Examples Corporation"
},
user: {
id: id,
name: username,
displayName: displayName
},
pubKeyCredParams: [
{
type: "public-key", alg: -7 // "ES256" IANA COSE Algorithms registry
}
]
}
}
router.post('/register', (request, response) => {
...
let challengeMakeCred = utils.generateServerMakeCredRequest(username,
name, database[username].id)
challengeMakeCred.status = 'ok'
request.session.challenge = challengeMakeCred.challenge;
request.session.username = username;
response.json(challengeMakeCred)
})
Generate makeCred challenge: passing username, name, and id
Saving username and challenge in session for later
Don't forget to let browser know that you are ok!
Send response
In routes/webauthn.js add new block of code
Back to the html...
Remember this?
var randomChallengeBuffer = new Uint8Array(32);
window.crypto.getRandomValues(randomChallengeBuffer);
var base64id = 'MIIBkzCCATigAwIBAjCCAZMwggE4oAMCAQIwggGTMII='
var idBuffer = Uint8Array.from(window.atob(base64id), c=>c.charCodeAt(0))
var publicKey = {
challenge: randomChallengeBuffer,
rp: { name: "FIDO Example Corporation" },
user: {
id: idBuffer,
name: "alice@example.com",
displayName: "Alice von Wunderland"
},
pubKeyCredParams: [
{ type: 'public-key', alg: -7 }, // ES256
{ type: 'public-key', alg: -257 } // RS256
]
}
// Note: The following call will cause the authenticator to display UI.
navigator.credentials.create({ publicKey })
.then((newCredentialInfo) => {
console.log('SUCCESS', newCredentialInfo)
})
.catch((error) => {
console.log('FAIL', error)
})
challenge is a buffer
id is a buffer
Here is our server response
{
"challenge": "IAomGjp6nnS9GvPhdRdd3ATQWdL0PXTOAHDR6pPgeXM",
"rp": {
"name": "ACME Corporation"
},
"user": {
"id": "38cuhE0p0bN5PM9hSp7WEE8oTS08OQE0igXtuBifxfo",
"name": "alice",
"displayName": "Alice"
},
"pubKeyCredParams": [{
"type": "public-key",
"alg": -7
}],
"status": "ok"
}
Oh boy, challenge is not BUFFER! ...cause no buffers in JSON
...and id as well
Good that helpers.js have "preformatMakeCredReq" method
var preformatMakeCredReq = (makeCredReq) => {
makeCredReq.challenge = base64url.decode(makeCredReq.challenge);
makeCredReq.user.id = base64url.decode(makeCredReq.user.id);
return makeCredReq
}
/* Handle for register form submission */
$('#register').submit(function(event) {
event.preventDefault();
let username = this.username.value;
let name = this.name.value;
if(!username || !name) {
alert('Name or username is missing!')
return
}
getMakeCredentialsChallenge({username, name})
.then((response) => {
let publicKey = preformatMakeCredReq(response);
return navigator.credentials.create({ publicKey })
})
.then((newCred) => {
console.log(newCred)
})
})
Updating #registration processor in webauthn.auth.js
Back to MakeCredentials response
BUFFER
BUFFER
BUFFER
Guess what? JSON does not do buffers *(
But don't worry, utils have publicKeyCredentialToJSON method
getMakeCredentialsChallenge({username, name})
.then((response) => {
let publicKey = preformatMakeCredReq(response);
return navigator.credentials.create({ publicKey })
})
.then((newCred) => {
let makeCredResponse = publicKeyCredentialToJSON(newCred);
console.log(makeCredResponse)
})
That's better!
Updating #registration processor in webauthn.auth.js
{
"rawId": "Gw7nqgWzci8jwIX9yXzYtynmJbQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
"response": {
"attestationObject": "o2NmbXRoZmlkby11MmZnYXR0U3RtdKJjc2lnWEcwRQIhAIdC3J6jt_rxTF3mo_HdQ_HUWOW3b9GPzpLMz-Wt78UTAiBjE0JgQNTkglEg2eEv8aJIYZCqJUzm5LO8tVax7m-gz2N4NWOBWQGCMIIBfjCCASSgAwIBAgIBATAKBggqhkjOPQQDAjA8MREwDwYDVQQDDAhTb2Z0IFUyRjEUMBIGA1UECgwLR2l0SHViIEluYy4xETAPBgNVBAsMCFNlY3VyaXR5MB4XDTE3MDcyNjIwMDkwOFoXDTI3MDcyNDIwMDkwOFowPDERMA8GA1UEAwwIU29mdCBVMkYxFDASBgNVBAoMC0dpdEh1YiBJbmMuMREwDwYDVQQLDAhTZWN1cml0eTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABPacqyQUS7Tvh_cPIxxc1PV4BKz44Mays-NSGD2AOR9r0nnSakyDZHTmwtojk_-sHVA0bFwjkGVXkz7Lk_9u3tGjFzAVMBMGCysGAQQBguUcAgEBBAQDAgMIMAoGCCqGSM49BAMCA0gAMEUCIQD-Ih2XuOrqErufQhSFD0gXZbXglZNeoaPWbQ-xbzn3IgIgZNfcL1xsOCr3ZfV4ajmwsUqXRSjvfd8hAhUbiErUQXpoYXV0aERhdGFYykmWDeWIDoxodDQXD2R2YFuP5K65ooYyx5lc87qDHZdjQQAAAAAAAAAAAAAAAAAAAAAAAAAAAEYbDueqBbNyLyPAhf3JfNi3KeYltAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAApQECAyYgASFYIJ8Lv8N1eB_A9d6z3k0B4d9ii7fHSyZChIG3lwlqsgHcIlggglrXCklNPmjLdnXDijGxDh0b2k52p2N6EDET0BScCjo",
"clientDataJSON": "eyJjaGFsbGVuZ2UiOiJLMlEwdHdnXzVGNDJRMEtnYll6OXdxaGVEN3ZBbmlFdEJ0N190a3g3ZEo0IiwiaGFzaEFsZ29yaXRobSI6IlNIQS0yNTYiLCJvcmlnaW4iOiJodHRwOi8vbG9jYWxob3N0OjMwMDAifQ"
},
"id": "Gw7nqgWzci8jwIX9yXzYtynmJbQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
"type": "public-key"
}
For registration:
Get username and name(password field is obsolete, lol)Send them to the serverServer responds with challengeMakeCredential- Send response to the server
- Check that server likes it
- PROFIT!
Sending response to the server
- Auth and Reg responses both going to the same endpoint /webauthn/response
let sendWebAuthnResponse = (body) => {
return fetch('/webauthn/response', {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(body)
})
.then((response) => response.json())
.then((response) => {
if(response.status !== 'ok')
throw new Error(`Server responed with error. The message is: ${response.message}`);
return response
})
}
.then((response) => {
let makeCredResponse = publicKeyCredentialToJSON(response);
return sendWebAuthnResponse(makeCredResponse)
})
.then((response) => {
if(response.status === 'ok') {
loadMainContainer()
} else {
alert(`Server responed with error. The message is: ${response.message}`);
}
})
.catch((error) => alert(error))
New function to be added to webauthn.auth.js
Updating #registration processor in webauthn.auth.js
Back to the server
router.post('/response', (request, response) => {
if(!request.body || !request.body.id
|| !request.body.rawId || !request.body.response
|| !request.body.type || request.body.type !== 'public-key' ) {
response.json({
'status': 'failed',
'message': 'Response missing one or more of id/rawId/response/type fields, or type is not public-key!'
})
return
}
let webauthnResp = request.body
let clientData = JSON.parse(base64url.decode(webauthnResp.response.clientDataJSON));
/* Check challenge... */
if(clientData.challenge !== request.session.challenge) {
response.json({
'status': 'failed',
'message': 'Challenges don\'t match!'
})
}
/* ...and origin */
if(clientData.origin !== config.origin) {
response.json({
'status': 'failed',
'message': 'Origins don\'t match!'
})
}
})
Add this code to routes/webauthn.js
Checking response
Parsing client data
Checking that origin and challenge match
Ok, so we got the response. How do we know that it's a reg?
attestationObject
MakeCredentials
authenticatorData
GetAssertion
router.post('/response', (request, response) => {
...
if(webauthnResp.response.attestationObject !== undefined) {
/* This is create cred */
} else if(webauthnResp.response.authenticatorData !== undefined) {
/* This is get assertion */
} else {
response.json({
'status': 'failed',
'message': 'Can not determine type of response!'
})
}
})
Updating /response endpoint in routes/webauthn.js
AttestationVerification
In utils.js there is a method "verifyAuthenticatorAttestationResponse"
let verifyAuthenticatorAttestationResponse = (webAuthnResponse) => {
let attestationBuffer = base64url.toBuffer(webAuthnResponse.response.attestationObject);
let ctapMakeCredResp = cbor.decodeAllSync(attestationBuffer)[0];
let response = {'verified': false};
if(ctapMakeCredResp.fmt === 'fido-u2f') {
let authrDataStruct = parseMakeCredAuthData(ctapMakeCredResp.authData);
if(!(authrDataStruct.flags & U2F_USER_PRESENTED))
throw new Error('User was NOT presented durring authentication!');
let clientDataHash = hash(base64url.toBuffer(webAuthnResponse.response.clientDataJSON))
let reservedByte = Buffer.from([0x00]);
let publicKey = COSEECDHAtoPKCS(authrDataStruct.COSEPublicKey)
let signatureBase = Buffer.concat([reservedByte, authrDataStruct.rpIdHash, clientDataHash, authrDataStruct.credID, publicKey]);
let PEMCertificate = ASN1toPEM(ctapMakeCredResp.attStmt.x5c[0]);
let signature = ctapMakeCredResp.attStmt.sig;
response.verified = verifySignature(signature, signatureBase, PEMCertificate)
if(response.verified) {
response.authrInfo = {
fmt: 'fido-u2f',
publicKey: base64url.encode(publicKey),
counter: authrDataStruct.counter,
credID: base64url.encode(authrDataStruct.credID)
}
}
}
return response
}
Decode base64url encoded assertion buffer, and CBOR parse it
It's a U2F statement
Parsing raw authData buffer
Check that TUP flag is set
Generate signature base
COSE to PKCS conversion
X509 Cert buffer into PEM
Verify signature
On verification, return response with new Authr
AuthenticatorData
let parseMakeCredAuthData = (buffer) => {
let rpIdHash = buffer.slice(0, 32); buffer = buffer.slice(32);
let flagsBuf = buffer.slice(0, 1); buffer = buffer.slice(1);
let flags = flagsBuf[0];
let counterBuf = buffer.slice(0, 4); buffer = buffer.slice(4);
let counter = counterBuf.readUInt32BE(0);
let aaguid = buffer.slice(0, 16); buffer = buffer.slice(16);
let credIDLenBuf = buffer.slice(0, 2); buffer = buffer.slice(2);
let credIDLen = credIDLenBuf.readUInt16BE(0);
let credID = buffer.slice(0, credIDLen); buffer = buffer.slice(credIDLen);
let COSEPublicKey = buffer;
return {rpIdHash, flagsBuf, flags, counter, counterBuf, aaguid, credID, COSEPublicKey}
}
COSE PublicKey to PKCS
let COSEECDHAtoPKCS = (COSEPublicKey) => {
/*
+------+-------+-------+---------+----------------------------------+
| name | key | label | type | description |
| | type | | | |
+------+-------+-------+---------+----------------------------------+
| crv | 2 | -1 | int / | EC Curve identifier - Taken from |
| | | | tstr | the COSE Curves registry |
| | | | | |
| x | 2 | -2 | bstr | X Coordinate |
| | | | | |
| y | 2 | -3 | bstr / | Y Coordinate |
| | | | bool | |
| | | | | |
| d | 2 | -4 | bstr | Private key |
+------+-------+-------+---------+----------------------------------+
*/
let coseStruct = cbor.decodeAllSync(COSEPublicKey)[0];
let tag = Buffer.from([0x04]);
let x = coseStruct.get(-2);
let y = coseStruct.get(-3);
return Buffer.concat([tag, x, y])
}
Final registration response
let result;
if(webauthnResp.response.attestationObject !== undefined) {
/* This is create cred */
result = utils.verifyAuthenticatorAttestationResponse(webauthnResp);
if(result.verified) {
database[request.session.username].authenticators.push(result.authrInfo);
database[request.session.username].registered = true
}
} else if(webauthnResp.response.authenticatorData !== undefined) {
/* This is get assertion */
} else {
response.json({
'status': 'failed',
'message': 'Can not determine type of response!'
})
}
if(result.verified) {
request.session.loggedIn = true;
response.json({ 'status': 'ok' })
} else {
response.json({
'status': 'failed',
'message': 'Can not authenticate signature!'
})
}
Updating /response endpoint in routes/webauthn.js
For registration:
Get username and name(password field is obsolete, lol)Send them to the serverServer responds with challengeMakeCredentialSend response to the serverCheck that server likes it- PROFIT!
DEMO
For authentication:
- Get username(remove password field)
- Send to the server
- Server responds with challenge
- GetAssertion
- Send response to the server
- Check that server likes it
- PROFIT!
- Take username
- Check that it's exits
- Generate challenge
- Send it to the browser
This time we start with server
/webauthn/login endpoint
router.post('/login', (request, response) => {
if(!request.body || !request.body.username) {
response.json({
'status': 'failed',
'message': 'Request missing username field!'
})
return
}
let username = request.body.username;
if(!database[username] || !database[username].registered) {
response.json({
'status': 'failed',
'message': `User ${username} does not exist!`
})
return
}
let getAssertion = utils.generateServerGetAssertion(database[username].authenticators)
getAssertion.status = 'ok'
request.session.challenge = getAssertion.challenge;
request.session.username = username;
response.json(getAssertion)
})
Save username and challenge
Adding new /login endpoint in routes/webauthn.js
let generateServerGetAssertion = (authenticators) => {
let allowCredentials = [];
for(let authr of authenticators) {
allowCredentials.push({
type: 'public-key',
id: authr.credID,
transports: ['usb', 'nfc', 'ble']
})
}
return {
challenge: randomBase64URLBuffer(32),
allowCredentials: allowCredentials
}
}
generateServerGetAssertion
Back to html...
- Comment login handler in password.auth.js
- Add getAssertionChallenge function to webauthn.auth.js
let getGetAssertionChallenge = (formBody) => {
return fetch('/webauthn/login', {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(formBody)
})
.then((response) => response.json())
.then((response) => {
if(response.status !== 'ok')
throw new Error(`Server responed with error. The message is: ${response.message}`);
return response
})
}
/* Handle for login form submission */
$('#login').submit(function(event) {
event.preventDefault();
let username = this.username.value;
if(!username) {
alert('Username is missing!')
return
}
getGetAssertionChallenge({username})
.then((response) => {
let publicKey = preformatGetAssertReq(response);
return navigator.credentials.get({ publicKey })
})
.then((response) => {
let getAssertionResponse = publicKeyCredentialToJSON(response);
return sendWebAuthnResponse(getAssertionResponse)
})
.then((response) => {
if(response.status === 'ok') {
loadMainContainer()
} else {
alert(`Server responed with error. The message is: ${response.message}`);
}
})
.catch((error) => alert(error))
})
Adding login processor to webauthn.auth.js
} else if(webauthnResp.response.authenticatorData !== undefined) {
/* This is get assertion */
} else {
Back to /response processor in routes/webauthn.js
AssertionVerification
In utils.js there is a method "verifyAuthenticatorAssertionResponse"
let verifyAuthenticatorAssertionResponse = (webAuthnResponse, authenticators) => {
let authr = findAuthr(webAuthnResponse.id, authenticators);
let authenticatorData = base64url.toBuffer(webAuthnResponse.response.authenticatorData);
let response = {'verified': false};
if(authr.fmt === 'fido-u2f') {
let authrDataStruct = parseGetAssertAuthData(authenticatorData);
if(!(authrDataStruct.flags & U2F_USER_PRESENTED))
throw new Error('User was NOT presented durring authentication!');
let clientDataHash = hash(base64url.toBuffer(webAuthnResponse.response.clientDataJSON))
let signatureBase = Buffer.concat([authrDataStruct.rpIdHash, authrDataStruct.flagsBuf, authrDataStruct.counterBuf, clientDataHash]);
let publicKey = ASN1toPEM(base64url.toBuffer(authr.publicKey));
let signature = base64url.toBuffer(webAuthnResponse.response.signature);
response.verified = verifySignature(signature, signatureBase, publicKey)
if(response.verified) {
if(response.counter <= authr.counter)
throw new Error('Authr counter did not increase!');
authr.counter = authrDataStruct.counter
}
}
return response
}
Searching for authenticator specified by rawID
It's a U2F statement
Parsing raw authData buffer
Check that TUP flag is set
Generate signature base
PKCS PubKey to PEM
Verify signature
Check that counter increased
Update counter
authenticatorData parsing
let parseGetAssertAuthData = (buffer) => {
let rpIdHash = buffer.slice(0, 32); buffer = buffer.slice(32);
let flagsBuf = buffer.slice(0, 1); buffer = buffer.slice(1);
let flags = flagsBuf[0];
let counterBuf = buffer.slice(0, 4); buffer = buffer.slice(4);
let counter = counterBuf.readUInt32BE(0);
return {rpIdHash, flagsBuf, flags, counter, counterBuf}
}
} else if(webauthnResp.response.authenticatorData !== undefined) {
/* This is get assertion */
result = utils.verifyAuthenticatorAssertionResponse(webauthnResp, database[request.session.username].authenticators);
} else {
New function to be added to webauthn.auth.js
Updating assertion verification in webauthn.js
DEMO
Thanks you coming
Questions?
WORKSHOP: Authenticating your web like a boss
By FIDO Alliance
WORKSHOP: Authenticating your web like a boss
WebAuthn Workshop
- 56,589