2 * empathy-auth-uoa.c - Source for Uoa SASL authentication
3 * Copyright (C) 2012 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-uoa-auth-handler.h"
24 #include <libaccounts-glib/ag-account.h>
25 #include <libaccounts-glib/ag-account-service.h>
26 #include <libaccounts-glib/ag-auth-data.h>
27 #include <libaccounts-glib/ag-manager.h>
28 #include <libaccounts-glib/ag-service.h>
30 #include <libsignon-glib/signon-identity.h>
31 #include <libsignon-glib/signon-auth-session.h>
33 #include <tp-account-widgets/tpaw-keyring.h>
34 #include <tp-account-widgets/tpaw-uoa-utils.h>
36 #include "empathy-utils.h"
37 #include "empathy-sasl-mechanisms.h"
39 #define DEBUG_FLAG EMPATHY_DEBUG_SASL
40 #include "empathy-debug.h"
42 struct _EmpathyUoaAuthHandlerPriv
47 G_DEFINE_TYPE (EmpathyUoaAuthHandler
, empathy_uoa_auth_handler
, G_TYPE_OBJECT
);
50 empathy_uoa_auth_handler_init (EmpathyUoaAuthHandler
*self
)
52 self
->priv
= G_TYPE_INSTANCE_GET_PRIVATE (self
,
53 EMPATHY_TYPE_UOA_AUTH_HANDLER
, EmpathyUoaAuthHandlerPriv
);
55 self
->priv
->manager
= tpaw_uoa_manager_dup ();
59 empathy_uoa_auth_handler_dispose (GObject
*object
)
61 EmpathyUoaAuthHandler
*self
= (EmpathyUoaAuthHandler
*) object
;
63 tp_clear_object (&self
->priv
->manager
);
65 G_OBJECT_CLASS (empathy_uoa_auth_handler_parent_class
)->dispose (object
);
69 empathy_uoa_auth_handler_class_init (EmpathyUoaAuthHandlerClass
*klass
)
71 GObjectClass
*oclass
= G_OBJECT_CLASS (klass
);
73 oclass
->dispose
= empathy_uoa_auth_handler_dispose
;
75 g_type_class_add_private (klass
, sizeof (EmpathyUoaAuthHandlerPriv
));
78 EmpathyUoaAuthHandler
*
79 empathy_uoa_auth_handler_new (void)
81 return g_object_new (EMPATHY_TYPE_UOA_AUTH_HANDLER
, NULL
);
87 AgAccountService
*service
;
88 AgAuthData
*auth_data
;
89 SignonIdentity
*identity
;
90 SignonAuthSession
*session
;
96 auth_context_new (TpChannel
*channel
,
97 AgAccountService
*service
)
102 ctx
= g_slice_new0 (AuthContext
);
103 ctx
->channel
= g_object_ref (channel
);
104 ctx
->service
= g_object_ref (service
);
106 ctx
->auth_data
= ag_account_service_get_auth_data (service
);
107 if (ctx
->auth_data
== NULL
)
110 cred_id
= ag_auth_data_get_credentials_id (ctx
->auth_data
);
114 ctx
->identity
= signon_identity_new_from_db (cred_id
);
115 if (ctx
->identity
== NULL
)
118 ctx
->session
= signon_identity_create_session (ctx
->identity
,
119 ag_auth_data_get_method (ctx
->auth_data
), NULL
);
120 if (ctx
->session
== NULL
)
128 auth_context_free (AuthContext
*ctx
)
130 g_clear_object (&ctx
->channel
);
131 g_clear_object (&ctx
->service
);
132 tp_clear_pointer (&ctx
->auth_data
, ag_auth_data_unref
);
133 g_clear_object (&ctx
->session
);
134 g_clear_object (&ctx
->identity
);
135 g_free (ctx
->username
);
137 g_slice_free (AuthContext
, ctx
);
141 auth_context_done (AuthContext
*ctx
)
143 tp_channel_close_async (ctx
->channel
, NULL
, NULL
);
144 auth_context_free (ctx
);
148 request_password_session_process_cb (GObject
*source
,
149 GAsyncResult
*result
,
152 SignonAuthSession
*session
= (SignonAuthSession
*) source
;
153 AuthContext
*ctx
= user_data
;
155 GError
*error
= NULL
;
157 variant
= signon_auth_session_process_finish (session
, result
, &error
);
160 DEBUG ("Error processing the session to request user's attention: %s",
162 g_clear_error (&error
);
165 g_variant_unref (variant
);
167 auth_context_done (ctx
);
171 request_password (AuthContext
*ctx
)
173 GVariantBuilder builder
;
175 DEBUG ("Invalid credentials, request user action");
177 /* Inform SSO that the access token (or password) didn't work and it should
178 * ask user to re-grant access (or retype password). */
179 g_variant_builder_init (&builder
, G_VARIANT_TYPE_VARDICT
);
180 g_variant_builder_add (&builder
, "{sv}",
181 SIGNON_SESSION_DATA_UI_POLICY
,
182 g_variant_new_int32 (SIGNON_POLICY_REQUEST_PASSWORD
));
184 signon_auth_session_process_async (ctx
->session
,
185 ag_auth_data_get_login_parameters (ctx
->auth_data
,
186 g_variant_builder_end (&builder
)),
187 ag_auth_data_get_mechanism (ctx
->auth_data
),
189 request_password_session_process_cb
, ctx
);
193 auth_cb (GObject
*source
,
194 GAsyncResult
*result
,
197 TpChannel
*channel
= (TpChannel
*) source
;
198 AuthContext
*ctx
= user_data
;
199 GError
*error
= NULL
;
201 if (!empathy_sasl_auth_finish (channel
, result
, &error
))
203 DEBUG ("SASL Mechanism error: %s", error
->message
);
204 g_clear_error (&error
);
206 request_password (ctx
);
210 DEBUG ("Auth on %s suceeded", tp_proxy_get_object_path (channel
));
211 auth_context_done (ctx
);
216 session_process_cb (GObject
*source
,
217 GAsyncResult
*result
,
220 SignonAuthSession
*session
= (SignonAuthSession
*) source
;
221 AuthContext
*ctx
= user_data
;
222 GVariant
*session_data
;
223 const gchar
*access_token
;
224 const gchar
*client_id
;
227 GError
*error
= NULL
;
229 session_data
= signon_auth_session_process_finish (session
, result
, &error
);
232 DEBUG ("Error processing the session: %s", error
->message
);
233 auth_context_done (ctx
);
234 g_clear_error (&error
);
238 params
= g_variant_ref_sink (
239 ag_auth_data_get_login_parameters (ctx
->auth_data
, NULL
));
241 g_variant_lookup (params
, "ClientId", "&s", &client_id
);
242 g_variant_lookup (session_data
, "AccessToken", "&s", &access_token
);
243 g_variant_lookup (session_data
, "Secret", "&s", &secret
);
245 switch (empathy_sasl_channel_select_mechanism (ctx
->channel
))
247 case EMPATHY_SASL_MECHANISM_FACEBOOK
:
248 empathy_sasl_auth_facebook_async (ctx
->channel
,
249 client_id
, access_token
,
253 case EMPATHY_SASL_MECHANISM_WLM
:
254 empathy_sasl_auth_wlm_async (ctx
->channel
,
259 case EMPATHY_SASL_MECHANISM_GOOGLE
:
260 empathy_sasl_auth_google_async (ctx
->channel
,
261 ctx
->username
, access_token
,
265 case EMPATHY_SASL_MECHANISM_PASSWORD
:
266 empathy_sasl_auth_password_async (ctx
->channel
,
272 g_assert_not_reached ();
275 g_variant_unref (params
);
279 identity_query_info_cb (SignonIdentity
*identity
,
280 const SignonIdentityInfo
*info
,
284 AuthContext
*ctx
= user_data
;
288 DEBUG ("Error querying info from identity: %s", error
->message
);
289 auth_context_done (ctx
);
293 ctx
->username
= g_strdup (signon_identity_info_get_username (info
));
295 signon_auth_session_process_async (ctx
->session
,
296 ag_auth_data_get_login_parameters (ctx
->auth_data
, NULL
),
297 ag_auth_data_get_mechanism (ctx
->auth_data
),
304 set_account_password_cb (GObject
*source
,
305 GAsyncResult
*result
,
308 TpAccount
*tp_account
= (TpAccount
*) source
;
309 AuthContext
*ctx
= user_data
;
310 AuthContext
*new_ctx
;
311 GError
*error
= NULL
;
313 if (!tpaw_keyring_set_account_password_finish (tp_account
, result
, &error
))
315 DEBUG ("Failed to set empty password on UOA account: %s", error
->message
);
316 auth_context_done (ctx
);
320 new_ctx
= auth_context_new (ctx
->channel
, ctx
->service
);
321 auth_context_free (ctx
);
323 if (new_ctx
->session
!= NULL
)
325 /* The trick worked! */
326 request_password (new_ctx
);
330 DEBUG ("Still can't get a signon session, even after setting empty pwd");
331 auth_context_done (new_ctx
);
335 empathy_uoa_auth_handler_start (EmpathyUoaAuthHandler
*self
,
337 TpAccount
*tp_account
)
339 const GValue
*id_value
;
343 AgAccountService
*service
;
346 g_return_if_fail (TP_IS_CHANNEL (channel
));
347 g_return_if_fail (TP_IS_ACCOUNT (tp_account
));
348 g_return_if_fail (empathy_uoa_auth_handler_supports (self
, channel
,
351 DEBUG ("Start UOA auth for account: %s",
352 tp_proxy_get_object_path (tp_account
));
354 id_value
= tp_account_get_storage_identifier (tp_account
);
355 id
= g_value_get_uint (id_value
);
357 account
= ag_manager_get_account (self
->priv
->manager
, id
);
359 l
= ag_account_list_services_by_type (account
, TPAW_UOA_SERVICE_TYPE
);
362 DEBUG ("Couldn't find IM service for AgAccountId %u", id
);
363 g_object_unref (account
);
364 tp_channel_close_async (channel
, NULL
, NULL
);
368 /* Assume there is only one IM service */
369 service
= ag_account_service_new (account
, l
->data
);
370 ag_service_list_free (l
);
371 g_object_unref (account
);
373 ctx
= auth_context_new (channel
, service
);
374 if (ctx
->session
== NULL
)
376 /* This (usually?) means we never stored credentials for this account.
377 * To ask user to type his password SSO needs a SignonIdentity bound to
378 * our account. Let's store an empty password. */
379 DEBUG ("Couldn't create a signon session");
380 tpaw_keyring_set_account_password_async (tp_account
, "", FALSE
,
381 set_account_password_cb
, ctx
);
385 /* All is fine! Query UOA for more info */
386 signon_identity_query_info (ctx
->identity
,
387 identity_query_info_cb
, ctx
);
390 g_object_unref (service
);
394 empathy_uoa_auth_handler_supports (EmpathyUoaAuthHandler
*self
,
398 const gchar
*provider
;
399 EmpathySaslMechanism mech
;
401 g_return_val_if_fail (TP_IS_CHANNEL (channel
), FALSE
);
402 g_return_val_if_fail (TP_IS_ACCOUNT (account
), FALSE
);
404 provider
= tp_account_get_storage_provider (account
);
406 if (tp_strdiff (provider
, EMPATHY_UOA_PROVIDER
))
409 mech
= empathy_sasl_channel_select_mechanism (channel
);
410 return mech
== EMPATHY_SASL_MECHANISM_FACEBOOK
||
411 mech
== EMPATHY_SASL_MECHANISM_WLM
||
412 mech
== EMPATHY_SASL_MECHANISM_GOOGLE
||
413 mech
== EMPATHY_SASL_MECHANISM_PASSWORD
;