Files
markdown-hub/internal/auth/totp.go
T
anders 4df87cbf9a Phase 2-6: Git sync, sharing, 2FA, AI integration
- Git: init, commit, log, diff, restore, remotes, push/pull
- Auto-commit on every file save
- Sharing: share/unshare files with other users (ro/rw)
- Shared documents view in sidebar
- 2FA: TOTP setup/verify/disable, enforced at login
- AI: verify spec endpoint (LiteLLM), generate (summarize/prompt/expand)
- Light/dark theme with CSS variables
- File delete (recursive for folders)
- Admin panel + preferences panel
- File creation timestamp display
2026-05-22 19:53:24 +02:00

59 lines
1.5 KiB
Go

package auth
import (
"crypto/hmac"
"crypto/sha1"
"encoding/base32"
"encoding/binary"
"fmt"
"math/rand"
"strings"
"time"
)
// GenerateTOTPSecret creates a random base32-encoded secret.
func GenerateTOTPSecret() string {
b := make([]byte, 20)
for i := range b {
b[i] = byte(rand.Intn(256))
}
return base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(b)
}
// ValidateTOTP checks if the provided code matches the secret for the current time window.
func ValidateTOTP(secret, code string) bool {
secret = strings.ToUpper(strings.TrimSpace(secret))
key, err := base32.StdEncoding.WithPadding(base32.NoPadding).DecodeString(secret)
if err != nil {
return false
}
now := time.Now().Unix() / 30
// Check current window and ±1 for clock drift
for _, offset := range []int64{-1, 0, 1} {
if generateCode(key, now+offset) == code {
return true
}
}
return false
}
// TOTPUri generates an otpauth:// URI for QR code generation.
func TOTPUri(secret, email, issuer string) string {
return fmt.Sprintf("otpauth://totp/%s:%s?secret=%s&issuer=%s&algorithm=SHA1&digits=6&period=30",
issuer, email, secret, issuer)
}
func generateCode(key []byte, counter int64) string {
buf := make([]byte, 8)
binary.BigEndian.PutUint64(buf, uint64(counter))
mac := hmac.New(sha1.New, key)
mac.Write(buf)
hash := mac.Sum(nil)
offset := hash[len(hash)-1] & 0x0f
code := binary.BigEndian.Uint32(hash[offset:offset+4]) & 0x7fffffff
return fmt.Sprintf("%06d", code%1000000)
}