VW/Audi/Seat/Skoda: recover from marketing consent prompt (#29980)
This commit is contained in:
parent
1200fbc2f9
commit
5506053dd3
1 changed files with 67 additions and 0 deletions
|
|
@ -170,6 +170,15 @@ func (v *Service) loginLegacy(vars FormVars, user, password string) (url.Values,
|
|||
// GET identity.vwgroup.io/signin-service/v1/consent/users/bca09cc0-8eba-4110-af71-7242868e1bf1/b7a5bb47-f875-47cf-ab83-2ba3bf6bb738@apps_vw-dilab_com?scopes=openid%20profile%20birthdate%20nickname%20address%20phone%20cars%20mbb&relayState=15404cb51c8b4cc5efeee1d2c2a73e5b41562faa&callback=https://identity.vwgroup.io/oidc/v1/oauth/client/callback&hmac=a590931ca3cd9dc3a27f1d1c0c162bf1e5c5c32c9f5b40fcb36d4c6edc631e03
|
||||
// GET identity.vwgroup.io/oidc/v1/oauth/client/callback/success?user_id=bca09cc0-8eba-4110-af71-7242868e1bf1&client_id=b7a5bb47-f875-47cf-ab83-2ba3bf6bb738@apps_vw-dilab_com&scopes=openid%20profile%20birthdate%20nickname%20address%20phone%20cars%20mbb&consentedScopes=openid%20profile%20birthdate%20nickname%20address%20phone%20cars%20mbb&relayState=f89a0b750c93e278a7ace170ce374e9cb9eb0a74&hmac=2b728f463c3cfe80f3271fbb35680e5e5218ca70025a46e7fadf7c7982decc2b
|
||||
|
||||
// VW periodically interjects an optional marketing consent page after an
|
||||
// otherwise successful login. Skip it without consenting so evcc recovers
|
||||
// automatically instead of requiring a restart (#29760).
|
||||
if location, ok, err := v.skipMarketingConsent(resp); err != nil {
|
||||
return nil, err
|
||||
} else if ok {
|
||||
return parseAuthLocation(location)
|
||||
}
|
||||
|
||||
parsed, err := url.Parse(resp.Header.Get("Location"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -177,6 +186,57 @@ func (v *Service) loginLegacy(vars FormVars, user, password string) (url.Values,
|
|||
return parseAuthLocation(parsed)
|
||||
}
|
||||
|
||||
// marketingConsentCallback returns the OIDC callback url embedded in an
|
||||
// optional VW/Audi marketing consent page. VW periodically interjects this
|
||||
// page (path .../consent/marketing/...) after an otherwise successful login.
|
||||
// It returns a nil url if u is not a marketing consent page.
|
||||
func marketingConsentCallback(u *url.URL) (*url.URL, error) {
|
||||
if u == nil || !strings.Contains(u.Path, "/consent/marketing/") {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
callback := u.Query().Get("callback")
|
||||
if callback == "" {
|
||||
return nil, errors.New("marketing consent page is missing callback url")
|
||||
}
|
||||
|
||||
cb, err := url.Parse(callback)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// normalize spaces in query values (e.g. scopes) so the request is valid
|
||||
cb.RawQuery = cb.Query().Encode()
|
||||
|
||||
return cb, nil
|
||||
}
|
||||
|
||||
// skipMarketingConsent detects the optional VW/Audi marketing consent page that
|
||||
// the identity server periodically interjects and continues the login without
|
||||
// consenting by following the OIDC callback embedded in the consent request.
|
||||
// The bool result reports whether resp was such a consent page.
|
||||
func (v *Service) skipMarketingConsent(resp *http.Response) (*url.URL, bool, error) {
|
||||
if resp.Request == nil {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
cb, err := marketingConsentCallback(resp.Request.URL)
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
if cb == nil {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
cbResp, err := v.Get(cb.String())
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
defer cbResp.Body.Close()
|
||||
|
||||
location, err := url.Parse(cbResp.Header.Get("Location"))
|
||||
return location, true, err
|
||||
}
|
||||
|
||||
// loginNew performs the new VW identity login flow
|
||||
func (v *Service) loginNew(body []byte, user, password string) (url.Values, error) {
|
||||
state, err := extractState(body)
|
||||
|
|
@ -203,6 +263,13 @@ func (v *Service) loginNew(body []byte, user, password string) (url.Values, erro
|
|||
return nil, errors.New(resp.Status)
|
||||
}
|
||||
|
||||
// skip the optional marketing consent page without consenting (#29760)
|
||||
if loc, ok, err := v.skipMarketingConsent(resp); err != nil {
|
||||
return nil, err
|
||||
} else if ok {
|
||||
return parseAuthLocation(loc)
|
||||
}
|
||||
|
||||
if location == "" {
|
||||
return nil, errors.New("no redirect location in new login flow")
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue