feat(auth): complete sso subsystem integration

This commit is contained in:
mac
2026-07-16 15:52:56 +08:00
parent f8c389606f
commit eb2621d914
24 changed files with 1977 additions and 111 deletions
+123
View File
@@ -0,0 +1,123 @@
package wayne
import (
"crypto/hmac"
"crypto/rand"
"crypto/sha256"
"crypto/subtle"
"encoding/hex"
"errors"
"fmt"
"net/http"
"strconv"
"strings"
"time"
)
const (
HeaderService = "X-Wayne-Service"
HeaderTimestamp = "X-Wayne-Timestamp"
HeaderNonce = "X-Wayne-Nonce"
HeaderSignature = "X-Wayne-Signature"
SignaturePrefix = "sha256="
)
var (
ErrMissingSecret = errors.New("wayne service api secret key is empty")
ErrMissingHeader = errors.New("wayne service header value is empty")
)
type SignedHeaders struct {
Service string
Timestamp string
Nonce string
Signature string
}
func BodySHA256Hex(body []byte) string {
sum := sha256.Sum256(body)
return hex.EncodeToString(sum[:])
}
func SigningPayload(method, uri, timestamp, nonce string, body []byte) string {
return strings.Join([]string{
strings.ToUpper(strings.TrimSpace(method)),
uri,
timestamp,
nonce,
BodySHA256Hex(body),
}, "\n")
}
func Sign(secret, method, uri, timestamp, nonce string, body []byte) (string, error) {
secret = secretValue(secret)
if secret == "" {
return "", ErrMissingSecret
}
payload := SigningPayload(method, uri, timestamp, nonce, body)
mac := hmac.New(sha256.New, []byte(secret))
_, _ = mac.Write([]byte(payload))
return SignaturePrefix + hex.EncodeToString(mac.Sum(nil)), nil
}
func Verify(secret, signature, method, uri, timestamp, nonce string, body []byte) bool {
expected, err := Sign(secret, method, uri, timestamp, nonce, body)
if err != nil {
return false
}
return subtle.ConstantTimeCompare([]byte(normalizeSignature(signature)), []byte(expected)) == 1
}
func BuildSignedHeaders(service, secret, method, uri string, body []byte, now time.Time) (SignedHeaders, error) {
service = strings.TrimSpace(service)
if service == "" {
return SignedHeaders{}, fmt.Errorf("%w: service", ErrMissingHeader)
}
timestamp := strconv.FormatInt(now.Unix(), 10)
nonce, err := NewNonce()
if err != nil {
return SignedHeaders{}, err
}
signature, err := Sign(secret, method, uri, timestamp, nonce, body)
if err != nil {
return SignedHeaders{}, err
}
return SignedHeaders{
Service: service,
Timestamp: timestamp,
Nonce: nonce,
Signature: signature,
}, nil
}
func (h SignedHeaders) Apply(req *http.Request) {
req.Header.Set(HeaderService, h.Service)
req.Header.Set(HeaderTimestamp, h.Timestamp)
req.Header.Set(HeaderNonce, h.Nonce)
req.Header.Set(HeaderSignature, h.Signature)
}
func NewNonce() (string, error) {
buf := make([]byte, 16)
if _, err := rand.Read(buf); err != nil {
return "", err
}
return hex.EncodeToString(buf), nil
}
func normalizeSignature(signature string) string {
signature = strings.TrimSpace(signature)
if strings.HasPrefix(signature, SignaturePrefix) {
return signature
}
return SignaturePrefix + signature
}
func secretValue(secret string) string {
secret = strings.TrimSpace(secret)
if _, value, ok := strings.Cut(secret, ":"); ok {
return strings.TrimSpace(value)
}
return secret
}