Ik heb nog wat zitten stoeien en ik denk dat ik in de goede richting zit maar kom niet verder.
allereerst stukje gekopieerd
er werkt wel iets maar geeft niet het verwachte resultaat.
de response die ik krijg is
als ik die direct in de brouwser plak, moet ik inloggen op gmail en ga ik naar https://www.google.com/settings/personalinfo
kan iemand beetje basis uitleggen?
HTML Code:<HTML> <HEAD> <TITLE>Moved Temporarily</TITLE> </HEAD> <BODY BGCOLOR="#FFFFFF" TEXT="#000000"> <H1>Moved Temporarily</H1> The document has moved <A HREF="https://accounts.google.com/ServiceLogin?service=lso&passive=1209600&continue=https://accounts.google.com/o/oauth2/auth?scope%3Demail%2Bprofile%26response_type%3Dcode%26redirect_uri%3Durn:ietf:wg:oauth:2.0:oob%26client_id%3D1061037677408-iicgfue8cokmu3lebit9drju6n1tkuar.apps.googleusercontent.com%26hl%3Dnl%26from_login%3D1%26as%3D-59445728c703613&ltmpl=embedded&shdf=CnMLEhF0aGlyZFBhcnR5TG9nb1VybBoADAsSFXRoaXJkUGFydHlEaXNwbGF5TmFtZRoEdGVzdAwLEgZkb21haW4aBHRlc3QMCxIVdGhpcmRQYXJ0eURpc3BsYXlUeXBlGhJOQVRJVkVfQVBQTElDQVRJT04MEgNsc28iFElHU7Ft3nNQTyqE1q59shBaYXErKAEyFGgmzeEIj14BUjMBMAcY2pdtKJon&sarp=1&scc=1">here</A>. </BODY> </HTML>Gert-WillemCode:procedure Tklantgegevens.Button9Click(Sender: TObject); var URL: string; Params: string; Response: TMemoryStream; Response2: Tstrings; begin Response := TMemoryStream.Create; try URL := 'https://accounts.google.com/o/oauth2/auth?'; Params := 'response_type=' + EncodeURLElement('code') + '&client_id=' + EncodeURLElement('mijn google key Client ID for native application') + '&redirect_uri='+ EncodeURLElement('urn:ietf:wg:oauth:2.0:oob')+ '&scope=' + EncodeURLElement('email profile')//('https://www.googleapis.com/auth/calendar'); if HttpPostURL(URL, Params, Response) then Begin Response.SaveToFile('c:\ski\response.htm'); //met de hand url uit response.htm gehaald url := inputbox('hoi',url,''); params :=''; end; Response2 := Tstringlist.Create; if Httpgettext(URL, Response2) then// ook geprobeerd met httpposturl Begin Response.SaveToFile('c:\ski\response2.htm'); showmessage(response2.Text ); End; finally Response.Free; end; end;
Gaat bij mij prima.Code:https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=1111111111111-dddddddddddddddddddddddddddddddd.apps.googleusercontent.com&redirect_uri=urn:ietf:wg:oauth:2.0:oob&scope=email%20profile
Als ik dit in de browser intik moet ik wel eerst inloggen (als ik dat nog niet had gedaan) op een Google account (LET OP... dit is niet GMail maar gewoon Google account) en daarna krijg ik dit:
Daarna krijg ik dit:
Hoe had je dit inloggen overigens willen doen vanuit een programma?
Volgens mij krijgt je programma n.l. na je eerste HttpPostURL een account-kies scherm van Google.
(net zoals je in de browser krijgt als je niet ingelogd bent.)
Volg je deze?:
https://developers.google.com/accoun...uth2ForDevices
dat bedoel ik dus, ik wil automatiseren, alle andere handelingen moeten weg blijven, zoals ik in mijn eerdere berichten had gezegd.
Ik vind het vrij irritant dat er zo weinig over beschreven is in pascal, en ik weet eigenlijk alleen maar omdat er andere programma's zijn die het kunnen dat het moet kunnen.
als ik een response krijg, zou ik daar toch automatisch weer op moeten kunnen reageren.
nou heb ik idd ook die code dus dan zou ik daarmee toch in een keer een afspraak kunnen zetten in de agenda.
dat google vertalen werkt wel maar het blijft lastig lezen.
de link die jij stuurde was voor apparaten, ik dacht dat ik https://developers.google.com/accoun...h2InstalledApp moest volgen.
bedankt voor je geduld, mysql database is makkelijker te gebruiken dan dit
Al deze methodes vragen om een gebruikers authenticatie. Dus je zult altijd een browserscherm moeten laten zien (eerste keer) om toestemming te vragen. Ik zal morgen eens kijken of ik de methode van het p12-keybestand nog kan vinden. Die heb ik een keer gebruikt voor Google drive authenticatie en dat behoefde geen aparte gebruikers authenticatie.
Je zou normaal gesproken inderdaad direct een JSON terug moeten krijgen met een URL die je de gebruiker kan tonen voor authenticatie. Alleen als ik zie dat die pagina "Moved Temporarily" is dan denk ik dat Google weer fijn bezig is. Je zou wel direct de gebruiker naar de browser kunnen sturen met deze URL en dan krijgt ie een lange code terug die jij weer moet gebruiken. Maar zoals het nu is moet die gebruiker dat dus zelf intikken. Een andere methode is dus die voor devices gebruiken. De methode is bijna hetzelfde maar nu haal jij een code op die de gebruiker in de browser moet plakken voor akkoord. Lekker
De methode die ik ooit gebruikt heb zonder authenticatie is voor services. Je kunt dan een p12-key bestand downloaden waar een private key in staat. De eerste initiële communicatie doe je daarmee. Dat heb ik in PHP prima werkend gekregen (ik had toegang tot mijn agenda zonder ooit iets in de browser te hebben hoeven doen). Het probleem in Lazarus/Delphi is echter het signen van een gedeelte van de code met SHA256 en RSA. Dit zou je wel met openssl moeten kunnen doen (want dat doet PHP ook) maar dat heb ik niet werkend gekregen in Lazarus. (Misschien iemand anders die dit weet?)
Anders blijft misschien de methode over om gewoon een embeded webbrowser te laten zien en de URL aan te roepen. Als je uiteindelijk die lange code te zien krijgt in je pagina kun je dat zelf afvangen en bij de eerste communicatie dus de refresh_token opslaan om voortaan te gebruiken.
Mocht je overigens het voorbeeld met de service account zien... die staat hieronder.
Je kunt op dezelfde plaats een key aanmaken. In de Google API Console een "Create Client ID" en dan kiezen voor "Service account".
Je kunt het automatisch gedownloade .p12 bestand gebruiken als key.
Wat je wel even moet doen is in je agenda volledig toegang verlenen aan je @developer.gserviceaccount.com
(Dit doe je bij je agenda-instellingen onder "Shared: Edit settings" en dan "Share with specific people".)
Bij mij werkt onderstaande code perfect (dus zonder authenticatie):
Tsja... en het vertalen naar Lazarus....PHP Code:
<?php
error_reporting(E_ALL);
ini_set('display_errors', 'On');
require_once 'Google/Client.php';
require_once 'Google/Service/Calendar.php';
// https://console.developers.google.com/
$myTitle = 'Simple Calendar2';
$myClientId = '1000000000000-hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh.apps.googleusercontent.com';
$myServiceAccount = '1000000000000-hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh@developer.gserviceaccount.com';
$myKeyfile = 'mijn_private_key.p12';
$calendar_id = 'mijngmail@gmail.com';
$client = new Google_Client();
$client->setApplicationName($myTitle);
$client->setClientId($myClientId);
$key = file_get_contents($myKeyfile);
$client->setAssertionCredentials(new Google_Auth_AssertionCredentials(
$myServiceAccount,
array("https://www.googleapis.com/auth/calendar"),
$key)
);
if (true){
echo "<hr><font size=+1>I have access to your calendar</font>";
if (isset($_GET['subject'])) {
@$subject=rawurldecode($_GET['subject']);
@$location=rawurldecode($_GET['location']);
@$start_dt=rawurldecode($_GET['start']);
@$end_dt=rawurldecode($_GET['end']);
echo "<br><br>";
echo "Subject: $subject<br>";
echo "Location: $location<br>";
echo "Start: $start_dt<br>";
echo "End: $end_dt<br>";
// objecten creeren
$cal = new Google_Service_Calendar($client);
$event = new Google_Service_Calendar_Event();
$start = new Google_Service_Calendar_EventDateTime();
$end = new Google_Service_Calendar_EventDateTime();
// tijden vullen
$start->setDateTime($start_dt);
$end->setDateTime($end_dt);
// event vullen
$event->setSummary($subject);
$event->setLocation($location);
$event->setStart($start);
$event->setEnd($end);
// toevoegen
$createdEvent = $cal->events->insert($calendar_id, $event);
//
echo "<br><font size=+1>Event created</font><br>";
echo '<meta http-equiv=refresh content="4;url=http://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'] . '">';
echo "... autoreload in 4 seconds";
} else {
$subject='Afspraak';
$location='Ergens';
$start_dt=date('Y-m-d\TH:i:sP', strtotime('now'));
$end_dt=date('Y-m-d\TH:i:sP', strtotime('now + 1 hour'));
echo "<br><font size=+1>You need to specify an event in the URL</font>";
echo "<br><br>Example:<br>";
echo "<br>";
echo "Subject: $subject<br>";
echo "Location: $location<br>";
echo "Start: $start_dt<br>";
echo "End: $end_dt<br>";
echo "<br>";
$url = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'];
$url .= "?subject=".rawurlencode($subject)."&location=".rawurlencode($location)."&start=".rawurlencode($start_dt)."&end=".rawurlencode($end_dt);
echo "<a href=$url>$url</a>";
}
}
?>
Het probleem zit hem dus in die setAssertionCredentials.
In de manual zie je dat je een JWT aan moet maken en die moet "tekenen" met je private key. Ik ben zover gekomen dat ik de eerste twee gedeeltes van de JWT wel kan maken maar deze dus niet kan ondertekenen met de private key.
Delphi Code:
uses synacode; var jwt_header: string; jwt_claim: string; jwt: string; sign: string; Nu: integer; begin Nu := Trunc((Now - EncodeDate(1970, 1, 1)) * 24 * 60 * 60); jwt_header := '{"alg":"RS256","typ":"JWT"}'; jwt_claim := '{' + '"iss":"' + client_id + '",' + '"scope":"https://www.googleapis.com/auth/calendar",' + '"exp":' + IntToStr(Nu + 3600) + ',' + '"iat":' + IntToStr(Nu) + '}'; jwt := EncodeBase64(jwt_header) + '.' + EncodeBase64(jwt_claim); // jwt moet in deze vorm gesigned worden met een private key // SHA256 en RSA // code uit Google.php api // $this->privateKey = openssl_pkey_get_private($certs["pkey"]); // if (!openssl_sign($data = jwt, $signature = alleen output, $this->privateKey, "sha256")) jwt := jwt + '.' + EncodeBase64(sign); ShowMessage(jwt);
Pffffff. Je moet dus gewoon met jouw eerste URL een webbrowser openen en de gebruiker de toestemming laten geven. Jij moet de browser "aftasten" en de Success-code komt in de TITEL van de browser te staan. Dus die moet je continu in de gaten houden en als er Sucesscode staat dan pak je die dus om verder te gaan
urn : ietf : wg : oauth : 2.0 : oob
This value signals to the Google Authorization Server that the authorization code should be returned in the title bar of the browser, with the page text prompting the user to copy the code and paste it in the application (as shown in the screenshot above). This is useful when the client (such as a Windows application) cannot listen on an HTTP port without significant client configuration.
When you use this value, your application can then detect that the page has loaded, and can read the title of the HTML page to obtain the authorization code. It is then up to your application to close the browser window if you want to ensure that the user never sees the page that contains the authorization code. The mechanism for doing this varies from platform to platform.
If your platform doesn't allow you to detect that the page has loaded or read the title of the page, you can have the user paste the code back to your application, as prompted by the page text.
urn : ietf : wg : oauth : 2.0 : oob : auto
This is identical to urn : ietf : wg : oauth : 2.0 : oob, but the text in the confirmation page won't instruct the user to copy the authorization code, but instead will simply ask the user to close the window.
This is useful when your application reads the title of the HTML page (by checking window titles on the desktop, for example) to obtain the authorization code, but can't close the page on its own.
ehhh ja,
browser aftasten?? tuuuurlijk
Nee windows is oké.
maar als het eenmalig is dan lijkt me het niet nodig. als er een token is blijft deze toch geldig?
maar ik ben er wel nieuwsgierig naar want dan kan ik daar ook eens mee spelen. Ik heb vanuit een programma met internet spelen heb ik totaal geen ervaring.
!@#$%^&* - Ik merk nét dat de "Success code" die Google in de Tittel-balk van de browser terug geeft, afgekapt wordt in Chrome.
In Chrome notabene !!
Dus het uitlezen van alle Windows en zoeken naar "Success code" werkt niet omdat je dan een foute code terug krijgt.
Back to the drawing board
De gebruiker de code eenmalig laten knippen en plakken in je applicatie werkt nog gewoon wel.
Je moet daarna gelijk een refresh_token vragen zodat je die dus daarna altijd kunt blijven gebruiken.
Ik denk dat de enige goede andere oplossing is om de browser te embeden zodat je die uit kunt lezen
maar ik zie niet zo 123 een kant en klare oplossing daarvoor in Lazarus.
(ActiveX misschien importeren en zelf de browser embeden?)
Dit was overigens de code met uitlezen van de Window-titles (dus dus niet werkt voor Success code):
(1 knop om authorize aan te roepen en 1 knop om FoundTitle op Success code=abort te zetten om e.v. je programma af te breken uit een loop)Delphi Code:
const client_id = '11111111111111111111111111111111111111111111111.apps.googleusercontent.com'; client_secret = '111111111111111111111111'; SearchFor = 'Success code='; var FoundTitle: string; function EnumWindowsProc(WHandle: HWND; LParM: LParam): longbool; stdcall; export; var Title: array[0..128] of char; sTitle: string; begin Result := True; GetWindowText(wHandle, Title, 128); if IsWindowVisible(wHandle) then begin sTitle := Title; Form1.Memo1.Lines.Add(sTitle); if Pos(SearchFor, sTitle) = 2 then begin // Success code = 4/gP1111111111111111kQaiQbz02 - Google Chrome System.Delete(sTitle, 1, Length(SearchFor) + 1); if Pos('-', sTitle) > 0 then sTitle := Copy(sTitle, 1, Pos('-', sTitle) - 1); FoundTitle := Trim(sTitle); end; end; end; procedure ScanForSuccess; begin FoundTitle := ''; while FoundTitle = '' do begin EnumWindows(@EnumWindowsProc, 0); Sleep(500); Application.ProcessMessages; end; end; function PleaseAuthorize: string; var URL: string; Params: string; begin Result := ''; URL := 'https://accounts.google.com/o/oauth2/auth'; Params := ''; Params := Params + 'response_type=' + EncodeURLElement('code'); Params := Params + '&client_id=' + EncodeURLElement(client_id); Params := Params + '&redirect_uri=' + EncodeURLElement('urn:ietf:wg:oauth:2.0:oob'); Params := Params + '&scope=' + EncodeURLElement('email profile'); //OpenUrl(URL + '?' + Params); ScanForSuccess; if FoundTitle = 'abort' then ShowMessage('Authorize aborted'); Result := FoundTitle; end;
Okay... ik ben hier even aan het klooien geweest.
Het is werk in uitvoering maar ben al aardig op dreef
Ten eerste heb ik de google api dingen in een aparte unit ondergebracht. (Hier is die: google_api.pas)
De bedoeling is uiteindelijk om deze unit uit te breiden met allerlei functionaliteit zoals agenda bijwerken e.d. maar voorlopig is ie nu alleen nog om een Authenticatie te doen en een refresh en access token op te halen.
Ik maak, indien er nog geen authenticatie is, gebruik van Internet Explorer om de authenticatie in een apart venster weer te geven en uit te kunnen lezen voor de authenticatie-code. Daarna wordt een refresh-token en een access-token opgehaald. Het is ook mogelijk om deze in een token.dat bestand op te slaan en weer op te halen zodat er geen nieuwe authenticatie nodig is.
Het aanroepen gaat ongeveer zo:
Delphi Code:
uses google_api, httpsend; const client_id = '11111111111111111111111111111111111111111111111.apps.googleusercontent.com'; client_secret = '111111111111111111111111'; procedure TForm1.btnUittestenClick(Sender: TObject); var gApi: TGoogleApi; Access: String; URL: string; Response: TStringList; begin gApi := TGoogleApi.Create(client_id, client_secret); try gApi.Scopes.Add('profile'); gApi.Scopes.Add('email'); gApi.Scopes.Add('https://www.googleapis.com/auth/calendar'); gApi.LoadAccessRefreshTokens; // <-- indien er al een token.dat aanwezig is gApi.GetAccess_token; // <- deze roept ook e.v. authenticatie op indien nodig if gapi.access_token <> '' then gApi.SaveAccessRefreshTokens; // <- en opslaan Memo1.Lines.Add('auth = ' + gApi.Authorize_token); Memo1.Lines.Add('refr = ' + gApi.refresh_token); Memo1.Lines.Add('acc = ' + gapi.access_token); Access := gApi.access_token; finally gApi.Free; end; Url := 'https://www.googleapis.com/plus/v1/people/me?access_token=' + Access; Response := TStringList.Create; if HttpGetText(URL, Response) then begin // YAHOOOOOOO - we hebben toegang !! Memo1.Lines.Add(Response.Text); end; Response.Free; end;
Je had overigens zelf initieel alleen authenticatie voor de "email profile" gevraagd maar als je dus met de agenda wil pielen moet je natuurlijk ook "https://www.googleapis.com/auth/calendar" opnemen want anders krijgen je tokens geen toestemming voor de agenda. Eigenlijk heb je voor de agenda alleen maar de agenda nodig dus die andere zouden weg kunnen (tenzij je natuurlijk de agenda_id weer uit je profile gegevens wilt trekken).
Dit voorbeeld haalt alleen even je eigen gegevens op ("/plus/v1/people/me") maar het laat zien hoe het moet. De volgende stap is het uitvogelen van het toevoegen van een agenda-punt. Dat moet in een JSON-request gegoten worden.
Opmerkingen of vragen zijn welkom
Ps. Waarom moet ik in Lazarus ssl_openssl opnemen in mijn project als ik met synapse een https aan wil spreken? Waarom doet synapse dat in Lazarus niet onder het hoedje (zoals bij Delphi) ????
There are currently 1 users browsing this thread. (0 members and 1 guests)
Bookmarks