403/Forbidden using delphi application to access my Google calendar - delphi

Can anyone tell me why i'm having trouble accessing my calendar information? I'm getting 403 forbidden.
procedure TForm1.Button1Click(Sender: TObject);
var
stringStream: TStringStream;
slPost, slReply: TStringList;
sPostResult: string;
begin
slPost := TStringList.Create;
slReply := TStringList.Create;
try
slPost.LineBreak := '&';
slPost.Values['Email'] := 'me#gmail.com';
slPost.Values['Passwd'] := 'pass';
slPost.Values['service'] := 'cl';
slPost.Values['source'] := 'company-program-version';
stringStream := TStringStream.Create(slPost.Text);
try
IdHTTP1.Request.ContentType := 'application/x-www-form-urlencoded';
sPostResult := IdHTTP1.Post('https://www.google.com/accounts/ClientLogin', stringStream);
slReply.LineBreak:=#10;
slReply.Text:=sPostResult;
slReply.LineBreak:=#13#10;
Memo1.Lines.Add(slReply.Text);
Memo1.Lines.Add('response=' + IdHTTP1.ResponseText);
// 200 OK
sPostResult := IdHTTP1.Post('https://www.google.com/accounts/ClientLogin', stringStream);
IdHTTP1.Request.CustomHeaders.FoldLines:=false;
IdHTTP1.Request.CustomHeaders.Clear;
IdHTTP1.Request.CustomHeaders.Values['GData-Version']:='2.0';
IdHTTP1.Request.CustomHeaders.Values['Authorization']:='GoogleLogin auth=' + slReply.Values['auth'];
(* custom headers:
GData-Version: 2.0
Authorization: GoogleLogin (line continues) auth=DQwhateverwhateverwhateverwhateverwhateverwhateverwhateverwhateverwhateverwhateverwhateverwhateverwhateverwhateverwhateverwhateverwhateverwhateverwhateverwhateverwhateverwhatever *)
IdHTTP1.Request.ContentType := 'application/atom+xml';
// 403 Forbidden
memo1.Lines.Add(IdHTTP1.Get('https://www.googleapis.com/calendar/v3/users/me/calendarList'));
finally
stringStream.Free;
end;
finally
slPost.Free;
slReply.Free;
end;
end;
thank you!
mp

After some reading, I think you need to deal with Redirect. So If response is redirect, get the new url, reattach the authorization to the new request header with the new url. Otherwise your redirection request will be missing the required authorization and give you 403 error.

Related

Delphi: TOAuth2Authenticator with client_credentials

A while ago I wrote a method in Delphi 2010 to get the OAuth2 token using the Indy components (TidHttp). See code below.
I am now doing something new in Delphi 10.4 and would like to use the REST components such as RESTClient, RESTRequest, TOAuth2Authenticator, etc.
Our grant type is Client Credentials but in none of the examples on the net could I find on how to use TOAuth2Authenticator with Client Credentials. Is it even possible?
We have a client id, client secret and token URL. We do not have authorization or redirect endpoints. In Insomnia, the setup will look like this:
Does somebody know how to get the token using TOAuth2Authenticator with grant type = client_credentials?
Here is the Delphi 2010 code:
procedure TfrmToken.btnGetTokenClick(Sender: TObject);
var
IdHTTP: TidHttp;
lsHttpError: string;
loRequest: TStringStream;
loRespJson: TMemoryStream;
liSuper: iSuperObject;
ldtExpiry: TDateTime;
begin
IdHTTP := TIdHTTP.Create();
loRespJson := TMemoryStream.Create();
try
IdHTTP.HandleRedirects := False;
loRequest := TStringStream.Create('grant_type=client_credentials&client_id=' +
edtKey.Text + '&client_secret='+edtSecret.Text);
try
IdHttp.IOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(IdHttp);
IdHttp.Request.ContentType := 'application/x-www-form-urlencoded';
try
IdHTTP.Post(edtURL.Text, loRequest, loRespJson);
except
on E: EIdHTTPProtocolException do
begin
lsHttpError := E.ErrorMessage;
end;
on E: Exception do
begin
lsHttpError := E.Message;
end;
end;
if idHTTP.ResponseCode = 200 then
begin
liSuper := SO(StreamToString(loRespJSon));
edtToken.Text := liSuper.S['access_token'];
ldtExpiry := IncSecond(Now, liSuper.i['expires_in']);
edtExpiry.Text := 'Expires in ' + liSuper.S['expires_in'] +
' seconds. Time: ' +
FormatDateTime('yyyy/dd/mm hh:nn:ss', ldtExpiry);
end
else
begin
liSuper := SO(lsHttpError);
edtToken.Text := IdHTTP.ResponseText;
edtExpiry.Text := '';
end;
finally
FreeAndNil(loRequest);
end;
finally
FreeAndNil(IdHTTP);
FreeAndNil(loRespJson);
end;
end;

Delphi Devart SecureBridge POST Request

I am using the trial version of DevArt's SecureBridge product. I am trying to process POST, but somehow I could not print the request data.
XML:
<test>
<a>test1</a>
<b>test2</b>
</test>
Delphi:
ScHttpWebRequest1.Method := rmPOST;
ScHttpWebRequest1.ContentType := 'text/xml';
ScHttpWebRequest1.RequestUri := 'https://test.com/api';
ScHttpWebRequest1.KeepAlive := True;
ScHttpWebRequest1.ContentLength := Length(XML);
ScHttpWebRequest1.WriteBuffer(pAnsiChar(XML), 0, Length(XML)); ///I think I'm making a mistake here.
ShowMessage(ScHttpWebRequest1.GetResponse.ReadAsString);
I have reviewed the documents, but there is a feature called RequestStream. This feature was not available in the version I downloaded. I think WriteBuffer is used instead or different. all I want to do is request a POST with XML content on the relevant site. How can I do it?
Thanks.
Here's a chunk of code that has worked for me:
var
Response: TScHttpWebResponse;
ResponseStr: string;
buf: TBytes;
begin
ScHttpWebRequest1.Method := rmPOST;
ScHttpWebRequest1.ContentType := 'text/xml';
ScHttpWebRequest1.RequestUri := 'https://test.com/api';
ScHttpWebRequest1.KeepAlive := True;
buf := TEncoding.UTF8.GetBytes(xml);
ScHttpWebRequest1.ContentLength := Length(buf);
ScHttpWebRequest1.WriteBuffer(buf);
Response:=ScHttpWebRequest1.GetResponse;
ResponseStr:=Response.ReadAsString;
end;
Based on Devart forums information you can post/put stream or strings parameters as below:
var
Request: TScHttpWebRequest;
Response: TScHttpWebResponse;
ResponseStr: string;
Stream: TFileStream;
begin
Request := TScHttpWebRequest.Create(URL);
Stream := TFileStream.Create(FileName, fmOpenRead);
try
Request.Method := rmPut;
Request.ContentType := 'application/pdf';
Request.TransferEncoding := 'binary';
Request.Headers.Add('Content-Disposition', 'form-data; name="FormFile"; filename="Document1.pdf"');
Request.ContentLength := Stream.Size;
Request.SendChunked := True;
Request.RequestStream := Stream;
Response := Request.GetResponse;
ResponseStr := Response.ReadAsString;
Response.Free;
finally
Stream.Free;
Request.Free;
end;
end;

SOAP response class not assigned (nil)

I am using Delphi 10.2.3 to consume a SOAP web service. My problem is web service replies are not assigned to response classes defined in wsdl generated using WSDLImp.exe.
I see that raw response XML contains all information in it as can be seen below:
HTTP/1.1 200
X-HP-CAM-COLOR: V=1;ServerAddr=5F+cgNOCZPdfvCQ4naSfjw==;GUID=1|xpjJ6o0v_kd3rKz0c1ASIUXW--xZpHqeH8lJ3S2l30SxOi2DzpzfGfLpdqdxt0lg|L2VhcnNpdi93cy9FYXJzaXZXZWJTZXJ2aWNl
Content-Type: text/xml;charset=utf-8
Transfer-Encoding: chunked
Date: Wed, 24 Oct 2018 15:54:46 GMT
Server: EARSIV
Strict-Transport-Security: max-age=157680000
Set-Cookie: cookiesession1=1BEDB4A4PX73WJCTCIRPOUP46FNNFC28;Path=/;HttpOnly
1da
<?xml version="1.0" ?><S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"><S:Body><ns2:faturaOlusturResponse xmlns:ns2="http://service.earsiv.uut.cs.com.tr/"><return xmlns=""><resultCode>AE00067</resultCode><resultExtra></resultExtra><resultText>Web servis çağrısı "input" alanından gerekli bilgiler elde edilemedi. Eksik alanlar: null bu alan(lar)ın boş olmadığından emin olunuz!</resultText></return></ns2:faturaOlusturResponse></S:Body></S:Envelope>
0
return in faturaOlusturResponse is nil as I see while debugging. Trying to access to return.resultCode raises an access violation.
What you see is correct that there are garbage chars just before the line XML starts and right after it. I suspected that maybe the reason.
But;
1- I see Fiddler has no problem parsing this and displaying formatted XML view with all correct information.
2- I tried to use OnAfterExecute() event of THTTPRIO. I see that SOAPResponse.Size is 474 which is equal to XML line length in above raw response.
I cannot think of what maybe the problem and could not fix it. Internet searches did not help.
Any help is appreciated.
Thanks.
WSDL URL: https://earsiv.efinans.com.tr/earsiv/ws/EarsivWebService?wsdl
EDIT:
Consuming web service method is done using AFaturaOlustur() function below. There is SetSecurityHeader() procedure which requires another unit code. I did not include them all not to post a lot of code in here. Json routines are from mORMot framework. There are some constants used in some parts of the code as well. When I read file response.xml written in HTTPRIO1AfterExecute() procedure after running function, there is complete xml. It is identical to the one in raw response above:
class procedure TMyType.HTTPRIO1AfterExecute(const MethodName: string; SOAPResponse: TStream);
var
List: TStringList;
begin
List := TStringList.Create();
try
SOAPResponse.Position := 0;
List.LoadFromStream(SOAPResponse);
List.SaveToFile('response.xml');
finally
List.Free();
end;
end;
function AFaturaOlustur(const Input: TFaturaOlusturInput; const GidenBelgeFormati: TGidenBelgeFormatlari; const GidenDosya, GelenDosya: string): Boolean;
var
RIO: THTTPRIO;
WS: EarsivWebService;
Request: faturaOlustur;
Response: faturaOlusturResponse;
Json: RawUTF8;
begin
if not TFile.Exists(GidenDosya) then
begin
LastError := 'AFaturaOlustur(): GidenDosya mevcut değil!';
end;
RIO := THTTPRIO.Create(nil);
RIO.OnAfterExecute := TMyType.HTTPRIO1AfterExecute;
RIO.URL := URLEArsivFatura;
WS := (RIO as EarsivWebService);
SetSecurityHeader(WS);
Json := RecordSaveJSON(Input, TypeInfo(TFaturaOlusturInput));
Request := nil;
Response := nil;
try
Request := faturaOlustur.Create();
Request.input := string(Json);
Request.fatura := belge.Create();
case GidenBelgeFormati of
UBL: Request.fatura.belgeFormati := belgeFormatiEnum.UBL;
CSXML: Request.fatura.belgeFormati := belgeFormatiEnum.CSXML;
HTML: Request.fatura.belgeFormati := belgeFormatiEnum.HTML;
PDF: Request.fatura.belgeFormati := belgeFormatiEnum.PDF;
CUSTOM: Request.fatura.belgeFormati := belgeFormatiEnum.CUSTOM;
PDF_CUSTOM: Request.fatura.belgeFormati := belgeFormatiEnum.PDF_CUSTOM;
PDF_UBL: Request.fatura.belgeFormati := belgeFormatiEnum.PDF_UBL;
CSXML1: Request.fatura.belgeFormati := belgeFormatiEnum.CSXML1;
CSXML2: Request.fatura.belgeFormati := belgeFormatiEnum.CSXML2;
CSXML3: Request.fatura.belgeFormati := belgeFormatiEnum.CSXML3;
PDF_CSXML1: Request.fatura.belgeFormati := belgeFormatiEnum.PDF_CSXML1;
PDF_CSXML2: Request.fatura.belgeFormati := belgeFormatiEnum.PDF_CSXML2;
PDF_CSXML3: Request.fatura.belgeFormati := belgeFormatiEnum.PDF_CSXML3;
end;
Request.fatura.belgeIcerigi := ToByteDynArray(TFile.ReadAllBytes(GidenDosya));
try
Response := WS.faturaOlustur(Request);
except
on E: Exception do
begin
LastError := 'AFaturaOlustur(): ' + E.Message;
Exit(False);
end;
end;
Result := Assigned(Response.return);
if Result then Result := Response.return.resultCode = rcSuccess;
if Result then
begin
TFile.WriteAllBytes(GelenDosya, ToBytes(Response.output.belgeIcerigi));
end
else
begin
LastError := 'AFaturaOlustur(): ' + Response.return.resultText;
end;
finally
Request.Free();
Response.Free();
end;
end;

Convert cURL headers to Delphi TIdHTTP

I am trying to address the OriginStamp.org site to post a file hash and to get its timestamp
curl -X GET -H "Content-Type: application/json" -H "Authorization: xxxxxxxxxxxxxxxxxxx" http://api.originstamp.org/api/yyyyyyyyyyy
I used various formats to access this API but this seemed to be most hopeful but with out success
PROCEDURE TInterNetFm.GetTIMESTAMP;
var
i : integer;
response : string;
JSONToSend : TStringStream;
IndyH : TIdHTTP;
begin
IndyH := TIdHTTP.Create(Application);
JSONToSend := TStringStream.Create('{}');
IndyH.Request.Connection := 'Keep-Alive';
IndyH.Request.CustomHeaders.Clear;
IndyH.Request.CustomHeaders.Values['Content-Type'] := 'application/json';
IndyH.Request.CustomHeaders.Values['Authorization']:='xxxxxxxxxxxxxxx';
IndyH.Request.ContentType := 'application/json';
response:=IdHttp1.post('http://originstamp.org/api/
05c2422f44ddd24ba3f25848773a8fcb48435f8f966381da4732c40a7255780c',
JSONToSend);
JSONToSend.free;
IndyH.free;
end;
This gives a HTTP/1.1 error 403 forbidden.
I have also tried IdHttp1.getand the Delphi REST debugger and various attampts at REST.
Any suggestions about how I am going wrong with this would be grately appreciated.
A 403 error implies that you are likely using an invalid API key in the Authorization header. Also, make sure that Request.BasicAuthentication is set to False so TIdHTTP doesn't try to send its own Authorization header.
That being said, I see several other issues with your code.
You are leaking memory if TIdHTTP.Post() raises an exception on failure.
You are sending 2 Content-Type headers (one from Request.ContentType and the other from CustomerHeaders.Values['Content-Type']). Use Request.ContentType only.
You are sending your request to originstamp.org when you should be sending it to api.originstamp.org instead.
You are creating and configuring a TIdHTTP object named IndyH, but you are performing the actual Post() using another object named IdHttp1 instead.
Try this:
function TInterNetFm.GetTIMESTAMP(const AHash: string): string;
var
IndyH : TIdHTTP;
Response : string;
begin
IndyH := TIdHTTP.Create(nil);
try
IndyH.Request.BasicAuthentication := False;
IndyH.Request.CustomHeaders.Values['Authorization'] := 'YOUR_API_KEY';
Response := IndyH.Get('http://api.originstamp.org/api/' + AHash);
finally
IndyH.Free;
end;
// parse Response JSON as needed...
Result := ...;
end;
function TInterNetFm.GetTIMESTAMP(const AHash, AData: string): string;
var
IndyH : TIdHTTP;
JSONToSend : TStringStream;
Response : string;
begin
IndyH := TIdHTTP.Create(nil);
try
IndyH.Request.BasicAuthentication := False;
IndyH.Request.ContentType := 'application/json';
IndyH.Request.CustomHeaders.Values['Authorization'] := 'YOUR_API_KEY';
JSONToSend := TStringStream.Create(AData, TEncoding.UTF8);
try
Response := IndyH.Post('http://api.originstamp.org/api/' + AHash, JSONToSend);
finally
JSONToSend.Free;
end;
finally
IndyH.Free;
end;
// parse Response JSON as needed...
Result := ...;
end;

HTTP client error 403

I have the following problem: when I click a button on a site (http://domain-location.com) I receive information back to me and the url is changed to http://domain-location.com?key=value.
I want to create a sample http client to receive information easily. Here is my code:
function DoRequest(aVal: string): string;
const DOMAIN = 'http://domain-location.com';
var
request: TIdHTTP;
responseStream: TMemoryStream;
responseLoader: TStringList;
urlRequest: string;
begin
request := TIdHTTP.Create(nil);
responseStream := TMemoryStream.Create;
responseLoader := TStringList.Create;
try
try
// accept ranges didn't help
// request.Response.AcceptRanges := 'text/json';
// request.Response.AcceptRanges := 'text/xml';
urlRequest := DOMAIN + '?key=' + aVal;
request.Get(urlRequest, responseStream);
responseStream.Position := 0;
responseLoader.LoadFromStream(responseStream);
Result := responseLoader.Text;
except on E: Exception do
Result := e.Message;
end;
finally
responseLoader.Free;
responseStream.Free;
request.Free;
end;
end;
EDIT After the first answer I edited my function (still not working):
function DoRequest(aVal: string): string;
const DOMAIN = 'http://domain-location.com';
var
request: TIdHTTP;
responseStream: TMemoryStream;
responseLoader: TStringList;
urlRequest: string;
uri: TIdURI;
begin
request := TIdHTTP.Create(nil);
responseStream := TMemoryStream.Create;
responseLoader := TStringList.Create;
request.CookieManager := TIdCookieManager.Create(request);
uri := TIdURI.Create(DOMAIN);
try
try
// accept ranges didn't help
// request.Response.AcceptRanges := 'text/json';
// request.Response.AcceptRanges := 'text/xml';
urlRequest := DOMAIN + '?key=' + aVal;
request.CookieManager.AddServerCookie('cookie1', uri);
request.CookieManager.AddServerCookie('cookie2', uri);
request.CookieManager.AddServerCookie('cookie3', uri);
request.Get(urlRequest, responseStream);
responseStream.Position := 0;
responseLoader.LoadFromStream(responseStream);
Result := responseLoader.Text;
except on E: Exception do
Result := e.Message;
end;
finally
responseLoader.Free;
responseStream.Free;
request.Free;
end;
end;
And after I do the request the result is: HTTP1.1 403 Forbidden.
I inspected the page and the button I click is in a form like this:
<form action="http:/domiain.com" method="GET">
<input type="text" name="key">
<input type="submit" value="Click">
</form>
When I type http://domain-location.com?key=value there is no problem.
Any idea how to fix it?
The problem was with the UserAgent:
function DoRequest(aVal: string): string;
const DOMAIN = 'http://domain-location.com';
var
request: TIdHTTP;
urlRequest: string;
begin
request := TIdHTTP.Create(nil);
try
try
request.Request.UserAgent := 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36';
urlRequest := DOMAIN + '?key=' + aVal;
Result := request.Get(urlRequest);
except on E: Exception do
Result := e.Message;
end;
finally
request.Free;
end;
end;
If cookies are involved, then you should GET the original HTML page first so the server can send whatever cookies it needs to be posted back when the button is "clicked", then you can GET the next page and let TIdHTTP post whatever cookies it had received.
Try this:
function DoRequest(const aVal: string): string;
const
DOMAIN = 'http://domain-location.com';
var
request: TIdHTTP;
begin
try
request := TIdHTTP.Create(nil);
try
request.Get(DOMAIN, TStream(nil)); // get cookies, discard HTML
Result := request.Get(DOMAIN + '?key=' + TIdURI.ParamsEncode(aVal));
finally
request.Free;
end;
except
on E: Exception do
Result := e.Message;
end;
end;
Check the actual request that is sent by your browser. 403 suggests that there is some sort of authentication going on. This might be a token or a cookie that your browser has, and sends with the request, but your sample client application may not have. Open the browser debugging panel to check the get request made by your browser, and compare it to the one made by your application. I bet there will be some difference.

Resources