Don't override system proxy with empty non-set proxy
[profile/ivi/gsignond-plugin-oauth.git] / src / gsignond-oauth-plugin.c
1 /* vi: set et sw=4 ts=4 cino=t0,(0: */
2 /* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 /*
4  * This file is part of gsignond
5  *
6  * Copyright (C) 2012 Intel Corporation.
7  *
8  * Contact: Alexander Kanavin <alex.kanavin@gmail.com>
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2.1 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
23  * 02110-1301 USA
24  */
25
26 /**
27  * SECTION:gsignond-oauth-plugin
28  * @short_description: OAuth1/OAuth2 authentication plugin for gSSO single sign-on service
29  * @see_also: #GSignondPlugin
30  *
31  * The OAuth plugin provides a client-side implementation of OAuth 1 and OAuth 2
32  * authorization protocols. The overall flow is that the plugin is requested to
33  * perform authorization using supplied authorization parameters, and if it has
34  * succeeded in doing so, it returns a token string to the application that can
35  * be used to access protected resources over https. The plugin is not involved
36  * in accessing protected resources, only in initial authorization.
37  * 
38  * OAuth1 is specified in <ulink url="http://tools.ietf.org/html/rfc5849">RFC 5849</ulink>,
39  * OAuth2 is specified in <ulink url="http://tools.ietf.org/html/rfc6749">RFC 6749</ulink>
40  * (with additional info regarding the basic bearer token type in <ulink 
41  * url="http://tools.ietf.org/html/rfc6750">RFC6750</ulink>). The two versions are
42  * not compatible and specify significantly different authorization sequences, for
43  * that reason they are implemented as separate mechanisms in the plugin.
44  * 
45  * The plugin implements the standard #GSignondPlugin interface, and after instantiating
46  * a plugin object all interactions happen through that interface.
47  * 
48  * #GSignondPlugin:type property of the plugin object is set to "oauth".
49  * 
50  * #GSignondPlugin:mechanisms property of the plugin object is a list of "oauth1" and "oauth2".
51  *
52  * <refsect1><title>Authorization sequence</title></refsect1>
53  * 
54  * The authorization sequence begins with issuing gsignond_plugin_request_initial().
55  * The @mechanism parameter should be set to "oauth1" or "oauth2", and
56  * the contents of @session_data and @identity_method_cache parameters depend 
57  * on the mechanism and are described in detail below.
58  * 
59  * The plugin responds to the request with one of the following signals:
60  * - #GSignondPlugin::response-final This means the authorization sequence ended
61  * successfully, and the authorization token is delivered in @session_data parameter
62  * of the signal. This signal concludes the sequence.
63  * - #GSignondPlugin::user-action-required The plugin is requesting to perform a 
64  * user authorization procedure by opening a webpage in a user-agent (browser) where the user is expected
65  * to enter her credentials. Parameters for this step are specified in @ui_data.
66  * After the user interaction has completed, the results are returned to the 
67  * plugin with gsignond_plugin_user_action_finished() method, and the authorization
68  * process continues.
69  * - #GSignondPlugin::store The plugin is requesting to replace the token cache
70  * with the contents of @identity_method_cache parameter. There is no need to respond to this
71  * signal; the authorization process continues immediately.
72  * - #GSignondPlugin::error An error has happened in the authorization sequence 
73  * and it stops.
74  * Typical errors are %GSIGNOND_ERROR_MISSING_DATA which means there wasn't enough
75  * data provided in gsignond_plugin_request_initial() to perform the authorization,
76  * %GSIGNOND_ERROR_NOT_AUTHORIZED which means the server rejected the 
77  * authorization attempt, %GSIGNOND_ERROR_USER_INTERACTION which means there
78  * was an error during interaction with the user.
79  *
80  * At any point the application can request to stop the authorization by calling
81  * gsignond_plugin_cancel(). The plugin responds with an #GSignondPlugin::error signal
82  * containing a %GSIGNOND_ERROR_SESSION_CANCELED error.
83  * 
84  * <refsect1><title>Code examples</title></refsect1>
85  * 
86  * <example>
87  * <title>Using OAuth1</title>
88  * <programlisting>
89  * <xi:include href="../gsignond-oauth1-example.listing" parse="text" xmlns:xi="http://www.w3.org/2001/XInclude"/>
90  * </programlisting>
91  * </example>
92  * 
93  * <example>
94  * <title>Using OAuth2</title>
95  * <programlisting>
96  * <xi:include href="../gsignond-oauth2-example.listing" parse="text" xmlns:xi="http://www.w3.org/2001/XInclude"/>
97  * </programlisting>
98  * </example>
99  *
100  * <refsect1><title>HTTP-related parameters in requests</title></refsect1>
101  * 
102  * Both OAuth1 and OAuth2 are using HTTP requests for authorization. It's possible
103  * to use the following entries in gsignond_plugin_request_initial() @session_data
104  * parameter to influence those requests:
105  * - gsignond_session_data_set_network_proxy() provides a HTTP proxy to use.
106  * If this parameter is not set, the system proxy configuration is used.
107  * - "SslStrict" key whose value is a gboolean. If set to FALSE, then server
108  * certificates which are invalid (for example, expired, or self-signed) 
109  * will not be rejected. If set to TRUE or not set, then server certificates
110  * have to be valid.
111  * 
112  * <refsect1><title>OAuth version 1 parameters for authorization</title></refsect1>
113  *
114  * Where not specified otherwise, parameters are strings.
115  *
116  * <refsect2><title>Parameters in gsignond_plugin_request_initial() @identity_method_cache</title></refsect2>
117
118  * This parameter contains a cache of previously received tokens in the form of
119  * a #GSignondDictionary. Tokens are indexed by a ConsumerKey in the dictionary,
120  * and each token is itself a #GSignondDictionary, with keys and values described
121  * below in the token format section.
122  * 
123  * <refsect2><title>Parameters in gsignond_plugin_request_initial() @session_data</title></refsect2>
124  * 
125  * - "ConsumerKey" (mandatory) - the identifier portion of the client 
126  * credentials (equivalent to a username). Refer to <ulink url=
127  * "http://tools.ietf.org/html/rfc5849#section-3.1">RFC5849 section 3.1</ulink>
128  * - gsignond_session_data_set_ui_policy() (mandatory) - if set to %GSIGNOND_UI_POLICY_DEFAULT
129  * a default authorization sequence is used, which may involve re-using a
130  * previously cached token without making any authorization server requests at all.
131  * If set to %GSIGNOND_UI_POLICY_REQUEST_PASSWORD any cached token corresponding
132  * to the ConsumerKey is discarded and the authorization procedure is started
133  * from the beginning.
134  * - gsignond_session_data_set_allowed_realms (mandatory) - a list of domains that
135  * RequestEndpoint, AuthorizationEndpoint and TokenEndpoint hosts must be in. There
136  * authorization sequence will fail if any of the endpoints is not in this list.
137  * - "Realm" (optional) - a requested realm for the token, as specified in
138  * <ulink url="http://tools.ietf.org/html/rfc5849#section-3.5.1">RFC5849 section 3.5.1.</ulink>
139  * - "RequestEndpoint" (mandatory) - a URL that specifies an endpoint used by 
140  * the plugin to obtain a set of temporary credentials, as specified in 
141  * <ulink url="http://tools.ietf.org/html/rfc5849#section-2">RFC5849 section 2.</ulink>
142  * The endpoint must use HTTPS scheme.
143  * - "Callback" (mandatory) - a callback URI where the user is redirected after
144  * completing the Resource Owner Authorization step, as specified in 
145  * <ulink url="http://tools.ietf.org/html/rfc5849#section-2">RFC5849 section 2.</ulink>
146  * - "SignatureMethod" (mandatory) - one of "PLAINTEXT", "HMAC-SHA1", or 
147  * "RSA-SHA1" - a method used used by the plugin to sign the requests. 
148  * Specified in <ulink url="http://tools.ietf.org/html/rfc5849#section-3.4">RFC5849 section 3.4.</ulink>
149  * - "ConsumerSecret" (optional) - the shared secret portion of the client
150  * credentials, used to sign requests to the server when using PLAINTEXT or
151  * HMAC-SHA1 signature methods. An empty consumer secret is used if it's not supplied.
152  * - "RSAPrivateKey" (mandatory, if RSA-SHA1 signature method is used) - PEM
153  * formatted X.509 private key, used to sign requests to the server when using
154  * RSA-SHA1 signature methods.
155  * - "AuthorizationEndpoint" (mandatory) - Resource Owner Authorization endpoint,
156  * to which the user (resource owner) is redirected to grant authorization, as specified in 
157  * <ulink url="http://tools.ietf.org/html/rfc5849#section-2">RFC5849 section 2.</ulink>
158  * - gsignond_session_data_set_username() and gsignond_session_data_set_secret() 
159  * (optional) - these two parameters may be used when opening the authorization 
160  * endpoint URI to initialize corresponding  fields on the webpage.
161  * - "TokenEndpoint" (mandatory) - a URL that specifies an endpoint used by 
162  * the plugin to obtain a set of access credentials, as specified in 
163  * <ulink url="http://tools.ietf.org/html/rfc5849#section-2">RFC5849 section 2.</ulink>
164  * 
165  * <refsect2><title>Parameters for #GSignondPlugin::user-action-required signal issued by plugin</title></refsect2>
166  * - gsignond_signonui_data_set_open_url() (mandatory) - a URI that should be opened in a 
167  * user-agent (browser) for the user (resource owner) to authenticate herself. 
168  * This URI is taken from "AuthorizationEndpoint" parameter of gsignond_plugin_request_initial()
169  * and additional parameters may be appended to the query component.
170  * - gsignond_signonui_data_set_final_url() (mandatory) - a URI where the user-agent should 
171  * be redirected after a successfull authentication by the resource owner. This
172  * expected URI is taken from the "Callback" parameter of gsignond_plugin_request_initial() call
173  * to the plugin.The actual (vs. expected) URI may contain additional parameters in the query 
174  * component of the URI that are used to continue the authorization process.
175  * - gsignond_signonui_data_set_username() and gsignond_signonui_data_set_password() 
176  * (optional) - these two parameters may be used when opening the URI to initialize 
177  * corresponding fields on the webpage.
178  * 
179  * <refsect2><title>Parameters in gsignond_plugin_user_action_finished() @ui_data </title></refsect2>
180  * 
181  * - gsignond_signonui_data_set_query_error() (mandatory) - indicates if there
182  * was an error in UI interaction and what it was. May be %SIGNONUI_ERROR_NONE
183  * (which means no error), %SIGNONUI_ERROR_CANCELED or any other error.
184  * - gsignond_signonui_data_get_url_response() (mandatory) - the URI that the user-agent
185  * was redirected to. The callback URI supplied in parameters of
186  * gsignond_plugin_request_initial() must be a prefix of this URI. The URI also
187  * has to contain parameters in the query component that are necessary to continue
188  * the authorization sequence.
189  * 
190  * <refsect2><title>Token and its parameters in #GSignondPlugin::response-final signal</title></refsect2>
191  * gsignond_plugin_response_final() signal concludes the authorization process
192  * and returns a #GSignondDictionary parameter that contains the access token
193  * and some token parameters:
194  * 
195  * - "AccessToken" (mandatory) - the token itself
196  * - "TokenSecret" (mandatory) - the token shared-secret, used by the application
197  * to sign requests for protected resources
198  * - "Realm" (optional) - the token realm, as specified in
199  * <ulink url="http://tools.ietf.org/html/rfc5849#section-3.5.1">RFC5849 section 3.5.1.</ulink>
200  * - "TokenParameters" (mandatory) - a #GSignondDictionary containing any
201  * additional parameters returned by the server together with the access token. 
202  * This dictionary may be empty, or
203  * if it's not, it typically contains service-specific, non-standardized keys and
204  * values.
205  * 
206  * <refsect1><title>OAuth version 2 parameters for authorization</title></refsect1>
207  *
208  * Where not specified otherwise, parameters are strings.
209  *
210  * <refsect2><title>Parameters in gsignond_plugin_request_initial() @identity_method_cache</title></refsect2>
211  *
212  * This parameter contains a cache of previously received tokens in the form of
213  * a #GSignondDictionary. The keys are tokens' ClientId and values are also
214  * #GSignondDictionary. Those second-level dictionaries contain token scopes as keys
215  * and tokens as values. This two-level approach is done to allow caching several
216  * tokens with unrelated scopes per client.
217  *
218  * Finally, each token is itself a #GSignondDictionary, with keys and values described
219  * below in the token format section.
220  *
221  * <refsect2><title>Parameters in gsignond_plugin_request_initial() @session_data</title></refsect2>
222  *
223  * - "ClientId" (mandatory) - client identifier as described in 
224  * <ulink url="http://tools.ietf.org/html/rfc6749#section-2.2">RFC6749 section 2.2.</ulink>
225  * - "ClientSecret" (optional) - client password as described in
226  * <ulink url="http://tools.ietf.org/html/rfc6749#section-2.3">RFC6749 section 2.3.</ulink>
227  * - gsignond_session_data_set_ui_policy() (mandatory) - if set to %GSIGNOND_UI_POLICY_DEFAULT
228  * a default authorization sequence is used, which may involve re-using a
229  * previously cached token without making any authorization server requests at all.
230  * If set to %GSIGNOND_UI_POLICY_REQUEST_PASSWORD any cached token information 
231  * (including a refresh token) corresponding to the ClientId is discarded and 
232  * the authorization procedure is started from the beginning.
233  * - gsignond_session_data_set_allowed_realms (mandatory) - a list of domains that
234  * AuthHost and TokenHost must be in. The authorization sequence will fail if 
235  * either of the hosts is not in this list.
236  * - "Scope" (optional) - a space-separated list of scopes that are requested 
237  * for the token, as specified in 
238  * <ulink url="http://tools.ietf.org/html/rfc6749#section-3.3">RFC6749 section 3.3.</ulink>
239  * - "ForceClientAuthViaRequestBody" (optional) - by default the clients are authenticated via
240  * HTTP Basic authorization mechanism, as described in 
241  * <ulink url="http://tools.ietf.org/html/rfc6749#section-2.3.1">RFC6749 section 2.3.1.</ulink>
242  * The RFC stipulates that all OAuth 2 servers must support this, however, it 
243  * was discovered that at least Google and Facebook require
244  * client authorization in the request body (which is, according to standard,
245  * optional and not recommended). If set to TRUE, this parameter forces this
246  * non-compliant client authorization to be used.
247  * - "ForceTokenRefresh" (optional) - normally if the token cache contains a
248  * suitable token, it is returned immediately. If this parameter is set to TRUE,
249  * then a refresh token is always used instead to obtain a new token.
250  *
251  * <refsect3><title>Parameters used for authorization code grant or implicit grant flows</title></refsect3>
252  * - "ResponseType" (mandatory) - should be set to "code" or "token" as described in 
253  * <ulink url="http://tools.ietf.org/html/rfc6749#section-3.1.1">RFC6749 section 3.1.1.</ulink>
254  * - "AuthHost" (mandatory) - hostname component of the authorization endpoint URI, as described in
255  * <ulink url="http://tools.ietf.org/html/rfc6749#section-3.1">RFC6749 section 3.1.</ulink> 
256  * - "AuthPath" (mandatory) - pathname component of the authorization endpoint URI, as described in
257  * <ulink url="http://tools.ietf.org/html/rfc6749#section-3.1">RFC6749 section 3.1.</ulink> 
258  * - "AuthPort" (optional) - port component of the authorization endpoint URI, as described in
259  * <ulink url="http://tools.ietf.org/html/rfc6749#section-3.1">RFC6749 section 3.1.</ulink>
260  * If not specified, standard https port is used.
261  * - "AuthQuery" (optional) - query component of the authorization endpoint URI, as described in
262  * <ulink url="http://tools.ietf.org/html/rfc6749#section-3.1">RFC6749 section 3.1.</ulink>
263  * - "RedirectUri" (optional) - redirection endpoint as described in
264  * <ulink url="http://tools.ietf.org/html/rfc6749#section-3.1.2">RFC6749 section 3.1.2.</ulink>
265  * - gsignond_session_data_set_username() and gsignond_session_data_set_secret() 
266  * (optional) - these two parameters may be used by UI implementation to 
267  * initialize corresponding  fields on the webpage when opening the authorization 
268  * endpoint URI. Also see "UseLoginHint".
269  * - "UseLoginHint" (optional) - if set to TRUE, add the username (see above) to 
270  * the authorization URI as a "login_hint" parameter, so that the authorization 
271  * endpoint can pre-fill the login box, or selecte the proper multi-login session. 
272  * This is a Google extension specified at 
273  * <ulink url="https://developers.google.com/accounts/docs/OAuth2InstalledApp#formingtheurl">
274  * https://developers.google.com/accounts/docs/OAuth2InstalledApp#formingtheurl</ulink>.
275  * - "UseDisplay" (optional) - if set to a string, the parameter value is added to the authorization
276  * URI as a "display" parameter. This is a Facebook extension specified at
277  * <ulink url="https://developers.facebook.com/docs/reference/dialogs/oauth/">
278  * https://developers.facebook.com/docs/reference/dialogs/oauth/</ulink>
279  * and it affects the way the authorization page looks. Typical values are "page",
280  * "popup" and "touch".
281  *
282  * <refsect3><title>Parameters used for resource owner password credentials grant flow</title></refsect3>
283  * Refer to <ulink url="http://tools.ietf.org/html/rfc6749#section-4.3">RFC6749 section 4.3.</ulink>
284  * - "GrantType" (mandatory) - must be set to "password"
285  * - gsignond_session_data_set_username() and gsignond_session_data_set_secret() 
286  * (mandatory) - resource owner username and password
287  *
288  * <refsect3><title>Parameters used for client credentials grant flow</title></refsect3>
289  * Refer to <ulink url="http://tools.ietf.org/html/rfc6749#section-4.4">RFC6749 section 4.4.</ulink>
290  * - "GrantType" (mandatory) - must be set to "client_credentials"
291  *
292  * <refsect3><title>Parameters used for authorization code, resource owner 
293  * password or client credentials grant flows (but not implicit grant flow) </title></refsect3>
294  *
295  * - "TokenHost" (mandatory) - hostname component of the token endpoint URI, as described in
296  * <ulink url="http://tools.ietf.org/html/rfc6749#section-3.2">RFC6749 section 3.2.</ulink> 
297  * - "TokenPath" (mandatory) - pathname component of the token endpoint URI, as described in
298  * <ulink url="http://tools.ietf.org/html/rfc6749#section-3.2">RFC6749 section 3.2.</ulink> 
299  * - "TokenPort" (optional) - port component of the token endpoint URI, as described in
300  * <ulink url="http://tools.ietf.org/html/rfc6749#section-3.2">RFC6749 section 3.2.</ulink>
301  * If not specified, standard https port is used.
302  * - "TokenQuery" (optional) - query component of the token endpoint URI, as described in
303  * <ulink url="http://tools.ietf.org/html/rfc6749#section-3.2">RFC6749 section 3.2.</ulink>
304  *
305  * <refsect2><title>Parameters for #GSignondPlugin::user-action-required signal issued by plugin</title></refsect2>
306  * This signal is issued only when using authorization code grant or implicit code grant flows,
307  * and contains the following parameters:
308  * - gsignond_signonui_data_set_open_url() (mandatory) - an authorization endpoint URI that should be opened in a 
309  * user-agent (browser) for the user (resource owner) to authenticate herself. 
310  * This URI is constructed using parameters of gsignond_plugin_request_initial()
311  * and additional parameters may be appended to the query component.
312  * - gsignond_signonui_data_set_final_url() (optional) - a redirection endpoint URI where the user-agent should 
313  * be redirected after authentication by the resource owner has finished. This
314  * expected URI is taken from the "RedirectUri" parameter of gsignond_plugin_request_initial() call
315  * to the plugin. The actual (vs. expected) URI may contain additional parameters in the query or fragment
316  * components of the URI that are used to determine the outcome of the authorization process.
317  * - gsignond_signonui_data_set_username() and gsignond_signonui_data_set_password() 
318  * (optional) - these two parameters may be used when opening the authorization endpoint URI to initialize 
319  * corresponding fields on the webpage.
320  * 
321  * <refsect2><title>Parameters in gsignond_plugin_user_action_finished() @ui_data </title></refsect2>
322  * 
323  * This function is called when UI interaction has completed and only when using
324  * authorization code grant or implicit code grant flows.
325  * 
326  * - gsignond_signonui_data_set_query_error() (mandatory) - indicates if there
327  * was an error in UI interaction and what it was. May be %SIGNONUI_ERROR_NONE
328  * (which means no error), %SIGNONUI_ERROR_CANCELED or any other error.
329  * - gsignond_signonui_data_get_url_response() (mandatory) - the URI that the user-agent
330  * was redirected to. The redirection endpoint URI supplied in parameters of
331  * gsignond_plugin_request_initial() must be a prefix of this URI. The URI also
332  * has to contain parameters in the query component (if using authorization code grant) 
333  * or in the fragment component (if using implicit code grant) that are necessary to continue
334  * the authorization sequence. Specific information is provided at 
335  * <ulink url="http://tools.ietf.org/html/rfc6749#section-4.1.2">RFC6749 section 4.1.2</ulink>
336  * and <ulink url="http://tools.ietf.org/html/rfc6749#section-4.2.2">RFC6749 section 4.2.2</ulink>
337  * respectively.
338  *
339  * <refsect2><title>Token and its parameters in #GSignondPlugin::response-final signal</title></refsect2>
340  * gsignond_plugin_response_final() signal concludes the authorization process
341  * and returns a #GSignondDictionary parameter that contains the access token
342  * and some token parameters:
343  * 
344  * - "AccessToken" (mandatory) - the token itself
345  * - "TokenType" (mandatory) - the token type, as specified in
346  * <ulink url="http://tools.ietf.org/html/rfc6749#section-7.1">RFC6749 section 7.1</ulink>.
347  * Currently only one token type is supported (the bearer token, standardizied in
348  * <ulink url="http://tools.ietf.org/html/rfc6750">RFC6750</ulink>).
349  * - "TokenParameters" (mandatory) - a #GSignondDictionary containing any
350  * additional parameters returned by the server together with the access token. 
351  * The contents of this parameter is specific to the token type, and for bearer
352  * tokens it's empty.
353  * - "Scope" (optional) - the scopes of the issued token, a space separated list
354  * as specified in 
355  * <ulink url="http://tools.ietf.org/html/rfc6749#section-3.3">RFC6749 section 3.3.</ulink>
356  * - "Timestamp" (mandatory) - a gint64 Unix time specifying the time when token was issued.
357  * A Unix time is the number of seconds that have elapsed since 1970-01-01 00:00:00 UTC.
358  * - "Duration" (optional) - the lifetime in seconds of the access token. If specified, the token
359  * will expire at Timestamp+Duration point in time.
360  * - "RefreshToken" (optional) - refresh token as specified in
361  * <ulink url="http://tools.ietf.org/html/rfc6749#section-6">RFC6749 section 6.</ulink>
362  *
363  * <refsect1><title>Plugin API common to OAuth1 and Oauth2</title></refsect1>
364  *
365  * <refsect2><title>Parameters of #GSignondPlugin::store signal</title></refsect2>
366  * This signal is issued by the plugin when the token cache needs to be updated.
367  * The parameter is a #GSignondDictionary of tokens. The specific cache format 
368  * is same as @identity_method_cache parameter of gsignond_plugin_request_initial()
369  * and is desribed in detail in corresponding OAuth1 and OAuth2 sections.
370  * 
371  * The token cache should be entirely replaced with the parameter of the signal;
372  * the plugin preserves existing tokens that were supplied to 
373  * gsignond_plugin_request_initial() in @identity_method_cache parameter.
374  * 
375  * <refsect2><title>Errors issued via #GSignondPlugin::error signal</title></refsect2>
376  * At any point in the authorization process the plugin may issue this signal
377  * with an @error parameter that is a #GError. The @error has <literal>domain</literal> field set to
378  * %GSIGNOND_ERROR. <literal>code</literal> field can be one of %GSIGNOND_ERROR_MISSING_DATA 
379  * (not enough data was supplied in gsignond_plugin_request_initial()),
380  * %GSIGNOND_ERROR_NOT_AUTHORIZED (there was an error in the authorization
381  * process), %GSIGNOND_ERROR_USER_INTERACTION (there was an error in the interaction
382  * with the user), %GSIGNOND_ERROR_SESSION_CANCELED (the authorization process
383  * was canceled). <literal>message</literal> field tells additional details about the exact cause of the
384  * error, and it's intended to help programming and debugging, but not meant
385  * to be understood by end users directly (although it can be shown to them).
386  *
387  */
388
389
390 #include <gsignond/gsignond-plugin-interface.h>
391 #include "gsignond-oauth-plugin.h"
392 #include <gsignond/gsignond-error.h>
393 #include <gsignond/gsignond-log.h>
394 #include <stdlib.h>
395 #include "gsignond-oauth-plugin-oauth1.h"
396 #include "gsignond-oauth-plugin-oauth2.h"
397
398
399 static void gsignond_plugin_interface_init (GSignondPluginInterface *iface);
400
401 G_DEFINE_TYPE_WITH_CODE (GSignondOauthPlugin, gsignond_oauth_plugin, 
402                          G_TYPE_OBJECT,
403                          G_IMPLEMENT_INTERFACE (GSIGNOND_TYPE_PLUGIN,
404                                                 gsignond_plugin_interface_init));
405
406 static void _do_reset(GSignondOauthPlugin *self)
407 {
408     if (self->soup_session)
409         soup_session_abort(self->soup_session);
410     _do_reset_oauth2(self);
411     _do_reset_oauth1(self);
412 }
413
414
415 static void gsignond_oauth_plugin_cancel (GSignondPlugin *self)
416 {
417     _do_reset(GSIGNOND_OAUTH_PLUGIN(self));
418     GError* error = g_error_new(GSIGNOND_ERROR, 
419                                 GSIGNOND_ERROR_SESSION_CANCELED,
420                                 "Session canceled");
421     gsignond_plugin_error (self, error); 
422     g_error_free(error);
423 }
424
425 static void gsignond_oauth_plugin_request (
426     GSignondPlugin *plugin, GSignondSessionData *session_data)
427 {
428     GError* error = g_error_new(GSIGNOND_ERROR, 
429                                 GSIGNOND_ERROR_WRONG_STATE,
430                                 "Oauth plugin doesn't support request");
431     gsignond_plugin_error (plugin, error); 
432     g_error_free(error);
433     return;    
434 }
435
436 static void gsignond_oauth_plugin_request_initial (
437     GSignondPlugin *plugin, GSignondSessionData *session_data, 
438     GSignondDictionary *token_cache,
439     const gchar *mechanism)
440 {
441     GSignondOauthPlugin *self = GSIGNOND_OAUTH_PLUGIN (plugin);
442
443     _do_reset(self);
444     
445     const gchar* proxy = gsignond_session_data_get_network_proxy(session_data);
446     SoupURI* uri = NULL;
447     if (proxy != NULL) {
448         uri = soup_uri_new(proxy);
449         g_object_set(self->soup_session, "proxy-uri", uri, NULL);
450     }
451     
452     gboolean ssl_strict;
453     gboolean res = gsignond_dictionary_get_boolean(session_data,
454                                                    "SslStrict",
455                                                    &ssl_strict);
456     if (res == FALSE)
457         ssl_strict = TRUE;
458     g_object_set(self->soup_session, "ssl-strict", ssl_strict, NULL);
459     
460     if (g_strcmp0(mechanism, "oauth2") == 0)
461         _process_oauth2_request(self, session_data, token_cache);
462     else if (g_strcmp0(mechanism, "oauth1") == 0)
463         _process_oauth1_request(self, session_data, token_cache);
464     else {
465         GError* error = g_error_new(GSIGNOND_ERROR, 
466                                 GSIGNOND_ERROR_MECHANISM_NOT_AVAILABLE,
467                                 "Requested mechanism is not available");
468         gsignond_plugin_error (plugin, error); 
469         g_error_free(error);
470     }
471 }
472
473 static void gsignond_oauth_plugin_user_action_finished (
474     GSignondPlugin *plugin, 
475     GSignondSignonuiData *session_data)
476 {
477     GSignondOauthPlugin *self = GSIGNOND_OAUTH_PLUGIN (plugin);
478     
479     if (_is_active_oauth2_session(self) == TRUE)
480         _process_oauth2_user_action_finished(self, session_data);
481     else if (_is_active_oauth1_session(self) == TRUE)
482         _process_oauth1_user_action_finished(self, session_data);
483     else {
484         GError* error = g_error_new(GSIGNOND_ERROR, 
485                             GSIGNOND_ERROR_WRONG_STATE,
486                             "Oauth plugin doesn't support user actions");
487         gsignond_plugin_error (plugin, error); 
488         g_error_free(error);
489     }
490 }
491
492 static void gsignond_oauth_plugin_refresh (
493     GSignondPlugin *plugin, 
494     GSignondSignonuiData *session_data)
495 {
496     GError* error = g_error_new(GSIGNOND_ERROR, 
497                             GSIGNOND_ERROR_WRONG_STATE,
498                             "Oauth plugin doesn't support refresh");
499     gsignond_plugin_error (plugin, error); 
500     g_error_free(error);
501     return;
502 }
503
504 static void
505 gsignond_plugin_interface_init (GSignondPluginInterface *iface)
506 {
507     iface->cancel = gsignond_oauth_plugin_cancel;
508     iface->request_initial = gsignond_oauth_plugin_request_initial;
509     iface->request = gsignond_oauth_plugin_request;
510     iface->user_action_finished = gsignond_oauth_plugin_user_action_finished;
511     iface->refresh = gsignond_oauth_plugin_refresh;
512 }
513
514 static void
515 _http_authenticate (SoupSession *session, SoupMessage *msg,
516               SoupAuth *auth, gboolean retrying, gpointer data)
517 {
518     GSignondOauthPlugin *self = GSIGNOND_OAUTH_PLUGIN (data);
519     
520     if (!retrying)
521         if (_is_active_oauth2_session(self) == TRUE)
522             _oauth2_http_authenticate (self, auth);
523 }
524
525 static void _log_http_traffic(SoupLogger *logger,
526                               SoupLoggerLogLevel level,
527                               char direction,
528                               const char *data,
529                               gpointer user_data)
530 {
531     g_debug ("%c %s", direction, data);
532 }
533
534 static void
535 gsignond_oauth_plugin_init (GSignondOauthPlugin *self)
536 {
537     self->oauth2_request = NULL;
538     self->oauth1_request = NULL;
539     self->token_cache = NULL;
540
541     self->soup_session = soup_session_async_new_with_options (
542         SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_PROXY_RESOLVER_DEFAULT,
543         SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE, TRUE,
544         NULL);
545
546     SoupLogger *logger = soup_logger_new (SOUP_LOGGER_LOG_BODY, -1);
547     soup_logger_set_printer(logger, _log_http_traffic, NULL, NULL);
548     soup_session_add_feature (self->soup_session, SOUP_SESSION_FEATURE (logger));
549     g_object_unref (logger);    
550     
551     g_signal_connect (self->soup_session, "authenticate",
552                           G_CALLBACK (_http_authenticate), self);    
553 }
554
555 static void
556 gsignond_oauth_plugin_finalize (GObject *gobject)
557 {
558     GSignondOauthPlugin *self = GSIGNOND_OAUTH_PLUGIN (gobject);
559
560     if (self->oauth2_request)
561         gsignond_dictionary_unref(self->oauth2_request);
562     if (self->oauth1_request)
563         gsignond_dictionary_unref(self->oauth1_request);
564     if (self->token_cache)
565         gsignond_dictionary_unref(self->token_cache);
566     if (self->soup_session)
567         g_object_unref(self->soup_session);
568
569     /* Chain up to the parent class */
570     G_OBJECT_CLASS (gsignond_oauth_plugin_parent_class)->finalize (gobject);
571 }
572
573 enum
574 {
575     PROP_0,
576     
577     PROP_TYPE,
578     PROP_MECHANISMS
579 };
580
581 static void
582 gsignond_oauth_plugin_set_property (GObject      *object,
583                                        guint         property_id,
584                                        const GValue *value,
585                                        GParamSpec   *pspec)
586 {
587     switch (property_id)
588     {
589         default:
590             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
591             break;
592     }
593 }
594
595 static void
596 gsignond_oauth_plugin_get_property (GObject    *object,
597                                        guint       prop_id,
598                                        GValue     *value,
599                                        GParamSpec *pspec)
600 {
601
602     gchar *mechanisms[] = {"oauth1", "oauth2", NULL };
603     
604     switch (prop_id)
605     {
606         case PROP_TYPE:
607             g_value_set_string (value, "oauth");
608             break;
609         case PROP_MECHANISMS:
610             g_value_set_boxed (value, mechanisms);
611             break;
612             
613         default:
614             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
615             break;
616     }
617 }
618
619 static void
620 gsignond_oauth_plugin_class_init (GSignondOauthPluginClass *klass)
621 {
622     GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
623     
624     gobject_class->set_property = gsignond_oauth_plugin_set_property;
625     gobject_class->get_property = gsignond_oauth_plugin_get_property;
626     gobject_class->finalize = gsignond_oauth_plugin_finalize;
627
628     g_object_class_override_property (gobject_class, PROP_TYPE, "type");
629     g_object_class_override_property (gobject_class, PROP_MECHANISMS, 
630                                       "mechanisms");
631 }