Initial commit
This commit is contained in:
commit
afed023152
8152 files changed
+2086461
No files matched your search
@@ -0,0 +1,140 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestSignerSealOpen(t *testing.T) {
|
||||
s := NewSigner("test-secret")
|
||||
tx, err := NewLoginTx("/some/path")
|
||||
if err != nil {
|
||||
t.Fatalf("NewLoginTx: %v", err)
|
||||
}
|
||||
|
||||
sealed, err := s.Seal(tx)
|
||||
if err != nil {
|
||||
t.Fatalf("Seal: %v", err)
|
||||
}
|
||||
var opened LoginTx
|
||||
if err := s.Open(sealed, &opened); err != nil {
|
||||
t.Fatalf("Open: %v", err)
|
||||
}
|
||||
// Compare fields: time.Time equality also compares the monotonic clock,
|
||||
// which the JSON roundtrip drops.
|
||||
if opened.State != tx.State || opened.Nonce != tx.Nonce ||
|
||||
opened.Verifier != tx.Verifier || opened.ReturnTo != tx.ReturnTo ||
|
||||
!opened.ExpiresAt.Equal(tx.ExpiresAt) {
|
||||
t.Errorf("opened = %+v, want %+v", opened, tx)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSignerRejectsTampering(t *testing.T) {
|
||||
s := NewSigner("test-secret")
|
||||
sess := Session{Sub: "user-1", ExpiresAt: time.Now().Add(time.Hour)}
|
||||
sealed, err := s.Seal(sess)
|
||||
if err != nil {
|
||||
t.Fatalf("Seal: %v", err)
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
value func(string) string
|
||||
reason string
|
||||
}{
|
||||
{"flipped payload", func(v string) string {
|
||||
return flipLastChar(strings.Split(v, ".")[0]) + "." + strings.Split(v, ".")[1]
|
||||
}, "forged payload"},
|
||||
{"flipped mac", func(v string) string {
|
||||
return strings.Split(v, ".")[0] + "." + flipLastChar(strings.Split(v, ".")[1])
|
||||
}, "forged mac"},
|
||||
{"wrong key", func(v string) string {
|
||||
other, _ := NewSigner("other-secret").Seal(sess)
|
||||
return other
|
||||
}, "signed with a different key"},
|
||||
{"garbage", func(string) string { return "not-a-sealed-value" }, "garbage"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var out Session
|
||||
if err := s.Open(tt.value(sealed), &out); err == nil {
|
||||
t.Errorf("Open accepted %s", tt.reason)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSessionAndTxCookies(t *testing.T) {
|
||||
s := NewSigner("test-secret")
|
||||
|
||||
sessCookie, err := s.NewSessionCookie(Session{Sub: "u1"}, 2*time.Hour, true)
|
||||
if err != nil {
|
||||
t.Fatalf("NewSessionCookie: %v", err)
|
||||
}
|
||||
if sessCookie.Name != CookieSession || sessCookie.Path != "/" {
|
||||
t.Errorf("session cookie = %+v", sessCookie)
|
||||
}
|
||||
if !sessCookie.HttpOnly || !sessCookie.Secure || sessCookie.SameSite != http.SameSiteLaxMode {
|
||||
t.Errorf("session cookie attrs = %+v", sessCookie)
|
||||
}
|
||||
var sess Session
|
||||
if err := s.Open(sessCookie.Value, &sess); err != nil {
|
||||
t.Fatalf("Open session: %v", err)
|
||||
}
|
||||
if !sess.Valid() {
|
||||
t.Error("session with future ExpiresAt should be valid")
|
||||
}
|
||||
sess.ExpiresAt = time.Now().Add(-time.Minute)
|
||||
if sess.Valid() {
|
||||
t.Error("expired session should be invalid")
|
||||
}
|
||||
|
||||
tx, err := NewLoginTx("/")
|
||||
if err != nil {
|
||||
t.Fatalf("NewLoginTx: %v", err)
|
||||
}
|
||||
if !tx.Valid() {
|
||||
t.Error("fresh login tx should be valid")
|
||||
}
|
||||
tx.ExpiresAt = time.Now().Add(-time.Second)
|
||||
if tx.Valid() {
|
||||
t.Error("expired login tx should be invalid")
|
||||
}
|
||||
|
||||
tx, _ = NewLoginTx("/")
|
||||
txCookie, err := s.NewLoginTxCookie(tx, false)
|
||||
if err != nil {
|
||||
t.Fatalf("NewLoginTxCookie: %v", err)
|
||||
}
|
||||
if txCookie.Name != CookieLoginTx || txCookie.Path != "/api/auth" {
|
||||
t.Errorf("tx cookie = %+v", txCookie)
|
||||
}
|
||||
if !txCookie.HttpOnly || txCookie.Secure {
|
||||
t.Errorf("tx cookie attrs = %+v", txCookie)
|
||||
}
|
||||
|
||||
expired := Expired(CookieSession, true)
|
||||
if expired.Name != CookieSession || expired.MaxAge != -1 {
|
||||
t.Errorf("Expired cookie = %+v", expired)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewLoginTxUsesDistinctSecrets(t *testing.T) {
|
||||
a, _ := NewLoginTx("/")
|
||||
b, _ := NewLoginTx("/")
|
||||
if a.State == b.State || a.Nonce == b.Nonce || a.Verifier == b.Verifier {
|
||||
t.Error("state/nonce/verifier must be fresh per login")
|
||||
}
|
||||
}
|
||||
|
||||
// flipLastChar swaps the last character of a base64url value so any
|
||||
// signature or payload check must fail.
|
||||
func flipLastChar(v string) string {
|
||||
last := "A"
|
||||
if strings.HasSuffix(v, last) {
|
||||
last = "B"
|
||||
}
|
||||
return v[:len(v)-1] + last
|
||||
}
|
||||
Reference in new issue
Block a user