webauthn

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Aug 17, 2026 License: MIT Imports: 3 Imported by: 0

README

webauthn

webauthn provides WebAuthn passkey ceremonies for Go compiling to WebAssembly (//go:build wasm), with support for the PRF extension (deterministic secret derivation).

Passkeys & PRF Mechanics

A passkey's private key never leaves the authenticator. During standard authentication ceremonies, JavaScript asks the authenticator to sign a challenge.

CRITICAL WARNING — Signatures are NOT deterministic: Every ceremony produces a different signature because the signature covers authenticatorData (whose counter increments) and clientDataHash (a random challenge). Deriving encryption keys from assertion signatures will fail.

To derive deterministic key material (e.g. for unlocking encrypted local storage), you must use the PRF extension (hmac-secret). Given a fixed salt, the authenticator yields 32 deterministic output bytes released only after user verification.

Salt Warning: Changing the salt changes the derived key and renders previously encrypted data permanently unrecoverable.

Caller Requirements — Gesture & Goroutine Rules

IMPORTANT:

  1. Do not call Create or Get from the main WASM goroutine. Blocking the main goroutine deadlocks the JavaScript event loop. Always invoke Create or Get from a separate goroutine (e.g., go func() { ... }()).
  2. Must be triggered by a direct user gesture. Modern browsers reject WebAuthn requests unless initiated directly within a user gesture event handler (such as a button click).

Quickstart Example

package main

import (
	"syscall/js"

	"github.com/tinywasm/webauthn"
)

func onRegisterButtonClick(this js.Value, args []js.Value) any {
	go func() {
		cred, err := webauthn.Create(webauthn.CreateOptions{
			RPID:             "example.com",
			RPName:           "Example App",
			UserID:           []byte("user_12345"),
			UserName:         "alice@example.com",
			UserDisplayName: "Alice Smith",
			Challenge:        challenge32Bytes,
			ResidentKey:      true,
			UserVerification: "preferred",
			EnablePRF:        true,
		})
		if err != nil {
			// handle error (e.g. webauthn.ErrAborted)
			return
		}
		_ = cred.RawResponse // POST to verifier
	}()
	return nil
}

func onLoginButtonClick(this js.Value, args []js.Value) any {
	go func() {
		assertion, err := webauthn.Get(webauthn.GetOptions{
			RPID:             "example.com",
			Challenge:        challenge32Bytes,
			UserVerification: "preferred",
			PRFSalt:          fixed32ByteSalt,
		})
		if err != nil {
			// handle error
			return
		}
		_ = assertion.PRFOutput // 32 deterministic key bytes (if PRF supported)
	}()
	return nil
}

Documentation

Rendered for js/wasm

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Available added in v0.1.0

func Available() bool

Available reports whether the browser exposes the WebAuthn API at all.

func PlatformAuthenticatorAvailable added in v0.1.0

func PlatformAuthenticatorAvailable() bool

PlatformAuthenticatorAvailable blocks until the browser answers whether a built-in authenticator (Touch ID, Windows Hello, Android biometrics) exists.

Types

type Assertion added in v0.1.0

type Assertion struct {
	CredentialID []byte
	RawResponse  string // base64url JSON, ready to POST to a verifier
	PRFOutput    []byte // 32 bytes, or nil when prf was not requested or not supported
}

Assertion is the result of an assertion ceremony.

func Get added in v0.1.0

func Get(opts GetOptions) (*Assertion, error)

Get runs navigator.credentials.get. Same goroutine and gesture requirements as Create.

type CreateOptions added in v0.1.0

type CreateOptions struct {
	RPID             string // relying party id — the origin's domain, e.g. "app.example.com"
	RPName           string
	UserID           []byte // opaque, stable, never the email
	UserName         string // shown in the account picker
	UserDisplayName  string
	Challenge        []byte // 32 random bytes; server-issued for auth, local for encryption
	ResidentKey      bool   // true for a discoverable passkey
	UserVerification string // "required" | "preferred" | "discouraged"
	EnablePRF        bool   // request the prf extension
}

CreateOptions describes a registration ceremony.

type Credential added in v0.1.0

type Credential struct {
	ID          []byte
	RawResponse string // base64url JSON, ready to POST to a verifier
	PRFEnabled  bool   // the authenticator will honour prf on later assertions
}

Credential is the result of a registration ceremony.

func Create added in v0.1.0

func Create(opts CreateOptions) (*Credential, error)

Create runs navigator.credentials.create. It blocks the calling goroutine until the user completes or cancels the ceremony, and MUST be called from a goroutine started by a user-gesture event handler.

type Error added in v0.1.0

type Error string

Error is a comparable string type representing WebAuthn errors.

const (
	ErrUnavailable    Error = "webauthn: not available in this browser"
	ErrAborted        Error = "webauthn: ceremony cancelled by the user"
	ErrPRFUnsupported Error = "webauthn: authenticator does not support the prf extension"
	ErrNoCredential   Error = "webauthn: no credential returned"
	ErrBadChallenge   Error = "webauthn: challenge must be at least 16 bytes"
)

func (Error) Error added in v0.1.0

func (e Error) Error() string

type GetOptions added in v0.1.0

type GetOptions struct {
	RPID             string
	Challenge        []byte
	AllowCredentials [][]byte // empty means "any discoverable credential"
	UserVerification string
	PRFSalt          []byte // non-nil requests prf evaluation with this salt
}

GetOptions describes an assertion ceremony.

Jump to

Keyboard shortcuts

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