diff --git a/configure.ac b/configure.ac
index 20de382..4ed8d9e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -39,7 +39,7 @@ KEYRING_REQUIRED=2.26.0
 LIBCANBERRA_GTK_REQUIRED=0.25
 LIBNOTIFY_REQUIRED=0.7.0
 TELEPATHY_FARSIGHT_REQUIRED=0.0.14
-TELEPATHY_GLIB_REQUIRED=0.13.1.1
+TELEPATHY_GLIB_REQUIRED=0.13.7
 TELEPATHY_LOGGER=0.1.5
 
 # Optional deps
diff --git a/data/Empathy.Auth.client b/data/Empathy.Auth.client
index 4c6adc0..1fbf103 100644
--- a/data/Empathy.Auth.client
+++ b/data/Empathy.Auth.client
@@ -4,3 +4,7 @@ Interfaces=org.freedesktop.Telepathy.Client.Handler
 [org.freedesktop.Telepathy.Client.Handler.HandlerChannelFilter 0]
 org.freedesktop.Telepathy.Channel.ChannelType s=org.freedesktop.Telepathy.Channel.Type.ServerTLSConnection
 org.freedesktop.Telepathy.Channel.TargetHandleType u=0
+
+[org.freedesktop.Telepathy.Client.Handler.HandlerChannelFilter 1]
+org.freedesktop.Telepathy.Channel.ChannelType s=org.freedesktop.Telepathy.Channel.Type.ServerAuthentication
+org.freedesktop.Telepathy.Channel.Type.ServerAuthentication s=org.freedesktop.Telepathy.Channel.Interface.SASLAuthentication
diff --git a/libempathy-gtk/Makefile.am b/libempathy-gtk/Makefile.am
index 7e35929..72e489c 100644
--- a/libempathy-gtk/Makefile.am
+++ b/libempathy-gtk/Makefile.am
@@ -68,6 +68,7 @@ libempathy_gtk_handwritten_source =            	\
 	empathy-new-message-dialog.c		\
 	empathy-new-call-dialog.c		\
 	empathy-notify-manager.c		\
+	empathy-password-dialog.c 		\
 	empathy-persona-store.c			\
 	empathy-persona-view.c			\
 	empathy-presence-chooser.c		\
@@ -130,6 +131,7 @@ libempathy_gtk_headers =			\
 	empathy-new-message-dialog.h		\
 	empathy-new-call-dialog.h		\
 	empathy-notify-manager.h		\
+	empathy-password-dialog.h		\
 	empathy-persona-store.h			\
 	empathy-persona-view.h			\
 	empathy-presence-chooser.h		\
diff --git a/libempathy-gtk/empathy-password-dialog.c b/libempathy-gtk/empathy-password-dialog.c
new file mode 100644
index 0000000..179e68c
--- /dev/null
+++ b/libempathy-gtk/empathy-password-dialog.c
@@ -0,0 +1,327 @@
+/*
+ * empathy-password-dialog.c - Source for EmpathyPasswordDialog
+ * Copyright (C) 2010 Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <config.h>
+
+#include "empathy-password-dialog.h"
+
+#include <glib/gi18n-lib.h>
+
+#define DEBUG_FLAG EMPATHY_DEBUG_SASL
+#include <libempathy/empathy-debug.h>
+#include <libempathy/empathy-utils.h>
+
+G_DEFINE_TYPE (EmpathyPasswordDialog, empathy_password_dialog,
+    GTK_TYPE_MESSAGE_DIALOG)
+
+#define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyPasswordDialog);
+
+enum {
+  PROP_HANDLER = 1,
+
+  LAST_PROPERTY,
+};
+
+typedef struct {
+  EmpathyServerSASLHandler *handler;
+
+  GtkWidget *entry;
+  GtkWidget *ticky;
+
+  gboolean grabbing;
+
+  gboolean dispose_run;
+} EmpathyPasswordDialogPriv;
+
+static void
+empathy_password_dialog_get_property (GObject *object,
+    guint property_id,
+    GValue *value,
+    GParamSpec *pspec)
+{
+  EmpathyPasswordDialogPriv *priv = GET_PRIV (object);
+
+  switch (property_id)
+    {
+    case PROP_HANDLER:
+      g_value_set_object (value, priv->handler);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+static void
+empathy_password_dialog_set_property (GObject *object,
+    guint property_id,
+    const GValue *value,
+    GParamSpec *pspec)
+{
+  EmpathyPasswordDialogPriv *priv = GET_PRIV (object);
+
+  switch (property_id)
+    {
+    case PROP_HANDLER:
+      g_assert (priv->handler == NULL); /* construct only */
+      priv->handler = g_value_dup_object (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+static void
+empathy_password_dialog_dispose (GObject *object)
+{
+  EmpathyPasswordDialogPriv *priv = GET_PRIV (object);
+
+  if (priv->dispose_run)
+    return;
+
+  priv->dispose_run = TRUE;
+
+  tp_clear_object (&priv->handler);
+
+  G_OBJECT_CLASS (empathy_password_dialog_parent_class)->dispose (object);
+}
+
+static void
+password_dialog_response_cb (GtkDialog *dialog,
+    gint response,
+    gpointer user_data)
+{
+  EmpathyPasswordDialogPriv *priv = GET_PRIV (user_data);
+
+  if (response == GTK_RESPONSE_OK)
+    {
+      empathy_server_sasl_handler_provide_password (priv->handler,
+          gtk_entry_get_text (GTK_ENTRY (priv->entry)),
+          gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->ticky)));
+    }
+  else
+    {
+      empathy_server_sasl_handler_cancel (priv->handler);
+    }
+
+  gtk_widget_destroy (GTK_WIDGET (dialog));
+}
+
+static void
+clear_icon_released_cb (GtkEntry *entry,
+    GtkEntryIconPosition icon_pos,
+    GdkEvent *event,
+    gpointer user_data)
+{
+  gtk_entry_set_text (entry, "");
+}
+
+static void
+password_entry_changed_cb (GtkEditable *entry,
+    gpointer user_data)
+{
+  const gchar *str;
+
+  str = gtk_entry_get_text (GTK_ENTRY (entry));
+
+  gtk_entry_set_icon_sensitive (GTK_ENTRY (entry),
+      GTK_ENTRY_ICON_SECONDARY, !EMP_STR_EMPTY (str));
+}
+
+static gboolean
+password_dialog_grab_keyboard (GtkWidget *widget,
+    GdkEvent *event,
+    gpointer user_data)
+{
+  EmpathyPasswordDialogPriv *priv = GET_PRIV (user_data);
+
+  if (!priv->grabbing)
+    {
+      GdkGrabStatus status = gdk_keyboard_grab (gtk_widget_get_window (widget),
+          FALSE, gdk_event_get_time (event));
+
+      if (status != GDK_GRAB_SUCCESS)
+        DEBUG ("Could not grab keyboard; grab status was %u", status);
+      else
+        priv->grabbing = TRUE;
+    }
+
+  return FALSE;
+}
+
+static gboolean
+password_dialog_ungrab_keyboard (GtkWidget *widget,
+    GdkEvent *event,
+    gpointer user_data)
+{
+  EmpathyPasswordDialogPriv *priv = GET_PRIV (user_data);
+
+  if (priv->grabbing)
+    {
+      gdk_keyboard_ungrab (gdk_event_get_time (event));
+      priv->grabbing = FALSE;
+    }
+
+  return FALSE;
+}
+
+static gboolean
+password_dialog_window_state_changed (GtkWidget *widget,
+    GdkEventWindowState *event,
+    gpointer data)
+{
+  GdkWindowState state = gdk_window_get_state (gtk_widget_get_window (widget));
+
+  if (state & GDK_WINDOW_STATE_WITHDRAWN
+      || state & GDK_WINDOW_STATE_ICONIFIED
+      || state & GDK_WINDOW_STATE_FULLSCREEN
+      || state & GDK_WINDOW_STATE_MAXIMIZED)
+    {
+      password_dialog_ungrab_keyboard (widget, (GdkEvent*) event, data);
+    }
+  else
+    {
+      password_dialog_grab_keyboard (widget, (GdkEvent*) event, data);
+    }
+
+  return FALSE;
+}
+
+static void
+empathy_password_dialog_constructed (GObject *object)
+{
+  EmpathyPasswordDialog *dialog;
+  EmpathyPasswordDialogPriv *priv;
+  TpAccount *account;
+  GtkWidget *icon;
+  GtkBox *box;
+  gchar *text;
+
+  dialog = EMPATHY_PASSWORD_DIALOG (object);
+  priv = GET_PRIV (dialog);
+
+  g_assert (priv->handler != NULL);
+
+  priv->grabbing = FALSE;
+
+  account = empathy_server_sasl_handler_get_account (priv->handler);
+
+  /* dialog */
+  gtk_dialog_add_buttons (GTK_DIALOG (dialog),
+      GTK_STOCK_OK, GTK_RESPONSE_OK,
+      GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+      NULL);
+
+  text = g_strdup_printf (_("Enter your password for account\n<b>%s</b>"),
+      tp_account_get_display_name (account));
+  gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG (dialog), text);
+  g_free (text);
+
+  gtk_window_set_icon_name (GTK_WINDOW (dialog),
+      GTK_STOCK_DIALOG_AUTHENTICATION);
+
+  box = GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog)));
+
+  /* dialog icon */
+  icon = gtk_image_new_from_icon_name (tp_account_get_icon_name (account),
+      GTK_ICON_SIZE_DIALOG);
+  gtk_message_dialog_set_image (GTK_MESSAGE_DIALOG (dialog), icon);
+  gtk_widget_show (icon);
+
+  /* entry */
+  priv->entry = gtk_entry_new ();
+  gtk_entry_set_visibility (GTK_ENTRY (priv->entry), FALSE);
+
+  /* entry clear icon */
+  gtk_entry_set_icon_from_stock (GTK_ENTRY (priv->entry),
+      GTK_ENTRY_ICON_SECONDARY, GTK_STOCK_CLEAR);
+  gtk_entry_set_icon_sensitive (GTK_ENTRY (priv->entry),
+      GTK_ENTRY_ICON_SECONDARY, FALSE);
+
+  g_signal_connect (priv->entry, "icon-release",
+      G_CALLBACK (clear_icon_released_cb), NULL);
+  g_signal_connect (priv->entry, "changed",
+      G_CALLBACK (password_entry_changed_cb), NULL);
+
+  gtk_box_pack_start (box, priv->entry, FALSE, FALSE, 0);
+  gtk_widget_show (priv->entry);
+
+  /* remember password ticky box */
+  priv->ticky = gtk_check_button_new_with_label (_("Remember password"));
+
+  /* Don't add this to the dialog yet because we haven't set up
+   * everything in the UI properly yet and the MC transition isn't
+   * ready etc. so we'll just force it to never remember a
+   * password. */
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->ticky), FALSE);
+  /*
+  gtk_box_pack_start (box, priv->ticky, FALSE, FALSE, 0);
+  gtk_widget_show (priv->ticky);
+  */
+
+  g_signal_connect (dialog, "response",
+      G_CALLBACK (password_dialog_response_cb), dialog);
+  g_signal_connect (dialog, "window-state-event",
+      G_CALLBACK (password_dialog_window_state_changed), dialog);
+  g_signal_connect (dialog, "map-event",
+      G_CALLBACK (password_dialog_grab_keyboard), dialog);
+  g_signal_connect (dialog, "unmap-event",
+      G_CALLBACK (password_dialog_ungrab_keyboard), dialog);
+
+  gtk_widget_grab_focus (priv->entry);
+
+  gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER_ALWAYS);
+}
+
+static void
+empathy_password_dialog_init (EmpathyPasswordDialog *self)
+{
+  self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+      EMPATHY_TYPE_PASSWORD_DIALOG, EmpathyPasswordDialogPriv);
+}
+
+static void
+empathy_password_dialog_class_init (EmpathyPasswordDialogClass *klass)
+{
+  GParamSpec *pspec;
+  GObjectClass *oclass = G_OBJECT_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (EmpathyPasswordDialogPriv));
+
+  oclass->set_property = empathy_password_dialog_set_property;
+  oclass->get_property = empathy_password_dialog_get_property;
+  oclass->dispose = empathy_password_dialog_dispose;
+  oclass->constructed = empathy_password_dialog_constructed;
+
+  pspec = g_param_spec_object ("handler", "The EmpathyServerSASLHandler",
+      "The EmpathyServerSASLHandler to be used.",
+      EMPATHY_TYPE_SERVER_SASL_HANDLER,
+      G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (oclass, PROP_HANDLER, pspec);
+}
+
+GtkWidget *
+empathy_password_dialog_new (EmpathyServerSASLHandler *handler)
+{
+  g_assert (EMPATHY_IS_SERVER_SASL_HANDLER (handler));
+
+  return g_object_new (EMPATHY_TYPE_PASSWORD_DIALOG,
+      "handler", handler, NULL);
+}
diff --git a/libempathy-gtk/empathy-password-dialog.h b/libempathy-gtk/empathy-password-dialog.h
new file mode 100644
index 0000000..566322d
--- /dev/null
+++ b/libempathy-gtk/empathy-password-dialog.h
@@ -0,0 +1,66 @@
+/*
+ * empathy-password-dialog.h - Header for EmpathyPasswordDialog
+ * Copyright (C) 2010 Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __EMPATHY_PASSWORD_DIALOG_H__
+#define __EMPATHY_PASSWORD_DIALOG_H__
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#include <libempathy/empathy-server-sasl-handler.h>
+
+#include <extensions/extensions.h>
+
+G_BEGIN_DECLS
+
+typedef struct _EmpathyPasswordDialog EmpathyPasswordDialog;
+typedef struct _EmpathyPasswordDialogClass EmpathyPasswordDialogClass;
+
+struct _EmpathyPasswordDialogClass {
+    GtkMessageDialogClass parent_class;
+};
+
+struct _EmpathyPasswordDialog {
+    GtkMessageDialog parent;
+    gpointer priv;
+};
+
+GType empathy_password_dialog_get_type (void);
+
+#define EMPATHY_TYPE_PASSWORD_DIALOG \
+  (empathy_password_dialog_get_type ())
+#define EMPATHY_PASSWORD_DIALOG(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj), EMPATHY_TYPE_PASSWORD_DIALOG, \
+    EmpathyPasswordDialog))
+#define EMPATHY_PASSWORD_DIALOG_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass), EMPATHY_TYPE_PASSWORD_DIALOG, \
+  EmpathyPasswordDialogClass))
+#define EMPATHY_IS_PASSWORD_DIALOG(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj), EMPATHY_TYPE_PASSWORD_DIALOG))
+#define EMPATHY_IS_PASSWORD_DIALOG_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass), EMPATHY_TYPE_PASSWORD_DIALOG))
+#define EMPATHY_PASSWORD_DIALOG_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), EMPATHY_TYPE_PASSWORD_DIALOG, \
+  EmpathyPasswordDialogClass))
+
+GtkWidget * empathy_password_dialog_new (EmpathyServerSASLHandler *handler);
+
+G_END_DECLS
+
+#endif /* #ifndef __EMPATHY_PASSWORD_DIALOG_H__*/
diff --git a/libempathy/Makefile.am b/libempathy/Makefile.am
index 9b0dcfb..ae9e2b5 100644
--- a/libempathy/Makefile.am
+++ b/libempathy/Makefile.am
@@ -47,8 +47,10 @@ libempathy_headers =				\
 	empathy-irc-network-manager.h		\
 	empathy-irc-network.h			\
 	empathy-irc-server.h			\
+	empathy-keyring.h 			\
 	empathy-location.h			\
 	empathy-message.h			\
+	empathy-server-sasl-handler.h		\
 	empathy-server-tls-handler.h		\
 	empathy-status-presets.h		\
 	empathy-time.h				\
@@ -86,7 +88,9 @@ libempathy_la_SOURCES =					\
 	empathy-irc-network-manager.c			\
 	empathy-irc-network.c				\
 	empathy-irc-server.c				\
+	empathy-keyring.c				\
 	empathy-message.c				\
+	empathy-server-sasl-handler.c			\
 	empathy-server-tls-handler.c			\
 	empathy-status-presets.c			\
 	empathy-time.c					\
diff --git a/libempathy/empathy-auth-factory.c b/libempathy/empathy-auth-factory.c
index fa2b7dc..578b6d6 100644
--- a/libempathy/empathy-auth-factory.c
+++ b/libempathy/empathy-auth-factory.c
@@ -26,6 +26,7 @@
 
 #define DEBUG_FLAG EMPATHY_DEBUG_TLS
 #include "empathy-debug.h"
+#include "empathy-server-sasl-handler.h"
 #include "empathy-server-tls-handler.h"
 #include "empathy-utils.h"
 
@@ -36,11 +37,17 @@ G_DEFINE_TYPE (EmpathyAuthFactory, empathy_auth_factory, G_TYPE_OBJECT);
 typedef struct {
   TpBaseClient *handler;
 
+  /* Keep a ref here so the auth client doesn't have to mess with
+   * refs. It will be cleared when the channel (and so the handler)
+   * gets invalidated. */
+  EmpathyServerSASLHandler *sasl_handler;
+
   gboolean dispose_run;
 } EmpathyAuthFactoryPriv;
 
 enum {
   NEW_SERVER_TLS_HANDLER,
+  NEW_SERVER_SASL_HANDLER,
   LAST_SIGNAL,
 };
 
@@ -111,6 +118,52 @@ server_tls_handler_ready_cb (GObject *source,
 }
 
 static void
+sasl_handler_invalidated_cb (EmpathyServerSASLHandler *handler,
+    gpointer user_data)
+{
+  EmpathyAuthFactory *self = user_data;
+  EmpathyAuthFactoryPriv *priv = GET_PRIV (self);
+
+  DEBUG ("SASL handler is invalidated, unref it");
+
+  tp_clear_object (&priv->sasl_handler);
+}
+
+static void
+server_sasl_handler_ready_cb (GObject *source,
+    GAsyncResult *res,
+    gpointer user_data)
+{
+  EmpathyAuthFactoryPriv *priv;
+  GError *error = NULL;
+  HandlerContextData *data = user_data;
+
+  priv = GET_PRIV (data->self);
+  priv->sasl_handler = empathy_server_sasl_handler_new_finish (res, &error);
+
+  if (error != NULL)
+    {
+      DEBUG ("Failed to create a server SASL handler; error %s",
+          error->message);
+      tp_handle_channels_context_fail (data->context, error);
+
+      g_error_free (error);
+    }
+  else
+    {
+      tp_handle_channels_context_accept (data->context);
+
+      g_signal_connect (priv->sasl_handler, "invalidated",
+          G_CALLBACK (sasl_handler_invalidated_cb), data->self);
+
+      g_signal_emit (data->self, signals[NEW_SERVER_SASL_HANDLER], 0,
+          priv->sasl_handler);
+    }
+
+  handler_context_data_free (data);
+}
+
+static void
 handle_channels_cb (TpSimpleHandler *handler,
     TpAccount *account,
     TpConnection *connection,
@@ -124,18 +177,22 @@ handle_channels_cb (TpSimpleHandler *handler,
   const GError *dbus_error;
   GError *error = NULL;
   EmpathyAuthFactory *self = user_data;
+  EmpathyAuthFactoryPriv *priv = GET_PRIV (self);
   HandlerContextData *data;
+  GHashTable *props;
+  const gchar * const *available_mechanisms;
 
-  DEBUG ("Handle TLS carrier channels.");
+  DEBUG ("Handle TLS or SASL carrier channels.");
 
-  /* there can't be more than one ServerTLSConnection channels
-   * at the same time, for the same connection/account.
+  /* there can't be more than one ServerTLSConnection or
+   * ServerAuthentication channels at the same time, for the same
+   * connection/account.
    */
   if (g_list_length (channels) != 1)
     {
       g_set_error_literal (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
-          "Can't handle more than one ServerTLSConnection channel "
-          "for the same connection.");
+          "Can't handle more than one ServerTLSConnection or ServerAuthentication "
+          "channel for the same connection.");
 
       goto error;
     }
@@ -143,11 +200,38 @@ handle_channels_cb (TpSimpleHandler *handler,
   channel = channels->data;
 
   if (tp_channel_get_channel_type_id (channel) !=
-      EMP_IFACE_QUARK_CHANNEL_TYPE_SERVER_TLS_CONNECTION)
+      EMP_IFACE_QUARK_CHANNEL_TYPE_SERVER_TLS_CONNECTION
+      && tp_channel_get_channel_type_id (channel) !=
+      TP_IFACE_QUARK_CHANNEL_TYPE_SERVER_AUTHENTICATION)
     {
       g_set_error (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
-          "Can only handle ServerTLSConnection channels, this was a %s "
-          "channel", tp_channel_get_channel_type (channel));
+          "Can only handle ServerTLSConnection or ServerAuthentication channels, "
+          "this was a %s channel", tp_channel_get_channel_type (channel));
+
+      goto error;
+    }
+
+  if (tp_channel_get_channel_type_id (channel) ==
+      TP_IFACE_QUARK_CHANNEL_TYPE_SERVER_AUTHENTICATION
+      && priv->sasl_handler != NULL)
+    {
+      g_set_error_literal (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
+          "Can't handle more than one ServerAuthentication channel at one time");
+
+      goto error;
+    }
+
+  props = tp_channel_borrow_immutable_properties (channel);
+  available_mechanisms = tp_asv_get_boxed (props,
+      TP_PROP_CHANNEL_INTERFACE_SASL_AUTHENTICATION_AVAILABLE_MECHANISMS,
+      G_TYPE_STRV);
+
+  if (tp_channel_get_channel_type_id (channel) ==
+      TP_IFACE_QUARK_CHANNEL_TYPE_SERVER_AUTHENTICATION
+      && !tp_strv_contains (available_mechanisms, "X-TELEPATHY-PASSWORD"))
+    {
+      g_set_error_literal (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
+          "Only the X-TELEPATHY-PASSWORD SASL mechanism is supported");
 
       goto error;
     }
@@ -160,12 +244,22 @@ handle_channels_cb (TpSimpleHandler *handler,
       goto error;
     }
 
-  /* create a handler */
   data = handler_context_data_new (self, context);
   tp_handle_channels_context_delay (context);
-  empathy_server_tls_handler_new_async (channel, server_tls_handler_ready_cb,
-      data);
 
+  /* create a handler */
+  if (tp_channel_get_channel_type_id (channel) ==
+      EMP_IFACE_QUARK_CHANNEL_TYPE_SERVER_TLS_CONNECTION)
+    {
+      empathy_server_tls_handler_new_async (channel, server_tls_handler_ready_cb,
+          data);
+    }
+  else if (tp_channel_get_channel_type_id (channel) ==
+      TP_IFACE_QUARK_CHANNEL_TYPE_SERVER_AUTHENTICATION)
+    {
+      empathy_server_sasl_handler_new_async (account, channel,
+          server_sasl_handler_ready_cb, data);
+    }
   return;
 
  error:
@@ -218,12 +312,23 @@ empathy_auth_factory_init (EmpathyAuthFactory *self)
       FALSE, handle_channels_cb, self, NULL);
 
   tp_base_client_take_handler_filter (priv->handler, tp_asv_new (
+          /* ChannelType */
           TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING,
           EMP_IFACE_CHANNEL_TYPE_SERVER_TLS_CONNECTION,
+          /* AuthenticationMethod */
           TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT,
           TP_HANDLE_TYPE_NONE, NULL));
 
-  g_object_unref (bus);
+  tp_base_client_take_handler_filter (priv->handler, tp_asv_new (
+          /* ChannelType */
+          TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING,
+          TP_IFACE_CHANNEL_TYPE_SERVER_AUTHENTICATION,
+          /* AuthenticationMethod */
+          TP_PROP_CHANNEL_TYPE_SERVER_AUTHENTICATION_AUTHENTICATION_METHOD,
+          G_TYPE_STRING, TP_IFACE_CHANNEL_INTERFACE_SASL_AUTHENTICATION,
+          NULL));
+
+ g_object_unref (bus);
 }
 
 static void
@@ -237,6 +342,7 @@ empathy_auth_factory_dispose (GObject *object)
   priv->dispose_run = TRUE;
 
   tp_clear_object (&priv->handler);
+  tp_clear_object (&priv->sasl_handler);
 
   G_OBJECT_CLASS (empathy_auth_factory_parent_class)->dispose (object);
 }
@@ -259,6 +365,15 @@ empathy_auth_factory_class_init (EmpathyAuthFactoryClass *klass)
       g_cclosure_marshal_VOID__OBJECT,
       G_TYPE_NONE,
       1, EMPATHY_TYPE_SERVER_TLS_HANDLER);
+
+  signals[NEW_SERVER_SASL_HANDLER] =
+    g_signal_new ("new-server-sasl-handler",
+      G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, 0,
+      NULL, NULL,
+      g_cclosure_marshal_VOID__OBJECT,
+      G_TYPE_NONE,
+      1, EMPATHY_TYPE_SERVER_SASL_HANDLER);
 }
 
 EmpathyAuthFactory *
diff --git a/libempathy/empathy-debug.c b/libempathy/empathy-debug.c
index dd507bc..4f624f8 100644
--- a/libempathy/empathy-debug.c
+++ b/libempathy/empathy-debug.c
@@ -53,6 +53,7 @@ static GDebugKey keys[] = {
   { "Tests", EMPATHY_DEBUG_TESTS },
   { "Voip", EMPATHY_DEBUG_VOIP },
   { "Tls", EMPATHY_DEBUG_TLS },
+  { "Sasl", EMPATHY_DEBUG_SASL },
   { 0, }
 };
 
diff --git a/libempathy/empathy-debug.h b/libempathy/empathy-debug.h
index ece3af7..44e1977 100644
--- a/libempathy/empathy-debug.h
+++ b/libempathy/empathy-debug.h
@@ -47,6 +47,7 @@ typedef enum
   EMPATHY_DEBUG_TESTS = 1 << 12,
   EMPATHY_DEBUG_VOIP = 1 << 13,
   EMPATHY_DEBUG_TLS = 1 << 14,
+  EMPATHY_DEBUG_SASL = 1 << 15,
 } EmpathyDebugFlags;
 
 gboolean empathy_debug_flag_is_set (EmpathyDebugFlags flag);
diff --git a/libempathy/empathy-keyring.c b/libempathy/empathy-keyring.c
new file mode 100644
index 0000000..2ce53d3
--- /dev/null
+++ b/libempathy/empathy-keyring.c
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2010 Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "config.h"
+
+#include "empathy-keyring.h"
+
+#include <string.h>
+
+#include <gnome-keyring.h>
+
+#define DEBUG_FLAG EMPATHY_DEBUG_OTHER
+#include "empathy-debug.h"
+
+static GnomeKeyringPasswordSchema keyring_schema =
+  { GNOME_KEYRING_ITEM_GENERIC_SECRET,
+    { { "account-id", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
+      { "param-name", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
+      { NULL } } };
+
+static void
+find_items_cb (GnomeKeyringResult result,
+    GList *list,
+    gpointer user_data)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+
+  if (result != GNOME_KEYRING_RESULT_OK)
+    {
+      GError *error = g_error_new_literal (TP_ERROR,
+          TP_ERROR_DOES_NOT_EXIST,
+          gnome_keyring_result_to_message (result));
+      g_simple_async_result_set_from_error (simple, error);
+      g_clear_error (&error);
+    }
+
+  if (g_list_length (list) == 1)
+    {
+      GnomeKeyringFound *found = list->data;
+
+      DEBUG ("Got secret");
+
+      g_simple_async_result_set_op_res_gpointer (simple, found->secret, NULL);
+    }
+
+  g_simple_async_result_complete (simple);
+  g_object_unref (simple);
+}
+
+void
+empathy_keyring_get_password_async (TpAccount *account,
+    GAsyncReadyCallback callback,
+    gpointer user_data)
+{
+  GSimpleAsyncResult *simple;
+  GnomeKeyringAttributeList *match;
+  const gchar *account_id;
+
+  g_return_if_fail (TP_IS_ACCOUNT (account));
+  g_return_if_fail (callback != NULL);
+
+  simple = g_simple_async_result_new (G_OBJECT (account), callback,
+      user_data, empathy_keyring_get_password_async);
+
+  account_id = tp_proxy_get_object_path (account) +
+    strlen (TP_ACCOUNT_OBJECT_PATH_BASE);
+
+  DEBUG ("Trying to get password for: %s", account_id);
+
+  match = gnome_keyring_attribute_list_new ();
+  gnome_keyring_attribute_list_append_string (match, "account-id",
+      account_id);
+  gnome_keyring_attribute_list_append_string (match, "param-name", "password");
+
+  gnome_keyring_find_items (GNOME_KEYRING_ITEM_GENERIC_SECRET,
+      match, find_items_cb, simple, NULL);
+
+  gnome_keyring_attribute_list_free (match);
+}
+
+const gchar *
+empathy_keyring_get_password_finish (TpAccount *account,
+    GAsyncResult *result,
+    GError **error)
+{
+  GSimpleAsyncResult *simple;
+
+  g_return_val_if_fail (TP_IS_ACCOUNT (account), NULL);
+  g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), NULL);
+
+  simple = G_SIMPLE_ASYNC_RESULT (result);
+
+  if (g_simple_async_result_propagate_error (simple, error))
+    return NULL;
+
+  g_return_val_if_fail (g_simple_async_result_is_valid (result,
+          G_OBJECT (account), empathy_keyring_get_password_async), NULL);
+
+  return g_simple_async_result_get_op_res_gpointer (simple);
+}
+
+static void
+store_password_cb (GnomeKeyringResult result,
+    gpointer user_data)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+
+  if (result != GNOME_KEYRING_RESULT_OK)
+    {
+      GError *error = g_error_new_literal (TP_ERROR,
+          TP_ERROR_DOES_NOT_EXIST,
+          gnome_keyring_result_to_message (result));
+      g_simple_async_result_set_from_error (simple, error);
+      g_clear_error (&error);
+    }
+
+  g_simple_async_result_complete (simple);
+  g_object_unref (simple);
+}
+
+void
+empathy_keyring_set_password_async (TpAccount *account,
+    const gchar *password,
+    GAsyncReadyCallback callback,
+    gpointer user_data)
+{
+  GSimpleAsyncResult *simple;
+  const gchar *account_id;
+  gchar *name;
+
+  g_return_if_fail (TP_IS_ACCOUNT (account));
+  g_return_if_fail (password != NULL);
+
+  simple = g_simple_async_result_new (G_OBJECT (account), callback,
+      user_data, empathy_keyring_set_password_async);
+
+  account_id = tp_proxy_get_object_path (account) +
+    strlen (TP_ACCOUNT_OBJECT_PATH_BASE);
+
+  DEBUG ("Remembering password for %s", account_id);
+
+  name = g_strdup_printf ("account: %s; param: password", account_id);
+
+  gnome_keyring_store_password (&keyring_schema, NULL, name, password,
+      store_password_cb, simple, NULL,
+      "account-id", account_id,
+      "param-name", "password",
+      NULL);
+
+  g_free (name);
+}
+
+gboolean
+empathy_keyring_set_password_finish (TpAccount *account,
+    GAsyncResult *result,
+    GError **error)
+{
+  GSimpleAsyncResult *simple;
+
+  g_return_val_if_fail (TP_IS_ACCOUNT (account), FALSE);
+  g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), FALSE);
+
+  simple = G_SIMPLE_ASYNC_RESULT (result);
+
+  if (g_simple_async_result_propagate_error (simple, error))
+    return FALSE;
+
+  g_return_val_if_fail (g_simple_async_result_is_valid (result,
+          G_OBJECT (account), empathy_keyring_set_password_async), FALSE);
+
+  return TRUE;
+}
+
+static void
+item_delete_cb (GnomeKeyringResult result,
+    gpointer user_data)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+
+  if (result != GNOME_KEYRING_RESULT_OK)
+    {
+      GError *error = g_error_new_literal (TP_ERROR,
+          TP_ERROR_DOES_NOT_EXIST,
+          gnome_keyring_result_to_message (result));
+      g_simple_async_result_set_from_error (simple, error);
+      g_clear_error (&error);
+    }
+
+  g_simple_async_result_complete (simple);
+  g_object_unref (simple);
+}
+
+static void
+find_item_to_delete_cb (GnomeKeyringResult result,
+    GList *list,
+    gpointer user_data)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+  GnomeKeyringFound *found;
+
+  if (result != GNOME_KEYRING_RESULT_OK || g_list_length (list) != 1)
+    {
+      GError *error = g_error_new_literal (TP_ERROR,
+          TP_ERROR_DOES_NOT_EXIST,
+          gnome_keyring_result_to_message (result));
+      g_simple_async_result_set_from_error (simple, error);
+      g_clear_error (&error);
+
+      g_simple_async_result_complete (simple);
+      g_object_unref (simple);
+      return;
+    }
+
+  found = list->data;
+
+  gnome_keyring_item_delete (NULL, found->item_id, item_delete_cb,
+      simple, NULL);
+}
+
+void
+empathy_keyring_delete_password_async (TpAccount *account,
+    GAsyncReadyCallback callback,
+    gpointer user_data)
+{
+  GSimpleAsyncResult *simple;
+  GnomeKeyringAttributeList *match;
+  const gchar *account_id;
+
+  g_return_if_fail (TP_IS_ACCOUNT (account));
+
+  simple = g_simple_async_result_new (G_OBJECT (account), callback,
+      user_data, empathy_keyring_delete_password_async);
+
+  account_id = tp_proxy_get_object_path (account) +
+    strlen (TP_ACCOUNT_OBJECT_PATH_BASE);
+
+  match = gnome_keyring_attribute_list_new ();
+  gnome_keyring_attribute_list_append_string (match, "account-id",
+      account_id);
+  gnome_keyring_attribute_list_append_string (match, "param-name", "password");
+
+  gnome_keyring_find_items (GNOME_KEYRING_ITEM_GENERIC_SECRET,
+      match, find_item_to_delete_cb, simple, NULL);
+
+  gnome_keyring_attribute_list_free (match);
+}
+
+gboolean
+empathy_keyring_delete_password_finish (TpAccount *account,
+    GAsyncResult *result,
+    GError **error)
+{
+  GSimpleAsyncResult *simple;
+
+  g_return_val_if_fail (TP_IS_ACCOUNT (account), FALSE);
+  g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), FALSE);
+
+  simple = G_SIMPLE_ASYNC_RESULT (result);
+
+  if (g_simple_async_result_propagate_error (simple, error))
+    return FALSE;
+
+  g_return_val_if_fail (g_simple_async_result_is_valid (result,
+          G_OBJECT (account), empathy_keyring_delete_password_async), FALSE);
+
+  return TRUE;
+}
+
diff --git a/libempathy/empathy-keyring.h b/libempathy/empathy-keyring.h
new file mode 100644
index 0000000..000f987
--- /dev/null
+++ b/libempathy/empathy-keyring.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2010 Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __EMPATHY_KEYRING_H__
+#define __EMPATHY_KEYRING_H__
+
+#include <gio/gio.h>
+
+#include <telepathy-glib/account.h>
+
+G_BEGIN_DECLS
+
+void empathy_keyring_get_password_async (TpAccount *account,
+    GAsyncReadyCallback callback, gpointer user_data);
+
+const gchar * empathy_keyring_get_password_finish (TpAccount *account,
+    GAsyncResult *result, GError **error);
+
+void empathy_keyring_set_password_async (TpAccount *account,
+    const gchar *password, GAsyncReadyCallback callback,
+    gpointer user_data);
+
+gboolean empathy_keyring_set_password_finish (TpAccount *account,
+    GAsyncResult *result, GError **error);
+
+void empathy_keyring_delete_password_async (TpAccount *account,
+    GAsyncReadyCallback callback, gpointer user_data);
+
+gboolean empathy_keyring_delete_password_finish (TpAccount *account,
+    GAsyncResult *result, GError **error);
+
+G_END_DECLS
+
+#endif /* __EMPATHY_KEYRING_H__ */
+
diff --git a/libempathy/empathy-server-sasl-handler.c b/libempathy/empathy-server-sasl-handler.c
new file mode 100644
index 0000000..4b5a441
--- /dev/null
+++ b/libempathy/empathy-server-sasl-handler.c
@@ -0,0 +1,462 @@
+/*
+ * empathy-server-sasl-handler.c - Source for EmpathyServerSASLHandler
+ * Copyright (C) 2010 Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "empathy-server-sasl-handler.h"
+
+#include <telepathy-glib/util.h>
+
+#define DEBUG_FLAG EMPATHY_DEBUG_SASL
+#include "empathy-debug.h"
+#include "empathy-utils.h"
+#include "empathy-keyring.h"
+
+enum {
+  PROP_CHANNEL = 1,
+  PROP_ACCOUNT,
+  LAST_PROPERTY,
+};
+
+/* signal enum */
+enum {
+  INVALIDATED,
+  LAST_SIGNAL,
+};
+
+static guint signals[LAST_SIGNAL] = {0};
+
+typedef struct {
+  TpChannel *channel;
+  TpAccount *account;
+
+  GSimpleAsyncResult *result;
+
+  gchar *password;
+
+  GSimpleAsyncResult *async_init_res;
+} EmpathyServerSASLHandlerPriv;
+
+static void async_initable_iface_init (GAsyncInitableIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (EmpathyServerSASLHandler, empathy_server_sasl_handler,
+    G_TYPE_OBJECT,
+    G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, async_initable_iface_init));
+
+#define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyServerSASLHandler);
+
+static const gchar *sasl_statuses[] = {
+  "not started",
+  "in progress",
+  "server succeeded",
+  "client accepted",
+  "succeeded",
+  "server failed",
+  "client failed",
+};
+
+static void
+sasl_status_changed_cb (TpChannel *channel,
+    TpSASLStatus status,
+    const gchar *error,
+    GHashTable *details,
+    gpointer user_data,
+    GObject *weak_object)
+{
+  EmpathyServerSASLHandlerPriv *priv = GET_PRIV (weak_object);
+
+  /* buh boh */
+  if (status >= G_N_ELEMENTS (sasl_statuses))
+    {
+      DEBUG ("SASL status changed to unknown status");
+      return;
+    }
+
+  DEBUG ("SASL status changed to '%s'", sasl_statuses[status]);
+
+  if (status == TP_SASL_STATUS_SERVER_SUCCEEDED)
+    {
+      tp_cli_channel_interface_sasl_authentication_call_accept_sasl (
+          priv->channel, -1, NULL, NULL, NULL, NULL);
+
+      tp_cli_channel_call_close (priv->channel, -1,
+          NULL, NULL, NULL, NULL);
+    }
+}
+
+static gboolean
+empathy_server_sasl_handler_give_password (gpointer data)
+{
+  EmpathyServerSASLHandler *self = data;
+  EmpathyServerSASLHandlerPriv *priv = GET_PRIV (self);
+
+  empathy_server_sasl_handler_provide_password (self,
+      priv->password, FALSE);
+
+  return FALSE;
+}
+
+static void
+empathy_server_sasl_handler_get_password_async_cb (GObject *source,
+    GAsyncResult *result,
+    gpointer user_data)
+{
+  EmpathyServerSASLHandlerPriv *priv;
+  const gchar *password;
+  GError *error = NULL;
+
+  priv = GET_PRIV (user_data);
+
+  password = empathy_keyring_get_password_finish (TP_ACCOUNT (source),
+      result, &error);
+
+  if (password != NULL)
+    {
+      priv->password = g_strdup (password);
+
+      /* Do this in an idle so the async result will get there
+       * first. */
+      g_idle_add (empathy_server_sasl_handler_give_password, user_data);
+    }
+
+  g_simple_async_result_complete (priv->async_init_res);
+  tp_clear_object (&priv->async_init_res);
+}
+
+static void
+empathy_server_sasl_handler_init_async (GAsyncInitable *initable,
+    gint io_priority,
+    GCancellable *cancellable,
+    GAsyncReadyCallback callback,
+    gpointer user_data)
+{
+  EmpathyServerSASLHandler *self = EMPATHY_SERVER_SASL_HANDLER (initable);
+  EmpathyServerSASLHandlerPriv *priv = GET_PRIV (self);
+
+  g_assert (priv->account != NULL);
+
+  priv->async_init_res = g_simple_async_result_new (G_OBJECT (self),
+      callback, user_data, empathy_server_sasl_handler_new_async);
+
+  empathy_keyring_get_password_async (priv->account,
+      empathy_server_sasl_handler_get_password_async_cb, self);
+}
+
+static gboolean
+empathy_server_sasl_handler_init_finish (GAsyncInitable *initable,
+    GAsyncResult *res,
+    GError **error)
+{
+  if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res),
+          error))
+    return FALSE;
+
+  return TRUE;
+}
+
+static void
+async_initable_iface_init (GAsyncInitableIface *iface)
+{
+  iface->init_async = empathy_server_sasl_handler_init_async;
+  iface->init_finish = empathy_server_sasl_handler_init_finish;
+}
+
+static void
+channel_invalidated_cb (TpProxy *proxy,
+    guint domain,
+    gint code,
+    gchar *message,
+    EmpathyServerSASLHandler *self)
+{
+  g_signal_emit (self, signals[INVALIDATED], 0);
+}
+
+static void
+empathy_server_sasl_handler_constructed (GObject *object)
+{
+  EmpathyServerSASLHandlerPriv *priv;
+  GError *error = NULL;
+
+  priv = GET_PRIV (object);
+
+  tp_cli_channel_interface_sasl_authentication_connect_to_sasl_status_changed (priv->channel,
+      sasl_status_changed_cb, NULL, NULL, object, &error);
+
+  if (error != NULL)
+    {
+      DEBUG ("Failed to connect to SASLStatusChanged: %s", error->message);
+      g_clear_error (&error);
+    }
+
+  tp_g_signal_connect_object (priv->channel, "invalidated",
+      G_CALLBACK (channel_invalidated_cb), object, 0);
+}
+
+static void
+empathy_server_sasl_handler_get_property (GObject *object,
+    guint property_id,
+    GValue *value,
+    GParamSpec *pspec)
+{
+  EmpathyServerSASLHandlerPriv *priv = GET_PRIV (object);
+
+  switch (property_id)
+    {
+    case PROP_CHANNEL:
+      g_value_set_object (value, priv->channel);
+      break;
+    case PROP_ACCOUNT:
+      g_value_set_object (value, priv->account);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+static void
+empathy_server_sasl_handler_set_property (GObject *object,
+    guint property_id,
+    const GValue *value,
+    GParamSpec *pspec)
+{
+  EmpathyServerSASLHandlerPriv *priv = GET_PRIV (object);
+
+  switch (property_id)
+    {
+    case PROP_CHANNEL:
+      priv->channel = g_value_dup_object (value);
+      break;
+    case PROP_ACCOUNT:
+      priv->account = g_value_dup_object (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+static void
+empathy_server_sasl_handler_dispose (GObject *object)
+{
+  EmpathyServerSASLHandlerPriv *priv = GET_PRIV (object);
+
+  DEBUG ("%p", object);
+
+  tp_clear_object (&priv->channel);
+  tp_clear_object (&priv->account);
+
+  G_OBJECT_CLASS (empathy_server_sasl_handler_parent_class)->dispose (object);
+}
+
+static void
+empathy_server_sasl_handler_finalize (GObject *object)
+{
+  EmpathyServerSASLHandlerPriv *priv = GET_PRIV (object);
+
+  DEBUG ("%p", object);
+
+  tp_clear_pointer (&priv->password, g_free);
+
+  G_OBJECT_CLASS (empathy_server_sasl_handler_parent_class)->finalize (object);
+}
+
+static void
+empathy_server_sasl_handler_class_init (EmpathyServerSASLHandlerClass *klass)
+{
+  GObjectClass *oclass = G_OBJECT_CLASS (klass);
+  GParamSpec *pspec;
+
+  oclass->constructed = empathy_server_sasl_handler_constructed;
+  oclass->get_property = empathy_server_sasl_handler_get_property;
+  oclass->set_property = empathy_server_sasl_handler_set_property;
+  oclass->dispose = empathy_server_sasl_handler_dispose;
+  oclass->dispose = empathy_server_sasl_handler_finalize;
+
+  g_type_class_add_private (klass, sizeof (EmpathyServerSASLHandlerPriv));
+
+  pspec = g_param_spec_object ("channel", "The TpChannel",
+      "The TpChannel this handler is supposed to handle.",
+      TP_TYPE_CHANNEL,
+      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (oclass, PROP_CHANNEL, pspec);
+
+  pspec = g_param_spec_object ("account", "The TpAccount",
+      "The TpAccount this channel belongs to.",
+      TP_TYPE_ACCOUNT,
+      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (oclass, PROP_ACCOUNT, pspec);
+
+  signals[INVALIDATED] = g_signal_new ("invalidated",
+      G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, 0,
+      NULL, NULL,
+      g_cclosure_marshal_VOID__VOID,
+      G_TYPE_NONE, 0);
+}
+
+static void
+empathy_server_sasl_handler_init (EmpathyServerSASLHandler *self)
+{
+  self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+      EMPATHY_TYPE_SERVER_SASL_HANDLER, EmpathyServerSASLHandlerPriv);
+}
+
+EmpathyServerSASLHandler *
+empathy_server_sasl_handler_new_finish (GAsyncResult *result,
+    GError **error)
+{
+  GObject *object, *source_object;
+
+  source_object = g_async_result_get_source_object (result);
+
+  object = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object),
+      result, error);
+  g_object_unref (source_object);
+
+  if (object != NULL)
+    return EMPATHY_SERVER_SASL_HANDLER (object);
+  else
+    return NULL;
+}
+
+void
+empathy_server_sasl_handler_new_async (TpAccount *account,
+    TpChannel *channel,
+    GAsyncReadyCallback callback,
+    gpointer user_data)
+{
+  g_return_if_fail (TP_IS_ACCOUNT (account));
+  g_return_if_fail (TP_IS_CHANNEL (channel));
+  g_return_if_fail (callback != NULL);
+
+  g_async_initable_new_async (EMPATHY_TYPE_SERVER_SASL_HANDLER,
+      G_PRIORITY_DEFAULT, NULL, callback, user_data,
+      "account", account,
+      "channel", channel,
+      NULL);
+}
+
+static void
+start_mechanism_with_data_cb (TpChannel *proxy,
+    const GError *error,
+    gpointer user_data,
+    GObject *weak_object)
+{
+  if (error != NULL)
+    {
+      DEBUG ("Failed to start mechanism: %s", error->message);
+      return;
+    }
+
+  DEBUG ("Started mechanism successfully");
+}
+
+static void
+empathy_server_sasl_handler_set_password_cb (GObject *source,
+    GAsyncResult *result,
+    gpointer user_data)
+{
+  GError *error = NULL;
+
+  if (!empathy_keyring_set_password_finish (TP_ACCOUNT (source), result,
+          &error))
+    {
+      DEBUG ("Failed to set password: %s", error->message);
+      g_clear_error (&error);
+    }
+  else
+    {
+      DEBUG ("Password set successfully.");
+    }
+}
+
+void
+empathy_server_sasl_handler_provide_password (
+    EmpathyServerSASLHandler *handler,
+    const gchar *password,
+    gboolean remember)
+{
+  EmpathyServerSASLHandlerPriv *priv;
+  GArray *array;
+
+  g_return_if_fail (EMPATHY_IS_SERVER_SASL_HANDLER (handler));
+
+  priv = GET_PRIV (handler);
+
+  array = g_array_sized_new (TRUE, FALSE,
+      sizeof (gchar), strlen (password));
+
+  g_array_append_vals (array, password, strlen (password));
+
+  DEBUG ("Calling StartMechanismWithData with our password");
+
+  tp_cli_channel_interface_sasl_authentication_call_start_mechanism_with_data (
+      priv->channel, -1, "X-TELEPATHY-PASSWORD", array, start_mechanism_with_data_cb,
+      NULL, NULL, G_OBJECT (handler));
+
+  g_array_unref (array);
+
+  DEBUG ("%sremembering the password", remember ? "" : "not ");
+
+  if (remember)
+    {
+      empathy_keyring_set_password_async (priv->account, password,
+          empathy_server_sasl_handler_set_password_cb, NULL);
+    }
+}
+
+void
+empathy_server_sasl_handler_cancel (EmpathyServerSASLHandler *handler)
+{
+  EmpathyServerSASLHandlerPriv *priv;
+
+  g_return_if_fail (EMPATHY_IS_SERVER_SASL_HANDLER (handler));
+
+  priv = GET_PRIV (handler);
+
+  DEBUG ("Cancelling SASL mechanism...");
+
+  tp_cli_channel_interface_sasl_authentication_call_abort_sasl (
+      priv->channel, -1, TP_SASL_ABORT_REASON_USER_ABORT,
+      "User cancelled the authentication",
+      NULL, NULL, NULL, NULL);
+}
+
+TpAccount *
+empathy_server_sasl_handler_get_account (EmpathyServerSASLHandler *handler)
+{
+  EmpathyServerSASLHandlerPriv *priv;
+
+  g_return_val_if_fail (EMPATHY_IS_SERVER_SASL_HANDLER (handler), NULL);
+
+  priv = GET_PRIV (handler);
+
+  return priv->account;
+}
+
+gboolean
+empathy_server_sasl_handler_has_password (EmpathyServerSASLHandler *handler)
+{
+  EmpathyServerSASLHandlerPriv *priv;
+
+  g_return_val_if_fail (EMPATHY_IS_SERVER_SASL_HANDLER (handler), FALSE);
+
+  priv = GET_PRIV (handler);
+
+  return (priv->password != NULL);
+}
diff --git a/libempathy/empathy-server-sasl-handler.h b/libempathy/empathy-server-sasl-handler.h
new file mode 100644
index 0000000..1eedc5b
--- /dev/null
+++ b/libempathy/empathy-server-sasl-handler.h
@@ -0,0 +1,82 @@
+/*
+ * empathy-server-sasl-handler.h - Header for EmpathyServerSASLHandler
+ * Copyright (C) 2010 Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __EMPATHY_SERVER_SASL_HANDLER_H__
+#define __EMPATHY_SERVER_SASL_HANDLER_H__
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include <telepathy-glib/account.h>
+#include <telepathy-glib/channel.h>
+
+G_BEGIN_DECLS
+
+typedef struct _EmpathyServerSASLHandler EmpathyServerSASLHandler;
+typedef struct _EmpathyServerSASLHandlerClass EmpathyServerSASLHandlerClass;
+
+struct _EmpathyServerSASLHandlerClass {
+    GObjectClass parent_class;
+};
+
+struct _EmpathyServerSASLHandler {
+    GObject parent;
+    gpointer priv;
+};
+
+GType empathy_server_sasl_handler_get_type (void);
+
+#define EMPATHY_TYPE_SERVER_SASL_HANDLER \
+  (empathy_server_sasl_handler_get_type ())
+#define EMPATHY_SERVER_SASL_HANDLER(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj), EMPATHY_TYPE_SERVER_SASL_HANDLER, \
+    EmpathyServerSASLHandler))
+#define EMPATHY_SERVER_SASL_HANDLER_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass), EMPATHY_TYPE_SERVER_SASL_HANDLER, \
+  EmpathyServerSASLHandlerClass))
+#define EMPATHY_IS_SERVER_SASL_HANDLER(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj), EMPATHY_TYPE_SERVER_SASL_HANDLER))
+#define EMPATHY_IS_SERVER_SASL_HANDLER_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass), EMPATHY_TYPE_SERVER_SASL_HANDLER))
+#define EMPATHY_SERVER_SASL_HANDLER_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), EMPATHY_TYPE_SERVER_SASL_HANDLER, \
+  EmpathyServerSASLHandlerClass))
+
+void empathy_server_sasl_handler_new_async (
+    TpAccount *account, TpChannel *channel,
+    GAsyncReadyCallback callback, gpointer user_data);
+
+EmpathyServerSASLHandler * empathy_server_sasl_handler_new_finish (
+    GAsyncResult *result, GError **error);
+
+void empathy_server_sasl_handler_provide_password (
+    EmpathyServerSASLHandler *handler, const gchar *password,
+    gboolean remember);
+
+void empathy_server_sasl_handler_cancel (EmpathyServerSASLHandler *handler);
+
+TpAccount * empathy_server_sasl_handler_get_account (
+    EmpathyServerSASLHandler *handler);
+
+gboolean empathy_server_sasl_handler_has_password (
+    EmpathyServerSASLHandler *handler);
+
+G_END_DECLS
+
+#endif /* #ifndef __EMPATHY_SERVER_SASL_HANDLER_H__*/
diff --git a/src/empathy-auth-client.c b/src/empathy-auth-client.c
index 17b66a5..98a736f 100644
--- a/src/empathy-auth-client.c
+++ b/src/empathy-auth-client.c
@@ -29,9 +29,12 @@
 #define DEBUG_FLAG EMPATHY_DEBUG_TLS
 #include <libempathy/empathy-debug.h>
 #include <libempathy/empathy-auth-factory.h>
+#include <libempathy/empathy-server-sasl-handler.h>
 #include <libempathy/empathy-server-tls-handler.h>
 #include <libempathy/empathy-tls-verifier.h>
+#include <libempathy/empathy-utils.h>
 
+#include <libempathy-gtk/empathy-password-dialog.h>
 #include <libempathy-gtk/empathy-tls-dialog.h>
 #include <libempathy-gtk/empathy-ui-utils.h>
 
@@ -180,7 +183,7 @@ verifier_verify_cb (GObject *source,
 }
 
 static void
-auth_factory_new_handler_cb (EmpathyAuthFactory *factory,
+auth_factory_new_tls_handler_cb (EmpathyAuthFactory *factory,
     EmpathyServerTLSHandler *handler,
     gpointer user_data)
 {
@@ -204,6 +207,23 @@ auth_factory_new_handler_cb (EmpathyAuthFactory *factory,
   g_free (hostname);
 }
 
+static void
+auth_factory_new_sasl_handler_cb (EmpathyAuthFactory *factory,
+    EmpathyServerSASLHandler *handler,
+    gpointer user_data)
+{
+  GtkWidget *dialog;
+
+  DEBUG ("New SASL server handler received from the factory");
+
+  /* If the handler has the password it will deal with it itself. */
+  if (!empathy_server_sasl_handler_has_password (handler))
+    {
+      dialog = empathy_password_dialog_new (handler);
+      gtk_widget_show (dialog);
+    }
+}
+
 int
 main (int argc,
     char **argv)
@@ -238,7 +258,10 @@ main (int argc,
   factory = empathy_auth_factory_dup_singleton ();
 
   g_signal_connect (factory, "new-server-tls-handler",
-      G_CALLBACK (auth_factory_new_handler_cb), NULL);
+      G_CALLBACK (auth_factory_new_tls_handler_cb), NULL);
+
+  g_signal_connect (factory, "new-server-sasl-handler",
+      G_CALLBACK (auth_factory_new_sasl_handler_cb), NULL);
 
   if (!empathy_auth_factory_register (factory, &error))
     {
