am 25.02.2025 11:53
Hi,
ich versuche mich momentan über die comdirect REST Api mit meinem Konto zu verbinden in der Hoffnung einiges automatisieren zu können.
Folgende Schritte funktionieren soweit ich das absehe:
Beim Schritt: "2.5 CD Secondary Flow" bekomme ich einen Fehler zurück:
{"error":"invalid_client","error_description":"Bad client credentials"}
Ich verwende libCURL und baue mir den POST wie folgt zusammen:
/**
** ===================================================
** URL: https://api.comdirect.de/oauth/token
** HTTP-Method: POST
** ===================================================
***/
curl_easy_setopt(curl, CURLOPT_URL, "https://api.comdirect.de/oauth/token");
/* Now specify we want to POST data */
curl_easy_setopt(curl, CURLOPT_POST, 1L);
/* provide a buffer to store errors in */
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf);
/**
** ===================================================
** HEADER
** ===================================================
***/
/* Create header according to comdirect api description */
list = curl_slist_append(list, "Accept: application/json");
list = curl_slist_append(list, "Content-Type: application/x-www-form-urlencoded");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);
/**
** ===================================================
** DATA
** ===================================================
***/
/* Create data according to comdirect api description */
std::string str_DATA;
char *escaped_field = curl_easy_escape(NULL, "User_********************************", 0);
str_DATA+= std::string("client_id=")+std::string(escaped_field)+std::string("&");
curl_free(escaped_field);
escaped_field = curl_easy_escape(NULL, "********************************", 0);
str_DATA+= std::string("client_secret=")+std::string(escaped_field)+std::string("&");
curl_free(escaped_field);
escaped_field = curl_easy_escape(NULL, "cd_secondary", 0);
str_DATA+= std::string("grant_type=")+std::string(escaped_field)+std::string("&");
curl_free(escaped_field);
escaped_field = curl_easy_escape(NULL, "*****************", 0);
str_DATA+= std::string("token=")+std::string(escaped_field);
curl_free(escaped_field);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, str_DATA.c_str());
res = curl_easy_perform(curl);
Den Wert für das "Access-Token" habe ich aus dem:
2.1 OAuth2 Resource Owner Password Credentials Flow
Die beiden werte "client_id" und "client_secret" habe ich gleich wie beim
2.1 OAuth2 Resource Owner Password Credentials Flow
gesetzt und von comdirect erhalten beim anmelden für die API.
Kann mir jemand sagen was ich bei diesem POST falsch mache?
Viele Grüße,
TinTin
am 27.05.2025 18:12
Moin,
Das ist wahrscheinlich der Hauptfehler! Der User schreibt:
"Den Wert für das 'Access-Token' habe ich aus dem: 2.1 OAuth2 Resource Owner Password Credentials Flow"
Das ist falsch! Beim CD Secondary Flow muss im token Parameter das Session-Token aus Schritt 2.4 (TAN-Aktivierung) verwendet werden, NICHT das Access-Token aus Schritt 2.1.
Beim CD Secondary Flow wird normalerweise zusätzlich der Authorization-Header mit dem Access-Token aus Schritt 2.1 benötigt.
Probier das mal aus? 😉
am 27.05.2025 18:13
...kam so übrigens aus ClaudeAI aufs erste Prompt raus 😉
am 09.12.2025 07:17
Hatte auch das Problem.
Die Antwort von FSQuant ist falsch. Er hat halt einfach nur AI befragt ohne zu Validieren!
Mein Fehler war, dass ich den Schritt 2.4 vergessen hatte. Der curl Aufruf zur Aktivierung der Session ist so (PATCH):
curl --location --request PATCH 'https://api.comdirect.de/api/session/clients/<ClientID>/v1/sessions/<SessionID>' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer <Access Token>' \
--header 'x-http-request-info: {"clientRequestId":{"sessionId":"<SessionID>","requestId":"123456789"}}' \
--header 'Content-Type: application/json' \
--header 'x-once-authentication-info: {"id":"<TAN-ID>"}' \
--header 'Cookie: qSession=a11705d9.a6f46707e31346968d26a3e' \
--data '{
"identifier": "<SessionID>",
"sessionTanActive": true,
"activated2FA": true
}'<Access Token> ist das Access Token aus dem ersten Schritt 2.1
<SessionID> wird mehrfach angegeben und entspricht dem Wert von clientRequestId.sessionId, was du im Schritt 2.2.1 selber gesetzt hast.
<ClientID> ist die Client-ID aus deinen OAuth-Credentials.
<TAN-ID> bekommst du aus dem vorherigen Schritt (2.3 session validate). Die ID steht im Return Header im x-once-authentication-info Feld.
Der Aufruf für den Secondary Flow ist nun so (POST)
curl --location 'https://api.comdirect.de/oauth/token' \ --header 'Accept: application/json' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --header 'Authorization: Bearer <Access Token>' \ --header 'Cookie: qSession=a11705d9.a6f46707e31346968d26a3e' \ --data-urlencode 'client_id=<Client ID>' \ --data-urlencode 'client_secret=<Client Secret>' \ --data-urlencode 'grant_type=cd_secondary' \ --data-urlencode 'token=<Access Token>'
Wichtig ist also, dass unter token das Access Token aus 2.1 kommt. Aber der Aufruf funktioniert nur, wenn mit der PhotoTAN-App die Session (2FA) authorisiert wurde und Schritt 2.4 erfolgreich war.
Falls wer von Comdirect das hier liest:
1) Bitte mal die Doku (PDF) aktualisieren und klarer darstellen.
2) Vllt. mal die API dahingehend überarbeiten, dass in einem Call nicht identische Werte in Header und Body übergeben werden müssen. Das verwirrt nur, weil es völlig unlogisch ist zweimal das gleiche anzugeben.
3) Access Flows vereinfachen. Warum braucht es 4 Calls PLUS einen Swipe in der Photo TAN App um das secondary access token zu erhalten. Also 2FA ist ja supi und soll bitte bleiben. Aber 4 API Calls um letzendlich ein access token zu erhalten ist crazy. Security through obscurity oder was soll das sein?
09.12.2025 08:47 - bearbeitet 09.12.2025 08:49
09.12.2025 08:47 - bearbeitet 09.12.2025 08:49
Moin svebert,
keine Ahnung, warum du dich so aufregst?
Erstens steht da, dass ich das aus ClaudeAI rauskopiert habe als Antwort aus meinem Workspace, in den ich die Frage von @TinTin82 reinkopiert hatte.
Zweitens: Wenn dir der fehlerhafte Ansatz trotzdem ein kleine Inspiration gegeben hat und du es jetzt gelöst hast, ist es ja gut?
Drittens: Ich arbeite selbst mit C# und habe eine Client-Lib, die all das für mich kappselt - solltest du auch machen: Du kannst als Pasis die Postman Docu nehmen. Dann musst du dich mit diesen lowlevel Fragen weniger rumschlagen.
Zum unteren Teil deines Beitrags:
Deine Wünsche sind gut und nett aber mit den Verbesserungsvorschlägen wirst du hier leider gegen die Wand laufen 🙂
Das mit der Photo-TAN kann man diskutieren... Viel wichtiger wäre, dass die Sessions serverseitig nicht immer wieder regelmäßig gecancelt werden, sonst könnte die App dauerhaft eingeloggt sein....