googleclient.pp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  1. {$IFNDEF FPC_DOTTEDUNITS}
  2. unit googleclient;
  3. {$ENDIF FPC_DOTTEDUNITS}
  4. {$mode objfpc}{$H+}
  5. interface
  6. {$IFDEF FPC_DOTTEDUNITS}
  7. uses
  8. System.Classes, System.SysUtils, System.IniFiles, FpJson.Data, FpWeb.Client, Jwt.Oauth2, Jwt.Types;
  9. {$ELSE FPC_DOTTEDUNITS}
  10. uses
  11. Classes, SysUtils, inifiles, fpjson, fpwebclient, fpoauth2, fpjwt;
  12. {$ENDIF FPC_DOTTEDUNITS}
  13. Type
  14. { TGoogleClaims }
  15. TGoogleClaims = Class(TClaims)
  16. private
  17. Fat_hash: string;
  18. Fazp: string;
  19. FEmail: String;
  20. Femail_verified: Boolean;
  21. Published
  22. Property azp : string read Fazp Write Fazp;
  23. Property email : String read FEmail Write FEmail;
  24. Property email_verified : Boolean read Femail_verified Write Femail_verified;
  25. Property at_hash : string Read Fat_hash Write Fat_hash;
  26. end;
  27. { TGoogleIDToken }
  28. TGoogleIDToken = Class(TJWTIDtoken)
  29. private
  30. function GetGoogleClaims: TGoogleClaims;
  31. Protected
  32. Function CreateClaims : TClaims; override;
  33. Public
  34. Constructor Create; override;
  35. Function GetUniqueUserName : String; override;
  36. Property GoogleClaims : TGoogleClaims Read GetGoogleClaims;
  37. end;
  38. { TGoogleOAuth2Handler }
  39. TGoogleOAuth2Handler = Class(TOAuth2Handler)
  40. Protected
  41. function CreateIDToken: TJWTIDToken;override;
  42. Public
  43. Constructor Create(AOwner : TComponent);override;
  44. end;
  45. TAuthMethod = (amOAuth2, amOpenID, amDeveloperKey, amServiceAccount);
  46. { TGoogleClientConfig }
  47. TGoogleClientConfig = Class(TPersistent)
  48. private
  49. FApplicationName: String;
  50. FAuthMethod: TAuthMethod;
  51. FEnableGZIP: Boolean;
  52. procedure SetAuthMethod(AValue: TAuthMethod);
  53. Public
  54. Procedure Assign(Source : TPersistent); override;
  55. procedure LoadFromIni(AIni : TCustomIniFile);
  56. procedure LoadFromJSON(AJSON : TJSONObject);
  57. Procedure LoadFromFile(Const AFileName : String);
  58. Published
  59. Property EnableGZIP : Boolean Read FEnableGZIP Write FEnableGZIP;
  60. Property ApplicationName : String Read FApplicationName Write FApplicationName;
  61. Property AuthMethod : TAuthMethod Read FAuthMethod Write SetAuthMethod;
  62. end;
  63. TGoogleClient = CLass(TComponent)
  64. Private
  65. FConfig: TGoogleClientConfig;
  66. FWebClient: TAbstractWebClient;
  67. FAuthHandler : TOAuth2Handler;
  68. function GetOnUserConsent: TUserConsentHandler;
  69. procedure SetAuthHandler(AValue: TOAuth2Handler);
  70. procedure SetClient(AValue: TAbstractWebClient);
  71. procedure SetConfig(AValue: TGoogleClientConfig);
  72. procedure SetOnUserConsent(AValue: TUserConsentHandler);
  73. Protected
  74. Procedure CheckDefaults; virtual;
  75. Public
  76. Constructor Create(AOwner : TComponent); override;
  77. Destructor Destroy; override;
  78. Function GetAuthHandler : TOAuth2Handler;
  79. Published
  80. Property AuthHandler : TOAuth2Handler Read GetAuthHandler Write SetAuthHandler;
  81. Property WebClient : TAbstractWebClient Read FWebClient Write SetClient;
  82. Property Config : TGoogleClientConfig Read FConfig Write SetConfig;
  83. Property OnUserConsent : TUserConsentHandler Read GetOnUserConsent Write SetOnUserConsent;
  84. end;
  85. EGoogleClient = Class(Exception);
  86. Const
  87. DefAUTHURL='https://accounts.google.com/o/oauth2/auth';
  88. DefTOKENURL='https://accounts.google.com/o/oauth2/token';
  89. implementation
  90. {$IFDEF FPC_DOTTEDUNITS}
  91. uses FpWeb.Http.Defs;
  92. {$ELSE FPC_DOTTEDUNITS}
  93. uses httpdefs;
  94. {$ENDIF FPC_DOTTEDUNITS}
  95. Const
  96. SClient = 'Client';
  97. SAuth = 'Authorization';
  98. KeyenableGZIP = 'EnableGZIP';
  99. KeyApplicationName = 'ApplicationName';
  100. KeyMethod = 'Method';
  101. {
  102. KeyDeveloperKey = 'developerkey';
  103. KeyOpenIDRealm = 'OpenIDRealm';
  104. KeyHostedDomain = 'HostedDomain';
  105. }
  106. { TGoogleOAuth2Handler }
  107. constructor TGoogleOAuth2Handler.Create(AOwner: TComponent);
  108. begin
  109. inherited Create(AOwner);
  110. Config.TokenURL:=DefTOKENURL;
  111. Config.AuthURL:=DefAuthURL;
  112. end;
  113. function TGoogleOAuth2Handler.CreateIDToken: TJWTIDToken;
  114. begin
  115. Result:=TGoogleIDToken.Create;
  116. end;
  117. { TGoogleIDToken }
  118. function TGoogleIDToken.GetGoogleClaims: TGoogleClaims;
  119. begin
  120. if Claims is TGoogleClaims then
  121. Result:=TGoogleClaims(Claims)
  122. else
  123. Result:=Nil;
  124. end;
  125. function TGoogleIDToken.CreateClaims: TClaims;
  126. begin
  127. If ClaimsClass=Nil then
  128. Result:=TGoogleClaims.Create
  129. else
  130. Result:=inherited CreateClaims;
  131. end;
  132. constructor TGoogleIDToken.Create;
  133. begin
  134. Inherited CreateWithClasses(TGoogleClaims,Nil)
  135. end;
  136. function TGoogleIDToken.GetUniqueUserName: String;
  137. begin
  138. if Assigned(GoogleClaims) then
  139. Result:=GoogleClaims.email
  140. else
  141. Result:=inherited GetUniqueUserName;
  142. end;
  143. {
  144. Function TOAuth2Handler.AuthenticateURL : String;
  145. begin
  146. Result:=Config.AuthURL
  147. + '?'+ 'scope='+HTTPEncode(Config.AuthScope)
  148. +'&redirect_uri='+HTTPEncode(Config.RedirectUri)
  149. +'&response_type=code'
  150. +'&client_id='+HTTPEncode(Config.ClientID);
  151. if (Config.AccessType=atOffline) then
  152. Result:=Result+'&access_type=offline'; // Request refresh token.
  153. if (Config.State<>'') then
  154. Result:=Result +'&state='+HTTPEncode(Config.State);
  155. end;
  156. try
  157. Req:=WebClient.CreateRequest;
  158. Req.Headers.Values['Content-Type']:='application/x-www-form-urlencoded';
  159. url:=Config.TOKENURL;
  160. Body:='client_id='+HTTPEncode(Config.ClientID)+
  161. '&client_secret='+ HTTPEncode(Config.ClientSecret);
  162. if (Config.RefreshToken<>'') then
  163. body:=Body+'&refresh_token='+HTTPEncode(Config.RefreshToken)+
  164. '&grant_type=refresh_token'
  165. else
  166. begin
  167. body:=Body+'&code='+HTTPEncode(Config.AuthCode)+
  168. '&redirect_uri='+HTTPEncode(Config.RedirectUri)+
  169. '&scope='+HTTPEncode(Config.AuthScope)+
  170. '&grant_type=authorization_code';
  171. if Config.AccessType=atOffline then
  172. Body:=Body;
  173. end;
  174. Req.SetContentFromString(Body);
  175. Resp:=WebClient.ExecuteRequest('POST',url,Req);
  176. if Not (Resp.StatusCode=200) then
  177. Raise Exception.CreateFmt(SErrFailedToRefreshToken,[Resp.StatusCode,Resp.StatusText]);
  178. I:=Resp.Content.Size;
  179. D:=GetJSON(Resp.Content);
  180. O:=D as TJSONObject;
  181. S:=O.Get('access_token',Config.AccessToken);
  182. Config.AccessToken:=S;
  183. S:=O.Get('refresh_token',Config.RefreshToken);
  184. Config.RefreshToken:=S;
  185. S:=O.Get('token_type',Config.AuthTokenType);
  186. Config.AuthTokenType:=S;
  187. I:=O.get('expires_in',0);
  188. if (I>0) then
  189. Config.AuthExpires:=Now+(I-10.0) / (3600*24); //skim off 10 secs to avoid race conditions
  190. Result:=True;
  191. finally
  192. D.Free;
  193. Resp.Free;
  194. Req.Free;
  195. end;
  196. end;
  197. }
  198. { TAuthHandler }
  199. { TGoogleClientConfig }
  200. procedure TGoogleClientConfig.SetAuthMethod(AValue: TAuthMethod);
  201. begin
  202. if FAuthMethod=AValue then Exit;
  203. FAuthMethod:=AValue;
  204. end;
  205. Procedure TGoogleClientConfig.Assign(Source: TPersistent);
  206. Var
  207. C : TGoogleClientConfig;
  208. begin
  209. if (Source is TGoogleClientConfig) then
  210. begin
  211. C:=Source as TGoogleClientConfig;
  212. EnableGZIP:=C.EnableGZIP;
  213. ApplicationName:=C.ApplicationName;
  214. AuthMethod:=C.AuthMethod;
  215. end;
  216. inherited Assign(Source);
  217. end;
  218. Function StringToAuthMethod (Const S : String) : TAuthMethod;
  219. begin
  220. Case Lowercase(S) of
  221. 'oauth2' : Result:=amOAuth2;
  222. 'openid' : Result:=amOpeniD;
  223. 'developerkey' : Result:=amDeveloperkey;
  224. end;
  225. end;
  226. procedure TGoogleClientConfig.LoadFromIni(AIni: TCustomIniFile);
  227. begin
  228. With AIni do
  229. begin
  230. EnableGZip:=AIni.ReadBool(SClient,KeyenableGZIP,EnableGZip);
  231. ApplicationName:=AIni.ReadString(SClient,KeyApplicationName,ApplicationName);
  232. AuthMethod:=StringToAuthMethod(AIni.ReadString(SAuth,KeyMethod,'oauth2'));
  233. end;
  234. end;
  235. procedure TGoogleClientConfig.LoadFromJSON(AJSON: TJSONObject);
  236. begin
  237. With AJSON do
  238. begin
  239. EnableGZip:=Get(KeyenableGZIP,EnableGZip);
  240. ApplicationName:=Get(KeyApplicationName,ApplicationName);
  241. AuthMethod:=StringToAuthMethod(Get(KeyMethod,'oauth2'));
  242. end;
  243. end;
  244. Procedure TGoogleClientConfig.LoadFromFile(Const AFileName: String);
  245. Var
  246. J : TJSONData;
  247. F : TFileStream;
  248. Ini : TMemIniFile;
  249. begin
  250. if (lowercase(ExtractFileExt(AFileName))='.json') then
  251. begin
  252. J:=Nil;
  253. F:=TFileStream.Create(AFileName,fmOpenRead or fmShareDenyWrite);
  254. try
  255. J:=GetJSON(F);
  256. finally
  257. F.Free
  258. end;
  259. try
  260. LoadFromJSON(J as TJSONObject);
  261. finally
  262. J.Free;
  263. end;
  264. end
  265. else
  266. begin
  267. Ini:=TMemIniFIle.Create(AFileName);
  268. try
  269. LoadFromIni(Ini);
  270. finally
  271. Ini.Free;
  272. end;
  273. end;
  274. end;
  275. { TGoogleClient }
  276. procedure TGoogleClient.SetClient(AValue: TAbstractWebClient);
  277. Var
  278. AH : TOAuth2Handler;
  279. begin
  280. if FWebClient=AValue then Exit;
  281. if Assigned(FWebClient) then
  282. FWebClient.RemoveFreeNotification(Self);
  283. FWebClient:=AValue;
  284. if Assigned(FWebClient) then
  285. begin
  286. FWebClient.FreeNotification(Self);
  287. AH:=GetAuthHandler;
  288. FWebClient.RequestSigner:=AH;
  289. AH.WebClient:=FWebClient;
  290. end;
  291. end;
  292. function TGoogleClient.GetOnUserConsent: TUserConsentHandler;
  293. begin
  294. Result:=GetAuthHandler.OnUserConsent;
  295. end;
  296. procedure TGoogleClient.SetAuthHandler(AValue: TOAuth2Handler);
  297. begin
  298. if FAuthHandler=AValue then Exit;
  299. if Assigned(FAuthHandler) then
  300. FAuthHandler.RemoveFreeNotification(Self);
  301. FAuthHandler:=AValue;
  302. if Assigned(FAuthHandler) then
  303. FAuthHandler.FreeNotification(Self);
  304. end;
  305. procedure TGoogleClient.SetConfig(AValue: TGoogleClientConfig);
  306. begin
  307. if FConfig=AValue then Exit;
  308. FConfig.Assign(AValue);
  309. CheckDefaults;
  310. end;
  311. procedure TGoogleClient.SetOnUserConsent(AValue: TUserConsentHandler);
  312. begin
  313. GetAuthHandler.OnUserConsent:=AValue;
  314. end;
  315. constructor TGoogleClient.Create(AOwner: TComponent);
  316. begin
  317. inherited Create(AOwner);
  318. FConfig:=TGoogleClientConfig.Create;
  319. CheckDefaults;
  320. end;
  321. destructor TGoogleClient.Destroy;
  322. begin
  323. FConfig.Free;
  324. inherited Destroy;
  325. end;
  326. procedure TGoogleClient.CheckDefaults;
  327. begin
  328. With AuthHandler.Config do
  329. begin
  330. If (AuthURL='') then
  331. AuthURL:=DefAuthURL;
  332. If (TokenURL='') then
  333. TokenURL:=DefTokenURL;
  334. end;
  335. end;
  336. function TGoogleClient.GetAuthHandler: TOAuth2Handler;
  337. begin
  338. if (FAuthHandler=Nil) then
  339. begin
  340. FAuthHandler:=TGoogleOAuth2Handler.Create(Self);
  341. FAuthHandler.SetSubComponent(True);
  342. if Assigned(FWebClient) then
  343. begin
  344. FWebClient.RequestSigner:=FAuthHandler;
  345. FAuthHandler.WebClient:=FWebClient;
  346. end;
  347. end;
  348. Result:=FAuthHandler;
  349. end;
  350. end.