webauthn-open-source / fido2-lib

A node.js library for performing FIDO 2.0 / WebAuthn server functionality
https://webauthn.io
MIT License
408 stars 120 forks source link

Node/Deno CI Code Coverage Known Vulnerabilities npm version OpenSSF Scorecard

Overview

A library for performing FIDO 2.0 / WebAuthn server functionality

This library contains all the functionality necessary for implementing a full FIDO2 / WebAuthn server. It intentionally does not implement any kind of networking protocol (e.g. - REST endpoints) so that it can remain independent of any messaging protocols.

There are four primary functions:

  1. attestationOptions - creates the challenge that will be sent to the client (e.g. - browser) for the credential create call. Note that the library does not keep track of sessions or context, so the caller is expected to associate the resulting challenge with a session so that it can be appropriately matched with a response.
  2. attestationResult - parses and validates the response from the client
  3. assertionOptions - creates the challenge that will be sent to the client for credential assertion.
  4. assertionResult - parses and validates the response from the client

There is also an extension point for adding new attestation formats.

Full documentation can be found here.

For working examples see OWASP Single Sign-On and / or webauthn.io

Features

Getting started

Node

npm install fido2-lib --save

Import Library using CommonJS

const { Fido2Lib } = require("fido2-lib");

Import Library using ESM-syntax

import { Fido2Lib } from "fido2-lib";

Deno

Import dist/main.js from a trusted source. Below is only an example, using the official deno.land repository. It is recommended to enable integrity checking.

import { Fido2Lib } from "https://deno.land/x/fido2@$VERSION/dist/main.js";
Add Type Declarations Firstly, you need to set up a [import map](https://deno.land/manual/basics/import_maps). Put the following lines into your import map. ```json { "imports": { "fido2-lib": "https://deno.land/x/fido2@$VERSION/dist/main.js" } } ``` Then you can import the library like this: ```ts // @deno-types="https://deno.land/x/fido2@$VERSION/types/main.d.ts" import { Fido2Lib } from "fido2-lib"; ```

Don't forget to replace $VERSION with the specific version. You can find the latest version by checking the redirection of deno.land/x/fido2/dist/main.js and deno.land/x/fido2/types/main.d.ts.

Examples

Instantiate Library (Complex):

// could also use one or more of the options below,
// which just makes the options calls easier later on:
const f2l = new Fido2Lib({
    timeout: 42,
    rpId: "example.com",
    rpName: "ACME",
    rpIcon: "https://example.com/logo.png",
    challengeSize: 128,
    attestation: "none",
    cryptoParams: [-7, -257],
    authenticatorAttachment: "platform",
    authenticatorRequireResidentKey: false,
    authenticatorUserVerification: "required"
});

Registration:

const registrationOptions = await f2l.attestationOptions();

// make sure to add registrationOptions.user.id and registrationOptions.user.name
// save the challenge in the session information...
// send registrationOptions to client and pass them in to `navigator.credentials.create()`...
// get response back from client (clientAttestationResponse)

const attestationExpectations = {
    challenge: "33EHav-jZ1v9qwH783aU-j0ARx6r5o-YHh-wd7C6jPbd7Wh6ytbIZosIIACehwf9-s6hXhySHO-HHUjEwZS29w",
    origin: "https://localhost:8443",
    factor: "either"
};
const regResult = await f2l.attestationResult(clientAttestationResponse, attestationExpectations); // will throw on error

// registration complete!
// save publicKey and counter from regResult to user's info for future authentication calls

Authentication:

const authnOptions = await f2l.assertionOptions();

// add allowCredentials to limit the number of allowed credential for the authentication process. For further details refer to webauthn specs: (https://www.w3.org/TR/webauthn-2/#dom-publickeycredentialrequestoptions-allowcredentials).
// save the challenge in the session information...
// send authnOptions to client and pass them in to `navigator.credentials.get()`...
// get response back from client (clientAssertionResponse)

const assertionExpectations = {
    // Remove the following comment if allowCredentials has been added into authnOptions so the credential received will be validate against allowCredentials array.
    // allowCredentials: [{
    //     id: "lTqW8H/lHJ4yT0nLOvsvKgcyJCeO8LdUjG5vkXpgO2b0XfyjLMejRvW5oslZtA4B/GgkO/qhTgoBWSlDqCng4Q==",
    //     type: "public-key",
    //     transports: ["usb"]
    // }],
    challenge: "eaTyUNnyPDDdK8SNEgTEUvz1Q8dylkjjTimYd5X7QAo-F8_Z1lsJi3BilUpFZHkICNDWY8r9ivnTgW7-XZC3qQ",
    origin: "https://localhost:8443",
    factor: "either",
    publicKey: "-----BEGIN PUBLIC KEY-----\n" +
        "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAERez9aO2wBAWO54MuGbEqSdWahSnG\n" +
        "MAg35BCNkaE3j8Q+O/ZhhKqTeIKm7El70EG6ejt4sg1ZaoQ5ELg8k3ywTg==\n" +
        "-----END PUBLIC KEY-----\n",
    prevCounter: 362
};
const authnResult = await f2l.assertionResult(clientAssertionResponse, assertionExpectations); // will throw on error

// authentication complete!

For a real-life example, refer to OWASP Single Sign-On.

Migration from v2 to v3

Generally v3 is assumed to be completely compatible with v2 - compatibility should have increased. As many inner workings have been changed, please verify that your application still works with v3 and report issues, if you newly encounter bugs.

Contributing

Setting up the environment

It's recommended to have both Deno (>=1.20) and Node 16-18 available to be able to run all checks and tests.

Before committing

Please run npm run lint, npm run test and deno task test before committing, to make sure every test and check passes.

See package.json for available npm scripts, and deno.jsonc for available Deno tasks.

Make sure to add tests if you add new features.

Important: Do not stage/commit dist/main.js and dist/main.cjs. These are generated and committed automatically by the CI-pipeline.

Dependencies

When adding, removing or updating dependencies, start out with npm as usual. Then update import_map.json to the same versions shown by npm list, and run deno task update-deps to update the Deno lock-file.

Pull Request

When you're finished with the changes, create a pull request.

Sponsor

Work for this project was supported by Adam Power.