Documentation
¶
Overview ¶
Package webauthn asks Windows to authenticate somebody, in pure Go with CGO_ENABLED=0.
It calls the Win32 WebAuthn API in webauthn.dll — the same one browsers use. Windows finds the authenticator, shows its own dialog, collects a PIN or a fingerprint, and hands back a signed assertion.
Why not CTAP, as on Linux and macOS ¶
Because Windows will not let a normal program talk to a security key at all. Since Windows 10 version 1903, opening a FIDO HID device requires elevation: Microsoft closed that door and pointed everyone at this API. A CTAP transport here would work only for administrators, which is not a program anybody should ship. (The one project that does it anyway installs an elevated Windows service and relays CTAPHID over a named pipe — the size of that workaround is the measure of the wall.)
So this package is shaped differently from its siblings, and deliberately. github.com/go-authn/fido is not underneath it: the protocol is Windows's business here, and what comes back is an assertion, not a CTAP reply.
What was proved, and what only somebody could tell you ¶
An assertion says a credential answered and, through its flags, whether the authenticator verified who was holding it. It does NOT say what convinced it. Windows Hello accepts a face, a fingerprint, or a **PIN** — and a PIN is something known, not something anyone is. A caller that read "user verified" as "biometrics" would be reporting the wrong kind of proof.
Two things narrow it, and this package offers both:
- Request.Attachment constrains what may answer at all. CrossPlatform asks for a security key — something carried. Platform asks for the one built into this machine.
- Assertion.Transport reports what actually answered, when Windows says. Asking is a request; this is an observation, and they can differ.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ( // ErrUnsupported is returned off Windows, and on a Windows too old to have // the API -- it arrived in version 1903. ErrUnsupported = errors.New("webauthn: the Windows WebAuthn API is not available here") // ErrCancelled is returned when the person dismissed the dialog, or the // context ended and the operation was cancelled. ErrCancelled = errors.New("webauthn: the operation was cancelled") )
Functions ¶
Types ¶
type Assertion ¶
type Assertion struct {
// AuthenticatorData is the signed statement: relying-party hash, flags,
// signature counter, and any extension output.
AuthenticatorData []byte
// Signature covers AuthenticatorData followed by the SHA-256 of
// ClientDataJSON.
Signature []byte
// ClientDataJSON is what was hashed into the signature. It is returned
// because a verifier needs the exact bytes, not a reconstruction: rebuild
// it and one different space makes every signature look forged.
ClientDataJSON []byte
// CredentialID says which credential answered, which matters when the
// request allowed several.
CredentialID []byte
// UserID is the user handle, present for a discoverable credential.
UserID []byte
// Transport is what actually answered, or [TransportUnknown] when this
// Windows did not say.
Transport Transport
}
Assertion is what Windows returns.
func Assert ¶
Assert is unavailable off Windows.
It reports ErrUnsupported rather than a failure: a Mac has not refused anybody, it has no Windows WebAuthn API. The macOS and Linux siblings are go-macos/fido and go-gnulinux/fido, over github.com/go-authn/fido.
type Attachment ¶
type Attachment uint32
Attachment says which authenticators may answer.
const ( // AnyAttachment lets Windows offer whatever it has. The answer does not // say which kind was used unless [Assertion.Transport] does. AnyAttachment Attachment = 0 // Platform is the authenticator built into this machine: Windows Hello. Platform Attachment = 1 // CrossPlatform is something carried and plugged in: a security key. CrossPlatform Attachment = 2 )
The attachments, numbered as webauthn.h numbers them.
func (Attachment) String ¶
func (a Attachment) String() string
type Credential ¶
type Credential struct {
// ID is what a registration returned.
ID []byte
}
Credential names one registered credential.
type Error ¶
type Error struct {
// Op is the call that failed.
Op string
// HResult is the code Windows returned.
HResult uint32
// Name is what WebAuthNGetErrorName calls it, when it could be asked.
Name string
}
Error is a failure reported by Windows, carrying the name Windows gives it.
func (*Error) Cancelled ¶
Cancelled reports whether Windows says the person dismissed the dialog.
It is not a failure of authentication: nobody was refused, somebody changed their mind. A caller that reported it as a refusal would be accusing them.
func (*Error) Unavailable ¶
Unavailable reports whether there was nothing here to ask.
NTE_DEVICE_NOT_FOUND and NTE_NOT_FOUND mean no authenticator could serve the request -- no key plugged in, or none holding the credential that was asked for. NTE_NOT_SUPPORTED means this Windows cannot do what was asked. None of those is somebody failing to authenticate, and a policy counts them separately.
type Request ¶
type Request struct {
// RPID is the relying party: the domain the credential belongs to.
RPID string
// Origin is what goes in the client data, and Windows checks that it
// matches RPID. For a program that is not a web page, use
// "https://" + RPID.
Origin string
// Challenge is the bytes to be signed over. It must be fresh: a repeated
// challenge makes a recorded assertion replayable for ever.
Challenge []byte
// Allow lists the credentials that may answer. Empty asks the
// authenticator for a discoverable one, which it has only if a
// registration asked for that.
Allow []Credential
// Attachment constrains what may answer. See the package documentation for
// why this is how a caller keeps a claim about KINDS honest.
Attachment Attachment
// Verification says whether the authenticator must establish who is
// holding it, rather than only that somebody is.
Verification UserVerification
// TimeoutMilliseconds is guidance to Windows, which may override it. Zero
// leaves the choice to Windows.
TimeoutMilliseconds uint32
// Window is the HWND the Windows dialog belongs to. Zero borrows whatever
// is in the foreground, which is what a console program has to do.
//
// A program that HAS a window should pass its own. Borrowing means the
// modal dialog parents to somebody else's window: it can end up behind
// theirs, or move when they move. go-mswin/win32 makes windows; this field
// is a uintptr rather than a win32.HWND because Request is compiled on
// every platform and that type is not.
Window uintptr
}
Request is one authentication.
type Transport ¶
type Transport uint32
Transport is how an authenticator was reached. Windows reports it in the assertion, from version 4 of that structure onwards.
const ( TransportUnknown Transport = 0 TransportUSB Transport = 0x01 TransportNFC Transport = 0x02 TransportBLE Transport = 0x04 TransportTest Transport = 0x08 TransportInternal Transport = 0x10 TransportHybrid Transport = 0x20 TransportSmartCard Transport = 0x40 )
The transports, as the WEBAUTHN_CTAP_TRANSPORT_* bits.
func (Transport) Carried ¶
Carried reports whether the authenticator was a separate object rather than part of this machine.
It is the honest half of the question a policy wants to ask. "internal" is this computer; usb, nfc, ble, smart-card and hybrid are all something brought to it. An UNREPORTED transport answers neither way, and this says so through ok rather than guessing -- older Windows fills in no transport at all, and treating silence as "not carried" would quietly downgrade every security key on those machines.
type UserVerification ¶
type UserVerification uint32
UserVerification says how hard Windows should insist on knowing who is there.
const ( VerificationAny UserVerification = 0 VerificationRequired UserVerification = 1 VerificationPreferred UserVerification = 2 VerificationDiscouraged UserVerification = 3 )
The requirements, numbered as webauthn.h numbers them.
func (UserVerification) String ¶
func (v UserVerification) String() string