2 * empathy-sasl-mechanisms.h - Header for SASL authentication mechanisms
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 <libsoup/soup.h>
25 #define DEBUG_FLAG EMPATHY_DEBUG_SASL
26 #include "empathy-debug.h"
27 #include "empathy-utils.h"
28 #include "empathy-sasl-mechanisms.h"
30 #define MECH_FACEBOOK "X-FACEBOOK-PLATFORM"
31 #define MECH_WLM "X-MESSENGER-OAUTH2"
32 #define MECH_GOOGLE "X-OAUTH2"
33 #define MECH_PASSWORD "X-TELEPATHY-PASSWORD"
37 EmpathySaslMechanism id;
41 static SupportedMech supported_mechanisms[] = {
42 { EMPATHY_SASL_MECHANISM_FACEBOOK, MECH_FACEBOOK },
43 { EMPATHY_SASL_MECHANISM_WLM, MECH_WLM },
44 { EMPATHY_SASL_MECHANISM_GOOGLE, MECH_GOOGLE },
46 /* Must be the last one, otherwise empathy_sasl_channel_select_mechanism()
47 * will prefer password over web auth for servers supporting both. */
48 { EMPATHY_SASL_MECHANISM_PASSWORD, MECH_PASSWORD }
52 generic_cb (TpChannel *proxy,
57 GSimpleAsyncResult *result = user_data;
61 g_simple_async_result_set_from_error (result, error);
62 g_simple_async_result_complete (result);
67 sasl_status_changed_cb (TpChannel *channel,
69 const gchar *dbus_error,
74 GSimpleAsyncResult *result = user_data;
78 case TP_SASL_STATUS_SERVER_SUCCEEDED:
79 tp_cli_channel_interface_sasl_authentication_call_accept_sasl (channel,
80 -1, generic_cb, g_object_ref (result), g_object_unref, NULL);
83 case TP_SASL_STATUS_SERVER_FAILED:
84 case TP_SASL_STATUS_CLIENT_FAILED:
88 tp_proxy_dbus_error_to_gerror (channel, dbus_error,
89 tp_asv_get_string (details, "debug-message"), &error);
91 DEBUG ("SASL failed: %s", error->message);
93 g_simple_async_result_take_error (result, error);
94 g_simple_async_result_complete (result);
98 case TP_SASL_STATUS_SUCCEEDED:
99 DEBUG ("SASL succeeded");
101 g_simple_async_result_complete (result);
109 static GSimpleAsyncResult *
110 empathy_sasl_auth_common_async (TpChannel *channel,
111 GAsyncReadyCallback callback,
114 GSimpleAsyncResult *result;
115 GError *error = NULL;
117 g_return_val_if_fail (TP_IS_CHANNEL (channel), NULL);
118 g_return_val_if_fail (tp_proxy_has_interface_by_id (channel,
119 TP_IFACE_QUARK_CHANNEL_INTERFACE_SASL_AUTHENTICATION), NULL);
121 result = g_simple_async_result_new ((GObject *) channel,
122 callback, user_data, empathy_sasl_auth_common_async);
124 tp_cli_channel_interface_sasl_authentication_connect_to_sasl_status_changed (
125 channel, sasl_status_changed_cb,
126 g_object_ref (result), g_object_unref, NULL, &error);
127 g_assert_no_error (error);
140 facebook_data_free (FacebookData *data)
142 g_object_unref (data->channel);
143 g_free (data->client_id);
144 g_free (data->access_token);
145 g_slice_free (FacebookData, data);
149 facebook_new_challenge_cb (TpChannel *channel,
150 const GArray *challenge,
152 GObject *weak_object)
154 GSimpleAsyncResult *result = user_data;
159 GArray *response_array;
161 DEBUG ("new challenge: %s", challenge->data);
163 data = g_simple_async_result_get_op_res_gpointer (result);
165 h = soup_form_decode (challenge->data);
167 /* See https://developers.facebook.com/docs/chat/#platauth */
168 params = g_hash_table_new (g_str_hash, g_str_equal);
169 g_hash_table_insert (params, "method", g_hash_table_lookup (h, "method"));
170 g_hash_table_insert (params, "nonce", g_hash_table_lookup (h, "nonce"));
171 g_hash_table_insert (params, "access_token", data->access_token);
172 g_hash_table_insert (params, "api_key", data->client_id);
173 g_hash_table_insert (params, "call_id", "0");
174 g_hash_table_insert (params, "v", "1.0");
176 response = soup_form_encode_hash (params);
177 DEBUG ("Response: %s", response);
179 response_array = g_array_new (FALSE, FALSE, sizeof (gchar));
180 g_array_append_vals (response_array, response, strlen (response));
182 tp_cli_channel_interface_sasl_authentication_call_respond (data->channel, -1,
183 response_array, generic_cb, g_object_ref (result), g_object_unref, NULL);
185 g_hash_table_unref (h);
186 g_hash_table_unref (params);
188 g_array_unref (response_array);
192 empathy_sasl_auth_facebook_async (TpChannel *channel,
193 const gchar *client_id,
194 const gchar *access_token,
195 GAsyncReadyCallback callback,
198 GSimpleAsyncResult *result;
200 GError *error = NULL;
202 result = empathy_sasl_auth_common_async (channel, callback, user_data);
204 g_return_if_fail (result != NULL);
205 g_return_if_fail (empathy_sasl_channel_supports_mechanism (channel,
207 g_return_if_fail (!tp_str_empty (client_id));
208 g_return_if_fail (!tp_str_empty (access_token));
210 DEBUG ("Start %s mechanism", MECH_FACEBOOK);
212 data = g_slice_new0 (FacebookData);
213 data->channel = g_object_ref (channel);
214 data->client_id = g_strdup (client_id);
215 data->access_token = g_strdup (access_token);
217 g_simple_async_result_set_op_res_gpointer (result, data,
218 (GDestroyNotify) facebook_data_free);
220 tp_cli_channel_interface_sasl_authentication_connect_to_new_challenge (
221 channel, facebook_new_challenge_cb,
222 g_object_ref (result), g_object_unref,
224 g_assert_no_error (error);
226 tp_cli_channel_interface_sasl_authentication_call_start_mechanism (
227 channel, -1, MECH_FACEBOOK, generic_cb,
228 g_object_ref (result), g_object_unref, NULL);
230 g_object_unref (result);
234 empathy_sasl_auth_wlm_async (TpChannel *channel,
235 const gchar *access_token,
236 GAsyncReadyCallback callback,
239 GSimpleAsyncResult *result;
240 guchar *token_decoded;
241 gsize token_decoded_len;
242 GArray *token_decoded_array;
244 result = empathy_sasl_auth_common_async (channel, callback, user_data);
246 g_return_if_fail (result != NULL);
247 g_return_if_fail (empathy_sasl_channel_supports_mechanism (channel,
249 g_return_if_fail (!tp_str_empty (access_token));
251 DEBUG ("Start %s mechanism", MECH_WLM);
253 /* Wocky will base64 encode, but token actually already is base64, so we
254 * decode now and it will be re-encoded. */
255 token_decoded = g_base64_decode (access_token, &token_decoded_len);
256 token_decoded_array = g_array_new (FALSE, FALSE, sizeof (guchar));
257 g_array_append_vals (token_decoded_array, token_decoded, token_decoded_len);
259 tp_cli_channel_interface_sasl_authentication_call_start_mechanism_with_data (
260 channel, -1, MECH_WLM, token_decoded_array,
261 generic_cb, g_object_ref (result), g_object_unref, NULL);
263 g_array_unref (token_decoded_array);
264 g_free (token_decoded);
265 g_object_unref (result);
269 empathy_sasl_auth_google_async (TpChannel *channel,
270 const gchar *username,
271 const gchar *access_token,
272 GAsyncReadyCallback callback,
275 GSimpleAsyncResult *result;
278 result = empathy_sasl_auth_common_async (channel, callback, user_data);
280 g_return_if_fail (result != NULL);
281 g_return_if_fail (empathy_sasl_channel_supports_mechanism (channel,
283 g_return_if_fail (!tp_str_empty (username));
284 g_return_if_fail (!tp_str_empty (access_token));
286 DEBUG ("Start %s mechanism", MECH_GOOGLE);
288 credential = g_array_sized_new (FALSE, FALSE, sizeof (gchar),
289 strlen (access_token) + strlen (username) + 2);
291 g_array_append_val (credential, "\0");
292 g_array_append_vals (credential, username, strlen (username));
293 g_array_append_val (credential, "\0");
294 g_array_append_vals (credential, access_token, strlen (access_token));
296 tp_cli_channel_interface_sasl_authentication_call_start_mechanism_with_data (
297 channel, -1, MECH_GOOGLE, credential,
298 generic_cb, g_object_ref (result), g_object_unref, NULL);
300 g_array_unref (credential);
301 g_object_unref (result);
305 empathy_sasl_auth_password_async (TpChannel *channel,
306 const gchar *password,
307 GAsyncReadyCallback callback,
310 GSimpleAsyncResult *result;
311 GArray *password_array;
313 result = empathy_sasl_auth_common_async (channel, callback, user_data);
315 g_return_if_fail (result != NULL);
316 g_return_if_fail (empathy_sasl_channel_supports_mechanism (channel,
318 g_return_if_fail (!tp_str_empty (password));
320 DEBUG ("Start %s mechanism", MECH_PASSWORD);
322 password_array = g_array_sized_new (FALSE, FALSE, sizeof (gchar),
324 g_array_append_vals (password_array, password, strlen (password));
326 tp_cli_channel_interface_sasl_authentication_call_start_mechanism_with_data (
327 channel, -1, MECH_PASSWORD, password_array,
328 generic_cb, g_object_ref (result), g_object_unref, NULL);
330 g_array_unref (password_array);
331 g_object_unref (result);
335 empathy_sasl_auth_finish (TpChannel *channel,
336 GAsyncResult *result,
339 empathy_implement_finish_void (channel, empathy_sasl_auth_common_async);
343 empathy_sasl_channel_supports_mechanism (TpChannel *channel,
344 const gchar *mechanism)
347 GStrv available_mechanisms;
350 props = tp_channel_dup_immutable_properties (channel);
352 g_variant_lookup (props,
353 TP_PROP_CHANNEL_INTERFACE_SASL_AUTHENTICATION_AVAILABLE_MECHANISMS,
354 "^as", &available_mechanisms);
356 result = tp_strv_contains ((const gchar * const *) available_mechanisms,
359 g_variant_unref (props);
360 g_strfreev (available_mechanisms);
365 empathy_sasl_channel_select_mechanism (TpChannel *channel)
369 for (i = 0; i < G_N_ELEMENTS (supported_mechanisms); i++)
371 if (empathy_sasl_channel_supports_mechanism (channel,
372 supported_mechanisms[i].name))
373 return supported_mechanisms[i].id;
376 return EMPATHY_SASL_MECHANISM_UNSUPPORTED;