Release 4.0.0-preview1-00051
[platform/core/csapi/tizenfx.git] / src / Tizen.Account.OAuth2 / Tizen.Account.OAuth2 / Authorizer.cs
1 /*
2  * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  * Licensed under the Apache License, Version 2.0 (the License);
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an AS IS BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 using System;
18 using System.Collections.Generic;
19 using System.Runtime.InteropServices;
20 using System.Threading.Tasks;
21
22 namespace Tizen.Account.OAuth2
23 {
24     /// <summary>
25     /// An abstract class to represent various OAuth 2.0 authorization code flows.
26     /// Refer to http://tools.ietf.org/html/rfc6749 about OAuth 2.0 protocols.
27     /// Also service provider document needs to be referred for using end points and additional parameters.
28     /// </summary>
29     /// <since_tizen> 3 </since_tizen>
30     public abstract class Authorizer : IDisposable
31     {
32
33         internal IntPtr _managerHandle;
34         private bool _disposed = false;
35
36         /// <summary>
37         /// Constructor for Authoirzer instances
38         /// </summary>
39         /// <since_tizen> 3 </since_tizen>
40         public Authorizer()
41         {
42             int ret = Interop.Manager.Create(out _managerHandle);
43             if (ret != (int)OAuth2Error.None)
44             {
45                 Log.Error(ErrorFactory.LogTag, "Interop failed");
46                 throw ErrorFactory.GetException(ret);
47             }
48         }
49
50         /// <summary>
51         /// Destructor of the Authorizer class.
52         /// </summary>
53         /// <since_tizen> 3 </since_tizen>
54         ~Authorizer()
55         {
56             Dispose(false);
57         }
58
59         /// <summary>
60         /// Indicates if the current instance is already handling an authorization request
61         /// </summary>
62         /// <since_tizen> 3 </since_tizen>
63         public bool IsRequestInProgress
64         {
65             get
66             {
67                 return Interop.Manager.IsRequestInProgress(_managerHandle);
68             }
69         }
70
71         /// <summary>
72         /// Authorizes the client with access toekn / authorizaion code in Implicit and Authorization Code grant flows respectively.
73         /// </summary>
74         /// <since_tizen> 3 </since_tizen>
75         /// <see cref="CodeGrantAuthorizer.AuthorizeAsync(AuthorizationRequest)"/>
76         /// <see cref="ImplicitGrantAuthorizer.AuthorizeAsync(AuthorizationRequest)"/>
77         /// <param name="request">An authorization request</param>
78         /// <returns> The authorization response from server</returns>
79         public virtual Task<AuthorizationResponse> AuthorizeAsync(AuthorizationRequest request)
80         {
81             throw new NotImplementedException();
82         }
83
84         /// <summary>
85         /// Gets the access token in OAuth2 supported grant flows except Implicit Grant flow.
86         /// </summary>
87         /// <since_tizen> 3 </since_tizen>
88         /// <see cref="CodeGrantAuthorizer.AuthorizeAsync(AuthorizationRequest)"/>
89         /// <see cref="ImplicitGrantAuthorizer.AuthorizeAsync(AuthorizationRequest)"/>
90         /// <param name="request">A token request</param>
91         /// <returns>The response from server</returns>
92         public virtual Task<TokenResponse> GetAccessTokenAsync(TokenRequest request)
93         {
94             throw new NotImplementedException();
95         }
96
97         /// <summary>
98         /// Releases any unmanaged resources used by this object.
99         /// </summary>
100         /// <since_tizen> 3 </since_tizen>
101         public void Dispose()
102         {
103             Dispose(true);
104             GC.SuppressFinalize(this);
105         }
106
107         /// <summary>
108         /// Retrieves access token using a refresh token.
109         /// </summary>
110         /// <since_tizen> 3 </since_tizen>
111         /// <param name="request">Request containing refresh token</param>
112         /// <returns>The response containing access token.</returns>
113         /// <privilege>http://tizen.org/privilege/internet</privilege>
114         /// <exception cref="ArgumentException">Thrown when method failed due to invalid argumets</exception>
115         /// <exception cref="OAuth2Exception">Thrown when method fails due to server error</exception>
116         public virtual async Task<TokenResponse> RefreshAccessTokenAsync(RefreshTokenRequest request)
117         {
118             IntPtr requestHandle = GetRequestHandle(request);
119             return await Task.Run(() => GetRefreshToken(requestHandle));
120         }
121
122         private TokenResponse GetRefreshToken(IntPtr requestHandle)
123         {
124             int ret = (int)OAuth2Error.None;
125             IntPtr error = IntPtr.Zero;
126             TokenResponse response = null;
127             Interop.Manager.Oauth2RefreshTokenCallback accessTokenCb = (IntPtr responseHandle, IntPtr usrData) =>
128             {
129                 Interop.Response.GetError(responseHandle, out error);
130                 if (error != IntPtr.Zero)
131                 {
132                     Log.Error(ErrorFactory.LogTag, "Error occured");
133                 }
134                 else
135                 {
136                     IntPtr accessToken;
137                     ret = Interop.Response.GetAccessToken(responseHandle, out accessToken);
138                     if (ret != (int)OAuth2Error.None)
139                     {
140                         Log.Error(ErrorFactory.LogTag, "Interop failed");
141                         throw ErrorFactory.GetException(ret);
142                     }
143
144                     IntPtr tokenType;
145                     ret = Interop.Response.GetTokenType(responseHandle, out tokenType);
146                     if (ret != (int)OAuth2Error.None)
147                     {
148                         Log.Error(ErrorFactory.LogTag, "Failed to get token type");
149                     }
150
151                     long expiresIn;
152                     ret = Interop.Response.GetExpiresIn(responseHandle, out expiresIn);
153                     if (ret != (int)OAuth2Error.None)
154                     {
155                         Log.Error(ErrorFactory.LogTag, "Failed to get expires in");
156                     }
157
158                     IntPtr refreshToken;
159                     ret = Interop.Response.GetRefreshToken(responseHandle, out refreshToken);
160                     if (ret != (int)OAuth2Error.None)
161                     {
162                         Log.Error(ErrorFactory.LogTag, "Interop failed");
163                         throw ErrorFactory.GetException(ret);
164                     }
165
166                     IntPtr scope;
167                     ret = Interop.Response.GetScope(responseHandle, out scope);
168                     if (ret != (int)OAuth2Error.None)
169                     {
170                         Log.Error(ErrorFactory.LogTag, "Failed to get scope");
171                     }
172
173                     IEnumerable<string> scopes = (scope == IntPtr.Zero) ? null : Marshal.PtrToStringAnsi(scope)?.Split(' ');
174
175                     var token = new AccessToken();
176                     token.Token = (accessToken == IntPtr.Zero) ? null : Marshal.PtrToStringAnsi(accessToken);
177                     token.TokenType = (tokenType == IntPtr.Zero) ? null : Marshal.PtrToStringAnsi(tokenType);
178                     token.Scope = scopes;
179                     token.ExpiresIn = expiresIn;
180
181                     response = new TokenResponse(responseHandle);
182                     response.AccessToken = token;
183                     response.RefreshToken = (refreshToken == IntPtr.Zero) ? null : new RefreshToken() { Token = Marshal.PtrToStringAnsi(refreshToken) };
184                 }
185             };
186
187             ret = Interop.Manager.RefreshAccessToken(_managerHandle, requestHandle, accessTokenCb, IntPtr.Zero);
188             Interop.Request.Destroy(requestHandle);
189             if (ret != (int)OAuth2Error.None || error != IntPtr.Zero)
190             {
191                 if (error != IntPtr.Zero)
192                 {
193                     throw ErrorFactory.GetException(error);
194                 }
195                 else
196                 {
197                     Log.Error(ErrorFactory.LogTag, "Interop failed");
198                     throw ErrorFactory.GetException(ret);
199                 }
200             }
201
202             return response;
203         }
204
205         /// <summary>
206         /// Releases any unmanaged resources used by this object. Can also dispose any other disposable objects.
207         /// </summary>
208         /// <since_tizen> 3 </since_tizen>
209         /// <param name="disposing">If true, disposes any disposable objects. If false, does not dispose disposable objects.</param>
210         protected virtual void Dispose(bool disposing)
211         {
212             if (_disposed)
213                 return;
214
215             if (disposing)
216             {
217                 // Free managed objects
218             }
219
220             Interop.Manager.Destroy(_managerHandle);
221             _disposed = true;
222         }
223
224         // Fill device request handle for refreshing access token
225         internal IntPtr GetRequestHandle(RefreshTokenRequest request)
226         {
227             IntPtr requestHandle;
228             int ret = Interop.Request.Create(out requestHandle);
229             if (ret != (int)OAuth2Error.None)
230             {
231                 Log.Error(ErrorFactory.LogTag, "Interop failed");
232                 throw ErrorFactory.GetException(ret);
233             }
234
235             ret = Interop.Request.SetRefreshTokenUrl(requestHandle, request.TokenEndpoint.ToString());
236             if (ret != (int)OAuth2Error.None)
237             {
238                 Log.Error(ErrorFactory.LogTag, "Interop failed");
239                 throw ErrorFactory.GetException(ret);
240             }
241
242             ret = Interop.Request.SetGrantType(requestHandle, Interop.GrantType.Refresh);
243             if (ret != (int)OAuth2Error.None)
244             {
245                 Log.Error(ErrorFactory.LogTag, "Interop failed");
246                 throw ErrorFactory.GetException(ret);
247             }
248
249             ret = Interop.Request.SetRefreshToken(requestHandle, request.RefreshToken);
250             if (ret != (int)OAuth2Error.None)
251             {
252                 Log.Error(ErrorFactory.LogTag, "Interop failed");
253                 throw ErrorFactory.GetException(ret);
254             }
255
256             if (request.ClientSecrets.Id != null)
257             {
258                 ret = Interop.Request.SetClientId(requestHandle, request.ClientSecrets.Id);
259                 if (ret != (int)OAuth2Error.None)
260                 {
261                     Log.Error(ErrorFactory.LogTag, "Interop failed");
262                     throw ErrorFactory.GetException(ret);
263                 }
264             }
265
266             if (request.ClientSecrets.Secret != null)
267             {
268                 ret = Interop.Request.SetClientSecret(requestHandle, request.ClientSecrets.Secret);
269                 if (ret != (int)OAuth2Error.None)
270                 {
271                     Log.Error(ErrorFactory.LogTag, "Interop failed");
272                     throw ErrorFactory.GetException(ret);
273                 }
274             }
275
276             if (request.Scopes != null)
277             {
278                 string scope = string.Join(" ", request.Scopes);
279                 ret = Interop.Request.SetScope(requestHandle, scope);
280                 if (ret != (int)OAuth2Error.None)
281                 {
282                     Log.Error(ErrorFactory.LogTag, "Interop failed");
283                     throw ErrorFactory.GetException(ret);
284                 }
285             }
286
287             ret = Interop.Request.SetClientAuthenticationType(requestHandle, (int)request.AuthenticationScheme);
288             if (ret != (int)OAuth2Error.None)
289             {
290                 Log.Error(ErrorFactory.LogTag, "Interop failed");
291                 throw ErrorFactory.GetException(ret);
292             }
293
294             return requestHandle;
295         }
296
297         internal TokenResponse GetAccessToken(IntPtr requestHandle)
298         {
299             int ret = (int)OAuth2Error.None;
300             IntPtr error = IntPtr.Zero;
301             TokenResponse response = null;
302             Interop.Manager.Oauth2TokenCallback accessTokenCb = (IntPtr responseHandle, IntPtr usrData) =>
303             {
304                 if (responseHandle == IntPtr.Zero)
305                 {
306                     Log.Error(ErrorFactory.LogTag, "Error occured");
307                     throw (new ArgumentNullException());
308                 }
309
310                 Interop.Response.GetError(responseHandle, out error);
311                 if (error != IntPtr.Zero)
312                 {
313                     Log.Error(ErrorFactory.LogTag, "Server Error occured");
314                 }
315                 else
316                 {
317                     IntPtr accessToken = IntPtr.Zero;
318                     ret = Interop.Response.GetAccessToken(responseHandle, out accessToken);
319                     if (ret != (int)OAuth2Error.None)
320                     {
321                         Log.Error(ErrorFactory.LogTag, "Failed to get access token");
322                         throw ErrorFactory.GetException(ret);
323                     }
324
325                     IntPtr tokenType;
326                     ret = Interop.Response.GetTokenType(responseHandle, out tokenType);
327                     if (ret != (int)OAuth2Error.None)
328                     {
329                         Log.Debug(ErrorFactory.LogTag, "TokenType can't be found");
330                     }
331
332                     long expiresIn = -1;
333                     ret = Interop.Response.GetExpiresIn(responseHandle, out expiresIn);
334                     if (ret != (int)OAuth2Error.None)
335                     {
336                         Log.Debug(ErrorFactory.LogTag, "ExpiresIn can't be found");
337                     }
338
339                     IntPtr refreshToken;
340                     ret = Interop.Response.GetRefreshToken(responseHandle, out refreshToken);
341                     if (ret != (int)OAuth2Error.None)
342                     {
343                         Log.Debug(ErrorFactory.LogTag, "Refresh Token can't be found");
344                     }
345
346                     IntPtr scope;
347                     ret = Interop.Response.GetScope(responseHandle, out scope);
348                     if (ret != (int)OAuth2Error.None)
349                     {
350                         Log.Debug(ErrorFactory.LogTag, "Scope can't be found");
351                     }
352
353                     IntPtr state;
354                     ret = Interop.Response.GetState(responseHandle, out state);
355                     if (ret != (int)OAuth2Error.None)
356                     {
357                         Log.Debug(ErrorFactory.LogTag, "State can't be found");
358                     }
359
360                     IEnumerable<string> scopes = (scope == IntPtr.Zero) ? null : Marshal.PtrToStringAnsi(scope)?.Split(' ');
361
362                     var token = new AccessToken();
363                     token.Token = (accessToken == IntPtr.Zero)? null : Marshal.PtrToStringAnsi(accessToken);
364                     token.TokenType = (tokenType == IntPtr.Zero) ? null : Marshal.PtrToStringAnsi(tokenType);
365                     token.Scope = scopes;
366                     token.ExpiresIn = expiresIn;
367
368                     response = new TokenResponse(responseHandle);
369                     response.AccessToken = token;
370                     response.State = (state == IntPtr.Zero) ? null : Marshal.PtrToStringAnsi(state);
371                     response.RefreshToken = (refreshToken == IntPtr.Zero) ? null : new RefreshToken() { Token = Marshal.PtrToStringAnsi(refreshToken) };
372                 }
373             };
374
375             ret = Interop.Manager.RequestToken(_managerHandle, requestHandle, accessTokenCb, IntPtr.Zero);
376             Interop.Request.Destroy(requestHandle);
377             if (ret != (int)OAuth2Error.None || error != IntPtr.Zero)
378             {
379                 if (error != IntPtr.Zero)
380                 {
381                     throw ErrorFactory.GetException(error);
382                 }
383                 else
384                 {
385                     Log.Error(ErrorFactory.LogTag, "Interop failed");
386                     throw ErrorFactory.GetException(ret);
387                 }
388             }
389
390             return response;
391         }
392
393         internal TokenResponse GetAccessTokenByCode(IntPtr requestHandle)
394         {
395             int ret = (int)OAuth2Error.None;
396             IntPtr error = IntPtr.Zero;
397             TokenResponse response = null;
398             Interop.Manager.Oauth2AccessTokenCallback accessTokenCb = (IntPtr responseHandle, IntPtr usrData) =>
399             {
400                 if (responseHandle == IntPtr.Zero)
401                 {
402                     Log.Error(ErrorFactory.LogTag, "Error occured");
403                     throw (new ArgumentNullException());
404                 }
405
406                 Interop.Response.GetError(responseHandle, out error);
407                 if (error != IntPtr.Zero)
408                 {
409                     Log.Error(ErrorFactory.LogTag, "Server Error occured");
410                 }
411                 else
412                 {
413                     IntPtr accessToken = IntPtr.Zero;
414                     ret = Interop.Response.GetAccessToken(responseHandle, out accessToken);
415                     if (ret != (int)OAuth2Error.None)
416                     {
417                         Log.Error(ErrorFactory.LogTag, "Failed to get access token");
418                         throw ErrorFactory.GetException(ret);
419                     }
420
421                     IntPtr tokenType;
422                     ret = Interop.Response.GetTokenType(responseHandle, out tokenType);
423                     if (ret != (int)OAuth2Error.None)
424                     {
425                         Log.Debug(ErrorFactory.LogTag, "TokenType can't be found");
426                     }
427
428                     long expiresIn = -1;
429                     ret = Interop.Response.GetExpiresIn(responseHandle, out expiresIn);
430                     if (ret != (int)OAuth2Error.None)
431                     {
432                         Log.Debug(ErrorFactory.LogTag, "ExpiresIn can't be found");
433                     }
434
435                     IntPtr refreshToken;
436                     ret = Interop.Response.GetRefreshToken(responseHandle, out refreshToken);
437                     if (ret != (int)OAuth2Error.None)
438                     {
439                         Log.Debug(ErrorFactory.LogTag, "Refresh Token can't be found");
440                     }
441
442                     IntPtr scope;
443                     ret = Interop.Response.GetScope(responseHandle, out scope);
444                     if (ret != (int)OAuth2Error.None)
445                     {
446                         Log.Debug(ErrorFactory.LogTag, "Scope can't be found");
447                     }
448
449                     IntPtr state;
450                     ret = Interop.Response.GetState(responseHandle, out state);
451                     if (ret != (int)OAuth2Error.None)
452                     {
453                         Log.Debug(ErrorFactory.LogTag, "State can't be found");
454                     }
455
456                     IEnumerable<string> scopes = (scope == IntPtr.Zero) ? null : Marshal.PtrToStringAnsi(scope)?.Split(' ');
457
458                     var token = new AccessToken();
459                     token.Token = (accessToken == IntPtr.Zero) ? null : Marshal.PtrToStringAnsi(accessToken);
460                     token.TokenType = (tokenType == IntPtr.Zero) ? null : Marshal.PtrToStringAnsi(tokenType);
461                     token.Scope = scopes;
462                     token.ExpiresIn = expiresIn;
463
464                     response = new TokenResponse(responseHandle);
465                     response.AccessToken = token;
466                     response.State = (state == IntPtr.Zero) ? null : Marshal.PtrToStringAnsi(state);
467                     response.RefreshToken = (refreshToken == IntPtr.Zero) ? null : new RefreshToken() { Token = Marshal.PtrToStringAnsi(refreshToken) };
468                 }
469             };
470
471             ret = Interop.Manager.RequestAccessToken(_managerHandle, requestHandle, accessTokenCb, IntPtr.Zero);
472             Interop.Request.Destroy(requestHandle);
473             if (ret != (int)OAuth2Error.None || error != IntPtr.Zero)
474             {
475                 if (error != IntPtr.Zero)
476                 {
477                     throw ErrorFactory.GetException(error);
478                 }
479                 else
480                 {
481                     Log.Error(ErrorFactory.LogTag, "Interop failed : " + ret);
482                     throw ErrorFactory.GetException(ret);
483                 }
484             }
485
486             return response;
487         }
488     }
489 }
490