webauthn

package module
v0.0.0-...-a1cc377 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 17, 2026 License: BSD-3-Clause Imports: 6 Imported by: 0

README

go-ruby-webauthn/webauthn

webauthn — go-ruby-webauthn

Go Reference CI Coverage

A pure-Go (no cgo), MRI-faithful reimplementation of Ruby's webauthn gem — the WebAuthn / passkeys relying-party library. It builds the ceremony options a browser passes to navigator.credentials.create() / .get(), and verifies the authenticator's responses (attestation and assertion) against a configured relying party — without any Ruby runtime and with CGO_ENABLED=0.

It completes the go-ruby-* authentication family alongside go-ruby-jwt, go-ruby-oauth2 and the OIDC/SAML siblings, and is the WebAuthn backend for go-embedded-ruby.

What it consumes

The cryptographic heavy lifting is delegated to the pure-Go github.com/go-webauthn/webauthn library: CBOR decoding (fxamacker/cbor), COSE key parsing, the attestation-format verifiers (packed, fido-u2f, none, tpm, android-key, android-safetynet, apple, compound) and the EC2/RSA/OKP signature checks. This package supplies the gem-shaped orchestration — option builders, the staged ceremony verification, the sign-count regression rule and the WebAuthn::Error exception tree — on top.

MRI-faithful surface

Ruby (webauthn gem) Go (this package)
WebAuthn::RelyingParty.new(origin:, name:, id:) webauthn.NewRelyingParty(origin, name, id)
WebAuthn::Credential.options_for_create(...) (*RelyingParty).OptionsForCreate(CreateOptions)
WebAuthn::Credential.options_for_get(...) (*RelyingParty).OptionsForGet(GetOptions)
WebAuthn::Credential.from_create(response) (*RelyingParty).FromCreate(response)
credential.verify(expected_challenge) (*RegistrationCredential).Verify(challenge)
WebAuthn::Credential.from_get(response) (*RelyingParty).FromGet(response)
credential.verify(challenge, public_key:, sign_count:) (*AuthenticationCredential).Verify(challenge, publicKey, signCount)
WebAuthn::PublicKey webauthn.PublicKey
WebAuthn::Error and subclasses webauthn.Error sentinels (errors.Is)
Registration
rp := webauthn.NewRelyingParty("https://example.com", "Example", "example.com")

opts, _ := rp.OptionsForCreate(webauthn.CreateOptions{
    User: webauthn.User{ID: userID, Name: "amy", DisplayName: "Amy"},
})
// send opts to the browser; store opts.Challenge

cred, _ := rp.FromCreate(clientResponseJSON)
if err := cred.Verify(storedChallenge); err != nil { /* ... */ }
// persist: cred.ID(), cred.PublicKey().COSEKey(), cred.SignCount()
Authentication
opts, _ := rp.OptionsForGet(webauthn.GetOptions{Allow: [][]byte{credID}})
// send opts to the browser; store opts.Challenge

auth, _ := rp.FromGet(clientResponseJSON)
if err := auth.Verify(storedChallenge, storedPublicKey, storedSignCount); err != nil {
    if errors.Is(err, webauthn.SignCountVerificationError) { /* possible clone */ }
}
// persist the new sign count: auth.SignCount()

Errors

Every failure is a *webauthn.Error matched with errors.Is against a sentinel that mirrors the gem's exception class: ChallengeVerificationError, OriginVerificationError, TypeVerificationError, RpIdVerificationError, UserPresenceVerificationError, UserVerificationError, SignatureVerificationError, SignCountVerificationError, AttestationStatementVerificationError, all rooted at ErrWebAuthn.

Attestation scope

Attestation-statement verification (packed / fido-u2f / none / tpm / android-key / android-safetynet / apple / compound) is provided by go-webauthn. This package performs self / none attestation trust decisions; it does not ship a FIDO Metadata Service (MDS) trust-anchor store, so full attestation-certificate chain validation against a metadata provider is out of scope (mirroring a relying party configured without an MDS).

Tests & coverage

go test ./... is fully deterministic: a software authenticator fabricates the registration and authentication ceremony fixtures from a fixed key and fixed challenges, so the suite needs no network, no Ruby and no security key. Each tampered case — wrong challenge, wrong origin, bad signature, sign-count regression, wrong RP ID hash, invalid attestation — is asserted to be rejected with the right error. CI enforces 100% statement coverage under -race and builds/tests on all six 64-bit Go targets — amd64, arm64, riscv64, loong64, ppc64le and the big-endian s390x — with CGO_ENABLED=0.

License

BSD-3-Clause. Copyright (c) the go-ruby-webauthn/webauthn authors.

WebAssembly

Being pure Go (CGO=0), this library also compiles to WebAssembly — both GOOS=js GOARCH=wasm (browser / Node.js) and GOOS=wasip1 GOARCH=wasm (WASI). CI builds both targets on every push, alongside the six 64-bit native/qemu arches.

GOOS=js     GOARCH=wasm go build ./...   # browser / Node
GOOS=wasip1 GOARCH=wasm go build ./...   # WASI (wasmtime, wasmer, wasmedge, …)

Documentation

Overview

Package webauthn is a pure-Go (CGO=0), MRI-faithful reimplementation of the Ruby webauthn gem — the WebAuthn / passkeys relying-party library.

It mirrors the gem's surface: a RelyingParty (the gem's WebAuthn::RelyingParty / WebAuthn::Configuration) builds ceremony options with RelyingParty.OptionsForCreate and RelyingParty.OptionsForGet, and verifies client responses via RelyingParty.FromCreate / RegistrationCredential.Verify (registration) and RelyingParty.FromGet / AuthenticationCredential.Verify (authentication). Failures surface as the Error tree that mirrors WebAuthn::Error and its subclasses (ChallengeVerificationError, OriginVerificationError, SignatureVerificationError, SignCountVerificationError, AttestationStatementVerificationError, …).

The heavy lifting — CBOR decoding, COSE key parsing, attestation-format verification (packed, fido-u2f, none, tpm, android-key, apple, …) and signature checks — is delegated to the pure-Go github.com/go-webauthn/webauthn library; this package supplies the gem-shaped orchestration and error mapping on top. All verification is endianness- and timezone-independent and builds with CGO disabled on every supported 64-bit target, including the big-endian s390x.

Index

Constants

View Source
const DefaultChallengeLength = 32

DefaultChallengeLength is the number of random bytes used for a ceremony challenge when the caller does not supply one, matching the webauthn gem's default (WebAuthn.configuration.encoding aside, 32 bytes of entropy).

Variables

View Source
var (
	// ErrWebAuthn is the root of the tree, corresponding to WebAuthn::Error.
	// Every other sentinel reports true for errors.Is against a value produced
	// from the same class only; use the specific sentinels to discriminate.
	ErrWebAuthn = &Error{class: "Error", msg: "webauthn error"}

	// ChallengeVerificationError is raised when the challenge echoed back in the
	// client data does not match the challenge the relying party issued.
	ChallengeVerificationError = &Error{class: "ChallengeVerificationError", msg: "challenge verification failed"}

	// OriginVerificationError is raised when the client data origin does not
	// match the relying party origin.
	OriginVerificationError = &Error{class: "OriginVerificationError", msg: "origin verification failed"}

	// TypeVerificationError is raised when the client data type is not the value
	// expected for the ceremony (webauthn.create or webauthn.get).
	TypeVerificationError = &Error{class: "TypeVerificationError", msg: "type verification failed"}

	// RpIdVerificationError is raised when the RP ID hash in the authenticator
	// data does not match the SHA-256 of the relying party ID.
	RpIdVerificationError = &Error{class: "RpIdVerificationError", msg: "RP ID hash verification failed"}

	// UserPresenceVerificationError is raised when the user-present flag is not
	// set in the authenticator data.
	UserPresenceVerificationError = &Error{class: "UserPresenceVerificationError", msg: "user presence verification failed"}

	// UserVerificationError is raised when user verification was required but the
	// user-verified flag is not set in the authenticator data.
	UserVerificationError = &Error{class: "UserVerificationError", msg: "user verification failed"}

	// SignatureVerificationError is raised when the assertion signature does not
	// verify against the stored credential public key.
	SignatureVerificationError = &Error{class: "SignatureVerificationError", msg: "signature verification failed"}

	// SignCountVerificationError is raised when the authenticator sign count did
	// not increase relative to the stored value (a possible cloned credential).
	SignCountVerificationError = &Error{class: "SignCountVerificationError", msg: "sign count verification failed"}

	// AttestationStatementVerificationError is raised when the attestation
	// statement in a registration response fails verification.
	AttestationStatementVerificationError = &Error{class: "AttestationStatementVerificationError", msg: "attestation statement verification failed"}

	// AuthenticatorDataVerificationError is raised when the authenticator data is
	// malformed or too short to be parsed.
	AuthenticatorDataVerificationError = &Error{class: "AuthenticatorDataVerificationError", msg: "authenticator data verification failed"}

	// ClientDataMissingError is raised when the client response could not be
	// parsed into a well-formed credential.
	ClientDataMissingError = &Error{class: "ClientDataMissingError", msg: "client data missing or malformed"}
)

The exported sentinels mirror WebAuthn::Error and its subclasses. Wrap them with fmt.Errorf("%w", …) checks via errors.Is(err, ChallengeVerificationError) and friends.

Functions

This section is empty.

Types

type AuthenticationCredential

type AuthenticationCredential struct {
	// contains filtered or unexported fields
}

AuthenticationCredential mirrors the object returned by WebAuthn::Credential.from_get. Call Verify with the stored credential public key and sign count to validate the assertion.

func (*AuthenticationCredential) ID

func (c *AuthenticationCredential) ID() []byte

ID returns the raw credential ID from the assertion.

func (*AuthenticationCredential) Response

Response returns the decoded assertion response.

func (*AuthenticationCredential) SignCount

func (c *AuthenticationCredential) SignCount() uint32

SignCount returns the sign count reported by the authenticator in the verified assertion. Persist it as the new stored sign count.

func (*AuthenticationCredential) Verify

func (c *AuthenticationCredential) Verify(expectedChallenge, publicKey []byte, storedSignCount uint32, opts ...VerifyOption) error

Verify mirrors AuthenticatorAssertionResponse#verify(expected_challenge, public_key:, sign_count:). It checks the client data type, challenge, origin, RP ID hash and user flags, verifies the assertion signature against the stored COSE public key, and enforces the sign-count regression rule. storedSignCount is the sign count persisted at registration (or the previous assertion).

type AuthenticatorAssertionResponse

type AuthenticatorAssertionResponse struct {
	// contains filtered or unexported fields
}

AuthenticatorAssertionResponse mirrors WebAuthn::AuthenticatorAssertionResponse: the authenticator's answer to an authentication ceremony (authenticatorData, signature and clientDataJSON), already decoded.

func (*AuthenticatorAssertionResponse) AuthenticatorData

func (r *AuthenticatorAssertionResponse) AuthenticatorData() []byte

AuthenticatorData returns the raw authenticatorData bytes.

func (*AuthenticatorAssertionResponse) ClientDataJSON

func (r *AuthenticatorAssertionResponse) ClientDataJSON() []byte

ClientDataJSON returns the raw clientDataJSON bytes.

func (*AuthenticatorAssertionResponse) Signature

func (r *AuthenticatorAssertionResponse) Signature() []byte

Signature returns the raw assertion signature bytes.

type AuthenticatorAttestationResponse

type AuthenticatorAttestationResponse struct {
	// contains filtered or unexported fields
}

AuthenticatorAttestationResponse mirrors WebAuthn::AuthenticatorAttestationResponse: the authenticator's answer to a registration ceremony (attestationObject and clientDataJSON), already decoded.

func (*AuthenticatorAttestationResponse) AttestationObject

func (r *AuthenticatorAttestationResponse) AttestationObject() []byte

AttestationObject returns the raw CBOR attestationObject bytes.

func (*AuthenticatorAttestationResponse) ClientDataJSON

func (r *AuthenticatorAttestationResponse) ClientDataJSON() []byte

ClientDataJSON returns the raw clientDataJSON bytes.

type CreateOptions

type CreateOptions struct {
	// User is the account the credential is being created for.
	User User

	// Exclude is the list of existing credential IDs to place in
	// excludeCredentials so the authenticator refuses to re-register.
	Exclude [][]byte

	// AuthenticatorSelection constrains the authenticators the client may use.
	AuthenticatorSelection *protocol.AuthenticatorSelection

	// UserVerification sets authenticatorSelection.userVerification when
	// AuthenticatorSelection is nil.
	UserVerification protocol.UserVerificationRequirement

	// Attestation is the attestation conveyance preference (none, indirect,
	// direct, enterprise).
	Attestation protocol.ConveyancePreference

	// Challenge, when non-empty, is used verbatim instead of a fresh random
	// challenge. Supply it to make a ceremony deterministic.
	Challenge []byte
}

CreateOptions are the inputs to OptionsForCreate, mirroring the keyword arguments of WebAuthn::Credential.options_for_create.

type Error

type Error struct {
	// contains filtered or unexported fields
}

Error is the common type for every error raised by this package. It mirrors the exception tree of Ruby's webauthn gem, where every error descends from WebAuthn::Error. The Class method returns the Ruby class name of the corresponding exception (for example "ChallengeVerificationError"), and errors.Is matches an error against one of the exported sentinels regardless of any wrapped cause or contextual message.

func (*Error) Class

func (e *Error) Class() string

Class returns the name of the corresponding Ruby WebAuthn exception class.

func (*Error) Error

func (e *Error) Error() string

Error implements the error interface.

func (*Error) Is

func (e *Error) Is(target error) bool

Is reports whether target is a *Error describing the same failure class. This makes the exported sentinels usable with errors.Is even after a cause or a custom message has been attached.

func (*Error) Unwrap

func (e *Error) Unwrap() error

Unwrap returns the wrapped cause, if any, so that errors.Unwrap and errors.As traverse into the underlying go-webauthn error.

type GetOptions

type GetOptions struct {
	// Allow is the list of credential IDs to place in allowCredentials.
	Allow [][]byte

	// UserVerification sets the userVerification requirement.
	UserVerification protocol.UserVerificationRequirement

	// Challenge, when non-empty, is used verbatim instead of a fresh random
	// challenge.
	Challenge []byte
}

GetOptions are the inputs to OptionsForGet, mirroring the keyword arguments of WebAuthn::Credential.options_for_get.

type PublicKey

type PublicKey struct {
	// contains filtered or unexported fields
}

PublicKey mirrors WebAuthn::PublicKey: a thin wrapper over a COSE_Key encoded credential public key. It exposes the raw COSE bytes (what a relying party stores next to a credential) and can verify a signature over arbitrary data, delegating the COSE/CBOR decoding and the EC2/RSA/OKP signature checks to go-webauthn's webauthncose package.

func NewPublicKey

func NewPublicKey(cose []byte) (*PublicKey, error)

NewPublicKey decodes a COSE_Key encoded credential public key. It returns an error wrapping UnsupportedKey semantics when the bytes are not a valid COSE key of a supported type.

func (*PublicKey) COSEKey

func (p *PublicKey) COSEKey() []byte

COSEKey returns the raw COSE_Key encoding of the public key. This is the value a relying party persists and later passes back to AuthenticationCredential.Verify.

func (*PublicKey) Verify

func (p *PublicKey) Verify(data, sig []byte) (bool, error)

Verify reports whether sig is a valid signature over data for this public key. The hashing implied by the COSE algorithm is applied by the underlying verifier.

type RegistrationCredential

type RegistrationCredential struct {
	// contains filtered or unexported fields
}

RegistrationCredential mirrors the object returned by WebAuthn::Credential.from_create. Call Verify to validate the attestation against the relying party; afterwards the credential ID, public key and sign count are available.

func (*RegistrationCredential) AttestationFormat

func (c *RegistrationCredential) AttestationFormat() string

AttestationFormat returns the attestation statement format (e.g. "none", "packed", "fido-u2f").

func (*RegistrationCredential) AttestationType

func (c *RegistrationCredential) AttestationType() string

AttestationType returns the attestation trust type reported by the verifier (e.g. "none", "basic_full", "basic_surrogate").

func (*RegistrationCredential) ID

func (c *RegistrationCredential) ID() []byte

ID returns the raw credential ID. It is only populated after a successful Verify.

func (*RegistrationCredential) PublicKey

func (c *RegistrationCredential) PublicKey() *PublicKey

PublicKey returns the credential's COSE public key wrapper. It is only populated after a successful Verify.

func (*RegistrationCredential) Response

Response returns the decoded attestation response.

func (*RegistrationCredential) SignCount

func (c *RegistrationCredential) SignCount() uint32

SignCount returns the initial authenticator sign count.

func (*RegistrationCredential) Verify

func (c *RegistrationCredential) Verify(expectedChallenge []byte, opts ...VerifyOption) error

Verify mirrors AuthenticatorAttestationResponse#verify(expected_challenge). It checks, in order, the client data type, the challenge, the origin, the RP ID hash and the user-presence/verification flags, then delegates attestation statement verification (packed / fido-u2f / none / …) to go-webauthn. On success the credential ID, public key and sign count are populated.

type RelyingParty

type RelyingParty struct {
	// Origin is the fully-qualified origin the browser reports, e.g.
	// "https://example.com".
	Origin string

	// Name is the human-palatable relying party name shown to the user.
	Name string

	// ID is the RP ID, an effective domain such as "example.com".
	ID string

	// Algorithms is the ordered list of acceptable COSE algorithm names offered
	// in pubKeyCredParams. When empty the gem defaults ES256, PS256 and RS256
	// are used.
	Algorithms []string

	// Timeout, when non-zero, is echoed into the ceremony options in
	// milliseconds.
	Timeout int
}

RelyingParty mirrors WebAuthn::RelyingParty / WebAuthn::Configuration. It carries the identity of the relying party (origin, name and RP ID) plus the list of acceptable COSE algorithms, and is the entry point for building ceremony options and verifying client responses.

Construct one with NewRelyingParty:

rp := webauthn.NewRelyingParty("https://example.com", "Example", "example.com")

func NewRelyingParty

func NewRelyingParty(origin, name, id string) *RelyingParty

NewRelyingParty builds a RelyingParty with the default algorithm set.

func (*RelyingParty) FromCreate

func (rp *RelyingParty) FromCreate(clientResponse []byte) (*RegistrationCredential, error)

FromCreate mirrors WebAuthn::Credential.from_create: it parses the JSON client response produced by navigator.credentials.create() into a RegistrationCredential bound to this relying party. It does not yet verify the attestation; call Verify for that.

func (*RelyingParty) FromGet

func (rp *RelyingParty) FromGet(clientResponse []byte) (*AuthenticationCredential, error)

FromGet mirrors WebAuthn::Credential.from_get: it parses the JSON client response produced by navigator.credentials.get() into an AuthenticationCredential bound to this relying party.

func (*RelyingParty) OptionsForCreate

OptionsForCreate mirrors WebAuthn::Credential.options_for_create: it returns a PublicKeyCredentialCreationOptions carrying the challenge, relying party, user and pubKeyCredParams that the browser passes to navigator.credentials.create(). The returned challenge is the value to store and later hand to RegistrationCredential.Verify.

func (*RelyingParty) OptionsForGet

OptionsForGet mirrors WebAuthn::Credential.options_for_get: it returns a PublicKeyCredentialRequestOptions carrying the challenge, RP ID and allowCredentials for navigator.credentials.get().

type User

type User struct {
	ID          []byte
	Name        string
	DisplayName string
}

User mirrors the user account passed to WebAuthn::Credential.options_for_create (a PublicKeyCredentialUserEntity). ID is the opaque user handle.

type VerifyOption

type VerifyOption func(*verifyConfig)

VerifyOption tunes a call to RegistrationCredential.Verify or AuthenticationCredential.Verify, mirroring the optional keyword arguments of the webauthn gem's #verify methods.

func RequireUserVerification

func RequireUserVerification() VerifyOption

RequireUserVerification asserts that the authenticator set the user-verified (UV) flag, mirroring passing user_verification: true to the gem.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL