108 lines
4.1 KiB
Go
108 lines
4.1 KiB
Go
package oauth
|
||
|
||
import (
|
||
"errors"
|
||
"strings"
|
||
"testing"
|
||
)
|
||
|
||
// The RFC 7636 appendix B worked example. Using the spec's own vector rather
|
||
// than a value this implementation produced means the test would catch an
|
||
// encoding mistake that is self-consistent — base64 standard instead of
|
||
// base64url, say, or padded instead of raw — which a round-trip test could not.
|
||
const (
|
||
specVerifier = "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"
|
||
specChallenge = "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM"
|
||
)
|
||
|
||
func TestChallengeForMatchesTheRFCVector(t *testing.T) {
|
||
if got := ChallengeFor(specVerifier); got != specChallenge {
|
||
t.Errorf("ChallengeFor(RFC 7636 verifier) = %q, want %q", got, specChallenge)
|
||
}
|
||
}
|
||
|
||
func TestVerifyChallengeAcceptsTheCorrectVerifier(t *testing.T) {
|
||
if err := VerifyChallenge(specVerifier, specChallenge, MethodS256); err != nil {
|
||
t.Errorf("the RFC's own verifier was rejected: %v", err)
|
||
}
|
||
}
|
||
|
||
func TestVerifyChallengeRejectsAWrongVerifier(t *testing.T) {
|
||
// Same length and character set, one character different. A comparison
|
||
// that was accidentally checking length or prefix would let this through.
|
||
wrong := "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXX"
|
||
err := VerifyChallenge(wrong, specChallenge, MethodS256)
|
||
if !errors.Is(err, ErrVerifierMismatch) {
|
||
t.Errorf("err = %v, want ErrVerifierMismatch", err)
|
||
}
|
||
}
|
||
|
||
func TestVerifyChallengeRejectsAMissingVerifier(t *testing.T) {
|
||
if err := VerifyChallenge("", specChallenge, MethodS256); !errors.Is(err, ErrMalformedVerifier) {
|
||
t.Errorf("err = %v, want ErrMalformedVerifier", err)
|
||
}
|
||
}
|
||
|
||
// `plain` must be refused wherever it appears. It is legal in RFC 7636 and
|
||
// forbidden by OAuth 2.1 for public clients, because the challenge IS the
|
||
// verifier and anyone who stole one stole both.
|
||
func TestPlainMethodIsRejected(t *testing.T) {
|
||
for _, method := range []string{"plain", "PLAIN", "", "s256", "S512"} {
|
||
t.Run("method="+method, func(t *testing.T) {
|
||
if err := ValidateChallenge(specChallenge, method); !errors.Is(err, ErrUnsupportedChallengeMethod) {
|
||
t.Errorf("ValidateChallenge: err = %v, want ErrUnsupportedChallengeMethod", err)
|
||
}
|
||
if err := VerifyChallenge(specVerifier, specChallenge, method); !errors.Is(err, ErrUnsupportedChallengeMethod) {
|
||
t.Errorf("VerifyChallenge: err = %v, want ErrUnsupportedChallengeMethod", err)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
func TestMalformedChallengeIsRejected(t *testing.T) {
|
||
for name, challenge := range map[string]string{
|
||
"empty": "",
|
||
"too short": "abc",
|
||
"too long": strings.Repeat("a", 44),
|
||
"padded base64": "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM=",
|
||
"standard base64": "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw+cM",
|
||
"illegal char": "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw!cM",
|
||
"whitespace": "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw cM",
|
||
"newline injected": "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw\ncM",
|
||
} {
|
||
t.Run(name, func(t *testing.T) {
|
||
if err := ValidateChallenge(challenge, MethodS256); !errors.Is(err, ErrMalformedChallenge) {
|
||
t.Errorf("err = %v, want ErrMalformedChallenge", err)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// RFC 7636 section 4.1 constrains the verifier to 43–128 unreserved characters.
|
||
// A verifier outside that range is malformed regardless of what it hashes to.
|
||
func TestMalformedVerifierIsRejected(t *testing.T) {
|
||
for name, verifier := range map[string]string{
|
||
"too short (42)": strings.Repeat("a", 42),
|
||
"too long (129)": strings.Repeat("a", 129),
|
||
"illegal char": strings.Repeat("a", 42) + "!",
|
||
"whitespace": strings.Repeat("a", 42) + " ",
|
||
} {
|
||
t.Run(name, func(t *testing.T) {
|
||
if err := VerifyChallenge(verifier, specChallenge, MethodS256); !errors.Is(err, ErrMalformedVerifier) {
|
||
t.Errorf("err = %v, want ErrMalformedVerifier", err)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// A verifier at each end of the legal range must be accepted, or clients
|
||
// generating the maximum length would fail against this server.
|
||
func TestVerifierBoundariesAreAccepted(t *testing.T) {
|
||
for _, length := range []int{43, 128} {
|
||
verifier := strings.Repeat("a", length)
|
||
if err := VerifyChallenge(verifier, ChallengeFor(verifier), MethodS256); err != nil {
|
||
t.Errorf("a %d-character verifier was rejected: %v", length, err)
|
||
}
|
||
}
|
||
}
|