5

I am trying to request access and refresh tokens from Google using oauth2, all I am getting is a "HTTP/1.1 400 Bad Request" error.

I am using IdHTTP in Delphi XE5 with the IdSSLIOHandlerSocketOpenSSL handler. I have got my Client ID, Client Secret and API Key. I have the Authorization Code.

IdHTTP is configured:

AllowCookies = True
HandleRedirects = True
HTTPOptions.hoKeepOrigProtocol = True
HTTPOptions.hoNoProtocolErrorException = True

Everything else is default.

Is there anything I should be doing beforehand, such as authentication?

What is the relevance of the redirect_uri when creating a Win32 application?

All help will be gratefully received.

This is my code:

var  Params: TStringList;
     Resp: TStringStream;
     URI: String;
begin

     Params := TStringList.Create;
     Resp := TStringStream.Create;

     try
        URI := 'https://accounts.google.com/o/oauth2/token';

        Params.Add('client_id=' + clientID);
        Params.Add('client_secret=' + clientSecret);
        Params.Add('code=' + authCode);
        Params.Add('redirect_uri=http://localhost');
        Params.Add('grant_type=authorization_code');

        IdHTTP.Request.ContentType := 'application/x-www-form-urlencoded';
        IdHTTP.Post(URI, Params, Resp);

        Memo1.Lines.LoadFromStream(Resp);
        Memo1.Lines.Insert(0, IdHTTP.ResponseText);

        finally
        FreeAndNil(Params);
        FreeAndNil(Resp);
        end;

end;

EDIT:

I changed the Accept header to a setting I found elsewhere.
I added charset=utf-8 to Content-Type.
I replaced the clientID and clientSecret.

This is what IdHTTP is sending:

POST /o/oauth2/token HTTP/1.1<br>
Content-Type: application/x-www-form-urlencoded; charset=utf-8<br>
Content-Length: 240<br>
Host: accounts.google.com<br>
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8<br>
Accept-Encoding: identity<br>
User-Agent: Mozilla/3.0 (compatible; Indy Library)<br>

client_id=clientID&client_secret=clientSecret&code=4%2FtmTyuvpcSMIkoyJcFmicfXQEDpYiw_QilkO2dXv_nUc&redirect_uri=http%3A%2F%2Flocalhost&grant_type=authorization_code

Kromster
  • 7,181
  • 7
  • 63
  • 111
Alan Finch
  • 101
  • 1
  • 8
  • The code should work. Some parameter has to be wrong. Note that the redirect_uri has to exactly match to previous request and value in API settings. – smooty86 Dec 14 '15 at 18:10
  • @smooty86 I downloaded the Client_ID from the Google Developers Console in JSON and there were 2 Redirects in there: "urn:ietf:wg:oauth:2.0:oob","h t t p://localhost" – Alan Finch Dec 15 '15 at 09:10
  • 1
    Btw. you are not supposed to set ContentType. Indy does that. You should also probably check the error message returned from google. Use try/except for the Post, catch the exception and get error message using "(E as EIdHTTPProtocolException).ErrorMessage" – smooty86 Dec 16 '15 at 10:44
  • If I try Indy again that may be helpful. Why does Indy have to use Exceptions so much? Why can't it just return the error as the return code and put the content in the content? This is how Synapse works and was so much easier to debug. – Alan Finch Dec 17 '15 at 11:53
  • @smooty86 Just as a side note. Thanks for your feedback, your help is appreciated. – Alan Finch Dec 17 '15 at 11:56
  • Indy will work the same and in some cases it is much easier to use it because it does a lot of things for you (eg. encoding parameters). You just have to learn how to use it. Result "400 Bad Request" is an error so that's why it raises an exception. You can clearly find out immediately that this should not be returned and handle it with try/except. So it is logical. I don't think it is more difficult, it is just different. – smooty86 Dec 20 '15 at 08:27

3 Answers3

3

I solved it!

Indy was the problem.

I used synapse 4.0 components instead and had it working within 10 mins.

That's a day and a half I'll never get back. Thanks Indy :(

Alan Finch
  • 101
  • 1
  • 8
  • 1
    No, it is not a problem of Indy. You did something wrong. – smooty86 Dec 16 '15 at 10:34
  • 1
    @smooty86 I'll accept that, but whatever it was wasn't obvious. I don't use http very much and, because of that, I don't use Indy very much. I just tried my settings in Synapse and they worked. The feedback from Synapse allowed me to realise that the Authorization Code only works once, but the rest of the settings were fine. And, yes, I had used new codes with Indy. I'm sure that Indy is very good, just too many "nooks and crannies" where things can go wrong for an inexperienced user. Just my opinion. – Alan Finch Dec 17 '15 at 11:50
  • Indy IS the problem - see: https://stackoverflow.com/questions/59430119/how-to-get-oauth2-functionality-in-delphi – Toby Sep 22 '20 at 01:41
0

Save yourself some headache. Get Clever Internet Suite for Delphi. Because, the shit developers don't understand that SSL HTTP client is such a basic requirement. They just keep on proposing just HTTP clients instead. Forget Indy and Synapse. I am nowhere affiliated to CIS but my 2 cents. Works flawlessly with my XE5.

user30478
  • 347
  • 1
  • 3
  • 13
0

My Code Using Delphi XE2:

var  Params: TStringList;
     Resp: TStringStream;
     URI: String;
     HTTP: TIdHTTP;
     json: string;`enter code here`
     IOHandle: TIdSSLIOHandlerSocketOpenSSL;
begin

   Params := TStringList.Create;
   Resp := TStringStream.Create;

   HTTP := TIdHTTP.Create(nil);
   HTTP.Request.BasicAuthentication := False;
   HTTP.Request.UserAgent := 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0';
   IOHandle := TIdSSLIOHandlerSocketOpenSSL.Create(HTTP);
   IOHandle.SSLOptions.Method := sslvTLSv1_2;
   IOHandle.SSLOptions.Mode := sslmClient;
   HTTP.IOHandler := IOHandle;
   HTTP.Request.ContentType := 'application/x-www-form-urlencoded';
   HTTP.Request.CharSet := 'UTF-8';

   try
      URI := 'https://oauth.hm.bb.com.br/oauth/token';

      Params.Add('client_id=' + CLIENT_ID);
      Params.Add('client_secret=' + CLIENT_SECRET);
      Params.Add('redirect_uri=http://localhost');
      Params.Add('grant_type=client_credentials');

      HTTP.Request.CustomHeaders.Add('Authorization: Basic sua chave aqui');
      HTTP.Request.ContentType := 'application/x-www-form-urlencoded';
      json := HTTP.Post(URI, Params);

      finally
        FreeAndNil(Params);
        FreeAndNil(Resp);
      end;

end;