feat: implement register Passkey user API v2 (#5873)

* command/crypto: DRY the code

- reuse the the algorithm switch to create a secret generator
- add a verifyCryptoCode function

* command: crypto code tests

* migrate webauthn package

* finish integration tests with webauthn mock client
This commit is contained in:
Tim Möhlmann
2023-05-24 13:22:00 +03:00
committed by GitHub
parent 6839a5c203
commit a301c40f9f
44 changed files with 2528 additions and 517 deletions

View File

@@ -0,0 +1,36 @@
package webauthn
import (
"fmt"
"github.com/descope/virtualwebauthn"
)
type Client struct {
rp virtualwebauthn.RelyingParty
auth virtualwebauthn.Authenticator
credential virtualwebauthn.Credential
}
func NewClient(name, domain, origin string) *Client {
rp := virtualwebauthn.RelyingParty{
Name: name,
ID: domain,
Origin: origin,
}
return &Client{
rp: rp,
auth: virtualwebauthn.NewAuthenticator(),
credential: virtualwebauthn.NewCredential(virtualwebauthn.KeyTypeEC2),
}
}
func (c *Client) CreateAttestationResponse(options []byte) ([]byte, error) {
parsedAttestationOptions, err := virtualwebauthn.ParseAttestationOptions(string(options))
if err != nil {
return nil, fmt.Errorf("webauthn.Client.CreateAttestationResponse: %w", err)
}
return []byte(virtualwebauthn.CreateAttestationResponse(
c.rp, c.auth, c.credential, *parsedAttestationOptions,
)), nil
}

View File

@@ -1,8 +1,9 @@
package webauthn
import (
"github.com/duo-labs/webauthn/protocol"
"github.com/duo-labs/webauthn/webauthn"
"github.com/go-webauthn/webauthn/protocol"
"github.com/go-webauthn/webauthn/webauthn"
"github.com/zitadel/zitadel/internal/domain"
)

View File

@@ -5,8 +5,8 @@ import (
"context"
"encoding/json"
"github.com/duo-labs/webauthn/protocol"
"github.com/duo-labs/webauthn/webauthn"
"github.com/go-webauthn/webauthn/protocol"
"github.com/go-webauthn/webauthn/webauthn"
"github.com/zitadel/logging"
"github.com/zitadel/zitadel/internal/api/authz"
@@ -177,9 +177,13 @@ func (w *Config) FinishLogin(ctx context.Context, user *domain.Human, webAuthN *
func (w *Config) serverFromContext(ctx context.Context) (*webauthn.WebAuthn, error) {
instance := authz.GetInstance(ctx)
return webauthn.New(&webauthn.Config{
webAuthn, err := webauthn.New(&webauthn.Config{
RPDisplayName: w.DisplayName,
RPID: instance.RequestedDomain(),
RPOrigin: http.BuildOrigin(instance.RequestedHost(), w.ExternalSecure),
RPOrigins: []string{http.BuildOrigin(instance.RequestedHost(), w.ExternalSecure)},
})
if err != nil {
return nil, caos_errs.ThrowInternal(err, "WEBAU-UX9ta", "Errors.User.WebAuthN.ServerConfig")
}
return webAuthn, nil
}

View File

@@ -0,0 +1,58 @@
package webauthn
import (
"context"
"testing"
"github.com/go-webauthn/webauthn/webauthn"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/zitadel/zitadel/internal/api/authz"
caos_errs "github.com/zitadel/zitadel/internal/errors"
)
func TestConfig_serverFromContext(t *testing.T) {
type args struct {
ctx context.Context
}
tests := []struct {
name string
args args
want *webauthn.WebAuthn
wantErr error
}{
{
name: "webauthn error",
args: args{context.Background()},
wantErr: caos_errs.ThrowInternal(nil, "WEBAU-UX9ta", "Errors.User.WebAuthN.ServerConfig"),
},
{
name: "success",
args: args{authz.WithRequestedDomain(context.Background(), "example.com")},
want: &webauthn.WebAuthn{
Config: &webauthn.Config{
RPDisplayName: "DisplayName",
RPID: "example.com",
RPOrigins: []string{"https://example.com"},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
w := &Config{
DisplayName: "DisplayName",
ExternalSecure: true,
}
got, err := w.serverFromContext(tt.args.ctx)
require.ErrorIs(t, err, tt.wantErr)
if tt.want != nil {
require.NotNil(t, got)
assert.Equal(t, tt.want.Config.RPDisplayName, got.Config.RPDisplayName)
assert.Equal(t, tt.want.Config.RPID, got.Config.RPID)
assert.Equal(t, tt.want.Config.RPOrigins, got.Config.RPOrigins)
}
})
}
}