summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorMatthew Miller <matthew@millerti.me>2020-05-25 11:11:48 -0700
committerMatthew Miller <matthew@millerti.me>2020-05-25 11:11:48 -0700
commitce486db665fd805fff59772dfc852ac2d4cb3a43 (patch)
treea727ea2d928d50cc576bd150d08cc4de679e22f5
parent279723fc1cb10e653431a9d7dbe3592f760b3c8b (diff)
Run Prettier over everything
-rw-r--r--example/README.md2
-rw-r--r--example/index.js70
-rw-r--r--example/public/index.html30
-rw-r--r--example/public/login/index.html106
-rw-r--r--example/public/register/index.html102
-rw-r--r--example/public/styles.css8
-rw-r--r--packages/browser/README.md2
-rw-r--r--packages/browser/src/helpers/supportsWebauthn.test.ts10
-rw-r--r--packages/browser/src/helpers/supportsWebauthn.ts3
-rw-r--r--packages/browser/src/helpers/toBase64String.ts4
-rw-r--r--packages/browser/src/index.ts6
-rw-r--r--packages/browser/src/methods/startAssertion.test.ts91
-rw-r--r--packages/browser/src/methods/startAssertion.ts10
-rw-r--r--packages/browser/src/methods/startAttestation.test.ts85
-rw-r--r--packages/browser/src/methods/startAttestation.ts4
-rw-r--r--packages/browser/tsconfig.json2
-rw-r--r--packages/browser/webpack.config.js4
-rw-r--r--packages/server/README.md3
-rw-r--r--packages/server/src/assertion/generateAssertionOptions.test.ts11
-rw-r--r--packages/server/src/assertion/generateAssertionOptions.ts1
-rw-r--r--packages/server/src/assertion/verifyAssertionResponse.test.ts45
-rw-r--r--packages/server/src/assertion/verifyAssertionResponse.ts21
-rw-r--r--packages/server/src/attestation/generateAttestationOptions.test.ts10
-rw-r--r--packages/server/src/attestation/generateAttestationOptions.ts11
-rw-r--r--packages/server/src/attestation/verifications/verifyAndroidSafetyNet.ts16
-rw-r--r--packages/server/src/attestation/verifications/verifyFIDOU2F.ts11
-rw-r--r--packages/server/src/attestation/verifications/verifyNone.ts13
-rw-r--r--packages/server/src/attestation/verifications/verifyPacked.ts54
-rw-r--r--packages/server/src/attestation/verifyAttestationResponse.test.ts56
-rw-r--r--packages/server/src/attestation/verifyAttestationResponse.ts8
-rw-r--r--packages/server/src/helpers/convertCOSEtoPKCS.test.ts1
-rw-r--r--packages/server/src/helpers/convertCOSEtoPKCS.ts3
-rw-r--r--packages/server/src/helpers/decodeAttestationObject.test.ts34
-rw-r--r--packages/server/src/helpers/decodeAttestationObject.ts1
-rw-r--r--packages/server/src/helpers/decodeClientDataJSON.test.ts19
-rw-r--r--packages/server/src/helpers/getCertificateInfo.ts12
-rw-r--r--packages/server/src/helpers/parseAuthenticatorData.ts2
-rw-r--r--packages/server/src/helpers/verifySignature.ts4
-rw-r--r--packages/server/tsconfig.json2
-rw-r--r--packages/typescript-types/README.md1
-rw-r--r--packages/typescript-types/src/index.ts157
-rw-r--r--packages/typescript-types/tsconfig.json2
42 files changed, 512 insertions, 525 deletions
diff --git a/example/README.md b/example/README.md
index 599faa8..1154d77 100644
--- a/example/README.md
+++ b/example/README.md
@@ -8,7 +8,7 @@ A fully-functional reference implementation of [@webauthntine/server](../package
### SSL Certificate
-Websites that wish to leverage WebAuthn *must* be served over HTTPS, **including during development!**
+Websites that wish to leverage WebAuthn _must_ be served over HTTPS, **including during development!**
Here's one technique for setting up SSL for a local dev instance:
diff --git a/example/index.js b/example/index.js
index 1f1c0b8..6cc1408 100644
--- a/example/index.js
+++ b/example/index.js
@@ -28,7 +28,7 @@ app.use(express.json());
* RP ID represents the "scope" of websites on which a authenticator should be usable. The Origin
* represents the expected URL from which an attestation or assertion occurs.
*/
-const rpID = 'dev.yourdomain.com';
+const rpID = 'dev.dontneeda.pw';
const origin = `https://${rpID}`;
/**
* WebAuthn expects you to be able to uniquely identify the user that performs an attestation or
@@ -113,13 +113,9 @@ app.get('/generate-attestation-options', (req, res) => {
const challenge = 'totallyUniqueValueEveryAttestation';
inMemoryUserDeviceDB[loggedInUserId].currentChallenge = challenge;
- res.send(generateAttestationOptions(
- 'WebAuthntine Example',
- rpID,
- challenge,
- loggedInUserId,
- username,
- ));
+ res.send(
+ generateAttestationOptions('WebAuthntine Example', rpID, challenge, loggedInUserId, username),
+ );
});
app.post('/verify-attestation', (req, res) => {
@@ -131,11 +127,7 @@ app.post('/verify-attestation', (req, res) => {
let verification;
try {
- verification = verifyAttestationResponse(
- body,
- expectedChallenge,
- origin,
- );
+ verification = verifyAttestationResponse(body, expectedChallenge, origin);
} catch (error) {
console.error(error);
return res.status(400).send({ error: error.message });
@@ -147,7 +139,7 @@ app.post('/verify-attestation', (req, res) => {
const { base64PublicKey, base64CredentialID, counter } = authenticatorInfo;
const existingDevice = user.devices.find(
- (device) => device.base64CredentialID === base64CredentialID,
+ device => device.base64CredentialID === base64CredentialID,
);
if (!existingDevice) {
@@ -180,10 +172,12 @@ app.get('/generate-assertion-options', (req, res) => {
const challenge = 'totallyUniqueValueEveryAssertion';
inMemoryUserDeviceDB[loggedInUserId].currentChallenge = challenge;
- res.send(generateAssertionOptions(
- challenge,
- user.devices.map(data => data.base64CredentialID),
- ));
+ res.send(
+ generateAssertionOptions(
+ challenge,
+ user.devices.map(data => data.base64CredentialID),
+ ),
+ );
});
app.post('/verify-assertion', (req, res) => {
@@ -195,7 +189,7 @@ app.post('/verify-assertion', (req, res) => {
let dbAuthenticator;
// "Query the DB" here for an authenticator matching `base64CredentialID`
- for(let dev of user.devices) {
+ for (let dev of user.devices) {
if (dev.base64CredentialID === body.base64CredentialID) {
dbAuthenticator = dev;
break;
@@ -204,12 +198,7 @@ app.post('/verify-assertion', (req, res) => {
let verification;
try {
- verification = verifyAssertionResponse(
- body,
- expectedChallenge,
- origin,
- dbAuthenticator,
- );
+ verification = verifyAssertionResponse(body, expectedChallenge, origin, dbAuthenticator);
} catch (error) {
console.error(error);
return res.status(400).send({ error: error.message });
@@ -225,16 +214,21 @@ app.post('/verify-assertion', (req, res) => {
res.send({ verified });
});
-https.createServer({
- /**
- * You'll need to provide a SSL cert and key here because
- * WebAuthn can only be run from HTTPS:// URLs
- *
- * HINT: If you create a `dev` subdomain A-record that points to 127.0.0.1,
- * you can manually generate an HTTPS certificate for it using Let's Encrypt certbot.
- */
- key: fs.readFileSync('./dev.yourdomain.com.key'),
- cert: fs.readFileSync('./dev.yourdomain.com.crt'),
-}, app).listen(port, host, () => {
- console.log(`🚀 Server ready at https://${host}:${port}`);
-});
+https
+ .createServer(
+ {
+ /**
+ * You'll need to provide a SSL cert and key here because
+ * WebAuthn can only be run from HTTPS:// URLs
+ *
+ * HINT: If you create a `dev` subdomain A-record that points to 127.0.0.1,
+ * you can manually generate an HTTPS certificate for it using Let's Encrypt certbot.
+ */
+ key: fs.readFileSync('./dev.yourdomain.com.key'),
+ cert: fs.readFileSync('./dev.yourdomain.com.crt'),
+ },
+ app,
+ )
+ .listen(port, host, () => {
+ console.log(`🚀 Server ready at https://${host}:${port}`);
+ });
diff --git a/example/public/index.html b/example/public/index.html
index 4a42d55..e313ba7 100644
--- a/example/public/index.html
+++ b/example/public/index.html
@@ -1,20 +1,16 @@
<!DOCTYPE html>
<html lang="en">
-<head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <link rel="stylesheet" href="./styles.css" />
- <title>WebAuthntine Example Site</title>
-</head>
-<body>
- <div class="container">
- <h1>WebAuthntine Example Site</h1>
- <p>
- 🚪&nbsp;<a href="/register">Register</a>
- </p>
- <p>
- 🔐&nbsp;<a href="/login">Login</a>
- </p>
- </div>
-</body>
+ <head>
+ <meta charset="UTF-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <link rel="stylesheet" href="./styles.css" />
+ <title>WebAuthntine Example Site</title>
+ </head>
+ <body>
+ <div class="container">
+ <h1>WebAuthntine Example Site</h1>
+ <p>🚪&nbsp;<a href="/register">Register</a></p>
+ <p>🔐&nbsp;<a href="/login">Login</a></p>
+ </div>
+ </body>
</html>
diff --git a/example/public/login/index.html b/example/public/login/index.html
index be4ea71..8652ede 100644
--- a/example/public/login/index.html
+++ b/example/public/login/index.html
@@ -1,62 +1,64 @@
<!DOCTYPE html>
<html lang="en">
-<head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <script src="https://unpkg.com/@webauthntine/browser@0.2.2/dist/webauthntine-browser.min.js"></script>
- <link rel="stylesheet" href="../styles.css" />
- <title>WebAuthntine Example Site | Login</title>
-</head>
-<body>
- <div class="container">
- <p>
- <span>⬅️&nbsp;<a href="/">Go Back</a></span>
- </p>
- <h1>🔐&nbsp;Login</h1>
- <h2>(a.k.a. "Assertion")</h2>
- <button id="btnBegin">Begin Login</button>
- <p id="success"></p>
- <p id="error"></p>
- </div>
- <script>
- const elemBegin = document.getElementById('btnBegin');
- const elemSuccess = document.getElementById('success');
- const elemError = document.getElementById('error');
+ <head>
+ <meta charset="UTF-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <script src="https://unpkg.com/@webauthntine/browser@0.2.2/dist/webauthntine-browser.min.js"></script>
+ <link rel="stylesheet" href="../styles.css" />
+ <title>WebAuthntine Example Site | Login</title>
+ </head>
+ <body>
+ <div class="container">
+ <p>
+ <span>⬅️&nbsp;<a href="/">Go Back</a></span>
+ </p>
+ <h1>🔐&nbsp;Login</h1>
+ <h2>(a.k.a. "Assertion")</h2>
+ <button id="btnBegin">Begin Login</button>
+ <p id="success"></p>
+ <p id="error"></p>
+ </div>
+ <script>
+ const elemBegin = document.getElementById('btnBegin');
+ const elemSuccess = document.getElementById('success');
+ const elemError = document.getElementById('error');
- const { startAssertion } = WebAuthntineBrowser;
+ const { startAssertion } = WebAuthntineBrowser;
- elemBegin.addEventListener('click', (async () => {
- // Reset success/error messages
- elemSuccess.innerHTML = '';
- elemError.innerHTML = '';
+ elemBegin.addEventListener('click', async () => {
+ // Reset success/error messages
+ elemSuccess.innerHTML = '';
+ elemError.innerHTML = '';
- const resp = await fetch('/generate-assertion-options');
+ const resp = await fetch('/generate-assertion-options');
- let asseResp;
- try {
- const opts = await resp.json();
- asseResp = await startAssertion(opts);
- } catch (error) {
- elemError.innerText = error;
- throw new Error(error);
- }
+ let asseResp;
+ try {
+ const opts = await resp.json();
+ asseResp = await startAssertion(opts);
+ } catch (error) {
+ elemError.innerText = error;
+ throw new Error(error);
+ }
- const verificationResp = await fetch('/verify-assertion', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify(asseResp),
- });
+ const verificationResp = await fetch('/verify-assertion', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(asseResp),
+ });
- const verificationJSON = await verificationResp.json();
+ const verificationJSON = await verificationResp.json();
- if (verificationJSON && verificationJSON.verified) {
- elemSuccess.innerHTML = 'Success! <a href="/register">Try to register again?</a>&nbsp;🚪';
- } else {
- elemError.innerHTML = `Oh no, something went wrong! Response: <pre>${JSON.stringify(verificationJSON)}</pre>`;
- }
- }));
- </script>
-</body>
+ if (verificationJSON && verificationJSON.verified) {
+ elemSuccess.innerHTML = 'Success! <a href="/register">Try to register again?</a>&nbsp;🚪';
+ } else {
+ elemError.innerHTML = `Oh no, something went wrong! Response: <pre>${JSON.stringify(
+ verificationJSON,
+ )}</pre>`;
+ }
+ });
+ </script>
+ </body>
</html>
diff --git a/example/public/register/index.html b/example/public/register/index.html
index 583f678..ea4403b 100644
--- a/example/public/register/index.html
+++ b/example/public/register/index.html
@@ -1,60 +1,62 @@
<!DOCTYPE html>
<html lang="en">
-<head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <script src="https://unpkg.com/@webauthntine/browser@0.2.2/dist/webauthntine-browser.min.js"></script>
- <link rel="stylesheet" href="../styles.css" />
- <title>WebAuthntine Example Site | Register</title>
-</head>
-<body>
- <div class="container">
- <p>
- <span>⬅️&nbsp;<a href="/">Go Back</a></span>
- </p>
- <h1>🚪&nbsp;Register</h1>
- <h2>(a.k.a. "Attestation")</h2>
- <button id="btnBegin">Begin Registration</button>
- <p id="success"></p>
- <p id="error"></p>
- </div>
- <script>
- const elemBegin = document.getElementById('btnBegin');
- const elemSuccess = document.getElementById('success');
- const elemError = document.getElementById('error');
+ <head>
+ <meta charset="UTF-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <script src="https://unpkg.com/@webauthntine/browser@0.2.2/dist/webauthntine-browser.min.js"></script>
+ <link rel="stylesheet" href="../styles.css" />
+ <title>WebAuthntine Example Site | Register</title>
+ </head>
+ <body>
+ <div class="container">
+ <p>
+ <span>⬅️&nbsp;<a href="/">Go Back</a></span>
+ </p>
+ <h1>🚪&nbsp;Register</h1>
+ <h2>(a.k.a. "Attestation")</h2>
+ <button id="btnBegin">Begin Registration</button>
+ <p id="success"></p>
+ <p id="error"></p>
+ </div>
+ <script>
+ const elemBegin = document.getElementById('btnBegin');
+ const elemSuccess = document.getElementById('success');
+ const elemError = document.getElementById('error');
- const { startAttestation } = WebAuthntineBrowser;
+ const { startAttestation } = WebAuthntineBrowser;
- elemBegin.addEventListener('click', (async () => {
- // Reset success/error messages
- elemSuccess.innerHTML = '';
- elemError.innerHTML = '';
+ elemBegin.addEventListener('click', async () => {
+ // Reset success/error messages
+ elemSuccess.innerHTML = '';
+ elemError.innerHTML = '';
- const resp = await fetch('/generate-attestation-options');
+ const resp = await fetch('/generate-attestation-options');
- let attResp;
- try {
- attResp = await startAttestation(await resp.json());
- } catch (error) {
- elemError.innerText = error;
- }
+ let attResp;
+ try {
+ attResp = await startAttestation(await resp.json());
+ } catch (error) {
+ elemError.innerText = error;
+ }
- const verificationResp = await fetch('/verify-attestation', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify(attResp),
- });
+ const verificationResp = await fetch('/verify-attestation', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(attResp),
+ });
- const verificationJSON = await verificationResp.json();
+ const verificationJSON = await verificationResp.json();
- if (verificationJSON && verificationJSON.verified) {
- elemSuccess.innerHTML = 'Success! <a href="/login">Now try to log in</a>&nbsp;🔐';
- } else {
- elemError.innerHTML = `Oh no, something went wrong! Response: <pre>${JSON.stringify(verificationJSON)}</pre>`;
- }
- }));
- </script>
-</body>
+ if (verificationJSON && verificationJSON.verified) {
+ elemSuccess.innerHTML = 'Success! <a href="/login">Now try to log in</a>&nbsp;🔐';
+ } else {
+ elemError.innerHTML = `Oh no, something went wrong! Response: <pre>${JSON.stringify(
+ verificationJSON,
+ )}</pre>`;
+ }
+ });
+ </script>
+ </body>
</html>
diff --git a/example/public/styles.css b/example/public/styles.css
index 3eeeb7b..f99b391 100644
--- a/example/public/styles.css
+++ b/example/public/styles.css
@@ -10,7 +10,7 @@ body {
justify-content: center;
align-items: center;
height: 100vh;
- background: #F7F7F7;
+ background: #f7f7f7;
}
h1 {
@@ -28,11 +28,11 @@ button {
}
button:active {
- background: #EEEEEE;
+ background: #eeeeee;
}
button:hover {
- background: #EFEFEF;
+ background: #efefef;
}
.container {
@@ -46,5 +46,5 @@ button:hover {
}
#success {
- color: #11A000;
+ color: #11a000;
}
diff --git a/packages/browser/README.md b/packages/browser/README.md
index b1da211..df22dbd 100644
--- a/packages/browser/README.md
+++ b/packages/browser/README.md
@@ -1,5 +1,7 @@
<!-- omit in toc -->
+
# @webauthntine/browser
+
![WebAuthn](https://img.shields.io/badge/WebAuthn-Simplified-blueviolet?style=for-the-badge&logo=WebAuthn)
[![npm (scoped)](https://img.shields.io/npm/v/@webauthntine/browser?style=for-the-badge&logo=npm)](https://www.npmjs.com/package/@webauthntine/browser)
![Browser Support](https://img.shields.io/badge/Browser-ES2018+-brightgreen?style=for-the-badge&logo=Mozilla+Firefox)
diff --git a/packages/browser/src/helpers/supportsWebauthn.test.ts b/packages/browser/src/helpers/supportsWebauthn.test.ts
index f6b8a8e..6eb42c9 100644
--- a/packages/browser/src/helpers/supportsWebauthn.test.ts
+++ b/packages/browser/src/helpers/supportsWebauthn.test.ts
@@ -1,4 +1,4 @@
-import supportsWebauthn from './supportsWebauthn'
+import supportsWebauthn from './supportsWebauthn';
beforeEach(() => {
// @ts-ignore 2741
@@ -17,12 +17,12 @@ test('should return false when browser does not support WebAuthn', () => {
test('should return false when window is undefined', () => {
// Make window undefined as it is in node environments.
// @ts-expect-error
- const windowSpy = jest.spyOn(global, "window", "get");
+ const windowSpy = jest.spyOn(global, 'window', 'get');
windowSpy.mockImplementation(() => undefined);
- expect(window).toBe(undefined)
+ expect(window).toBe(undefined);
expect(supportsWebauthn()).toBe(false);
// Restore original window value.
- windowSpy.mockRestore()
-})
+ windowSpy.mockRestore();
+});
diff --git a/packages/browser/src/helpers/supportsWebauthn.ts b/packages/browser/src/helpers/supportsWebauthn.ts
index 605bb67..b572080 100644
--- a/packages/browser/src/helpers/supportsWebauthn.ts
+++ b/packages/browser/src/helpers/supportsWebauthn.ts
@@ -3,7 +3,6 @@
*/
export default function supportsWebauthn(): boolean {
return (
- window?.PublicKeyCredential !== undefined
- && typeof window.PublicKeyCredential === 'function'
+ window?.PublicKeyCredential !== undefined && typeof window.PublicKeyCredential === 'function'
);
}
diff --git a/packages/browser/src/helpers/toBase64String.ts b/packages/browser/src/helpers/toBase64String.ts
index 9c949be..3178695 100644
--- a/packages/browser/src/helpers/toBase64String.ts
+++ b/packages/browser/src/helpers/toBase64String.ts
@@ -2,7 +2,5 @@ import base64js from 'base64-js';
export default function toBase64String(buffer: ArrayBuffer): string {
// TODO: Make sure converting buffer to Uint8Array() is correct
- return base64js.fromByteArray(new Uint8Array(buffer))
- .replace(/\+/g, "-")
- .replace(/\//g, "_");
+ return base64js.fromByteArray(new Uint8Array(buffer)).replace(/\+/g, '-').replace(/\//g, '_');
}
diff --git a/packages/browser/src/index.ts b/packages/browser/src/index.ts
index 38ce91b..18f5944 100644
--- a/packages/browser/src/index.ts
+++ b/packages/browser/src/index.ts
@@ -2,8 +2,4 @@ import startAttestation from './methods/startAttestation';
import startAssertion from './methods/startAssertion';
import supportsWebauthn from './helpers/supportsWebauthn';
-export {
- startAttestation,
- startAssertion,
- supportsWebauthn,
-};
+export { startAttestation, startAssertion, supportsWebauthn };
diff --git a/packages/browser/src/methods/startAssertion.test.ts b/packages/browser/src/methods/startAssertion.test.ts
index 259400c..2287b49 100644
--- a/packages/browser/src/methods/startAssertion.test.ts
+++ b/packages/browser/src/methods/startAssertion.test.ts
@@ -1,6 +1,9 @@
import base64js from 'base64-js';
-import { AssertionCredential, PublicKeyCredentialRequestOptionsJSON } from '@webauthntine/typescript-types';
+import {
+ AssertionCredential,
+ PublicKeyCredentialRequestOptionsJSON,
+} from '@webauthntine/typescript-types';
import toUint8Array from '../helpers/toUint8Array';
import supportsWebauthn from '../helpers/supportsWebauthn';
@@ -10,8 +13,8 @@ import startAssertion from './startAssertion';
jest.mock('../helpers/supportsWebauthn');
-const mockNavigatorGet = (window.navigator.credentials.get as jest.Mock);
-const mockSupportsWebauthn = (supportsWebauthn as jest.Mock);
+const mockNavigatorGet = window.navigator.credentials.get as jest.Mock;
+const mockSupportsWebauthn = supportsWebauthn as jest.Mock;
const mockAuthenticatorData = toBase64String(toUint8Array('mockAuthenticatorData'));
const mockClientDataJSON = toBase64String(toUint8Array('mockClientDataJSON'));
@@ -21,11 +24,13 @@ const mockUserHandle = toBase64String(toUint8Array('mockUserHandle'));
const goodOpts1: PublicKeyCredentialRequestOptionsJSON = {
publicKey: {
challenge: 'fizz',
- allowCredentials: [{
- id: 'abcdefgfdnsdfunguisdfgs',
- type: 'public-key',
- transports: ['nfc'],
- }],
+ allowCredentials: [
+ {
+ id: 'abcdefgfdnsdfunguisdfgs',
+ type: 'public-key',
+ transports: ['nfc'],
+ },
+ ],
timeout: 1,
},
};
@@ -35,15 +40,17 @@ beforeEach(() => {
mockSupportsWebauthn.mockReset();
});
-test('should convert options before passing to navigator.credentials.get(...)', async (done) => {
+test('should convert options before passing to navigator.credentials.get(...)', async done => {
mockSupportsWebauthn.mockReturnValue(true);
// Stub out a response so the method won't throw
- mockNavigatorGet.mockImplementation((): Promise<any> => {
- return new Promise((resolve) => {
- resolve({ response: {} });
- });
- });
+ mockNavigatorGet.mockImplementation(
+ (): Promise<any> => {
+ return new Promise(resolve => {
+ resolve({ response: {} });
+ });
+ },
+ );
await startAssertion(goodOpts1);
@@ -57,27 +64,29 @@ test('should convert options before passing to navigator.credentials.get(...)',
done();
});
-test('should return base64-encoded response values', async (done) => {
+test('should return base64-encoded response values', async done => {
mockSupportsWebauthn.mockReturnValue(true);
const credentialID = 'foobar';
- mockNavigatorGet.mockImplementation((): Promise<AssertionCredential> => {
- return new Promise((resolve) => {
- resolve({
- id: 'foobar',
- rawId: toUint8Array('foobar'),
- response: {
- authenticatorData: base64js.toByteArray(mockAuthenticatorData),
- clientDataJSON: base64js.toByteArray(mockClientDataJSON),
- signature: base64js.toByteArray(mockSignature),
- userHandle: base64js.toByteArray(mockUserHandle),
- },
- getClientExtensionResults: () => ({}),
- type: 'webauthn.get',
+ mockNavigatorGet.mockImplementation(
+ (): Promise<AssertionCredential> => {
+ return new Promise(resolve => {
+ resolve({
+ id: 'foobar',
+ rawId: toUint8Array('foobar'),
+ response: {
+ authenticatorData: base64js.toByteArray(mockAuthenticatorData),
+ clientDataJSON: base64js.toByteArray(mockClientDataJSON),
+ signature: base64js.toByteArray(mockSignature),
+ userHandle: base64js.toByteArray(mockUserHandle),
+ },
+ getClientExtensionResults: () => ({}),
+ type: 'webauthn.get',
+ });
});
- });
- });
+ },
+ );
const response = await startAssertion(goodOpts1);
@@ -90,24 +99,28 @@ test('should return base64-encoded response values', async (done) => {
});
done();
-})
+});
-test('should throw error if WebAuthn isn\'t supported', async (done) => {
+test("should throw error if WebAuthn isn't supported", async done => {
mockSupportsWebauthn.mockReturnValue(false);
- await expect(startAssertion(goodOpts1)).rejects.toThrow('WebAuthn is not supported in this browser');
+ await expect(startAssertion(goodOpts1)).rejects.toThrow(
+ 'WebAuthn is not supported in this browser',
+ );
done();
});
-test('should throw error if assertion is cancelled for some reason', async (done) => {
+test('should throw error if assertion is cancelled for some reason', async done => {
mockSupportsWebauthn.mockReturnValue(true);
- mockNavigatorGet.mockImplementation((): Promise<null> => {
- return new Promise((resolve) => {
- resolve(null);
- });
- });
+ mockNavigatorGet.mockImplementation(
+ (): Promise<null> => {
+ return new Promise(resolve => {
+ resolve(null);
+ });
+ },
+ );
await expect(startAssertion(goodOpts1)).rejects.toThrow('Assertion was not completed');
diff --git a/packages/browser/src/methods/startAssertion.ts b/packages/browser/src/methods/startAssertion.ts
index 0733194..36c7194 100644
--- a/packages/browser/src/methods/startAssertion.ts
+++ b/packages/browser/src/methods/startAssertion.ts
@@ -15,7 +15,7 @@ import supportsWebauthn from '../helpers/supportsWebauthn';
* @param requestOptionsJSON Output from @webauthntine/server's generateAssertionOptions(...)
*/
export default async function startAssertion(
- requestOptionsJSON: PublicKeyCredentialRequestOptionsJSON
+ requestOptionsJSON: PublicKeyCredentialRequestOptionsJSON,
): Promise<AuthenticatorAssertionResponseJSON> {
if (!supportsWebauthn()) {
throw new Error('WebAuthn is not supported in this browser');
@@ -25,16 +25,16 @@ export default async function startAssertion(
const publicKey: PublicKeyCredentialRequestOptions = {
...requestOptionsJSON.publicKey,
challenge: toUint8Array(requestOptionsJSON.publicKey.challenge),
- allowCredentials: requestOptionsJSON.publicKey.allowCredentials.map((cred) => {
+ allowCredentials: requestOptionsJSON.publicKey.allowCredentials.map(cred => {
// Make sure the credential ID length is a multiple of 4
- const padLength = 4 - cred.id.length % 4;
+ const padLength = 4 - (cred.id.length % 4);
let id = cred.id.padEnd(cred.id.length + padLength, '=');
return {
...cred,
id: base64js.toByteArray(id),
};
- })
+ }),
};
// Wait for the user to complete assertion
@@ -44,7 +44,7 @@ export default async function startAssertion(
throw new Error('Assertion was not completed');
}
- const { response } = (credential as AssertionCredential);
+ const { response } = credential as AssertionCredential;
let base64UserHandle = undefined;
if (response.userHandle) {
diff --git a/packages/browser/src/methods/startAttestation.test.ts b/packages/browser/src/methods/startAttestation.test.ts
index 0efec48..539ffe5 100644
--- a/packages/browser/src/methods/startAttestation.test.ts
+++ b/packages/browser/src/methods/startAttestation.test.ts
@@ -1,6 +1,9 @@
import base64js from 'base64-js';
-import { AttestationCredential, PublicKeyCredentialCreationOptionsJSON } from '@webauthntine/typescript-types';
+import {
+ AttestationCredential,
+ PublicKeyCredentialCreationOptionsJSON,
+} from '@webauthntine/typescript-types';
import toUint8Array from '../helpers/toUint8Array';
import supportsWebauthn from '../helpers/supportsWebauthn';
@@ -9,8 +12,8 @@ import startAttestation from './startAttestation';
jest.mock('../helpers/supportsWebauthn');
-const mockNavigatorCreate = (window.navigator.credentials.create as jest.Mock);
-const mockSupportsWebauthn = (supportsWebauthn as jest.Mock);
+const mockNavigatorCreate = window.navigator.credentials.create as jest.Mock;
+const mockSupportsWebauthn = supportsWebauthn as jest.Mock;
const mockAttestationObject = 'mockAtte';
const mockClientDataJSON = 'mockClie';
@@ -19,10 +22,12 @@ const goodOpts1: PublicKeyCredentialCreationOptionsJSON = {
publicKey: {
challenge: 'fizz',
attestation: 'direct',
- pubKeyCredParams: [{
- alg: -7,
- type: "public-key",
- }],
+ pubKeyCredParams: [
+ {
+ alg: -7,
+ type: 'public-key',
+ },
+ ],
rp: {
id: '1234',
name: 'webauthntine',
@@ -41,15 +46,17 @@ beforeEach(() => {
mockSupportsWebauthn.mockReset();
});
-test('should convert options before passing to navigator.credentials.create(...)', async (done) => {
+test('should convert options before passing to navigator.credentials.create(...)', async done => {
mockSupportsWebauthn.mockReturnValue(true);
// Stub out a response so the method won't throw
- mockNavigatorCreate.mockImplementation((): Promise<any> => {
- return new Promise((resolve) => {
- resolve({ response: {} });
- });
- });
+ mockNavigatorCreate.mockImplementation(
+ (): Promise<any> => {
+ return new Promise(resolve => {
+ resolve({ response: {} });
+ });
+ },
+ );
await startAttestation(goodOpts1);
@@ -61,23 +68,25 @@ test('should convert options before passing to navigator.credentials.create(...)
done();
});
-test('should return base64-encoded response values', async (done) => {
+test('should return base64-encoded response values', async done => {
mockSupportsWebauthn.mockReturnValue(true);
- mockNavigatorCreate.mockImplementation((): Promise<AttestationCredential> => {
- return new Promise((resolve) => {
- resolve({
- id: 'foobar',
- rawId: toUint8Array('foobar'),
- response: {
- attestationObject: base64js.toByteArray(mockAttestationObject),
- clientDataJSON: base64js.toByteArray(mockClientDataJSON),
- },
- getClientExtensionResults: () => ({}),
- type: 'webauthn.create',
+ mockNavigatorCreate.mockImplementation(
+ (): Promise<AttestationCredential> => {
+ return new Promise(resolve => {
+ resolve({
+ id: 'foobar',
+ rawId: toUint8Array('foobar'),
+ response: {
+ attestationObject: base64js.toByteArray(mockAttestationObject),
+ clientDataJSON: base64js.toByteArray(mockClientDataJSON),
+ },
+ getClientExtensionResults: () => ({}),
+ type: 'webauthn.create',
+ });
});
- });
- });
+ },
+ );
const response = await startAttestation(goodOpts1);
@@ -87,24 +96,28 @@ test('should return base64-encoded response values', async (done) => {
});
done();
-})
+});
-test('should throw error if WebAuthn isn\'t supported', async (done) => {
+test("should throw error if WebAuthn isn't supported", async done => {
mockSupportsWebauthn.mockReturnValue(false);
- await expect(startAttestation(goodOpts1)).rejects.toThrow('WebAuthn is not supported in this browser');
+ await expect(startAttestation(goodOpts1)).rejects.toThrow(
+ 'WebAuthn is not supported in this browser',
+ );
done();
});
-test('should throw error if attestation is cancelled for some reason', async (done) => {
+test('should throw error if attestation is cancelled for some reason', async done => {
mockSupportsWebauthn.mockReturnValue(true);
- mockNavigatorCreate.mockImplementation((): Promise<null> => {
- return new Promise((resolve) => {
- resolve(null);
- });
- });
+ mockNavigatorCreate.mockImplementation(
+ (): Promise<null> => {
+ return new Promise(resolve => {
+ resolve(null);
+ });
+ },
+ );
await expect(startAttestation(goodOpts1)).rejects.toThrow('Attestation was not completed');
diff --git a/packages/browser/src/methods/startAttestation.ts b/packages/browser/src/methods/startAttestation.ts
index 1a4b13d..c095670 100644
--- a/packages/browser/src/methods/startAttestation.ts
+++ b/packages/browser/src/methods/startAttestation.ts
@@ -14,7 +14,7 @@ import supportsWebauthn from '../helpers/supportsWebauthn';
* @param creationOptionsJSON Output from @webauthntine/server's generateAttestationOptions(...)
*/
export default async function startAttestation(
- creationOptionsJSON: PublicKeyCredentialCreationOptionsJSON
+ creationOptionsJSON: PublicKeyCredentialCreationOptionsJSON,
): Promise<AuthenticatorAttestationResponseJSON> {
if (!supportsWebauthn()) {
throw new Error('WebAuthn is not supported in this browser');
@@ -37,7 +37,7 @@ export default async function startAttestation(
throw new Error('Attestation was not completed');
}
- const { response } = (credential as AttestationCredential);
+ const { response } = credential as AttestationCredential;
// Convert values to base64 to make it easier to send back to the server
return {
diff --git a/packages/browser/tsconfig.json b/packages/browser/tsconfig.json
index 9ddf207..349e40c 100644
--- a/packages/browser/tsconfig.json
+++ b/packages/browser/tsconfig.json
@@ -2,6 +2,6 @@
"extends": "../../tsconfig.json",
"compilerOptions": {
"baseUrl": "./src",
- "outDir": "./dist",
+ "outDir": "./dist"
}
}
diff --git a/packages/browser/webpack.config.js b/packages/browser/webpack.config.js
index c70d050..54e8f83 100644
--- a/packages/browser/webpack.config.js
+++ b/packages/browser/webpack.config.js
@@ -13,7 +13,7 @@ module.exports = {
test: /.ts$/,
use: 'ts-loader',
exclude: /node_modules/,
- }
+ },
],
},
resolve: {
@@ -38,6 +38,6 @@ module.exports = {
tag: 'Version: {version} - {date}',
},
},
- })
+ }),
],
};
diff --git a/packages/server/README.md b/packages/server/README.md
index 4610926..83bc845 100644
--- a/packages/server/README.md
+++ b/packages/server/README.md
@@ -1,5 +1,7 @@
<!-- omit in toc -->
+
# @webauthntine/server
+
![WebAuthn](https://img.shields.io/badge/WebAuthn-Simplified-blueviolet?style=for-the-badge&logo=WebAuthn)
[![npm (scoped)](https://img.shields.io/npm/v/@webauthntine/server?style=for-the-badge&logo=npm)](https://www.npmjs.com/package/@webauthntine/server)
![node-lts (scoped)](https://img.shields.io/node/v/@webauthntine/server?style=for-the-badge&logo=Node.js)
@@ -23,7 +25,6 @@ It can then be imported into a Node project as usual:
import WebAuthntineServer from '@webauthntine/server';
// CommonJS
const WebAuthntineServer = require('@webauthntine/server');
-
```
## Usage - Coming Soon
diff --git a/packages/server/src/assertion/generateAssertionOptions.test.ts b/packages/server/src/assertion/generateAssertionOptions.test.ts
index f979c74..c125f48 100644
--- a/packages/server/src/assertion/generateAssertionOptions.test.ts
+++ b/packages/server/src/assertion/generateAssertionOptions.test.ts
@@ -33,13 +33,10 @@ test('should generate credential request options suitable for sending via JSON',
});
test('defaults to 60 seconds if no timeout is specified', () => {
- const options = generateAssertionOptions(
- 'totallyrandomvalue',
- [
- Buffer.from('1234', 'ascii').toString('base64'),
- Buffer.from('5678', 'ascii').toString('base64'),
- ],
- );
+ const options = generateAssertionOptions('totallyrandomvalue', [
+ Buffer.from('1234', 'ascii').toString('base64'),
+ Buffer.from('5678', 'ascii').toString('base64'),
+ ]);
expect(options.publicKey.timeout).toEqual(60000);
});
diff --git a/packages/server/src/assertion/generateAssertionOptions.ts b/packages/server/src/assertion/generateAssertionOptions.ts
index e6107d6..5dc4c84 100644
--- a/packages/server/src/assertion/generateAssertionOptions.ts
+++ b/packages/server/src/assertion/generateAssertionOptions.ts
@@ -1,6 +1,5 @@
import { PublicKeyCredentialRequestOptionsJSON } from '@webauthntine/typescript-types';
-
/**
* Prepare a value to pass into navigator.credentials.get(...) for authenticator "login"
*
diff --git a/packages/server/src/assertion/verifyAssertionResponse.test.ts b/packages/server/src/assertion/verifyAssertionResponse.test.ts
index 293a4a6..5895b66 100644
--- a/packages/server/src/assertion/verifyAssertionResponse.test.ts
+++ b/packages/server/src/assertion/verifyAssertionResponse.test.ts
@@ -47,7 +47,9 @@ test('should return authenticator info after verification', () => {
);
expect(verification.authenticatorInfo.counter).toEqual(144);
- expect(verification.authenticatorInfo.base64CredentialID).toEqual(authenticator.base64CredentialID);
+ expect(verification.authenticatorInfo.base64CredentialID).toEqual(
+ authenticator.base64CredentialID,
+ );
});
test('should throw when response challenge is not expected value', () => {
@@ -81,12 +83,7 @@ test('should throw when assertion type is not webauthn.create', () => {
});
expect(() => {
- verifyAssertionResponse(
- assertionResponse,
- assertionChallenge,
- assertionOrigin,
- authenticator,
- );
+ verifyAssertionResponse(assertionResponse, assertionChallenge, assertionOrigin, authenticator);
}).toThrow(/assertion type/i);
});
@@ -96,12 +93,7 @@ test('should throw error if user was not present', () => {
});
expect(() => {
- verifyAssertionResponse(
- assertionResponse,
- assertionChallenge,
- assertionOrigin,
- authenticator,
- );
+ verifyAssertionResponse(assertionResponse, assertionChallenge, assertionOrigin, authenticator);
}).toThrow(/not present/i);
});
@@ -114,32 +106,29 @@ test('should throw error if previous counter value is not less than in response'
};
expect(() => {
- verifyAssertionResponse(
- assertionResponse,
- assertionChallenge,
- assertionOrigin,
- badDevice,
- );
+ verifyAssertionResponse(assertionResponse, assertionChallenge, assertionOrigin, badDevice);
}).toThrow(/counter value/i);
});
const assertionResponse = {
- base64CredentialID: 'KEbWNCc7NgaYnUyrNeFGX9_3Y-8oJ3KwzjnaiD1d1LVTxR7v3CaKfCz2Vy_g_MHSh7yJ8yL0Px' +
- 'g6jo_o0hYiew',
+ base64CredentialID:
+ 'KEbWNCc7NgaYnUyrNeFGX9_3Y-8oJ3KwzjnaiD1d1LVTxR7v3CaKfCz2Vy_g_MHSh7yJ8yL0Px' + 'g6jo_o0hYiew',
base64AuthenticatorData: 'PdxHEOnAiLIp26idVjIguzn3Ipr_RlsKZWsa-5qK-KABAAAAkA==',
- base64ClientDataJSON: 'eyJjaGFsbGVuZ2UiOiJkRzkwWVd4c2VWVnVhWEYxWlZaaGJIVmxSWFpsY25sVWFXMWwiLCJj' +
+ base64ClientDataJSON:
+ 'eyJjaGFsbGVuZ2UiOiJkRzkwWVd4c2VWVnVhWEYxWlZaaGJIVmxSWFpsY25sVWFXMWwiLCJj' +
'bGllbnRFeHRlbnNpb25zIjp7fSwiaGFzaEFsZ29yaXRobSI6IlNIQS0yNTYiLCJvcmlnaW4iOiJodHRwczovL2Rldi5k' +
'b250bmVlZGEucHciLCJ0eXBlIjoid2ViYXV0aG4uZ2V0In0=',
- base64Signature: 'MEUCIQDYXBOpCWSWq2Ll4558GJKD2RoWg958lvJSB_GdeokxogIgWuEVQ7ee6AswQY0OsuQ6y8Ks6' +
- 'jhd45bDx92wjXKs900='
+ base64Signature:
+ 'MEUCIQDYXBOpCWSWq2Ll4558GJKD2RoWg958lvJSB_GdeokxogIgWuEVQ7ee6AswQY0OsuQ6y8Ks6' +
+ 'jhd45bDx92wjXKs900=',
};
const assertionChallenge = 'totallyUniqueValueEveryTime';
const assertionOrigin = 'https://dev.dontneeda.pw';
const authenticator = {
- base64PublicKey: 'BIheFp-u6GvFT2LNGovf3ZrT0iFVBsA_76rRysxRG9A18WGeA6hPmnab0HAViUYVRkwTNcN77QBf_' +
- 'RR0dv3lIvQ',
- base64CredentialID: 'KEbWNCc7NgaYnUyrNeFGX9_3Y-8oJ3KwzjnaiD1d1LVTxR7v3CaKfCz2Vy_g_MHSh7yJ8yL0Px' +
- 'g6jo_o0hYiew',
+ base64PublicKey:
+ 'BIheFp-u6GvFT2LNGovf3ZrT0iFVBsA_76rRysxRG9A18WGeA6hPmnab0HAViUYVRkwTNcN77QBf_' + 'RR0dv3lIvQ',
+ base64CredentialID:
+ 'KEbWNCc7NgaYnUyrNeFGX9_3Y-8oJ3KwzjnaiD1d1LVTxR7v3CaKfCz2Vy_g_MHSh7yJ8yL0Px' + 'g6jo_o0hYiew',
counter: 0,
};
diff --git a/packages/server/src/assertion/verifyAssertionResponse.ts b/packages/server/src/assertion/verifyAssertionResponse.ts
index 86f7a6b..6f840c8 100644
--- a/packages/server/src/assertion/verifyAssertionResponse.ts
+++ b/packages/server/src/assertion/verifyAssertionResponse.ts
@@ -3,9 +3,9 @@ import {
AuthenticatorAssertionResponseJSON,
AuthenticatorDevice,
VerifiedAssertion,
-} from "@webauthntine/typescript-types";
+} from '@webauthntine/typescript-types';
-import decodeClientDataJSON from "@helpers/decodeClientDataJSON";
+import decodeClientDataJSON from '@helpers/decodeClientDataJSON';
import toHash from '@helpers/toHash';
import convertASN1toPEM from '@helpers/convertASN1toPEM';
@@ -37,7 +37,7 @@ export default function verifyAssertionResponse(
if (challenge !== expectedChallenge) {
throw new Error(
- `Unexpected assertion challenge "${challenge}", expected "${expectedChallenge}"`
+ `Unexpected assertion challenge "${challenge}", expected "${expectedChallenge}"`,
);
}
@@ -55,7 +55,7 @@ export default function verifyAssertionResponse(
const authDataStruct = parseAuthenticatorData(authDataBuffer);
const { flags, counter } = authDataStruct;
- if (!(flags.up)) {
+ if (!flags.up) {
throw new Error('User not present during assertion');
}
@@ -69,19 +69,10 @@ export default function verifyAssertionResponse(
);
}
- const {
- rpIdHash,
- flagsBuf,
- counterBuf,
- } = authDataStruct;
+ const { rpIdHash, flagsBuf, counterBuf } = authDataStruct;
const clientDataHash = toHash(base64url.toBuffer(base64ClientDataJSON));
- const signatureBase = Buffer.concat([
- rpIdHash,
- flagsBuf,
- counterBuf,
- clientDataHash,
- ]);
+ const signatureBase = Buffer.concat([rpIdHash, flagsBuf, counterBuf, clientDataHash]);
const publicKey = convertASN1toPEM(base64url.toBuffer(authenticator.base64PublicKey));
const signature = base64url.toBuffer(base64Signature);
diff --git a/packages/server/src/attestation/generateAttestationOptions.test.ts b/packages/server/src/attestation/generateAttestationOptions.test.ts
index 4c6e605..d3d49c7 100644
--- a/packages/server/src/attestation/generateAttestationOptions.test.ts
+++ b/packages/server/src/attestation/generateAttestationOptions.test.ts
@@ -31,10 +31,12 @@ test('should generate credential request options suitable for sending via JSON',
name: username,
displayName: username,
},
- pubKeyCredParams: [{
- alg: -7,
- type: 'public-key',
- }],
+ pubKeyCredParams: [
+ {
+ alg: -7,
+ type: 'public-key',
+ },
+ ],
timeout,
attestation: attestationType,
},
diff --git a/packages/server/src/attestation/generateAttestationOptions.ts b/packages/server/src/attestation/generateAttestationOptions.ts
index 68cac94..6d1f7d9 100644
--- a/packages/server/src/attestation/generateAttestationOptions.ts
+++ b/packages/server/src/attestation/generateAttestationOptions.ts
@@ -1,6 +1,5 @@
import { PublicKeyCredentialCreationOptionsJSON } from '@webauthntine/typescript-types';
-
/**
* Prepare a value to pass into navigator.credentials.create(...) for authenticator "registration"
*
@@ -35,10 +34,12 @@ export default function generateAttestationOptions(
name: username,
displayName: username,
},
- pubKeyCredParams: [{
- alg: -7,
- type: 'public-key',
- }],
+ pubKeyCredParams: [
+ {
+ alg: -7,
+ type: 'public-key',
+ },
+ ],
timeout,
attestation: attestationType,
},
diff --git a/packages/server/src/attestation/verifications/verifyAndroidSafetyNet.ts b/packages/server/src/attestation/verifications/verifyAndroidSafetyNet.ts
index 5705065..110e665 100644
--- a/packages/server/src/attestation/verifications/verifyAndroidSafetyNet.ts
+++ b/packages/server/src/attestation/verifications/verifyAndroidSafetyNet.ts
@@ -5,15 +5,14 @@ import {
SafetyNetJWTHeader,
SafetyNetJWTPayload,
SafetyNetJWTSignature,
-} from "@webauthntine/typescript-types";
+} from '@webauthntine/typescript-types';
-import toHash from "@helpers/toHash";
+import toHash from '@helpers/toHash';
import verifySignature from '@helpers/verifySignature';
import convertCOSEtoPKCS from '@helpers/convertCOSEtoPKCS';
import getCertificateInfo from '@helpers/getCertificateInfo';
import parseAuthenticatorData from '@helpers/parseAuthenticatorData';
-
/**
* Verify an attestation response with fmt 'android-safetynet'
*/
@@ -55,10 +54,7 @@ export default function verifyAttestationAndroidSafetyNet(
const { nonce, ctsProfileMatch } = PAYLOAD;
const clientDataHash = toHash(base64url.toBuffer(base64ClientDataJSON));
- const nonceBase = Buffer.concat([
- authData,
- clientDataHash,
- ]);
+ const nonceBase = Buffer.concat([authData, clientDataHash]);
const nonceBuffer = toHash(nonceBase);
const expectedNonce = nonceBuffer.toString('base64');
@@ -77,7 +73,7 @@ export default function verifyAttestationAndroidSafetyNet(
* START Verify Header
*/
// Generate an array of certs constituting a full certificate chain
- const fullpathCert = HEADER.x5c.concat([GlobalSignRootCAR2]).map((cert) => {
+ const fullpathCert = HEADER.x5c.concat([GlobalSignRootCAR2]).map(cert => {
let pem = '';
// Take a string of characters and chop them up into 64-char lines (just like a PEM cert)
for (let i = 0; i < cert.length; i += 64) {
@@ -118,7 +114,6 @@ export default function verifyAttestationAndroidSafetyNet(
* END Verify Signature
*/
-
if (toReturn.verified) {
toReturn.userVerified = flags.uv;
@@ -141,7 +136,8 @@ export default function verifyAttestationAndroidSafetyNet(
*
* The certificate is valid until Dec 15, 2021
*/
-const GlobalSignRootCAR2 = 'MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4GA1UEC' +
+const GlobalSignRootCAR2 =
+ 'MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4GA1UEC' +
'xMXR2xvYmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhc' +
'NMDYxMjE1MDgwMDAwWhcNMjExMjE1MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMjETMBEGA' +
'1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKb' +
diff --git a/packages/server/src/attestation/verifications/verifyFIDOU2F.ts b/packages/server/src/attestation/verifications/verifyFIDOU2F.ts
index 6768abc..c12dc4a 100644
--- a/packages/server/src/attestation/verifications/verifyFIDOU2F.ts
+++ b/packages/server/src/attestation/verifications/verifyFIDOU2F.ts
@@ -7,7 +7,6 @@ import convertASN1toPEM from '@helpers/convertASN1toPEM';
import verifySignature from '@helpers/verifySignature';
import parseAuthenticatorData from '@helpers/parseAuthenticatorData';
-
/**
* Verify an attestation response with fmt 'fido-u2f'
*/
@@ -18,15 +17,9 @@ export default function verifyAttestationFIDOU2F(
const { fmt, authData, attStmt } = attestationObject;
const authDataStruct = parseAuthenticatorData(authData);
- const {
- flags,
- COSEPublicKey,
- rpIdHash,
- credentialID,
- counter,
- } = authDataStruct;
+ const { flags, COSEPublicKey, rpIdHash, credentialID, counter } = authDataStruct;
- if (!(flags.up)) {
+ if (!flags.up) {
throw new Error('User was NOT present during authentication (FIDOU2F)');
}
diff --git a/packages/server/src/attestation/verifications/verifyNone.ts b/packages/server/src/attestation/verifications/verifyNone.ts
index e820ee9..1aeafd0 100644
--- a/packages/server/src/attestation/verifications/verifyNone.ts
+++ b/packages/server/src/attestation/verifications/verifyNone.ts
@@ -1,11 +1,9 @@
import base64url from 'base64url';
-import { AttestationObject, VerifiedAttestation } from "@webauthntine/typescript-types";
+import { AttestationObject, VerifiedAttestation } from '@webauthntine/typescript-types';
-import convertCOSEtoPKCS from "@helpers/convertCOSEtoPKCS";
+import convertCOSEtoPKCS from '@helpers/convertCOSEtoPKCS';
import parseAuthenticatorData from '@helpers/parseAuthenticatorData';
-
-
/**
* Verify an attestation response with fmt 'none'
*
@@ -17,12 +15,7 @@ export default function verifyAttestationNone(
const { fmt, authData } = attestationObject;
const authDataStruct = parseAuthenticatorData(authData);
- const {
- credentialID,
- COSEPublicKey,
- counter,
- flags,
- } = authDataStruct;
+ const { credentialID, COSEPublicKey, counter, flags } = authDataStruct;
if (!flags.up) {
throw new Error('User was not present for attestation (None)');
diff --git a/packages/server/src/attestation/verifications/verifyPacked.ts b/packages/server/src/attestation/verifications/verifyPacked.ts
index 0a64c27..a27962d 100644
--- a/packages/server/src/attestation/verifications/verifyPacked.ts
+++ b/packages/server/src/attestation/verifications/verifyPacked.ts
@@ -2,20 +2,25 @@ import base64url from 'base64url';
import cbor from 'cbor';
import elliptic from 'elliptic';
import NodeRSA, { SigningSchemeHash } from 'node-rsa';
-import { AttestationObject, VerifiedAttestation, COSEKEYS, COSEPublicKey } from "@webauthntine/typescript-types";
-
-import convertCOSEtoPKCS from "@helpers/convertCOSEtoPKCS";
-import toHash from "@helpers/toHash";
+import {
+ AttestationObject,
+ VerifiedAttestation,
+ COSEKEYS,
+ COSEPublicKey,
+} from '@webauthntine/typescript-types';
+
+import convertCOSEtoPKCS from '@helpers/convertCOSEtoPKCS';
+import toHash from '@helpers/toHash';
import convertASN1toPEM from '@helpers/convertASN1toPEM';
import getCertificateInfo from '@helpers/getCertificateInfo';
import verifySignature from '@helpers/verifySignature';
import parseAuthenticatorData from '@helpers/parseAuthenticatorData';
-
/**
* Verify an attestation response with fmt 'packed'
*/
-export default function verifyAttestationPacked(attestationObject: AttestationObject,
+export default function verifyAttestationPacked(
+ attestationObject: AttestationObject,
base64ClientDataJSON: string,
): VerifiedAttestation {
const { fmt, authData, attStmt } = attestationObject;
@@ -43,10 +48,7 @@ export default function verifyAttestationPacked(attestationObject: AttestationOb
const clientDataHash = toHash(base64url.toBuffer(base64ClientDataJSON));
- const signatureBase = Buffer.concat([
- authData,
- clientDataHash,
- ]);
+ const signatureBase = Buffer.concat([authData, clientDataHash]);
const toReturn: VerifiedAttestation = {
verified: false,
@@ -59,12 +61,7 @@ export default function verifyAttestationPacked(attestationObject: AttestationOb
const leafCertInfo = getCertificateInfo(leafCert);
const { subject, basicConstraintsCA, version } = leafCertInfo;
- const {
- OU,
- CN,
- O,
- C,
- } = subject;
+ const { OU, CN, O, C } = subject;
if (OU !== 'Authenticator Attestation') {
throw new Error('Batch certificate OU was not "Authenticator Attestation" (Packed|Full');
@@ -105,7 +102,7 @@ export default function verifyAttestationPacked(attestationObject: AttestationOb
throw new Error('COSE public key was missing kty (Packed|Self)');
}
- const hashAlg: string = COSEALGHASH[(alg as number)];
+ const hashAlg: string = COSEALGHASH[alg as number];
if (kty === COSEKTY.EC2) {
const crv = cosePublicKey.get(COSEKEYS.crv);
@@ -126,7 +123,7 @@ export default function verifyAttestationPacked(attestationObject: AttestationOb
* For now, it's worth noting that this line is probably the reason why it can take
* 5-6 seconds to run tests.
*/
- const ec = new elliptic.ec(COSECRV[(crv as number)]);
+ const ec = new elliptic.ec(COSECRV[crv as number]);
const key = ec.keyFromPublic(pkcsPublicKey);
toReturn.verified = key.verify(signatureBaseHash, sig);
@@ -142,10 +139,13 @@ export default function verifyAttestationPacked(attestationObject: AttestationOb
// TODO: Verify this works
const key = new NodeRSA();
key.setOptions({ signingScheme });
- key.importKey({
- n: (n as Buffer),
- e: 65537,
- }, 'components-public');
+ key.importKey(
+ {
+ n: n as Buffer,
+ e: 65537,
+ },
+ 'components-public',
+ );
toReturn.verified = key.verify(signatureBase, sig);
} else if (kty === COSEKTY.OKP) {
@@ -158,7 +158,7 @@ export default function verifyAttestationPacked(attestationObject: AttestationOb
const signatureBaseHash = toHash(signatureBase, hashAlg);
const key = new elliptic.eddsa('ed25519');
- key.keyFromPublic((x as Buffer));
+ key.keyFromPublic(x as Buffer);
// TODO: is `publicKey` right here?
toReturn.verified = key.verify(signatureBaseHash, sig, publicKey);
@@ -190,8 +190,8 @@ const COSERSASCHEME: { [key: string]: SigningSchemeHash } = {
'-65535': 'pkcs1-sha1',
'-257': 'pkcs1-sha256',
'-258': 'pkcs1-sha384',
- '-259': 'pkcs1-sha512'
-}
+ '-259': 'pkcs1-sha512',
+};
const COSECRV: { [key: number]: string } = {
1: 'p256',
@@ -209,5 +209,5 @@ const COSEALGHASH: { [key: string]: string } = {
'-37': 'sha256',
'-7': 'sha256',
'-8': 'sha512',
- '-36': 'sha512'
-}
+ '-36': 'sha512',
+};
diff --git a/packages/server/src/attestation/verifyAttestationResponse.test.ts b/packages/server/src/attestation/verifyAttestationResponse.test.ts
index 4747066..375264a 100644
--- a/packages/server/src/attestation/verifyAttestationResponse.test.ts
+++ b/packages/server/src/attestation/verifyAttestationResponse.test.ts
@@ -38,8 +38,8 @@ test('should verify Packed (EC2) attestation', () => {
const verification = verifyAttestationResponse(
attestationPacked,
attestationPackedChallenge,
- 'https://dev.dontneeda.pw'
- )
+ 'https://dev.dontneeda.pw',
+ );
expect(verification.verified).toEqual(true);
expect(verification.authenticatorInfo?.fmt).toEqual('packed');
@@ -49,11 +49,11 @@ test('should verify Packed (EC2) attestation', () => {
);
expect(verification.authenticatorInfo?.base64CredentialID).toEqual(
'AYThY1csINY4JrbHyGmqTl1nL_F1zjAF3hSAIngz8kAcjugmAMNVvxZRwqpEH-bNHHAIv291OX5ko9eDf_5mu3U' +
- 'B2BvsScr2K-ppM4owOpGsqwg5tZglqqmxIm1Q',
+ 'B2BvsScr2K-ppM4owOpGsqwg5tZglqqmxIm1Q',
);
});
-test ('should verify Packed (X5C) attestation', () => {
+test('should verify Packed (X5C) attestation', () => {
const verification = verifyAttestationResponse(
attestationPackedX5C,
attestationPackedX5CChallenge,
@@ -75,7 +75,7 @@ test('should verify None attestation', () => {
const verification = verifyAttestationResponse(
attestationNone,
attestationNoneChallenge,
- 'https://dev.dontneeda.pw'
+ 'https://dev.dontneeda.pw',
);
expect(verification.verified).toEqual(true);
@@ -93,7 +93,7 @@ test('should verify Android SafetyNet attestation', () => {
const verification = verifyAttestationResponse(
attestationAndroidSafetyNet,
attestationAndroidSafetyNetChallenge,
- 'https://dev.dontneeda.pw'
+ 'https://dev.dontneeda.pw',
);
expect(verification.verified).toEqual(true);
@@ -122,7 +122,7 @@ test('should throw when response origin is not expected value', () => {
verifyAttestationResponse(
attestationNone,
attestationNoneChallenge,
- 'https://different.address'
+ 'https://different.address',
);
}).toThrow(/attestation origin/i);
});
@@ -139,11 +139,7 @@ test('should throw when attestation type is not webauthn.create', () => {
});
expect(() => {
- verifyAttestationResponse(
- attestationNone,
- challenge,
- origin,
- );
+ verifyAttestationResponse(attestationNone, challenge, origin);
}).toThrow(/attestation type/i);
});
@@ -165,7 +161,8 @@ test('should throw if an unexpected attestation format is specified', () => {
});
const attestationFIDOU2F = {
- base64AttestationObject: 'o2NmbXRoZmlkby11MmZnYXR0U3RtdKJjc2lnWEgwRgIhAK40WxA0t7py7AjEXvwGw' +
+ base64AttestationObject:
+ 'o2NmbXRoZmlkby11MmZnYXR0U3RtdKJjc2lnWEgwRgIhAK40WxA0t7py7AjEXvwGw' +
'TlmqlvrOks5g9lf+9zXzRiVAiEA3bv60xyXveKDOusYzniD7CDSostCet9PYK7FLdnTdZNjeDVjgVkCwTCCAr0wg' +
'gGloAMCAQICBCrnYmMwDQYJKoZIhvcNAQELBQAwLjEsMCoGA1UEAxMjWXViaWNvIFUyRiBSb290IENBIFNlcmlhb' +
'CA0NTcyMDA2MzEwIBcNMTQwODAxMDAwMDAwWhgPMjA1MDA5MDQwMDAwMDBaMG4xCzAJBgNVBAYTAlNFMRIwEAYDV' +
@@ -181,27 +178,31 @@ const attestationFIDOU2F = {
'NjDq86cN9vm6+APoAM20wtBAAAAAAAAAAAAAAAAAAAAAAAAAAAAQGFYevaR71ptU5YtXOSnVzPQTsGgK+gLiBKnq' +
'PWBmZXNRvjISqlLxiwApzlrfkTc3lEMYMatjeACCnsijOkNEGOlAQIDJiABIVggdWLG6UvGyHFw/k/bv6/k6z/LL' +
'gSO5KXzXw2EcUxkEX8iWCBeaVLz/cbyoKvRIg/q+q7tan0VN+i3WR0BOBCcuNP7yw==',
- base64ClientDataJSON: 'eyJjaGFsbGVuZ2UiOiJVMmQ0TjNZME0wOU1jbGRQYjFSNVpFeG5UbG95IiwiY2xpZW50' +
+ base64ClientDataJSON:
+ 'eyJjaGFsbGVuZ2UiOiJVMmQ0TjNZME0wOU1jbGRQYjFSNVpFeG5UbG95IiwiY2xpZW50' +
'RXh0ZW5zaW9ucyI6e30sImhhc2hBbGdvcml0aG0iOiJTSEEtMjU2Iiwib3JpZ2luIjoiaHR0cHM6Ly9jbG92ZXIu' +
'bWlsbGVydGltZS5kZXY6MzAwMCIsInR5cGUiOiJ3ZWJhdXRobi5jcmVhdGUifQ==',
};
const attestationFIDOU2FChallenge = 'Sgx7v43OLrWOoTydLgNZ2';
const attestationPacked = {
- base64AttestationObject: 'o2NmbXRmcGFja2VkZ2F0dFN0bXSiY2FsZyZjc2lnWEcwRQIhANvrPZMUFrl_rvlgR' +
+ base64AttestationObject:
+ 'o2NmbXRmcGFja2VkZ2F0dFN0bXSiY2FsZyZjc2lnWEcwRQIhANvrPZMUFrl_rvlgR' +
'qz6lCPlF6B4y885FYUCCrhrzAYXAiAb4dQKXbP3IimsTTadkwXQlrRVdxzlbmPXt847-Oh6r2hhdXRoRGF0YVjhP' +
'dxHEOnAiLIp26idVjIguzn3Ipr_RlsKZWsa-5qK-KBFXsOO-a3OAAI1vMYKZIsLJfHwVQMAXQGE4WNXLCDWOCa2x' +
'8hpqk5dZy_xdc4wBd4UgCJ4M_JAHI7oJgDDVb8WUcKqRB_mzRxwCL9vdTl-ZKPXg3_-Zrt1Adgb7EnK9ivqaTOKM' +
'DqRrKsIObWYJaqpsSJtUKUBAgMmIAEhWCBKMVVaivqCBpqqAxMjuCo5jMeUdh3jDOC0EF4fLBNNTyJYILc7rqDDe' +
'X1pwCLrl3ZX7IThrtZNwKQVLQyfHiorqP-n',
- base64ClientDataJSON: 'eyJjaGFsbGVuZ2UiOiJjelpRU1dKQ2JsQlFibkpIVGxOQ2VFNWtkRVJ5VkRkVmNsWlpT' +
+ base64ClientDataJSON:
+ 'eyJjaGFsbGVuZ2UiOiJjelpRU1dKQ2JsQlFibkpIVGxOQ2VFNWtkRVJ5VkRkVmNsWlpT' +
'a3M1U0UwIiwib3JpZ2luIjoiaHR0cHM6Ly9kZXYuZG9udG5lZWRhLnB3IiwidHlwZSI6IndlYmF1dGhuLmNyZWF0' +
'ZSJ9',
};
const attestationPackedChallenge = 's6PIbBnPPnrGNSBxNdtDrT7UrVYJK9HM';
const attestationPackedX5C = {
- base64AttestationObject: 'o2NmbXRmcGFja2VkZ2F0dFN0bXSjY2FsZyZjc2lnWEcwRQIhAIMt_hGMtdgpIVIwMOeKK' +
+ base64AttestationObject:
+ 'o2NmbXRmcGFja2VkZ2F0dFN0bXSjY2FsZyZjc2lnWEcwRQIhAIMt_hGMtdgpIVIwMOeKK' +
'w0IkUUFkXSY8arKh3Q0c5QQAiB9Sv9JavAEmppeH_XkZjB7TFM3jfxsgl97iIkvuJOUImN4NWOBWQLBMIICvTCCAaWgA' +
'wIBAgIEKudiYzANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDEyNZdWJpY28gVTJGIFJvb3QgQ0EgU2VyaWFsIDQ1NzIwM' +
'DYzMTAgFw0xNDA4MDEwMDAwMDBaGA8yMDUwMDkwNDAwMDAwMFowbjELMAkGA1UEBhMCU0UxEjAQBgNVBAoMCVl1Ymljb' +
@@ -217,24 +218,28 @@ const attestationPackedX5C = {
'wBA4rrvMciHCkdLQ2HghazIp1sMc8TmV8W8RgoX-x8tqV_1AmlqWACqUK8mBGLandr-htduQKPzgb2yWxOFV56TlqUBA' +
'gMmIAEhWCBsJbGAjckW-AA_XMk8OnB-VUvrs35ZpjtVJXRhnvXiGiJYIL2ncyg_KesCi44GH8UcZXYwjBkVdGMjNd6LF' +
'myiD6xf',
- base64ClientDataJSON: 'eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiZEc5MFlXeHNlVlZ1YVhG' +
- 'MVpWWmhiSFZsUlhabGNubFVhVzFsIiwib3JpZ2luIjoiaHR0cHM6Ly9kZXYuZG9udG5lZWRhLnB3In0='
+ base64ClientDataJSON:
+ 'eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiZEc5MFlXeHNlVlZ1YVhG' +
+ 'MVpWWmhiSFZsUlhabGNubFVhVzFsIiwib3JpZ2luIjoiaHR0cHM6Ly9kZXYuZG9udG5lZWRhLnB3In0=',
};
const attestationPackedX5CChallenge = 'totallyUniqueValueEveryTime';
const attestationNone = {
- base64AttestationObject: 'o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVjFPdxHEOnAiLIp26idVjIguzn3I' +
+ base64AttestationObject:
+ 'o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVjFPdxHEOnAiLIp26idVjIguzn3I' +
'pr_RlsKZWsa-5qK-KBFAAAAAAAAAAAAAAAAAAAAAAAAAAAAQQHSlyRHIdWleVqO24-6ix7JFWODqDWo_arvEz3Se' +
'5EgIFHkcVjZ4F5XDSBreIHsWRilRnKmaaqlqK3V2_4XtYs2pQECAyYgASFYID5PQTZQQg6haZFQWFzqfAOyQ_ENs' +
'MH8xxQ4GRiNPsqrIlggU8IVUOV8qpgk_Jh-OTaLuZL52KdX1fTht07X4DiQPow',
- base64ClientDataJSON: 'eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiYUVWalkxQlhkWHBw' +
+ base64ClientDataJSON:
+ 'eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiYUVWalkxQlhkWHBw' +
'VURBd1NEQndOV2Q0YURKZmRUVmZVRU0wVG1WWloyUSIsIm9yaWdpbiI6Imh0dHBzOlwvXC9kZXYuZG9udG5lZWRh' +
- 'LnB3IiwiYW5kcm9pZFBhY2thZ2VOYW1lIjoib3JnLm1vemlsbGEuZmlyZWZveCJ9'
+ 'LnB3IiwiYW5kcm9pZFBhY2thZ2VOYW1lIjoib3JnLm1vemlsbGEuZmlyZWZveCJ9',
};
const attestationNoneChallenge = 'hEccPWuziP00H0p5gxh2_u5_PC4NeYgd';
const attestationAndroidSafetyNet = {
- base64AttestationObject: 'o2NmbXRxYW5kcm9pZC1zYWZldHluZXRnYXR0U3RtdKJjdmVyaDE3MTIyMDM3aHJlc' +
+ base64AttestationObject:
+ 'o2NmbXRxYW5kcm9pZC1zYWZldHluZXRnYXR0U3RtdKJjdmVyaDE3MTIyMDM3aHJlc' +
'3BvbnNlWRS9ZXlKaGJHY2lPaUpTVXpJMU5pSXNJbmcxWXlJNld5Sk5TVWxHYTJwRFEwSkljV2RCZDBsQ1FXZEpVV' +
'kpZY205T01GcFBaRkpyUWtGQlFVRkJRVkIxYm5wQlRrSm5hM0ZvYTJsSE9YY3dRa0ZSYzBaQlJFSkRUVkZ6ZDBOU' +
'ldVUldVVkZIUlhkS1ZsVjZSV1ZOUW5kSFFURlZSVU5vVFZaU01qbDJXako0YkVsR1VubGtXRTR3U1VaT2JHTnVXb' +
@@ -319,8 +324,9 @@ const attestationAndroidSafetyNet = {
'yKa_0ZbCmVrGvuaivigRQAAAAC5P9lh8uZGL7EiggAiR954AEEBDL2BKZVhBca7N3j3asDaoSrA3tJgT_E4KN25T' +
'hBVqBHCdffSZt9bvku7hPBcd76BzU7Y-ckXslUkD13Imbzde6UBAgMmIAEhWCCT4hId3ByJ_agRyznv1xIazx2nl' +
'VEGyvN7intoZr7C2CJYIKo3XB-cca9aUOLC-xhp3GfhyfTS0hjws5zL_bT_N1AL',
- base64ClientDataJSON: 'eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiWDNaV1VHOUZOREpF' +
+ base64ClientDataJSON:
+ 'eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiWDNaV1VHOUZOREpF' +
'YUMxM2F6Tmlka2h0WVd0MGFWWjJSVmxETFV4M1FsZyIsIm9yaWdpbiI6Imh0dHBzOlwvXC9kZXYuZG9udG5lZWRh' +
- 'LnB3IiwiYW5kcm9pZFBhY2thZ2VOYW1lIjoiY29tLmFuZHJvaWQuY2hyb21lIn0'
+ 'LnB3IiwiYW5kcm9pZFBhY2thZ2VOYW1lIjoiY29tLmFuZHJvaWQuY2hyb21lIn0',
};
const attestationAndroidSafetyNetChallenge = '_vVPoE42Dh-wk3bvHmaktiVvEYC-LwBX';
diff --git a/packages/server/src/attestation/verifyAttestationResponse.ts b/packages/server/src/attestation/verifyAttestationResponse.ts
index 41708fc..bd197ed 100644
--- a/packages/server/src/attestation/verifyAttestationResponse.ts
+++ b/packages/server/src/attestation/verifyAttestationResponse.ts
@@ -1,6 +1,10 @@
import decodeAttestationObject from '@helpers/decodeAttestationObject';
import decodeClientDataJSON from '@helpers/decodeClientDataJSON';
-import { ATTESTATION_FORMATS, AuthenticatorAttestationResponseJSON, VerifiedAttestation } from '@webauthntine/typescript-types';
+import {
+ ATTESTATION_FORMATS,
+ AuthenticatorAttestationResponseJSON,
+ VerifiedAttestation,
+} from '@webauthntine/typescript-types';
import verifyFIDOU2F from './verifications/verifyFIDOU2F';
import verifyPacked from './verifications/verifyPacked';
@@ -28,7 +32,7 @@ export default function verifyAttestationResponse(
if (challenge !== expectedChallenge) {
throw new Error(
- `Unexpected attestation challenge "${challenge}", expected "${expectedChallenge}"`
+ `Unexpected attestation challenge "${challenge}", expected "${expectedChallenge}"`,
);
}
diff --git a/packages/server/src/helpers/convertCOSEtoPKCS.test.ts b/packages/server/src/helpers/convertCOSEtoPKCS.test.ts
index 1d0ad6e..5bc970c 100644
--- a/packages/server/src/helpers/convertCOSEtoPKCS.test.ts
+++ b/packages/server/src/helpers/convertCOSEtoPKCS.test.ts
@@ -3,7 +3,6 @@ import { COSEKEYS } from '@webauthntine/typescript-types';
import convertCOSEtoPKCS from './convertCOSEtoPKCS';
-
test('should throw an error curve if, somehow, curve coordinate x is missing', () => {
const mockCOSEKey = new Map<number, number | Buffer>();
diff --git a/packages/server/src/helpers/convertCOSEtoPKCS.ts b/packages/server/src/helpers/convertCOSEtoPKCS.ts
index fbafd59..f3b4a7e 100644
--- a/packages/server/src/helpers/convertCOSEtoPKCS.ts
+++ b/packages/server/src/helpers/convertCOSEtoPKCS.ts
@@ -1,7 +1,6 @@
import cbor from 'cbor';
import { COSEKEYS, COSEPublicKey } from '@webauthntine/typescript-types';
-
/**
* Takes COSE-encoded public key and converts it to PKCS key
*
@@ -39,5 +38,5 @@ export default function convertCOSEtoPKCS(cosePublicKey: Buffer) {
throw new Error('COSE public key was missing y');
}
- return Buffer.concat([tag, (x as Buffer), (y as Buffer)]);
+ return Buffer.concat([tag, x as Buffer, y as Buffer]);
}
diff --git a/packages/server/src/helpers/decodeAttestationObject.test.ts b/packages/server/src/helpers/decodeAttestationObject.test.ts
index d36201e..e8eb364 100644
--- a/packages/server/src/helpers/decodeAttestationObject.test.ts
+++ b/packages/server/src/helpers/decodeAttestationObject.test.ts
@@ -3,9 +3,9 @@ import decodeAttestationObject from './decodeAttestationObject';
test('should decode base64-encoded indirect attestationObject', () => {
const decoded = decodeAttestationObject(
'o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVjEAbElFazplpnc037DORGDZNjDq86cN9vm6' +
- '+APoAM20wtBAAAAAAAAAAAAAAAAAAAAAAAAAAAAQKmPuEwByQJ3e89TccUSrCGDkNWquhevjLLn/' +
- 'KNZZaxQQ0steueoG2g12dvnUNbiso8kVJDyLa+6UiA34eniujWlAQIDJiABIVggiUk8wN2j' +
- '+3fkKI7KSiLBkKzs3FfhPZxHgHPnGLvOY/YiWCBv7+XyTqArnMVtQ947/8Xk8fnVCdLMRWJGM1VbNevVcQ=='
+ '+APoAM20wtBAAAAAAAAAAAAAAAAAAAAAAAAAAAAQKmPuEwByQJ3e89TccUSrCGDkNWquhevjLLn/' +
+ 'KNZZaxQQ0steueoG2g12dvnUNbiso8kVJDyLa+6UiA34eniujWlAQIDJiABIVggiUk8wN2j' +
+ '+3fkKI7KSiLBkKzs3FfhPZxHgHPnGLvOY/YiWCBv7+XyTqArnMVtQ947/8Xk8fnVCdLMRWJGM1VbNevVcQ==',
);
expect(decoded.fmt).toEqual('none');
@@ -16,20 +16,20 @@ test('should decode base64-encoded indirect attestationObject', () => {
test('should decode base64-encoded direct attestationObject', () => {
const decoded = decodeAttestationObject(
'o2NmbXRoZmlkby11MmZnYXR0U3RtdKJjc2lnWEgwRgIhAK40WxA0t7py7AjEXvwGwTlmqlvrOk' +
- 's5g9lf+9zXzRiVAiEA3bv60xyXveKDOusYzniD7CDSostCet9PYK7FLdnTdZNjeDVjgVkCwTCCAr0wggGloAMCAQICBCrn' +
- 'YmMwDQYJKoZIhvcNAQELBQAwLjEsMCoGA1UEAxMjWXViaWNvIFUyRiBSb290IENBIFNlcmlhbCA0NTcyMDA2MzEwIBcNMT' +
- 'QwODAxMDAwMDAwWhgPMjA1MDA5MDQwMDAwMDBaMG4xCzAJBgNVBAYTAlNFMRIwEAYDVQQKDAlZdWJpY28gQUIxIjAgBgNV' +
- 'BAsMGUF1dGhlbnRpY2F0b3IgQXR0ZXN0YXRpb24xJzAlBgNVBAMMHll1YmljbyBVMkYgRUUgU2VyaWFsIDcxOTgwNzA3NT' +
- 'BZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCoDhl5gQ9meEf8QqiVUV4S/Ca+Oax47MhcpIW9VEhqM2RDTmd3HaL3+SnvH' +
- '49q8YubSRp/1Z1uP+okMynSGnj+jbDBqMCIGCSsGAQQBgsQKAgQVMS4zLjYuMS40LjEuNDE0ODIuMS4xMBMGCysGAQQBgu' +
- 'UcAgEBBAQDAgQwMCEGCysGAQQBguUcAQEEBBIEEG1Eupv27C5JuTAMj+kgy3MwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0B' +
- 'AQsFAAOCAQEAclfQPNzD4RVphJDW+A75W1MHI3PZ5kcyYysR3Nx3iuxr1ZJtB+F7nFQweI3jL05HtFh2/4xVIgKb6Th4eV' +
- 'cjMecncBaCinEbOcdP1sEli9Hk2eVm1XB5A0faUjXAPw/+QLFCjgXG6ReZ5HVUcWkB7riLsFeJNYitiKrTDXFPLy+sNtVN' +
- 'utcQnFsCerDKuM81TvEAigkIbKCGlq8M/NvBg5j83wIxbCYiyV7mIr3RwApHieShzLdJo1S6XydgQjC+/64G5r8C+8AVvN' +
- 'FR3zXXCpio5C3KRIj88HEEIYjf6h1fdLfqeIsq+cUUqbq5T+c4nNoZUZCysTB9v5EY4akp+GhhdXRoRGF0YVjEAbElFazp' +
- 'lpnc037DORGDZNjDq86cN9vm6+APoAM20wtBAAAAAAAAAAAAAAAAAAAAAAAAAAAAQGFYevaR71ptU5YtXOSnVzPQTsGgK+' +
- 'gLiBKnqPWBmZXNRvjISqlLxiwApzlrfkTc3lEMYMatjeACCnsijOkNEGOlAQIDJiABIVggdWLG6UvGyHFw/k/bv6/k6z/L' +
- 'LgSO5KXzXw2EcUxkEX8iWCBeaVLz/cbyoKvRIg/q+q7tan0VN+i3WR0BOBCcuNP7yw=='
+ 's5g9lf+9zXzRiVAiEA3bv60xyXveKDOusYzniD7CDSostCet9PYK7FLdnTdZNjeDVjgVkCwTCCAr0wggGloAMCAQICBCrn' +
+ 'YmMwDQYJKoZIhvcNAQELBQAwLjEsMCoGA1UEAxMjWXViaWNvIFUyRiBSb290IENBIFNlcmlhbCA0NTcyMDA2MzEwIBcNMT' +
+ 'QwODAxMDAwMDAwWhgPMjA1MDA5MDQwMDAwMDBaMG4xCzAJBgNVBAYTAlNFMRIwEAYDVQQKDAlZdWJpY28gQUIxIjAgBgNV' +
+ 'BAsMGUF1dGhlbnRpY2F0b3IgQXR0ZXN0YXRpb24xJzAlBgNVBAMMHll1YmljbyBVMkYgRUUgU2VyaWFsIDcxOTgwNzA3NT' +
+ 'BZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCoDhl5gQ9meEf8QqiVUV4S/Ca+Oax47MhcpIW9VEhqM2RDTmd3HaL3+SnvH' +
+ '49q8YubSRp/1Z1uP+okMynSGnj+jbDBqMCIGCSsGAQQBgsQKAgQVMS4zLjYuMS40LjEuNDE0ODIuMS4xMBMGCysGAQQBgu' +
+ 'UcAgEBBAQDAgQwMCEGCysGAQQBguUcAQEEBBIEEG1Eupv27C5JuTAMj+kgy3MwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0B' +
+ 'AQsFAAOCAQEAclfQPNzD4RVphJDW+A75W1MHI3PZ5kcyYysR3Nx3iuxr1ZJtB+F7nFQweI3jL05HtFh2/4xVIgKb6Th4eV' +
+ 'cjMecncBaCinEbOcdP1sEli9Hk2eVm1XB5A0faUjXAPw/+QLFCjgXG6ReZ5HVUcWkB7riLsFeJNYitiKrTDXFPLy+sNtVN' +
+ 'utcQnFsCerDKuM81TvEAigkIbKCGlq8M/NvBg5j83wIxbCYiyV7mIr3RwApHieShzLdJo1S6XydgQjC+/64G5r8C+8AVvN' +
+ 'FR3zXXCpio5C3KRIj88HEEIYjf6h1fdLfqeIsq+cUUqbq5T+c4nNoZUZCysTB9v5EY4akp+GhhdXRoRGF0YVjEAbElFazp' +
+ 'lpnc037DORGDZNjDq86cN9vm6+APoAM20wtBAAAAAAAAAAAAAAAAAAAAAAAAAAAAQGFYevaR71ptU5YtXOSnVzPQTsGgK+' +
+ 'gLiBKnqPWBmZXNRvjISqlLxiwApzlrfkTc3lEMYMatjeACCnsijOkNEGOlAQIDJiABIVggdWLG6UvGyHFw/k/bv6/k6z/L' +
+ 'LgSO5KXzXw2EcUxkEX8iWCBeaVLz/cbyoKvRIg/q+q7tan0VN+i3WR0BOBCcuNP7yw==',
);
expect(decoded.fmt).toEqual('fido-u2f');
diff --git a/packages/server/src/helpers/decodeAttestationObject.ts b/packages/server/src/helpers/decodeAttestationObject.ts
index fa39454..509c129 100644
--- a/packages/server/src/helpers/decodeAttestationObject.ts
+++ b/packages/server/src/helpers/decodeAttestationObject.ts
@@ -2,7 +2,6 @@ import base64url from 'base64url';
import cbor from 'cbor';
import { AttestationObject } from '@webauthntine/typescript-types';
-
/**
* Convert an AttestationObject from base64 string to a proper object
*
diff --git a/packages/server/src/helpers/decodeClientDataJSON.test.ts b/packages/server/src/helpers/decodeClientDataJSON.test.ts
index d3cea88..7674ec5 100644
--- a/packages/server/src/helpers/decodeClientDataJSON.test.ts
+++ b/packages/server/src/helpers/decodeClientDataJSON.test.ts
@@ -4,13 +4,14 @@ test('should convert base64-encoded attestation clientDataJSON to JSON', () => {
expect(
decodeClientDataJSON(
'eyJjaGFsbGVuZ2UiOiJVMmQ0TjNZME0wOU1jbGRQYjFSNVpFeG5UbG95IiwiY2xpZW50RXh0ZW5zaW9ucyI6e30' +
- 'sImhhc2hBbGdvcml0aG0iOiJTSEEtMjU2Iiwib3JpZ2luIjoiaHR0cHM6Ly9jbG92ZXIubWlsbGVydGltZS5kZX' +
- 'Y6MzAwMCIsInR5cGUiOiJ3ZWJhdXRobi5jcmVhdGUifQ=='
- )).toEqual({
- challenge: 'Sgx7v43OLrWOoTydLgNZ2',
- clientExtensions: {},
- hashAlgorithm: 'SHA-256',
- origin: 'https://clover.millertime.dev:3000',
- type: 'webauthn.create'
- });
+ 'sImhhc2hBbGdvcml0aG0iOiJTSEEtMjU2Iiwib3JpZ2luIjoiaHR0cHM6Ly9jbG92ZXIubWlsbGVydGltZS5kZX' +
+ 'Y6MzAwMCIsInR5cGUiOiJ3ZWJhdXRobi5jcmVhdGUifQ==',
+ ),
+ ).toEqual({
+ challenge: 'Sgx7v43OLrWOoTydLgNZ2',
+ clientExtensions: {},
+ hashAlgorithm: 'SHA-256',
+ origin: 'https://clover.millertime.dev:3000',
+ type: 'webauthn.create',
+ });
});
diff --git a/packages/server/src/helpers/getCertificateInfo.ts b/packages/server/src/helpers/getCertificateInfo.ts
index 6bf6546..68f9c20 100644
--- a/packages/server/src/helpers/getCertificateInfo.ts
+++ b/packages/server/src/helpers/getCertificateInfo.ts
@@ -2,16 +2,16 @@ import jsrsasign from 'jsrsasign';
import { CertificateInfo } from '@webauthntine/typescript-types';
type ExtInfo = {
- critical: boolean,
- oid: string,
- vidx: number,
+ critical: boolean;
+ oid: string;
+ vidx: number;
};
interface x5cCertificate extends jsrsasign.X509 {
version: number;
foffset: number;
aExtInfo: ExtInfo[];
-};
+}
/**
* Extract PEM certificate info
@@ -26,12 +26,12 @@ export default function getCertificateInfo(pemCertificate: string): CertificateI
const subjectParts = subjectString.slice(1).split('/');
const subject: { [key: string]: string } = {};
- subjectParts.forEach((field) => {
+ subjectParts.forEach(field => {
const [key, val] = field.split('=');
subject[key] = val;
});
- const { version } = (subjectCert as x5cCertificate);
+ const { version } = subjectCert as x5cCertificate;
const basicConstraintsCA = !!subjectCert.getExtBasicConstraints().cA;
return {
diff --git a/packages/server/src/helpers/parseAuthenticatorData.ts b/packages/server/src/helpers/parseAuthenticatorData.ts
index a3dd868..969e9ec 100644
--- a/packages/server/src/helpers/parseAuthenticatorData.ts
+++ b/packages/server/src/helpers/parseAuthenticatorData.ts
@@ -1,4 +1,4 @@
-import { ParsedAuthenticatorData } from "@webauthntine/typescript-types";
+import { ParsedAuthenticatorData } from '@webauthntine/typescript-types';
/**
* Make sense of the authData buffer contained in an Attestation
diff --git a/packages/server/src/helpers/verifySignature.ts b/packages/server/src/helpers/verifySignature.ts
index c938a23..79fcdba 100644
--- a/packages/server/src/helpers/verifySignature.ts
+++ b/packages/server/src/helpers/verifySignature.ts
@@ -12,7 +12,5 @@ export default function verifySignature(
signatureBase: Buffer,
publicKey: string,
): boolean {
- return crypto.createVerify('SHA256')
- .update(signatureBase)
- .verify(publicKey, signature);
+ return crypto.createVerify('SHA256').update(signatureBase).verify(publicKey, signature);
}
diff --git a/packages/server/tsconfig.json b/packages/server/tsconfig.json
index 2267eb5..5dda000 100644
--- a/packages/server/tsconfig.json
+++ b/packages/server/tsconfig.json
@@ -4,7 +4,7 @@
"baseUrl": "./src",
"outDir": "./dist",
"paths": {
- "@helpers/*": ["helpers/*"],
+ "@helpers/*": ["helpers/*"]
},
"plugins": [
// These replace the path helpers above with relative paths at build time
diff --git a/packages/typescript-types/README.md b/packages/typescript-types/README.md
index da321f5..c51948a 100644
--- a/packages/typescript-types/README.md
+++ b/packages/typescript-types/README.md
@@ -1,4 +1,5 @@
# @webauthntine/typescript-types
+
![WebAuthn](https://img.shields.io/badge/WebAuthn-Simplified-blueviolet?style=for-the-badge&logo=WebAuthn)
[![npm (scoped)](https://img.shields.io/npm/v/@webauthntine/typescript-types?style=for-the-badge&logo=npm)](https://www.npmjs.com/package/@webauthntine/typescript-types)
diff --git a/packages/typescript-types/src/index.ts b/packages/typescript-types/src/index.ts
index 9e5ec5d..cab623c 100644
--- a/packages/typescript-types/src/index.ts
+++ b/packages/typescript-types/src/index.ts
@@ -9,24 +9,26 @@
*/
export type PublicKeyCredentialCreationOptionsJSON = {
publicKey: {
- challenge: string,
+ challenge: string;
// The organization registering and authenticating the user
rp: {
- name: string,
- id: string,
- },
+ name: string;
+ id: string;
+ };
user: {
- id: string,
- name: string,
- displayName: string,
- },
- pubKeyCredParams: [{
- alg: -7,
- type: 'public-key',
- }],
- timeout?: number,
- attestation: 'direct' | 'indirect',
- },
+ id: string;
+ name: string;
+ displayName: string;
+ };
+ pubKeyCredParams: [
+ {
+ alg: -7;
+ type: 'public-key';
+ },
+ ];
+ timeout?: number;
+ attestation: 'direct' | 'indirect';
+ };
};
/**
@@ -41,18 +43,18 @@ export type PublicKeyCredentialCreationOptionsJSON = {
export type PublicKeyCredentialRequestOptionsJSON = {
publicKey: {
//
- challenge: string,
+ challenge: string;
allowCredentials: {
// Will be converted to a Uint8Array in the browser
- id: string,
- type: 'public-key',
- transports?: AuthenticatorTransport[],
- }[],
+ id: string;
+ type: 'public-key';
+ transports?: AuthenticatorTransport[];
+ }[];
// extensions?: AuthenticationExtensionsClientInputs,
- rpId?: string,
- timeout?: number,
- userVerification?: UserVerificationRequirement,
- },
+ rpId?: string;
+ timeout?: number;
+ userVerification?: UserVerificationRequirement;
+ };
};
/**
@@ -73,10 +75,9 @@ export interface AssertionCredential extends PublicKeyCredential {
* A slightly-modified AuthenticatorAttestationResponse to simplify working with ArrayBuffers that
* are base64-encoded in the browser so that they can be sent as JSON to the server.
*/
-export interface AuthenticatorAttestationResponseJSON extends Omit<
-AuthenticatorAttestationResponse, 'clientDataJSON' | 'attestationObject'
-> {
- base64ClientDataJSON: string,
+export interface AuthenticatorAttestationResponseJSON
+ extends Omit<AuthenticatorAttestationResponse, 'clientDataJSON' | 'attestationObject'> {
+ base64ClientDataJSON: string;
base64AttestationObject: string;
}
@@ -84,9 +85,11 @@ AuthenticatorAttestationResponse, 'clientDataJSON' | 'attestationObject'
* A slightly-modified AuthenticatorAttestationResponse to simplify working with ArrayBuffers that
* are base64-encoded in the browser so that they can be sent as JSON to the server.
*/
-export interface AuthenticatorAssertionResponseJSON extends Omit<
-AuthenticatorAssertionResponse, 'clientDataJSON' | 'authenticatorData' | 'signature' | 'userHandle'
-> {
+export interface AuthenticatorAssertionResponseJSON
+ extends Omit<
+ AuthenticatorAssertionResponse,
+ 'clientDataJSON' | 'authenticatorData' | 'signature' | 'userHandle'
+ > {
base64CredentialID: string;
base64AuthenticatorData: string;
base64ClientDataJSON: string;
@@ -102,36 +105,36 @@ export enum ATTESTATION_FORMATS {
}
export type AttestationObject = {
- fmt: ATTESTATION_FORMATS,
+ fmt: ATTESTATION_FORMATS;
attStmt: {
- sig?: Buffer,
- x5c?: Buffer[],
- response?: Buffer,
- },
- authData: Buffer,
+ sig?: Buffer;
+ x5c?: Buffer[];
+ response?: Buffer;
+ };
+ authData: Buffer;
};
export type ParsedAuthenticatorData = {
- rpIdHash: Buffer,
- flagsBuf: Buffer,
+ rpIdHash: Buffer;
+ flagsBuf: Buffer;
flags: {
- up: boolean,
- uv: boolean,
- at: boolean,
- ed: boolean,
- flagsInt: number,
- },
- counter: number,
- counterBuf: Buffer,
- aaguid?: Buffer,
- credentialID?: Buffer,
- COSEPublicKey?: Buffer,
+ up: boolean;
+ uv: boolean;
+ at: boolean;
+ ed: boolean;
+ flagsInt: number;
+ };
+ counter: number;
+ counterBuf: Buffer;
+ aaguid?: Buffer;
+ credentialID?: Buffer;
+ COSEPublicKey?: Buffer;
};
export type ClientDataJSON = {
- type: string,
- challenge: string,
- origin: string,
+ type: string;
+ challenge: string;
+ origin: string;
};
/**
@@ -149,14 +152,14 @@ export type ClientDataJSON = {
* reference!**
*/
export type VerifiedAttestation = {
- verified: boolean,
+ verified: boolean;
userVerified: boolean;
authenticatorInfo?: {
- fmt: ATTESTATION_FORMATS,
- counter: number,
- base64PublicKey: string,
- base64CredentialID: string,
- },
+ fmt: ATTESTATION_FORMATS;
+ counter: number;
+ base64PublicKey: string;
+ base64CredentialID: string;
+ };
};
/**
@@ -172,15 +175,15 @@ export type VerifiedAttestation = {
export type VerifiedAssertion = {
verified: boolean;
authenticatorInfo: {
- counter: number,
- base64CredentialID: string,
- },
+ counter: number;
+ base64CredentialID: string;
+ };
};
export type CertificateInfo = {
- subject: { [key: string]: string },
- version: number,
- basicConstraintsCA: boolean,
+ subject: { [key: string]: string };
+ version: number;
+ basicConstraintsCA: boolean;
};
export enum COSEKEYS {
@@ -196,18 +199,18 @@ export enum COSEKEYS {
export type COSEPublicKey = Map<COSEAlgorithmIdentifier, number | Buffer>;
export type SafetyNetJWTHeader = {
- alg: 'string',
- x5c: string[],
+ alg: 'string';
+ x5c: string[];
};
export type SafetyNetJWTPayload = {
- nonce: string,
- timestampMs: number,
- apkPackageName: string,
- apkDigestSha256: string,
- ctsProfileMatch: boolean,
- apkCertificateDigestSha256: string[],
- basicIntegrity: boolean,
+ nonce: string;
+ timestampMs: number;
+ apkPackageName: string;
+ apkDigestSha256: string;
+ ctsProfileMatch: boolean;
+ apkCertificateDigestSha256: string[];
+ basicIntegrity: boolean;
};
export type SafetyNetJWTSignature = string;
@@ -216,8 +219,8 @@ export type SafetyNetJWTSignature = string;
* A WebAuthn-compatible device and the information needed to verify assertions by it
*/
export type AuthenticatorDevice = {
- base64PublicKey: string,
- base64CredentialID: string,
+ base64PublicKey: string;
+ base64CredentialID: string;
// Number of times this device is expected to have been used
- counter: number,
+ counter: number;
};
diff --git a/packages/typescript-types/tsconfig.json b/packages/typescript-types/tsconfig.json
index 9ddf207..349e40c 100644
--- a/packages/typescript-types/tsconfig.json
+++ b/packages/typescript-types/tsconfig.json
@@ -2,6 +2,6 @@
"extends": "../../tsconfig.json",
"compilerOptions": {
"baseUrl": "./src",
- "outDir": "./dist",
+ "outDir": "./dist"
}
}