I am having trouble obtaining an access token from the Instapaper api using Overbyte's ICS suite. The odd thing is that I am able to get an access token using Indy. I am using Delphi XE2 and ICS version 7.
For debugging purposes, I am using fixed strings for post data and authorization headers. Since the Indy code works, the problem does not seem to be signature related.
I would like to get this working with ICS ... any ideas on why the code does not work? I'm getting an invalid signature error (signature does not match expected value). The authorization header and post body should be identical to what I'm sending with Indy, so sounds like it has something to do with the ICS request.
Here is the Indy code that works (I get oauth_token and oauth_token_secret back)
var
Params: TStringList;
AuthHeader: STring;
idHTTP: TIDHTTP;
SSLIOHandler: TIdSSLIOHandlerSocketOpenSSL;
begin
idHTTP := TIDHTTP.Create(Self);
SslIOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(Self);
idHTTP.IOHandler := SSLIOHandler;
idHTTP.HTTPOptions := [];
Params := TStringList.Create;
Params.Add('x_auth_mode=client_auth');
Params.Add('x_auth_password=mypassword');
Params.Add('x_auth_username=myusername%40gmail.com');
idHTTP.Request.Host := 'https://www.instapaper.com';
AuthHeader := 'Authorization: ' + 'OAuth oauth_consumer_key="myconsumerkey", oauth_nonce="BAFWMTGFXNXSTNKUENBC", oauth_signature_method="HMAC-SHA1", oauth_signature="sEfq8gHlHYfrmLy5u0kKBgTYmVA%3D", oauth_timestamp="1369669197", oauth_version="1.0"';
idHTTP.request.CustomHeaders.Add(AuthHeader);
Response := idHTTP.Post('https://www.instapaper.com/api/1/oauth/access_token', Params);
end;
Here is the ICS code that fails (invalid signature):
var
PostData: AnsiString;
begin
PostData := 'x_auth_mode=client_auth&x_auth_password=mypassword&x_auth_username=myusername%40gmail.com';
SSLHTTPClient := TSslHttpCli.Create(nil);
With SSLHTTPClient do
begin
RcvdStream := TMemoryStream.Create;
SendStream := TMemoryStream.Create;
SendStream.Seek(0, soFromBeginning);
SendStream.Write(PostData[1], Length(PostData));
SendStream.Seek(0, soFromBeginning);
URL := 'https://www.instapaper.com/api/1/oauth/access_token';
OnRequestDone := SSLHTTPClientRequestDone;
OnBeforeHeaderSend := HTTPClientBeforeHeaderSend; // add the authorization header
SslContext := TSslContext.Create(SSLHTTPClient);
PostAsync;
end;
end;
procedure TInstapaperOAuth.HTTPClientBeforeHeaderSend(Sender: TObject; const Method: string; Headers: TStrings);
var
Header: String;
begin
Header := 'Authorization: ' + 'OAuth oauth_consumer_key="myconsumerkey", oauth_nonce="BAFWMTGFXNXSTNKUENBC", oauth_signature_method="HMAC-SHA1", oauth_signature="sEfq8gHlHYfrmLy5u0kKBgTYmVA%3D", oauth_timestamp="1369669197", oauth_version="1.0"';
Headers.Add(Header);
end;
I'm accepting the above comment as the answer to this question. So to work around this problem, update HTTPClientBeforeHeaderSend to replace the Host record along with adding the oauth header:
procedure TInstapaperOAuth.HTTPClientBeforeHeaderSend(Sender: TObject; const Method: string; Headers: TStrings);
var
Header: String;
I: Integer;
begin
Header := 'Authorization: ' + 'OAuth oauth_consumer_key="myconsumerkey", oauth_nonce="BAFWMTGFXNXSTNKUENBC", oauth_signature_method="HMAC-SHA1", oauth_signature="sEfq8gHlHYfrmLy5u0kKBgTYmVA%3D", oauth_timestamp="1369669197", oauth_version="1.0"';
Headers.Add(Header);
//=======================================
// find the host record and replace it
//=======================================
for I := 0 to Headers.Count - 1 do
begin
if pos('Host:',Headers.Strings[I]) > 0 then
begin
Headers.Strings[I] := 'Host: www.instapaper.com';
break;
end;
end;
end;