delphioauth-2.0ssl-certificaterest-clienthandshake

How to convert my REST API Delphi project which uses MVCFramework to use standard RAD Studio components instead


I develop a project which an old programmer in our company has started. He has used MVCFramework (https://github.com/danieleteti/delphimvcframework) to connect to a portal. Because his code is written so complicated, I gathered it temporarily like this working function just for you to see the logic behind the connection:

function ConnectionTest_MinimumCodeJustForStackoverflowUsersToSeeHowItWorks: Boolean;
var
  FBaseURL, FAuthURL, FClientID, FClientSecret, FAccessToken: string;
  FSSLSocket: TIdSSLIOHandlerSocketOpenSSL;
  FRestClient: MVCFramework.RESTClient.TRESTClient;
  http: TIdHTTP;
  ParameterList: TStringList;
  ping: string;
begin
  Result := False;
  FBaseURL := 'https://www.the_portal_addresse.de';
  FAuthURL := 'https://auth.the_portal_addresse.de/auth/token';
  FClientID := 'Bla'; 
  FClientSecret := 'Bla'; 
  FSSLSocket := TIdSSLIOHandlerSocketOpenSSL.Create;
  FSSLSocket.SSLOptions.Method := sslvSSLv23;
  FRestClient := MVCFramework.RESTClient.TRESTClient.Create(FBaseURL, INTERNET_DEFAULT_HTTPS_PORT, FSSLSocket);
  FRestClient.ContentType('application/json');
  FRestClient.Accept('application/json');
  FAccessToken := '';
  http := TIdHTTP.Create(nil);
  ParameterList := TStringList.Create;
  try
    try
      http.HandleRedirects := False;
      http.IOHandler := FSSLSocket;
      http.Request.ContentType := CONTENTTYPE_APPLICATION_X_WWW_FORM_URLENCODED;
      http.Request.BasicAuthentication := False;
      ParameterList.add('grant_type=client_credentials');
      ParameterList.add('client_id=' + FClientID);
      ParameterList.add('client_secret=' + FClientSecret);
      FAccessToken := http.Post(FAuthURL, ParameterList);
      FAccessToken := TJSonObject.ParseJSONValue(FAccessToken).GetValue<string>('access_token');
    except
      Exit;
    end;
    FRestClient.SetBearerToken(FAccessToken);
    ping := FRestClient.doGet(BasePath + '/system/ping', []).BodyAsString;
    if LowerCase(ping) <> 'pong' then
      Exit;
    Result := True;
  finally
    FreeAndNil(ParameterList);
    FreeAndNil(http);
    FreeAndNil(TIdSSLIOHandlerSocketOpenSSL);
    FreeAndNil(FRestClient);
  end;
end;

Note that I'm not going to use this code in my project but I want to change it. Also I have no access to this programmer because he is fired!

This function is working with MVCFramework but I cannot use the TRESTClient component which is defined in REST.Client instead, because REST.Client.TRESTClient.Create differs from the MVCFramework.RESTClient.TRESTClient.Create.

Maybe it is a good idea that I use the MVCFramework as well but I want to learn why my converted code (without MVCFramework) does not work and I get this error:

Error connecting with SSL.
error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure.

What I have tried is:

function ConnectionTest_MinimumCodeJustForStackoverflowUsersToSeeHowItWorks_2: Boolean;
var
  ...
  FRestClient: REST.Client.TRESTClient;
  ...
begin
  ...
  FBaseURL := 'https://www.the_portal_addresse.de:443';
  FAuthURL := 'https://auth.the_portal_addresse.de:443/auth/token';
  ...
  FRestClient := REST.Client.TRESTClient.Create(FBaseURL);
  ...
      FAccessToken := http.Post(FAuthURL, ParameterList); // ERROR!
  ...

I also tried placing the TRest... components on a form, and also OAuth1Authenticator and OAuth2Authenticator components, but I couldn't go further because I didn't know how should I set the 'grant_type=client_credentials'.

How can I convert my function to work using REST.Client.TRESTClient instead of MVCFramework.RESTClient.TRestClient?


Solution

  • OMG! I've discovered the problem! It turns out the issue was not with the code, but rather with incorrect system settings set by our IT administrator. We are using a virtual machine with Delphi installed, along with numerous components, certificates, Windows settings, and software installations. In our large main project, which takes a significant amount of time to compile and run, I initially wrote the first function, but modified function in a new project located in a temporary folder. The intention was to speed up testing by running the project separately.

    After numerous attempts and exhaustive research (which gave me a headache), I finally identified the cause. It appears that certain DLL files, specifically "libeay32.dll" and "ssleay32.dll" must reside in the same directory as my executable file. These DLL files were present in the folders of our main project, which is why the original function worked while my changes did not. Ideally, our IT administrator should have correctly configured the environment path settings for Delphi or Windows, which would have prevented me from encountering this problem.

    I believe it's important for everyone to be aware of this issue, which is why I posted this answer. I want to express my sincere gratitude to all the participants, particularly David Schwartz, who dedicated a significant amount of time.