googleclient.pp 9.8 KB

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