go-webauthn / webauthn

Webauthn/FIDO2 library in golang
Other
816 stars 78 forks source link

Add support for fasthttp or decouple FinishRegistration from net/http #297

Closed william-mens closed 1 month ago

william-mens commented 2 months ago

Description

This is a feature request to improve the flexibility of the WebAuthn library by decoupling the FinishRegistration method (and similar methods) from being tied to net/http. Currently, frameworks like Fiber, which are based on fasthttp, encounter compatibility issues because the library only supports *http.Request.

When using Fiber’s c.Request() (which returns a fasthttp.Request), developers encounter a type mismatch error, forcing them to rely on adaptors (such as fasthttpadaptor) to convert between fasthttp.Request and http.Request. While this workaround works, it adds unnecessary complexity and dependency bloat to the application.

Frameworks like Fiber are popular for their performance and low resource usage. Adding native support for fasthttp or abstracting the HTTP request layer will remove the need for adaptors and keep applications lightweight. A more flexible API will broaden the potential use of the library across a wider range of Go web frameworks.

Proposed Solution:

Steps to Reproduce:

Current Workaround: To bypass the type incompatibility, developers must use an adaptor: req, err := fasthttpadaptor.ConvertRequest(c.Context()) if err != nil { // Handle error } credential, err := webAuthn.FinishRegistration(authAuth, user, sessionData, req)

Use Case

No response

Documentation

No response

james-d-elliott commented 2 months ago

It's already supported. Take a look at the implementation specifics of these functions, you'll realize they're just decorators for lower lower level functions which can parse bytes and io.Readers.

Example:

    if response, err := protocol.ParseCredentialCreationResponseBody(bytes.NewReader(ctx.PostBody())); err != nil {
        panic(err)
    } else if credential, err := webauthn.CreateCredential(user, sessionData, response); err != nil {
            panic(err)
    } else {
                ...
    }
william-mens commented 2 months ago

Hello James, Thank you for your prompt response! I really appreciate the clarification. I have implemented the suggested changes, and everything is working smoothly now—no more errors!

I also think it would be beneficial to update the documentation with these examples. If you're open to it, I would be happy to assist with that.

Lastly, I'm encountering a minor challenge when I log the error from the credential creation process. I'm receiving the message: "Error validating origin." Below is the relevant test code for my WebAuthn configuration:

app := fiber.New()

app.Use(cors.New(cors.Config{
    AllowOrigins: "http://localhost:80",
    AllowHeaders: "Origin, Content-Type, Accept",
}))

wconfig := &webauthn.Config{
    RPDisplayName: "Go Webauthn",
    RPID:          "localhost",
    RPOrigins:     []string{"http://localhost:80"},
}
func completeRegistration(c *fiber.Ctx) error {
    // Retrieve the user
    userID := "11111111" // sample userId
    user, exists := users[userID]
    if !exists {
        return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "User not found"})
    }

    var requestBody bytes.Buffer
    if _, err := requestBody.ReadFrom(c.Body()); err != nil {
        return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Unable to read request body"})
    }

    response, err := protocol.ParseCredentialCreationResponseBody(&requestBody)
    if err != nil {
        return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()})
    }

    credential, err := webAuthn.CreateCredential(user, *user.Session, response)
    if err != nil {
        fmt.Println("Error during credential creation:", err) //
<img width="1081" alt="Screenshot 2024-09-20 at 1 37 56 PM" src="https://github.com/user-attachments/assets/eca47e27-05b1-473c-8357-f8ae36aa1e08">
Error validating origin.
        return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Error during credential creation"})
    }

    user.Credentials = append(user.Credentials, *credential)

    return c.JSON(fiber.Map{
        "status":     "registration completed",
        "credential": credential,
    })
}
james-d-elliott commented 1 month ago

I'd welcome the docs change. Ideally at some stage I'll convert all docs into godoc but that's not necessary for now.

I'll look into the error later, do you know where the error is coming from using GDB?

Does the following func called on the error give more helpful error output?

func formatWebAuthnError(err error) error {
    out := &protocol.Error{}

    if errors.As(err, &out) {
        if len(out.DevInfo) == 0 {
            return err
        }

        if len(out.Type) == 0 {
            return fmt.Errorf("%w: %s", err, out.DevInfo)
        }

        return fmt.Errorf("%w (%s): %s", err, out.Type, out.DevInfo)
    }

    return err
}
william-mens commented 1 month ago

Hello @james-d-elliott Sorry for the late reply, and thank you so much for your support and help with the WebAuthn integration! I managed to resolve the error – it was a small mistake on my end. Thanks to your guidance and the amazing library, we’ve successfully integrated WebAuthn and are already live.

I’ll also work on updating the docs