I want to work with Google Api using Rest (Delphi XE7), do it this way:
uses
...
{$IF DEFINED(ANDROID)}
REST.Authenticator.OAuth.WebForm.FMX;
{$ENDIF}
{$IF DEFINED(MsWindows)}
REST.Authenticator.OAuth.WebForm.Win;
{$ENDIF}
That procedure is working on Windows, after that programm changes AuchCode to access token and everything working.
procedure TForm2.OAuth2_GoogleTasks_BrowserTitleChanged(const ATitle: string; var DoCloseWebView: boolean);
begin
if Pos('Success code', ATitle) > 0 then
begin
AuthCode := Copy(ATitle, 14, Length(ATitle));
if (AuthCode <> '') then
begin
editactoken.Text:= AuthCode;
DoCloseWebView := true;
webform.Release;
end;
end;
end;
procedure TForm2.Button59Click(Sender: TObject);
begin
WebForm:=Tfrm_OAuthWebForm.Create(nil);
WebForm.OnTitleChanged := self.OAuth2_GoogleTasks_BrowserTitleChanged;
WebForm.ShowWithURL(OAuth2Authenticator1.AuthorizationRequestURI);
end;
But I've met a trouble that I cant copy auth code on android:
Form2.OAuth2_GoogleTasks_BrowserTitleChanged doesn't work because title doesn't change;
There is no title at all in REST.Authenticator.OAuth.WebForm.FMX :
private
{ Private declarations }
FOnBeforeRedirect: TOAuth2WebFormRedirectEvent;
FOnAfterRedirect: TOAuth2WebFormRedirectEvent;
FOnBrowserTitleChanged : TOAuth2WebFormTitleChangedEvent;
FLastURL: string;
public
while in REST.Authenticator.OAuth.WebForm.Win it looks like this:
private
{ Private declarations }
FOnBeforeRedirect: TOAuth2WebFormRedirectEvent;
FOnAfterRedirect: TOAuth2WebFormRedirectEvent;
FOnBrowserTitleChanged : TOAuth2WebFormTitleChangedEvent;
FLastTitle: string; // <-------
FLastURL: string
there is no property webform.lasttitle in fmx variant (in win i could get it)
LastUrl property looks like that after redirecting : "......" but I can't understand how to use it to get auth code.
Try to use IFMXClipboardService but could not get this text from Twebbrowser.
Google not allowed to use Http get method for lastUrl adress to get response and parse this code (error 405).
I've read some article that there is no way to get html code from webbrowser fmx, is it so?
Any ideas how can I get this code in my programm?
Android (with Google Play Services installed) provides a native solution for authentication with other Google APIs, where no web browser is required:
See https://developers.google.com/android/guides/http-auth:
When you want your Android app to access Google APIs using the user's
Google account over HTTP, the GoogleAuthUtil class and related APIs
provide your users a secure and consistent experience for picking an
account and retrieving an OAuth 2.0 token for your app.
You can then use that token in your HTTP-based communications with
Google API services that are not included in the Google Play services
library, such as the Blogger or Translate APIs.
I suggest to do some research in this direction. The page linked above also mentions the GoogleApiClient for some (non HTTP) services.
Related
Currently I have a code like this to display a google map with my current location in my TWebBrowser
procedure TForm1.LocationSensor1LocationChanged(Sender: TObject; const
OldLocation, NewLocation: TLocationCoord2D);
begin
var URLString := Format('https://maps.google.com/maps?q=%s,%s&output=embed', [Format('%2.6f', [NewLocation.Latitude]), Format('%2.6f', [NewLocation.Longitude])]);
WebBrowser1.Navigate(URLString);
end;
If I use my URL as https://maps.google.com/maps?q=%s,%s then it works properly but when I use my URL as https://maps.google.com/maps?q=%s,%s&output=embed then it will prompt an error "The Google Maps Embed API must be used in an iframe" as shown in the picture
Is there a way I could have an iframe in my delphi project?
As the error message says, Google's embedded map wants to be hosted in an HTML <iframe>. TWebBrowser has a LoadFromStrings() method that you can use for that purpose, eg:
procedure TForm1.LocationSensor1LocationChanged(Sender: TObject;
const OldLocation, NewLocation: TLocationCoord2D);
begin
var URL := Format('https://maps.google.com/maps?q=%2.6f,%2.6f&output=embed', [NewLocation.Latitude, NewLocation.Longitude]);
var HTML = Format('<iframe src="%s" width="%d" height="%d" style="border:0;" allowfullscreen="" loading="lazy"></iframe>', [URL, <DesiredWidth>, <DesiredHeight>]);
WebBrowser1.LoadFromStrings(HTML, URL);
end;
This code is working in a Firemonkey Windows app, but doesn't work in Android app, i get Goodbye instead of Welcome, what is wrong?
Edit8 Text : 162496 //Computer unique code
Edit9 Text : 1564224593 //serial #
procedure TForm2.Button5Click(Sender: TObject);
var
f2,f1:textfile;
i,j:byte;
s1,s2,s3,c:string;
F: TextFile;
begin
j:=0;
s2 := Edit8.Text;
for i:=1 to Length(s2) do
if (s2[i]>='0') and (s2[i]<='9') then
s3:=s3+s2[i];
for i:=1 to Length(s3)-1 do
if (edit9.Text[i*2-1]<>s3[i]) or (abs(strtoint(s3[i+1])-strtoint(s3[i]))<> strtoint(edit9.Text[i*2])) then
inc(j);
if j=0 then
ShowMessage('Welcome')
else
ShowMessage('Goodbye');
end;
Delphi mobile compilers use zero-based strings.
You have three choices:
As #Günter_the_Beautiful points out, your best choice is to rewrite your code to use string helpers (which are always 0-based)
Rewrite your code to use 0-based indexing: for I := 0 to ...
If you need a quick fix, turn it off locally for your code snippet using the {$ZEROBASEDSTRINGS OFF} directive (and revert it back with {$ZEROBASEDSTRINGS ON} again).
For options 2. and 3., if you need your code to be cross-platform, consider using appropriate platform conditional defines. This is what makes option 1. compelling: no need to clutter your code with conditional defines.
I am using these two helper routines:
FUNCTION GetChar(CONST S : STRING ; OneBasedIndex : LongWord) : CHAR;
BEGIN
{$IF CompilerVersion>=24 }
Result:=S[SUCC(OneBasedIndex-LOW(S))]
{$ELSE }
Result:=S[OneBasedIndex]
{$IFEND }
END;
PROCEDURE SetChar(VAR S : STRING ; OneBasedIndex : LongWord ; NewChar : CHAR);
BEGIN
{$IF CompilerVersion>=24 }
S[SUCC(OneBasedIndex-LOW(S))]:=NewChar
{$ELSE }
S[OneBasedIndex]:=NewChar
{$IFEND }
END;
This way, you can continue working with strings as 1-based (which is the logical choice :-)) as long as you always access the strings as characters using these two functions.
I am finding the way to validate ID token for Google sign-in for Android with a Go backend server project.
What is the equivalent function for validating ID tokens by using a Google API Client Library in Go?
From this page on Using a Google API Client Library section
https://developers.google.com/identity/sign-in/android/backend-auth#using-a-google-api-client-library
There are Java and Python examples and there are links for verify ID tokens with the Google API Client Library for PHP, Node.js, and other languages. I checked for my target language; Go here
https://github.com/google/google-api-go-client/blob/master/GettingStarted.md
However, I found not equivalent function for validating token like in Java and Python example. Is there any function in Go for doing such thing?
I don't want to use token info endpoint
https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=XYZ123
since it introduces possible latency and network error. I wish to use Google API Client Library. Please guide me where should I look into.
This is how I've done it using https://github.com/google/google-api-go-client library:
import (
"google.golang.org/api/oauth2/v2"
"net/http"
)
var httpClient = &http.Client{}
func verifyIdToken(idToken string) (*oauth2.Tokeninfo, error) {
oauth2Service, err := oauth2.New(httpClient)
tokenInfoCall := oauth2Service.Tokeninfo()
tokenInfoCall.IdToken(idToken)
tokenInfo, err := tokenInfoCall.Do()
if err != nil {
return nil, err
}
return tokenInfo, nil
}
oauth2.Tokeninfo object has info about the user.
Note that this makes a call to https://www.googleapis.com/oauth2/v2/tokeninfo and I think that all Google API Client Libraries make this http call under the hood.
It's very easy and has a one-liner solution. Just use the Official library:
go get google.golang.org/api/idtoken
and then write this code:
payload, err := idtoken.Validate(context.Background(), tokenString, audience)
if err != nil {
panic(err)
}
fmt.Print(payload.Claims)
Then you will get this output:
map[
aud:<Your web application client id>
azp:<Your android application client id>
email:<Authenticated user email>
email_verified:true
exp:<expire at>
family_name:<Authenticated user lastname>
given_name:<Authenticated user firstname>
iat:<issued at>
iss: <accounts.google.com or https://accounts.google.com>
locale:en
name:<Authenticated User fullname>
picture:<Authenticated User Photo URL>
sub: <Google Account ID [Use this to identify a id uniquely]>
]
Google's idToken is actually in JWT format, which is compact and self-contained JSON with signature.
See also: https://jwt.io/introduction/
google-auth-library-nodejs's OAuth2Client.prototype.verifyIdToken verify the idtoken using Google's public key and extract ClaimSet from the idtoken without calling the tokeninfo endpoint.
I just ported the verifyIdToken function from google-auth-library-nodejs, and created a library for this: https://github.com/futurenda/google-auth-id-token-verifier.
Usage:
import (
"github.com/futurenda/google-auth-id-token-verifier"
)
v := googleAuthIDTokenVerifier.Verifier{}
aud := "xxxxxx-yyyyyyy.apps.googleusercontent.com"
err := v.VerifyIDToken(TOKEN, []string{
aud,
})
if err == nil {
claimSet, err := googleAuthIDTokenVerifier.Decode(TOKEN)
// claimSet.Iss,claimSet.Email ... (See claimset.go)
}
import (
"google.golang.org/api/idtoken"
)
var token string // this comes from your web or mobile app maybe
const googleClientId = "" // from credentials in the Google dev console
tokenValidator, err := idtoken.NewValidator(context.Background())
if err != nil {
// handle error, stop execution
}
payload, err := tokenValidator.Validate(context.Background(), token, googleClientId)
if err != nil {
// handle error, stop execution
}
email := payload.Claims["email"]
name := payload.Claims["name"]
// and so on...
You may need to provide your Google credentials to your application:
https://cloud.google.com/docs/authentication/production
I want to ping to a server with indy component TidIcmpClient in Android plattform but the debugger stops the code with a "Socket Error #1". The code is in a separate thread, so i post here the thread code:
procedure TEco.Execute;
var
contadoreco: Cardinal;
buffer: string;
begin
buffer:='12345678901234567890123456789012';
eco:=TIdIcmpClient.Create(nil);
for contadoreco:=1 to 4 do
begin
with eco do
begin
ReceiveTimeout:=2000;
Host:=servidor;
PacketSize:=32;
Ping(buffer,contadoreco);
ipservidor:=ReplyStatus.FromIpAddress;
end;
Synchronize(procedure
begin
Form1.StringGrid1.Cells[0,contadoreco]:=ipservidor;
Form1.StringGrid1.Cells[1,contadoreco]:=IntToStr(tiempoeco);
end);
end;
eco.Free;
end;
The TEco object is declared here:
TEco = class(TThread)
private
servidor: string;
eco: TIdIcmpClient;
terminado: Boolean;
tiempoeco: Cardinal;
ipservidor: string;
protected
procedure Execute; override;
end;
How i can ping a server with TidIcmpClient in Android? Am i doing something wrong? Superuser rights or some so? Thanks in advance for help me, and sorry for poor english. I expect you understand my question ;) :)
TIdIcmpClient uses a RAW socket, which requires admin/root access on most systems, including Android.
There are two ways to perform a ping in Android using Android's own APIs:
use the isReachable() method of the InetAddress class. However, apparently this does not work correctly.
Use java.lang.ProcessBuilder() to spawn /system/bin/ping. In fact, the java.lang.Process documentation shows an example of that. The downside is that you would have to manually parse the output.
Either solution would require you to use Delphi's JNI wrapper to access the relevant Android APIs.
I'm trying to use Indy's TIdHTTP component to send data to a website.
The code works perfectly on Windows platform but unfortunately it behaves strangely on Android platform. The problem occurs when I use TIdMultipartFormDataStream to send POST parameters.
On Android platform TIdMultipartFormDataStream behaves strangely and this what happens:
Suppose your POST data is myparam=myvalue where "myparam" is parameter name and "myvalue" is the parametervalue.
The parameter values gets changed to {FIRST CHARACTER REMOVED}yvalue{NULL CHARACTER} so the final output will look like this yvalue\x00 where \x00 is a null character.
I can replace TIdMultipartFormDataStream with TStringList and I won't face such issue but I prefer to use TIdMultipartFormDataStream because it enables me to upload files + send POST data at the same time.
Sample code:
procedure HTTPPOST;
var
HTTP: TIdHTTP;
POSTData: TIdMultipartFormDataStream;
begin
HTTP := TIdHTTP.Create(nil);
POSTData := TIdMultipartFormDataStream.Create;
try
POSTData.AddFile('myfile','file.txt'); // works
POSTData.AddFormField('username', 'user1'); // On Android the value gets changed to ser1\x00 where \x00 = Null character
HTTP.Post('http://www.example.com', POSTData)
finally
POSTData.Free;
end;
end;
Note: the code was tested using Delphi XE5 and Delphi XE5 Update 1
What you describe sounds like a ZEROBASEDSTRINGS bug, which should not exist in the current SVN version because Indy disables ZEROBASEDSTRINGS globally in all of its units after it kept suffering from lots of ZBS bugs in XE4. So I do suggest you upgrade to the lastest SVN verson. If you are having problems doing so, please update your question with details explaining why
Works..
procedure HTTPPOST;
var
HTTP: TIdHTTP;
POSTData: TIdMultipartFormDataStream;
begin
HTTP := TIdHTTP.Create(nil);
POSTData := TIdMultipartFormDataStream.Create;
try
POSTData.AddFile('myfile','file.txt'); // works
POSTData.AddFormField('username', UTF8Encode('user1'), 'utf-8').ContentTransfer:= '8bit';
HTTP.Post('http://www.example.com', POSTData)
finally
POSTData.Free;
end;
end;