Beim Zugriff auf die Keycloak-API zum Abruf eines Tokens kommt die
Fehlermeldung 401 Not Authorized wenn das Passwort ein Sonderzeichen
enthält.
In einer Python-Testumgebung kann der Token ohne Probleme abgerufen
werden.
Mit Wireshark kann man sich die erzeugten Abfragen anzeigen (damit man in
Wireshark die unverschlüsselten Daten angezeigt bekommt muss man zum Debuggen
die URL von https:// auf http:// umstellen):
Python erzeugt:
POST /keycloak/rest/v1/token/generate HTTP/1.1
Host: demo.wlsoft.de
User-Agent: python-requests/2.22.0
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Authorization: Basic xxxx:yyyyyyyyyyyyyyyy
Content-Length: 70
grant_type=password&username=xxxxx@xxxxxx.xx&password=123456789%C3%840
Delphi erzeugt
POST /keycloak/rest/v1/token/generate HTTP/1.1
Host: demo.wlsoft.de
User-Agent: Embarcadero URI Client/1.0
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded; charset=utf-8
Authorization: Basic xxxx:yyyyyyyyyyyyyyyy
Content-Length: 70
grant_type=password&username=xxxxx@xxxxxx.xx&password=123456789%C3%840
Der Unterschied steckt im Content-Type
Python: Content-Type: application/x-www-form-urlencoded;
Delphi: Content-Type: application/x-www-form-urlencoded; charset=utf-8
Der falsche Eintrag in Delphi wird durch die Funktion
THTTPClient.CreateFormFromStrings() erzeugt:
Deshalb darf bei Abruf eines Token nur eine Post-Anweisung aufgerufen werden,
die CreateFormFromStrings() nicht aufruft.
Das wäre zum Beispiel ein solcher Aufruf:
function TNetHTTPClient.Post(
const AURL: string;
const ASource, AResponseContent: TStream;
const AHeaders: TNetHeaders): IHTTPResponse;
type
THCloudZugangsdaten = record
User: string;
Pass: string;
Token:string;
TokenResponseCode: integer;
TokenResponseText: string;
end;
function getToken(var aZugangsdaten: TZugangsdaten): boolean;
var
s, aURL: string;
j: TJSONObject;
aSource: TStringStream;
m: TMemoryStream;
sl: TStringlist;
Response: IHTTPResponse;
HTTP: TNetHTTPClient;
aHeaders: TNetHeaders;
begin
Result := False;
sl := TStringlist.Create;
aSource := TStringStream.Create(
'grant_type=password' +
'&username=' + EncodeURIComponent(aZugangsdaten.User) +
'&password=' + EncodeURIComponent(aZugangsdaten.Pass));
m := TMemoryStream.Create;
HTTP := TNetHTTPClient.Create(nil);
try
try
HTTP.ContentType := cContentType_x_www_form_urlencoded;
HTTP.AcceptEncoding := 'gzip, deflate';
HTTP.CustomHeaders['Accept'] := '*/*';
HTTP.CustomHeaders['Authorization'] := 'Basic xxxx:yyyyyyyyyyyyyyyy';
aURL := 'https://demo.wlsoft.de/keycloak/rest/v1/token/generate';
Response := HTTP.Post(aURL, aSource, m, aHeaders);
if Response.StatusCode = 200 then begin
m.Position := 0;
sl.LoadFromStream(m);
s := sl.Text;
j := TJSONObject.ParseJSONValue(s) as TJSONObject;
try
if Assigned(j) then begin
if j.TryGetValue('access_token', aZugangsdaten.Token) then begin
end;
end;
finally
FreeAndNil(j);
end;
end;
aZugangsdaten.TokenResponseCode := Response.StatusCode;
aZugangsdaten.TokenResponseText := Response.StatusText;
Result := not aZugangsdaten.Token.IsEmpty;
except
on E: Exception do begin
aZugangsdaten.TokenResponseCode := Response.StatusCode;
aZugangsdaten.TokenResponseText := E.Message;
// kein raise;
end;
end;
finally
m.Free;
aSource.Free;
FreeAndNil(sl);
FreeNetHttp(HTTP);
end;
end;