Coder Social home page Coder Social logo

webauthn's People

Contributors

aseigler avatar berwyn avatar christian-2 avatar cmueller-tp avatar dependabot[bot] avatar e3b0c442 avatar fmitra avatar hanzhao-yu avatar james-d-elliott avatar jordan-wright avatar kylelady avatar lambang1 avatar machiel avatar matoous avatar mitar avatar nicksteele avatar printfn avatar pschultz avatar pulsejet avatar renovate[bot] avatar robinbrandt avatar smiller171 avatar step-security-bot avatar tarrencev avatar tobiaszheller avatar torkelrogstad avatar vincentserpoul avatar vvakame avatar zahrakeshtkar avatar zeidlermicha avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

webauthn's Issues

UserEntity.DisplayName should not have omitempty

Version

0.9.1

Description

Its value’s name, displayName and id members are REQUIRED.

but when a user is registering for the first time the RP might not require the user to enter their name and the displayName might be empty, which would then get omitted and cause an error in JavaScript when calling navigator.credentials.create.

Reproduction

  1. Create a user with an empty displayName
  2. Pass user to wa.BeginRegistration.
  3. Encode the result as JSON
  4. Pass to navigator.credentials.create

Expectations

Expectation is that the resulting JSON would contain displayName: '' but instead displayName is undefined.

Documentation

https://w3c.github.io/webauthn/#dom-publickeycredentialcreationoptions-user

metadata: Cached provider codepath may close file before attempting to truncate it

Version

0.11.0

Description

Hello again,

As mentioned in #247 I've updated to v0.11 and had been using the latest changes to the metadata service with the provided cache provider. It was working well until today when the service was restarted, at which point it attempted to update the mds blob.

It kept failing with truncate <file name>: file already closed

I noticed here that the decoder when passed the file is closing the file when it is finished. However just a few lines below, if the data is out of date and a file needs updating, then doTruncateCopyAndSeekStart is called which attempts to truncate the file that the decoder has already closed.

I tried passing the WithForceUpdate true option as a workaround but that fails with "invalid argument", I suspect this is because the file was Opened with READ only, and truncate is a write-level permission.

I think the suggested fix would be to not defer close the passed readcloser in the decoder (perhaps just pass it as a reader), and also open the file with OpenFile passing RW flags rather than just R flags. I can submit a PR if that helps.

Reproduction

Open an outdated MDS blob with the cached provider, default settings should reproduce it. For reference here is my abridged constructor, I pass a NewFunc because I need to be able to configure PermitZeroAAGUID to the underlying memory provider

wconf.MDS, err = cached.New(
			cached.WithClient(common.CleanHTTP()),
			cached.WithPath(conf.MDSPath),
			cached.WithNew(providerFunc(conf.MDSPermitZeroAAGUID)),
		)

// providerFunc
func providerFunc(permitZeroAAGUID bool) cached.NewFunc {
	// This is the same as the default in webauthn/metadata/providers/cached
	// but with a configurable permit zero.
	return func(mds *metadata.Metadata) (metadata.Provider, error) {
		return memory.New(
			memory.WithMetadata(mds.ToMap()),
			memory.WithValidateEntry(true),
			memory.WithValidateEntryPermitZeroAAGUID(permitZeroAAGUID),
			memory.WithValidateTrustAnchor(true),
			memory.WithValidateStatus(true),
		)
	}
}

Expectations

No response

Documentation

No response

Secure Payment Confirmation Support

Description

Adding support for secure payment confirmation would enable developers who already use this library for webauthn to facilitate and validate secure payment confirmation payloads without needing to migrate. Since registration is unaffected only new methods need to be added to support the payment (authentication) flow.

Use Case

Using WebAuthn and SPC to authenticate payments on Chrome and other supported browsers.

Documentation

Secure payment confirmation spec: https://www.w3.org/TR/secure-payment-confirmation/

Google docs: https://developer.chrome.com/articles/secure-payment-confirmation/

Inconsistent base64 encoding/decoding via JSON

Version

0.3.1

Description

The protocol.CredentialCreation object returned by webauthn.BeginRegistration uses the WebauthnID method from the webauthn.User interface to get the Id of the user.

If this object is serialized via the encoding/json module, the field gets serialized as base64 via base64.StdEncoding.

Later, after registration, if the credential is sent back as part of the login process, it will be decoded as the UserHandle portion of a AuthenticatorAssertionResponse, which has the type defined as URLEncodedBase64, which defines a custom json marshaller which builds on base64.RawURLEncoding.

Since these two base64 encoding aren't compatible, parsing the assertion fails and login can't proceed, specifically generating the error "Parse error for Assertion".

Reproduction

This issue can be reproduced by registering a new credential, and returning the result of BeginRegistration directly encoded as json. Finish the registration without changing or parsing the Id on the client side.

Attempt to use the credential without changing the UserHandle field after deserialzing via the standard json encoder.

Expectations

The expected behavior is that the WebauthnID as returned from the interface method would be able to make a roundtrip in this fashion without needed modification on the server or client side.

Documentation

Updating the locations that reference []byte to use URLEncodedBase64 should do it, but there might be backwards compatibility concerns?

WebAuthnID() []byte

ID []byte `json:"id"`

UserHandle URLEncodedBase64 `json:"userHandle,omitempty"`

return nil, ErrBadRequest.WithDetails("Parse error for Assertion")

Add AttestationConveyancePreference enterprise support

Description

It would be nice if this library added support for enterprise attestation

Use Case

For enterprise deployments (where all hardware and configurations and centrally maintained) it would be nice to be able to request enterprise attestation.

Documentation

https://www.w3.org/TR/webauthn-2/#enum-attestation-convey

https://www.w3.org/TR/webauthn-2/#dom-attestationconveyancepreference-enterprise

https://groups.google.com/a/fidoalliance.org/g/fido-dev/c/TdCoQUsgFZU?pli=1

Project Takeover Tasks

Description

The following information is to be used for the purposes of keeping track of issues closed in the migration to this repository from the previous. I will be sifting through them as I get time and creating relevant issues as necessary and/or contact users who made PR's. Anyone is welcome to do the same or reply if they know the status of an issue (some were already fixed).

Use Case

Ensuring the future of the project migration is seamless.

Documentation

PR's

Issues

x509: unhandled critical extension error when using TPM attestation format

Version

0.11.0

Description

Thank you for developing this library

My Test Environment:
OS: Windows 11 Home
Version: 23H2
OS Build: 22631.3880
Browser: Brave (Brave 1.68.137 Chromium: 127.0.6533.100 (Official Build) (64 bit)

Actual library version I used is 0.11.1 but Version selection of issue template is not shown so selected 0.11.0.

I encountered below error when using this library at registration logic with Windows Hello.

Unable to validate attestation signature statement during attestation validation: invalid certificate chain from MDS: x509: unhandled critical extension

When I used version 0.10.2 of this library, this error never happened.
I tried using metadata.Provider feature so I guessed it is the reason.
I read some code and found the reason.

The error happens here.

if _, err = x5c.Verify(entry.MetadataStatement.Verifier()); err != nil {
return ErrInvalidAttestation.WithDetails(fmt.Sprintf("Unable to validate attestation signature statement during attestation validation: invalid certificate chain from MDS: %v", err))
}

and when I was debugging the code in x509.ParseCertificate in standard library, I reached here unhandled = true
https://github.com/golang/go/blob/72735094660a475a69050b7368c56b25346f5406/src/crypto/x509/parser.go#L691

out.DNSNames, out.EmailAddresses, out.IPAddresses, out.URIs, err = parseSANExtension(e.Value)
if err != nil {
    return err
}

if len(out.DNSNames) == 0 && len(out.EmailAddresses) == 0 && len(out.IPAddresses) == 0 && len(out.URIs) == 0 {
    // If we didn't parse anything then we do the critical check, below.
    unhandled = true
}

after that, reaches here
https://github.com/golang/go/blob/72735094660a475a69050b7368c56b25346f5406/src/crypto/x509/parser.go#L819-L821

if e.Critical && unhandled {
      out.UnhandledCriticalExtensions = append(out.UnhandledCriticalExtensions, e.Id)
}

and then in x5c.Verify finally reaches here
https://github.com/golang/go/blob/72735094660a475a69050b7368c56b25346f5406/src/crypto/x509/verify.go#L564-L566

if len(c.UnhandledCriticalExtensions) > 0 {
    return UnhandledCriticalExtension{}
}

parseSANExtension in standard library is here.
https://github.com/golang/go/blob/72735094660a475a69050b7368c56b25346f5406/src/crypto/x509/parser.go#L374-L417

func parseSANExtension(der cryptobyte.String) (dnsNames, emailAddresses []string, ipAddresses []net.IP, uris []*url.URL, err error) {
	err = forEachSAN(der, func(tag int, data []byte) error {
		switch tag {
		case nameTypeEmail:
			email := string(data)
			if err := isIA5String(email); err != nil {
				return errors.New("x509: SAN rfc822Name is malformed")
			}
			emailAddresses = append(emailAddresses, email)
		case nameTypeDNS:
			name := string(data)
			if err := isIA5String(name); err != nil {
				return errors.New("x509: SAN dNSName is malformed")
			}
			dnsNames = append(dnsNames, string(name))
		case nameTypeURI:
			uriStr := string(data)
			if err := isIA5String(uriStr); err != nil {
				return errors.New("x509: SAN uniformResourceIdentifier is malformed")
			}
			uri, err := url.Parse(uriStr)
			if err != nil {
				return fmt.Errorf("x509: cannot parse URI %q: %s", uriStr, err)
			}
			if len(uri.Host) > 0 {
				if _, ok := domainToReverseLabels(uri.Host); !ok {
					return fmt.Errorf("x509: cannot parse URI %q: invalid domain", uriStr)
				}
			}
			uris = append(uris, uri)
		case nameTypeIP:
			switch len(data) {
			case net.IPv4len, net.IPv6len:
				ipAddresses = append(ipAddresses, data)
			default:
				return errors.New("x509: cannot parse IP address of length " + strconv.Itoa(len(data)))
			}
		}

		return nil
	})

	return
}

By the way, at attestation_tpm.go, custom certificate extension validation logic exists like below.

for _, ext := range aikCert.Extensions {
if ext.Id.Equal([]int{2, 5, 29, 17}) {
manufacturer, model, version, err = parseSANExtension(ext.Value)
if err != nil {
return "", nil, err
}
}
}

and custom parseSANExtension which is not compatible with standard library
func parseSANExtension(value []byte) (manufacturer string, model string, version string, err error) {
err = forEachSAN(value, func(tag int, data []byte) error {
switch tag {
case nameTypeDN:
tpmDeviceAttributes := pkix.RDNSequence{}
_, err := asn1.Unmarshal(data, &tpmDeviceAttributes)
if err != nil {
return err
}
for _, rdn := range tpmDeviceAttributes {
if len(rdn) == 0 {
continue
}
for _, atv := range rdn {
value, ok := atv.Value.(string)
if !ok {
continue
}
if atv.Type.Equal(tcgAtTpmManufacturer) {
manufacturer = strings.TrimPrefix(value, "id:")
}
if atv.Type.Equal(tcgAtTpmModel) {
model = value
}
if atv.Type.Equal(tcgAtTpmVersion) {
version = strings.TrimPrefix(value, "id:")
}
}
}
}
return nil
})
return
}

that's why this error happens.

Reproduction

static/register.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Bug Test</title>
</head>
<body>
    <script type="module">
        
        const base64ToUint8Array = (base64) =>{
            const base64Characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
            //const base64URLCharacters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';

            let cleanedBase64 = String(base64).replace(/-/g, '+').replace(/_/g, '/');
            const padding = (4 - (cleanedBase64.length % 4)) % 4;
            cleanedBase64 += '='.repeat(padding);

            const rawLength = cleanedBase64.length;
            const decodedLength = (rawLength * 3) / 4 - padding;

            const uint8Array = new Uint8Array(decodedLength);

            let byteIndex = 0;
            for (let i = 0; i < rawLength; i += 4) {
                const encoded1 = base64Characters.indexOf(cleanedBase64[i]);
                const encoded2 = base64Characters.indexOf(cleanedBase64[i + 1]);
                const encoded3 = base64Characters.indexOf(cleanedBase64[i + 2]);
                const encoded4 = base64Characters.indexOf(cleanedBase64[i + 3]);

                const decoded1 = (encoded1 << 2) | (encoded2 >> 4);
                const decoded2 = ((encoded2 & 15) << 4) | (encoded3 >> 2);
                const decoded3 = ((encoded3 & 3) << 6) | encoded4;

                uint8Array[byteIndex++] = decoded1;
                if (encoded3 !== 64) uint8Array[byteIndex++] = decoded2;
                if (encoded4 !== 64) uint8Array[byteIndex++] = decoded3;
            }

            return uint8Array;
        }

        const Uint8ArrayToBase64 = (uint8Array) =>{
            //const base64Characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
            const base64URLCharacters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
            const base64Characters = base64URLCharacters;

            let base64 = '';
            const { length } = uint8Array;

            for (let i = 0; i < length; i += 3) {
                const byte1 = uint8Array[i];
                const byte2 = uint8Array[i + 1];
                const byte3 = uint8Array[i + 2];

                const encoded1 = byte1 >> 2;
                const encoded2 = ((byte1 & 3) << 4) | (byte2 >> 4);
                const encoded3 = ((byte2 & 15) << 2) | (byte3 >> 6);
                const encoded4 = byte3 & 63;

                base64 += base64Characters[encoded1] + base64Characters[encoded2];
                base64 += byte2 !== undefined ? base64Characters[encoded3] : '=';
                base64 += byte3 !== undefined ? base64Characters[encoded4] : '=';
            }

            return base64;
        }

        const j = await fetch("/register").then(r => r.json());
        j.publicKey.challenge = base64ToUint8Array(j.publicKey.challenge).buffer;
        j.publicKey.user.id = base64ToUint8Array(j.publicKey.user.id).buffer;
        console.log(j);
        const r = await navigator.credentials.create({
            publicKey: {
                ...j.publicKey,
               attestation: "direct" 
            }
        });
        console.log(r);
        //r.rawId = Uint8ArrayToBase64(new Uint8Array(r.rawId))
        const js =JSON.stringify({
            id: r.id,
            rawId: Uint8ArrayToBase64(new Uint8Array(r.rawId)),
            response: {
                clientDataJSON: Uint8ArrayToBase64(new Uint8Array(r.response.clientDataJSON)),
                attestationObject: Uint8ArrayToBase64(new Uint8Array(r.response.attestationObject))
            },
            type: r.type
        })
        console.log(js);
        await fetch("/verify", {
            method: "POST",
            headers: {
                "Content-Type": "application/json"
            },
            body: js
        });


    </script>
</body>
</html>

main.go

package main

import (
	_ "embed"
	"encoding/base64"
	"encoding/json"
	"log"
	"net/http"

	"github.com/go-webauthn/webauthn/metadata/providers/cached"
	"github.com/go-webauthn/webauthn/protocol"
	"github.com/go-webauthn/webauthn/webauthn"
)

type user struct{}

func (u *user) WebAuthnID() []byte {
	return []byte("test")
}

func (u *user) WebAuthnName() string {
	return "test"
}

func (u *user) WebAuthnDisplayName() string {
	return "test"
}

func (u *user) WebAuthnIcon() string {
	return ""
}

func (u *user) WebAuthnCredentials() []webauthn.Credential {
	return nil
}

var _ webauthn.User = &user{}

func marshal(v interface{}) string {
	b, err := json.Marshal(v)
	if err != nil {
		panic(err)
	}
	return string(b)
}

func marshalAndBase64(v interface{}) string {
	return base64.StdEncoding.EncodeToString([]byte(marshal(v)))
}

func unmarshal(s string, v interface{}) error {
	return json.Unmarshal([]byte(s), v)
}

func unmarshalFromBase64(s string, v interface{}) error {
	b, err := base64.StdEncoding.DecodeString(s)
	if err != nil {
		return err
	}
	return unmarshal(string(b), v)
}

//go:embed static/register.html
var registerHTML string

func main() {
	v, err := cached.New(cached.WithPath("metadata.json"))
	if err != nil {
		log.Fatal("metadata.json error:", err)
	}
	a, err := webauthn.New(&webauthn.Config{
		RPID: "localhost",
		RPOrigins: []string{
			"http://localhost:8081",
		},
		RPDisplayName: "localhost",
		MDS:           v,
	})
	if err != nil {
		log.Fatal("webauthn error:", err)
	}
	http.HandleFunc("/register", func(w http.ResponseWriter, r *http.Request) {
		creation, session, err := a.BeginRegistration(&user{}, webauthn.WithAttestationFormats([]protocol.AttestationFormat{
			protocol.AttestationFormatTPM,
		}))
		if err != nil {
			log.Println(err)
			return
		}
		http.SetCookie(w, &http.Cookie{
			Name:     "session",
			Value:    marshalAndBase64(session),
			SameSite: http.SameSiteStrictMode,
		})
		w.Header().Set("Content-Type", "application/json")
		w.Write([]byte(marshal(creation)))
	})

	http.HandleFunc("/verify", func(w http.ResponseWriter, r *http.Request) {
		session := &webauthn.SessionData{}
		cookie, err := r.Cookie("session")
		if err != nil {
			log.Println(err)
			return
		}
		err = unmarshalFromBase64(cookie.Value, session)
		if err != nil {
			log.Println(err)
			return
		}
		attestation, err := a.FinishRegistration(&user{}, *session, r)
		if err != nil {
			log.Println(err) // error will be printed from here
			return
		}
		w.Header().Set("Content-Type", "application/json")
		w.Write([]byte(marshal(attestation)))
	})

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "text/html")
		w.Write([]byte(registerHTML))
	})
        
        log.Println("listening on localhost:8081")
	log.Fatal(http.ListenAndServe("localhost:8081", nil))
}

(sorry, the code may be bit dirty)

  1. paste above code into main.go and static/register.html
  2. go mod init if at the first time
  3. go run .
  4. then access to localhost:8081 and do WebAuthn registration
  5. the error will be printed on the terminal

Expectations

Currently, I would skip this check by using metadata/providers/memory.WithValidateTrustAnchor(false) option.
But I think this additional validation feature is maybe unnecessary because custom trust anchor verification logics looks already implemented for each attestation type. Otherwise I think verification features should be merged in different way.
(note that this is only my opinion from my narrow (not understanding all of features of this library and not perfectly understanding certificate verification) perspective)

Documentation

No response

Verify topOrigin

Description

The latest draft discusses verifying topOrigin along with origin. Right now this library only validates origin. Maybe an RPTopOrigins field could be added to Config, though this means the default would be to not allow any topOrigin value which might not be backwards-compatible. It also wouldn't be easy for an RP to allow any topOrigin value. Maybe instead it could be some sort of enum like AllowAll, AllowRPOrigins, AllowNone, but then you couldn't specify third-party ones.

Use Case

Our particular use-case is that we don't allow any topOrigin value, but longer-term we might want to allow our top-level domain as a valid topOrigin.

Documentation

See w3c/webauthn#1891 and https://w3c.github.io/webauthn/#sctn-validating-origin.

Methods should be designed to be flexible and not dictate or constrain the project’s API /Protocol definitions

Description

The problem is if you wan't to use another protocol to communicate with the server, method that require http.Request such as FinishRegistration makes it tedious to use the library.

The issue is that if you include additional data in the same request (which is my current approach), the library cannot be used according to the documentation.

Use Case

  • Sending custom data with the finish registration
  • Using other protocol than http

Documentation

credential, err := webAuthn.FinishRegistration(user, session, r)
credential, err := webAuthn.FinishLogin(user, session, r)

RP ID should be optional

Version

0.8.4

Description

Hi,

It seems like not passing RP ID is invalid, but it should be valid. In case we don't set the RP ID in the options, it will use the current domain. This will also make it usable in localhost, for example.

By default, the RP ID for a WebAuthn operation is set to the caller’s origin's effective domain.

Reproduction

Try to not pass RP ID

Expectations

No response

Documentation

No response

Functional option WithValidateAttestationTypes of in-memory provider sets wrong flag

Version

0.11.0

Description

func WithValidateAttestationTypes(validate bool) Option {
return func(provider *Provider) (err error) {
provider.status = validate
return nil
}

This functional option of the in-memory provider implementation should set the provider.attestation flag instead of the status flag. The status flag can be set via WithValidateStatus.

Reproduction

  1. Create an in-memory provider with the memory.WithValidateAttestationTypes(false) option.
  2. Verify an attestation.
  3. Observe, that the attestation types of the attestation are still verified against the entry of the authenticator of the attestation.

Expectations

I expect the attestation types not to be verified if I set the option WithValidateAttestationTypes to false.

Documentation

No response

Library uses json.NewDecoder(body).Decode which might not error on trailing extra data

Version

0.9.2

Description

For example, in ParseCredentialCreationResponseBody you can see:

	var ccr CredentialCreationResponse

	if err = json.NewDecoder(body).Decode(&ccr); err != nil {
		return nil, ErrBadRequest.WithDetails("Parse error for Registration").WithInfo(err.Error())
	}

json.NewDecoder(r).Decode by itself is meant as a streaming parser and it does not fail on its own if there is trailing data after payload. I use such check in my own code to mitigate that.

Reproduction

N/A

Expectations

No response

Documentation

See: golang/go#36225

Handle is nil when allowCredentials is non-empty during discoverable login

Version

0.8.6

Description

Using phone as a passkey / credential seems to give a response without a handle duing a discoverable login with allowedCredentials set.

Currently there are checks that invalidates the response if the handle is empty.

Reading about user handles in w3c github;

Discoverable credentials store this identifier and MUST return it as response.userHandle in authentication ceremonies started with an empty allowCredentials argument.

This doesn't specifically say anything about what must and musnt be provided when starting authentication ceremonies with povided allowedCredentials, however, I take it that responses with empty handle are valid if the ceremony was started with given allowedCredentials?

Reproduction

Register a phone as a discoverable credential, then try login with the credential id listed in allowedCredentials.

Expectations

No response

Documentation

w3c github about user handles

Response timeout - unclear code

Version

0.9.2

Description

I am reading code in BeginRegistration and I wonder why such code:

	if creation.Response.Timeout == 0 {
		switch {
		case creation.Response.AuthenticatorSelection.UserVerification == protocol.VerificationDiscouraged:
			creation.Response.Timeout = int(webauthn.Config.Timeouts.Registration.Timeout.Milliseconds())
		default:
			creation.Response.Timeout = int(webauthn.Config.Timeouts.Registration.Timeout.Milliseconds())
		}
	}

Shouldn't the first case be creation.Response.Timeout = int(webauthn.Config.Timeouts.Registration.TimeoutUVD.Milliseconds())?

Documentation

It says like that in documentation fro TimeoutConfig:

// TimeoutConfig represents the WebAuthn timeouts configuration for either registration or login..
type TimeoutConfig struct {
	// Enforce the timeouts at the Relying Party / Server. This means if enabled and the user takes too long that even
	// if the browser does not enforce the timeout the Relying Party / Server will.
	Enforce bool

	// Timeout is the timeout for logins/registrations when the UserVerificationRequirement is set to anything other
	// than discouraged.
	Timeout time.Duration

	// TimeoutUVD is the timeout for logins/registrations when the UserVerificationRequirement is set to discouraged.
	TimeoutUVD time.Duration
}

Implement live comprehensive example

Description

It would be good as some users have mentioned to produce a live example that's published showcasing not only what this library is capable of but also what webauthn is capable of. This would help users implement the library, and show what options are available.

The following things can be showcased:

  • Simple email/username and password registration/login for demonstration purposes
  • Webauthn Registration
    • Standard Registration (non-discoverable)
    • Discoverable Registration
    • Identityless/Userless Registration i.e. via a discoverable credential which links to an account UUID/GUID
    • Support for all of the standard registration options:
      • Attachment
      • Discoverable / Resident Key / Require Resident Key
      • User Verification
      • Conveyancing Preference
  • Webauthn Authentication
    • "True" passwordless logins (no username required) which also supports usernameless login
    • 2FA demonstration

Implementation Specifics (all ideas at this point):

  • React (via go embed)
  • Database
    • Postgres?
  • Hosting:
    • I can probably self host it in the cloud but it'd be good to find some form of sponsor for this which allows a distributed model maybe

Use Case

No response

Documentation

#47

Add additional fields to AuthenticatorAttestationResponse

Description

So AuthenticatorAttestationResponse struct contains only minimal fields which might be sent in JSON, but it looks the spec allows additional fields (which are just extracted from attestationOjbect):

To remove the need to parse CBOR at all in many cases, getAuthenticatorData() returns the authenticator data from attestationObject. The authenticator data contains other fields that are encoded in a binary format. However, helper functions are not provided to access them because Relying Parties already need to extract those fields when getting an assertion.

The issue I have is that I send AuthenticatorAttestationResponse over the wire in JSON from the client. And client adds those fields. But on the server I want to use JSON unmarshal with DisallowUnknownFields set (primarily to detect changes in API I should check and possibly adapt to). And this is then currently not possible.

So I wonder if struct should define those additional optional fields, but not use them/ignore them?

Use Case

No response

Documentation

No response

Attestation with full attestation from authenticator that does not support full attestation

Version

0.8.2

Description

Doing an enrollment with a Windows Hello Authenticator device with "direct" attestation throws this error that happens at attestation.go:187.

This happens because the MetatadataStatement for Windows Hello Authenticator (aaguid 08987058-cadc-4b81-b6e1-30de50dcbe96) doesn't have "basic_full" in the attestationTypes array.

"attestationTypes": [
"attca"
]

Is this a correct behavior?

Reproduction

Enroll a device with:
AttestationType "direct"
AuthenticatorType "platform"
Authenticator: "Windows Hello Authenticator"

Expectations

The device should be enrolled succesfully.

Documentation

No response

Direct Extensions Support

Description

Implement all of the extensions directly in the library including all validations. It would be nice to allow backwards compat and a low level implementation as this is an evolving area, however I don't believe that either of these ideas should be a critical requirement.

Use Case

No response

Documentation

package protocol

// AppIDExtensionsClientInputs is the input parameters for the appid extension.
//
// This extension allows WebAuthn Relying Parties that have previously registered a credential using the legacy FIDO U2F
// JavaScript API FIDOU2FJavaScriptAPI to request an assertion. The FIDO APIs use an alternative identifier for Relying
// Parties called an AppID FIDO-APPID, and any credentials created using those APIs will be scoped to that identifier.
// Without this extension, they would need to be re-registered in order to be scoped to an RP ID.
//
// Stages: Authentication
//
// Specification: Β§10.2. FIDO AppID Extension (https://www.w3.org/TR/webauthn/#sctn-appid-extension)
type AppIDExtensionsClientInputs struct {
	AppID string `json:"appid,omitempty"`
}

// AppIDExtensionsClientOutputs is the output parameters for the appid extension.
//
// This extension allows WebAuthn Relying Parties that have previously registered a credential using the legacy FIDO U2F
// JavaScript API FIDOU2FJavaScriptAPI to request an assertion. The FIDO APIs use an alternative identifier for Relying
// Parties called an AppID FIDO-APPID, and any credentials created using those APIs will be scoped to that identifier.
// Without this extension, they would need to be re-registered in order to be scoped to an RP ID.
//
// Stages: Authentication
//
// Specification: Β§10.2. FIDO AppID Extension (https://www.w3.org/TR/webauthn/#sctn-appid-extension)
type AppIDExtensionsClientOutputs struct {
	AppID bool `json:"appid"`
}

// AppIDExcludeExtensionsClientInputs is the input parameters for the appidExclude extension.
//
// This registration extension allows WebAuthn Relying Parties to exclude authenticators that contain specified
// credentials that were created with the legacy FIDO U2F JavaScript API FIDOU2FJavaScriptAPI.
//
// Stages: Registration
//
// Specification: Β§10.2. FIDO AppID Exclusion Extension (https://www.w3.org/TR/webauthn/#sctn-appid-exclude-extension)
type AppIDExcludeExtensionsClientInputs struct {
	AppID string `json:"appidExclude,omitempty"`
}

// AppIDExcludeExtensionsClientOutputs is the output parameters for the appidExclude extension.
//
// This registration extension allows WebAuthn Relying Parties to exclude authenticators that contain specified
// credentials that were created with the legacy FIDO U2F JavaScript API FIDOU2FJavaScriptAPI.
//
// Stages: Registration
//
// Specification: Β§10.2. FIDO AppID Exclusion Extension (https://www.w3.org/TR/webauthn/#sctn-appid-exclude-extension)
type AppIDExcludeExtensionsClientOutputs struct {
	AppID bool `json:"appidExclude"`
}

// UVMClientInputs is the input parameters for the uvm extension.
//
// This extension enables use of a user verification method.
//
// Stages: Registration, Authentication
//
// Specification: Β§10.3. User Verification Method Extension (https://www.w3.org/TR/webauthn/#sctn-uvm-extension)
type UVMClientInputs struct {
	UVM bool `json:"uvm"`
}

// UVMClientOutputs is the input parameters for the uvm extension.
//
// This extension enables use of a user verification method.
//
// TODO: Investigation of the CBOR structure.
//
// Stages: Registration, Authentication
//
// Specification: Β§10.3. User Verification Method Extension (https://www.w3.org/TR/webauthn/#sctn-uvm-extension)
type UVMClientOutputs struct {
	UVM [][]uint `json:"uvm"`
}

// CredentialPropertiesClientInputs is the input parameters for the credProps extension.
//
// This client registration extension facilitates reporting certain credential properties known by the client to the
// requesting WebAuthn Relying Party upon creation of a public key credential source as a result of a registration
// ceremony.
//
// Stages: Registration
//
// Specification: Β§10.4. Credential Properties Extension (https://www.w3.org/TR/webauthn/#sctn-authenticator-credential-properties-extension)
type CredentialPropertiesClientInputs struct {
	CredentialProperties bool `json:"credProps"`
}

// CredentialPropertiesClientOutputs is the output parameters for the credProps extension.
//
// This client registration extension facilitates reporting certain credential properties known by the client to the
// requesting WebAuthn Relying Party upon creation of a public key credential source as a result of a registration
// ceremony.
//
// Stages: Registration
//
// Specification: Β§10.4. Credential Properties Extension (https://www.w3.org/TR/webauthn/#sctn-authenticator-credential-properties-extension)
type CredentialPropertiesClientOutputs struct {
	ClientSideDiscoverableCredential bool `json:"rk"`
}

// LargeBlobSupport represents the IDL of the same name.
//
// Specification: Β§10.5. Large blob storage extension (https://www.w3.org/TR/webauthn/#enumdef-largeblobsupport)
type LargeBlobSupport string

const (
	LargeBlobSupportRequired  LargeBlobSupport = "required"
	LargeBlobSupportPreferred LargeBlobSupport = "preferred"
)

// LargeBlobStorageClientRegistrationInputs is the input parameters for the largeBlob extension.
//
// This client registration extension and authentication extension allows a Relying Party to store opaque data
// associated with a credential. Since authenticators can only store small amounts of data, and most Relying Parties are
// online services that can store arbitrary amounts of state for a user, this is only useful in specific cases. For
// example, the Relying Party might wish to issue certificates rather than run a centralised authentication service.
//
// Stages: Registration, Authentication
//
// Specification: Β§10.5. Large blob storage extension (https://www.w3.org/TR/webauthn/#sctn-authenticator-credential-properties-extension)
type LargeBlobStorageClientRegistrationInputs struct {
	LargeBlob RegistrationExtensionsLargeBlobInputs `json:"largeBlob"`
}

// LargeBlobStorageClientAuthenticationInputs is the input parameters for the largeBlob extension.
//
// This client registration extension and authentication extension allows a Relying Party to store opaque data
// associated with a credential. Since authenticators can only store small amounts of data, and most Relying Parties are
// online services that can store arbitrary amounts of state for a user, this is only useful in specific cases. For
// example, the Relying Party might wish to issue certificates rather than run a centralised authentication service.
//
// Stages: Authentication
//
// Specification: Β§10.5. Large blob storage extension (https://www.w3.org/TR/webauthn/#sctn-authenticator-credential-properties-extension)
type LargeBlobStorageClientAuthenticationInputs struct {
	LargeBlob AuthenticationExtensionsLargeBlobInputs `json:"largeBlob"`
}

type RegistrationExtensionsLargeBlobInputs struct {
	Support LargeBlobSupport `json:"support"`
}

type AuthenticationExtensionsLargeBlobInputs struct {
	Read bool             `json:"read"`
	Data URLEncodedBase64 `json:"write,omitempty"`
}

// LargeBlobStorageClientRegistrationOutputs is the output parameters for the largeBlob extension.
//
// This client registration extension and authentication extension allows a Relying Party to store opaque data
// associated with a credential. Since authenticators can only store small amounts of data, and most Relying Parties are
// online services that can store arbitrary amounts of state for a user, this is only useful in specific cases. For
// example, the Relying Party might wish to issue certificates rather than run a centralised authentication service.
//
// Stages: Registration
//
// Specification: Β§10.5. Large blob storage extension (https://www.w3.org/TR/webauthn/#sctn-authenticator-credential-properties-extension)
type LargeBlobStorageClientRegistrationOutputs struct {
	LargeBlob RegistrationExtensionsLargeBlobOutputs `json:"largeBlob"`
}

// LargeBlobStorageClientAuthenticationOutputs is the output parameters for the largeBlob extension.
//
// This client registration extension and authentication extension allows a Relying Party to store opaque data
// associated with a credential. Since authenticators can only store small amounts of data, and most Relying Parties are
// online services that can store arbitrary amounts of state for a user, this is only useful in specific cases. For
// example, the Relying Party might wish to issue certificates rather than run a centralised authentication service.
//
// Stages: Registration, Authentication
//
// Specification: Β§10.5. Large blob storage extension (https://www.w3.org/TR/webauthn/#sctn-authenticator-credential-properties-extension)
type LargeBlobStorageClientAuthenticationOutputs struct {
	LargeBlob AuthenticationExtensionsLargeBlobOutputs `json:"largeBlob"`
}

type RegistrationExtensionsLargeBlobOutputs struct {
	Support bool `json:"supported"`
}

type AuthenticationExtensionsLargeBlobOutputs struct {
	Written bool             `json:"written"`
	Data    URLEncodedBase64 `json:"blob,omitempty"`
}

[API Consistency] There is no FinishDiscoverableLogin in Webauthn

Version

0.8.6

Description

While using the code, I wanted to use the discoverable credentials feature. There is a BeginDiscoverableLogin method, a ValidateDiscoverableLogin method but no FinishDiscoverableLogin method.

I was wondering if there is a reason for this. I imagine that the code would be :

// FinishDiscoverableLogin takes the response from the client and validate it against the user handler and stored session data.
func (webauthn *WebAuthn) FinishDiscoverableLogin(handler DiscoverableUserHandler, session SessionData, response *http.Request) (*Credential, error) {
	parsedResponse, err := protocol.ParseCredentialRequestResponse(response)
	if err != nil {
		return nil, err
	}

	return webauthn.ValidateDiscoverableLogin(handler, session, parsedResponse)
}

but I may miss something.

Reproduction

This is related to the API and the available methods.

Expectations

I would expect that a method FinishDiscoverableLogin would be available.

Documentation

No response

Attestation verification through trust anchor

Description

Hello all.

After studying a bit the library and stumbling in this piece of comment, I would like to open a discussion on how could be an interesting way of supporting a trust verification using the library.
My understanding is that up to this point, the library does not export any facility for doing trust assessments on the embedded attestation certificates. I couldn't also find a convenient way through a public method to extract the embedded certificates without having to copy the whole procedure of attestation-object protocol decoding.
I'm opening this feature-request to trigger a discussion around how that could be accomplished. Here are some options that come to my mind, any others would be welcomed:

  1. Implement in the library the possibility to provide a list of trusted root certificates against which the attestation certificate could be verified.
  2. Implement in the library something like ExportAttestationCertificates method that would delegate to the client the responsibility to implement the chain-of-trust verification.
  3. Document if there is an existing mechanism to achieve that.

What are your thoughts on that? I would be happy to try to help wherever possible with some code-contributions. πŸ˜ƒ

Thank you in advance for your work and support,
Rodrigo

Use Case

Some types of webauthn attestation verification would require verification against an RP policy. This verification is done by verifying the embedded attestation certificate against an RP-trusted set of root certificates (otherwise called a trust store).
Use cases:

  • Allow RP to filter which kinds/types of authenticators to allow. Some RPs might only want to support apple devices. Others only Yubico, and so on and so forth.
  • Check the validity and trustworthiness of the provided attestation-certificate

Documentation

No response

UserDisplayName of SessionData is unused

Version

0.8.2

Description

The UserDisplayName field in the SessionData struct is never referenced in the package.
I suppose it is designed to store the user information in the methods like BeginRegistration()?
Maybe consider copying from User.WebAuthnDisplayName() or remove this field to avoid confusion.

Reproduction

Run the following code

code
package main

import (
	"log"

	"github.com/go-webauthn/webauthn/webauthn"
)

func main() {
	wconfig := &webauthn.Config{
		RPDisplayName: "Go Webauthn",                               // Display Name for your site
		RPID:          "go-webauthn.local",                         // Generally the FQDN for your site
		RPOrigins:     []string{"https://login.go-webauthn.local"}, // The origin URLs allowed for WebAuthn requests
	}

	w, err := webauthn.New(wconfig)
	if err != nil {
		log.Fatal(err)
	}

	u := user{id: []byte{1, 2, 3}, displayName: "Go Webauthn", name: "webauthn"}
	_, session, err := w.BeginRegistration(u)
	if err != nil {
		log.Fatal(err)
	}

	log.Println("display name: ", session.UserDisplayName)
}

type user struct {
	id                []byte
	displayName, name string
	credentials       []webauthn.Credential
}

func (u user) WebAuthnID() []byte {
	return u.id
}

func (u user) WebAuthnName() string {
	return u.name
}

func (u user) WebAuthnDisplayName() string {
	return u.displayName
}

func (u user) WebAuthnCredentials() []webauthn.Credential {
	return u.credentials
}

func (u user) WebAuthnIcon() string {
	return ""
}

The terminal prints

2023/05/29 11:48:07 display name:  

Expectations

I would expect

2023/05/29 11:48:07 display name:  Go Webauthn

Documentation

No response

Api design issue

Version

0.8.2

Description

Hi,

I don't understand why the user is required to BeginLogin and BeginRegistration to me in that situation, we don't have enough elements to know who the user is

Reproduction

Look at BeginLogin and BeginRegistration functions

Expectations

I would expect to be able to use BeginLogin and BeginRegistration without having to provide a user as what matters in that case is the challenge and RPID/RPOrigins for the credendial manager to be able to create the correct key.

Maybe it makes sense for registration as the user can provide personal information like email at this point of time. But for login, it forces the user to enter his email while he could only press a button and being logged in right away

Documentation

No response

Allow multiple RPOrigins

Description

Allow multiple RPOrigins as suggested in duo-labs/webauthn#143.

Currently only one RPOrigin can be defined, which is sufficient for most use cases. But if you have a webpage and a mobile app (Android or/and iOS) and they should share the (passkeys) credentials you can't do this right now.

Use Case

Share (passkeys) credentials between a mobile app and a webpage.

Documentation

No response

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

This repository currently has no open or pending branches.

Detected dependencies

gomod
go.mod
  • go 1.23
  • go 1.23.0
  • github.com/fxamacker/cbor/v2 v2.7.0
  • github.com/go-webauthn/x v0.1.14
  • github.com/golang-jwt/jwt/v5 v5.2.1
  • github.com/google/go-tpm v0.9.1
  • github.com/google/uuid v1.6.0
  • github.com/mitchellh/mapstructure v1.5.0
  • github.com/stretchr/testify v1.9.0
  • golang.org/x/crypto v0.26.0

  • Check this box to trigger a request for Renovate to run again on this repository

Credential struct should have json struct tags

Description

It seems one has to persist credentials. One easy way to do so would be to store them as JSON. But corresponding JSON is ugly because Credential does not have json struct tags.

Use Case

No response

Documentation

No response

Trailing extraneous bytes passed to `cbor.Unmarshal()`, also it may contain authenticator extension data

Version

0.8.6

Description

This issue has a public test from August 31, 2023 at gravitational/teleport#31322. I maintain fxamacker/cbor, my apologies for not catching some aspects of this sooner. πŸ™

ParseCredentialCreationResponseBody() is called with data that causes code in webauthncbor.go to eventually pass 91 bytes to cbor.Unmarshal().

Those 91 bytes represent a CBOR Sequence (RFC 8742) instead of CBOR data item (RFC 8949).

cbor.Unmarshal() is for parsing a CBOR data item rather than CBOR Sequence (concatenation of CBOR data items).

In old versions of cbor library, the extra trailing bytes are ignored by cbor.Unmarshal() instead of returning error.

For some uses cases, ignoring trailing bytes is undesirable and can create risks. In this test case, the extraneous 14 bytes is a CBOR data item that appears to represent authenticator extension data {"credProtect": 2}.

Upgrading to fxamacker/cbor v2.5.0 makes it easier to detect and handle extraneous data:

  • cbor.Unmarshal() detects trailing bytes and returns ExtraneousDataError.
  • cbor.UnmarshalFirst() function was added to explicitly handle extraneous bytes without ExtraneousDataError.
  • cbor.DiagnoseFirst() function was added to return human readable text (Diagnostic Notation) for logging/debugging.
// UnmarshalFirst decodes first CBOR data item and returns remaining bytes.
rest, err = cbor.UnmarshalFirst(b, &v)   // decode []byte b to v

If you choose to upgrade to v2.5.0, please see ⭐ Notable Changes to Review Before Upgrading in the release notes.

My apologies again for not catching some of this sooner. Please let me know if I can help make the upgrade go smoothly.

Reproduction

See gravitational/teleport#31322 for test and reproducer.

TestIssue31187_errorParsingAttestationResponse

// TestIssue31187_errorParsingAttestationResponse reproduces the root cause of
// https://github.com/gravitational/teleport/issues/31187 by attempting to parse
// a current CCR created using a Chrome/Yubikey pair.
//
// The test exposes a poor interaction between go-webauthn/webauthn v0.8.6 and
// fxamacker/cbor/v2 v2.5.0.
func TestIssue31187_errorParsingAttestationResponse(t *testing.T) {
	// Captured from an actual Yubikey 5Ci registration request.
	const body = `{"id":"ibfM_71b4q2_xWPZDyvhZmJ_KU8f-mOCCLXHp-fTVoHZpDelym5lvBJDPr1EtD_l","type":"public-key","rawId":"ibfM_71b4q2_xWPZDyvhZmJ_KU8f-mOCCLXHp-fTVoHZpDelym5lvBJDPr1EtD_l","response":{"clientDataJSON":"eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoidEdiVFhEbzBGMXRNUVlmamRSLWNETlV1TUNvVURTX0w0OElSWmY4MUVuWSIsIm9yaWdpbiI6Imh0dHBzOi8vemFycXVvbi5kZXY6MzA4MCIsImNyb3NzT3JpZ2luIjpmYWxzZSwib3RoZXJfa2V5c19jYW5fYmVfYWRkZWRfaGVyZSI6ImRvIG5vdCBjb21wYXJlIGNsaWVudERhdGFKU09OIGFnYWluc3QgYSB0ZW1wbGF0ZS4gU2VlIGh0dHBzOi8vZ29vLmdsL3lhYlBleCJ9","attestationObject":"o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVjCnNjmsqMh0nu-_tuMkxVZkZShAhdoz0tK9evxg8ys9CLFAAAAAQAAAAAAAAAAAAAAAAAAAAAAMIm3zP-9W-Ktv8Vj2Q8r4WZifylPH_pjggi1x6fn01aB2aQ3pcpuZbwSQz69RLQ_5aUBAgMmIAEhWCCJt8z_vVvirb_FY9kPpoIwbfhER3VHTmOV0Y6xs7uHySJYIMFARJxlUoR4DbDzlKYnfJKitWgR3GHK9_Lz211z-128oWtjcmVkUHJvdGVjdAI"}}`

	_, err := protocol.ParseCredentialCreationResponseBody(strings.NewReader(body))
	require.NoError(t, err, "ParseCredentialCreationResponseBody failed")
}

Reproducer calls ParseCredentialCreationResponseBody with data that causes code in webauthncbor.go to eventually pass 91 bytes to cbor.Unmarshal().

Those 91 bytes represent a CBOR Sequence (RFC 8742) instead of CBOR data item (RFC 8949).

In Diagnostic Notation, first CBOR data item represents:

{1: 2, 3: -7, -1: 1, 
-2: h'89b7ccffbd5be2adbfc563d90fa682306df8444775474e6395d18eb1b3bb87c9',
-3: h'c140449c655284780db0f394a6277c92a2b56811dc61caf7f2f3db5d73fb5dbc'}

The 2nd CBOR data item (aka extraneous data in RFC 8949) represents:

{"credProtect": 2}

Expectations

Detect or prevent extraneous bytes or CBOR Sequence being passed to cbor.Unmarshal().

If needed, handle authenticator extension data currently contained in the trailing bytes.

Documentation

No response

Strip base64 padding in protocol.URLEncodedBase64

Description

(version: github.com/go-webauthn/webauthn v0.5.0)

It seems the type protocol.URLEncodedBase64 is, correctly, decoding base64 without padding. (ref) however, a frontend tool I'm using to implement webauthn seems to be adding padding to the publicKey.response.userHandle.

Now I can strip this out myself, but would it be incorrect for this library to conveniently strip padding automatically given that it's not meant to be there anyway?

Use Case

No response

Documentation

No response

ValidateDiscoverableLogin hides handler error

Version

0.8.2

Description

The ValidateDiscoverableLogin method masks the underlying error message returned by DiscoverableUserHandler, which makes troubleshooting harder. Please consider methods like wrapping the error to allow the source problem to be exposed.

Reproduction

Run the following

code
package main

import (
	"fmt"
	"log"

	"github.com/go-webauthn/webauthn/protocol"
	"github.com/go-webauthn/webauthn/webauthn"
)

func main() {
	wconfig := &webauthn.Config{
		RPDisplayName: "Go Webauthn",                               // Display Name for your site
		RPID:          "go-webauthn.local",                         // Generally the FQDN for your site
		RPOrigins:     []string{"https://login.go-webauthn.local"}, // The origin URLs allowed for WebAuthn requests
	}

	w, err := webauthn.New(wconfig)
	if err != nil {
		log.Fatal(err)
	}

	handler := func(rawID, userHandle []byte) (webauthn.User, error) {
		return nil, fmt.Errorf("Oops, bad things happened")
	}

	_, err = w.ValidateDiscoverableLogin(
		handler,
		webauthn.SessionData{},
		&protocol.ParsedCredentialAssertionData{Response: protocol.ParsedAssertionResponse{UserHandle: []byte{1, 2, 3}}},
	)
	log.Println(err)
}

type user struct {
	id                []byte
	displayName, name string
	credentials       []webauthn.Credential
}

func (u user) WebAuthnID() []byte {
	return u.id
}

func (u user) WebAuthnName() string {
	return u.name
}

func (u user) WebAuthnDisplayName() string {
	return u.displayName
}

func (u user) WebAuthnCredentials() []webauthn.Credential {
	return u.credentials
}

func (u user) WebAuthnIcon() string {
	return ""
}

The terminal will print something like

2023/05/29 12:48:41 Failed to lookup Client-side Discoverable Credential

which does not reveal the cause of the error.

Expectations

Maybe wrapping the error like

Failed to lookup Client-side Discoverable Credential: Oops, bad things happened

Documentation

No response

Windows Hello - Mismatch between ECCParameters in pubArea and credentialPublicKey

Version

0.9.4

Description

Attestation bug with Direct & TPM on Windows Hello (Windows 11 22H2).
Searching found it was reported in another library too:
MasterKale/SimpleWebAuthn#238

Reproduction

Trying to enroll Windows 11 22H2 Hello authenticator with
Attestation=Direct
AuthenticatorType=Platform(TPM)

It ends in "Mismatch between ECCParameters in pubArea and credentialPublicKey" error but it should work.

Expectations

Should enroll the device successfully.

Documentation

No response

PatternAccuracyDescriptor.MinComplexity needs to be uint64 not uint32

Version

0.10.1

Description

When parsing the metadata, PatternAccuracyDescriptor.MinComplexity is too small; needs to be uint64 not uint32.

Reproduction

Parsing the latest version of the metadata blob (no=80):

json: cannot unmarshal number 34359738368 into Go struct field PatternAccuracyDescriptor.metadataStatement.userVerificationDetails.paDesc.minComplexity of type uint32

some pseudo code:

import (
	webauthnMetadata "github.com/go-webauthn/webauthn/metadata"
)

...
func() whatever(j *jwt) {

	raw := j.Payload.RawVal("entries")
	var entries []webauthnMetadata.MetadataBLOBPayloadEntry
	if err := json.Unmarshal(raw, &entries); err != nil {
		return err
	}
}

Expectations

modify to uint64

MinComplexity uint32 `json:"minComplexity"`

Documentation

No response

metadata: Make unmarshalMDSBLOB public/exported

Description

Hello!

I was looking at adding bits of the metadata package to my service for showing names & descriptions of registered authenticators (via aaguid). As far as I know the correct way for doing this is with the FIDO MDS service which is usable via the metadata package with the ProductionMDSURL const and the PopulateMetadata function.

However the options to the latter are few, it uses a hardcoded http.Client followed by a io.Readall (which is generally something I'd recommend against).

It looks like there is a private function that could be exposed to allow more uses cases, that function being unmarshalMDSBLOB for use cases I've listed below.

PS: I don't mind submitting a MR for this as it's a relatively easy change. Or perhaps there is another reason this functionality wasn't already exposed in the metadata package? I searched existing/previous issues and didn't see anything on this.

Thank you!

Use Case

  1. For development, caching the blob locally instead of reading it remotely each time would save on network traffic.
  2. The ParseMetadata function loads the data into a global variable in an unsafe way (mostly concurrency issues). Perhaps the client would like to parse/load some or all of the data into state stored elsewhere.
  3. unmarshalMDSBLOB doesn't appear to use the http.Client it is being passed. Plus if it were exposed the blob could be retrieved using a custom http.Client or other method (before being passed to the unmarhal function).

Documentation

No response

Level Implementations

Description

Convert the specs into a feature matrix and advertise library features. I consider it important that we properly support the spec and document the library itself and provide adequate documentation in the user-facing portion of the library.

Use Case

No response

Documentation

Specifications:

Diffs:

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.