88 lines
2.3 KiB
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)
|
|
}
|