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) } } }