diff --git a/.golangci.yml b/.golangci.yml index c138617a8..c241475ce 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -33,11 +33,6 @@ issues: - staticcheck text: 'SA1019: "github.com/rclone/rclone/cmd/serve/httplib" is deprecated' - # TODO: Remove if/when this is fixed by merging PR #6277. - - linters: - - staticcheck - text: 'SA1019: "golang.org/x/oauth2/jws" is deprecated' - run: # timeout for analysis, e.g. 30s, 5m, default is 1m timeout: 10m diff --git a/backend/box/box.go b/backend/box/box.go index e0cfbed07..474443d39 100644 --- a/backend/box/box.go +++ b/backend/box/box.go @@ -27,6 +27,7 @@ import ( "sync/atomic" "time" + "github.com/golang-jwt/jwt/v4" "github.com/rclone/rclone/backend/box/api" "github.com/rclone/rclone/fs" "github.com/rclone/rclone/fs/config" @@ -45,7 +46,6 @@ import ( "github.com/rclone/rclone/lib/rest" "github.com/youmark/pkcs8" "golang.org/x/oauth2" - "golang.org/x/oauth2/jws" ) const ( @@ -76,6 +76,11 @@ var ( } ) +type boxCustomClaims struct { + jwt.RegisteredClaims + BoxSubType string `json:"box_sub_type,omitempty"` +} + // Register with Fs func init() { fs.Register(&fs.RegInfo{ @@ -178,7 +183,7 @@ func refreshJWTToken(ctx context.Context, jsonFile string, boxSubType string, na signingHeaders := getSigningHeaders(boxConfig) queryParams := getQueryParams(boxConfig) client := fshttp.NewClient(ctx) - err = jwtutil.Config("box", name, claims, signingHeaders, queryParams, privateKey, m, client) + err = jwtutil.Config("box", name, tokenURL, *claims, signingHeaders, queryParams, privateKey, m, client) return err } @@ -194,34 +199,29 @@ func getBoxConfig(configFile string) (boxConfig *api.ConfigJSON, err error) { return boxConfig, nil } -func getClaims(boxConfig *api.ConfigJSON, boxSubType string) (claims *jws.ClaimSet, err error) { +func getClaims(boxConfig *api.ConfigJSON, boxSubType string) (claims *boxCustomClaims, err error) { val, err := jwtutil.RandomHex(20) if err != nil { return nil, fmt.Errorf("box: failed to generate random string for jti: %w", err) } - claims = &jws.ClaimSet{ - Iss: boxConfig.BoxAppSettings.ClientID, - Sub: boxConfig.EnterpriseID, - Aud: tokenURL, - Exp: time.Now().Add(time.Second * 45).Unix(), - PrivateClaims: map[string]interface{}{ - "box_sub_type": boxSubType, - "aud": tokenURL, - "jti": val, + claims = &boxCustomClaims{ + RegisteredClaims: jwt.RegisteredClaims{ + ID: val, + Issuer: boxConfig.BoxAppSettings.ClientID, + Subject: boxConfig.EnterpriseID, + Audience: jwt.ClaimStrings{tokenURL}, + ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Second * 45)), }, + BoxSubType: boxSubType, } - return claims, nil } -func getSigningHeaders(boxConfig *api.ConfigJSON) *jws.Header { - signingHeaders := &jws.Header{ - Algorithm: "RS256", - Typ: "JWT", - KeyID: boxConfig.BoxAppSettings.AppAuth.PublicKeyID, +func getSigningHeaders(boxConfig *api.ConfigJSON) map[string]interface{} { + signingHeaders := map[string]interface{}{ + "kid": boxConfig.BoxAppSettings.AppAuth.PublicKeyID, } - return signingHeaders } diff --git a/go.mod b/go.mod index ddc395619..7cad6cb8a 100644 --- a/go.mod +++ b/go.mod @@ -141,7 +141,7 @@ require ( require ( github.com/Microsoft/go-winio v0.5.2 // indirect github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 - github.com/golang-jwt/jwt/v4 v4.5.0 // indirect + github.com/golang-jwt/jwt/v4 v4.5.0 github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/pkg/xattr v0.4.9 diff --git a/lib/jwtutil/jwtutil.go b/lib/jwtutil/jwtutil.go index da3b0e7cb..e5f0dbd8e 100644 --- a/lib/jwtutil/jwtutil.go +++ b/lib/jwtutil/jwtutil.go @@ -14,12 +14,12 @@ import ( "strings" "time" + "github.com/golang-jwt/jwt/v4" "github.com/rclone/rclone/fs" "github.com/rclone/rclone/fs/config/configmap" "github.com/rclone/rclone/lib/oauthutil" "golang.org/x/oauth2" - "golang.org/x/oauth2/jws" ) // RandomHex creates a random string of the given length @@ -32,12 +32,16 @@ func RandomHex(n int) (string, error) { } // Config configures rclone using JWT -func Config(id, name string, claims *jws.ClaimSet, header *jws.Header, queryParams map[string]string, privateKey *rsa.PrivateKey, m configmap.Mapper, client *http.Client) (err error) { - payload, err := jws.Encode(header, claims, privateKey) +func Config(id, name, url string, claims jwt.Claims, headerParams map[string]interface{}, queryParams map[string]string, privateKey *rsa.PrivateKey, m configmap.Mapper, client *http.Client) (err error) { + jwtToken := jwt.NewWithClaims(jwt.SigningMethodRS256, claims) + for key, value := range headerParams { + jwtToken.Header[key] = value + } + payload, err := jwtToken.SignedString(privateKey) if err != nil { return fmt.Errorf("jwtutil: failed to encode payload: %w", err) } - req, err := http.NewRequest("POST", claims.Aud, nil) + req, err := http.NewRequest("POST", url, nil) if err != nil { return fmt.Errorf("jwtutil: failed to create new request: %w", err) } @@ -49,7 +53,7 @@ func Config(id, name string, claims *jws.ClaimSet, header *jws.Header, queryPara } queryString := q.Encode() - req, err = http.NewRequest("POST", claims.Aud, bytes.NewBuffer([]byte(queryString))) + req, err = http.NewRequest("POST", url, bytes.NewBuffer([]byte(queryString))) if err != nil { return fmt.Errorf("jwtutil: failed to create new request: %w", err) }