evcc-io/vehicle/skoda/tokenrefreshservice/endpoint.go
2025-11-09 20:23:24 +01:00

95 lines
2.3 KiB
Go

package tokenrefreshservice
import (
"encoding/json"
"net/url"
"time"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/request"
"github.com/evcc-io/evcc/util/urlvalues"
"github.com/evcc-io/evcc/vehicle/vag"
)
const (
BaseURL = "https://mysmob.api.connect.skoda-auto.cz"
CodeExchangeURL = BaseURL + "/api/v1/authentication/exchange-authorization-code?tokenType=CONNECT"
RefreshTokenURL = BaseURL + "/api/v1/authentication/refresh-token?tokenType=CONNECT"
)
var _ vag.TokenExchanger = (*Service)(nil)
type Service struct {
*request.Helper
data url.Values
}
type skodaTokenResponse struct {
AccessToken string `json:"accessToken"`
RefreshToken string `json:"refreshToken"`
IdToken string `json:"idToken"`
}
func (s *skodaTokenResponse) toVagToken(v *vag.Token) {
v.AccessToken = s.AccessToken
v.RefreshToken = s.RefreshToken
v.IDToken = s.IdToken
v.Expiry = time.Now().Add(time.Minute * 60)
}
func New(log *util.Logger, q url.Values) *Service {
return &Service{
Helper: request.NewHelper(log),
data: q,
}
}
func (v *Service) Exchange(q url.Values) (*vag.Token, error) {
if err := urlvalues.Require(q, "code"); err != nil {
return nil, err
}
var skTok skodaTokenResponse
data := map[string]string{
"code": q.Get("code"),
"redirectUri": "myskoda://redirect/login/",
"verifier": q.Get("code_verifier"),
}
resp, err := v.Post(CodeExchangeURL, request.JSONContent, request.MarshalJSON(data))
if err == nil {
defer resp.Body.Close()
if resp.StatusCode != 200 {
return nil, request.NewStatusError(resp)
}
}
if err == nil {
json.NewDecoder(resp.Body).Decode(&skTok)
}
var res vag.Token
skTok.toVagToken(&res)
return &res, err
}
func (v *Service) Refresh(token *vag.Token) (*vag.Token, error) {
var skTok skodaTokenResponse
data := map[string]string{"token": token.RefreshToken}
resp, err := v.Post(RefreshTokenURL, request.JSONContent, request.MarshalJSON(data))
if err == nil {
defer resp.Body.Close()
json.NewDecoder(resp.Body).Decode(&skTok)
}
var res vag.Token
skTok.toVagToken(&res)
return &res, err
}
// TokenSource creates token source. Token is refreshed automatically.
func (v *Service) TokenSource(token *vag.Token) vag.TokenSource {
return vag.RefreshTokenSource(token, v.Refresh)
}