2 * empathy-goa-auth-handler.c - Source for Goa SASL authentication
3 * Copyright (C) 2011 Collabora Ltd.
4 * @author Xavier Claessens <xavier.claessens@collabora.co.uk>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 #include "empathy-goa-auth-handler.h"
24 #define GOA_API_IS_SUBJECT_TO_CHANGE /* awesome! */
27 #include "empathy-sasl-mechanisms.h"
29 #define DEBUG_FLAG EMPATHY_DEBUG_SASL
30 #include "empathy-debug.h"
32 struct _EmpathyGoaAuthHandlerPriv
35 gboolean client_preparing
;
37 /* List of AuthData waiting for client to be created */
41 G_DEFINE_TYPE (EmpathyGoaAuthHandler
, empathy_goa_auth_handler
, G_TYPE_OBJECT
);
44 empathy_goa_auth_handler_init (EmpathyGoaAuthHandler
*self
)
46 self
->priv
= G_TYPE_INSTANCE_GET_PRIVATE (self
,
47 EMPATHY_TYPE_GOA_AUTH_HANDLER
, EmpathyGoaAuthHandlerPriv
);
51 empathy_goa_auth_handler_dispose (GObject
*object
)
53 EmpathyGoaAuthHandler
*self
= (EmpathyGoaAuthHandler
*) object
;
55 /* AuthData keeps a ref on self */
56 g_assert (self
->priv
->auth_queue
== NULL
);
58 tp_clear_object (&self
->priv
->client
);
60 G_OBJECT_CLASS (empathy_goa_auth_handler_parent_class
)->dispose (object
);
64 empathy_goa_auth_handler_class_init (EmpathyGoaAuthHandlerClass
*klass
)
66 GObjectClass
*oclass
= G_OBJECT_CLASS (klass
);
68 oclass
->dispose
= empathy_goa_auth_handler_dispose
;
70 g_type_class_add_private (klass
, sizeof (EmpathyGoaAuthHandlerPriv
));
73 EmpathyGoaAuthHandler
*
74 empathy_goa_auth_handler_new (void)
76 return g_object_new (EMPATHY_TYPE_GOA_AUTH_HANDLER
, NULL
);
81 EmpathyGoaAuthHandler
*self
;
85 GoaObject
*goa_object
;
90 auth_data_new (EmpathyGoaAuthHandler
*self
,
96 data
= g_slice_new0 (AuthData
);
97 data
->self
= g_object_ref (self
);
98 data
->channel
= g_object_ref (channel
);
99 data
->account
= g_object_ref (account
);
105 auth_data_free (AuthData
*data
)
107 tp_clear_object (&data
->self
);
108 tp_clear_object (&data
->channel
);
109 tp_clear_object (&data
->account
);
110 tp_clear_object (&data
->goa_object
);
111 g_free (data
->access_token
);
112 g_slice_free (AuthData
, data
);
116 fail_auth (AuthData
*data
)
118 DEBUG ("Auth failed for account %s",
119 tp_proxy_get_object_path (data
->account
));
121 tp_channel_close_async (data
->channel
, NULL
, NULL
);
122 auth_data_free (data
);
126 auth_cb (GObject
*source
,
127 GAsyncResult
*result
,
130 TpChannel
*channel
= (TpChannel
*) source
;
131 AuthData
*data
= user_data
;
132 GError
*error
= NULL
;
134 if (!empathy_sasl_auth_finish (channel
, result
, &error
))
136 DEBUG ("SASL Mechanism error: %s", error
->message
);
138 g_clear_error (&error
);
143 tp_channel_close_async (channel
, NULL
, NULL
);
144 auth_data_free (data
);
148 got_oauth2_access_token_cb (GObject
*source
,
149 GAsyncResult
*result
,
152 GoaOAuth2Based
*oauth2
= (GoaOAuth2Based
*) source
;
153 AuthData
*data
= user_data
;
156 GError
*error
= NULL
;
158 if (!goa_oauth2_based_call_get_access_token_finish (oauth2
,
159 &access_token
, &expires_in
, result
, &error
))
161 DEBUG ("Failed to get access token: %s", error
->message
);
163 g_clear_error (&error
);
167 DEBUG ("Got access token for %s:\n%s",
168 tp_proxy_get_object_path (data
->account
),
171 switch (empathy_sasl_channel_select_mechanism (data
->channel
))
173 case EMPATHY_SASL_MECHANISM_FACEBOOK
:
174 empathy_sasl_auth_facebook_async (data
->channel
,
175 goa_oauth2_based_get_client_id (oauth2
), access_token
,
179 case EMPATHY_SASL_MECHANISM_WLM
:
180 empathy_sasl_auth_wlm_async (data
->channel
,
185 case EMPATHY_SASL_MECHANISM_GOOGLE
:
186 empathy_sasl_auth_google_async (data
->channel
,
187 goa_account_get_identity (goa_object_peek_account (data
->goa_object
)),
188 access_token
, auth_cb
, data
);
192 g_assert_not_reached ();
195 g_free (access_token
);
199 got_password_passwd_cb (GObject
*source
,
200 GAsyncResult
*result
,
203 GoaPasswordBased
*password
= (GoaPasswordBased
*) source
;
204 AuthData
*data
= user_data
;
206 GError
*error
= NULL
;
208 if (!goa_password_based_call_get_password_finish (password
,
209 &passwd
, result
, &error
))
211 DEBUG ("Failed to get password: %s", error
->message
);
213 g_clear_error (&error
);
217 DEBUG ("Got password for %s", tp_proxy_get_object_path (data
->account
));
219 empathy_sasl_auth_password_async (data
->channel
, passwd
, auth_cb
, data
);
224 ensure_credentials_cb (GObject
*source
,
225 GAsyncResult
*result
,
228 AuthData
*data
= user_data
;
229 GoaAccount
*goa_account
= (GoaAccount
*) source
;
230 GoaOAuth2Based
*oauth2
;
231 GoaPasswordBased
*password
;
232 EmpathySaslMechanism mech
;
233 gboolean supports_password
;
235 GError
*error
= NULL
;
237 if (!goa_account_call_ensure_credentials_finish (goa_account
, &expires_in
,
240 DEBUG ("Failed to EnsureCredentials: %s", error
->message
);
242 g_clear_error (&error
);
246 /* We prefer oauth2, if available */
247 oauth2
= goa_object_get_oauth2_based (data
->goa_object
);
248 mech
= empathy_sasl_channel_select_mechanism (data
->channel
);
249 if (oauth2
!= NULL
&& mech
!= EMPATHY_SASL_MECHANISM_PASSWORD
)
251 DEBUG ("Goa daemon has credentials for %s, get the access token",
252 tp_proxy_get_object_path (data
->account
));
254 goa_oauth2_based_call_get_access_token (oauth2
, NULL
,
255 got_oauth2_access_token_cb
, data
);
257 g_object_unref (oauth2
);
261 /* Else we use the password */
262 password
= goa_object_get_password_based (data
->goa_object
);
263 supports_password
= empathy_sasl_channel_supports_mechanism (data
->channel
,
264 "X-TELEPATHY-PASSWORD");
265 if (password
!= NULL
&& supports_password
)
267 DEBUG ("Goa daemon has credentials for %s, get the password",
268 tp_proxy_get_object_path (data
->account
));
270 /* arg_id is currently unused */
271 goa_password_based_call_get_password (password
, "", NULL
,
272 got_password_passwd_cb
, data
);
274 g_object_unref (password
);
278 DEBUG ("GoaObject does not implement oauth2 or password");
283 start_auth (AuthData
*data
)
285 EmpathyGoaAuthHandler
*self
= data
->self
;
286 const GValue
*id_value
;
288 GList
*goa_accounts
, *l
;
289 gboolean found
= FALSE
;
291 id_value
= tp_account_get_storage_identifier (data
->account
);
292 id
= g_value_get_string (id_value
);
294 goa_accounts
= goa_client_get_accounts (self
->priv
->client
);
295 for (l
= goa_accounts
; l
!= NULL
&& !found
; l
= l
->next
)
297 GoaObject
*goa_object
= l
->data
;
298 GoaAccount
*goa_account
;
300 goa_account
= goa_object_get_account (goa_object
);
301 if (!tp_strdiff (goa_account_get_id (goa_account
), id
))
303 data
->goa_object
= g_object_ref (goa_object
);
305 DEBUG ("Found the GoaAccount for %s, ensure credentials",
306 tp_proxy_get_object_path (data
->account
));
308 goa_account_call_ensure_credentials (goa_account
, NULL
,
309 ensure_credentials_cb
, data
);
314 g_object_unref (goa_account
);
316 g_list_free_full (goa_accounts
, g_object_unref
);
320 DEBUG ("Cannot find GoaAccount");
326 client_new_cb (GObject
*source
,
327 GAsyncResult
*result
,
330 EmpathyGoaAuthHandler
*self
= user_data
;
332 GError
*error
= NULL
;
334 self
->priv
->client_preparing
= FALSE
;
335 self
->priv
->client
= goa_client_new_finish (result
, &error
);
336 if (self
->priv
->client
== NULL
)
338 DEBUG ("Error getting GoaClient: %s", error
->message
);
339 g_clear_error (&error
);
342 /* process queued data */
343 for (l
= self
->priv
->auth_queue
; l
!= NULL
; l
= l
->next
)
345 AuthData
*data
= l
->data
;
347 if (self
->priv
->client
!= NULL
)
353 tp_clear_pointer (&self
->priv
->auth_queue
, g_list_free
);
357 empathy_goa_auth_handler_start (EmpathyGoaAuthHandler
*self
,
363 g_return_if_fail (TP_IS_CHANNEL (channel
));
364 g_return_if_fail (TP_IS_ACCOUNT (account
));
365 g_return_if_fail (empathy_goa_auth_handler_supports (self
, channel
, account
));
367 DEBUG ("Start Goa auth for account: %s",
368 tp_proxy_get_object_path (account
));
370 data
= auth_data_new (self
, channel
, account
);
372 if (self
->priv
->client
== NULL
)
374 /* GOA client not ready yet, queue data */
375 if (!self
->priv
->client_preparing
)
377 goa_client_new (NULL
, client_new_cb
, self
);
378 self
->priv
->client_preparing
= TRUE
;
381 self
->priv
->auth_queue
= g_list_prepend (self
->priv
->auth_queue
, data
);
390 empathy_goa_auth_handler_supports (EmpathyGoaAuthHandler
*self
,
394 const gchar
*provider
;
395 EmpathySaslMechanism mech
;
397 g_return_val_if_fail (TP_IS_CHANNEL (channel
), FALSE
);
398 g_return_val_if_fail (TP_IS_ACCOUNT (account
), FALSE
);
400 provider
= tp_account_get_storage_provider (account
);
401 if (tp_strdiff (provider
, EMPATHY_GOA_PROVIDER
))
404 mech
= empathy_sasl_channel_select_mechanism (channel
);
405 return mech
== EMPATHY_SASL_MECHANISM_FACEBOOK
||
406 mech
== EMPATHY_SASL_MECHANISM_WLM
||
407 mech
== EMPATHY_SASL_MECHANISM_GOOGLE
||
408 mech
== EMPATHY_SASL_MECHANISM_PASSWORD
;