diff --git a/libempathy-gtk/empathy-chat.c b/libempathy-gtk/empathy-chat.c
index ba439b4..dc332c6 100644
--- a/libempathy-gtk/empathy-chat.c
+++ b/libempathy-gtk/empathy-chat.c
@@ -92,6 +92,7 @@ typedef struct {
 	GtkWidget         *hbox_topic;
 	GtkWidget         *label_topic;
 	GtkWidget         *contact_list_view;
+	GtkWidget         *info_bar_vbox;
 } EmpathyChatPriv;
 
 typedef struct {
@@ -1421,7 +1422,9 @@ chat_input_realize_cb (GtkWidget   *widget,
 		       EmpathyChat *chat)
 {
 	DEBUG ("Setting focus to the input text view");
-	gtk_widget_grab_focus (widget);
+	if (gtk_widget_is_sensitive (widget)) {
+		gtk_widget_grab_focus (widget);
+	}
 }
 
 static void
@@ -1947,6 +1950,7 @@ chat_create_ui (EmpathyChat *chat)
 					"hbox_topic", &priv->hbox_topic,
 					"label_topic", &priv->label_topic,
 					"scrolled_window_contacts", &priv->scrolled_window_contacts,
+					"info_bar_vbox", &priv->info_bar_vbox,
 					NULL);
 	g_free (filename);
 
@@ -2309,6 +2313,162 @@ empathy_chat_get_tp_chat (EmpathyChat *chat)
 	return priv->tp_chat;
 }
 
+static void display_password_info_bar (EmpathyChat *self,
+			  gboolean retry);
+
+static void
+provide_password_cb (GObject *tp_chat,
+			  GAsyncResult *res,
+			  gpointer user_data)
+{
+	EmpathyChat *self = EMPATHY_CHAT (user_data);
+	EmpathyChatPriv *priv = GET_PRIV (self);
+	GError *error = NULL;
+
+	if (!empathy_tp_chat_provide_password_finish (EMPATHY_TP_CHAT (tp_chat), res,
+			       &error)) {
+		DEBUG ("error: %s", error->message);
+		/* FIXME: what should we do if that's another error? Close the channel?
+		 * Display the raw D-Bus error to the user isn't very useful */
+		if (g_error_matches (error, TP_ERRORS, TP_ERROR_AUTHENTICATION_FAILED))
+			display_password_info_bar (self, TRUE);
+		g_error_free (error);
+		return;
+	}
+
+	/* Room joined */
+	gtk_widget_set_sensitive (priv->hpaned, TRUE);
+	gtk_widget_grab_focus (self->input_text_view);
+}
+
+static void
+password_infobar_response_cb (GtkWidget *info_bar,
+			  gint response_id,
+			  EmpathyChat *self)
+{
+	EmpathyChatPriv *priv = GET_PRIV (self);
+	GtkWidget *entry;
+	const gchar *password;
+
+	if (response_id != GTK_RESPONSE_OK)
+		goto out;
+
+	entry = g_object_get_data (G_OBJECT (info_bar), "password-entry");
+	g_assert (entry != NULL);
+
+	password = gtk_entry_get_text (GTK_ENTRY (entry));
+
+	empathy_tp_chat_provide_password_async (priv->tp_chat, password,
+			       provide_password_cb, self);
+
+out:
+	gtk_widget_destroy (info_bar);
+}
+
+static void
+password_entry_activate_cb (GtkWidget *entry,
+			  GtkWidget *info_bar)
+{
+	gtk_info_bar_response (GTK_INFO_BAR (info_bar), GTK_RESPONSE_OK);
+}
+
+static void
+passwd_join_button_cb (GtkButton *button,
+			  GtkWidget *info_bar)
+{
+	gtk_info_bar_response (GTK_INFO_BAR (info_bar), GTK_RESPONSE_OK);
+}
+
+static void
+display_password_info_bar (EmpathyChat *self,
+			  gboolean retry)
+{
+	EmpathyChatPriv *priv = GET_PRIV (self);
+	GtkWidget *info_bar;
+	GtkWidget *content_area;
+	GtkWidget *hbox;
+	GtkWidget *image;
+	GtkWidget *label;
+	GtkWidget *entry;
+	GtkWidget *alig;
+	GtkWidget *button;
+	GtkMessageType type;
+	const gchar *msg, *button_label;
+
+	if (retry) {
+		/* Previous password was wrong */
+		type = GTK_MESSAGE_ERROR;
+		msg = _("Wrong password; please try again:");
+		button_label = _("Retry");
+	}
+	else {
+		/* First time we're trying to join */
+		type = GTK_MESSAGE_QUESTION;
+		msg = _("This room is protected by a password:");
+		button_label = _("Join");
+	}
+
+	info_bar = gtk_info_bar_new ();
+	gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), type);
+
+	content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar));
+
+	hbox = gtk_hbox_new (FALSE, 3);
+	gtk_container_add (GTK_CONTAINER (content_area), hbox);
+
+	/* Add image */
+	image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_AUTHENTICATION,
+			       GTK_ICON_SIZE_DIALOG);
+	gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
+
+	/* Add message */
+	label = gtk_label_new (msg);
+	gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+
+	/* Add password entry */
+	entry = gtk_entry_new ();
+	gtk_entry_set_visibility (GTK_ENTRY (entry), FALSE);
+	gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
+
+	g_signal_connect (entry, "activate",
+			       G_CALLBACK (password_entry_activate_cb), info_bar);
+
+	/* Focus the password entry once it's realized */
+	g_signal_connect (entry, "realize", G_CALLBACK (gtk_widget_grab_focus), NULL);
+
+	/* Add 'Join' button */
+	alig = gtk_alignment_new (0, 0.5, 0, 0);
+
+	button = gtk_button_new_with_label (button_label);
+	gtk_container_add (GTK_CONTAINER (alig), button);
+	gtk_box_pack_start (GTK_BOX (hbox), alig, FALSE, FALSE, 0);
+
+	g_signal_connect (button, "clicked", G_CALLBACK (passwd_join_button_cb),
+			       info_bar);
+
+	g_object_set_data (G_OBJECT (info_bar), "password-entry", entry);
+
+	gtk_box_pack_start (GTK_BOX (priv->info_bar_vbox), info_bar,
+			       FALSE, FALSE, 3);
+	gtk_widget_show_all (hbox);
+
+	g_signal_connect (info_bar, "response",
+			       G_CALLBACK (password_infobar_response_cb), self);
+
+	gtk_widget_show_all (info_bar);
+}
+
+static void
+chat_password_needed_changed_cb (EmpathyChat *self)
+{
+	EmpathyChatPriv *priv = GET_PRIV (self);
+
+	if (empathy_tp_chat_password_needed (priv->tp_chat)) {
+		display_password_info_bar (self, FALSE);
+		gtk_widget_set_sensitive (priv->hpaned, FALSE);
+	}
+}
+
 void
 empathy_chat_set_tp_chat (EmpathyChat   *chat,
 			  EmpathyTpChat *tp_chat)
@@ -2354,6 +2514,9 @@ empathy_chat_set_tp_chat (EmpathyChat   *chat,
 	g_signal_connect_swapped (tp_chat, "notify::remote-contact",
 				  G_CALLBACK (chat_remote_contact_changed_cb),
 				  chat);
+	g_signal_connect_swapped (tp_chat, "notify::password-needed",
+				  G_CALLBACK (chat_password_needed_changed_cb),
+				  chat);
 
 	/* Get initial value of properties */
 	properties = empathy_tp_chat_get_properties (priv->tp_chat);
@@ -2391,6 +2554,9 @@ empathy_chat_set_tp_chat (EmpathyChat   *chat,
 	 * the pending messages to be show when it's set on the object after it has
 	 * been created */
 	show_pending_messages (chat);
+
+	/* check if a password is needed */
+	chat_password_needed_changed_cb (chat);
 }
 
 TpAccount *
diff --git a/libempathy-gtk/empathy-chat.ui b/libempathy-gtk/empathy-chat.ui
index 7af4832..de82fdd 100644
--- a/libempathy-gtk/empathy-chat.ui
+++ b/libempathy-gtk/empathy-chat.ui
@@ -8,6 +8,15 @@
     <property name="orientation">vertical</property>
     <property name="spacing">6</property>
     <child>
+      <object class="GtkVBox" id="info_bar_vbox">
+        <property name="visible">True</property>
+      </object>
+      <packing>
+        <property name="expand">False</property>
+        <property name="position">2</property>
+      </packing>
+     </child>
+    <child>
       <object class="GtkHPaned" id="hpaned">
         <property name="visible">True</property>
         <property name="can_focus">True</property>
@@ -71,7 +80,7 @@
         </child>
       </object>
       <packing>
-        <property name="position">0</property>
+        <property name="position">2</property>
       </packing>
     </child>
     <child>
@@ -114,7 +123,7 @@
         <property name="expand">False</property>
         <property name="fill">False</property>
         <property name="padding">2</property>
-        <property name="position">0</property>
+        <property name="position">1</property>
       </packing>
     </child>
   </object>
diff --git a/libempathy/empathy-tp-chat.c b/libempathy/empathy-tp-chat.c
index 83faaff..9a235b7 100644
--- a/libempathy/empathy-tp-chat.c
+++ b/libempathy/empathy-tp-chat.c
@@ -55,6 +55,10 @@ typedef struct {
 	GQueue                *pending_messages_queue;
 	gboolean               had_properties_list;
 	GPtrArray             *properties;
+	TpChannelPasswordFlags password_flags;
+	/* TRUE if we fetched the password flag of the channel or if it's not needed
+	 * (channel doesn't implement the Password interface) */
+	gboolean               got_password_flags;
 	gboolean               ready;
 } EmpathyTpChatPriv;
 
@@ -64,6 +68,7 @@ enum {
 	PROP_0,
 	PROP_CHANNEL,
 	PROP_REMOTE_CONTACT,
+	PROP_PASSWORD_NEEDED,
 	PROP_READY,
 };
 
@@ -777,10 +782,21 @@ tp_chat_check_if_ready (EmpathyTpChat *chat)
 {
 	EmpathyTpChatPriv *priv = GET_PRIV (chat);
 
-	if (priv->ready || priv->user == NULL ||
-	    (priv->members == NULL && priv->remote_contact == NULL)) {
+	if (priv->ready)
+		return;
+
+	if (priv->user == NULL)
+		return;
+
+	if (!priv->got_password_flags)
+		return;
+
+	/* We need either the members (room) or the remote contact (private chat).
+	 * If the chat is protected by a password we can't get these information so
+	 * consider the chat as ready so it can be presented to the user. */
+	if (!empathy_tp_chat_password_needed (chat) && priv->members == NULL &&
+						      priv->remote_contact == NULL)
 		return;
-	}
 
 	DEBUG ("Ready!");
 
@@ -1030,6 +1046,44 @@ tp_chat_got_self_contact_cb (EmpathyTpContactFactory *factory,
 	tp_chat_check_if_ready (EMPATHY_TP_CHAT (chat));
 }
 
+static void
+password_flags_changed_cb (TpChannel *channel,
+    guint added,
+    guint removed,
+    gpointer user_data,
+    GObject *weak_object)
+{
+	EmpathyTpChat *self = EMPATHY_TP_CHAT (weak_object);
+	EmpathyTpChatPriv *priv = GET_PRIV (self);
+	gboolean was_needed, needed;
+
+	was_needed = empathy_tp_chat_password_needed (self);
+
+	priv->password_flags |= added;
+	priv->password_flags ^= removed;
+
+	needed = empathy_tp_chat_password_needed (self);
+
+	if (was_needed != needed)
+		g_object_notify (G_OBJECT (self), "password-needed");
+}
+
+static void
+got_password_flags_cb (TpChannel *proxy,
+			     guint password_flags,
+			     const GError *error,
+			     gpointer user_data,
+			     GObject *weak_object)
+{
+	EmpathyTpChat *self = EMPATHY_TP_CHAT (weak_object);
+	EmpathyTpChatPriv *priv = GET_PRIV (self);
+
+	priv->got_password_flags = TRUE;
+	priv->password_flags = password_flags;
+
+	tp_chat_check_if_ready (EMPATHY_TP_CHAT (self));
+}
+
 static GObject *
 tp_chat_constructor (GType                  type,
 		     guint                  n_props,
@@ -1100,6 +1154,22 @@ tp_chat_constructor (GType                  type,
 									       G_OBJECT (chat), NULL);
 	}
 
+	/* Check if the chat is password protected */
+	if (tp_proxy_has_interface_by_id (priv->channel,
+					  TP_IFACE_QUARK_CHANNEL_INTERFACE_PASSWORD)) {
+		priv->got_password_flags = FALSE;
+
+		tp_cli_channel_interface_password_connect_to_password_flags_changed (
+									       priv->channel, password_flags_changed_cb, chat, NULL,
+									       G_OBJECT (chat), NULL);
+
+		tp_cli_channel_interface_password_call_get_password_flags (priv->channel,
+									       -1, got_password_flags_cb, chat, NULL, chat);
+	} else {
+		/* No Password interface, so no need to fetch the password flags */
+		priv->got_password_flags = TRUE;
+	}
+
 	return chat;
 }
 
@@ -1109,6 +1179,7 @@ tp_chat_get_property (GObject    *object,
 		      GValue     *value,
 		      GParamSpec *pspec)
 {
+	EmpathyTpChat *self = EMPATHY_TP_CHAT (object);
 	EmpathyTpChatPriv *priv = GET_PRIV (object);
 
 	switch (param_id) {
@@ -1121,6 +1192,9 @@ tp_chat_get_property (GObject    *object,
 	case PROP_READY:
 		g_value_set_boolean (value, priv->ready);
 		break;
+	case PROP_PASSWORD_NEEDED:
+		g_value_set_boolean (value, empathy_tp_chat_password_needed (self));
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
 		break;
@@ -1181,6 +1255,14 @@ empathy_tp_chat_class_init (EmpathyTpChatClass *klass)
 							       FALSE,
 							       G_PARAM_READABLE));
 
+	g_object_class_install_property (object_class,
+					 PROP_PASSWORD_NEEDED,
+					 g_param_spec_boolean ("password-needed",
+							       "password needed",
+							       "TRUE if a password is needed to join the channel",
+							       FALSE,
+							       G_PARAM_READABLE));
+
 	/* Signals */
 	signals[MESSAGE_RECEIVED] =
 		g_signal_new ("message-received",
@@ -1472,3 +1554,64 @@ empathy_tp_chat_acknowledge_messages (EmpathyTpChat *chat,
 	g_list_free (msgs);
 }
 
+gboolean
+empathy_tp_chat_password_needed (EmpathyTpChat *self)
+{
+	EmpathyTpChatPriv *priv = GET_PRIV (self);
+
+	return priv->password_flags & TP_CHANNEL_PASSWORD_FLAG_PROVIDE;
+}
+
+static void
+provide_password_cb (TpChannel *channel,
+				      gboolean correct,
+				      const GError *error,
+				      gpointer user_data,
+				      GObject *weak_object)
+{
+	GSimpleAsyncResult *result = user_data;
+
+	if (error != NULL) {
+			g_simple_async_result_set_from_error (result, error);
+	}
+	else if (!correct) {
+			/* The current D-Bus API is a bit weird so re-use the
+			* AuthenticationFailed error */
+			g_simple_async_result_set_error (result, TP_ERRORS,
+									 TP_ERROR_AUTHENTICATION_FAILED, "Wrong password");
+	}
+
+	g_simple_async_result_complete (result);
+	g_object_unref (result);
+}
+
+void
+empathy_tp_chat_provide_password_async (EmpathyTpChat *self,
+						     const gchar *password,
+						     GAsyncReadyCallback callback,
+						     gpointer user_data)
+{
+	EmpathyTpChatPriv *priv = GET_PRIV (self);
+	GSimpleAsyncResult *result;
+
+	result = g_simple_async_result_new (G_OBJECT (self),
+					callback, user_data, empathy_tp_chat_provide_password_finish);
+
+	tp_cli_channel_interface_password_call_provide_password (priv->channel, -1,
+		password, provide_password_cb, result, NULL, G_OBJECT (self));
+}
+
+gboolean
+empathy_tp_chat_provide_password_finish (EmpathyTpChat *self,
+						      GAsyncResult *result,
+						      GError **error)
+{
+	if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
+		error))
+		return FALSE;
+
+	g_return_val_if_fail (g_simple_async_result_is_valid (result,
+					G_OBJECT (self), empathy_tp_chat_provide_password_finish), FALSE);
+
+	return TRUE;
+}
diff --git a/libempathy/empathy-tp-chat.h b/libempathy/empathy-tp-chat.h
index f07f064..013a926 100644
--- a/libempathy/empathy-tp-chat.h
+++ b/libempathy/empathy-tp-chat.h
@@ -86,6 +86,17 @@ void           empathy_tp_chat_acknowledge_message (EmpathyTpChat *chat,
 void           empathy_tp_chat_acknowledge_messages (EmpathyTpChat *chat,
 						     const GList *messages);
 
+gboolean       empathy_tp_chat_password_needed (EmpathyTpChat *chat);
+
+void empathy_tp_chat_provide_password_async (EmpathyTpChat *chat,
+						     const gchar *password,
+						     GAsyncReadyCallback callback,
+						     gpointer user_data);
+
+gboolean empathy_tp_chat_provide_password_finish (EmpathyTpChat *chat,
+						     GAsyncResult *result,
+						     GError **error);
+
 G_END_DECLS
 
 #endif /* __EMPATHY_TP_CHAT_H__ */
