4df87cbf9a
- 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
59 lines
1.5 KiB
Go
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)
|
|
}
|