Watch, Follow, &
Connect with Us

Please visit our new home
community.embarcadero.com.


Welcome, Guest
Guest Settings
Help

Thread: Help on SOAP web service authentication



Permlink Replies: 2 - Last Post: Aug 27, 2017 3:32 AM Last Post By: pss edn
pss edn

Posts: 45
Registered: 1/13/04
Help on SOAP web service authentication
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jul 30, 2017 11:57 AM
Using XE7 and Indy that comes with it

As far as I recently learned, basic authentication, like in TIdHTTP/TidHTTPServer is not avaiable for SOAP web services, so the common solution for implementing user/password authentication in a SOAP web service is to use SOAP Headers.
First question: Is this correct, or I am missing more things.

It is very hard to find some working example, but I found a starting point with the information contained in http://docwiki.embarcadero.com/RADStudio/Tokyo/en/Defining_and_Using_SOAP_Headers

I succeeded in the client part of the equation, and I am able to send a GetQuote request to the SOAP web service, but in the web service part, in the implementation module, the creation of the Headers object does not succeed
TServiceImpl.GetQuote(Symbol: string): Double;
var
  Headers: ISOAPHeaders;
  H: TAuthHeader;
begin
  Headers := Self as ISOAPHeaders;
  Headers.Get(AuthHeader, TSOAPHeader(H)); { Retrieve the authentication header }
  .../...

With a message "EIntfCast error: Interface not supported".
I do not understand the use of "Self as ISOAPHeaders", because the implementation module is the implementation of the full web service, not only the soapheaders.
So, my question here is: what is the correct cast for getting the Headers object.
pss edn

Posts: 45
Registered: 1/13/04
Re: Help on SOAP web service authentication
Click to report abuse...   Click to reply to this thread Reply
  Posted: Aug 3, 2017 2:52 AM   in response to: pss edn in response to: pss edn
Going one step further.
I catched what comes in with the event WebModuleBeforeDispatch and using Request.Content.
There I can see that there is a Header section in the XML/SOAP message:

'<?xml version="1.0"?>'#$D#$A'<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"><SOAP-ENV:Header SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:NS1="urn:fIni"><NS1:THeader xsi:type="NS1:THeader"><Username xsi:type="xsd:string">Frank Borland</Username><Password xsi:type="xsd:string">SuperDelphi</Password></NS1:THeader></SOAP-ENV:Header><SOAP-ENV:Body xmlns:NS2="urn:AgeSvcIntf-IAgeSvc" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><NS2:TstUsrPwd/></SOAP-ENV:Body></SOAP-ENV:Envelope>'#$D#$A

This header is created in the client test program with the following code
var
 Hdr: THeader;
  ws: IAgeSvc;
begin
  ws := GetIAgeSvc(False, '', HTTPRIO1);
  Hdr := THeader.Create;
  Hdr.UserName := 'Frank Borland';
  Hdr.Password := 'SuperDelphi';
  HTTPRIO1.SOAPHeaders.Send(Hdr);
  sTx := ws.TstUsrPwd();
end;


In the web service server, as explained before, as I get errors with the following code
var
  Headers: ISOAPHeaders;
  H: THeader;
begin
  Headers := Self as ISOAPHeaders;        //   --> Interface not supported


I use GetSOAPHeaders instead
  Headers := GetSOAPHeaders;
  Headers.Get(THeader, TSOAPHeader(H));


But then Headers.Get returns Nil in H header.

I am suspecting that somewhere in the Invoker/Dispatcher does not recognize correctly the arrived XML/SOAP message.
The problem is that trying to follow the calls to GetSOAPHeaders is difficult because goes to assembly code.
pss edn

Posts: 45
Registered: 1/13/04
Re: Help on SOAP web service authentication
Click to report abuse...   Click to reply to this thread Reply
  Posted: Aug 27, 2017 3:31 AM   in response to: pss edn in response to: pss edn
Forgot using SOAP headers, finally I got it working using HTML headers as follows.

In the client side, In the HTTPRIO1HTTPWebNode1_BeforePost event I use the following:

const
  AUTH_USER = 'sherlock';
  AUTH_PASS = 'holmes';
var
  sAuth: String;
begin
  sAuth := 'Authorization: Basic ' + IdCoderMime.TIdEncoderMIME.EncodeString(AUTH_USER + ':' + AUTH_PASS);
  If Not HttpAddRequestHeaders(Data, PChar(sAuth), Length(sAuth), HTTP_ADDREQ_FLAG_ADD) then
    ShowMessage('HttpAddRequestHeaders' + SysErrorMessage(GetLastError()));
end;


In the server side I use the following

  FServer := TIdHTTPWebBrokerBridge.Create(Self);
  FServer.OnParseAuthentication := OnParseAuthentication;
 
 .../...
 
procedure TForm.OnParseAuthentication(AContext: TIdContext; const AAuthType, AAuthData: String; var VUsername, VPassword: String; var VHandled: Boolean);
const
  AUTH_USER = 'sherlock';
  AUTH_PASS = 'holmes';
var
  sTx: String;
  iK: SmallInt;
  idBase64 : TidDecoderMIME;
begin
  if AAuthType =  'Basic' then
  begin
    sTx := AAuthData;
 
    idBase64 := TidDecoderMIME.Create(nil);
    try
      sTx := idBase64.DecodeString(sTx);
    finally
      FreeAndNil(idBase64);
    end;
    iK := Pos(':', sTx);
    VUsername := LeftStr(sTx, iK-1);
    VPassword := MidStr(sTx, iK+1, 999);
    if (VUserName <> AUTH_USER) Or (VPassword <> AUTH_PASS)then
    begin
      AContext.Connection.IOHandler.WriteLn('HTTP/1.1 401 Authentication Required');
      AContext.Connection.IOHandler.WriteLn('Proxy-Authenticate: Basic realm="Authentication Required for this Web Service');
      AContext.Connection.IOHandler.WriteLn('Content-Length: 0');
      AContext.Connection.IOHandler.WriteLn('Connection: close');
      AContext.Connection.IOHandler.WriteLn('');
      Acontext.Connection.Disconnect;
    end;
  end;
end;


Tested to work using an HTTPRIO client, REST Debuger and SOAPUI.

Edited by: pss edn on Aug 27, 2017 3:31 AM
Legend
Helpful Answer (5 pts)
Correct Answer (10 pts)

Server Response from: ETNAJIVE02