Recent

Author Topic: authentiacte through google account  (Read 260 times)

Packs

  • Hero Member
  • *****
  • Posts: 508
authentiacte through google account
« on: July 17, 2026, 08:52:36 pm »
I want to authenticated through google account . My application get freezed on this line  FLocalServer.Active := True;
Code: Pascal  [Select][+][-]
  1. unit ulogin;
  2.  
  3. {$mode ObjFPC}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls,
  9.   LCLIntf, fphttpclient, fphttpserver, opensslsockets, fpjson, jsonparser,
  10.   URIParser, FileInfo;
  11.  
  12. type
  13.  
  14.   { TFrmlogin }
  15.  
  16.   TFrmlogin = class(TForm)
  17.     BtnLogin: TButton;
  18.     lbl_version: TLabel;
  19.     MemoLog: TMemo;
  20.     procedure BtnLoginClick(Sender: TObject);
  21.     procedure FormCreate(Sender: TObject);
  22.   private
  23.     FLocalServer: TFPHttpServer;
  24.     FAuthCode: string;
  25.     procedure OnRequest(Sender: TObject; var ARequest: TFPHTTPConnectionRequest;
  26.       var AResponse: TFPHTTPConnectionResponse);
  27.     function ExchangeCodeForToken(const Code: string): string;
  28.     function GetAppVersionString: string;
  29.   public
  30.  
  31.   end;
  32.  
  33. var
  34.   Frmlogin: TFrmlogin;
  35.  
  36. const
  37.  
  38.   GOOGLE_URL = 'https://accounts.google.com/o/oauth2/v2/auth?';
  39.   CLIENT_ID = 'xxxxxxxx';
  40.   CLIENT_SECRET = 'yyyyy';
  41.  
  42.   // Choose a random high port for localhost redirect
  43.   REDIRECT_PORT = 8080;
  44.   REDIRECT_URI = 'http://localhost:8080/callback';
  45.  
  46. implementation
  47.  
  48. {$R *.lfm}
  49.  
  50. { TFrmlogin }
  51.  
  52. procedure TFrmlogin.BtnLoginClick(Sender: TObject);
  53. var
  54.   AuthURL: string;
  55. begin
  56.   MemoLog.Lines.Add('Starting local listener...');
  57.   FAuthCode := '';
  58.  
  59.   // 1. Set up a quick temporary server to listen for Google's callback
  60.   FLocalServer := TFPHttpServer.Create(nil);
  61.   try
  62.  
  63.     FLocalServer.Active := False;
  64.     FLocalServer.Port := REDIRECT_PORT;
  65.     FLocalServer.OnRequest := @OnRequest;
  66.  
  67.     try
  68.       MemoLog.Lines.Add('Before Active');
  69.       Application.ProcessMessages;
  70.       MemoLog.Lines.Add('Server before Started');
  71.       FLocalServer.Active := True;
  72.       FLocalServer.Threaded := True;
  73.       MemoLog.Lines.Add('Server Started');
  74.     except
  75.       on E: Exception do
  76.         MemoLog.Lines.Add(E.ClassName + ': ' + E.Message);
  77.     end;
  78.     //FLocalServer.Active := True; // Starts listening on background thread
  79.  
  80.     // 2. Build the exact Google Auth Request URL
  81.     AuthURL :=
  82.       'https://accounts.google.com/o/oauth2/v2/auth?' + 'client_id=' +
  83.       CLIENT_ID + '&redirect_uri=' + REDIRECT_URI + '&response_type=code' +
  84.       '&scope=openid%20email%20profile' + '&access_type=offline' +
  85.       '&prompt=consent';
  86.     // Asking for basic user profile
  87.  
  88.     MemoLog.Lines.Add('Opening browser for Google Login...');
  89.  
  90.     // Open system browser (Cross-platform LCL function)
  91.     OpenURL(AuthURL);
  92.  
  93.     // 3. Keep application processing alive until the server catches the code
  94.     while (FAuthCode = '') and (FLocalServer.Active) do
  95.     begin
  96.       Application.ProcessMessages;
  97.       Sleep(50);
  98.     end;
  99.  
  100.   finally
  101.     FLocalServer.Active := False;
  102.     FLocalServer.Free;
  103.   end;
  104.  
  105.   // 4. Once we have the authorization code, exchange it for tokens
  106.   if FAuthCode <> '' then
  107.   begin
  108.     MemoLog.Lines.Add('Auth code received. Exchanging for Access Token...');
  109.     ExchangeCodeForToken(FAuthCode);
  110.   end;
  111. end;
  112.  
  113. procedure TFrmlogin.FormCreate(Sender: TObject);
  114. begin
  115.   lbl_version.Caption := GetAppVersionString;
  116. end;
  117.  
  118. procedure TFrmlogin.OnRequest(Sender: TObject; var ARequest: TFPHTTPConnectionRequest;
  119.   var AResponse: TFPHTTPConnectionResponse);
  120. begin
  121.   if Pos('/callback', ARequest.URL) > 0 then
  122.   begin
  123.     // Extract the authorization code out of the URL query parameters
  124.     FAuthCode := ARequest.QueryFields.Values['code'];
  125.  
  126.     // Respond back to the browser so the user sees a completion message
  127.     AResponse.ContentType := 'text/html; charset=utf-8';
  128.     AResponse.Content :=
  129.       '<h1>Login Successful!</h1><p>You can close this tab now and return to your app.</p>';
  130.  
  131.     // Stop our temporary web server
  132.     FLocalServer.Active := False;
  133.   end;
  134. end;
  135.  
  136. function TFrmlogin.ExchangeCodeForToken(const Code: string): string;
  137. var
  138.   HTTP: TFPHTTPClient;
  139.   RawPayload, ResponseStr: string;
  140.   JSONData: TJSONData;
  141.   AccessToken: string;
  142. begin
  143.   Result := '';
  144.   HTTP := TFPHTTPClient.Create(nil);
  145.   try
  146.     try
  147.       // Format parameters into standard x-www-form-urlencoded format
  148.       RawPayload := 'code=' + Code + '&client_id=' + CLIENT_ID +
  149.         '&client_secret=' + CLIENT_SECRET + '&redirect_uri=' +
  150.         REDIRECT_URI + '&grant_type=authorization_code';
  151.  
  152.       HTTP.AddHeader('Content-Type', 'application/x-www-form-urlencoded');
  153.  
  154.       // Prepare the client payload
  155.       HTTP.RequestBody := TStringStream.Create(RawPayload, TEncoding.UTF8);
  156.  
  157.       // Exchange the authorization code at Google's endpoint
  158.       ResponseStr := HTTP.Post('https://oauth2.googleapis.com/token');
  159.  
  160.       // Parse the returned JSON response safely
  161.       JSONData := GetJSON(ResponseStr);
  162.       try
  163.         if Assigned(JSONData.FindPath('access_token')) then
  164.         begin
  165.           AccessToken := JSONData.FindPath('access_token').AsString;
  166.           MemoLog.Lines.Add('Access Token obtained: ' +
  167.             Copy(AccessToken, 1, 15) + '...');
  168.           Result := AccessToken;
  169.  
  170.           // Note: You can also extract an "id_token" which is a JWT containing name, email, and photo!
  171.         end
  172.         else
  173.         begin
  174.           MemoLog.Lines.Add('Failed to get token: ' + ResponseStr);
  175.         end;
  176.       finally
  177.         JSONData.Free;
  178.       end;
  179.  
  180.     except
  181.       on E: Exception do
  182.         MemoLog.Lines.Add('Network Exception: ' + E.Message);
  183.     end;
  184.   finally
  185.     HTTP.RequestBody.Free;
  186.     HTTP.Free;
  187.   end;
  188. end;
  189.  
  190. function TFrmlogin.GetAppVersionString: string;
  191. var
  192.   FileVerInfo: TFileVersionInfo;
  193.   sVer: string;
  194. begin
  195.   sVer := '1.0.0.0'; // Fallback default
  196.   FileVerInfo := TFileVersionInfo.Create(nil);
  197.   try
  198.     // Read the version data from the currently running executable
  199.     FileVerInfo.ReadFileInfo;
  200.     sVer := Format('%s', [FileVerInfo.VersionStrings[3]]);
  201.  
  202.     Result := sVer;
  203.   finally
  204.     FileVerInfo.Free;
  205.   end;
  206.  
  207. end;
  208.  
  209.  
  210. end.
  211.  

dbannon

  • Hero Member
  • *****
  • Posts: 3882
    • tomboy-ng, a rewrite of the classic Tomboy
Re: authentiacte through google account
« Reply #1 on: July 18, 2026, 08:15:25 am »
Interesting code Packs, I'd like to see how you go here.
Questions ?
  • Should you set the server to multithreaded after setting the server active ? Does not look good to me.
  • Don't you need a google auth account to do this ?
  • Finally, (and here might be your problem) by running the server as multithreaded, the server will launch multiple threads all listening. I don't think it works the you seem to think, launch one listening thread and continue the programme loop. But that is just a guess.


I have an app that starts a (single threaded) server, in the StartUp() method of https://github.com/tomboy-notes/tomboy-ng/blob/master/experimental/Misty-Small/twebserver.pas #689 I get all the settings right, including a call back methods, and then set active to true. It does not return until termination.

If you need to keep doing stuff after the server is started, I suspect you need to create your own new thread and run the server there.

I think !  (hoping I am not just adding confusion)

Davo
Lazarus 4, Linux (and reluctantly Win10/11, OSX Monterey)
My Project - https://github.com/tomboy-notes/tomboy-ng and my github - https://github.com/davidbannon

Packs

  • Hero Member
  • *****
  • Posts: 508
Re: authentiacte through google account
« Reply #2 on: July 18, 2026, 08:32:15 am »
Sir ,

I need login with google account functionalty in my application .

user will login through his/her gmail account in my system.


Thaddy

  • Hero Member
  • *****
  • Posts: 19625
  • Glad to be alive.
Re: authentiacte through google account
« Reply #3 on: July 18, 2026, 08:45:18 am »
Google just uses OAUTH2 and there is an example in the Google package.
I believe dbannon's code works in a similar way.

There are many more examples on the forum for OAUTH2, not just using Google API's.

Have a look at https://github.com/rvk01/google-oauth2 and that is really the simplest.
(and rvk is on this forum and very helpful)
« Last Edit: July 18, 2026, 08:52:33 am by Thaddy »
Any "programmer" that knows only one programming language is not a programmer

Thaddy

  • Hero Member
  • *****
  • Posts: 19625
  • Glad to be alive.
Re: authentiacte through google account
« Reply #4 on: July 18, 2026, 09:12:56 am »
A very very very simplest example, for the purpose of the demo, uses a gmail login.:
First step: make sure you have the right Google credentials.
https://console.cloud.google.com/
Well, simpler than simple, gets you only a valid response. Uses only standard units:
Code: Pascal  [Select][+][-]
  1. program GmailOAuth2Minimal;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.   Classes, SysUtils, fphttpclient, opensslsockets, fpjson, jsonparser;
  7.  
  8. const
  9.   // REPLACE THESE WITH YOUR VALUES
  10.   CLIENT_ID = 'your-client-id.apps.googleusercontent.com';
  11.   CLIENT_SECRET = 'your-client-secret';
  12.   REDIRECT_URI = 'http://localhost';  // Must match Google Console config
  13.   AUTH_CODE = '4/0AX4XfWi...'; // The code from Step 1
  14.  
  15.   TOKEN_URL = 'https://oauth2.googleapis.com/token';
  16.  
  17. function ExchangeAuthCodeForToken(AuthCode: string): string;
  18. var
  19.   HTTP: TFPHTTPClient;
  20.   RequestBody: TStringStream;
  21.   Response: string;
  22. begin
  23.   HTTP := TFPHTTPClient.Create(nil);
  24.   RequestBody := TStringStream.Create('', TEncoding.UTF8);
  25.   try
  26.     HTTP.RequestHeaders.Clear;
  27.     HTTP.RequestHeaders.Add('Content-Type: application/x-www-form-urlencoded');
  28.    
  29.     // Build the POST body
  30.     RequestBody.WriteString(Format(
  31.       'grant_type=authorization_code&' +
  32.       'code=%s&' +
  33.       'redirect_uri=%s&' +
  34.       'client_id=%s&' +
  35.       'client_secret=%s',
  36.       [AuthCode, REDIRECT_URI, CLIENT_ID, CLIENT_SECRET]
  37.     ));
  38.     RequestBody.Position := 0;
  39.    
  40.     // Make the POST request
  41.     Response := HTTP.Post(TOKEN_URL, RequestBody);
  42.    
  43.     // Pretty print the JSON response for readability
  44.     Result := Response;
  45.    
  46.     // Optional: Parse and extract tokens
  47.     // var JSON := GetJSON(Response) as TJSONObject;
  48.     // Writeln('Access Token: ', JSON.Get('access_token', ''));
  49.     // Writeln('Refresh Token: ', JSON.Get('refresh_token', ''));
  50.    
  51.   finally
  52.     HTTP.Free;
  53.     RequestBody.Free;
  54.   end;
  55. end;
  56.  
  57. var
  58.   TokenResponse: string;
  59. begin
  60.   // Enable SSL support
  61.   InitSSLInterface;
  62.  
  63.   if AUTH_CODE = '4/0AX4XfWi...' then
  64.   begin
  65.     Writeln('ERROR: You must replace AUTH_CODE with your actual authorization code');
  66.     Writeln;
  67.     Writeln('To get a code, open in your browser:');
  68.     Writeln(Format(
  69.       'https://accounts.google.com/o/oauth2/auth?' +
  70.       'client_id=%s&' +
  71.       'redirect_uri=%s&' +
  72.       'scope=https://mail.google.com/&' +
  73.       'response_type=code&' +
  74.       'access_type=offline',
  75.       [CLIENT_ID, REDIRECT_URI]
  76.     ));
  77.     Exit;
  78.   end;
  79.  
  80.   try
  81.     Writeln('Exchanging authorization code for tokens...');
  82.     TokenResponse := ExchangeAuthCodeForToken(AUTH_CODE);
  83.     Writeln('SUCCESS! Google responded with:');
  84.     Writeln(TokenResponse);
  85.    
  86.   except
  87.     on E: Exception do
  88.     begin
  89.       Writeln('Error: ', E.Message);
  90.       Writeln('Check your client_id, client_secret, and auth_code');
  91.     end;
  92.   end;
  93.  
  94.   Readln; // Pause to see output
  95. end.
Please follow the steps in the comments EXACTLY! otherwise it won't work. And don't hard code the credentials in of course.

rvk's code I linked to above is much better and more complete than this.

The subject is not easy for beginners, be aware of that. This code is a reduction to the bare minimum.

Maybe one brilliant mind can come up with something easier still, surprise me.
« Last Edit: July 18, 2026, 09:56:25 am by Thaddy »
Any "programmer" that knows only one programming language is not a programmer

 

TinyPortal © 2005-2018