mirror of
https://github.com/sonr-io/sonr.git
synced 2026-08-04 10:21:40 +00:00
@@ -0,0 +1,338 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user