evcc-io / evcc

Sonne tanken ☀️🚘
https://evcc.io
MIT License
2.75k stars 521 forks source link

Add Genesis API #13929

Open runtologist opened 2 weeks ago

pasqualito79 commented 2 weeks ago

Danke schon mal!

Hier die AppId:

GenesisAppID = "f11f2b86-e0e7-4851-90df-5600b01d8b70"

pasqualito79 commented 2 weeks ago

stimmt, sorry habs gesehen. Ich blicke noch nicht so ganz durch wie ich mir diesen PR lokal clone...hab mir dein master genommen und da wars nicht drin.

diese AppID habe ich aus https://github.com/Hyundai-Kia-Connect/hyundai_kia_connect_api/blob/master/hyundai_kia_connect_api/KiaUvoApiEU.py

für Hyundai/Kia wurde das auch so gemacht, natürlich unschön da das theoretisch jederzeit widerrufen werden könnte, aber leider kann man sich nicht selber dafür registrieren, da bräuchte es jemanden aus Korea dafür (koreanische Telefonnummer nötig). Also soweit alles "evcc-konform" ;-)

pasqualito79 commented 2 weeks ago

habs geschafft dein PR bei mir zu testen. Erste Änderung für Genesis in der identity.go müsste so sein:

uri := fmt.Sprintf(
    "%s/api/v1/user/oauth2/authorize?response_type=code&state=test&client_id=%s&redirect_uri=%s/api/v1/user/oauth2/redirect",
    v.config.APIURI,
    v.config.CCSPServiceID,
    v.config.AuthURI,
)

d.h. da müsste man den Abschnitt so gestalten, dass für Genesis die APIURI verwendet wird und für die anderen beiden die AuthURI

pasqualito79 commented 2 weeks ago
  1. Schritt, auch identity.go dasselbe wie bei 1:

func (v Identity) brandLogin(cookieClient request.Helper, user, password string) (string, error) { req, err := request.New(http.MethodGet, v.config.APIURI+IntegrationInfoURL, nil, request.JSONEncoding)

pasqualito79 commented 2 weeks ago
  1. Schritt in bluelink.go Abschnitt NewGenesisFromConfig die URL ersetzen mit: BrandAuthUrl: "https://accounts-eu.genesis.com/realms/eugenesisidm/protocol/openid-connect/auth?client_id=%s&scope=openid+profile+email+phone&response_type=code&hkid_session_reset=true&redirect_uri=%s/api/v1/user/integration/redirect/login&ui_locales=%s&state=%s:%s",
pasqualito79 commented 2 weeks ago
  1. Das in identitgy.go wieder rückgängig machen, sonst gibts Ärger mit Kia/Hyundai:

alt: data := url.Values{ "grant_type": {"authorization_code"}, "redirect_uri": {"https://accounts-eu.genesis.com/realms/eugenesisidm/ga-api/redirect2"}, "code": {accCode}, } neu: data := url.Values{ "grant_type": {"authorization_code"}, "redirect_uri": {v.config.URI + "/api/v1/user/oauth2/redirect"}, "code": {accCode}, }

runtologist commented 2 weeks ago
  1. Das in identitgy.go wieder rückgängig machen, sonst gibts Ärger mit Kia/Hyundai:

Die Änderung gibt es in dem PR nicht, das war wohl bei Dir lokal. ;) Ich habe mal die APIURI analog zu den anderen Stellen eingefügt.

pasqualito79 commented 2 weeks ago

sorry, oh mann ich hab ein durcheinander ;-)

  1. Schritt für Genesis muss APIURI rein in identity.go: req, err := request.New(http.MethodPost, v.config.APIURI+LoginURL, request.MarshalJSON(data), request.JSONEncoding) if err != nil { return "", err }
runtologist commented 2 weeks ago

@pasqualito79 Wenn Du oben auf den Tab Files Changed wechselst, kannst Du in der Zeile, wo die Änderung hin soll auf plus drücken, und dort direkt den Kommentar rein schreiben.

pasqualito79 commented 2 weeks ago

Jetzt kommt was kniffliges. Login hat funktioniert, soweit gut...

[genesis] TRACE 2024/05/16 15:11:40 POST https://prd-eu-ccapi.genesis.com/api/v1/user/signin
[genesis] TRACE 2024/05/16 15:11:40 {"email":"***","password":"***"}
--
{"step":4,"upgrade":false}

nächster Request sieht aktuell so aus:

[genesis] TRACE 2024/05/16 15:11:40 POST https://prd-eu-ccapi.genesis.com/api/v1/user/oauth2/token
[genesis] TRACE 2024/05/16 15:11:40 code=&grant_type=authorization_code&redirect_uri=https%3A%2F%2Fprd-eu-ccapi.genesis.com%2Fapi%2Fv1%2Fuser%2Foauth2%2Fredirect
--
{"errId":"2ea53d2b-38b7-4f09-88ad-62c5937fe9d3","errCode":"4002","errMsg":"Invalid parameters"}

hier sieht man, dass Parameter "code" leer ist. Der wird hier irgendwo abgeholt:

    var accCode string
    if err = cookieClient.DoJSON(req, &res); err == nil {
        if parsed, err := url.Parse(res.RedirectURL); err == nil {
            accCode = parsed.Query().Get("code")
        }
    } else if res.ErrCode != "" {
        err = fmt.Errorf("%w: %s (%s)", err, res.ErrMsg, res.ErrCode)
    }

    return accCode, err

Wenn ich den original-App anschaue sieht der so aus:

URL: https://prd-eu-ccapi.genesis.com/api/v1/user/oauth2/token

client_id=3020afa2-30ff-412a-aa51-d28fbe901e10&code=XXX&grant_type=authorization_code&redirect_uri=https%3A//accounts-eu.genesis.com/realms/eugenesisidm/ga-api/redirect2
runtologist commented 2 weeks ago

@pasqualito79 den code würde ich XXXen. ;)

runtologist commented 2 weeks ago

Email und Passwort sollten nicht leer sein. Kann es sein, dass Du die credentials noch konfigurieren musst?

pasqualito79 commented 2 weeks ago

So ist besser formatiert, da sieht man auch dass user/pwd gefüllt sind... {"email":"***","password":"***"} das bedeutet, dass der Login erfolgreich war: {"step":4,"upgrade":false}

runtologist commented 2 weeks ago

Kannst Du das komplette log zwischen signin und token posten? Da müsste noch irgendwo die response von dem signin call sein.

pasqualito79 commented 2 weeks ago

Ok, hier die Requests:

URL: https://prd-eu-ccapi.genesis.com/api/v1/user/silentsignin

Request Body:

Body

{
  "intUserId": ""
}

Response Body:

{
  "step": 4,
  "upgrade": false
}

2.

URL: https://prd-eu-ccapi.genesis.com/api/v1/user/agreement?lang=de

Request Body:

{
  "agrees": [
    true,
    true
  ]
}

Response Body (hier ist der Code drin):

{
  "redirectUrl": "https://accounts-eu.genesis.com/realms/eugenesisidm/ga-api/redirect2?code=XXX",
  "popup": false,
  "method": "",
  "upgrade": false,
  "integrated": false,
  "deleteAccountLink": ""
}

3.

URL: https://prd-eu-ccapi.genesis.com/api/v1/user/oauth2/token

Request Body (Code wird mitgeschickt):

client_id=3020afa2-30ff-412a-aa51-d28fbe901e10&code=XXX&grant_type=authorization_code&redirect_uri=https%3A//accounts-eu.genesis.com/realms/eugenesisidm/ga-api/redirect2

Response Body (Tokens kommen zurück):


{
  "access_token": "XYZ",
  "token_type": "Bearer",
  "refresh_token": "ZZZ",
  "expires_in": 86400
}
pasqualito79 commented 2 weeks ago

Das sind die meiner Meinung nach relevanten Requests beim Login. Es gibt natürlich noch einige Requests dazwischen, das sind aber vor allem GET requests auf Ressourcen wie CSS Files. Falls du denkst dass vielleicht doch noch was fehlt, einfach melden...

pasqualito79 commented 2 weeks ago

Spannend, jetzt habe ich einen Code in dem Request (POST https://prd-eu-ccapi.genesis.com/api/v1/user/oauth2/token) drin! An dem kanns also nicht liegen...vielleicht muss man zuerst das License Agreement akzeptieren, damit der Code generiert wird. Die redirect_uri ist in diesem Request jedenfalls noch komplett falsch, ist: "https%3A%2F%2Fprd-eu-ccapi.genesis.com%2Fapi%2Fv1%2Fuser%2Foauth2%2Fredirect" und müssten sein "https%3A//accounts-eu.genesis.com/realms/eugenesisidm/ga-api/redirect2".

Zusätzlich wird via App noch der Parameter "client_id" mitgesendet. Den haben wir ja in deer bluelink.go als CCSPServiceID drin.

pasqualito79 commented 2 weeks ago

Hier der aktuelle Stand:

[genesis] TRACE 2024/05/16 22:11:16 POST https://prd-eu-ccapi.genesis.com/api/v1/user/signin
[genesis] TRACE 2024/05/16 22:11:16 {"email":"***","password":"***"}
--
{"redirectUrl":"https://accounts-eu.genesis.com/api/v1/user/oauth2/redirect?code=XXX\u0026state=test","popup":false,"method":"","upgrade":false,"integrated":false,"deleteAccountLink":""}
[genesis] TRACE 2024/05/16 22:11:16 POST https://prd-eu-ccapi.genesis.com/api/v1/user/oauth2/token
[genesis] TRACE 2024/05/16 22:11:16 code=XXX&grant_type=authorization_code&redirect_uri=https%3A%2F%2Fprd-eu-ccapi.genesis.com%2Fapi%2Fv1%2Fuser%2Foauth2%2Fredirect
--
{"errId":"621ed2d2-cc8a-4f07-9db0-12358f03a969","errCode":"4002","errMsg":"Invalid parameters"}
pasqualito79 commented 2 weeks ago

Habe noch einen anderen Fehler entdeckt:

BrandAuthUrl: "https://accounts-eu.genesis.com/realms/eugenesisidm/protocol/openid-connect/auth?client_id=%s&scope=openid+profile+email+phone&response_type=code&hkid_session_reset=true&redirect_uri=%s/api/v1/user/integration/redirect/login&ui_locales=%s&state=%s:%s",

hier zeigt das %s nach redirect_uri auf die AuthURI, müsste aber APIURI sein.

runtologist commented 1 week ago

@pasqualito79 Ich denke ich habe Deine letzten Anmerkungen drin. Bin mal gespannt, ob das mit dem RedirectPath so funktioniert. Das sieht doch deutlich anders aus als bei Hyundai/Kia.

pasqualito79 commented 1 week ago

@runtologist Habs getestet, leider noch selbes Ergebnis:

[genesis] TRACE 2024/05/21 13:59:30 client_id=3020afa2-30ff-412a-aa51-d28fbe901e10&code=XXX&grant_type=authorization_code&redirect_uri=https%3A%2F%2Faccounts-eu.genesis.com%2Frealms%2Feugenesisidm%2Fga-api%2Fredirect2
--
{"errId":"717f8257-8ecb-4f69-8581-ec61df7486e0","errCode":"4002","errMsg":"Invalid parameters"}

Der einzige Unterschied zum "Original"-Request ist, dass "/" nicht als %2F gesendet wird, sondern eben so: "https%3A//accounts-eu.genesis.com/realms/eugenesisidm/ga-api/redirect2". Aber ob das zu einem anderen Ergebnis führen kann...

Was ich noch angepasst habe sind die beiden:

    "Content-type":  "application/x-www-form-urlencoded; charset=utf-8",
    "User-Agent":    "EU_BlueLink/1.0.5 (com.genesis.eu.ux20; build:10511; iOS 17.5.0) Alamofire/5.8.0",

aber auch das führt zum gleichen Ergebnis.

Hier mal der ganze Originalrequest-Header in RAW:

POST /api/v1/user/oauth2/token HTTP/1.1
Host: prd-eu-ccapi.genesis.com
Content-Type: application/x-www-form-urlencoded; charset=utf-8
Accept: */*
Cookie: account=XXX
Connection: keep-alive
Content-Length: 188
User-Agent: EU_BlueLink/1.0.5 (com.genesis.eu.ux20; build:10511; iOS 17.5.0) Alamofire/5.8.0
Accept-Language: de;q=1.0
Authorization: Basic YYY==
Accept-Encoding: br;q=1.0, gzip;q=0.9, deflate;q=0.8

client_id=3020afa2-30ff-412a-aa51-d28fbe901e10&code=ZZZ&grant_type=authorization_code&redirect_uri=https%3A//accounts-eu.genesis.com/realms/eugenesisidm/ga-api/redirect2

XXX, YYY und ZZZ sind lange Strings. Zusätzlich gibt es noch ein "account"-Cookie und als Authorisierung basicauth mit Username & PWD

pasqualito79 commented 2 days ago

Hast du schon Zeit gehabt, den Token-Request anzuschauen? Hier noch als curl-Befehl:

curl 'https://prd-eu-ccapi.genesis.com/api/v1/user/oauth2/token' \ -X POST \ -H 'Host: prd-eu-ccapi.genesis.com' \ -H 'Accept: */*' \ -H 'Connection: keep-alive' \ -H 'User-Agent: EU_BlueLink/1.0.5 (com.genesis.eu.ux20; build:10511; iOS 17.5.1) Alamofire/5.8.0' \ -H 'Accept-Language: de;q=1.0' \ -H 'Authorization: Basic YYY==' \ -H 'Content-Type: application/x-www-form-urlencoded; charset=utf-8' \ --cookie 'account=XXX' \ --data-raw 'client_id=3020afa2-30ff-412a-aa51-d28fbe901e10&code=ZZZ&grant_type=authorization_code&redirect_uri=https%3A//accounts-eu.genesis.com/realms/eugenesisidm/ga-api/redirect2'

Was sich bei jedem neuen Login ändert sind die beiden Variablen YYY und ZZZ. Alles andere bleibt gleich.