Repository: lestrrat-go/jwx
Pull request #641 (2022-03-11 01:19:45)

Enable validate verify by default
jwt.Parse no longer allows unverified and/or unvalidated tokens to be obtained by the caller by default. Allowing this had significant security problems.

Now you need to explicitly ask for it to be disabked to get the original behavior



codecov-commenter: # [Codecov](https://codecov.io/gh/lestrrat-go/jwx/pull/641?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lestrrat-go) Report
> Merging [#641](https://codecov.io/gh/lestrrat-go/jwx/pull/641?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lestrrat-go) (2ac9cf4) into [v2](https://codecov.io/gh/lestrrat-go/jwx/commit/4fe758d73c4bda77fa366c8b73b4d43eccc70060?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lestrrat-go) (4fe758d) will **decrease** coverage by `0.01%`.
> The diff coverage is `66.66%`.

[![Impacted file tree graph](https://codecov.io/gh/lestrrat-go/jwx/pull/641/graphs/tree.svg?width=650&height=150&src=pr&token=kU2RSDosPt&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lestrrat-go)](https://codecov.io/gh/lestrrat-go/jwx/pull/641?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lestrrat-go)

```diff
@@            Coverage Diff             @@
##               v2     #641      +/-   ##
==========================================
- Coverage   72.18%   72.17%   -0.02%     
==========================================
  Files          88       88              
  Lines        9803     9817      +14     
==========================================
+ Hits         7076     7085       +9     
- Misses       1922     1926       +4     
- Partials      805      806       +1     
```


| [Impacted Files](https://codecov.io/gh/lestrrat-go/jwx/pull/641?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lestrrat-go) | Coverage Δ | |
|---|---|---|
| [jwt/jwt.go](https://codecov.io/gh/lestrrat-go/jwx/pull/641/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lestrrat-go#diff-and0L2p3dC5nbw==) | `74.12% <61.53%> (-1.45%)` | :arrow_down: |
| [jwt/options\_gen.go](https://codecov.io/gh/lestrrat-go/jwx/pull/641/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lestrrat-go#diff-and0L29wdGlvbnNfZ2VuLmdv) | `60.00% <100.00%> (+2.10%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/lestrrat-go/jwx/pull/641?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lestrrat-go).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lestrrat-go)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/lestrrat-go/jwx/pull/641?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lestrrat-go). Last update [4bc69fe...2ac9cf4](https://codecov.io/gh/lestrrat-go/jwx/pull/641?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lestrrat-go). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lestrrat-go).


Patch:
diff --git a/Changes.v2 b/Changes.v2
index f8cc881c..7ba2a8d7 100644
--- a/Changes.v2
+++ b/Changes.v2
@@ -184,6 +184,13 @@ Changes
 
   # JWT
 
+  * [jwt] `jwt.Parse` now verifies the signature and validates the token
+    by default. You must disable it explicitly using `jwt.WithValidate(false)`
+    and/or `jwt.WithVerify(false)` if you only want to parse the JWT message.
+
+    If you don't want either, a convenience function `jwt.ParseInsecure`
+    has been added.
+
   * [jwt] `jwt.Parse` can only parse raw JWT (JSON) or JWS (JSON or Compact).
     It no longer accepts JWE messages.
 
diff --git a/jwt/jwt.go b/jwt/jwt.go
index ffe45c8b..af316fb9 100644
--- a/jwt/jwt.go
+++ b/jwt/jwt.go
@@ -73,6 +73,24 @@ func Parse(s []byte, options ...ParseOption) (Token, error) {
 	return parseBytes(s, options...)
 }
 
+// ParseInsecure is exactly the same as Parse(), but it disables
+// signature verification and token validation.
+//
+// You cannot override `jwt.WithVerify()` or `jwt.WithValidate()`
+// using this function. Providing these options would result in
+// an error
+func ParseInsecure(s []byte, options ...ParseOption) (Token, error) {
+	for _, option := range options {
+		switch option.Ident() {
+		case identVerify{}, identValidate{}:
+			return nil, fmt.Errorf(`jwt.ParseInsecure: jwt.WithVerify() and jwt.WithValidate() may not be specified`)
+		}
+	}
+
+	options = append(options, WithVerify(false), WithValidate(false))
+	return Parse(s, options...)
+}
+
 // ParseReader calls Parse against an io.Reader
 func ParseReader(src io.Reader, options ...ParseOption) (Token, error) {
 	// We're going to need the raw bytes regardless. Read it.
@@ -95,6 +113,16 @@ type parseCtx struct {
 
 func parseBytes(data []byte, options ...ParseOption) (Token, error) {
 	var ctx parseCtx
+
+	// Validation is turned on by default. You need to specify
+	// jwt.WithValidate(false) if you want to disable it
+	ctx.validate = true
+
+	// Verification is required (i.e., it is assumed that the incoming
+	// data is in JWS format) unless the user explicitly asks for
+	// it to be skipped.
+	verification := true
+
 	var verifyOpts []Option
 	for _, o := range options {
 		if v, ok := o.(ValidateOption); ok {
@@ -116,6 +144,8 @@ func parseBytes(data []byte, options ...ParseOption) (Token, error) {
 			ctx.pedantic = o.Value().(bool)
 		case identValidate{}:
 			ctx.validate = o.Value().(bool)
+		case identVerify{}:
+			verification = o.Value().(bool)
 		case identTypedClaim{}:
 			pair := o.Value().(claimPair)
 			if ctx.localReg == nil {
@@ -125,7 +155,12 @@ func parseBytes(data []byte, options ...ParseOption) (Token, error) {
 		}
 	}
 
-	if len(verifyOpts) > 0 {
+	lvo := len(verifyOpts)
+	if lvo == 0 && verification {
+		return nil, fmt.Errorf(`jwt.Parse: no keys for verification are provided (use jwt.WithVerify(false) to explicitly skip)`)
+	}
+
+	if lvo > 0 {
 		converted, err := toVerifyOptions(verifyOpts...)
 		if err != nil {
 			return nil, fmt.Errorf(`jwt.Parse: failed to convert options into jws.VerifyOption: %w`, err)
diff --git a/jwt/options.yaml b/jwt/options.yaml
index a2459171..4fbd1a8f 100644
--- a/jwt/options.yaml
+++ b/jwt/options.yaml
@@ -100,8 +100,28 @@ options:
     argument_type: bool
     comment: |
       WithValidate is passed to `Parse()` method to denote that the
-      validation of the JWT token should be performed after a successful
-      parsing of the incoming payload.
+      validation of the JWT token should be performed (or not) after
+      a successful parsing of the incoming payload.
+
+      This option is enabled by default. 
+
+      If you would like disable validation,
+      you must use `jwt.WithValidate(false)` or use `jwt.ParseInsecure()`
+  - ident: Verify
+    interface: ParseOption
+    argument_type: bool
+    comment: |
+      WithVerify is passed to `Parse()` method to denote that the
+      signature verification should be performed after a successful
+      deserialization of the incoming payload.
+
+      This option is enabled by default.
+
+      If you do not provide any verification key sources, `jwt.Parse()`
+      would return an error.
+      
+      If you would like to only parse the JWT payload and not verify it,
+      you must use `jwt.WithVerify(false)` or use `jwt.ParseInsecure()`
   - ident: KeyProvider
     interface: ParseOption
     argument_type: jws.KeyProvider
diff --git a/jwt/options_gen.go b/jwt/options_gen.go
index 0a4f74ca..b2b6a8d0 100644
--- a/jwt/options_gen.go
+++ b/jwt/options_gen.go
@@ -122,6 +122,7 @@ type identSignOption struct{}
 type identToken struct{}
 type identValidate struct{}
 type identValidator struct{}
+type identVerify struct{}
 
 func (identAcceptableSkew) String() string {
 	return "WithAcceptableSkew"
@@ -175,6 +176,10 @@ func (identValidator) String() string {
 	return "WithValidator"
 }
 
+func (identVerify) String() string {
+	return "WithVerify"
+}
+
 // WithAcceptableSkew specifies the duration in which exp and nbf
 // claims may differ by. This value should be positive
 func WithAcceptableSkew(v time.Duration) ValidateOption {
@@ -260,8 +265,13 @@ func WithToken(v Token) ParseOption {
 }
 
 // WithValidate is passed to `Parse()` method to denote that the
-// validation of the JWT token should be performed after a successful
-// parsing of the incoming payload.
+// validation of the JWT token should be performed (or not) after
+// a successful parsing of the incoming payload.
+//
+// This option is enabled by default.
+//
+// If you would like disable validation,
+// you must use `jwt.WithValidate(false)` or use `jwt.ParseInsecure()`
 func WithValidate(v bool) ParseOption {
 	return &parseOption{option.New(identValidate{}, v)}
 }
@@ -280,3 +290,18 @@ func WithValidate(v bool) ParseOption {
 func WithValidator(v Validator) ValidateOption {
 	return &validateOption{option.New(identValidator{}, v)}
 }
+
+// WithVerify is passed to `Parse()` method to denote that the
+// signature verification should be performed after a successful
+// deserialization of the incoming payload.
+//
+// This option is enabled by default.
+//
+// If you do not provide any verification key sources, `jwt.Parse()`
+// would return an error.
+//
+// If you would like to only parse the JWT payload and not verify it,
+// you must use `jwt.WithVerify(false)` or use `jwt.ParseInsecure()`
+func WithVerify(v bool) ParseOption {
+	return &parseOption{option.New(identVerify{}, v)}
+}


Test patch:
diff --git a/examples/jwt_example_test.go b/examples/jwt_example_test.go
index 574d54ef..9a00a3f7 100644
--- a/examples/jwt_example_test.go
+++ b/examples/jwt_example_test.go
@@ -288,7 +288,7 @@ func ExampleJWT_OpenIDToken() {
 	}
 	fmt.Printf("%s\n", buf)
 
-	t2, err := jwt.Parse(buf, jwt.WithToken(openid.New()))
+	t2, err := jwt.Parse(buf, jwt.WithToken(openid.New()), jwt.WithVerify(false), jwt.WithValidate(false))
 	if err != nil {
 		fmt.Printf("failed to parse JSON: %s\n", err)
 		return
@@ -314,87 +314,3 @@ func ExampleJWT_OpenIDToken() {
 	//   "sub": "https://github.com/lestrrat-go/jwx/v2/jwt"
 	// }
 }
-
-type TokenWithSource struct {
-	jwt.Token
-	undecoded []byte
-}
-
-func ParseTokenWithSource(serialized []byte, options ...jwt.ParseOption) (*TokenWithSource, error) {
-	tok := TokenWithSource{
-		Token: jwt.New(),
-	}
-
-	options = append(options, jwt.WithToken(&tok))
-
-	_, err := jwt.Parse(serialized, options...)
-	if err != nil {
-		return nil, fmt.Errorf(`failed to verify token: %w`, err)
-	}
-
-	tok.undecoded = serialized
-	return &tok, nil
-}
-
-func (t *TokenWithSource) Verify(options ...jws.VerifyOption) error {
-	if _, err := jws.Verify(t.undecoded, options...); err != nil {
-		return fmt.Errorf(`jws.Verify failed: %w`, err)
-	}
-	return nil
-}
-
-func (t *TokenWithSource) UnmarshalJSON(data []byte) error {
-	return json.Unmarshal(data, t.Token)
-}
-
-func (t *TokenWithSource) MarshalJSON() ([]byte, error) {
-	return json.Marshal(t.Token)
-}
-
-// This example shows how you might want to customize a token without
-// verifying it, but carry the original serialized payload so that it can be
-// verified at a later point
-func ExampleJWT_Extended() {
-	key, err := jwk.FromRaw([]byte(`abracadavra`))
-	if err != nil {
-		fmt.Printf("failed to create key: %s\n", err)
-		return
-	}
-
-	var serialized []byte
-	{
-		t0, err := jwt.NewBuilder().
-			Issuer(`github.com/lestrrat-go/jwx`).
-			Audience([]string{`custom-token`}).
-			Build()
-		if err != nil {
-			fmt.Printf("failed to create token t0: %s\n", err)
-			return
-		}
-
-		v, err := jwt.Sign(t0, jwt.WithKey(jwa.HS256, key))
-		if err != nil {
-			fmt.Printf("failed to sign token t0: %s\n", err)
-			return
-		}
-		serialized = v
-	}
-
-	tok, err := ParseTokenWithSource(serialized)
-	if err != nil {
-		fmt.Printf("failed to parse custom token: %s\n", err)
-		return
-	}
-
-	if tok.Issuer() != `github.com/lestrrat-go/jwx` {
-		fmt.Printf("issuer did not match (got %q)\n", tok.Issuer())
-		return
-	}
-
-	if err := tok.Verify(jws.WithKey(jwa.HS256, key)); err != nil {
-		fmt.Printf("failed to verify token: %s\n", err)
-		return
-	}
-
-	// OUTPUT:
-}
diff --git a/examples/jwt_parse_example_test.go b/examples/jwt_parse_example_test.go
index de7b58d3..eb41e8cb 100644
--- a/examples/jwt_parse_example_test.go
+++ b/examples/jwt_parse_example_test.go
@@ -3,14 +3,12 @@ package examples_test
 import (
 	"fmt"
 
+	"github.com/lestrrat-go/jwx/v2/jwa"
 	"github.com/lestrrat-go/jwx/v2/jwt"
 )
 
 func ExampleJWT_Parse() {
-	// Note: this JWT has NOT been verified because we have not
-	// passed jwt.WithKey() et al. You need to pass these values
-	// if you want the token to be parsed and verified in one go
-	tok, err := jwt.Parse([]byte(exampleJWTSignedHMAC))
+	tok, err := jwt.Parse(jwtSignedWithHS256, jwt.WithKey(jwa.HS256, jwkSymmetricKey))
 	if err != nil {
 		fmt.Printf("%s\n", err)
 		return
diff --git a/examples/jwt_parse_request_example_test.go b/examples/jwt_parse_request_example_test.go
index 0baad3d9..f3eab155 100644
--- a/examples/jwt_parse_request_example_test.go
+++ b/examples/jwt_parse_request_example_test.go
@@ -43,10 +43,8 @@ func ExampleJWT_ParseRequest_Authorization() {
 	}
 
 	for _, tc := range testcases {
-		// Note: this JWT has NOT been verified because we have not
-		// passed jwt.WithKey() et al. You need to pass these values
-		// if you want the token to be parsed and verified in one go
-		tok, err := jwt.ParseRequest(req, tc.options...)
+		options := append(tc.options, []jwt.ParseOption{jwt.WithVerify(false), jwt.WithValidate(false)}...)
+		tok, err := jwt.ParseRequest(req, options...)
 		if err != nil {
 			fmt.Printf("jwt.ParseRequest with options %#v failed: %s\n", tc.options, err)
 			return
diff --git a/examples/jwt_parse_with_key_example_test.go b/examples/jwt_parse_with_key_example_test.go
index 18b429d5..c9bc3e8f 100644
--- a/examples/jwt_parse_with_key_example_test.go
+++ b/examples/jwt_parse_with_key_example_test.go
@@ -17,7 +17,7 @@ func ExampleJWT_ParseWithKey() {
 		return
 	}
 
-	tok, err := jwt.Parse([]byte(exampleJWTSignedHMAC), jwt.WithKey(jwa.HS256, key))
+	tok, err := jwt.Parse([]byte(exampleJWTSignedHMAC), jwt.WithKey(jwa.HS256, key), jwt.WithValidate(false))
 	if err != nil {
 		fmt.Printf("jwt.Parse failed: %s\n", err)
 		return
diff --git a/examples/jwt_readfile_example_test.go b/examples/jwt_readfile_example_test.go
index 7e71e644..f489c5dd 100644
--- a/examples/jwt_readfile_example_test.go
+++ b/examples/jwt_readfile_example_test.go
@@ -22,7 +22,7 @@ func ExampleJWT_ReadFile() {
 	// Note: this JWT has NOT been verified because we have not
 	// passed jwt.WithKey() et al. You need to pass these values
 	// if you want the token to be parsed and verified in one go
-	tok, err := jwt.ReadFile(f.Name())
+	tok, err := jwt.ReadFile(f.Name(), jwt.WithVerify(false), jwt.WithValidate(false))
 	if err != nil {
 		fmt.Printf("failed to read file %q: %s\n", f.Name(), err)
 		return
diff --git a/examples/jwt_validate_detect_error_type_example_test.go b/examples/jwt_validate_detect_error_type_example_test.go
index aef0aec1..01cc32c1 100644
--- a/examples/jwt_validate_detect_error_type_example_test.go
+++ b/examples/jwt_validate_detect_error_type_example_test.go
@@ -29,7 +29,7 @@ func ExampleJWT_ValidateDetectErrorType() {
 		// Case 1: Parsing error. We're not showing verification faiure
 		// but it is about the same in the context of wanting to know
 		// if it's a validation error or not
-		_, err := jwt.Parse(buf[:len(buf)-1], jwt.WithValidate(true))
+		_, err := jwt.Parse(buf[:len(buf)-1], jwt.WithVerify(false), jwt.WithValidate(true))
 		if err == nil {
 			fmt.Printf("token should fail parsing\n")
 			return
@@ -45,7 +45,7 @@ func ExampleJWT_ValidateDetectErrorType() {
 		// Case 2: Parsing works, validation fails
 		// NOTE: This token has NOT been verified for demonstration
 		// purposes. Use `jwt.WithKey()` or the like in your production code
-		_, err = jwt.Parse(buf, jwt.WithValidate(true))
+		_, err = jwt.Parse(buf, jwt.WithVerify(false), jwt.WithValidate(true))
 		if err == nil {
 			fmt.Printf("token should fail parsing\n")
 			return
diff --git a/examples/jwt_validate_example_test.go b/examples/jwt_validate_example_test.go
index d3c1bca8..6b63c5c1 100644
--- a/examples/jwt_validate_example_test.go
+++ b/examples/jwt_validate_example_test.go
@@ -38,7 +38,7 @@ func ExampleJWT_Validate() {
 
 		// NOTE: This token has NOT been verified for demonstration
 		// purposes. Use `jwt.WithKey()` or the like in your production code
-		_, err = jwt.Parse(buf, jwt.WithValidate(true))
+		_, err = jwt.Parse(buf, jwt.WithVerify(false), jwt.WithValidate(true))
 		if err == nil {
 			fmt.Printf("token should fail validation\n")
 			return
diff --git a/examples/jwx_example_test.go b/examples/jwx_example_test.go
index 14dc24cf..177993f7 100644
--- a/examples/jwx_example_test.go
+++ b/examples/jwx_example_test.go
@@ -4,16 +4,21 @@ import (
 	"crypto/rand"
 	"crypto/rsa"
 	"fmt"
+	"time"
 
 	"github.com/lestrrat-go/jwx/v2/internal/json"
+	"github.com/lestrrat-go/jwx/v2/jwa"
 	"github.com/lestrrat-go/jwx/v2/jwk"
+	"github.com/lestrrat-go/jwx/v2/jwt"
 )
 
 var payloadLoremIpsum []byte
+var jwtSignedWithHS256 []byte
 var rawRSAPrivateKey *rsa.PrivateKey
 var rawRSAPublicKey *rsa.PublicKey
 var jwkRSAPrivateKey jwk.Key
 var jwkRSAPublicKey jwk.Key
+var jwkSymmetricKey jwk.Key
 var jsonRSAPrivateKey []byte
 var jsonRSAPublicKey []byte
 
@@ -27,6 +32,15 @@ func init() {
 func Setup() error {
 	payloadLoremIpsum = []byte(`Lorem ipsum`)
 
+	tok, err := jwt.NewBuilder().
+		Issuer(`github.com/lestrrat-go/jwx`).
+		IssuedAt(time.Now().Add(-5 * time.Minute)).
+		Expiration(time.Now().Add(time.Hour)).
+		Build()
+	if err != nil {
+		return fmt.Errorf(`failed to build token: %w`, err)
+	}
+
 	{
 		v, err := rsa.GenerateKey(rand.Reader, 2048)
 		if err != nil {
@@ -69,5 +83,21 @@ func Setup() error {
 		jsonRSAPublicKey = v
 	}
 
+	{
+		v, err := jwk.FromRaw([]byte(`abracadavra`))
+		if err != nil {
+			return fmt.Errorf(`failed to create jwk.Key from symmetric key: %w`, err)
+		}
+		jwkSymmetricKey = v
+	}
+
+	{
+		v, err := jwt.Sign(tok, jwt.WithKey(jwa.HS256, jwkSymmetricKey))
+		if err != nil {
+			return fmt.Errorf(`failed to sign token with HS256: %w`, err)
+		}
+		jwtSignedWithHS256 = v
+	}
+
 	return nil
 }
diff --git a/examples/jwx_readme_example_test.go b/examples/jwx_readme_example_test.go
index 50d0c0e2..d6d95bff 100644
--- a/examples/jwx_readme_example_test.go
+++ b/examples/jwx_readme_example_test.go
@@ -61,7 +61,7 @@ func ExampleJWX() {
 			req, err := http.NewRequest(http.MethodGet, `https://github.com/lestrrat-go/jwx`, nil)
 			req.Header.Set(`Authorization`, fmt.Sprintf(`Bearer %s`, signed))
 
-			verifiedToken, err := jwt.ParseRequest(req)
+			verifiedToken, err := jwt.ParseRequest(req, jwt.WithKey(jwa.RS256, pubkey))
 			if err != nil {
 				fmt.Printf("failed to verify token from HTTP request: %s\n", err)
 				return
diff --git a/jwt/jwt_test.go b/jwt/jwt_test.go
index 9f831562..b2c98daf 100644
--- a/jwt/jwt_test.go
+++ b/jwt/jwt_test.go
@@ -64,7 +64,7 @@ func TestJWTParse(t *testing.T) {
 
 	t.Run("Parse (no signature verification)", func(t *testing.T) {
 		t.Parallel()
-		t2, err := jwt.Parse(signed)
+		t2, err := jwt.ParseInsecure(signed)
 		if !assert.NoError(t, err, `jwt.Parse should succeed`) {
 			return
 		}
@@ -74,7 +74,7 @@ func TestJWTParse(t *testing.T) {
 	})
 	t.Run("ParseString (no signature verification)", func(t *testing.T) {
 		t.Parallel()
-		t2, err := jwt.ParseString(string(signed))
+		t2, err := jwt.ParseString(string(signed), jwt.WithVerify(false), jwt.WithValidate(false))
 		if !assert.NoError(t, err, `jwt.ParseString should succeed`) {
 			return
 		}
@@ -84,7 +84,7 @@ func TestJWTParse(t *testing.T) {
 	})
 	t.Run("ParseReader (no signature verification)", func(t *testing.T) {
 		t.Parallel()
-		t2, err := jwt.ParseReader(bytes.NewReader(signed))
+		t2, err := jwt.ParseReader(bytes.NewReader(signed), jwt.WithVerify(false), jwt.WithValidate(false))
 		if !assert.NoError(t, err, `jwt.ParseReader should succeed`) {
 			return
 		}
@@ -735,10 +735,10 @@ func TestReadFile(t *testing.T) {
 		return
 	}
 
-	if _, err := jwt.ReadFile(f.Name(), jwt.WithValidate(true), jwt.WithIssuer("lestrrat")); !assert.NoError(t, err, `jwt.ReadFile should succeed`) {
+	if _, err := jwt.ReadFile(f.Name(), jwt.WithVerify(false), jwt.WithValidate(true), jwt.WithIssuer("lestrrat")); !assert.NoError(t, err, `jwt.ReadFile should succeed`) {
 		return
 	}
-	if _, err := jwt.ReadFile(f.Name(), jwt.WithValidate(true), jwt.WithIssuer("lestrrrrrat")); !assert.Error(t, err, `jwt.ReadFile should fail`) {
+	if _, err := jwt.ReadFile(f.Name(), jwt.WithVerify(false), jwt.WithValidate(true), jwt.WithIssuer("lestrrrrrat")); !assert.Error(t, err, `jwt.ReadFile should fail`) {
 		return
 	}
 }
@@ -758,7 +758,7 @@ func TestCustomField(t *testing.T) {
 	src := b.String()
 
 	t.Run("jwt.Parse", func(t *testing.T) {
-		token, err := jwt.Parse([]byte(src))
+		token, err := jwt.ParseInsecure([]byte(src))
 		if !assert.NoError(t, err, `jwt.Parse should succeed`) {
 			t.Logf("%q", src)
 			return
@@ -1133,7 +1133,8 @@ func TestJWTParseWithTypedClaim(t *testing.T) {
 	for _, tc := range testcases {
 		tc := tc
 		t.Run(tc.Name, func(t *testing.T) {
-			got, err := jwt.Parse(signed, tc.Options...)
+			options := append(tc.Options, jwt.WithVerify(false))
+			got, err := jwt.Parse(signed, options...)
 			if !assert.NoError(t, err, `jwt.Parse should succeed`) {
 				return
 			}
diff --git a/jwt/openid/openid_test.go b/jwt/openid/openid_test.go
index 10ea68f4..8c41c1ca 100644
--- a/jwt/openid/openid_test.go
+++ b/jwt/openid/openid_test.go
@@ -455,7 +455,7 @@ func TestOpenIDClaims(t *testing.T) {
 				return
 			}
 
-			tokenTmp, err := jwt.Parse(signed, jwt.WithToken(openid.New()), jwt.WithKey(alg, &key.PublicKey))
+			tokenTmp, err := jwt.Parse(signed, jwt.WithToken(openid.New()), jwt.WithKey(alg, &key.PublicKey), jwt.WithValidate(false))
 			if !assert.NoError(t, err, `parsing the token via jwt.Parse should succeed`) {
 				return
 			}
diff --git a/jwt/options_gen_test.go b/jwt/options_gen_test.go
index 1ad09668..12926755 100644
--- a/jwt/options_gen_test.go
+++ b/jwt/options_gen_test.go
@@ -22,4 +22,5 @@ func TestOptionIdent(t *testing.T) {
 	require.Equal(t, "WithToken", identToken{}.String())
 	require.Equal(t, "WithValidate", identValidate{}.String())
 	require.Equal(t, "WithValidator", identValidator{}.String())
+	require.Equal(t, "WithVerify", identVerify{}.String())
 }
diff --git a/jwt/token_test.go b/jwt/token_test.go
index 0f9b2c2d..9f37932b 100644
--- a/jwt/token_test.go
+++ b/jwt/token_test.go
@@ -237,7 +237,7 @@ func TestToken(t *testing.T) {
 			return
 		}
 
-		newtok, err := jwt.Parse(buf)
+		newtok, err := jwt.ParseInsecure(buf)
 		if !assert.NoError(t, err, `jwt.Parse should succeed`) {
 			return
 		}
diff --git a/jwt/validate_test.go b/jwt/validate_test.go
index e46229d1..c64ae851 100644
--- a/jwt/validate_test.go
+++ b/jwt/validate_test.go
@@ -338,13 +338,13 @@ func TestGHIssue10(t *testing.T) {
 			return
 		}
 
-		_, err = jwt.Parse(buf, jwt.WithValidate(true))
+		_, err = jwt.Parse(buf, jwt.WithVerify(false), jwt.WithValidate(true))
 		// This should fail, because exp is set in the past
 		if !assert.Error(t, err, "jwt.Parse should fail") {
 			return
 		}
 
-		_, err = jwt.Parse(buf, jwt.WithValidate(true), jwt.WithAcceptableSkew(time.Hour))
+		_, err = jwt.Parse(buf, jwt.WithVerify(false), jwt.WithValidate(true), jwt.WithAcceptableSkew(time.Hour))
 		// This should succeed, because we have given big skew
 		// that is well enough to get us accepted
 		if !assert.NoError(t, err, "jwt.Parse should succeed (1)") {
@@ -356,7 +356,7 @@ func TestGHIssue10(t *testing.T) {
 		clock := jwt.ClockFunc(func() time.Time {
 			return tm.Add(-59 * time.Minute)
 		})
-		_, err = jwt.Parse(buf, jwt.WithValidate(true), jwt.WithClock(clock))
+		_, err = jwt.Parse(buf, jwt.WithVerify(false), jwt.WithValidate(true), jwt.WithClock(clock))
 		if !assert.NoError(t, err, "jwt.Parse should succeed (2)") {
 			return
 		}