123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417 |
- {$IFNDEF FPC_DOTTEDUNITS}
- unit googleclient;
- {$ENDIF FPC_DOTTEDUNITS}
- {$mode objfpc}{$H+}
- interface
- {$IFDEF FPC_DOTTEDUNITS}
- uses
- System.Classes, System.SysUtils, System.IniFiles, FpJson.Data, FpWeb.Client, Jwt.Oauth2, Jwt.Types;
- {$ELSE FPC_DOTTEDUNITS}
- uses
- Classes, SysUtils, inifiles, fpjson, fpwebclient, fpoauth2, fpjwt;
- {$ENDIF FPC_DOTTEDUNITS}
- Type
- { TGoogleClaims }
- TGoogleClaims = Class(TClaims)
- private
- Fat_hash: string;
- Fazp: string;
- FEmail: String;
- Femail_verified: Boolean;
- Published
- Property azp : string read Fazp Write Fazp;
- Property email : String read FEmail Write FEmail;
- Property email_verified : Boolean read Femail_verified Write Femail_verified;
- Property at_hash : string Read Fat_hash Write Fat_hash;
- end;
- { TGoogleIDToken }
- TGoogleIDToken = Class(TJWTIDtoken)
- private
- function GetGoogleClaims: TGoogleClaims;
- Protected
- Function CreateClaims : TClaims; override;
- Public
- Constructor Create; override;
- Function GetUniqueUserName : String; override;
- Property GoogleClaims : TGoogleClaims Read GetGoogleClaims;
- end;
- { TGoogleOAuth2Handler }
- TGoogleOAuth2Handler = Class(TOAuth2Handler)
- Protected
- function CreateIDToken: TJWTIDToken;override;
- Public
- Constructor Create(AOwner : TComponent);override;
- end;
- TAuthMethod = (amOAuth2, amOpenID, amDeveloperKey, amServiceAccount);
- { TGoogleClientConfig }
- TGoogleClientConfig = Class(TPersistent)
- private
- FApplicationName: String;
- FAuthMethod: TAuthMethod;
- FEnableGZIP: Boolean;
- procedure SetAuthMethod(AValue: TAuthMethod);
- Public
- Procedure Assign(Source : TPersistent); override;
- procedure LoadFromIni(AIni : TCustomIniFile);
- procedure LoadFromJSON(AJSON : TJSONObject);
- Procedure LoadFromFile(Const AFileName : String);
- Published
- Property EnableGZIP : Boolean Read FEnableGZIP Write FEnableGZIP;
- Property ApplicationName : String Read FApplicationName Write FApplicationName;
- Property AuthMethod : TAuthMethod Read FAuthMethod Write SetAuthMethod;
- end;
- TGoogleClient = CLass(TComponent)
- Private
- FConfig: TGoogleClientConfig;
- FWebClient: TAbstractWebClient;
- FAuthHandler : TOAuth2Handler;
- function GetOnUserConsent: TUserConsentHandler;
- procedure SetAuthHandler(AValue: TOAuth2Handler);
- procedure SetClient(AValue: TAbstractWebClient);
- procedure SetConfig(AValue: TGoogleClientConfig);
- procedure SetOnUserConsent(AValue: TUserConsentHandler);
- Protected
- Procedure CheckDefaults; virtual;
- Public
- Constructor Create(AOwner : TComponent); override;
- Destructor Destroy; override;
- Function GetAuthHandler : TOAuth2Handler;
- Published
- Property AuthHandler : TOAuth2Handler Read GetAuthHandler Write SetAuthHandler;
- Property WebClient : TAbstractWebClient Read FWebClient Write SetClient;
- Property Config : TGoogleClientConfig Read FConfig Write SetConfig;
- Property OnUserConsent : TUserConsentHandler Read GetOnUserConsent Write SetOnUserConsent;
- end;
- EGoogleClient = Class(Exception);
- Const
- DefAUTHURL='https://accounts.google.com/o/oauth2/auth';
- DefTOKENURL='https://accounts.google.com/o/oauth2/token';
- implementation
- {$IFDEF FPC_DOTTEDUNITS}
- uses FpWeb.Http.Defs;
- {$ELSE FPC_DOTTEDUNITS}
- uses httpdefs;
- {$ENDIF FPC_DOTTEDUNITS}
- Const
- SClient = 'Client';
- SAuth = 'Authorization';
- KeyenableGZIP = 'EnableGZIP';
- KeyApplicationName = 'ApplicationName';
- KeyMethod = 'Method';
- {
- KeyDeveloperKey = 'developerkey';
- KeyOpenIDRealm = 'OpenIDRealm';
- KeyHostedDomain = 'HostedDomain';
- }
- { TGoogleOAuth2Handler }
- constructor TGoogleOAuth2Handler.Create(AOwner: TComponent);
- begin
- inherited Create(AOwner);
- Config.TokenURL:=DefTOKENURL;
- Config.AuthURL:=DefAuthURL;
- end;
- function TGoogleOAuth2Handler.CreateIDToken: TJWTIDToken;
- begin
- Result:=TGoogleIDToken.Create;
- end;
- { TGoogleIDToken }
- function TGoogleIDToken.GetGoogleClaims: TGoogleClaims;
- begin
- if Claims is TGoogleClaims then
- Result:=TGoogleClaims(Claims)
- else
- Result:=Nil;
- end;
- function TGoogleIDToken.CreateClaims: TClaims;
- begin
- If ClaimsClass=Nil then
- Result:=TGoogleClaims.Create
- else
- Result:=inherited CreateClaims;
- end;
- constructor TGoogleIDToken.Create;
- begin
- Inherited CreateWithClasses(TGoogleClaims,Nil)
- end;
- function TGoogleIDToken.GetUniqueUserName: String;
- begin
- if Assigned(GoogleClaims) then
- Result:=GoogleClaims.email
- else
- Result:=inherited GetUniqueUserName;
- end;
- {
- Function TOAuth2Handler.AuthenticateURL : String;
- begin
- Result:=Config.AuthURL
- + '?'+ 'scope='+HTTPEncode(Config.AuthScope)
- +'&redirect_uri='+HTTPEncode(Config.RedirectUri)
- +'&response_type=code'
- +'&client_id='+HTTPEncode(Config.ClientID);
- if (Config.AccessType=atOffline) then
- Result:=Result+'&access_type=offline'; // Request refresh token.
- if (Config.State<>'') then
- Result:=Result +'&state='+HTTPEncode(Config.State);
- end;
- try
- Req:=WebClient.CreateRequest;
- Req.Headers.Values['Content-Type']:='application/x-www-form-urlencoded';
- url:=Config.TOKENURL;
- Body:='client_id='+HTTPEncode(Config.ClientID)+
- '&client_secret='+ HTTPEncode(Config.ClientSecret);
- if (Config.RefreshToken<>'') then
- body:=Body+'&refresh_token='+HTTPEncode(Config.RefreshToken)+
- '&grant_type=refresh_token'
- else
- begin
- body:=Body+'&code='+HTTPEncode(Config.AuthCode)+
- '&redirect_uri='+HTTPEncode(Config.RedirectUri)+
- '&scope='+HTTPEncode(Config.AuthScope)+
- '&grant_type=authorization_code';
- if Config.AccessType=atOffline then
- Body:=Body;
- end;
- Req.SetContentFromString(Body);
- Resp:=WebClient.ExecuteRequest('POST',url,Req);
- if Not (Resp.StatusCode=200) then
- Raise Exception.CreateFmt(SErrFailedToRefreshToken,[Resp.StatusCode,Resp.StatusText]);
- I:=Resp.Content.Size;
- D:=GetJSON(Resp.Content);
- O:=D as TJSONObject;
- S:=O.Get('access_token',Config.AccessToken);
- Config.AccessToken:=S;
- S:=O.Get('refresh_token',Config.RefreshToken);
- Config.RefreshToken:=S;
- S:=O.Get('token_type',Config.AuthTokenType);
- Config.AuthTokenType:=S;
- I:=O.get('expires_in',0);
- if (I>0) then
- Config.AuthExpires:=Now+(I-10.0) / (3600*24); //skim off 10 secs to avoid race conditions
- Result:=True;
- finally
- D.Free;
- Resp.Free;
- Req.Free;
- end;
- end;
- }
- { TAuthHandler }
- { TGoogleClientConfig }
- procedure TGoogleClientConfig.SetAuthMethod(AValue: TAuthMethod);
- begin
- if FAuthMethod=AValue then Exit;
- FAuthMethod:=AValue;
- end;
- Procedure TGoogleClientConfig.Assign(Source: TPersistent);
- Var
- C : TGoogleClientConfig;
- begin
- if (Source is TGoogleClientConfig) then
- begin
- C:=Source as TGoogleClientConfig;
- EnableGZIP:=C.EnableGZIP;
- ApplicationName:=C.ApplicationName;
- AuthMethod:=C.AuthMethod;
- end;
- inherited Assign(Source);
- end;
- Function StringToAuthMethod (Const S : String) : TAuthMethod;
- begin
- Case Lowercase(S) of
- 'oauth2' : Result:=amOAuth2;
- 'openid' : Result:=amOpeniD;
- 'developerkey' : Result:=amDeveloperkey;
- end;
- end;
- procedure TGoogleClientConfig.LoadFromIni(AIni: TCustomIniFile);
- begin
- With AIni do
- begin
- EnableGZip:=AIni.ReadBool(SClient,KeyenableGZIP,EnableGZip);
- ApplicationName:=AIni.ReadString(SClient,KeyApplicationName,ApplicationName);
- AuthMethod:=StringToAuthMethod(AIni.ReadString(SAuth,KeyMethod,'oauth2'));
- end;
- end;
- procedure TGoogleClientConfig.LoadFromJSON(AJSON: TJSONObject);
- begin
- With AJSON do
- begin
- EnableGZip:=Get(KeyenableGZIP,EnableGZip);
- ApplicationName:=Get(KeyApplicationName,ApplicationName);
- AuthMethod:=StringToAuthMethod(Get(KeyMethod,'oauth2'));
- end;
- end;
- Procedure TGoogleClientConfig.LoadFromFile(Const AFileName: String);
- Var
- J : TJSONData;
- F : TFileStream;
- Ini : TMemIniFile;
- begin
- if (lowercase(ExtractFileExt(AFileName))='.json') then
- begin
- J:=Nil;
- F:=TFileStream.Create(AFileName,fmOpenRead or fmShareDenyWrite);
- try
- J:=GetJSON(F);
- finally
- F.Free
- end;
- try
- LoadFromJSON(J as TJSONObject);
- finally
- J.Free;
- end;
- end
- else
- begin
- Ini:=TMemIniFIle.Create(AFileName);
- try
- LoadFromIni(Ini);
- finally
- Ini.Free;
- end;
- end;
- end;
- { TGoogleClient }
- procedure TGoogleClient.SetClient(AValue: TAbstractWebClient);
- Var
- AH : TOAuth2Handler;
- begin
- if FWebClient=AValue then Exit;
- if Assigned(FWebClient) then
- FWebClient.RemoveFreeNotification(Self);
- FWebClient:=AValue;
- if Assigned(FWebClient) then
- begin
- FWebClient.FreeNotification(Self);
- AH:=GetAuthHandler;
- FWebClient.RequestSigner:=AH;
- AH.WebClient:=FWebClient;
- end;
- end;
- function TGoogleClient.GetOnUserConsent: TUserConsentHandler;
- begin
- Result:=GetAuthHandler.OnUserConsent;
- end;
- procedure TGoogleClient.SetAuthHandler(AValue: TOAuth2Handler);
- begin
- if FAuthHandler=AValue then Exit;
- if Assigned(FAuthHandler) then
- FAuthHandler.RemoveFreeNotification(Self);
- FAuthHandler:=AValue;
- if Assigned(FAuthHandler) then
- FAuthHandler.FreeNotification(Self);
- end;
- procedure TGoogleClient.SetConfig(AValue: TGoogleClientConfig);
- begin
- if FConfig=AValue then Exit;
- FConfig.Assign(AValue);
- CheckDefaults;
- end;
- procedure TGoogleClient.SetOnUserConsent(AValue: TUserConsentHandler);
- begin
- GetAuthHandler.OnUserConsent:=AValue;
- end;
- constructor TGoogleClient.Create(AOwner: TComponent);
- begin
- inherited Create(AOwner);
- FConfig:=TGoogleClientConfig.Create;
- CheckDefaults;
- end;
- destructor TGoogleClient.Destroy;
- begin
- FConfig.Free;
- inherited Destroy;
- end;
- procedure TGoogleClient.CheckDefaults;
- begin
- With AuthHandler.Config do
- begin
- If (AuthURL='') then
- AuthURL:=DefAuthURL;
- If (TokenURL='') then
- TokenURL:=DefTokenURL;
- end;
- end;
- function TGoogleClient.GetAuthHandler: TOAuth2Handler;
- begin
- if (FAuthHandler=Nil) then
- begin
- FAuthHandler:=TGoogleOAuth2Handler.Create(Self);
- FAuthHandler.SetSubComponent(True);
- if Assigned(FWebClient) then
- begin
- FWebClient.RequestSigner:=FAuthHandler;
- FAuthHandler.WebClient:=FWebClient;
- end;
- end;
- Result:=FAuthHandler;
- end;
- end.
|