lib/jwtutil: upgrade jwt-go (golang-jwt) from v4 to v5

This commit is contained in:
albertony 2025-01-15 16:33:18 +01:00
parent f4d7df1511
commit 8c3ea2842c
2 changed files with 27 additions and 127 deletions

View File

@ -1,18 +1,18 @@
package jwtutil package jwtutil
import ( import (
"crypto/subtle"
"fmt"
"time" "time"
"github.com/golang-jwt/jwt/v4" "github.com/golang-jwt/jwt/v5"
) )
// The following is the StandardClaims implementation from jwt-go v4, // The following is the declaration of the StandardClaims type from jwt-go v4
// where it was marked as deprecated before removed in v5. Some small // (https://github.com/golang-jwt/jwt/blob/v4/claims.go), where it was marked
// adjustments the original code have been made, to satisfy linters etc. // as deprecated before later removed in v5. It was distributed under the terms
// The type has also been renamed to LegacyStandardClaims to avoid confusion. // of the MIT License (https://github.com/golang-jwt/jwt/blob/v4/LICENSE), with
// Source: https://github.com/golang-jwt/jwt/blob/v4/claims.go // the copy right notice included below. We have renamed the type to
// LegacyStandardClaims to avoid confusion, and made it compatible with
// jwt-go v5 by implementing functions to satisfy the changed Claims interface.
// Copyright (c) 2012 Dave Grijalva // Copyright (c) 2012 Dave Grijalva
// Copyright (c) 2021 golang-jwt maintainers // Copyright (c) 2021 golang-jwt maintainers
@ -53,132 +53,32 @@ type LegacyStandardClaims struct {
Subject string `json:"sub,omitempty"` Subject string `json:"sub,omitempty"`
} }
// Valid validates time based claims "exp, iat, nbf". There is no accounting for clock skew. // GetExpirationTime implements the Claims interface.
// As well, if any of the above claims are not in the token, it will still func (c LegacyStandardClaims) GetExpirationTime() (*jwt.NumericDate, error) {
// be considered a valid claim. return jwt.NewNumericDate(time.Unix(c.ExpiresAt, 0)), nil
func (c LegacyStandardClaims) Valid() error {
vErr := new(jwt.ValidationError)
now := jwt.TimeFunc().Unix()
// The claims below are optional, by default, so if they are set to the
// default value in Go, let's not fail the verification for them.
if !c.VerifyExpiresAt(now, false) {
delta := time.Unix(now, 0).Sub(time.Unix(c.ExpiresAt, 0))
vErr.Inner = fmt.Errorf("%s by %s", jwt.ErrTokenExpired, delta)
vErr.Errors |= jwt.ValidationErrorExpired
} }
if !c.VerifyIssuedAt(now, false) { // GetIssuedAt implements the Claims interface.
vErr.Inner = jwt.ErrTokenUsedBeforeIssued func (c LegacyStandardClaims) GetIssuedAt() (*jwt.NumericDate, error) {
vErr.Errors |= jwt.ValidationErrorIssuedAt return jwt.NewNumericDate(time.Unix(c.IssuedAt, 0)), nil
} }
if !c.VerifyNotBefore(now, false) { // GetNotBefore implements the Claims interface.
vErr.Inner = jwt.ErrTokenNotValidYet func (c LegacyStandardClaims) GetNotBefore() (*jwt.NumericDate, error) {
vErr.Errors |= jwt.ValidationErrorNotValidYet return jwt.NewNumericDate(time.Unix(c.NotBefore, 0)), nil
} }
if vErr.Errors == 0 { // GetIssuer implements the Claims interface.
return nil func (c LegacyStandardClaims) GetIssuer() (string, error) {
return c.Issuer, nil
} }
return vErr // GetSubject implements the Claims interface.
func (c LegacyStandardClaims) GetSubject() (string, error) {
return c.Subject, nil
} }
// VerifyAudience compares the aud claim against cmp. // GetAudience implements the Claims interface.
// If required is false, this method will return true if the value matches or is unset func (c LegacyStandardClaims) GetAudience() (jwt.ClaimStrings, error) {
func (c *LegacyStandardClaims) VerifyAudience(cmp string, req bool) bool { return []string{c.Audience}, nil
return verifyAud([]string{c.Audience}, cmp, req)
}
// VerifyExpiresAt compares the exp claim against cmp (cmp < exp).
// If req is false, it will return true, if exp is unset.
func (c *LegacyStandardClaims) VerifyExpiresAt(cmp int64, req bool) bool {
if c.ExpiresAt == 0 {
return verifyExp(nil, time.Unix(cmp, 0), req)
}
t := time.Unix(c.ExpiresAt, 0)
return verifyExp(&t, time.Unix(cmp, 0), req)
}
// VerifyIssuedAt compares the iat claim against cmp (cmp >= iat).
// If req is false, it will return true, if iat is unset.
func (c *LegacyStandardClaims) VerifyIssuedAt(cmp int64, req bool) bool {
if c.IssuedAt == 0 {
return verifyIat(nil, time.Unix(cmp, 0), req)
}
t := time.Unix(c.IssuedAt, 0)
return verifyIat(&t, time.Unix(cmp, 0), req)
}
// VerifyNotBefore compares the nbf claim against cmp (cmp >= nbf).
// If req is false, it will return true, if nbf is unset.
func (c *LegacyStandardClaims) VerifyNotBefore(cmp int64, req bool) bool {
if c.NotBefore == 0 {
return verifyNbf(nil, time.Unix(cmp, 0), req)
}
t := time.Unix(c.NotBefore, 0)
return verifyNbf(&t, time.Unix(cmp, 0), req)
}
// 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 *LegacyStandardClaims) VerifyIssuer(cmp string, req bool) bool {
return verifyIss(c.Issuer, cmp, req)
}
// ----- helpers
func verifyAud(aud []string, cmp string, required bool) bool {
if len(aud) == 0 {
return !required
}
// use a var here to keep constant time compare when looping over a number of claims
result := false
var stringClaims string
for _, a := range aud {
if subtle.ConstantTimeCompare([]byte(a), []byte(cmp)) != 0 {
result = true
}
stringClaims += a
}
// case where "" is sent in one or many aud claims
if len(stringClaims) == 0 {
return !required
}
return result
}
func verifyExp(exp *time.Time, now time.Time, required bool) bool {
if exp == nil {
return !required
}
return now.Before(*exp)
}
func verifyIat(iat *time.Time, now time.Time, required bool) bool {
if iat == nil {
return !required
}
return now.After(*iat) || now.Equal(*iat)
}
func verifyNbf(nbf *time.Time, now time.Time, required bool) bool {
if nbf == nil {
return !required
}
return now.After(*nbf) || now.Equal(*nbf)
}
func verifyIss(iss string, cmp string, required bool) bool {
if iss == "" {
return !required
}
return subtle.ConstantTimeCompare([]byte(iss), []byte(cmp)) != 0
} }

View File

@ -14,7 +14,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/golang-jwt/jwt/v4" "github.com/golang-jwt/jwt/v5"
"github.com/rclone/rclone/fs" "github.com/rclone/rclone/fs"
"github.com/rclone/rclone/fs/config/configmap" "github.com/rclone/rclone/fs/config/configmap"
"github.com/rclone/rclone/lib/oauthutil" "github.com/rclone/rclone/lib/oauthutil"