ultisuite-backend/internal/api/office/jwt.go
2026-06-04 00:12:11 +02:00

76 lines
1.8 KiB
Go

package office
import (
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"encoding/json"
"fmt"
)
func signJWT(payload any, secret string) (string, error) {
if secret == "" {
return "", nil
}
header := base64.RawURLEncoding.EncodeToString([]byte(`{"alg":"HS256","typ":"JWT"}`))
bodyBytes, err := json.Marshal(payload)
if err != nil {
return "", err
}
body := base64.RawURLEncoding.EncodeToString(bodyBytes)
mac := hmac.New(sha256.New, []byte(secret))
_, _ = mac.Write([]byte(header + "." + body))
sig := base64.RawURLEncoding.EncodeToString(mac.Sum(nil))
return header + "." + body + "." + sig, nil
}
func wrapConfig(config map[string]any, secret string) (map[string]any, error) {
if secret == "" {
return config, nil
}
token, err := signJWT(config, secret)
if err != nil {
return nil, err
}
config["token"] = token
return config, nil
}
func verifyJWT(token, secret string) (map[string]any, error) {
if secret == "" || token == "" {
return nil, fmt.Errorf("missing token or secret")
}
parts := splitJWT(token)
if len(parts) != 3 {
return nil, fmt.Errorf("invalid token")
}
mac := hmac.New(sha256.New, []byte(secret))
_, _ = mac.Write([]byte(parts[0] + "." + parts[1]))
expected := base64.RawURLEncoding.EncodeToString(mac.Sum(nil))
if !hmac.Equal([]byte(expected), []byte(parts[2])) {
return nil, fmt.Errorf("invalid signature")
}
raw, err := base64.RawURLEncoding.DecodeString(parts[1])
if err != nil {
return nil, err
}
var payload map[string]any
if err := json.Unmarshal(raw, &payload); err != nil {
return nil, err
}
return payload, nil
}
func splitJWT(token string) []string {
var parts []string
start := 0
for i := 0; i < len(token); i++ {
if token[i] == '.' {
parts = append(parts, token[start:i])
start = i + 1
}
}
parts = append(parts, token[start:])
return parts
}