Module Webs_passkey

WebAuthn passkey authentication.

This module provides primitives to handle passkeys as your authentication mecanism.

While it makes a few choices on what to expose of that terrible mess, it does not prescribe a particular user interface workflow or passkey storage. It is only concerned about extracting data from the client into proper data structures and provides primitives for handling challenges, passkey registrations and verifying client provided assertions.

Note. The support provided by this module is thread-safe.

TODO.

References.

module Challenge : sig ... end

Unique, expirable, challenges.

module Aaguid : sig ... end

Authenticator attestation GUIDs (AAGUID).

module Credential_id : sig ... end

Credential IDs.

module Public_key : sig ... end

Public keys.

module Passkey : sig ... end

Passkeys (client provided public keys).

module Registration : sig ... end

Registrations (client passkey registration).

module Assertion : sig ... end

Assertions (client signed challenges).

module Relying_party : sig ... end

Relying party (registers and verifies client assertions).

Register and verify passkeys

type error =
  1. | Invalid_relying_party_origin of {
    1. found : string;
    2. expected : string;
    }
  2. | Invalid_relying_party_hash of {
    1. found : Bytesrw_crypto.Sha_256.t;
    2. expected : Bytesrw_crypto.Sha_256.t;
    }
  3. | Invalid_client_data_type of {
    1. found : string;
    2. expected : string;
    }
  4. | Invalid_challenge of Challenge.t
  5. | Invalid_signature of {
    1. signature : string;
      (*

      binary

      *)
    2. msg : string;
    }

The type for operation errors.

val error_message : error -> string

error_message e is an error message for e.

val challenge : ?validity_s:int -> 'a Relying_party.t -> payload:'a -> Challenge.t

challenge rp is a challenge generated by rp. Note this library also uses a challenge for register.

val register : 'a Relying_party.t -> Registration.t -> ('a * Passkey.t, error) Stdlib.result

register rp r registers a new passkey from registration data r. This verifies that (and only that), in order:

  1. The client data's type is webauth.create
  2. The client data's origin matches the relying party identifier of rp.
  3. The authenticator data SHA-256 hash of the relying party identifier is the one of rp.
  4. The authenticator SHA-256 hash of the relying party identifier is correct.
  5. The challenge still exists and was never used.
  6. The registration's origin and relying party identifier matches the one in rp
val verify : 'a Relying_party.t -> Passkey.t -> Assertion.t -> ('a, error) Stdlib.result

verify rp pub_key assertion is Ok () if assertion is validated by pub_key and a. This verifies that (and only that), in order:

  1. The client data's type is webauth.get
  2. The client data's origin matches the relying party identifier of rp.
  3. The authenticator data SHA-256 hash of the relying party identifier is the one of rp.
  4. The assertion signature is verified by the public key of pub_key.
  5. The challenge still exists and was never used.

Note. It is your duty to find out which pub_key must be used to validate assertion, by looking them up somewhere via Assertion.credential_id. Note that formally only the Passkey.public_key field is needed to perform the check, the rest is metadata. you can use Passkey.of_public_key if that's the only thing you have.

Basics

TODO meanwhile checkout this example.