diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ec7bd9ac..4ada1176 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,6 +8,18 @@ on: types: [opened, synchronize, reopened] jobs: + check: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + - uses: reviewdog/action-staticcheck@v1 + with: + github_token: ${{ secrets.github_token }} + reporter: github-pr-review + filter_mode: nofilter + fail_on_error: true + build: runs-on: ubuntu-latest strategy: @@ -17,8 +29,6 @@ jobs: steps: - name: Checkout uses: actions/checkout@v2 - with: - path: src/github.com/golang-jwt/jwt - name: Setup Go uses: actions/setup-go@v2 with: @@ -28,6 +38,3 @@ jobs: go vet ./... go test -v ./... go build ./... - env: - GO111MODULE: auto - GOPATH: ${{ github.workspace }} diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index c4efbd2a..0b621493 100644 --- a/MIGRATION_GUIDE.md +++ b/MIGRATION_GUIDE.md @@ -1,22 +1,22 @@ -## Migration Guide (v3.2.1) +## Migration Guide (v4.0.0) -Starting from [v3.2.1](https://github.com/golang-jwt/jwt/releases/tag/v3.2.1]), the import path has changed from `github.com/dgrijalva/jwt-go` to `github.com/golang-jwt/jwt`. Future releases will be using the `github.com/golang-jwt/jwt` import path and continue the existing versioning scheme of `v3.x.x+incompatible`. Backwards-compatible patches and fixes will be done on the `v3` release branch, where as new build-breaking features will be developed in a `v4` release, possibly including a SIV-style import path. +Starting from [v4.0.0](https://github.com/golang-jwt/jwt/releases/tag/v4.0.0]), the import path will be: -### go.mod replacement + "github.com/golang-jwt/jwt/v4" -In a first step, the easiest way is to use `go mod edit` to issue a replacement. +The `/v4` version will be backwards compatible with existing `v3.x.y` tags in this repo, as well as +`github.com/dgrijalva/jwt-go`. For most users this should be a drop-in replacement, if you're having +troubles migrating, please open an issue. + +You can replace all occurrences of `github.com/dgrijalva/jwt-go` or `github.com/golang-jwt/jwt` with `github.com/golang-jwt/jwt/v4`, either manually or by using tools such as `sed` or `gofmt`. + +And then you'd typically run: ``` -go mod edit -replace github.com/dgrijalva/jwt-go=github.com/golang-jwt/jwt@v3.2.1+incompatible +go get github.com/golang-jwt/jwt/v4 go mod tidy ``` -This will still keep the old import path in your code but replace it with the new package and also introduce a new indirect dependency to `github.com/golang-jwt/jwt`. Try to compile your project; it should still work. - -### Cleanup - -If your code still consistently builds, you can replace all occurences of `github.com/dgrijalva/jwt-go` with `github.com/golang-jwt/jwt`, either manually or by using tools such as `sed`. Finally, the `replace` directive in the `go.mod` file can be removed. - ## Older releases (before v3.2.0) The original migration guide for older releases can be found at https://github.com/dgrijalva/jwt-go/blob/master/MIGRATION_GUIDE.md. \ No newline at end of file diff --git a/README.md b/README.md index 9b653e46..96fe3b97 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,11 @@ A [go](http://www.golang.org) (or 'golang' for search engine friendliness) implementation of [JSON Web Tokens](https://datatracker.ietf.org/doc/html/rfc7519). -**IMPORT PATH CHANGE:** Starting from [v3.2.1](https://github.com/golang-jwt/jwt/releases/tag/v3.2.1), the import path has changed from `github.com/dgrijalva/jwt-go` to `github.com/golang-jwt/jwt`. 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 existing library into this repository. See [dgrijalva/jwt-go#462](https://github.com/dgrijalva/jwt-go/issues/462) for a detailed discussion on this topic. +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 compataibility with older `v3.x.y` tags and upstream `github.com/dgrijalva/jwt-go`. +See the `MIGRATION_GUIDE.md` for more information. + +> 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 existing library into this repository. See [dgrijalva/jwt-go#462](https://github.com/dgrijalva/jwt-go/issues/462) for a detailed discussion on this topic. -Future releases will be using the `github.com/golang-jwt/jwt` import path and continue the existing versioning scheme of `v3.x.x+incompatible`. Backwards-compatible patches and fixes will be done on the `v3` release branch, where as new build-breaking features will be developed in a `v4` release, possibly including a SIV-style import path. **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 [dgrijalva/jwt-go#216](https://github.com/dgrijalva/jwt-go/issues/216) for more detail. @@ -60,10 +62,8 @@ This library is considered production ready. Feedback and feature requests are This project uses [Semantic Versioning 2.0.0](http://semver.org). Accepted pull 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). -While we try to make it obvious when we make breaking changes, there isn't a great mechanism for pushing announcements out to users. You may want to use this alternative package include: `gopkg.in/golang-jwt/jwt.v3`. It will do the right thing WRT semantic versioning. - **BREAKING CHANGES:*** -* Version 3.0.0 includes _a lot_ of changes from the 2.x line, including a few that break the API. We've tried to break as few things as possible, so there should just be a few type signature changes. A full list of breaking changes is available in `VERSION_HISTORY.md`. See `MIGRATION_GUIDE.md` for more information on updating your code. +A full list of breaking changes is available in `VERSION_HISTORY.md`. See `MIGRATION_GUIDE.md` for more information on updating your code. ## Usage Tips diff --git a/VERSION_HISTORY.md b/VERSION_HISTORY.md index 637f2ba6..afbfc4e4 100644 --- a/VERSION_HISTORY.md +++ b/VERSION_HISTORY.md @@ -1,5 +1,9 @@ ## `jwt-go` Version History +#### 4.0.0 + +* Introduces support for Go modules. The `v4` version will be backwards compatible with `v3.x.y`. + #### 3.2.2 * Starting from this release, we are adopting the policy to support the most 2 recent versions of Go currently available. By the time of this release, this is Go 1.15 and 1.16 ([#28](https://github.com/golang-jwt/jwt/pull/28)). diff --git a/claims.go b/claims.go index f1dba3cb..7c2f33bc 100644 --- a/claims.go +++ b/claims.go @@ -6,13 +6,13 @@ import ( "time" ) -// For a type to be a Claims object, it must just have a Valid method that determines +// Claims must just have a Valid method that determines // if the token is invalid for any supported reason type Claims interface { Valid() error } -// Structured version of Claims Section, as referenced at +// StandardClaims are a structured version of the Claims Section, as referenced at // https://tools.ietf.org/html/rfc7519#section-4.1 // See examples for how to use this with your own claim types type StandardClaims struct { @@ -25,8 +25,7 @@ type StandardClaims struct { Subject string `json:"sub,omitempty"` } -// Validates time based claims "exp, iat, nbf". -// There is no accounting for clock skew. +// Valid validates time based claims "exp, iat, nbf". There is no accounting for clock skew. // As well, if any of the above claims are not in the token, it will still // be considered a valid claim. func (c StandardClaims) Valid() error { @@ -58,31 +57,31 @@ func (c StandardClaims) Valid() error { return vErr } -// Compares the aud claim against cmp. +// VerifyAudience compares the aud claim against cmp. // If required is false, this method will return true if the value matches or is unset func (c *StandardClaims) VerifyAudience(cmp string, req bool) bool { return verifyAud([]string{c.Audience}, cmp, req) } -// Compares the exp claim against cmp. +// VerifyExpiresAt compares the exp claim against cmp. // If required is false, this method will return true if the value matches or is unset func (c *StandardClaims) VerifyExpiresAt(cmp int64, req bool) bool { return verifyExp(c.ExpiresAt, cmp, req) } -// Compares the iat claim against cmp. +// VerifyIssuedAt compares the iat claim against cmp. // If required is false, this method will return true if the value matches or is unset func (c *StandardClaims) VerifyIssuedAt(cmp int64, req bool) bool { return verifyIat(c.IssuedAt, cmp, req) } -// Compares the iss claim against cmp. +// VerifyIssuer compares the iss claim against cmp. // If required is false, this method will return true if the value matches or is unset func (c *StandardClaims) VerifyIssuer(cmp string, req bool) bool { return verifyIss(c.Issuer, cmp, req) } -// Compares the nbf claim against cmp. +// VerifyNotBefore compares the nbf claim against cmp. // If required is false, this method will return true if the value matches or is unset func (c *StandardClaims) VerifyNotBefore(cmp int64, req bool) bool { return verifyNbf(c.NotBefore, cmp, req) diff --git a/cmd/jwt/README.md b/cmd/jwt/README.md index 8c659e9c..4388e5f9 100644 --- a/cmd/jwt/README.md +++ b/cmd/jwt/README.md @@ -16,5 +16,4 @@ To simply display a token, use: You can install this tool with the following command: - go install github.com/golang-jwt/jwt/cmd/jwt - + go install github.com/golang-jwt/jwt/v4/cmd/jwt \ No newline at end of file diff --git a/cmd/jwt/args.go b/cmd/jwt/args.go deleted file mode 100644 index a5bba5b1..00000000 --- a/cmd/jwt/args.go +++ /dev/null @@ -1,23 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "strings" -) - -type ArgList map[string]string - -func (l ArgList) String() string { - data, _ := json.Marshal(l) - return string(data) -} - -func (l ArgList) Set(arg string) error { - parts := strings.SplitN(arg, "=", 2) - if len(parts) != 2 { - return fmt.Errorf("Invalid argument '%v'. Must use format 'key=value'. %v", arg, parts) - } - l[parts[0]] = parts[1] - return nil -} diff --git a/cmd/jwt/app.go b/cmd/jwt/main.go similarity index 82% rename from cmd/jwt/app.go rename to cmd/jwt/main.go index c8fd9361..8b16b22a 100644 --- a/cmd/jwt/app.go +++ b/cmd/jwt/main.go @@ -16,7 +16,7 @@ import ( "regexp" "strings" - "github.com/golang-jwt/jwt" + "github.com/golang-jwt/jwt/v4" ) var ( @@ -67,14 +67,14 @@ func start() error { return showToken() } else { 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?") } } // Helper func: Read input from specified file or stdin func loadData(p string) ([]byte, error) { if p == "" { - return nil, fmt.Errorf("No path specified") + return nil, fmt.Errorf("no path specified") } var rdr io.Reader @@ -117,7 +117,7 @@ func verifyToken() error { // get the token tokData, err := loadData(*flagVerify) if err != nil { - return fmt.Errorf("Couldn't read token: %v", err) + return fmt.Errorf("couldn't read token: %w", err) } // trim possible whitespace from token @@ -150,17 +150,17 @@ func verifyToken() error { // Print an error if we can't parse for some reason if err != nil { - return fmt.Errorf("Couldn't parse token: %v", err) + return fmt.Errorf("couldn't parse token: %w", err) } // Is token invalid? if !token.Valid { - return fmt.Errorf("Token is invalid") + return fmt.Errorf("token is invalid") } // Print the token details if err := printJSON(token.Claims); err != nil { - return fmt.Errorf("Failed to output claims: %v", err) + return fmt.Errorf("failed to output claims: %w", err) } return nil @@ -172,7 +172,7 @@ func signToken() error { // get the token data from command line arguments tokData, err := loadData(*flagSign) if err != nil { - return fmt.Errorf("Couldn't read token: %v", err) + return fmt.Errorf("couldn't read token: %w", err) } else if *flagDebug { fmt.Fprintf(os.Stderr, "Token: %v bytes", len(tokData)) } @@ -180,7 +180,7 @@ func signToken() error { // parse the JSON of the claims var claims jwt.MapClaims if err := json.Unmarshal(tokData, &claims); err != nil { - return fmt.Errorf("Couldn't parse claims JSON: %v", err) + return fmt.Errorf("couldn't parse claims JSON: %w", err) } // add command line claims @@ -194,13 +194,13 @@ func signToken() error { var key interface{} key, err = loadData(*flagKey) if err != nil { - return fmt.Errorf("Couldn't read key: %v", err) + return fmt.Errorf("couldn't read key: %w", err) } // get the signing alg alg := jwt.GetSigningMethod(*flagAlg) if alg == nil { - return fmt.Errorf("Couldn't find signing method: %v", *flagAlg) + return fmt.Errorf("couldn't find signing method: %v", *flagAlg) } // create a new token @@ -215,7 +215,7 @@ func signToken() error { if isEs() { if k, ok := key.([]byte); !ok { - return fmt.Errorf("Couldn't convert key data to key") + return fmt.Errorf("couldn't convert key data to key") } else { key, err = jwt.ParseECPrivateKeyFromPEM(k) if err != nil { @@ -224,7 +224,7 @@ func signToken() error { } } else if isRs() { if k, ok := key.([]byte); !ok { - return fmt.Errorf("Couldn't convert key data to key") + return fmt.Errorf("couldn't convert key data to key") } else { key, err = jwt.ParseRSAPrivateKeyFromPEM(k) if err != nil { @@ -233,7 +233,7 @@ func signToken() error { } } else if isEd() { if k, ok := key.([]byte); !ok { - return fmt.Errorf("Couldn't convert key data to key") + return fmt.Errorf("couldn't convert key data to key") } else { key, err = jwt.ParseEdPrivateKeyFromPEM(k) if err != nil { @@ -245,7 +245,7 @@ func signToken() error { if out, err := token.SignedString(key); err == nil { fmt.Println(out) } else { - return fmt.Errorf("Error signing token: %v", err) + return fmt.Errorf("error signing token: %w", err) } return nil @@ -256,7 +256,7 @@ func showToken() error { // get the token tokData, err := loadData(*flagShow) if err != nil { - return fmt.Errorf("Couldn't read token: %v", err) + return fmt.Errorf("couldn't read token: %w", err) } // trim possible whitespace from token @@ -267,18 +267,18 @@ func showToken() error { token, err := jwt.Parse(string(tokData), nil) if token == nil { - return fmt.Errorf("malformed token: %v", err) + return fmt.Errorf("malformed token: %w", err) } // Print the token details fmt.Println("Header:") if err := printJSON(token.Header); err != nil { - return fmt.Errorf("Failed to output header: %v", err) + return fmt.Errorf("failed to output header: %w", err) } fmt.Println("Claims:") if err := printJSON(token.Claims); err != nil { - return fmt.Errorf("Failed to output claims: %v", err) + return fmt.Errorf("failed to output claims: %w", err) } return nil @@ -295,3 +295,19 @@ func isRs() bool { func isEd() bool { return strings.HasPrefix(strings.ToUpper(*flagAlg), "Ed") } + +type ArgList map[string]string + +func (l ArgList) String() string { + data, _ := json.Marshal(l) + return string(data) +} + +func (l ArgList) Set(arg string) error { + parts := strings.SplitN(arg, "=", 2) + if len(parts) != 2 { + return fmt.Errorf("invalid argument '%v'. Must use format 'key=value'. %v", arg, parts) + } + l[parts[0]] = parts[1] + return nil +} diff --git a/ecdsa.go b/ecdsa.go index 15e23435..eac023fc 100644 --- a/ecdsa.go +++ b/ecdsa.go @@ -13,7 +13,7 @@ var ( ErrECDSAVerification = errors.New("crypto/ecdsa: verification error") ) -// Implements the ECDSA family of signing methods signing methods +// SigningMethodECDSA implements the ECDSA family of signing methods. // Expects *ecdsa.PrivateKey for signing and *ecdsa.PublicKey for verification type SigningMethodECDSA struct { Name string @@ -53,7 +53,7 @@ func (m *SigningMethodECDSA) Alg() string { return m.Name } -// Implements the Verify method from SigningMethod +// Verify implements token verification for the SigningMethod. // For this verify method, key must be an ecdsa.PublicKey struct func (m *SigningMethodECDSA) Verify(signingString, signature string, key interface{}) error { var err error @@ -95,7 +95,7 @@ func (m *SigningMethodECDSA) Verify(signingString, signature string, key interfa return ErrECDSAVerification } -// Implements the Sign method from SigningMethod +// Sign implements token signing for the SigningMethod. // For this signing method, key must be an ecdsa.PrivateKey struct func (m *SigningMethodECDSA) Sign(signingString string, key interface{}) (string, error) { // Get the key diff --git a/ecdsa_test.go b/ecdsa_test.go index c384608e..5b503eba 100644 --- a/ecdsa_test.go +++ b/ecdsa_test.go @@ -6,7 +6,7 @@ import ( "strings" "testing" - "github.com/golang-jwt/jwt" + "github.com/golang-jwt/jwt/v4" ) var ecdsaTestData = []struct { diff --git a/ecdsa_utils.go b/ecdsa_utils.go index db9f4be7..5700636d 100644 --- a/ecdsa_utils.go +++ b/ecdsa_utils.go @@ -8,11 +8,11 @@ import ( ) var ( - ErrNotECPublicKey = errors.New("Key is not a valid ECDSA public key") - ErrNotECPrivateKey = errors.New("Key is not a valid ECDSA private key") + ErrNotECPublicKey = errors.New("key is not a valid ECDSA public key") + ErrNotECPrivateKey = errors.New("key is not a valid ECDSA private key") ) -// Parse PEM encoded Elliptic Curve Private Key Structure +// ParseECPrivateKeyFromPEM parses a PEM encoded Elliptic Curve Private Key Structure func ParseECPrivateKeyFromPEM(key []byte) (*ecdsa.PrivateKey, error) { var err error @@ -39,7 +39,7 @@ func ParseECPrivateKeyFromPEM(key []byte) (*ecdsa.PrivateKey, error) { return pkey, nil } -// Parse PEM encoded PKCS1 or PKCS8 public key +// ParseECPublicKeyFromPEM parses a PEM encoded PKCS1 or PKCS8 public key func ParseECPublicKeyFromPEM(key []byte) (*ecdsa.PublicKey, error) { var err error diff --git a/ed25519.go b/ed25519.go index a2f8ddbe..9f40dc0c 100644 --- a/ed25519.go +++ b/ed25519.go @@ -10,7 +10,7 @@ var ( ErrEd25519Verification = errors.New("ed25519: verification error") ) -// Implements the EdDSA family +// SigningMethodEd25519 implements the EdDSA family. // Expects ed25519.PrivateKey for signing and ed25519.PublicKey for verification type SigningMethodEd25519 struct{} @@ -30,7 +30,7 @@ func (m *SigningMethodEd25519) Alg() string { return "EdDSA" } -// Implements the Verify method from SigningMethod +// Verify implements token verification for the SigningMethod. // For this verify method, key must be an ed25519.PublicKey func (m *SigningMethodEd25519) Verify(signingString, signature string, key interface{}) error { var err error @@ -59,7 +59,7 @@ func (m *SigningMethodEd25519) Verify(signingString, signature string, key inter return nil } -// Implements the Sign method from SigningMethod +// Sign implements token signing for the SigningMethod. // For this signing method, key must be an ed25519.PrivateKey func (m *SigningMethodEd25519) Sign(signingString string, key interface{}) (string, error) { var ed25519Key ed25519.PrivateKey diff --git a/ed25519_test.go b/ed25519_test.go index 26177e28..c7109c76 100644 --- a/ed25519_test.go +++ b/ed25519_test.go @@ -5,7 +5,7 @@ import ( "strings" "testing" - "github.com/golang-jwt/jwt" + "github.com/golang-jwt/jwt/v4" ) var ed25519TestData = []struct { diff --git a/ed25519_utils.go b/ed25519_utils.go index c6357275..cdb5e68e 100644 --- a/ed25519_utils.go +++ b/ed25519_utils.go @@ -9,11 +9,11 @@ import ( ) var ( - ErrNotEdPrivateKey = errors.New("Key is not a valid Ed25519 private key") - ErrNotEdPublicKey = errors.New("Key is not a valid Ed25519 public key") + ErrNotEdPrivateKey = errors.New("key is not a valid Ed25519 private key") + ErrNotEdPublicKey = errors.New("key is not a valid Ed25519 public key") ) -// Parse PEM-encoded Edwards curve private key +// ParseEdPrivateKeyFromPEM parses a PEM-encoded Edwards curve private key func ParseEdPrivateKeyFromPEM(key []byte) (crypto.PrivateKey, error) { var err error @@ -38,7 +38,7 @@ func ParseEdPrivateKeyFromPEM(key []byte) (crypto.PrivateKey, error) { return pkey, nil } -// Parse PEM-encoded Edwards curve public key +// ParseEdPublicKeyFromPEM parses a PEM-encoded Edwards curve public key func ParseEdPublicKeyFromPEM(key []byte) (crypto.PublicKey, error) { var err error diff --git a/errors.go b/errors.go index 1c93024a..f309878b 100644 --- a/errors.go +++ b/errors.go @@ -27,7 +27,7 @@ const ( ValidationErrorClaimsInvalid // Generic claims validation error ) -// Helper for constructing a ValidationError with a string error message +// NewValidationError is a helper for constructing a ValidationError with a string error message func NewValidationError(errorText string, errorFlags uint32) *ValidationError { return &ValidationError{ text: errorText, @@ -35,14 +35,14 @@ func NewValidationError(errorText string, errorFlags uint32) *ValidationError { } } -// The error from Parse if token is not valid +// ValidationError represents an error from Parse if token is not valid type ValidationError struct { Inner error // stores the error returned by external dependencies, i.e.: KeyFunc Errors uint32 // bitfield. see ValidationError... constants text string // errors that do not have a valid error just have text } -// Validation error is an error type +// Error is the implementation of the err interface. func (e ValidationError) Error() string { if e.Inner != nil { return e.Inner.Error() diff --git a/example_test.go b/example_test.go index 5be4a296..aae1c558 100644 --- a/example_test.go +++ b/example_test.go @@ -4,7 +4,7 @@ import ( "fmt" "time" - "github.com/golang-jwt/jwt" + "github.com/golang-jwt/jwt/v4" ) // Example (atypical) using the StandardClaims type by itself to parse a token. diff --git a/go.mod b/go.mod new file mode 100644 index 00000000..6bc53fdc --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/golang-jwt/jwt/v4 + +go 1.15 diff --git a/go.sum b/go.sum new file mode 100644 index 00000000..e69de29b diff --git a/hmac.go b/hmac.go index addbe5d4..011f68a2 100644 --- a/hmac.go +++ b/hmac.go @@ -6,7 +6,7 @@ import ( "errors" ) -// Implements the HMAC-SHA family of signing methods signing methods +// SigningMethodHMAC implements the HMAC-SHA family of signing methods. // Expects key type of []byte for both signing and validation type SigningMethodHMAC struct { Name string @@ -45,7 +45,7 @@ func (m *SigningMethodHMAC) Alg() string { return m.Name } -// Verify the signature of HSXXX tokens. Returns nil if the signature is valid. +// Verify implements token verification for the SigningMethod. Returns nil if the signature is valid. func (m *SigningMethodHMAC) Verify(signingString, signature string, key interface{}) error { // Verify the key is the right type keyBytes, ok := key.([]byte) @@ -77,7 +77,7 @@ func (m *SigningMethodHMAC) Verify(signingString, signature string, key interfac return nil } -// Implements the Sign method from SigningMethod for this signing method. +// Sign implements token signing for the SigningMethod. // Key must be []byte func (m *SigningMethodHMAC) Sign(signingString string, key interface{}) (string, error) { if keyBytes, ok := key.([]byte); ok { diff --git a/hmac_example_test.go b/hmac_example_test.go index 76576771..8972f1fb 100644 --- a/hmac_example_test.go +++ b/hmac_example_test.go @@ -5,7 +5,7 @@ import ( "io/ioutil" "time" - "github.com/golang-jwt/jwt" + "github.com/golang-jwt/jwt/v4" ) // For HMAC signing method, the key can be any []byte. It is recommended to generate diff --git a/hmac_test.go b/hmac_test.go index f45d5dbe..9ccf799c 100644 --- a/hmac_test.go +++ b/hmac_test.go @@ -5,7 +5,7 @@ import ( "strings" "testing" - "github.com/golang-jwt/jwt" + "github.com/golang-jwt/jwt/v4" ) var hmacTestData = []struct { diff --git a/http_example_test.go b/http_example_test.go index 3b6330b5..71a4cc8b 100644 --- a/http_example_test.go +++ b/http_example_test.go @@ -16,8 +16,8 @@ import ( "strings" "time" - "github.com/golang-jwt/jwt" - "github.com/golang-jwt/jwt/request" + "github.com/golang-jwt/jwt/v4" + "github.com/golang-jwt/jwt/v4/request" ) // location of the files used for signing and verification @@ -30,11 +30,6 @@ var ( verifyKey *rsa.PublicKey signKey *rsa.PrivateKey serverPort int - // storing sample username/password pairs - // don't do this on a real server - users = map[string]string{ - "test": "known", - } ) // read the key files before starting http handlers @@ -65,8 +60,6 @@ func init() { }() } -var start func() - func fatal(err error) { if err != nil { log.Fatal(err) @@ -199,11 +192,11 @@ func authHandler(w http.ResponseWriter, r *http.Request) { // only accessible with a valid token func restrictedHandler(w http.ResponseWriter, r *http.Request) { // Get token from request - token, err := request.ParseFromRequestWithClaims(r, request.OAuth2Extractor, &CustomClaimsExample{}, func(token *jwt.Token) (interface{}, error) { + token, err := request.ParseFromRequest(r, request.OAuth2Extractor, func(token *jwt.Token) (interface{}, error) { // since we only use the one private key to sign the tokens, // we also only use its public counter part to verify return verifyKey, nil - }) + }, request.WithClaims(&CustomClaimsExample{})) // If the token is missing or invalid, return error if err != nil { @@ -214,5 +207,4 @@ func restrictedHandler(w http.ResponseWriter, r *http.Request) { // Token is valid fmt.Fprintln(w, "Welcome,", token.Claims.(*CustomClaimsExample).Name) - return } diff --git a/map_claims.go b/map_claims.go index 72c79f92..7e00e753 100644 --- a/map_claims.go +++ b/map_claims.go @@ -6,7 +6,7 @@ import ( // "fmt" ) -// Claims type that uses the map[string]interface{} for JSON decoding +// MapClaims is a claims type that uses the map[string]interface{} for JSON decoding. // This is the default claims type if you don't supply one type MapClaims map[string]interface{} @@ -31,7 +31,7 @@ func (m MapClaims) VerifyAudience(cmp string, req bool) bool { return verifyAud(aud, cmp, req) } -// Compares the exp claim against cmp. +// VerifyExpiresAt compares the exp claim against cmp. // If required is false, this method will return true if the value matches or is unset func (m MapClaims) VerifyExpiresAt(cmp int64, req bool) bool { exp, ok := m["exp"] @@ -48,7 +48,7 @@ func (m MapClaims) VerifyExpiresAt(cmp int64, req bool) bool { return false } -// Compares the iat claim against cmp. +// VerifyIssuedAt compares the iat claim against cmp. // If required is false, this method will return true if the value matches or is unset func (m MapClaims) VerifyIssuedAt(cmp int64, req bool) bool { iat, ok := m["iat"] @@ -65,14 +65,14 @@ func (m MapClaims) VerifyIssuedAt(cmp int64, req bool) bool { return false } -// Compares the iss claim against cmp. +// VerifyIssuer compares the iss claim against cmp. // If required is false, this method will return true if the value matches or is unset func (m MapClaims) VerifyIssuer(cmp string, req bool) bool { iss, _ := m["iss"].(string) return verifyIss(iss, cmp, req) } -// Compares the nbf claim against cmp. +// VerifyNotBefore compares the nbf claim against cmp. // If required is false, this method will return true if the value matches or is unset func (m MapClaims) VerifyNotBefore(cmp int64, req bool) bool { nbf, ok := m["nbf"] @@ -89,7 +89,7 @@ func (m MapClaims) VerifyNotBefore(cmp int64, req bool) bool { return false } -// Validates time based claims "exp, iat, nbf". +// Valid calidates time based claims "exp, iat, nbf". // There is no accounting for clock skew. // As well, if any of the above claims are not in the token, it will still // be considered a valid claim. diff --git a/none.go b/none.go index f04d189d..f19835d2 100644 --- a/none.go +++ b/none.go @@ -1,6 +1,6 @@ package jwt -// Implements the none signing method. This is required by the spec +// SigningMethodNone implements the none signing method. This is required by the spec // but you probably should never use it. var SigningMethodNone *signingMethodNone diff --git a/none_test.go b/none_test.go index 9e3371a7..cbf6657e 100644 --- a/none_test.go +++ b/none_test.go @@ -4,7 +4,7 @@ import ( "strings" "testing" - "github.com/golang-jwt/jwt" + "github.com/golang-jwt/jwt/v4" ) var noneTestData = []struct { diff --git a/parser.go b/parser.go index d6901d9a..0c811f31 100644 --- a/parser.go +++ b/parser.go @@ -13,7 +13,7 @@ type Parser struct { SkipClaimsValidation bool // Skip claims validation during token parsing } -// Parse, validate, and return a token. +// Parse parses, validates, and returns a token. // keyFunc will receive the parsed token and should return the key for validating. // If everything is kosher, err will be nil func (p *Parser) Parse(tokenString string, keyFunc Keyfunc) (*Token, error) { @@ -87,12 +87,12 @@ func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyf return token, vErr } -// WARNING: Don't use this method unless you know what you're doing +// ParseUnverified parses the token but doesn't validate the signature. // -// This method parses the token but doesn't validate the signature. It's only -// ever useful in cases where you know the signature is valid (because it has -// been checked previously in the stack) and you want to extract values from -// it. +// WARNING: Don't use this method unless you know what you're doing. +// +// It's only ever useful in cases where you know the signature is valid (because it has +// been checked previously 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 { diff --git a/parser_test.go b/parser_test.go index abd245e8..870fd1cb 100644 --- a/parser_test.go +++ b/parser_test.go @@ -8,17 +8,17 @@ import ( "testing" "time" - "github.com/golang-jwt/jwt" - "github.com/golang-jwt/jwt/test" + "github.com/golang-jwt/jwt/v4" + "github.com/golang-jwt/jwt/v4/test" ) -var keyFuncError error = fmt.Errorf("error loading key") +var errKeyFuncError error = fmt.Errorf("error loading key") var ( jwtTestDefaultKey *rsa.PublicKey defaultKeyFunc jwt.Keyfunc = func(t *jwt.Token) (interface{}, error) { return jwtTestDefaultKey, nil } emptyKeyFunc jwt.Keyfunc = func(t *jwt.Token) (interface{}, error) { return nil, nil } - errorKeyFunc jwt.Keyfunc = func(t *jwt.Token) (interface{}, error) { return nil, keyFuncError } + errorKeyFunc jwt.Keyfunc = func(t *jwt.Token) (interface{}, error) { return nil, errKeyFuncError } nilKeyFunc jwt.Keyfunc = nil ) @@ -236,8 +236,8 @@ func TestParser_Parse(t *testing.T) { t.Errorf("[%v] Errors don't match expectation. %v != %v", data.name, e, data.errors) } - if err.Error() == keyFuncError.Error() && ve.Inner != keyFuncError { - t.Errorf("[%v] Inner error does not match expectation. %v != %v", data.name, ve.Inner, keyFuncError) + if err.Error() == errKeyFuncError.Error() && ve.Inner != errKeyFuncError { + t.Errorf("[%v] Inner error does not match expectation. %v != %v", data.name, ve.Inner, errKeyFuncError) } } } diff --git a/request/extractor.go b/request/extractor.go index 14414fe2..b297b8aa 100644 --- a/request/extractor.go +++ b/request/extractor.go @@ -10,15 +10,15 @@ var ( ErrNoTokenInRequest = errors.New("no token present in request") ) -// Interface for extracting a token from an HTTP request. +// Extractor is an interface for extracting a token from an HTTP request. // The ExtractToken method should return a token string or an error. // If no token is present, you must return ErrNoTokenInRequest. type Extractor interface { ExtractToken(*http.Request) (string, error) } -// Extractor for finding a token in a header. Looks at each specified -// header in order until there's a match +// HeaderExtractor is an extractor for finding a token in a header. +// Looks at each specified header in order until there's a match type HeaderExtractor []string func (e HeaderExtractor) ExtractToken(req *http.Request) (string, error) { @@ -31,7 +31,7 @@ func (e HeaderExtractor) ExtractToken(req *http.Request) (string, error) { return "", ErrNoTokenInRequest } -// Extract token from request arguments. This includes a POSTed form or +// ArgumentExtractor extracts a token from request arguments. This includes a POSTed form or // GET URL arguments. Argument names are tried in order until there's a match. // This extractor calls `ParseMultipartForm` on the request type ArgumentExtractor []string @@ -50,7 +50,7 @@ func (e ArgumentExtractor) ExtractToken(req *http.Request) (string, error) { return "", ErrNoTokenInRequest } -// Tries Extractors in order until one returns a token string or an error occurs +// MultiExtractor tries Extractors in order until one returns a token string or an error occurs type MultiExtractor []Extractor func (e MultiExtractor) ExtractToken(req *http.Request) (string, error) { @@ -65,7 +65,7 @@ func (e MultiExtractor) ExtractToken(req *http.Request) (string, error) { return "", ErrNoTokenInRequest } -// Wrap an Extractor in this to post-process the value before it's handed off. +// PostExtractionFilter wraps an Extractor in this to post-process the value before it's handed off. // See AuthorizationHeaderExtractor for an example type PostExtractionFilter struct { Extractor diff --git a/request/oauth2.go b/request/oauth2.go index 5948694a..5860a53f 100644 --- a/request/oauth2.go +++ b/request/oauth2.go @@ -13,14 +13,14 @@ func stripBearerPrefixFromTokenString(tok string) (string, error) { return tok, nil } -// Extract bearer token from Authorization header +// AuthorizationHeaderExtractor extracts a bearer token from Authorization header // Uses PostExtractionFilter to strip "Bearer " prefix from header var AuthorizationHeaderExtractor = &PostExtractionFilter{ HeaderExtractor{"Authorization"}, stripBearerPrefixFromTokenString, } -// Extractor for OAuth2 access tokens. Looks in 'Authorization' +// OAuth2Extractor is an Extractor for OAuth2 access tokens. Looks in 'Authorization' // header then 'access_token' argument for a token. var OAuth2Extractor = &MultiExtractor{ AuthorizationHeaderExtractor, diff --git a/request/request.go b/request/request.go index 32b27297..79f53f4e 100644 --- a/request/request.go +++ b/request/request.go @@ -3,10 +3,10 @@ package request import ( "net/http" - "github.com/golang-jwt/jwt" + "github.com/golang-jwt/jwt/v4" ) -// Extract and parse a JWT token from an HTTP request. +// ParseFromRequest extracts and parses a JWT token from an HTTP request. // This behaves the same as Parse, but accepts a request and an extractor // instead of a token string. The Extractor interface allows you to define // the logic for extracting a token. Several useful implementations are provided. @@ -39,8 +39,9 @@ func ParseFromRequest(req *http.Request, extractor Extractor, keyFunc jwt.Keyfun return p.parser.ParseWithClaims(tokenString, p.claims, keyFunc) } -// ParseFromRequest but with custom Claims type -// DEPRECATED: use ParseFromRequest and the WithClaims option +// ParseFromRequestWithClaims is an alias for ParseFromRequest but with custom Claims type. +// +// Deprecated: use ParseFromRequest and the WithClaims option func ParseFromRequestWithClaims(req *http.Request, extractor Extractor, claims jwt.Claims, keyFunc jwt.Keyfunc) (token *jwt.Token, err error) { return ParseFromRequest(req, extractor, keyFunc, WithClaims(claims)) } @@ -54,14 +55,14 @@ type fromRequestParser struct { type ParseFromRequestOption func(*fromRequestParser) -// Parse with custom claims +// WithClaims parses with custom claims func WithClaims(claims jwt.Claims) ParseFromRequestOption { return func(p *fromRequestParser) { p.claims = claims } } -// Parse using a custom parser +// WithParser parses using a custom parser func WithParser(parser *jwt.Parser) ParseFromRequestOption { return func(p *fromRequestParser) { p.parser = parser diff --git a/request/request_test.go b/request/request_test.go index fd3a850b..687392c7 100644 --- a/request/request_test.go +++ b/request/request_test.go @@ -8,8 +8,8 @@ import ( "strings" "testing" - "github.com/golang-jwt/jwt" - "github.com/golang-jwt/jwt/test" + "github.com/golang-jwt/jwt/v4" + "github.com/golang-jwt/jwt/v4/test" ) var requestTestData = []struct { diff --git a/rsa.go b/rsa.go index e4caf1ca..b910b19c 100644 --- a/rsa.go +++ b/rsa.go @@ -6,7 +6,7 @@ import ( "crypto/rsa" ) -// Implements the RSA family of signing methods signing methods +// SigningMethodRSA implements the RSA family of signing methods. // Expects *rsa.PrivateKey for signing and *rsa.PublicKey for validation type SigningMethodRSA struct { Name string @@ -44,7 +44,7 @@ func (m *SigningMethodRSA) Alg() string { return m.Name } -// Implements the Verify method from SigningMethod +// Verify implements token verification for the SigningMethod // For this signing method, must be an *rsa.PublicKey structure. func (m *SigningMethodRSA) Verify(signingString, signature string, key interface{}) error { var err error @@ -73,7 +73,7 @@ func (m *SigningMethodRSA) Verify(signingString, signature string, key interface return rsa.VerifyPKCS1v15(rsaKey, m.Hash, hasher.Sum(nil), sig) } -// Implements the Sign method from SigningMethod +// Sign implements token signing for the SigningMethod // For this signing method, must be an *rsa.PrivateKey structure. func (m *SigningMethodRSA) Sign(signingString string, key interface{}) (string, error) { var rsaKey *rsa.PrivateKey diff --git a/rsa_pss.go b/rsa_pss.go index c0147086..5a8502fe 100644 --- a/rsa_pss.go +++ b/rsa_pss.go @@ -8,7 +8,7 @@ import ( "crypto/rsa" ) -// Implements the RSAPSS family of signing methods signing methods +// SigningMethodRSAPSS implements the RSAPSS family of signing methods signing methods type SigningMethodRSAPSS struct { *SigningMethodRSA Options *rsa.PSSOptions @@ -79,7 +79,7 @@ func init() { }) } -// Implements the Verify method from SigningMethod +// Verify implements token verification for the SigningMethod. // For this verify method, key must be an rsa.PublicKey struct func (m *SigningMethodRSAPSS) Verify(signingString, signature string, key interface{}) error { var err error @@ -113,7 +113,7 @@ func (m *SigningMethodRSAPSS) Verify(signingString, signature string, key interf return rsa.VerifyPSS(rsaKey, m.Hash, hasher.Sum(nil), sig, opts) } -// Implements the Sign method from SigningMethod +// Sign implements token signing for the SigningMethod. // For this signing method, key must be an rsa.PrivateKey struct func (m *SigningMethodRSAPSS) Sign(signingString string, key interface{}) (string, error) { var rsaKey *rsa.PrivateKey diff --git a/rsa_pss_test.go b/rsa_pss_test.go index d1cafe71..716500eb 100644 --- a/rsa_pss_test.go +++ b/rsa_pss_test.go @@ -9,8 +9,8 @@ import ( "testing" "time" - "github.com/golang-jwt/jwt" - "github.com/golang-jwt/jwt/test" + "github.com/golang-jwt/jwt/v4" + "github.com/golang-jwt/jwt/v4/test" ) var rsaPSSTestData = []struct { diff --git a/rsa_test.go b/rsa_test.go index fba45e07..e0c66630 100644 --- a/rsa_test.go +++ b/rsa_test.go @@ -5,7 +5,7 @@ import ( "strings" "testing" - "github.com/golang-jwt/jwt" + "github.com/golang-jwt/jwt/v4" ) var rsaTestData = []struct { diff --git a/rsa_utils.go b/rsa_utils.go index 14c78c29..1966c450 100644 --- a/rsa_utils.go +++ b/rsa_utils.go @@ -8,12 +8,12 @@ import ( ) var ( - ErrKeyMustBePEMEncoded = errors.New("Invalid Key: Key must be a PEM encoded PKCS1 or PKCS8 key") - ErrNotRSAPrivateKey = errors.New("Key is not a valid RSA private key") - ErrNotRSAPublicKey = errors.New("Key is not a valid RSA public key") + ErrKeyMustBePEMEncoded = errors.New("invalid key: Key must be a PEM encoded PKCS1 or PKCS8 key") + ErrNotRSAPrivateKey = errors.New("key is not a valid RSA private key") + ErrNotRSAPublicKey = errors.New("key is not a valid RSA public key") ) -// Parse PEM encoded PKCS1 or PKCS8 private key +// ParseRSAPrivateKeyFromPEM parses a PEM encoded PKCS1 or PKCS8 private key func ParseRSAPrivateKeyFromPEM(key []byte) (*rsa.PrivateKey, error) { var err error @@ -39,7 +39,11 @@ func ParseRSAPrivateKeyFromPEM(key []byte) (*rsa.PrivateKey, error) { return pkey, nil } -// Parse PEM encoded PKCS1 or PKCS8 private key protected with password +// ParseRSAPrivateKeyFromPEMWithPassword parses a PEM encoded PKCS1 or PKCS8 private key protected with password +// +// Deprecated: This function is deprecated and should not be used anymore. It uses the deprecated x509.DecryptPEMBlock +// function, which was deprecated since RFC 1423 is regarded insecure by design. Unfortunately, there is no alternative +// in the Go standard library for now. See https://github.com/golang/go/issues/8860. func ParseRSAPrivateKeyFromPEMWithPassword(key []byte, password string) (*rsa.PrivateKey, error) { var err error @@ -71,7 +75,7 @@ func ParseRSAPrivateKeyFromPEMWithPassword(key []byte, password string) (*rsa.Pr return pkey, nil } -// Parse PEM encoded PKCS1 or PKCS8 public key +// ParseRSAPublicKeyFromPEM parses a PEM encoded PKCS1 or PKCS8 public key func ParseRSAPublicKeyFromPEM(key []byte) (*rsa.PublicKey, error) { var err error diff --git a/signing_method.go b/signing_method.go index ed1f212b..3269170f 100644 --- a/signing_method.go +++ b/signing_method.go @@ -7,14 +7,14 @@ import ( var signingMethods = map[string]func() SigningMethod{} var signingMethodLock = new(sync.RWMutex) -// Implement SigningMethod to add new methods for signing or verifying tokens. +// SigningMethod can be used add new methods for signing or verifying tokens. type SigningMethod interface { Verify(signingString, signature string, key interface{}) error // Returns nil if signature is valid Sign(signingString string, key interface{}) (string, error) // Returns encoded signature or error Alg() string // returns the alg identifier for this method (example: 'HS256') } -// Register the "alg" name and a factory function for signing method. +// RegisterSigningMethod registers the "alg" name and a factory function for signing method. // This is typically done during init() in the method's implementation func RegisterSigningMethod(alg string, f func() SigningMethod) { signingMethodLock.Lock() @@ -23,7 +23,7 @@ func RegisterSigningMethod(alg string, f func() SigningMethod) { signingMethods[alg] = f } -// Get a signing method from an "alg" string +// GetSigningMethod retrieves a signing method from an "alg" string func GetSigningMethod(alg string) (method SigningMethod) { signingMethodLock.RLock() defer signingMethodLock.RUnlock() diff --git a/staticcheck.conf b/staticcheck.conf new file mode 100644 index 00000000..53745d51 --- /dev/null +++ b/staticcheck.conf @@ -0,0 +1 @@ +checks = ["all", "-ST1000", "-ST1003", "-ST1016", "-ST1023"] diff --git a/test/helpers.go b/test/helpers.go index ef54aeab..c541edb1 100644 --- a/test/helpers.go +++ b/test/helpers.go @@ -4,7 +4,7 @@ import ( "crypto/rsa" "io/ioutil" - "github.com/golang-jwt/jwt" + "github.com/golang-jwt/jwt/v4" ) func LoadRSAPrivateKeyFromDisk(location string) *rsa.PrivateKey { diff --git a/token.go b/token.go index 6b30ced1..b896acb0 100644 --- a/token.go +++ b/token.go @@ -12,13 +12,13 @@ import ( // server uses a different time zone than your tokens. var TimeFunc = time.Now -// Parse methods use this callback function to supply +// Keyfunc will be used by the Parse methods as a callback function to supply // the key for verification. The function receives the parsed, // but unverified Token. This allows you to use properties in the // Header of the token (such as `kid`) to identify which key to use. type Keyfunc func(*Token) (interface{}, error) -// A JWT Token. Different fields will be used depending on whether you're +// Token represents a JWT Token. Different fields will be used depending on whether you're // creating or parsing/verifying a token. type Token struct { Raw string // The raw token. Populated when you Parse a token @@ -29,7 +29,7 @@ type Token struct { Valid bool // Is the token valid? Populated when you Parse/Verify a token } -// Create a new Token. Takes a signing method +// New creates a new Token. Takes a signing method func New(method SigningMethod) *Token { return NewWithClaims(method, MapClaims{}) } @@ -45,7 +45,7 @@ func NewWithClaims(method SigningMethod, claims Claims) *Token { } } -// Get the complete, signed token +// SignedString retrieves the complete, signed token func (t *Token) SignedString(key interface{}) (string, error) { var sig, sstr string var err error @@ -58,7 +58,7 @@ func (t *Token) SignedString(key interface{}) (string, error) { return strings.Join([]string{sstr, sig}, "."), nil } -// Generate the signing string. This is the +// SigningString generates the signing string. This is the // most expensive part of the whole deal. Unless you // need this for something special, just go straight for // the SignedString. @@ -82,7 +82,7 @@ func (t *Token) SigningString() (string, error) { return strings.Join(parts, "."), nil } -// Parse, validate, and return a token. +// Parse parses, validates, and returns a token. // keyFunc will receive the parsed token and should return the key for validating. // If everything is kosher, err will be nil func Parse(tokenString string, keyFunc Keyfunc) (*Token, error) { @@ -93,12 +93,18 @@ func ParseWithClaims(tokenString string, claims Claims, keyFunc Keyfunc) (*Token return new(Parser).ParseWithClaims(tokenString, claims, keyFunc) } -// Encode JWT specific base64url encoding with padding stripped +// EncodeSegment encodes a JWT specific base64url encoding with padding stripped +// +// Deprecated: In a future release, we will demote this function to a non-exported function, since it +// should only be used internally func EncodeSegment(seg []byte) string { return base64.RawURLEncoding.EncodeToString(seg) } -// Decode JWT specific base64url encoding with padding stripped +// DecodeSegment decodes a JWT specific base64url encoding with padding stripped +// +// Deprecated: In a future release, we will demote this function to a non-exported function, since it +// should only be used internally func DecodeSegment(seg string) ([]byte, error) { return base64.RawURLEncoding.DecodeString(seg) }