From 47c104ec6195df5af1db5349bd6097eaab7e737d Mon Sep 17 00:00:00 2001 From: Chris591 <39920986+Chris591@users.noreply.github.com> Date: Sat, 8 Jan 2022 15:54:55 +0100 Subject: [PATCH] Ford: fix HTTP 401 errors (#2240) --- vehicle/ford/api.go | 15 ++++------ vehicle/ford/identity.go | 65 +++++++++++++++++++++++++++------------- 2 files changed, 50 insertions(+), 30 deletions(-) diff --git a/vehicle/ford/api.go b/vehicle/ford/api.go index 63e2396af..c43d5a076 100644 --- a/vehicle/ford/api.go +++ b/vehicle/ford/api.go @@ -11,12 +11,7 @@ import ( "golang.org/x/oauth2" ) -// https://github.com/ianjwhite99/connected-car-node-sdk - -const ( - ApiURI = "https://usapi.cv.ford.com" - VehicleListURI = "https://api.mps.ford.com/api/users/vehicles" -) +const ApiURI = "https://usapi.cv.ford.com" // API is the VW api client type API struct { @@ -34,8 +29,8 @@ func NewAPI(log *util.Logger, ts oauth2.TokenSource) *API { token, err := ts.Token() if err == nil { for k, v := range map[string]string{ - "Content-type": "application/json", - "Application-Id": "71A3AD0A-CF46-4CCF-B473-FC7FE5BC4592", + "Content-type": request.JSONContent, + "Application-Id": ApplicationID, "Auth-Token": token.AccessToken, } { req.Header.Set(k, v) @@ -54,7 +49,9 @@ func (v *API) Vehicles() ([]string, error) { var resp VehiclesResponse var vehicles []string - err := v.GetJSON(VehicleListURI, &resp) + uri := fmt.Sprintf("%s/api/users/vehicles", TokenURI) + + err := v.GetJSON(uri, &resp) if err == nil { for _, v := range resp.Vehicles.Values { vehicles = append(vehicles, v.VIN) diff --git a/vehicle/ford/identity.go b/vehicle/ford/identity.go index 77d948028..ec19eb2f8 100644 --- a/vehicle/ford/identity.go +++ b/vehicle/ford/identity.go @@ -1,9 +1,9 @@ package ford import ( + "context" + "fmt" "net/http" - "net/url" - "strings" "github.com/evcc-io/evcc/util" "github.com/evcc-io/evcc/util/oauth" @@ -12,10 +12,19 @@ import ( ) const ( - AuthURI = "https://sso.ci.ford.ca" // fcis.ice.ibmcloud.com - ClientID = "9fb503e0-715b-47e8-adfd-ad4b7770f73b" + AuthURI = "https://sso.ci.ford.com" + TokenURI = "https://api.mps.ford.com" + ClientID = "9fb503e0-715b-47e8-adfd-ad4b7770f73b" + ApplicationID = "1E8C7794-FF5F-49BC-9596-A1E0C86C5B19" ) +var OAuth2Config = &oauth2.Config{ + ClientID: ClientID, + Endpoint: oauth2.Endpoint{ + TokenURL: fmt.Sprintf("%s/oidc/endpoint/default/token", AuthURI), + }, +} + type Identity struct { *request.Helper user, password string @@ -42,34 +51,48 @@ func (v *Identity) Login() error { // login authenticates with username/password to get new token func (v *Identity) login() (oauth.Token, error) { - data := url.Values{ - "client_id": []string{ClientID}, - "grant_type": []string{"password"}, - "username": []string{v.user}, - "password": []string{v.password}, - } + ctx, cancel := context.WithTimeout( + context.WithValue(context.Background(), oauth2.HTTPClient, v.Client), + request.Timeout, + ) + defer cancel() - uri := AuthURI + "/v1.0/endpoint/default/token" - req, err := request.New(http.MethodPost, uri, strings.NewReader(data.Encode()), request.URLEncoding) + tok, err := OAuth2Config.PasswordCredentialsToken(ctx, v.user, v.password) - var res oauth.Token + // exchange code for api token + var token oauth.Token if err == nil { - err = v.DoJSON(req, &res) + data := map[string]string{ + "code": tok.AccessToken, + } + + uri := fmt.Sprintf("%s/api/oauth2/v1/token", TokenURI) + + var req *http.Request + req, err = request.New(http.MethodPut, uri, request.MarshalJSON(data), map[string]string{ + "Content-type": request.JSONContent, + "Application-Id": ApplicationID, + }) + + if err == nil { + err = v.DoJSON(req, &token) + } } - return res, err + return token, err } // Refresh implements oauth.TokenRefresher func (v *Identity) RefreshToken(token *oauth2.Token) (*oauth2.Token, error) { - data := url.Values{ - "client_id": []string{ClientID}, - "grant_type": []string{"refresh_token"}, - "refresh_token": []string{token.RefreshToken}, + data := map[string]string{ + "refresh_token": token.RefreshToken, } - uri := AuthURI + "/v1.0/endpoint/default/token" - req, err := request.New(http.MethodPost, uri, strings.NewReader(data.Encode()), request.URLEncoding) + uri := fmt.Sprintf("%s/api/oauth2/v1/refresh", TokenURI) + req, err := request.New(http.MethodPut, uri, request.MarshalJSON(data), map[string]string{ + "Content-type": request.JSONContent, + "Application-Id": ApplicationID, + }) var res oauth.Token if err == nil {