diff --git a/go.mod b/go.mod index 8d76667bf5..6202fd4ef7 100644 --- a/go.mod +++ b/go.mod @@ -98,7 +98,7 @@ require ( golang.org/x/crypto/x509roots/fallback v0.0.0-20240806160748-b2d3a6a4b4d3 golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 golang.org/x/net v0.28.0 - golang.org/x/oauth2 v0.22.0 + golang.org/x/oauth2 v0.23.0 golang.org/x/sync v0.8.0 golang.org/x/text v0.17.0 golang.org/x/tools v0.24.0 diff --git a/go.sum b/go.sum index c2c2032712..57e484f6eb 100644 --- a/go.sum +++ b/go.sum @@ -750,8 +750,8 @@ golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= -golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= +golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/tariff/edf-tempo.go b/tariff/edf-tempo.go index c31465d3f8..6365f45a08 100644 --- a/tariff/edf-tempo.go +++ b/tariff/edf-tempo.go @@ -93,11 +93,11 @@ func (t *EdfTempo) RefreshToken(_ *oauth2.Token) (*oauth2.Token, error) { "Accept": request.JSONContent, }) - var res oauth.Token + var res oauth2.Token client := request.NewHelper(t.log) err := client.DoJSON(req, &res) - return (*oauth2.Token)(&res), err + return util.TokenWithExpiry(&res), err } func (t *EdfTempo) run(done chan error) { diff --git a/util/oauth/token.go b/util/oauth/token.go deleted file mode 100644 index 4f33034e42..0000000000 --- a/util/oauth/token.go +++ /dev/null @@ -1,29 +0,0 @@ -package oauth - -import ( - "encoding/json" - "time" - - "golang.org/x/oauth2" -) - -// Token is an OAuth token that supports the expires_in attribute -type Token oauth2.Token - -func (t *Token) UnmarshalJSON(data []byte) error { - var o struct { - oauth2.Token - ExpiresIn int64 `json:"expires_in,omitempty"` - } - - err := json.Unmarshal(data, &o) - if err == nil { - *t = (Token)(o.Token) - - if o.Expiry.IsZero() && o.ExpiresIn != 0 { - t.Expiry = time.Now().Add(time.Second * time.Duration(o.ExpiresIn)) - } - } - - return err -} diff --git a/util/oauth/token_test.go b/util/oauth/token_test.go deleted file mode 100644 index c64a31857a..0000000000 --- a/util/oauth/token_test.go +++ /dev/null @@ -1,31 +0,0 @@ -package oauth - -import ( - "encoding/json" - "testing" -) - -func TestUnmarshalJSON(t *testing.T) { - var tok Token - data := `{"access_token":"access","refresh_token":"refresh","token_type":"bearer","expires_in":3600}` - - if err := json.Unmarshal([]byte(data), &tok); err != nil { - t.Error(err) - } - - if tok.AccessToken != "access" { - t.Error("AccessToken") - } - - if tok.RefreshToken != "refresh" { - t.Error("RefreshToken") - } - - if tok.TokenType != "bearer" { - t.Error("TokenType") - } - - if tok.Expiry.IsZero() { - t.Error("Expiry") - } -} diff --git a/util/token.go b/util/token.go new file mode 100644 index 0000000000..17a366af0c --- /dev/null +++ b/util/token.go @@ -0,0 +1,14 @@ +package util + +import ( + "time" + + "golang.org/x/oauth2" +) + +func TokenWithExpiry(token *oauth2.Token) *oauth2.Token { + if token != nil && token.Expiry.IsZero() && token.ExpiresIn != 0 { + token.Expiry = time.Now().Add(time.Second * time.Duration(token.ExpiresIn)) + } + return token +} diff --git a/vehicle/bluelink/identity.go b/vehicle/bluelink/identity.go index 4aaf711b26..b1edd9602f 100644 --- a/vehicle/bluelink/identity.go +++ b/vehicle/bluelink/identity.go @@ -275,7 +275,7 @@ func (v *Identity) bluelinkLogin(cookieClient *request.Helper, user, password st return accCode, err } -func (v *Identity) exchangeCode(accCode string) (oauth.Token, error) { +func (v *Identity) exchangeCode(accCode string) (*oauth2.Token, error) { headers := map[string]string{ "Authorization": "Basic " + v.config.BasicToken, "Content-type": "application/x-www-form-urlencoded", @@ -288,14 +288,12 @@ func (v *Identity) exchangeCode(accCode string) (oauth.Token, error) { "code": {accCode}, } - var token oauth.Token + var token oauth2.Token - req, err := request.New(http.MethodPost, v.config.URI+TokenURL, strings.NewReader(data.Encode()), headers) - if err == nil { - err = v.DoJSON(req, &token) - } + req, _ := request.New(http.MethodPost, v.config.URI+TokenURL, strings.NewReader(data.Encode()), headers) + err := v.DoJSON(req, &token) - return token, err + return util.TokenWithExpiry(&token), err } // RefreshToken implements oauth.TokenRefresher @@ -314,12 +312,12 @@ func (v *Identity) RefreshToken(token *oauth2.Token) (*oauth2.Token, error) { req, err := request.New(http.MethodPost, v.config.URI+TokenURL, strings.NewReader(data.Encode()), headers) - var res oauth.Token + var res oauth2.Token if err == nil { err = v.DoJSON(req, &res) } - return (*oauth2.Token)(&res), err + return util.TokenWithExpiry(&res), err } func (v *Identity) Login(user, password, language string) (err error) { @@ -347,9 +345,9 @@ func (v *Identity) Login(user, password, language string) (err error) { } if err == nil { - var token oauth.Token + var token *oauth2.Token if token, err = v.exchangeCode(code); err == nil { - v.TokenSource = oauth.RefreshTokenSource((*oauth2.Token)(&token), v) + v.TokenSource = oauth.RefreshTokenSource(token, v) } } diff --git a/vehicle/bmw/identity.go b/vehicle/bmw/identity.go index f6ae9ecaa7..03faf3f588 100644 --- a/vehicle/bmw/identity.go +++ b/vehicle/bmw/identity.go @@ -138,12 +138,12 @@ func (v *Identity) retrieveToken(data url.Values) (*oauth2.Token, error) { "Authorization": v.region.Token.Authorization, }) - var tok oauth.Token + var tok oauth2.Token if err == nil { err = v.DoJSON(req, &tok) } - return (*oauth2.Token)(&tok), err + return util.TokenWithExpiry(&tok), err } func (v *Identity) RefreshToken(token *oauth2.Token) (*oauth2.Token, error) { diff --git a/vehicle/ford/autonomic/identity.go b/vehicle/ford/autonomic/identity.go index 2f062b37a4..ebfa69b601 100644 --- a/vehicle/ford/autonomic/identity.go +++ b/vehicle/ford/autonomic/identity.go @@ -58,13 +58,13 @@ func (v *Identity) exchange(token *oauth2.Token) (*oauth2.Token, error) { "subject_token_type": {"urn:ietf:params:oauth:token-type:jwt"}, } - var auto *oauth.Token + var res *oauth2.Token req, err := request.New(http.MethodPost, OAuth2Config.Endpoint.TokenURL, strings.NewReader(data.Encode()), request.URLEncoding) if err == nil { - err = v.DoJSON(req, &auto) + err = v.DoJSON(req, &res) } - return (*oauth2.Token)(auto), err + return util.TokenWithExpiry(res), err } // RefreshToken implements oauth.TokenRefresher diff --git a/vehicle/ford/identity.go b/vehicle/ford/identity.go index a9712dc656..3dd93e48da 100644 --- a/vehicle/ford/identity.go +++ b/vehicle/ford/identity.go @@ -73,13 +73,13 @@ func NewIdentity(log *util.Logger, user, password, domain string) *Identity { func (v *Identity) Login() error { token, err := v.login() if err == nil { - v.TokenSource = oauth.RefreshTokenSource((*oauth2.Token)(token), v) + v.TokenSource = oauth.RefreshTokenSource(token, v) } return err } // login authenticates with username/password to get new token -func (v *Identity) login() (*oauth.Token, error) { +func (v *Identity) login() (*oauth2.Token, error) { oc := NewOauth2Config(v.domain) cv := oauth2.GenerateVerifier() @@ -185,7 +185,7 @@ func (v *Identity) login() (*oauth.Token, error) { tok, err := oc.Exchange(ctx, code, oauth2.VerifierOption(cv)) // exchange code for api token - var token oauth.Token + var token oauth2.Token if err == nil { data := map[string]string{ "idpToken": tok.AccessToken, @@ -204,7 +204,7 @@ func (v *Identity) login() (*oauth.Token, error) { } } - return &token, err + return util.TokenWithExpiry(&token), err } // RefreshToken implements oauth.TokenRefresher @@ -214,19 +214,16 @@ func (v *Identity) RefreshToken(token *oauth2.Token) (*oauth2.Token, error) { } uri := fmt.Sprintf("%s/api/token/v2/cat-with-refresh-token", TokenURI) - req, err := request.New(http.MethodPost, uri, request.MarshalJSON(data), map[string]string{ + req, _ := request.New(http.MethodPost, uri, request.MarshalJSON(data), map[string]string{ "Content-type": request.JSONContent, "Application-Id": ApplicationID, }) - var res *oauth.Token - if err == nil { - err = v.DoJSON(req, &res) - } - + var res *oauth2.Token + err := v.DoJSON(req, &res) if err != nil { res, err = v.login() } - return (*oauth2.Token)(res), err + return util.TokenWithExpiry(res), err } diff --git a/vehicle/mercedes/identity.go b/vehicle/mercedes/identity.go index 8404d3db3b..0b2471fb58 100644 --- a/vehicle/mercedes/identity.go +++ b/vehicle/mercedes/identity.go @@ -112,12 +112,12 @@ func (v *Identity) RefreshToken(token *oauth2.Token) (*oauth2.Token, error) { uri := fmt.Sprintf("%s/as/token.oauth2", IdUri) req, _ := request.New(http.MethodPost, uri, strings.NewReader(data.Encode()), mbheaders(true, v.region)) - var res oauth.Token + var res oauth2.Token if err := v.DoJSON(req, &res); err != nil { return nil, err } - tok := (*oauth2.Token)(&res) + tok := util.TokenWithExpiry(&res) v.TokenSource = oauth.RefreshTokenSource(tok, v) err := settings.SetJson(v.settingsKey(), tok) diff --git a/vehicle/mercedes/setupapi.go b/vehicle/mercedes/setupapi.go index 605533c243..a9e63524bf 100644 --- a/vehicle/mercedes/setupapi.go +++ b/vehicle/mercedes/setupapi.go @@ -7,7 +7,6 @@ import ( "strings" "github.com/evcc-io/evcc/util" - "github.com/evcc-io/evcc/util/oauth" "github.com/evcc-io/evcc/util/request" "github.com/evcc-io/evcc/util/transport" "github.com/google/uuid" @@ -85,10 +84,10 @@ func (vs *SetupAPI) RequestAccessToken(nonce string, pin string) (*oauth2.Token, uri := fmt.Sprintf("%s/as/token.oauth2", IdUri) req, _ := request.New(http.MethodPost, uri, strings.NewReader(data.Encode()), mbheaders(true, vs.region)) - var res oauth.Token + var res oauth2.Token if err := vs.DoJSON(req, &res); err != nil { return nil, err } - return (*oauth2.Token)(&res), nil + return util.TokenWithExpiry(&res), nil } diff --git a/vehicle/tronity.go b/vehicle/tronity.go index 477b4756aa..06a685f110 100644 --- a/vehicle/tronity.go +++ b/vehicle/tronity.go @@ -154,10 +154,10 @@ func (v *Tronity) RefreshToken(_ *oauth2.Token) (*oauth2.Token, error) { req, _ := request.New(http.MethodPost, v.oc.Endpoint.TokenURL, request.MarshalJSON(data), request.JSONEncoding) - var token oauth.Token + var token oauth2.Token err := request.NewHelper(v.log).DoJSON(req, &token) - return (*oauth2.Token)(&token), err + return util.TokenWithExpiry(&token), err } // vehicles implements the vehicles api diff --git a/vehicle/volvo/connected/identity.go b/vehicle/volvo/connected/identity.go index 18a48affe5..60b96f8446 100644 --- a/vehicle/volvo/connected/identity.go +++ b/vehicle/volvo/connected/identity.go @@ -61,12 +61,12 @@ func (v *Identity) Login(user, password string) (oauth2.TokenSource, error) { return nil, err } - var tok oauth.Token + var tok oauth2.Token if err := v.DoJSON(req, &tok); err != nil { return nil, err } - token := (*oauth2.Token)(&tok) + token := util.TokenWithExpiry(&tok) ts := oauth2.ReuseTokenSourceWithExpiry(token, oauth.RefreshTokenSource(token, v), 15*time.Minute) go oauth.Refresh(v.log, token, ts) @@ -88,10 +88,10 @@ func (v *Identity) RefreshToken(token *oauth2.Token) (*oauth2.Token, error) { return nil, err } - var res oauth.Token + var res oauth2.Token if err := v.DoJSON(req, &res); err != nil { return nil, err } - return (*oauth2.Token)(&res), err + return util.TokenWithExpiry(&res), err }