Recent

Author Topic: Google Tokens  (Read 465 times)

Eduards

  • New Member
  • *
  • Posts: 27
Google Tokens
« on: July 01, 2019, 04:46:05 pm »
I have strange problem, and have no idea how to solve it. I am not even sure anymore which of my actions were relevant to this question...

Problem:
I have tool that connects to Google Calendar, read and write events. I also have laptop and desktop computer. On laptop my project works with no issues. On desktop it asks for Google user consent to access calendars, and when I copy/paste token it does not accept it. Laptop never asks for consent again, it uses token initially created. Same token does not work on desktop. Project is build on that desktop computer. 

It all started when I once accidentally compiled my project on that new laptop. At the time last November both machines had slightly different Lazarus versions, now they are both latest.

I guess something has changed outside my code, be it Lazarus versions, or Google policies, I dont know.



rvk

  • Hero Member
  • *****
  • Posts: 3842
Re: Google Tokens
« Reply #1 on: July 01, 2019, 05:02:50 pm »
On desktop it asks for Google user consent to access calendars, and when I copy/paste token it does not accept it. Laptop never asks for consent again, it uses token initially created. Same token does not work on desktop. Project is build on that desktop computer.
I don't understand. You create a token (via Google OAuth2 probably) and get a token. And that token doesn't work after copying to the laptop. But the token also doesn't work on the desktop????

So... you're using the token wrong if it doesn't work on the laptop and it doesn't work on the desktop.

Also... you'll get an access_token and a refresh_token after authorization.
The access_token is valid for a limited time (probably 3600 seconds, i.e. 60 minutes).
After that, the access_token is invalid and you need to retrieve a new access_token with the refresh_token you've got.

So, what is the actual error message you get when you access it with your (probably old) access_token?

Thaddy

  • Hero Member
  • *****
  • Posts: 9279
Re: Google Tokens
« Reply #2 on: July 01, 2019, 05:30:31 pm »
After that, the access_token is invalid and you need to retrieve a new access_token with the refresh_token you've got.

So, what is the actual error message you get when you access it with your (probably old) access_token?
That's exactly what I suspect too. With one remark: they can not be copied between machines either. It has to be the exact same device.
The authentication itself is device independent.
« Last Edit: July 01, 2019, 05:34:39 pm by Thaddy »
also related to equus asinus.

Eduards

  • New Member
  • *
  • Posts: 27
Re: Google Tokens
« Reply #3 on: July 01, 2019, 07:49:39 pm »
App saves refresh token in file. That file on laptop has not changed for ages. I can access Google Calendar, and work as intended from that machine.

Same refresh token does not work from other computer, my Desktop where I build my code. Both folders are synced with Dropbox.
My app opens Oauth2 in browser and asks user consent to copy/paste token from there, and DOES NOT ACCEPT IT.

As I understand from you- each machine needs seperate refresh token, right?

Also- how can I see whats in TStringStream? (Resp variable on line 60).

Below is my GetTokens procedure:
Code: Pascal  [Select]
  1. procedure TGCalendars.GetTokens;
  2.   function EncodeURL(url: string): string;
  3.   var
  4.     x: integer;
  5.     sBuff: string;
  6.   const
  7.     SafeMask = ['A'..'Z', '0'..'9', 'a'..'z', '*', '@', '.', '_', '-'];
  8.   begin
  9.     //Init
  10.     sBuff := '';
  11.  
  12.     for x := 1 to Length(url) do
  13.     begin
  14.       //Check if we have a safe char
  15.       if url[x] in SafeMask then
  16.       begin
  17.         //Append all other chars
  18.         sBuff := sBuff + url[x];
  19.       end
  20.       else if url[x] = ' ' then
  21.       begin
  22.         //Append space
  23.         sBuff := sBuff + '+';
  24.       end
  25.       else
  26.       begin
  27.         //Convert to hex
  28.         sBuff := sBuff + '%' + IntToHex(Ord(url[x]), 2);
  29.       end;
  30.     end;
  31.  
  32.     Result := sBuff;
  33.   end;
  34.  
  35. var
  36.   URLString: String;
  37.   OkResult: Boolean;
  38.   InputResult: string;
  39.   JSon: ISuperObject;
  40.   FCode: String;
  41.   Req, Resp: TStringStream;
  42.   Line: TStringList;
  43.  
  44. begin
  45.   FAccessToken := '';
  46.   if FRefreshToken <> '' then
  47.   begin
  48.     { Lets get access token using previous refresh token}
  49.     URLString := 'client_id=' + FClientID;
  50.     URLString := URLString + '&client_secret=' + FClientSecret;
  51.     URLString := URLString + '&refresh_token=' + FRefreshToken;
  52.     URLString := URLString + '&grant_type=refresh_token';
  53.  
  54.     Req := TStringStream.Create(URLString);
  55.     Resp := TStringStream.Create('');
  56.  
  57.     try
  58.       try
  59.         FHTTP.Request.ContentType := 'application/x-www-form-urlencoded';
  60.         FHTTP.DoRequest('POST', 'https://accounts.google.com/o/oauth2/token', Req, Resp, []);
  61.  
  62.       { how can I "see" Resp variable in showmessage()? }
  63.  
  64.       except
  65.       end;
  66.       if FHTTP.ResponseCode = 200 then
  67.       begin
  68.       //  showmessage('1 '+ Req.ReadString(9999));
  69.         JSon := SO(UTF8Decode(Resp.DataString));
  70.         FAccessToken := JSon['access_token'].AsString;
  71.         if JSon['refresh_token'] <> nil then
  72.           FRefreshToken := JSon['refresh_token'].AsString;
  73.         Exit;
  74.       end;
  75.     finally
  76.       Req.Free;
  77.       Resp.Free;
  78.     end;
  79.   end;
  80.  
  81.   { Ja kods augstāk bijis veiksmīgs, tas iziet ar Exit, un šis tālāk neizpildās.
  82.     Šeit tiek apstrādāts, ja refresh_token ir neveiksmīgs. Atveram browseri lai iegūtu "user consent" }
  83.  
  84.   URLString := 'https://accounts.google.com/o/oauth2/auth';
  85.   URLString := URLString + '?client_id=' + FClientID;
  86.   URLString := URLString + '&redirect_uri=urn:ietf:wg:oauth:2.0:oob';
  87.   URLString := URLString + '&scope=https://www.googleapis.com/auth/calendar https://www.google.com/m8/feeds';
  88.   URLString := URLString + '&response_type=code';
  89.   OpenURL(UrlString); { Lazarus equivalent of Delphi ShellExecute((Form.Handle), nil, PChar(URLString), nil, nil, 0); }
  90.  
  91.   repeat
  92.     OkResult := false;
  93.     InputResult := '';
  94.     if Assigned(FOnAuthorizationContinueQuery) then
  95.       FOnAuthorizationContinueQuery(Self, OkResult, InputResult)
  96.     else
  97.       OkResult := InputQuery(Application.Title, 'Enter your code here:', InputResult);
  98.     if OkResult then
  99.       FCode := InputResult  { that code I copy/pasted from browser }
  100.     else
  101.       Exit;
  102.  
  103.     URLString := 'client_id=' + FClientID;
  104.     URLString := URLString + '&client_secret=' + FClientSecret;
  105.     URLString := URLString + '&code=' + FCode;
  106.     URLString := URLString + '&redirect_uri=urn:ietf:wg:oauth:2.0:oob';
  107.     URLString := URLString + '&grant_type=authorization_code';
  108.     Req := TStringStream.Create(URLString);
  109.     Resp := TStringStream.Create('');
  110.  
  111.  
  112.     try
  113.       try
  114.         FHTTP.Request.ContentType := 'application/x-www-form-urlencoded';
  115.         FHTTP.DoRequest('POST', 'https://accounts.google.com/o/oauth2/token', Req, Resp, []);
  116.  
  117.         //Line := TStringList.Create;
  118.         //Line.Text:=Req.ReadString(9999);
  119.         //Line.SaveToFile('debug.txt');
  120.         //FreeAndNil(Line);
  121.  
  122.       except
  123.       end;
  124.       if FHTTP.ResponseCode <> 200 then
  125.       begin
  126.         if Assigned(FOnWrongVerifier) then
  127.           FOnWrongVerifier(Self)
  128.         else
  129.           MessageDlg('Wrong code! Try again.', mtError, [mbOK], -1);
  130.       end
  131.       else
  132.       begin
  133.         { iegūstam gan access_token, gan refresh_token }
  134.         JSon := SO(UTF8Decode(Resp.DataString));
  135.         FAccessToken := JSon['access_token'].AsString;
  136.         if JSon['refresh_token'] <> nil then
  137.           FRefreshToken := JSon['refresh_token'].AsString;
  138.       end;
  139.     finally
  140.       Req.Free;
  141.       Resp.Free;
  142.     end;
  143.   until FHTTP.ResponseCode = 200;
  144. end;

lucamar

  • Hero Member
  • *****
  • Posts: 2128
Re: Google Tokens
« Reply #4 on: July 01, 2019, 09:41:35 pm »
Also- how can I see whats in TStringStream? (Resp variable on line 60).

Code: Pascal  [Select]
  1. ShowMessage(Resp.DataString);
Turbo Pascal 3 CP/M - Amstrad PCW 8256 (512 KB !!!) :P
Lazarus 2.0.2/2.0.4  - FPC 3.0.4 on:
(K|L)Ubuntu 12..16, Windows XP SP3, various DOSes.

rvk

  • Hero Member
  • *****
  • Posts: 3842
Re: Google Tokens
« Reply #5 on: July 01, 2019, 10:37:55 pm »
Same refresh token does not work from other computer, my Desktop where I build my code. Both folders are synced with Dropbox.
My app opens Oauth2 in browser and asks user consent to copy/paste token from there, and DOES NOT ACCEPT IT.

As I understand from you- each machine needs seperate refresh token, right?
No, I don't think the refresh- and access-tokens are machine specific. They are App (client_id) specific. So your refresh token should work on the other machine.

So you better see what error-message you get back in the JSON and ResponseCode.