package auth import ( "context" "encoding/base64" "encoding/json" "fmt" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/sonr-io/sonr/x/did/types" ) // Mock DIDClient for testing type mockDIDClient struct { credentials []*types.WebAuthnCredential didDoc *types.DIDDocument error error } func (m *mockDIDClient) QueryCredentials(ctx context.Context, did string) ([]*types.WebAuthnCredential, error) { if m.error != nil { return nil, m.error } return m.credentials, nil } func (m *mockDIDClient) QueryCredential(ctx context.Context, did, credentialID string) (*types.WebAuthnCredential, error) { if m.error != nil { return nil, m.error } for _, cred := range m.credentials { if cred.CredentialId == credentialID { return cred, nil } } return nil, fmt.Errorf("key not found") } func (m *mockDIDClient) UpdateCredential(ctx context.Context, did string, credential *types.WebAuthnCredential) error { if m.error != nil { return m.error } for i, cred := range m.credentials { if cred.CredentialId == credential.CredentialId { m.credentials[i] = credential return nil } } return fmt.Errorf("key not found") } func (m *mockDIDClient) RevokeCredential(ctx context.Context, did, credentialID string) error { if m.error != nil { return m.error } for i, cred := range m.credentials { if cred.CredentialId == credentialID { // Remove from slice m.credentials = append(m.credentials[:i], m.credentials[i+1:]...) return nil } } return fmt.Errorf("key not found") } func (m *mockDIDClient) QueryDIDDocument(ctx context.Context, did string) (*types.DIDDocument, error) { if m.error != nil { return nil, m.error } return m.didDoc, nil } func (m *mockDIDClient) AuthenticateWithDID(ctx context.Context, did string, assertion []byte) (bool, error) { if m.error != nil { return false, m.error } return true, nil } func (m *mockDIDClient) SignTransaction(ctx context.Context, did string, txBytes []byte, credentialID string) ([]byte, error) { if m.error != nil { return nil, m.error } // Mock signature return []byte("mock_signature"), nil } // Helper function to create test credentials func createTestCredential(id string) *types.WebAuthnCredential { return &types.WebAuthnCredential{ CredentialId: id, RawId: base64.RawURLEncoding.EncodeToString([]byte(id)), ClientDataJson: `{"type":"webauthn.create","challenge":"test","origin":"http://localhost"}`, AttestationObject: base64.RawURLEncoding.EncodeToString([]byte("test_attestation")), PublicKey: []byte("test_public_key"), Algorithm: -7, // ES256 Origin: "http://localhost", CreatedAt: 1234567890, } } func TestWebAuthnClient_CompleteRegistration(t *testing.T) { client := &webAuthnClient{ origin: "http://localhost", rpID: "localhost", } challenge := &RegistrationChallenge{ Challenge: []byte("test_challenge"), User: &User{ ID: []byte("test_user"), Name: "Test User", DisplayName: "Test", }, } // Create valid client data JSON clientData := map[string]any{ "type": "webauthn.create", "challenge": challenge.Challenge, "origin": "http://localhost", } clientDataJSON, _ := json.Marshal(clientData) response := &AuthenticatorAttestationResponse{ ClientDataJSON: clientDataJSON, AttestationObject: []byte("test_attestation"), } // Test successful registration cred, err := client.CompleteRegistration(context.Background(), challenge, response) assert.NoError(t, err) assert.NotNil(t, cred) // Test with invalid client data response.ClientDataJSON = []byte("invalid_json") _, err = client.CompleteRegistration(context.Background(), challenge, response) assert.Error(t, err) } func TestWebAuthnClient_CompleteAuthentication(t *testing.T) { credential := createTestCredential("test_cred_1") client := &webAuthnClient{ origin: "http://localhost", rpID: "localhost", } challenge := &AuthenticationChallenge{ Challenge: []byte("test_challenge"), } // Create valid client data JSON clientData := map[string]any{ "type": "webauthn.get", "challenge": challenge.Challenge, "origin": "http://localhost", } clientDataJSON, _ := json.Marshal(clientData) response := &AuthenticatorAssertionResponse{ ClientDataJSON: clientDataJSON, AuthenticatorData: []byte("test_auth_data"), Signature: []byte("test_signature"), UserHandle: []byte("test_user"), } // Test successful authentication result, err := client.CompleteAuthentication(context.Background(), challenge, response, "test_cred_1") assert.NoError(t, err) assert.NotNil(t, result) assert.True(t, result.Verified) // Test with wrong origin clientData["origin"] = "http://evil.com" clientDataJSON, _ = json.Marshal(clientData) response.ClientDataJSON = base64.RawURLEncoding.EncodeToString(clientDataJSON) _, err = client.CompleteAuthentication(context.Background(), challenge, response, "test_cred_1") assert.Error(t, err) } func TestWebAuthnClient_ListCredentials(t *testing.T) { cred1 := createTestCredential("cred1") cred2 := createTestCredential("cred2") client := &webAuthnClient{ didClient: &mockDIDClient{ credentials: []*types.WebAuthnCredential{cred1, cred2}, }, } // Test successful list creds, err := client.ListCredentials(context.Background(), "did:test:123") require.NoError(t, err) assert.Len(t, creds, 2) assert.Equal(t, "cred1", creds[0].CredentialId) assert.Equal(t, "cred2", creds[1].CredentialId) // Test with error client.didClient = &mockDIDClient{error: assert.AnError} _, err = client.ListCredentials(context.Background(), "did:test:123") assert.Error(t, err) } func TestWebAuthnClient_GetCredential(t *testing.T) { cred := createTestCredential("test_cred") client := &webAuthnClient{ didClient: &mockDIDClient{ credentials: []*types.WebAuthnCredential{cred}, }, } // Test successful get retrieved, err := client.GetCredential(context.Background(), "did:test:123", "test_cred") require.NoError(t, err) assert.Equal(t, cred.CredentialId, retrieved.CredentialId) // Test credential not found _, err = client.GetCredential(context.Background(), "did:test:123", "non_existent") assert.Error(t, err) } func TestWebAuthnClient_UpdateCredential(t *testing.T) { cred := createTestCredential("test_cred") client := &webAuthnClient{ didClient: &mockDIDClient{ credentials: []*types.WebAuthnCredential{cred}, }, } // Update the credential updatedCred := createTestCredential("test_cred") updatedCred.Origin = "https://updated.example.com" err := client.UpdateCredential(context.Background(), "did:test:123", updatedCred) require.NoError(t, err) // Verify update mock := client.didClient.(*mockDIDClient) assert.Equal(t, "https://updated.example.com", mock.credentials[0].Origin) // Test update non-existent credential nonExistent := createTestCredential("non_existent") err = client.UpdateCredential(context.Background(), "did:test:123", nonExistent) assert.Error(t, err) } func TestWebAuthnClient_RevokeCredential(t *testing.T) { cred1 := createTestCredential("cred1") cred2 := createTestCredential("cred2") client := &webAuthnClient{ didClient: &mockDIDClient{ credentials: []*types.WebAuthnCredential{cred1, cred2}, }, } // Revoke first credential err := client.RevokeCredential(context.Background(), "did:test:123", "cred1") require.NoError(t, err) // Verify revocation mock := client.didClient.(*mockDIDClient) assert.Len(t, mock.credentials, 1) assert.Equal(t, "cred2", mock.credentials[0].CredentialId) // Test revoke non-existent credential err = client.RevokeCredential(context.Background(), "did:test:123", "non_existent") assert.Error(t, err) } func TestWebAuthnClient_AuthenticateWithDID(t *testing.T) { client := &webAuthnClient{ origin: "http://localhost", didClient: &mockDIDClient{ didDoc: &types.DIDDocument{ Id: "did:test:123", }, }, } assertion := &AuthenticationAssertion{ CredentialID: "test_cred", ClientDataJSON: base64.RawURLEncoding.EncodeToString([]byte(`{"type":"webauthn.get"}`)), AuthenticatorData: base64.RawURLEncoding.EncodeToString([]byte("auth_data")), Signature: base64.RawURLEncoding.EncodeToString([]byte("signature")), UserHandle: base64.RawURLEncoding.EncodeToString([]byte("user")), } // Test successful authentication result, err := client.AuthenticateWithDID(context.Background(), "did:test:123", assertion) require.NoError(t, err) assert.NotNil(t, result) assert.True(t, result.Success) assert.Equal(t, "test_cred", result.CredentialId) // Test with error client.didClient = &mockDIDClient{error: assert.AnError} _, err = client.AuthenticateWithDID(context.Background(), "did:test:123", assertion) assert.Error(t, err) } func TestWebAuthnClient_SignWithWebAuthn(t *testing.T) { client := &webAuthnClient{ didClient: &mockDIDClient{}, } txData := []byte("transaction_data") // Test successful signing sig, err := client.SignWithWebAuthn(context.Background(), "did:test:123", txData, "test_cred") require.NoError(t, err) assert.Equal(t, []byte("mock_signature"), sig) // Test with error client.didClient = &mockDIDClient{error: assert.AnError} _, err = client.SignWithWebAuthn(context.Background(), "did:test:123", txData, "test_cred") assert.Error(t, err) } func TestWebAuthnClient_BindCredential(t *testing.T) { client := &webAuthnClient{} cred := createTestCredential("test_cred") // Test that BindCredential returns the existing implementation message result, err := client.BindCredential(context.Background(), "did:test:123", cred) require.NoError(t, err) assert.NotNil(t, result) assert.Equal(t, "test_cred", result.CredentialId) // The actual binding logic would be implemented in a later phase // For now, it just returns the credential as-is }