Files
common/README.md

603 lines
15 KiB
Markdown
Raw Normal View History

# Sonr Common Package
[![Go Version](https://img.shields.io/badge/go-1.25-blue.svg)](https://golang.org)
[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
The `common` package provides high-level, simplified interfaces for IPFS and WebAuthn functionality, designed for easy integration into external libraries and applications within the Sonr network.
## Features
- **IPFS Integration**: Simple, high-level API for storing and retrieving data from IPFS
- **WebAuthn Support**: Comprehensive WebAuthn credential management and verification
- **Helper Functions**: Convenient wrapper functions that abstract complexity
- **Well-Tested**: Comprehensive test coverage with unit and integration tests
- **Production-Ready**: Battle-tested implementations with proper error handling
## Table of Contents
- [Installation](#installation)
- [Quick Start](#quick-start)
- [IPFS Module](#ipfs-module)
- [WebAuthn Module](#webauthn-module)
- [Helper Functions](#helper-functions)
- [Testing](#testing)
- [Examples](#examples)
- [Contributing](#contributing)
## Installation
```bash
go get github.com/sonr-io/common
```
## Quick Start
### IPFS Example
```go
package main
import (
"fmt"
"github.com/sonr-io/common"
)
func main() {
// Check if IPFS daemon is running
if !common.IsIPFSDaemonRunning() {
panic("IPFS daemon is not running")
}
// Store data
data := []byte("Hello, IPFS!")
cid, err := common.StoreData(data)
if err != nil {
panic(err)
}
fmt.Printf("Stored data with CID: %s\n", cid)
// Retrieve data
retrieved, err := common.RetrieveData(cid)
if err != nil {
panic(err)
}
fmt.Printf("Retrieved: %s\n", string(retrieved))
}
```
### WebAuthn Example
```go
package main
import (
"fmt"
"github.com/sonr-io/common"
)
func main() {
// Generate a challenge for WebAuthn ceremony
challenge, err := common.NewChallenge()
if err != nil {
panic(err)
}
fmt.Printf("Challenge: %s\n", challenge)
// Verify origin
origin := "https://example.com"
allowedOrigins := []string{"https://example.com", "https://app.example.com"}
if err := common.VerifyOrigin(origin, allowedOrigins); err != nil {
panic(err)
}
fmt.Println("Origin verified successfully!")
}
```
## IPFS Module
The IPFS module (`ipfs/`) provides a high-level interface for interacting with IPFS nodes.
### Core Functions
#### Client Management
```go
// Create a new IPFS client
client, err := common.NewIPFSClient()
// Or use the panic version for critical scenarios
client := common.MustGetIPFSClient()
// Check if daemon is running
if common.IsIPFSDaemonRunning() {
// Daemon is available
}
```
#### Data Storage
```go
// Store raw bytes
cid, err := common.StoreData([]byte("data"))
// Store a file with metadata
cid, err := common.StoreFile("document.txt", fileData)
// Store multiple files as a folder
files := map[string][]byte{
"file1.txt": []byte("content 1"),
"file2.txt": []byte("content 2"),
}
cid, err := common.StoreFolder(files)
```
#### Data Retrieval
```go
// Retrieve data by CID
data, err := common.RetrieveData("QmXxx...")
// Using client directly for advanced operations
client, _ := common.NewIPFSClient()
exists, err := client.Exists("QmXxx...")
entries, err := client.Ls("QmXxx...")
```
### Advanced IPFS Usage
```go
import "github.com/sonr-io/common/ipfs"
client, _ := ipfs.GetClient()
// Pin content
err := client.Pin("QmXxx...", "my-important-data")
// Unpin content
err := client.Unpin("QmXxx...")
// Check if pinned
pinned, err := client.IsPinned("ipns-name")
// Get node status
status, err := client.NodeStatus()
fmt.Printf("Peer ID: %s\n", status.PeerID)
fmt.Printf("Connected Peers: %d\n", status.ConnectedPeers)
```
## WebAuthn Module
The WebAuthn module (`webauthn/`) provides comprehensive support for WebAuthn authentication.
### Challenge Generation
```go
// Generate a cryptographic challenge (32 bytes, base64url encoded)
challenge, err := common.NewChallenge()
// Get the standard challenge length
length := common.ChallengeLength() // Returns 32
```
### Origin Verification
```go
// Verify origin against allowed list
allowedOrigins := []string{
"https://example.com",
"https://app.example.com:8080",
}
err := common.VerifyOrigin("https://example.com", allowedOrigins)
```
### Base64 URL Encoding
```go
// Encode data to URL-safe base64 (no padding)
encoded := common.EncodeBase64URL([]byte("data"))
// Decode URL-safe base64
decoded, err := common.DecodeBase64URL(encoded)
```
### Credential Management
#### Credential Creation (Registration)
```go
// Unmarshal credential creation response from client
credData := []byte(`{"id":"...","type":"public-key",...}`)
credResponse, err := common.UnmarshalCredentialCreation(credData)
// Parse and validate credential creation
parsedCred, err := common.ParseCredentialCreation(credData)
// Marshal credential for storage
jsonData, err := common.MarshalCredentialCreation(credResponse)
```
#### Credential Assertion (Authentication)
```go
// Unmarshal assertion response from client
assertionData := []byte(`{"id":"...","type":"public-key",...}`)
assertionResponse, err := common.UnmarshalCredentialAssertion(assertionData)
// Parse and validate assertion
parsedAssertion, err := common.ParseCredentialAssertion(assertionData)
// Marshal assertion for storage
jsonData, err := common.MarshalCredentialAssertion(assertionResponse)
```
### Advanced WebAuthn Usage
```go
import "github.com/sonr-io/common/webauthn"
// Create registration options
options := &webauthn.PublicKeyCredentialCreationOptions{
RelyingParty: webauthn.RelyingPartyEntity{
Name: "Example Corp",
ID: "example.com",
},
User: webauthn.UserEntity{
ID: []byte("user-id"),
Name: "user@example.com",
DisplayName: "User Name",
},
Challenge: challenge,
Parameters: []webauthn.CredentialParameter{
{Type: webauthn.PublicKeyCredentialType, Algorithm: -7}, // ES256
},
}
// Verify credential creation
clientDataHash, err := parsedCred.Verify(
storedChallenge,
verifyUser,
verifyUserPresence,
relyingPartyID,
rpOrigins,
rpTopOrigins,
rpTopOriginsVerify,
metadataProvider,
credParams,
)
// Verify credential assertion
err = parsedAssertion.Verify(
storedChallenge,
relyingPartyID,
rpOrigins,
rpTopOrigins,
rpTopOriginsVerify,
appID,
verifyUser,
verifyUserPresence,
credentialPublicKey,
)
```
## Helper Functions
### IPFS Helpers
| Function | Description |
|----------|-------------|
| `NewIPFSClient()` | Create IPFS client with detailed error messages |
| `MustGetIPFSClient()` | Panic version for critical initialization |
| `StoreData(data)` | Store raw bytes and return CID |
| `RetrieveData(cid)` | Retrieve content by CID |
| `IsIPFSDaemonRunning()` | Check if IPFS daemon is accessible |
| `StoreFile(name, data)` | Store file with metadata |
| `StoreFolder(files)` | Store multiple files as a folder |
### WebAuthn Helpers
| Function | Description |
|----------|-------------|
| `NewChallenge()` | Generate 32-byte cryptographic challenge |
| `ChallengeLength()` | Get standard challenge length (32) |
| `VerifyOrigin(origin, allowed)` | Verify origin against allowed list |
| `EncodeBase64URL(data)` | Encode to URL-safe base64 |
| `DecodeBase64URL(encoded)` | Decode URL-safe base64 |
| `UnmarshalCredentialCreation(data)` | Parse credential creation response |
| `MarshalCredentialCreation(ccr)` | Serialize credential creation |
| `ParseCredentialCreation(data)` | Parse and validate creation response |
| `UnmarshalCredentialAssertion(data)` | Parse assertion response |
| `MarshalCredentialAssertion(car)` | Serialize assertion response |
| `ParseCredentialAssertion(data)` | Parse and validate assertion |
## Testing
The package includes a comprehensive Makefile for testing and development tasks.
### Available Make Targets
```bash
# Run all tests
make test
# Run tests for specific modules
make test-ipfs
make test-webauthn
# Generate coverage reports
make coverage # Show coverage summary
make coverage-ipfs # IPFS module coverage
make coverage-webauthn # WebAuthn module coverage
make coverage-all # HTML coverage report
# Code quality
make fmt # Format code
make vet # Run go vet
make lint # Run golangci-lint
# Performance
make bench # Run benchmarks
make test-race # Run tests with race detector
# Maintenance
make clean # Clean test cache and coverage files
make deps # Install dependencies
# Quick check
make check # Run fmt, vet, and test
```
### Running Tests Manually
```bash
# Run all tests
go test ./...
# Run tests with coverage
go test -cover ./...
# Run specific tests
go test -v -run TestNewChallenge
# Run benchmarks
go test -bench=. -benchmem ./...
```
### Test Coverage
Current test coverage:
- **Common package**: 49.4%
- **WebAuthn module**: Comprehensive (50+ tests)
- **IPFS module**: Core functionality tested
## Examples
### Complete IPFS Example
```go
package main
import (
"fmt"
"log"
"github.com/sonr-io/common"
"github.com/sonr-io/common/ipfs"
)
func main() {
// Check daemon availability
if !common.IsIPFSDaemonRunning() {
log.Fatal("IPFS daemon is not running. Start it with: ipfs daemon")
}
// Get client for advanced operations
client, err := common.NewIPFSClient()
if err != nil {
log.Fatal(err)
}
// Store a file
fileData := []byte("This is my important document")
cid, err := common.StoreFile("document.txt", fileData)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Stored file with CID: %s\n", cid)
// Pin the content
if err := client.Pin(cid, "important-document"); err != nil {
log.Fatal(err)
}
fmt.Println("Content pinned successfully")
// Check if content exists
exists, err := client.Exists(cid)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Content exists: %v\n", exists)
// Get node status
status, err := client.NodeStatus()
if err != nil {
log.Fatal(err)
}
fmt.Printf("Node Status:\n")
fmt.Printf(" Peer ID: %s\n", status.PeerID)
fmt.Printf(" Connected Peers: %d\n", status.ConnectedPeers)
fmt.Printf(" Version: %s\n", status.Version)
// Store multiple files as a folder
folder := map[string][]byte{
"readme.md": []byte("# Project\nThis is a test project"),
"main.go": []byte("package main\n\nfunc main() {}"),
"config.yml": []byte("port: 8080\nhost: localhost"),
}
folderCID, err := common.StoreFolder(folder)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Stored folder with CID: %s\n", folderCID)
// List folder contents
entries, err := client.Ls(folderCID)
if err != nil {
log.Fatal(err)
}
fmt.Println("Folder contents:")
for _, entry := range entries {
fmt.Printf(" - %s\n", entry)
}
}
```
### Complete WebAuthn Example
```go
package main
import (
"fmt"
"log"
"github.com/sonr-io/common"
"github.com/sonr-io/common/webauthn"
)
func main() {
// Generate challenge for registration
challenge, err := common.NewChallenge()
if err != nil {
log.Fatal(err)
}
fmt.Printf("Registration Challenge: %s\n", challenge)
// Verify request origin
origin := "https://example.com"
allowedOrigins := []string{
"https://example.com",
"https://app.example.com",
}
if err := common.VerifyOrigin(origin, allowedOrigins); err != nil {
log.Fatalf("Origin verification failed: %v", err)
}
fmt.Println("Origin verified!")
// Simulate receiving credential creation response from client
// In a real scenario, this would come from the browser
credentialJSON := []byte(`{
"id": "base64-credential-id",
"type": "public-key",
"rawId": "base64-raw-id",
"response": {
"clientDataJSON": "base64-client-data",
"attestationObject": "base64-attestation"
}
}`)
// Parse credential creation response
credResponse, err := common.UnmarshalCredentialCreation(credentialJSON)
if err != nil {
log.Printf("Parse error (expected in demo): %v", err)
}
// For authentication, generate a new challenge
authChallenge, err := common.NewChallenge()
if err != nil {
log.Fatal(err)
}
fmt.Printf("Authentication Challenge: %s\n", authChallenge)
// Encode/decode example
secretData := []byte("secret-session-data")
encoded := common.EncodeBase64URL(secretData)
fmt.Printf("Encoded: %s\n", encoded)
decoded, err := common.DecodeBase64URL(encoded)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Decoded: %s\n", string(decoded))
// Challenge length validation
expectedLength := common.ChallengeLength()
fmt.Printf("Expected challenge length: %d bytes\n", expectedLength)
}
```
## Module Structure
```
common/
 common.go # Helper functions and simplified API
 common_test.go # Comprehensive test suite
 Makefile # Testing and development tasks
 README.md # This file
 go.mod # Go module definition
 ipfs/ # IPFS module
  client.go # IPFS client implementation
  file.go # File operations
  folder.go # Folder operations
 webauthn/ # WebAuthn module
 client.go # WebAuthn client operations
 credential.go # Credential types
 assertion.go # Authentication assertions
 attestation.go # Registration attestation
 challenge.go # Challenge generation
 base64.go # Base64 URL encoding
 decoder.go # JSON decoding utilities
 ... # Additional WebAuthn components
```
## Requirements
- Go 1.25 or higher
- IPFS daemon running (for IPFS operations)
- Dependencies managed via `go.mod`
## Contributing
We welcome contributions! Please follow these guidelines:
1. **Fork** the repository
2. **Create** a feature branch (`git checkout -b feature/amazing-feature`)
3. **Test** your changes (`make test`)
4. **Format** your code (`make fmt`)
5. **Commit** your changes (`git commit -m 'Add amazing feature'`)
6. **Push** to the branch (`git push origin feature/amazing-feature`)
7. **Open** a Pull Request
### Development Workflow
```bash
# Install dependencies
make deps
# Run tests during development
make test
# Check code quality
make check
# Generate coverage report
make coverage-all
# Run benchmarks
make bench
```
## License
This project is part of the Sonr network. See the LICENSE file for details.
## Support
For issues, questions, or contributions, please visit:
- GitHub: https://github.com/sonr-io/common
- Documentation: https://docs.sonr.io
## Acknowledgments
- Built on [Kubo](https://github.com/ipfs/kubo) for IPFS functionality
- WebAuthn implementation based on [W3C WebAuthn specification](https://www.w3.org/TR/webauthn/)