From 7c3f6dc56316e5e222a9df9612ec04243189a989 Mon Sep 17 00:00:00 2001 From: Ashik Paul Date: Sat, 16 Mar 2024 07:51:28 +0530 Subject: [PATCH 01/10] Update README.md (#382) --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 964598a3..a1e7db4f 100644 --- a/README.md +++ b/README.md @@ -10,11 +10,11 @@ implementation of [JSON Web Tokens](https://datatracker.ietf.org/doc/html/rfc7519). Starting with [v4.0.0](https://github.com/golang-jwt/jwt/releases/tag/v4.0.0) -this project adds Go module support, but maintains backwards compatibility with +this project adds Go module support, but maintains backward compatibility with older `v3.x.y` tags and upstream `github.com/dgrijalva/jwt-go`. See the [`MIGRATION_GUIDE.md`](./MIGRATION_GUIDE.md) for more information. Version v5.0.0 introduces major improvements to the validation of tokens, but is not -entirely backwards compatible. +entirely backward compatible. > After the original author of the library suggested migrating the maintenance > of `jwt-go`, a dedicated team of open source maintainers decided to clone the @@ -24,7 +24,7 @@ entirely backwards compatible. **SECURITY NOTICE:** Some older versions of Go have a security issue in the -crypto/elliptic. Recommendation is to upgrade to at least 1.15 See issue +crypto/elliptic. The recommendation is to upgrade to at least 1.15 See issue [dgrijalva/jwt-go#216](https://github.com/dgrijalva/jwt-go/issues/216) for more detail. @@ -32,7 +32,7 @@ detail. what you expect](https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/). This library attempts to make it easy to do the right thing by requiring key -types match the expected alg, but you should take the extra step to verify it in +types to match the expected alg, but you should take the extra step to verify it in your usage. See the examples provided. ### Supported Go versions @@ -41,7 +41,7 @@ Our support of Go versions is aligned with Go's [version release policy](https://golang.org/doc/devel/release#policy). So we will support a major version of Go until there are two newer major releases. We no longer support building jwt-go with unsupported Go versions, as these contain security -vulnerabilities which will not be fixed. +vulnerabilities that will not be fixed. ## What the heck is a JWT? @@ -117,7 +117,7 @@ notable differences: This library is considered production ready. Feedback and feature requests are appreciated. The API should be considered stable. There should be very few -backwards-incompatible changes outside of major version updates (and only with +backward-incompatible changes outside of major version updates (and only with good reason). This project uses [Semantic Versioning 2.0.0](http://semver.org). Accepted pull From c8043eab61f0ec5bdd924c1c30caf164a9bb2c66 Mon Sep 17 00:00:00 2001 From: Michael Fridman Date: Fri, 15 Mar 2024 22:24:06 -0400 Subject: [PATCH 02/10] build: add go1.22 to ci workflows (#383) --- .github/workflows/build.yml | 2 +- .github/workflows/lint.yml | 2 +- README.md | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 27974bbb..753bca5e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,7 +13,7 @@ jobs: strategy: fail-fast: false matrix: - go: ["1.19", "1.20", "1.21"] + go: ["1.20", "1.21", "1.22"] steps: - name: Checkout uses: actions/checkout@v4 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index f8bf5428..6f8c65c5 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -14,7 +14,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: "1.21" + go-version: "1.22" check-latest: true - name: golangci-lint uses: golangci/golangci-lint-action@v4 diff --git a/README.md b/README.md index a1e7db4f..0bb636f2 100644 --- a/README.md +++ b/README.md @@ -125,8 +125,8 @@ requests will land on `main`. Periodically, versions will be tagged from `main`. You can find all the releases on [the project releases page](https://github.com/golang-jwt/jwt/releases). -**BREAKING CHANGES:*** A full list of breaking changes is available in -`VERSION_HISTORY.md`. See `MIGRATION_GUIDE.md` for more information on updating +**BREAKING CHANGES:** A full list of breaking changes is available in +`VERSION_HISTORY.md`. See [`MIGRATION_GUIDE.md`](./MIGRATION_GUIDE.md) for more information on updating your code. ## Extensions From 1a56dcf532089fc2bb723a3cb4076a4e45cb1c1a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Apr 2024 12:14:30 +0200 Subject: [PATCH 03/10] Bump golangci/golangci-lint-action from 4 to 5 (#387) Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 4 to 5. - [Release notes](https://github.com/golangci/golangci-lint-action/releases) - [Commits](https://github.com/golangci/golangci-lint-action/compare/v4...v5) --- updated-dependencies: - dependency-name: golangci/golangci-lint-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 6f8c65c5..bd5e6b0a 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -17,7 +17,7 @@ jobs: go-version: "1.22" check-latest: true - name: golangci-lint - uses: golangci/golangci-lint-action@v4 + uses: golangci/golangci-lint-action@v5 with: # Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version version: latest From 62e504c2810b67f6b97313424411cfffb25e41b0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 May 2024 08:06:47 -0400 Subject: [PATCH 04/10] Bump golangci/golangci-lint-action from 5 to 6 (#389) Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 5 to 6. - [Release notes](https://github.com/golangci/golangci-lint-action/releases) - [Commits](https://github.com/golangci/golangci-lint-action/compare/v5...v6) --- updated-dependencies: - dependency-name: golangci/golangci-lint-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index bd5e6b0a..6d572866 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -17,7 +17,7 @@ jobs: go-version: "1.22" check-latest: true - name: golangci-lint - uses: golangci/golangci-lint-action@v5 + uses: golangci/golangci-lint-action@v6 with: # Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version version: latest From f961c72abd3b91442a9ab3d3e356bf547636e89b Mon Sep 17 00:00:00 2001 From: Michael Fridman Date: Fri, 16 Aug 2024 08:47:03 -0400 Subject: [PATCH 05/10] chore: bump ci tests to include go1.23 (#405) --- .github/workflows/build.yml | 2 +- .github/workflows/lint.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 753bca5e..032eb767 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,7 +13,7 @@ jobs: strategy: fail-fast: false matrix: - go: ["1.20", "1.21", "1.22"] + go: ["1.21", "1.22", "1.23"] steps: - name: Checkout uses: actions/checkout@v4 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 6d572866..cec3b92b 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -14,7 +14,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: "1.22" + go-version: "1.23" check-latest: true - name: golangci-lint uses: golangci/golangci-lint-action@v6 From 0123f1ad66cbc45013dbfba6eff0cd81472bfc12 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Fri, 6 Sep 2024 02:38:10 +0200 Subject: [PATCH 06/10] Fix jwt -show (#406) --- cmd/jwt/main.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cmd/jwt/main.go b/cmd/jwt/main.go index 37b4fccf..22031ca2 100644 --- a/cmd/jwt/main.go +++ b/cmd/jwt/main.go @@ -30,9 +30,9 @@ var ( flagHead = make(ArgList) // Modes - exactly one of these is required - flagSign = flag.String("sign", "", "path to claims object to sign, '-' to read from stdin, or '+' to use only -claim args") - flagVerify = flag.String("verify", "", "path to JWT token to verify or '-' to read from stdin") - flagShow = flag.String("show", "", "path to JWT file or '-' to read from stdin") + flagSign = flag.String("sign", "", "path to claims file to sign, '-' to read from stdin, or '+' to use only -claim args") + flagVerify = flag.String("verify", "", "path to JWT token file to verify or '-' to read from stdin") + flagShow = flag.String("show", "", "path to JWT token file to show without verification or '-' to read from stdin") ) func main() { @@ -43,7 +43,7 @@ func main() { // Usage message if you ask for -help or if you mess up inputs. flag.Usage = func() { fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) - fmt.Fprintf(os.Stderr, " One of the following flags is required: sign, verify\n") + fmt.Fprintf(os.Stderr, " One of the following flags is required: sign, verify or show\n") flag.PrintDefaults() } @@ -69,7 +69,7 @@ func start() error { return showToken() default: flag.Usage() - return fmt.Errorf("none of the required flags are present. What do you want me to do?") + return fmt.Errorf("none of the required flags are present. What do you want me to do?") } } @@ -273,7 +273,7 @@ func showToken() error { fmt.Fprintf(os.Stderr, "Token len: %v bytes\n", len(tokData)) } - token, err := jwt.Parse(string(tokData), nil) + token, _, err := jwt.NewParser().ParseUnverified(string(tokData), make(jwt.MapClaims)) if err != nil { return fmt.Errorf("malformed token: %w", err) } From 5ec246c074b71790eec1f2e05b54daf6ec29ec5f Mon Sep 17 00:00:00 2001 From: kvii <56432636+kvii@users.noreply.github.com> Date: Fri, 6 Sep 2024 08:39:08 +0800 Subject: [PATCH 07/10] docs: typo (#407) --- token.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/token.go b/token.go index 352873a2..9c7f4ab0 100644 --- a/token.go +++ b/token.go @@ -75,7 +75,7 @@ func (t *Token) SignedString(key interface{}) (string, error) { } // SigningString generates the signing string. This is the most expensive part -// of the whole deal. Unless you need this for something special, just go +// of the whole deal. Unless you need this for something special, just go // straight for the SignedString. func (t *Token) SigningString() (string, error) { h, err := json.Marshal(t.Header) From bc8bdca5cced1caa9787e4a1c313a3538544c877 Mon Sep 17 00:00:00 2001 From: Christian Banse Date: Mon, 4 Nov 2024 07:57:43 +0100 Subject: [PATCH 08/10] Update SECURITY.md (#416) --- SECURITY.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SECURITY.md b/SECURITY.md index b08402c3..2740597f 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,11 +2,11 @@ ## Supported Versions -As of February 2022 (and until this document is updated), the latest version `v4` is supported. +As of November 2024 (and until this document is updated), the latest version `v5` is supported. In critical cases, we might supply back-ported patches for `v4`. ## Reporting a Vulnerability -If you think you found a vulnerability, and even if you are not sure, please report it to jwt-go-security@googlegroups.com or one of the other [golang-jwt maintainers](https://github.com/orgs/golang-jwt/people). Please try be explicit, describe steps to reproduce the security issue with code example(s). +If you think you found a vulnerability, and even if you are not sure, please report it a [GitHub Security Advisory](https://github.com/golang-jwt/jwt/security/advisories/new). Please try be explicit, describe steps to reproduce the security issue with code example(s). You will receive a response within a timely manner. If the issue is confirmed, we will do our best to release a patch as soon as possible given the complexity of the problem. From c035977d9e11c351f4c05dfeae193923cbab49ee Mon Sep 17 00:00:00 2001 From: Mattt Date: Sat, 18 Jan 2025 06:57:31 -0800 Subject: [PATCH 09/10] Update Parse example to use WithValidMethods (#425) --- hmac_example_test.go | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/hmac_example_test.go b/hmac_example_test.go index 1b1edf46..f8f8c26b 100644 --- a/hmac_example_test.go +++ b/hmac_example_test.go @@ -49,14 +49,9 @@ func ExampleParse_hmac() { // head of the token to identify which key to use, but the parsed token (head and claims) is provided // to the callback, providing flexibility. token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { - // Don't forget to validate the alg is what you expect: - if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { - return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"]) - } - // hmacSampleSecret is a []byte containing your secret, e.g. []byte("my_secret_key") return hmacSampleSecret, nil - }) + }, jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Alg()})) if err != nil { log.Fatal(err) } From 0951d184286dece21f73c85673fd308786ffe9c3 Mon Sep 17 00:00:00 2001 From: Michael Fridman Date: Fri, 21 Mar 2025 16:42:51 -0400 Subject: [PATCH 10/10] Merge commit from fork * Remove strings.Split and add parseToken function * review and add tests --- jwt_test.go | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++++ parser.go | 36 ++++++++++++++++++++-- 2 files changed, 122 insertions(+), 3 deletions(-) create mode 100644 jwt_test.go diff --git a/jwt_test.go b/jwt_test.go new file mode 100644 index 00000000..b01e899d --- /dev/null +++ b/jwt_test.go @@ -0,0 +1,89 @@ +package jwt + +import ( + "testing" +) + +func TestSplitToken(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + input string + expected []string + isValid bool + }{ + { + name: "valid token with three parts", + input: "header.claims.signature", + expected: []string{"header", "claims", "signature"}, + isValid: true, + }, + { + name: "invalid token with two parts only", + input: "header.claims", + expected: nil, + isValid: false, + }, + { + name: "invalid token with one part only", + input: "header", + expected: nil, + isValid: false, + }, + { + name: "invalid token with extra delimiter", + input: "header.claims.signature.extra", + expected: nil, + isValid: false, + }, + { + name: "invalid empty token", + input: "", + expected: nil, + isValid: false, + }, + { + name: "valid token with empty parts", + input: "..signature", + expected: []string{"", "", "signature"}, + isValid: true, + }, + { + // We are just splitting the token into parts, so we don't care about the actual values. + // It is up to the caller to validate the parts. + name: "valid token with all parts empty", + input: "..", + expected: []string{"", "", ""}, + isValid: true, + }, + { + name: "invalid token with just delimiters and extra part", + input: "...", + expected: nil, + isValid: false, + }, + { + name: "invalid token with many delimiters", + input: "header.claims.signature..................", + expected: nil, + isValid: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + parts, ok := splitToken(tt.input) + if ok != tt.isValid { + t.Errorf("expected %t, got %t", tt.isValid, ok) + } + if ok { + for i, part := range tt.expected { + if parts[i] != part { + t.Errorf("expected %s, got %s", part, parts[i]) + } + } + } + }) + } +} diff --git a/parser.go b/parser.go index ecf99af7..054c7eb6 100644 --- a/parser.go +++ b/parser.go @@ -8,6 +8,8 @@ import ( "strings" ) +const tokenDelimiter = "." + type Parser struct { // If populated, only these methods will be considered valid. validMethods []string @@ -136,9 +138,10 @@ func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyf // It's only ever useful in cases where you know the signature is valid (since it has already // been or will be checked elsewhere in the stack) and you want to extract values from it. func (p *Parser) ParseUnverified(tokenString string, claims Claims) (token *Token, parts []string, err error) { - parts = strings.Split(tokenString, ".") - if len(parts) != 3 { - return nil, parts, newError("token contains an invalid number of segments", ErrTokenMalformed) + var ok bool + parts, ok = splitToken(tokenString) + if !ok { + return nil, nil, newError("token contains an invalid number of segments", ErrTokenMalformed) } token = &Token{Raw: tokenString} @@ -196,6 +199,33 @@ func (p *Parser) ParseUnverified(tokenString string, claims Claims) (token *Toke return token, parts, nil } +// splitToken splits a token string into three parts: header, claims, and signature. It will only +// return true if the token contains exactly two delimiters and three parts. In all other cases, it +// will return nil parts and false. +func splitToken(token string) ([]string, bool) { + parts := make([]string, 3) + header, remain, ok := strings.Cut(token, tokenDelimiter) + if !ok { + return nil, false + } + parts[0] = header + claims, remain, ok := strings.Cut(remain, tokenDelimiter) + if !ok { + return nil, false + } + parts[1] = claims + // One more cut to ensure the signature is the last part of the token and there are no more + // delimiters. This avoids an issue where malicious input could contain additional delimiters + // causing unecessary overhead parsing tokens. + signature, _, unexpected := strings.Cut(remain, tokenDelimiter) + if unexpected { + return nil, false + } + parts[2] = signature + + return parts, true +} + // DecodeSegment decodes a JWT specific base64url encoding. This function will // take into account whether the [Parser] is configured with additional options, // such as [WithStrictDecoding] or [WithPaddingAllowed].