mirror of
https://github.com/sonr-io/sonr.git
synced 2026-08-04 18:31:41 +00:00
@@ -0,0 +1,458 @@
|
||||
package e2e
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/testutil/network"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/zeebo/blake3"
|
||||
|
||||
"github.com/sonr-io/sonr/app"
|
||||
didtypes "github.com/sonr-io/sonr/x/did/types"
|
||||
dwntypes "github.com/sonr-io/sonr/x/dwn/types"
|
||||
)
|
||||
|
||||
// WebAuthnRegistrationTestSuite tests the complete WebAuthn registration flow
|
||||
type WebAuthnRegistrationTestSuite struct {
|
||||
suite.Suite
|
||||
cfg network.Config
|
||||
network *network.Network
|
||||
}
|
||||
|
||||
func (suite *WebAuthnRegistrationTestSuite) SetupSuite() {
|
||||
suite.T().Log("setting up WebAuthn registration test suite")
|
||||
|
||||
cfg := network.DefaultConfig()
|
||||
cfg.NumValidators = 1
|
||||
|
||||
// Custom app constructor
|
||||
cfg.AppConstructor = func(val network.Validator) app.TestApp {
|
||||
return app.NewTestApp(val.Ctx.Logger, val.Ctx.Config.DBDir(), nil, true, 0)
|
||||
}
|
||||
|
||||
suite.cfg = cfg
|
||||
suite.network = network.New(suite.T(), cfg)
|
||||
|
||||
suite.Require().NotNil(suite.network)
|
||||
|
||||
// Wait for network to start
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
|
||||
func (suite *WebAuthnRegistrationTestSuite) TearDownSuite() {
|
||||
suite.T().Log("tearing down WebAuthn registration test suite")
|
||||
suite.network.Cleanup()
|
||||
}
|
||||
|
||||
// TestRegisterStart tests the RegisterStart query handler
|
||||
func (suite *WebAuthnRegistrationTestSuite) TestRegisterStart() {
|
||||
val := suite.network.Validators[0]
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
assertionValue string
|
||||
assertionType string
|
||||
serviceOrigin string
|
||||
expectErr bool
|
||||
errMsg string
|
||||
}{
|
||||
{
|
||||
name: "valid email registration",
|
||||
assertionValue: "alice@example.com",
|
||||
assertionType: "email",
|
||||
serviceOrigin: "http://localhost:3000",
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "valid phone registration",
|
||||
assertionValue: "+1234567890",
|
||||
assertionType: "tel",
|
||||
serviceOrigin: "http://localhost:3000",
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "invalid assertion type",
|
||||
assertionValue: "alice",
|
||||
assertionType: "invalid",
|
||||
serviceOrigin: "http://localhost:3000",
|
||||
expectErr: true,
|
||||
errMsg: "unsupported assertion type",
|
||||
},
|
||||
{
|
||||
name: "invalid origin",
|
||||
assertionValue: "alice@example.com",
|
||||
assertionType: "email",
|
||||
serviceOrigin: "http://malicious.com",
|
||||
expectErr: true,
|
||||
errMsg: "invalid origin",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
suite.Run(tc.name, func() {
|
||||
// Create RegisterStart request
|
||||
req := &didtypes.QueryRegisterStartRequest{
|
||||
AssertionValue: tc.assertionValue,
|
||||
AssertionType: tc.assertionType,
|
||||
ServiceOrigin: tc.serviceOrigin,
|
||||
}
|
||||
|
||||
// Query RegisterStart
|
||||
clientCtx := val.ClientCtx
|
||||
queryClient := didtypes.NewQueryClient(clientCtx)
|
||||
|
||||
resp, err := queryClient.RegisterStart(context.Background(), req)
|
||||
|
||||
if tc.expectErr {
|
||||
suite.Require().Error(err)
|
||||
suite.Require().Contains(err.Error(), tc.errMsg)
|
||||
} else {
|
||||
suite.Require().NoError(err)
|
||||
suite.Require().NotNil(resp)
|
||||
suite.Require().NotNil(resp.DIDDocument)
|
||||
suite.Require().NotEmpty(resp.DIDDocument.Id)
|
||||
|
||||
// Verify challenge is generated
|
||||
suite.Require().NotNil(resp.DIDDocumentMetadata)
|
||||
// Note: In real implementation, challenge would be in metadata
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestWebAuthnCredentialRegistration tests the complete registration flow
|
||||
func (suite *WebAuthnRegistrationTestSuite) TestWebAuthnCredentialRegistration() {
|
||||
val := suite.network.Validators[0]
|
||||
|
||||
// Test data
|
||||
username := "testuser"
|
||||
email := "testuser@example.com"
|
||||
|
||||
// Generate mock WebAuthn credential data
|
||||
credentialID := base64.StdEncoding.EncodeToString([]byte("test-credential-id"))
|
||||
publicKey := generateMockPublicKey()
|
||||
attestationObject := generateMockAttestationObject()
|
||||
clientDataJSON := generateMockClientDataJSON()
|
||||
|
||||
// Create registration message
|
||||
msg := &didtypes.MsgRegisterWebAuthnCredential{
|
||||
Creator: val.Address.String(),
|
||||
Username: username,
|
||||
AssertionValue: email,
|
||||
AssertionType: "email",
|
||||
WebauthnCredential: &didtypes.WebAuthnCredential{
|
||||
CredentialId: credentialID,
|
||||
PublicKey: publicKey,
|
||||
AttestationObject: attestationObject,
|
||||
ClientDataJson: clientDataJSON,
|
||||
},
|
||||
CreateVault: true,
|
||||
}
|
||||
|
||||
// Broadcast transaction
|
||||
clientCtx := val.ClientCtx
|
||||
txResp, err := broadcastTx(clientCtx, msg)
|
||||
|
||||
suite.Require().NoError(err)
|
||||
suite.Require().Equal(uint32(0), txResp.Code)
|
||||
|
||||
// Wait for transaction to be processed
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// Verify DID document was created
|
||||
queryClient := didtypes.NewQueryClient(clientCtx)
|
||||
|
||||
// Calculate expected DID
|
||||
hasher := blake3.New()
|
||||
hasher.Write([]byte(email))
|
||||
hash := hasher.Sum(nil)
|
||||
expectedDID := fmt.Sprintf("did:email:%x", hash)
|
||||
|
||||
// Query for the DID document
|
||||
didResp, err := queryClient.GetDIDDocument(context.Background(), &didtypes.QueryGetDIDDocumentRequest{
|
||||
Did: expectedDID,
|
||||
})
|
||||
|
||||
suite.Require().NoError(err)
|
||||
suite.Require().NotNil(didResp)
|
||||
suite.Require().NotNil(didResp.DidDocument)
|
||||
suite.Require().Equal(expectedDID, didResp.DidDocument.Id)
|
||||
|
||||
// Verify assertion methods
|
||||
suite.Require().Len(didResp.DidDocument.AssertionMethod, 2)
|
||||
|
||||
// Verify authentication method (WebAuthn)
|
||||
suite.Require().Len(didResp.DidDocument.Authentication, 1)
|
||||
|
||||
// Verify controller is set
|
||||
suite.Require().Len(didResp.DidDocument.Controller, 1)
|
||||
}
|
||||
|
||||
// TestVaultCreation tests that vault is created during registration
|
||||
func (suite *WebAuthnRegistrationTestSuite) TestVaultCreation() {
|
||||
val := suite.network.Validators[0]
|
||||
|
||||
username := "vaultuser"
|
||||
email := "vaultuser@example.com"
|
||||
|
||||
// Create registration with vault
|
||||
msg := &didtypes.MsgRegisterWebAuthnCredential{
|
||||
Creator: val.Address.String(),
|
||||
Username: username,
|
||||
AssertionValue: email,
|
||||
AssertionType: "email",
|
||||
WebauthnCredential: &didtypes.WebAuthnCredential{
|
||||
CredentialId: "vault-cred-id",
|
||||
PublicKey: generateMockPublicKey(),
|
||||
AttestationObject: generateMockAttestationObject(),
|
||||
ClientDataJson: generateMockClientDataJSON(),
|
||||
},
|
||||
CreateVault: true,
|
||||
}
|
||||
|
||||
clientCtx := val.ClientCtx
|
||||
txResp, err := broadcastTx(clientCtx, msg)
|
||||
|
||||
suite.Require().NoError(err)
|
||||
suite.Require().Equal(uint32(0), txResp.Code)
|
||||
|
||||
// Wait for processing
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// Query vault
|
||||
dwnQueryClient := dwntypes.NewQueryClient(clientCtx)
|
||||
|
||||
// Calculate expected vault ID (based on DID)
|
||||
hasher := blake3.New()
|
||||
hasher.Write([]byte(email))
|
||||
hash := hasher.Sum(nil)
|
||||
expectedDID := fmt.Sprintf("did:email:%x", hash)
|
||||
|
||||
vaultResp, err := dwnQueryClient.GetVaultByDID(context.Background(), &dwntypes.QueryGetVaultByDIDRequest{
|
||||
Did: expectedDID,
|
||||
})
|
||||
|
||||
suite.Require().NoError(err)
|
||||
suite.Require().NotNil(vaultResp)
|
||||
suite.Require().NotNil(vaultResp.Vault)
|
||||
suite.Require().Equal(expectedDID, vaultResp.Vault.Did)
|
||||
suite.Require().NotEmpty(vaultResp.Vault.PublicKey)
|
||||
}
|
||||
|
||||
// TestAssertionUniqueness tests that duplicate assertions are rejected
|
||||
func (suite *WebAuthnRegistrationTestSuite) TestAssertionUniqueness() {
|
||||
val := suite.network.Validators[0]
|
||||
|
||||
email := "unique@example.com"
|
||||
|
||||
// First registration
|
||||
msg1 := &didtypes.MsgRegisterWebAuthnCredential{
|
||||
Creator: val.Address.String(),
|
||||
Username: "user1",
|
||||
AssertionValue: email,
|
||||
AssertionType: "email",
|
||||
WebauthnCredential: &didtypes.WebAuthnCredential{
|
||||
CredentialId: "cred-1",
|
||||
PublicKey: generateMockPublicKey(),
|
||||
AttestationObject: generateMockAttestationObject(),
|
||||
ClientDataJson: generateMockClientDataJSON(),
|
||||
},
|
||||
CreateVault: false,
|
||||
}
|
||||
|
||||
clientCtx := val.ClientCtx
|
||||
txResp1, err := broadcastTx(clientCtx, msg1)
|
||||
|
||||
suite.Require().NoError(err)
|
||||
suite.Require().Equal(uint32(0), txResp1.Code)
|
||||
|
||||
// Wait for processing
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// Attempt duplicate registration with same email
|
||||
msg2 := &didtypes.MsgRegisterWebAuthnCredential{
|
||||
Creator: val.Address.String(),
|
||||
Username: "user2",
|
||||
AssertionValue: email,
|
||||
AssertionType: "email",
|
||||
WebauthnCredential: &didtypes.WebAuthnCredential{
|
||||
CredentialId: "cred-2",
|
||||
PublicKey: generateMockPublicKey(),
|
||||
AttestationObject: generateMockAttestationObject(),
|
||||
ClientDataJson: generateMockClientDataJSON(),
|
||||
},
|
||||
CreateVault: false,
|
||||
}
|
||||
|
||||
txResp2, err := broadcastTx(clientCtx, msg2)
|
||||
|
||||
// Should fail due to duplicate assertion
|
||||
suite.Require().Error(err)
|
||||
if txResp2 != nil {
|
||||
suite.Require().NotEqual(uint32(0), txResp2.Code)
|
||||
}
|
||||
}
|
||||
|
||||
// TestUCANDelegationChain tests UCAN token creation during registration
|
||||
func (suite *WebAuthnRegistrationTestSuite) TestUCANDelegationChain() {
|
||||
val := suite.network.Validators[0]
|
||||
|
||||
username := "ucanuser"
|
||||
email := "ucanuser@example.com"
|
||||
|
||||
msg := &didtypes.MsgRegisterWebAuthnCredential{
|
||||
Creator: val.Address.String(),
|
||||
Username: username,
|
||||
AssertionValue: email,
|
||||
AssertionType: "email",
|
||||
WebauthnCredential: &didtypes.WebAuthnCredential{
|
||||
CredentialId: "ucan-cred-id",
|
||||
PublicKey: generateMockPublicKey(),
|
||||
AttestationObject: generateMockAttestationObject(),
|
||||
ClientDataJson: generateMockClientDataJSON(),
|
||||
},
|
||||
CreateVault: true,
|
||||
}
|
||||
|
||||
clientCtx := val.ClientCtx
|
||||
txResp, err := broadcastTx(clientCtx, msg)
|
||||
|
||||
suite.Require().NoError(err)
|
||||
suite.Require().Equal(uint32(0), txResp.Code)
|
||||
|
||||
// Wait for processing
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// Query DID document
|
||||
queryClient := didtypes.NewQueryClient(clientCtx)
|
||||
|
||||
hasher := blake3.New()
|
||||
hasher.Write([]byte(email))
|
||||
hash := hasher.Sum(nil)
|
||||
expectedDID := fmt.Sprintf("did:email:%x", hash)
|
||||
|
||||
didResp, err := queryClient.GetDIDDocument(context.Background(), &didtypes.QueryGetDIDDocumentRequest{
|
||||
Did: expectedDID,
|
||||
})
|
||||
|
||||
suite.Require().NoError(err)
|
||||
suite.Require().NotNil(didResp.DidDocumentMetadata)
|
||||
|
||||
// Verify UCAN delegation chain reference exists in metadata
|
||||
// In real implementation, this would check for the actual UCAN token
|
||||
suite.Require().NotEmpty(didResp.DidDocumentMetadata.Created)
|
||||
}
|
||||
|
||||
// TestMultipleAssertionTypes tests registration with both email and phone
|
||||
func (suite *WebAuthnRegistrationTestSuite) TestMultipleAssertionTypes() {
|
||||
val := suite.network.Validators[0]
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
username string
|
||||
assertionType string
|
||||
assertionValue string
|
||||
}{
|
||||
{
|
||||
name: "register with email",
|
||||
username: "emailuser",
|
||||
assertionType: "email",
|
||||
assertionValue: "multi@example.com",
|
||||
},
|
||||
{
|
||||
name: "register with phone",
|
||||
username: "phoneuser",
|
||||
assertionType: "tel",
|
||||
assertionValue: "+9876543210",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
suite.Run(tc.name, func() {
|
||||
msg := &didtypes.MsgRegisterWebAuthnCredential{
|
||||
Creator: val.Address.String(),
|
||||
Username: tc.username,
|
||||
AssertionValue: tc.assertionValue,
|
||||
AssertionType: tc.assertionType,
|
||||
WebauthnCredential: &didtypes.WebAuthnCredential{
|
||||
CredentialId: fmt.Sprintf("cred-%s", tc.username),
|
||||
PublicKey: generateMockPublicKey(),
|
||||
AttestationObject: generateMockAttestationObject(),
|
||||
ClientDataJson: generateMockClientDataJSON(),
|
||||
},
|
||||
CreateVault: false,
|
||||
}
|
||||
|
||||
clientCtx := val.ClientCtx
|
||||
txResp, err := broadcastTx(clientCtx, msg)
|
||||
|
||||
suite.Require().NoError(err)
|
||||
suite.Require().Equal(uint32(0), txResp.Code)
|
||||
|
||||
// Wait for processing
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// Verify DID was created with correct prefix
|
||||
hasher := blake3.New()
|
||||
hasher.Write([]byte(tc.assertionValue))
|
||||
hash := hasher.Sum(nil)
|
||||
expectedDID := fmt.Sprintf("did:%s:%x", tc.assertionType, hash)
|
||||
|
||||
queryClient := didtypes.NewQueryClient(clientCtx)
|
||||
didResp, err := queryClient.GetDIDDocument(context.Background(), &didtypes.QueryGetDIDDocumentRequest{
|
||||
Did: expectedDID,
|
||||
})
|
||||
|
||||
suite.Require().NoError(err)
|
||||
suite.Require().NotNil(didResp.DidDocument)
|
||||
suite.Require().Equal(expectedDID, didResp.DidDocument.Id)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
|
||||
func broadcastTx(clientCtx client.Context, msg sdk.Msg) (*sdk.TxResponse, error) {
|
||||
// This is a simplified version - in real tests, use proper tx broadcasting
|
||||
// with proper fee calculation and signing
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func generateMockPublicKey() string {
|
||||
// Generate a mock public key for testing
|
||||
// In real tests, this would be a proper WebAuthn public key
|
||||
return base64.StdEncoding.EncodeToString([]byte("mock-public-key"))
|
||||
}
|
||||
|
||||
func generateMockAttestationObject() string {
|
||||
// Generate a mock attestation object
|
||||
attestation := map[string]interface{}{
|
||||
"fmt": "packed",
|
||||
"attStmt": map[string]interface{}{},
|
||||
"authData": base64.StdEncoding.EncodeToString([]byte("mock-auth-data")),
|
||||
}
|
||||
data, _ := json.Marshal(attestation)
|
||||
return base64.StdEncoding.EncodeToString(data)
|
||||
}
|
||||
|
||||
func generateMockClientDataJSON() string {
|
||||
// Generate mock client data
|
||||
clientData := map[string]interface{}{
|
||||
"type": "webauthn.create",
|
||||
"challenge": "test-challenge",
|
||||
"origin": "http://localhost:3000",
|
||||
}
|
||||
data, _ := json.Marshal(clientData)
|
||||
return base64.StdEncoding.EncodeToString(data)
|
||||
}
|
||||
|
||||
func TestWebAuthnRegistrationTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(WebAuthnRegistrationTestSuite))
|
||||
}
|
||||
Reference in New Issue
Block a user