duo-labs / webauthn

WebAuthn (FIDO2) server library written in Go
https://webauthn.io/
BSD 3-Clause "New" or "Revised" License
1.03k stars 162 forks source link

appid extension: u2f registered keys compat #106

Closed james-d-elliott closed 2 years ago

james-d-elliott commented 2 years ago

So I'm experimenting with this library. Currently the issue is with the appid extension enabled it's claiming the device registered via u2f is not familiar. Newly registered keys work with my config, but the old FIDO ones do not. This issue occurs when navigator.credentials.get is called in either Chrome or Firefox. Code snips:

Creating the handler:

    if webauthnHandler, err = webauthn.New(&webauthn.Config{
        RPID:                  "webauthn.example.com",
        RPDisplayName:         "Webauthn",
        RPOrigin:              "https://webauthn.example.com",
        AttestationPreference: protocol.PreferIndirectAttestation,
    }); err != nil {
        panic(err)
    }

Credential added the users credentials:

    keyID, _ := base64.StdEncoding.DecodeString(encodedKeyHandleIDHere)
    pubkey, _ := base64.StdEncoding.DecodeString(encodedPubKeyHere)

    credential := webauthn.Credential{
        ID:              keyID,
        PublicKey:       pubkey,
        AttestationType: "fido-u2f", // Also tried with this commented.
        Authenticator: webauthn.Authenticator{
            SignCount: 0,
            AAGUID:    []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        },
    }

Creating the assertion:

    assertion, data, err := webauthnHandler.BeginLogin(user,
        webauthn.WithUserVerification(body.UserVerification),
        webauthn.WithAssertionExtensions(map[string]interface{}{"appid": "https://webauthn.example.com"}),
    )
james-d-elliott commented 2 years ago

For a side note, it fails before posting the assertion result back to the server for validation, the browser doesn't recognize the key at all.

james-d-elliott commented 2 years ago

Actually, I just got a step further by ensuring the appid was the same when the U2F key was the same, however I get the following error now:

Error verifying the authenticator data Error validating the authenticator response

My guess is based on a colleagues attempts is that the response is not being validated against the appid but is being validated against the rpid?

aseigler commented 2 years ago

Sounds about right. Extensions support never made it into the library. What you'd need to do is check for appid extension presence, and retrieve the appid from the extensions data and use that approximately here and also over here as well instead of using the rpID, then it should work.

james-d-elliott commented 2 years ago

Yep, that's looking like what my colleague roughly tried (he's currently taking a break). I am using his branch with a replace but something is still not quite right so guess I just have to comb through what else could be wrong or anything missed.

Thanks for the feedback, at least we're looking in the right area.

james-d-elliott commented 2 years ago

So it appears the browser is not adding that to the response for the assertion challenge response/reply or the library is failing to parse it at all. Is this something that has to be manually added or is the browser meant to add this to the authenticatorData/clientDataJSON?

james-d-elliott commented 2 years ago

Oh I think I see. We have to call PublicKeyCredential.getClientExtensionResults() I think and check the appId, if present we add it to the response.

aseigler commented 2 years ago

I think that is a boolean value that will tell you if appId is present in the extensions data, you'll have to pull it out of there and use it in place of rpId if I am remembering correctly.

james-d-elliott commented 2 years ago

Yeah that's a boolean, so what I'm doing is checking if it's true, then adding the appid from the PublicKeyCredentialRequestOptions into the response in the following typescript schema:

interface AssertionChallengeResponse {
    id: string;
    rawId: string;
    type: string;
    extensions?: AssertionChallengeResponseExtensions;
    response: AssertionResponse;
}

interface AssertionChallengeResponseExtensions {
    appid?: string;
}

I'm currently getting a parsing error when it tries to parse the JSON but debugging it as we speak.

james-d-elliott commented 2 years ago

That worked as expected, had to change protocol/extensions.go and also had to ensure the decoded extensions map made it into the parsed map which currently doesn't occcur, will double check later if it's actually necessary to change extensions.go as I don't see why it would be. Much further along, error is now: Error validating the assertion signature: Unsupported Public Key Type

Instead of:

type AuthenticationExtensionsClientOutputs map[interface{}]interface{}
type AuthenticationExtensionsClientOutputs map[string]interface{}

Looks like the fmt must be set to tpm for some reason to me. Or something else strange is occurring.

Edit: Was looking at the wrong thing. Should be simple now that I know where I'm meant to look.

james-d-elliott commented 2 years ago

So the limitation now is U2F/FIDO doesn't use CBOR I don't think:

https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-client-to-authenticator-protocol-v2.0-id-20180227.html#relationship-to-other-specifications