-
Notifications
You must be signed in to change notification settings - Fork 188
/
Quick.OAuth.pas
277 lines (246 loc) · 8.22 KB
/
Quick.OAuth.pas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
unit Quick.OAuth;
interface
uses
Quick.HttpClient,
Quick.HttpServer.Request,
Quick.HttpServer.Response,
Quick.HttpServer,
Quick.Threads,
Quick.OAuth.Utils,
SysUtils;
type
TOAuthToken = class
private
fAccessTokenExpiration: integer;
fAccessToken: string;
fRefreshToken: string;
fRetrieveDateTime: TDateTime;
public
property AccessToken: string read fAccessToken write fAccessToken;
property AccessTokenExpiration: integer read fAccessTokenExpiration write
fAccessTokenExpiration;
property RefreshToken: string read fRefreshToken write fRefreshToken;
property RetrieveDateTime: TDateTime read fRetrieveDateTime write
fRetrieveDateTime;
end;
TOnSaveToken = procedure (const aToken: TOAuthToken) of object;
TOnLoadToken = procedure (var aToken: TOAuthToken) of object;
TOnAuthorizationCompleted = reference to procedure (const aToken: TOAuthToken);
TOnRefreshCompleted = reference to procedure (const aToken: TOAuthToken);
{$M+}
TOAuthBase = class
private
fToken: TOAuthToken;
fOnAuthorizationCompleted: TOnAuthorizationCompleted;
fOnRefreshCompleted: TOnRefreshCompleted;
fAccessTokenParam: string;
fAuthCodeParam: string;
fAuthErrorParam: string;
fExpirationParam: string;
fRefreshTokenParam: string;
fCallbackURL: string;
fServer: THttpServer;
fClient: TJsonHttpClient;
fOnSaveToken: TOnSaveToken;
fOnLoadToken: TOnLoadToken;
procedure ExchangeAuthForAccessToken (const aAuthToken: string);
procedure RefreshAccessToken (const aRefreshToken: string);
function GetAccessToken: string;
function IsTokenValid: boolean;
protected
// Abstract
function CreateAuthorizationRequest: string; virtual; abstract;
function CreateAuthToAccessRequest (const aAuthToken: string): string; virtual; abstract;
function CreateRefreshRequest (const aRefreshToken: string): string; virtual; abstract;
// Available
function CreateAuthorizationHTMLPage (const aAuthorised: boolean): string; virtual;
procedure OnProcessRequest(aRequest: IHttpRequest; aResponse: IHttpResponse); virtual;
public
constructor Create;
destructor Destroy; override;
// Methods
procedure Authorize(const aOnAuthorizationCompleted: TOnAuthorizationCompleted = nil);
procedure RefreshToken (const aOnRefreshCompleted: TOnRefreshCompleted = nil);
// Properties
property AccessTokenParam: string read fAccessTokenParam write
fAccessTokenParam;
property AuthCodeParam: string read fAuthCodeParam write fAuthCodeParam;
property AuthErrorParam: string read fAuthErrorParam write fAuthErrorParam;
property ExpirationParam: string read fExpirationParam write fExpirationParam;
property RefreshTokenParam: string read fRefreshTokenParam write
fRefreshTokenParam;
property CallbackURL: string read fCallbackURL write fCallbackURL;
property AccessToken: string read GetAccessToken;
published
// Events
property OnSaveToken: TOnSaveToken read fOnSaveToken write fOnSaveToken;
property OnLoadToken: TOnLoadToken read fOnLoadToken write fOnLoadToken;
end;
{$M-}
EOAuthException = class (Exception);
implementation
uses
System.JSON, System.DateUtils, System.Types;
{$I QuickLib.INC}
constructor TOAuthBase.Create;
begin
inherited Create;
fClient:=TJsonHttpClient.Create;
{$IFNDEF DELPHIRX101_UP}
fServer:=nil;
{$ENDIF}
fToken:=TOAuthToken.Create;
fToken.AccessToken:='';
fToken.AccessTokenExpiration:=0;
fToken.RefreshToken:='';
fToken.RetrieveDateTime:=EncodeDateTime(1900, 1, 1, 23, 59, 00, 00);
end;
function TOAuthBase.CreateAuthorizationHTMLPage(
const aAuthorised: boolean): string;
begin
if aAuthorised then
result:='Access Authorised! You can now close this page and return to the application'
else
result:='Access Denied!';
end;
destructor TOAuthBase.Destroy;
begin
fClient.Free;
fServer.Free;
fToken.Free;
inherited;
end;
procedure TOAuthBase.ExchangeAuthForAccessToken(const aAuthToken: string);
var
resp: IHttpRequestResponse;
accToken: string;
refrToken: string;
expiry: integer;
begin
fToken.AccessToken:='';
fToken.AccessTokenExpiration:=0;
try
case GetMethodFromRequest(CreateAuthToAccessRequest(aAuthToken)) of
rmGET: resp:=fClient.Get(GetCleanRequest(CreateAuthToAccessRequest(aAuthToken)));
rmPOST: resp:=fClient.Post(GetCleanRequest(CreateAuthToAccessRequest(aAuthToken)), '');
end;
if (assigned(resp)) and (resp.StatusCode = 200) then
begin
if Assigned(resp.Response) then
begin
if resp.Response.TryGetValue(AccessTokenParam, accToken) then
fToken.AccessToken:=accToken;
if resp.Response.TryGetValue(ExpirationParam, expiry) then
fToken.AccessTokenExpiration:=expiry;
if resp.Response.TryGetValue(RefreshTokenParam, refrToken) then
fToken.RefreshToken:=refrToken;
fToken.RetrieveDateTime:=Now;
if Assigned(fOnSaveToken) then
fOnSaveToken(fToken);
if Assigned(fOnAuthorizationCompleted) then
fOnAuthorizationCompleted(fToken);
end;
end
else
raise EOAuthException.Create('Something went wrong. Please try again');
except
raise EOAuthException.Create('Something went wrong. Please try again');
end;
end;
procedure TOAuthBase.OnProcessRequest(aRequest: IHttpRequest;
aResponse: IHttpResponse);
begin
fToken.AccessToken:='';
fToken.AccessTokenExpiration:=0;
if aRequest.UnparsedParams.Contains(AuthErrorParam) then
aResponse.ContentText:= CreateAuthorizationHTMLPage(false)
else
if aRequest.UnparsedParams.Contains(AuthCodeParam) then
begin
ExchangeAuthForAccessToken(aRequest.Query[AuthCodeParam].AsString);
aResponse.ContentText:= CreateAuthorizationHTMLPage(true);
end;
end;
procedure TOAuthBase.RefreshAccessToken(const aRefreshToken: string);
var
resp: IHttpRequestResponse;
accToken: string;
expiry: integer;
begin
try
case GetMethodFromRequest(CreateRefreshRequest(aRefreshToken)) of
rmGET: resp:=fClient.Get(GetCleanRequest(CreateRefreshRequest(aRefreshToken)));
rmPOST: resp:=fClient.Post(GetCleanRequest(CreateRefreshRequest(aRefreshToken)), '');
end;
if (assigned(resp)) and (resp.StatusCode = 200) then
begin
if Assigned(resp.Response) then
begin
if resp.Response.TryGetValue(AccessTokenParam, accToken) then
fToken.AccessToken:=accToken;
if resp.Response.TryGetValue(ExpirationParam, expiry) then
fToken.AccessTokenExpiration:=expiry;
fToken.RetrieveDateTime:=Now;
if Assigned(fOnSaveToken) then
fOnSaveToken(fToken);
end;
end
else
raise EOAuthException.Create('Something went wrong. Please try again');
except
raise EOAuthException.Create('Something went wrong. Please try again');
end;
end;
procedure TOAuthBase.RefreshToken(const aOnRefreshCompleted:
TOnRefreshCompleted = nil);
begin
fOnRefreshCompleted:=aOnRefreshCompleted;
if Assigned(fOnLoadToken) then
fOnLoadToken(fToken);
if fToken.AccessToken = '' then
Authorize(TOnAuthorizationCompleted(fOnRefreshCompleted))
else
begin
GetAccessToken;
if Assigned(fOnRefreshCompleted) then
fOnRefreshCompleted(fToken);
end;
end;
procedure TOAuthBase.Authorize(const aOnAuthorizationCompleted:
TOnAuthorizationCompleted = nil);
begin
fOnAuthorizationCompleted:=aOnAuthorizationCompleted;
if Assigned(fOnLoadToken) then
fOnLoadToken(fToken);
if IsTokenValid then
Exit;
fClient:=TJsonHttpClient.Create;
if Assigned(fServer) then
fServer.Stop;
FreeAndNil(fServer);
fServer:=THttpServer.Create(GetDomain(fCallbackURL), GetPort(fCallbackURL), false, nil);
try
fServer.OnNewRequest:=OnProcessRequest;
fServer.Start;
OpenURL(GetCleanRequest(CreateAuthorizationRequest));
except
fServer.Stop;
FreeAndNil(fServer);
end;
end;
function TOAuthBase.GetAccessToken: string;
begin
result:='';
if IsTokenValid then
result:=fToken.AccessToken
else
RefreshAccessToken(fToken.RefreshToken);
end;
function TOAuthBase.IsTokenValid: boolean;
var
expDate: TDateTime;
begin
expDate:=IncSecond(fToken.RetrieveDateTime, fToken.AccessTokenExpiration);
result:= CompareDateTime(expDate, Now) = GreaterThanValue;
end;
end.