summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorMatthew Miller <matthew@millerti.me>2020-11-16 21:21:28 -0800
committerGitHub <noreply@github.com>2020-11-16 21:21:28 -0800
commit0f0a2d9b85ca549f3ebcb8cc4678085779dbf92c (patch)
tree8d03baba70c4969118647c361882c76443b114f9
parent79b8188f5a7dab3dc70234e20216cd3b24267c9a (diff)
parent5a05e786ced984f9b41a791cfa7d734d7a5b21e5 (diff)
Merge pull request #71 from MasterKale/feat/port-example-to-ts
feat/port-example-to-ts
-rw-r--r--example/example-server.d.ts39
-rw-r--r--example/fido-conformance.ts (renamed from example/fido-conformance.js)54
-rw-r--r--example/index.ts (renamed from example/index.js)76
-rw-r--r--example/package-lock.json70
-rw-r--r--example/package.json8
-rw-r--r--example/tsconfig.json17
-rw-r--r--package-lock.json99
-rw-r--r--package.json3
8 files changed, 292 insertions, 74 deletions
diff --git a/example/example-server.d.ts b/example/example-server.d.ts
new file mode 100644
index 0000000..6d1dfb8
--- /dev/null
+++ b/example/example-server.d.ts
@@ -0,0 +1,39 @@
+import type { AuthenticatorDevice } from '@simplewebauthn/typescript-types';
+
+/**
+ * You'll need a database to store a few things:
+ *
+ * 1. Users
+ *
+ * You'll need to be able to associate attestation and assertions challenges, and authenticators to
+ * a specific user. See `LoggedInUser` below for an idea of the minimum amount of info you'll need to
+ * track for a specific user during these flows.
+ *
+ * 2. Challenges
+ *
+ * The totally-random-unique-every-time values you pass into every execution of
+ * `generateAttestationOptions()` or `generateAssertionOptions()` MUST be stored until
+ * `verifyAttestationResponse()` or `verifyAssertionResponse()` (respectively) is called to verify
+ * that the response contains the signed challenge.
+ *
+ * These values only need to be persisted for `timeout` number of milliseconds (see the `generate`
+ * methods and their optional `timeout` parameter)
+ *
+ * 3. Authenticator Devices
+ *
+ * After an attestation, you'll need to store three things about the authenticator:
+ *
+ * - Base64-encoded "Credential ID" (varchar)
+ * - Base64-encoded "Public Key" (varchar)
+ * - Counter (int)
+ *
+ * Each authenticator must also be associated to a user so that you can generate a list of
+ * authenticator credential IDs to pass into `generateAssertionOptions()`, from which one is
+ * expected to generate an assertion response.
+ */
+interface LoggedInUser {
+ id: string;
+ username: string;
+ devices: AuthenticatorDevice[];
+ currentChallenge?: string;
+}
diff --git a/example/fido-conformance.js b/example/fido-conformance.ts
index 9317a27..4b8e0c3 100644
--- a/example/fido-conformance.js
+++ b/example/fido-conformance.ts
@@ -1,32 +1,39 @@
/* eslint-disable @typescript-eslint/no-var-requires */
-const fs = require('fs');
-const express = require('express');
-const fetch = require('node-fetch');
+import fs from 'fs';
+import express from 'express';
+import fetch from 'node-fetch';
-const {
+import {
generateAttestationOptions,
verifyAttestationResponse,
generateAssertionOptions,
verifyAssertionResponse,
MetadataService,
-} = require('@simplewebauthn/server');
+} from '@simplewebauthn/server';
+import { MetadataStatement } from '@simplewebauthn/server/dist/metadata/metadataService';
+
+import { LoggedInUser } from './example-server';
+
+interface LoggedInFIDOUser extends LoggedInUser {
+ currentAssertionUserVerification?: 'discouraged' | 'preferred' | 'required' | undefined;
+}
/**
* Create paths specifically for testing with the FIDO Conformance Tools
*/
-const fidoConformanceRouter = express.Router();
+export const fidoConformanceRouter = express.Router();
+export const fidoRouteSuffix = '/fido';
const rpName = 'FIDO Conformance Test';
const rpID = 'localhost';
const origin = 'https://localhost';
-const fidoRouteSuffix = '/fido';
/**
* Load JSON metadata statements provided by the Conformance Tools
*
* FIDO2 > TESTS CONFIGURATION > DOWNLOAD SERVER METADATA (button)
*/
-const statements = [];
+const statements: MetadataStatement[] = [];
try {
// Update this to whatever folder you extracted the statements to
@@ -53,7 +60,7 @@ fetch('https://mds.certinfra.fidoalliance.org/getEndpoints', {
.then(resp => resp.json())
.then(json => {
const routes = json.result;
- const mdsServers = routes.map(url => ({
+ const mdsServers = routes.map((url: string) => ({
url,
rootCertURL: 'https://mds.certinfra.fidoalliance.org/pki/MDSROOT.crt',
metadataURLSuffix: '',
@@ -72,7 +79,7 @@ fetch('https://mds.certinfra.fidoalliance.org/getEndpoints', {
console.log('🔐 FIDO Conformance routes ready');
});
-const inMemoryUserDeviceDB = {
+const inMemoryUserDeviceDB: { [username: string]: LoggedInFIDOUser } = {
// [username]: string: {
// id: loggedInUserId,
// username: 'user@yourdomain.com',
@@ -90,7 +97,7 @@ const inMemoryUserDeviceDB = {
// },
};
// A cheap way of remembering who's "logged in" between the request for options and the response
-let loggedInUsername = undefined;
+let loggedInUsername: string | undefined = undefined;
/**
* [FIDO2] Server Tests > MakeCredential Request
@@ -146,7 +153,7 @@ fidoConformanceRouter.post('/attestation/options', (req, res) => {
fidoConformanceRouter.post('/attestation/result', async (req, res) => {
const { body } = req;
- const user = inMemoryUserDeviceDB[loggedInUsername];
+ const user = inMemoryUserDeviceDB[`${loggedInUsername}`];
const expectedChallenge = user.currentChallenge;
@@ -154,7 +161,7 @@ fidoConformanceRouter.post('/attestation/result', async (req, res) => {
try {
verification = await verifyAttestationResponse({
credential: body,
- expectedChallenge,
+ expectedChallenge: `${expectedChallenge}`,
expectedOrigin: origin,
});
} catch (error) {
@@ -164,7 +171,7 @@ fidoConformanceRouter.post('/attestation/result', async (req, res) => {
const { verified, authenticatorInfo } = verification;
- if (verified) {
+ if (verified && authenticatorInfo) {
const { base64PublicKey, base64CredentialID, counter } = authenticatorInfo;
const existingDevice = user.devices.find(device => device.credentialID === base64CredentialID);
@@ -196,7 +203,7 @@ fidoConformanceRouter.post('/assertion/options', (req, res) => {
loggedInUsername = username;
- let user = inMemoryUserDeviceDB[username];
+ const user = inMemoryUserDeviceDB[username];
const { devices } = user;
@@ -224,7 +231,7 @@ fidoConformanceRouter.post('/assertion/result', (req, res) => {
const { body } = req;
const { id } = body;
- const user = inMemoryUserDeviceDB[loggedInUsername];
+ const user = inMemoryUserDeviceDB[`${loggedInUsername}`];
// Pull up values specified when generation assertion options
const expectedChallenge = user.currentChallenge;
@@ -232,11 +239,15 @@ fidoConformanceRouter.post('/assertion/result', (req, res) => {
const existingDevice = user.devices.find(device => device.credentialID === id);
+ if (!existingDevice) {
+ throw new Error(`Could not find device matching ${id}`);
+ }
+
let verification;
try {
verification = verifyAssertionResponse({
credential: body,
- expectedChallenge,
+ expectedChallenge: `${expectedChallenge}`,
expectedOrigin: origin,
expectedRPID: rpID,
authenticator: existingDevice,
@@ -250,8 +261,8 @@ fidoConformanceRouter.post('/assertion/result', (req, res) => {
const { verified, authenticatorInfo } = verification;
if (verified) {
- const { base64CredentialID, counter } = authenticatorInfo;
- const existingDevice = user.devices.find(device => device.credentialID === base64CredentialID);
+ const { counter } = authenticatorInfo;
+
existingDevice.counter = counter;
}
@@ -272,8 +283,3 @@ fidoConformanceRouter.all('*', (req, res, next) => {
next();
});
-
-module.exports = {
- fidoConformanceRouter,
- fidoRouteSuffix,
-};
diff --git a/example/index.js b/example/index.ts
index 928eb64..b3ecd4d 100644
--- a/example/index.js
+++ b/example/index.ts
@@ -5,42 +5,52 @@
* The webpages served from ./public use @simplewebauthn/browser.
*/
-const https = require('https');
-const fs = require('fs');
+import https from 'https';
+import fs from 'fs';
-const express = require('express');
+import express from 'express';
+import dotenv from 'dotenv';
-const {
+dotenv.config();
+
+import {
// Registration ("Attestation")
generateAttestationOptions,
verifyAttestationResponse,
// Login ("Assertion")
generateAssertionOptions,
verifyAssertionResponse,
-} = require('@simplewebauthn/server');
+} from '@simplewebauthn/server';
+
+import { LoggedInUser } from './example-server';
const app = express();
const host = '0.0.0.0';
const port = 443;
+const { ENABLE_CONFORMANCE } = process.env;
+
app.use(express.static('./public/'));
app.use(express.json());
/**
- * If the words "metadata statements" mean anything to you, you'll want to check out this file. It
+ * If the words "metadata statements" mean anything to you, you'll want to enable this route. It
* contains an example of a more complex deployment of SimpleWebAuthn with support enabled for the
* FIDO Metadata Service. This enables greater control over the types of authenticators that can
* interact with the Rely Party (a.k.a. "RP", a.k.a. "this server").
*/
-// const { fidoRouteSuffix, fidoConformanceRouter } = require('./fido-conformance');
-// app.use(fidoRouteSuffix, fidoConformanceRouter);
+if (ENABLE_CONFORMANCE === 'true') {
+ import('./fido-conformance').then(({ fidoRouteSuffix, fidoConformanceRouter }) => {
+ app.use(fidoRouteSuffix, fidoConformanceRouter);
+ });
+}
/**
* 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 = 'localhost';
-const origin = `https://${rpID}`;
+const expectedOrigin = `https://${rpID}`;
/**
* 2FA and Passwordless WebAuthn flows expect you to be able to uniquely identify the user that
* performs an attestation or assertion. The user ID you specify here should be your internal,
@@ -51,37 +61,7 @@ const origin = `https://${rpID}`;
*/
const loggedInUserId = 'internalUserId';
-/**
- * You'll need a database to store a few things:
- *
- * 1. Users
- *
- * You'll need to be able to associate attestation and assertions challenges, and authenticators to
- * a specific user
- *
- * 2. Challenges
- *
- * The totally-random-unique-every-time values you pass into every execution of
- * `generateAttestationOptions()` or `generateAssertionOptions()` MUST be stored until
- * `verifyAttestationResponse()` or `verifyAssertionResponse()` (respectively) is called to verify
- * that the response contains the signed challenge.
- *
- * These values only need to be persisted for `timeout` number of milliseconds (see the `generate`
- * methods and their optional `timeout` parameter)
- *
- * 3. Authenticator Devices
- *
- * After an attestation, you'll need to store three things about the authenticator:
- *
- * - Base64-encoded "Credential ID" (varchar)
- * - Base64-encoded "Public Key" (varchar)
- * - Counter (int)
- *
- * Each authenticator must also be associated to a user so that you can generate a list of
- * authenticator credential IDs to pass into `generateAssertionOptions()`, from which one is
- * expected to generate an assertion response.
- */
-const inMemoryUserDeviceDB = {
+const inMemoryUserDeviceDB: { [loggedInUserId: string]: LoggedInUser } = {
[loggedInUserId]: {
id: loggedInUserId,
username: `user@${rpID}`,
@@ -165,8 +145,8 @@ app.post('/verify-attestation', async (req, res) => {
try {
verification = await verifyAttestationResponse({
credential: body,
- expectedChallenge,
- expectedOrigin: origin,
+ expectedChallenge: `${expectedChallenge}`,
+ expectedOrigin,
expectedRPID: rpID,
});
} catch (error) {
@@ -176,7 +156,7 @@ app.post('/verify-attestation', async (req, res) => {
const { verified, authenticatorInfo } = verification;
- if (verified) {
+ if (verified && authenticatorInfo) {
const { base64PublicKey, base64CredentialID, counter } = authenticatorInfo;
const existingDevice = user.devices.find(device => device.credentialID === base64CredentialID);
@@ -236,7 +216,7 @@ app.post('/verify-assertion', (req, res) => {
let dbAuthenticator;
// "Query the DB" here for an authenticator matching `credentialID`
- for (let dev of user.devices) {
+ for (const dev of user.devices) {
if (dev.credentialID === body.id) {
dbAuthenticator = dev;
break;
@@ -244,15 +224,15 @@ app.post('/verify-assertion', (req, res) => {
}
if (!dbAuthenticator) {
- throw new Error('could not find authenticator matching', body.id);
+ throw new Error(`could not find authenticator matching ${body.id}`);
}
let verification;
try {
verification = verifyAssertionResponse({
credential: body,
- expectedChallenge,
- expectedOrigin: origin,
+ expectedChallenge: `${expectedChallenge}`,
+ expectedOrigin,
expectedRPID: rpID,
authenticator: dbAuthenticator,
});
@@ -283,5 +263,5 @@ https
app,
)
.listen(port, host, () => {
- console.log(`🚀 Server ready at https://${host}:${port}`);
+ console.log(`🚀 Server ready at https://${rpID} (${host}:${port})`);
});
diff --git a/example/package-lock.json b/example/package-lock.json
index 2c5f201..8165712 100644
--- a/example/package-lock.json
+++ b/example/package-lock.json
@@ -171,6 +171,12 @@
"picomatch": "^2.0.4"
}
},
+ "arg": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
+ "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
+ "dev": true
+ },
"array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
@@ -287,6 +293,12 @@
"resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
"integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8="
},
+ "buffer-from": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
+ "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
+ "dev": true
+ },
"bytes": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
@@ -506,6 +518,12 @@
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
},
+ "diff": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+ "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+ "dev": true
+ },
"dot-prop": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz",
@@ -515,6 +533,11 @@
"is-obj": "^2.0.0"
}
},
+ "dotenv": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz",
+ "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw=="
+ },
"duplexer3": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz",
@@ -950,6 +973,12 @@
}
}
},
+ "make-error": {
+ "version": "1.3.6",
+ "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
+ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
+ "dev": true
+ },
"media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
@@ -1361,6 +1390,22 @@
"integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==",
"dev": true
},
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ },
+ "source-map-support": {
+ "version": "0.5.19",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
+ "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
+ "dev": true,
+ "requires": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ }
+ },
"statuses": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
@@ -1465,6 +1510,19 @@
"nopt": "~1.0.10"
}
},
+ "ts-node": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.0.0.tgz",
+ "integrity": "sha512-/TqB4SnererCDR/vb4S/QvSZvzQMJN8daAslg7MeaiHvD8rDZsSfXmNeNumyZZzMned72Xoq/isQljYSt8Ynfg==",
+ "dev": true,
+ "requires": {
+ "arg": "^4.1.0",
+ "diff": "^4.0.1",
+ "make-error": "^1.1.1",
+ "source-map-support": "^0.5.17",
+ "yn": "3.1.1"
+ }
+ },
"tslib": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz",
@@ -1494,6 +1552,12 @@
"is-typedarray": "^1.0.0"
}
},
+ "typescript": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.5.tgz",
+ "integrity": "sha512-ywmr/VrTVCmNTJ6iV2LwIrfG1P+lv6luD8sUJs+2eI9NLGigaN+nUQc13iHqisq7bra9lnmUSYqbJvegraBOPQ==",
+ "dev": true
+ },
"undefsafe": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.3.tgz",
@@ -1589,6 +1653,12 @@
"resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz",
"integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==",
"dev": true
+ },
+ "yn": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
+ "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
+ "dev": true
}
}
}
diff --git a/example/package.json b/example/package.json
index ae99dbb..3f76907 100644
--- a/example/package.json
+++ b/example/package.json
@@ -4,17 +4,21 @@
"description": "",
"main": "index.js",
"scripts": {
- "start": "nodemon index.js"
+ "start": "nodemon index.ts",
+ "build": "tsc"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"@simplewebauthn/server": "0.10.3",
+ "dotenv": "^8.2.0",
"express": "^4.17.1",
"node-fetch": "^2.6.0"
},
"devDependencies": {
- "nodemon": "^2.0.4"
+ "nodemon": "^2.0.4",
+ "ts-node": "^9.0.0",
+ "typescript": "^4.0.5"
}
}
diff --git a/example/tsconfig.json b/example/tsconfig.json
new file mode 100644
index 0000000..8cdf351
--- /dev/null
+++ b/example/tsconfig.json
@@ -0,0 +1,17 @@
+{
+ "compilerOptions": {
+ "target": "ES2018",
+ "lib": ["ES2018", "DOM"],
+ "module": "commonjs",
+ "esModuleInterop": true,
+ "declaration": true,
+ "sourceMap": true,
+ "allowSyntheticDefaultImports": true,
+ "strict": true,
+ "outDir": "dist"
+ },
+ "exclude": [
+ "node_modules",
+ "**/dist/*"
+ ]
+}
diff --git a/package-lock.json b/package-lock.json
index 8cd8f9c..a726a74 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2363,6 +2363,12 @@
"@types/node": ">= 8"
}
},
+ "@simplewebauthn/typescript-types": {
+ "version": "0.10.0",
+ "resolved": "https://registry.npmjs.org/@simplewebauthn/typescript-types/-/typescript-types-0.10.0.tgz",
+ "integrity": "sha512-Pvg3I76o1YNt7FYOe5K3upsuLwR2Q6xSu6ofGrs/ltIFdTXI2JLCJzTvOIsnIXNSBWY4Wp3c9gizb8mn6Q6ntw==",
+ "dev": true
+ },
"@sinonjs/commons": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.0.tgz",
@@ -2422,18 +2428,60 @@
"@babel/types": "^7.3.0"
}
},
+ "@types/body-parser": {
+ "version": "1.19.0",
+ "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz",
+ "integrity": "sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ==",
+ "dev": true,
+ "requires": {
+ "@types/connect": "*",
+ "@types/node": "*"
+ }
+ },
"@types/color-name": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
"integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==",
"dev": true
},
+ "@types/connect": {
+ "version": "3.4.33",
+ "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz",
+ "integrity": "sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*"
+ }
+ },
"@types/eslint-visitor-keys": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
"integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==",
"dev": true
},
+ "@types/express": {
+ "version": "4.17.9",
+ "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.9.tgz",
+ "integrity": "sha512-SDzEIZInC4sivGIFY4Sz1GG6J9UObPwCInYJjko2jzOf/Imx/dlpume6Xxwj1ORL82tBbmN4cPDIDkLbWHk9hw==",
+ "dev": true,
+ "requires": {
+ "@types/body-parser": "*",
+ "@types/express-serve-static-core": "*",
+ "@types/qs": "*",
+ "@types/serve-static": "*"
+ }
+ },
+ "@types/express-serve-static-core": {
+ "version": "4.17.13",
+ "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.13.tgz",
+ "integrity": "sha512-RgDi5a4nuzam073lRGKTUIaL3eF2+H7LJvJ8eUnCI0wA6SNjXc44DCmWNiTLs/AZ7QlsFWZiw/gTG3nSQGL0fA==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*",
+ "@types/qs": "*",
+ "@types/range-parser": "*"
+ }
+ },
"@types/glob": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz",
@@ -2494,6 +2542,12 @@
"integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==",
"dev": true
},
+ "@types/mime": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.3.tgz",
+ "integrity": "sha512-Jus9s4CDbqwocc5pOAnh8ShfrnMcPHuJYzVcSUU7lrh8Ni5HuIqX3oilL86p3dlTrk0LzHRCgA/GQ7uNCw6l2Q==",
+ "dev": true
+ },
"@types/minimatch": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
@@ -2512,6 +2566,29 @@
"integrity": "sha512-k3NqigXWRzQZVBDS5D1U70A5E8Qk4Kh+Ha/x4M8Bt9pF0X05eggfnC9+63Usc9Q928hRUIpIhTQaXsZwZBl4Ew==",
"dev": true
},
+ "@types/node-fetch": {
+ "version": "2.5.7",
+ "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.7.tgz",
+ "integrity": "sha512-o2WVNf5UhWRkxlf6eq+jMZDu7kjgpgJfl4xVNlvryc95O/6F2ld8ztKX+qu+Rjyet93WAWm5LjeX9H5FGkODvw==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*",
+ "form-data": "^3.0.0"
+ },
+ "dependencies": {
+ "form-data": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz",
+ "integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==",
+ "dev": true,
+ "requires": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "mime-types": "^2.1.12"
+ }
+ }
+ }
+ },
"@types/normalize-package-data": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
@@ -2530,6 +2607,28 @@
"integrity": "sha512-5qOlnZscTn4xxM5MeGXAMOsIOIKIbh9e85zJWfBRVPlRMEVawzoPhINYbRGkBZCI8LxvBe7tJCdWiarA99OZfQ==",
"dev": true
},
+ "@types/qs": {
+ "version": "6.9.5",
+ "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.5.tgz",
+ "integrity": "sha512-/JHkVHtx/REVG0VVToGRGH2+23hsYLHdyG+GrvoUGlGAd0ErauXDyvHtRI/7H7mzLm+tBCKA7pfcpkQ1lf58iQ==",
+ "dev": true
+ },
+ "@types/range-parser": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz",
+ "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==",
+ "dev": true
+ },
+ "@types/serve-static": {
+ "version": "1.13.7",
+ "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.7.tgz",
+ "integrity": "sha512-3diZWucbR+xTmbDlU+FRRxBf+31OhFew7cJXML/zh9NmvSPTNoFecAwHB66BUqFgENJtqMiyl7JAwUE/siqdLw==",
+ "dev": true,
+ "requires": {
+ "@types/mime": "*",
+ "@types/node": "*"
+ }
+ },
"@types/stack-utils": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz",
diff --git a/package.json b/package.json
index 8e2fc70..62a8a12 100644
--- a/package.json
+++ b/package.json
@@ -11,7 +11,10 @@
"build:server": "lerna bootstrap --scope=@simplewebauthn/server"
},
"devDependencies": {
+ "@simplewebauthn/typescript-types": "^0.10.0",
+ "@types/express": "^4.17.9",
"@types/jest": "^25.2.3",
+ "@types/node-fetch": "^2.5.7",
"@typescript-eslint/eslint-plugin": "^3.10.1",
"@typescript-eslint/parser": "^3.10.1",
"eslint": "^7.8.1",