Skip to content

Commit

Permalink
refactor(protocol): change *http.Request parameter
Browse files Browse the repository at this point in the history
  • Loading branch information
rgcruz010 committed Jun 21, 2024
1 parent 9541d20 commit ff6aec1
Show file tree
Hide file tree
Showing 8 changed files with 77 additions and 100 deletions.
69 changes: 45 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,34 +101,45 @@ func main() {
```go
package example

import "io"

func BeginRegistration(w http.ResponseWriter, r *http.Request) {
user := datastore.GetUser() // Find or create the new user
options, session, err := webAuthn.BeginRegistration(user)
// handle errors if present
// store the sessionData values
JSONResponse(w, options, http.StatusOK) // return the options generated
// options.publicKey contain our registration options
user := datastore.GetUser() // Find or create the new user
options, session, err := webAuthn.BeginRegistration(user)
// handle errors if present
// store the sessionData values
JSONResponse(w, options, http.StatusOK) // return the options generated
// options.publicKey contain our registration options
}

func FinishRegistration(w http.ResponseWriter, r *http.Request) {
user := datastore.GetUser() // Get the user

// Get the session data stored from the function above
session := datastore.GetSession()

credential, err := webAuthn.FinishRegistration(user, session, r)
if err != nil {
// Handle Error and return.
user := datastore.GetUser() // Get the user

return
}

// If creation was successful, store the credential object
// Pseudocode to add the user credential.
user.AddCredential(credential)
datastore.SaveUser(user)
// Get the session data stored from the function above
session := datastore.GetSession()

JSONResponse(w, "Registration Success", http.StatusOK) // Handle next steps
body, err := io.ReadAll(r.Body)
if err != nil{
// Handle Error and return.

return
}

defer body.Close()

credential, err := webAuthn.FinishRegistration(user, session, body)
if err != nil {
// Handle Error and return.

return
}

// If creation was successful, store the credential object
// Pseudocode to add the user credential.
user.AddCredential(credential)
datastore.SaveUser(user)

JSONResponse(w, "Registration Success", http.StatusOK) // Handle next steps
}
```

Expand Down Expand Up @@ -159,8 +170,17 @@ func FinishLogin(w http.ResponseWriter, r *http.Request) {

// Get the session data stored from the function above
session := datastore.GetSession()

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

body, err := io.ReadAll(r.Body)
if err != nil{
// Handle Error and return.

return
}

defer body.Close()

credential, err := webAuthn.FinishLogin(user, session, body)
if err != nil {
// Handle Error and return.

Expand All @@ -176,6 +196,7 @@ func FinishLogin(w http.ResponseWriter, r *http.Request) {

JSONResponse(w, "Login Success", http.StatusOK)
}

```

## Modifying Credential Options
Expand Down
19 changes: 10 additions & 9 deletions protocol/assertion.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"encoding/json"
"fmt"
"io"
"net/http"

"github.com/go-webauthn/webauthn/protocol/webauthncose"
)
Expand Down Expand Up @@ -49,24 +48,26 @@ type ParsedAssertionResponse struct {
// ParseCredentialRequestResponse parses the credential request response into a format that is either required by the
// specification or makes the assertion verification steps easier to complete. This takes a http.Request that contains
// the assertion response data in a raw, mostly base64 encoded format, and parses the data into manageable structures.
func ParseCredentialRequestResponse(response *http.Request) (*ParsedCredentialAssertionData, error) {
if response == nil || response.Body == nil {
func ParseCredentialRequestResponse(credentialResponse io.Reader) (*ParsedCredentialAssertionData, error) {
if credentialResponse == nil {
return nil, ErrBadRequest.WithDetails("No response given")
}

defer response.Body.Close()
defer io.Copy(io.Discard, response.Body)

return ParseCredentialRequestResponseBody(response.Body)
return ParseCredentialRequestResponseBody(credentialResponse)
}

// ParseCredentialRequestResponseBody parses the credential request response into a format that is either required by
// the specification or makes the assertion verification steps easier to complete. This takes an io.Reader that contains
// the assertion response data in a raw, mostly base64 encoded format, and parses the data into manageable structures.
func ParseCredentialRequestResponseBody(body io.Reader) (par *ParsedCredentialAssertionData, err error) {
func ParseCredentialRequestResponseBody(credentialResponse io.Reader) (par *ParsedCredentialAssertionData, err error) {
bytes, err := io.ReadAll(credentialResponse)
if err != nil {
return nil, err
}

var car CredentialAssertionResponse

if err = decodeBody(body, &car); err != nil {
if err = json.Unmarshal(bytes, &car); err != nil {
return nil, ErrBadRequest.WithDetails("Parse error for Assertion").WithInfo(err.Error())
}

Expand Down
14 changes: 1 addition & 13 deletions protocol/assertion_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package protocol
import (
"bytes"
"encoding/base64"
"io"
"testing"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -94,24 +93,13 @@ func TestParseCredentialRequestResponse(t *testing.T) {
},
errString: "",
},
{
name: "ShouldHandleTrailingData",
args: args{
"trailingData",
},
expected: nil,
errString: "Parse error for Assertion",
errType: "invalid_request",
errDetails: "Parse error for Assertion",
errInfo: "The body contains trailing data",
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()

body := io.NopCloser(bytes.NewReader([]byte(testAssertionResponses[tc.args.responseName])))
body := bytes.NewBuffer([]byte(testAssertionResponses[tc.args.responseName]))

actual, err := ParseCredentialRequestResponseBody(body)

Expand Down
22 changes: 12 additions & 10 deletions protocol/credential.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ package protocol
import (
"crypto/sha256"
"encoding/base64"
"encoding/json"
"io"
"net/http"
)

// Credential is the basic credential type from the Credential Management specification that is inherited by WebAuthn's
Expand Down Expand Up @@ -60,23 +60,25 @@ type ParsedCredentialCreationData struct {

// ParseCredentialCreationResponse is a non-agnostic function for parsing a registration response from the http library
// from stdlib. It handles some standard cleanup operations.
func ParseCredentialCreationResponse(response *http.Request) (*ParsedCredentialCreationData, error) {
if response == nil || response.Body == nil {
func ParseCredentialCreationResponse(creationResponse io.Reader) (*ParsedCredentialCreationData, error) {
if creationResponse == nil {
return nil, ErrBadRequest.WithDetails("No response given")
}

defer response.Body.Close()
defer io.Copy(io.Discard, response.Body)

return ParseCredentialCreationResponseBody(response.Body)
return ParseCredentialCreationResponseBody(creationResponse)
}

// ParseCredentialCreationResponseBody is an agnostic version of ParseCredentialCreationResponse. Implementers are
// therefore responsible for managing cleanup.
func ParseCredentialCreationResponseBody(body io.Reader) (pcc *ParsedCredentialCreationData, err error) {
func ParseCredentialCreationResponseBody(creationResponse io.Reader) (pcc *ParsedCredentialCreationData, err error) {
bytes, err := io.ReadAll(creationResponse)
if err != nil {
return nil, err
}

var ccr CredentialCreationResponse

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

Expand Down Expand Up @@ -204,7 +206,7 @@ func (pcc *ParsedCredentialCreationData) Verify(storedChallenge string, verifyUs
// 9. Return the appid extension value from the Session data.
func (ppkc ParsedPublicKeyCredential) GetAppID(authExt AuthenticationExtensions, credentialAttestationType string) (appID string, err error) {
var (
value, clientValue interface{}
value, clientValue any
enableAppID, ok bool
)

Expand Down
14 changes: 1 addition & 13 deletions protocol/credential_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package protocol
import (
"bytes"
"encoding/base64"
"io"
"testing"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -94,22 +93,11 @@ func TestParseCredentialCreationResponse(t *testing.T) {
},
errString: "",
},
{
name: "ShouldHandleTrailingData",
args: args{
responseName: "trailingData",
},
expected: nil,
errString: "Parse error for Registration",
errType: "invalid_request",
errDetails: "Parse error for Registration",
errInfo: "The body contains trailing data",
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
body := io.NopCloser(bytes.NewReader([]byte(testCredentialRequestResponses[tc.args.responseName])))
body := bytes.NewBuffer([]byte(testCredentialRequestResponses[tc.args.responseName]))

actual, err := ParseCredentialCreationResponseBody(body)

Expand Down
23 changes: 0 additions & 23 deletions protocol/decoder.go

This file was deleted.

10 changes: 5 additions & 5 deletions webauthn/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package webauthn
import (
"bytes"
"fmt"
"net/http"
"io"
"net/url"
"time"

Expand Down Expand Up @@ -162,8 +162,8 @@ func WithLoginRelyingPartyID(id string) LoginOption {
}

// FinishLogin takes the response from the client and validate it against the user credentials and stored session data.
func (webauthn *WebAuthn) FinishLogin(user User, session SessionData, response *http.Request) (*Credential, error) {
parsedResponse, err := protocol.ParseCredentialRequestResponse(response)
func (webauthn *WebAuthn) FinishLogin(user User, session SessionData, clientResponse io.Reader) (*Credential, error) {
parsedResponse, err := protocol.ParseCredentialRequestResponse(clientResponse)
if err != nil {
return nil, err
}
Expand All @@ -174,8 +174,8 @@ func (webauthn *WebAuthn) FinishLogin(user User, session SessionData, response *
// FinishDiscoverableLogin takes the response from the client and validate it against the handler and stored session data.
// The handler helps to find out which user must be used to validate the response. This is a function defined in your
// business code that will retrieve the user from your persistent data.
func (webauthn *WebAuthn) FinishDiscoverableLogin(handler DiscoverableUserHandler, session SessionData, response *http.Request) (*Credential, error) {
parsedResponse, err := protocol.ParseCredentialRequestResponse(response)
func (webauthn *WebAuthn) FinishDiscoverableLogin(handler DiscoverableUserHandler, session SessionData, clientResponse io.Reader) (*Credential, error) {
parsedResponse, err := protocol.ParseCredentialRequestResponse(clientResponse)
if err != nil {
return nil, err
}
Expand Down
6 changes: 3 additions & 3 deletions webauthn/registration.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package webauthn
import (
"bytes"
"fmt"
"net/http"
"io"
"net/url"
"time"

Expand Down Expand Up @@ -203,8 +203,8 @@ func WithRegistrationRelyingPartyName(name string) RegistrationOption {

// FinishRegistration takes the response from the authenticator and client and verify the credential against the user's
// credentials and session data.
func (webauthn *WebAuthn) FinishRegistration(user User, session SessionData, response *http.Request) (*Credential, error) {
parsedResponse, err := protocol.ParseCredentialCreationResponse(response)
func (webauthn *WebAuthn) FinishRegistration(user User, session SessionData, authenticatorResponse io.Reader) (*Credential, error) {
parsedResponse, err := protocol.ParseCredentialCreationResponse(authenticatorResponse)
if err != nil {
return nil, err
}
Expand Down

0 comments on commit ff6aec1

Please sign in to comment.