MasterKale / SimpleWebAuthn

WebAuthn, Simplified. A collection of TypeScript-first libraries for simpler WebAuthn integration. Supports modern browsers, Node, Deno, and more.
https://simplewebauthn.dev
MIT License
1.63k stars 137 forks source link

feat/settings-service #138

Closed MasterKale closed 3 years ago

MasterKale commented 3 years ago

This PR adds a new SettingsService singleton for managing various configurable aspects of @simplewebauthn/server. The first such configurable options are the root certs used for verifying registration responses containing android-safetynet and apple attestation statement formats.

The service allows for setting root certificates as either PEM-formatted string's, or Buffer's:

import { SettingsService } from '@simplewebauthn/server';

SettingsService.setRootCertificate({
  attestationFormat: 'android-safetynet',
  // Buffer
  certificates: [
    fs.readFileSync(path.resolve(__dirname, './GSRCA.crt')),
    fs.readFileSync(path.resolve(__dirname, './GSR2.crt')),
  ],
});

SettingsService.setRootCertificate({
  attestationFormat: 'apple',
  // PEM-formatted String
  certificates: [
    fs.readFileSync(path.resolve(__dirname, './Apple_WebAuthn_Root_CA.pem'), { encoding: 'utf-8' }),
  ],
});

Certificates for a format can be retrieved with the fmt of the attestation statement:

// Array of PEM-formatted strings (or an empty array if no cert is set for the given format)
const rootCertificates: string[] = settingsService.getRootCertificate({ attestationFormat: fmt });

When root certificates are registered for an attestation format, an attempt will be made to validate the certificate path using each root cert until a complete path is formed. If an empty array is set for a specific attestation format then certificate path validation will not occur!

The following root certificates are now registered via this API as default certificates for the following formats:

Root certs can be specified for all attestation formats. The specific certificates mentioned above can be overwritten by calling SettingsService.setRootCertificate() with the same attestationFormat identifier and a new array of certificates.

A quick note...

How these certificates are read is intentionally left up to the RP developer. There are a lot of different ways to retrieve these certificates, including HTTPS requests out to the internet, network requests to vaults on an intranet, direct access to the filesystem...trying to define a method to support all such potential use cases would quickly turn into a maintenance nightmare. Use of SettingsService is a more advanced feature of the library that most developers can ignore, so I have chosen to leave it to developers who opt into its use to retrieve certificates in a way that's most appropriate for their implementation.

MasterKale commented 3 years ago

@jstewmon If this comment makes it your way I'd love your feedback on this feature. It was inspired by our conversation in #125.