Files
silo-server/internal/auth/oauth_state_test.go

88 lines
2.3 KiB
Go

package auth
import (
"strings"
"testing"
"time"
)
var testStateSecret = []byte("test-secret-do-not-use-in-prod")
func TestSignAndVerifyState_Roundtrip(t *testing.T) {
now := time.Now().UTC().Truncate(time.Second)
in := StatePayload{
Nonce: "nonce-1",
InstallID: "inst-1",
ExpiresAt: now.Add(10 * time.Minute),
}
signed := SignState(testStateSecret, in)
if !strings.Contains(signed, ".") {
t.Errorf("signed state should contain '.' separator: %q", signed)
}
out, err := VerifyState(testStateSecret, signed)
if err != nil {
t.Fatalf("VerifyState: %v", err)
}
if out.Nonce != in.Nonce || out.InstallID != in.InstallID {
t.Errorf("payload mismatch: got %+v want %+v", out, in)
}
if !out.ExpiresAt.Equal(in.ExpiresAt) {
t.Errorf("ExpiresAt = %v, want %v", out.ExpiresAt, in.ExpiresAt)
}
}
func TestVerifyState_RejectsTampered(t *testing.T) {
in := StatePayload{Nonce: "n", InstallID: "i", ExpiresAt: time.Now().Add(time.Hour)}
signed := SignState(testStateSecret, in)
parts := strings.SplitN(signed, ".", 2)
if len(parts) != 2 {
t.Fatalf("malformed signed: %q", signed)
}
// Flip one character in the payload portion.
flipped := flipFirstChar(parts[0])
tampered := flipped + "." + parts[1]
if _, err := VerifyState(testStateSecret, tampered); err == nil {
t.Error("VerifyState should reject tampered payload")
}
}
func TestVerifyState_RejectsWrongSecret(t *testing.T) {
in := StatePayload{Nonce: "n", InstallID: "i", ExpiresAt: time.Now().Add(time.Hour)}
signed := SignState(testStateSecret, in)
if _, err := VerifyState([]byte("different-secret"), signed); err == nil {
t.Error("VerifyState should reject wrong secret")
}
}
func TestVerifyState_RejectsExpired(t *testing.T) {
in := StatePayload{Nonce: "n", InstallID: "i", ExpiresAt: time.Now().Add(-time.Minute)}
signed := SignState(testStateSecret, in)
if _, err := VerifyState(testStateSecret, signed); err == nil {
t.Error("VerifyState should reject expired state")
}
}
func TestVerifyState_RejectsMalformed(t *testing.T) {
cases := []string{"", "nodot", "....", "x.y.z"}
for _, c := range cases {
if _, err := VerifyState(testStateSecret, c); err == nil {
t.Errorf("VerifyState should reject %q", c)
}
}
}
func flipFirstChar(s string) string {
if s == "" {
return s
}
b := []byte(s)
if b[0] == 'A' {
b[0] = 'B'
} else {
b[0] = 'A'
}
return string(b)
}