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
23 #include <libaccounts-glib/ag-account.h>
24 #include <libaccounts-glib/ag-account-service.h>
25 #include <libaccounts-glib/ag-auth-data.h>
26 #include <libaccounts-glib/ag-manager.h>
27 #include <libaccounts-glib/ag-service.h>
29 #include <libsignon-glib/signon-identity.h>
30 #include <libsignon-glib/signon-auth-session.h>
32 #define DEBUG_FLAG EMPATHY_DEBUG_SASL
33 #include "empathy-debug.h"
34 #include "empathy-keyring.h"
35 #include "empathy-utils.h"
36 #include "empathy-uoa-auth-handler.h"
37 #include "empathy-uoa-utils.h"
38 #include "empathy-sasl-mechanisms.h"
40 struct _EmpathyUoaAuthHandlerPriv
45 G_DEFINE_TYPE (EmpathyUoaAuthHandler, empathy_uoa_auth_handler, G_TYPE_OBJECT);
48 empathy_uoa_auth_handler_init (EmpathyUoaAuthHandler *self)
50 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
51 EMPATHY_TYPE_UOA_AUTH_HANDLER, EmpathyUoaAuthHandlerPriv);
53 self->priv->manager = empathy_uoa_manager_dup ();
57 empathy_uoa_auth_handler_dispose (GObject *object)
59 EmpathyUoaAuthHandler *self = (EmpathyUoaAuthHandler *) object;
61 tp_clear_object (&self->priv->manager);
63 G_OBJECT_CLASS (empathy_uoa_auth_handler_parent_class)->dispose (object);
67 empathy_uoa_auth_handler_class_init (EmpathyUoaAuthHandlerClass *klass)
69 GObjectClass *oclass = G_OBJECT_CLASS (klass);
71 oclass->dispose = empathy_uoa_auth_handler_dispose;
73 g_type_class_add_private (klass, sizeof (EmpathyUoaAuthHandlerPriv));
76 EmpathyUoaAuthHandler *
77 empathy_uoa_auth_handler_new (void)
79 return g_object_new (EMPATHY_TYPE_UOA_AUTH_HANDLER, NULL);
85 AgAccountService *service;
86 AgAuthData *auth_data;
87 SignonIdentity *identity;
88 SignonAuthSession *session;
94 auth_context_new (TpChannel *channel,
95 AgAccountService *service)
100 ctx = g_slice_new0 (AuthContext);
101 ctx->channel = g_object_ref (channel);
102 ctx->service = g_object_ref (service);
104 ctx->auth_data = ag_account_service_get_auth_data (service);
105 if (ctx->auth_data == NULL)
108 cred_id = ag_auth_data_get_credentials_id (ctx->auth_data);
112 ctx->identity = signon_identity_new_from_db (cred_id);
113 if (ctx->identity == NULL)
116 ctx->session = signon_identity_create_session (ctx->identity,
117 ag_auth_data_get_method (ctx->auth_data), NULL);
118 if (ctx->session == NULL)
126 auth_context_free (AuthContext *ctx)
128 g_clear_object (&ctx->channel);
129 g_clear_object (&ctx->service);
130 tp_clear_pointer (&ctx->auth_data, ag_auth_data_unref);
131 g_clear_object (&ctx->session);
132 g_clear_object (&ctx->identity);
133 g_free (ctx->username);
135 g_slice_free (AuthContext, ctx);
139 auth_context_done (AuthContext *ctx)
141 tp_channel_close_async (ctx->channel, NULL, NULL);
142 auth_context_free (ctx);
146 request_password_session_process_cb (SignonAuthSession *session,
147 GHashTable *session_data,
151 AuthContext *ctx = user_data;
155 DEBUG ("Error processing the session to request user's attention: %s",
159 auth_context_done (ctx);
163 request_password (AuthContext *ctx)
165 GHashTable *extra_params;
167 DEBUG ("Invalid credentials, request user action");
169 /* Inform SSO that the access token (or password) didn't work and it should
170 * ask user to re-grant access (or retype password). */
171 extra_params = tp_asv_new (
172 SIGNON_SESSION_DATA_UI_POLICY, G_TYPE_INT,
173 SIGNON_POLICY_REQUEST_PASSWORD,
176 ag_auth_data_insert_parameters (ctx->auth_data, extra_params);
178 signon_auth_session_process (ctx->session,
179 ag_auth_data_get_parameters (ctx->auth_data),
180 ag_auth_data_get_mechanism (ctx->auth_data),
181 request_password_session_process_cb, ctx);
183 g_hash_table_unref (extra_params);
187 auth_cb (GObject *source,
188 GAsyncResult *result,
191 TpChannel *channel = (TpChannel *) source;
192 AuthContext *ctx = user_data;
193 GError *error = NULL;
195 if (!empathy_sasl_auth_finish (channel, result, &error))
197 DEBUG ("SASL Mechanism error: %s", error->message);
198 g_clear_error (&error);
200 request_password (ctx);
204 DEBUG ("Auth on %s suceeded", tp_proxy_get_object_path (channel));
205 auth_context_done (ctx);
210 session_process_cb (SignonAuthSession *session,
211 GHashTable *session_data,
215 AuthContext *ctx = user_data;
216 const gchar *access_token;
217 const gchar *client_id;
221 DEBUG ("Error processing the session: %s", error->message);
222 auth_context_done (ctx);
226 access_token = tp_asv_get_string (session_data, "AccessToken");
227 client_id = tp_asv_get_string (ag_auth_data_get_parameters (ctx->auth_data),
230 switch (empathy_sasl_channel_select_mechanism (ctx->channel))
232 case EMPATHY_SASL_MECHANISM_FACEBOOK:
233 empathy_sasl_auth_facebook_async (ctx->channel,
234 client_id, access_token,
238 case EMPATHY_SASL_MECHANISM_WLM:
239 empathy_sasl_auth_wlm_async (ctx->channel,
244 case EMPATHY_SASL_MECHANISM_GOOGLE:
245 empathy_sasl_auth_google_async (ctx->channel,
246 ctx->username, access_token,
250 case EMPATHY_SASL_MECHANISM_PASSWORD:
251 empathy_sasl_auth_password_async (ctx->channel,
252 tp_asv_get_string (session_data, "Secret"),
257 g_assert_not_reached ();
262 identity_query_info_cb (SignonIdentity *identity,
263 const SignonIdentityInfo *info,
267 AuthContext *ctx = user_data;
271 DEBUG ("Error querying info from identity: %s", error->message);
272 auth_context_done (ctx);
276 ctx->username = g_strdup (signon_identity_info_get_username (info));
278 signon_auth_session_process (ctx->session,
279 ag_auth_data_get_parameters (ctx->auth_data),
280 ag_auth_data_get_mechanism (ctx->auth_data),
286 set_account_password_cb (GObject *source,
287 GAsyncResult *result,
290 TpAccount *tp_account = (TpAccount *) source;
291 AuthContext *ctx = user_data;
292 AuthContext *new_ctx;
293 GError *error = NULL;
295 if (!empathy_keyring_set_account_password_finish (tp_account, result, &error))
297 DEBUG ("Failed to set empty password on UOA account: %s", error->message);
298 auth_context_done (ctx);
302 new_ctx = auth_context_new (ctx->channel, ctx->service);
303 auth_context_free (ctx);
305 if (new_ctx->session != NULL)
307 /* The trick worked! */
308 request_password (new_ctx);
312 DEBUG ("Still can't get a signon session, even after setting empty pwd");
313 auth_context_done (new_ctx);
317 empathy_uoa_auth_handler_start (EmpathyUoaAuthHandler *self,
319 TpAccount *tp_account)
321 const GValue *id_value;
325 AgAccountService *service;
328 g_return_if_fail (TP_IS_CHANNEL (channel));
329 g_return_if_fail (TP_IS_ACCOUNT (tp_account));
330 g_return_if_fail (empathy_uoa_auth_handler_supports (self, channel,
333 DEBUG ("Start UOA auth for account: %s",
334 tp_proxy_get_object_path (tp_account));
336 id_value = tp_account_get_storage_identifier (tp_account);
337 id = g_value_get_uint (id_value);
339 account = ag_manager_get_account (self->priv->manager, id);
341 l = ag_account_list_services_by_type (account, EMPATHY_UOA_SERVICE_TYPE);
344 DEBUG ("Couldn't find IM service for AgAccountId %u", id);
345 g_object_unref (account);
346 tp_channel_close_async (channel, NULL, NULL);
350 /* Assume there is only one IM service */
351 service = ag_account_service_new (account, l->data);
352 ag_service_list_free (l);
353 g_object_unref (account);
355 ctx = auth_context_new (channel, service);
356 if (ctx->session == NULL)
358 /* This (usually?) means we never stored credentials for this account.
359 * To ask user to type his password SSO needs a SignonIdentity bound to
360 * our account. Let's store an empty password. */
361 DEBUG ("Couldn't create a signon session");
362 empathy_keyring_set_account_password_async (tp_account, "", FALSE,
363 set_account_password_cb, ctx);
367 /* All is fine! Query UOA for more info */
368 signon_identity_query_info (ctx->identity,
369 identity_query_info_cb, ctx);
372 g_object_unref (service);
376 empathy_uoa_auth_handler_supports (EmpathyUoaAuthHandler *self,
380 const gchar *provider;
381 EmpathySaslMechanism mech;
383 g_return_val_if_fail (TP_IS_CHANNEL (channel), FALSE);
384 g_return_val_if_fail (TP_IS_ACCOUNT (account), FALSE);
386 provider = tp_account_get_storage_provider (account);
388 if (tp_strdiff (provider, EMPATHY_UOA_PROVIDER))
391 mech = empathy_sasl_channel_select_mechanism (channel);
392 return mech == EMPATHY_SASL_MECHANISM_FACEBOOK ||
393 mech == EMPATHY_SASL_MECHANISM_WLM ||
394 mech == EMPATHY_SASL_MECHANISM_GOOGLE ||
395 mech == EMPATHY_SASL_MECHANISM_PASSWORD;