Files
Maven/cmd/mavweb/credentials.go
T
kami 44807b612c webauthn: persist credentials to JSON file instead of in-memory map
- New credentialStore type in credentials.go loads/saves
  map[id]localCred to a JSON file. Thread-safe with sync.RWMutex,
  writes to disk on every mutation.
- PasskeyHandle replaces sync.RWMutex+map with *credentialStore.
  Inline save/lookip/update closures delegate to store methods.
- newPasskeyHandle now takes a storePath parameter and returns an
  error; callers updated.
- New -passkey-file flag (default ./passkeys.json) configures the
  credential store path in main.go.
- Tests use os.CreateTemp in t.TempDir() so each test gets an
  isolated, auto-cleaned store file.
2026-07-05 02:09:56 +04:00

78 lines
1.7 KiB
Go

package main
import (
"encoding/json"
"fmt"
"os"
"sync"
)
type credentialStore struct {
mu sync.RWMutex
path string
creds map[string]localCred
}
func newCredentialStore(path string) (*credentialStore, error) {
cs := &credentialStore{
path: path,
creds: make(map[string]localCred),
}
data, err := os.ReadFile(path)
if err != nil {
if os.IsNotExist(err) {
return cs, nil
}
return nil, fmt.Errorf("read %s: %w", path, err)
}
if len(data) > 0 {
if err := json.Unmarshal(data, &cs.creds); err != nil {
return nil, fmt.Errorf("parse %s: %w", path, err)
}
}
return cs, nil
}
func (cs *credentialStore) persist() error {
data, err := json.MarshalIndent(cs.creds, "", " ")
if err != nil {
return fmt.Errorf("marshal: %w", err)
}
if err := os.WriteFile(cs.path, data, 0600); err != nil {
return fmt.Errorf("write %s: %w", cs.path, err)
}
return nil
}
func (cs *credentialStore) Save(id string, publicKey []byte) error {
cs.mu.Lock()
defer cs.mu.Unlock()
if _, exists := cs.creds[id]; exists {
return fmt.Errorf("credential already exists")
}
cs.creds[id] = localCred{PublicKey: publicKey}
return cs.persist()
}
func (cs *credentialStore) Lookup(id string) (publicKey []byte, signCount int64, err error) {
cs.mu.RLock()
defer cs.mu.RUnlock()
cred, ok := cs.creds[id]
if !ok {
return nil, 0, fmt.Errorf("credential not found")
}
return cred.PublicKey, cred.SignCount, nil
}
func (cs *credentialStore) UpdateSignCount(id string, count int64) error {
cs.mu.Lock()
defer cs.mu.Unlock()
cred, ok := cs.creds[id]
if !ok {
return fmt.Errorf("credential not found")
}
cred.SignCount = count
cs.creds[id] = cred
return cs.persist()
}