diff --git a/libpurple/account.h b/libpurple/account.h
--- a/libpurple/account.h
+++ b/libpurple/account.h
@@ -39,6 +39,7 @@
 #include "connection.h"
 #include "group.h"
 #include "purpleconnectionerrorinfo.h"
+#include "purplecontact.h"
 #include "purpleprotocol.h"
 #include "purpleproxyinfo.h"
 #include "status.h"
@@ -956,6 +957,22 @@
  */
 gboolean purple_account_get_require_password(PurpleAccount *account);
 
+/**
+ * purple_account_get_contact:
+ * @account: The instance.
+ *
+ * Gets the [class@Purple.Contact] for @account that represents the user of
+ * @account.
+ *
+ * This can be used by protocol plugins to store additional information about
+ * the account.
+ *
+ * Returns: (transfer none): The contact that represents @account.
+ *
+ * Since: 3.0.0
+ */
+PurpleContact *purple_account_get_contact(PurpleAccount *account);
+
 G_END_DECLS
 
 #endif /* PURPLE_ACCOUNT_H */
# HG changeset patch
# Node ID 228f572a83d8
# Parent  8bdccbde0536
diff --git a/libpurple/account.c b/libpurple/account.c
--- a/libpurple/account.c
+++ b/libpurple/account.c
@@ -93,6 +93,8 @@
 
 	PurpleConnectionErrorInfo *current_error;	/* Errors */
 	PurpleNotification *error_notification;
+
+	PurpleContact *contact;
 } PurpleAccountPrivate;
 
 typedef struct
@@ -118,6 +120,7 @@
 	PROP_REMEMBER_PASSWORD,
 	PROP_PROXY_INFO,
 	PROP_ERROR,
+	PROP_CONTACT,
 	PROP_LAST
 };
 
@@ -761,6 +764,21 @@
 		g_checksum_free(checksum);
 	}
 
+	/* Create the contact for the account and bind our properties to it. */
+	account->contact = purple_contact_new(account, NULL);
+	/* Skip id for now as it's construct only and exposing it is more work than
+	 * it's worth right now.
+	 */
+#if 0
+	g_object_bind_property(account, "id", account->contact, "id",
+	                       G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+#endif
+	g_object_bind_property(account, "username", account->contact, "username",
+	                       G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+	g_object_bind_property(account, "private-alias",
+	                       account->contact, "display-name",
+	                       G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+
 	g_object_get(object,
 			"username",    &username,
 			"protocol-id", &protocol_id,
@@ -805,6 +823,7 @@
 
 	g_clear_object(&account->gc);
 	g_clear_object(&account->presence);
+	g_clear_object(&account->contact);
 
 	G_OBJECT_CLASS(purple_account_parent_class)->dispose(object);
 }
@@ -857,11 +876,9 @@
 {
 	GObjectClass *obj_class = G_OBJECT_CLASS(klass);
 
+	obj_class->constructed = purple_account_constructed;
 	obj_class->dispose = purple_account_dispose;
 	obj_class->finalize = purple_account_finalize;
-	obj_class->constructed = purple_account_constructed;
-
-	/* Setup properties */
 	obj_class->get_property = purple_account_get_property;
 	obj_class->set_property = purple_account_set_property;
 
@@ -952,6 +969,19 @@
 		PURPLE_TYPE_CONNECTION_ERROR_INFO,
 		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
 
+	/**
+	 * PurpleAccount:contact:
+	 *
+	 * The [class@Purple.Contact] that represents this account.
+	 *
+	 * Since: 3.0.0
+	 */
+	properties[PROP_CONTACT] = g_param_spec_object(
+		"contact", "contact",
+		"The contact for this account",
+		PURPLE_TYPE_CONTACT,
+		G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
 	g_object_class_install_properties(obj_class, PROP_LAST, properties);
 }
 
@@ -2308,3 +2338,10 @@
 
 	return account->require_password;
 }
+
+PurpleContact *
+purple_account_get_contact(PurpleAccount *account) {
+	g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
+
+	return account->contact;
+}
diff --git a/libpurple/purplecontact.h b/libpurple/purplecontact.h
--- a/libpurple/purplecontact.h
+++ b/libpurple/purplecontact.h
@@ -28,7 +28,6 @@
 
 #include <gdk-pixbuf/gdk-pixbuf.h>
 
-#include <libpurple/account.h>
 #include <libpurple/purpletags.h>
 
 G_BEGIN_DECLS
@@ -36,6 +35,7 @@
 #define PURPLE_TYPE_CONTACT (purple_contact_get_type())
 G_DECLARE_FINAL_TYPE(PurpleContact, purple_contact, PURPLE, CONTACT, GObject)
 
+#include <libpurple/account.h>
 #include <libpurple/purpleperson.h>
 
 /**
@@ -89,6 +89,20 @@
 const gchar *purple_contact_get_id(PurpleContact *contact);
 
 /**
+ * purple_contact_set_id:
+ * @contact: The instance.
+ * @id: The new identifier.
+ *
+ * Sets the identifier of @contact to @id. Note, this should be used rarely if
+ * at all. The main intent of this, is for protocols to update the id of
+ * [property@Purple.Account:contact] when an account is connected if it is
+ * missing.
+ *
+ * Since: 3.0.0
+ */
+void purple_contact_set_id(PurpleContact *contact, const char *id);
+
+/**
  * purple_contact_get_username:
  * @contact: The instance.
  *
diff --git a/libpurple/purplecontact.c b/libpurple/purplecontact.c
--- a/libpurple/purplecontact.c
+++ b/libpurple/purplecontact.c
@@ -67,21 +67,6 @@
 	}
 }
 
-static void
-purple_contact_set_id(PurpleContact *contact, const gchar *id) {
-	g_return_if_fail(PURPLE_IS_CONTACT(contact));
-
-	g_free(contact->id);
-
-	if(id != NULL) {
-		contact->id = g_strdup(id);
-	} else {
-		contact->id = g_uuid_string_random();
-	}
-
-	g_object_notify_by_pspec(G_OBJECT(contact), properties[PROP_ID]);
-}
-
 /******************************************************************************
  * GObject Implementation
  *****************************************************************************/
@@ -223,7 +208,7 @@
 		"id", "id",
 		"The id of the contact",
 		NULL,
-		G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * PurpleContact:account:
@@ -364,6 +349,16 @@
 	return contact->id;
 }
 
+void
+purple_contact_set_id(PurpleContact *contact, const gchar *id) {
+	g_return_if_fail(PURPLE_IS_CONTACT(contact));
+
+	g_free(contact->id);
+	contact->id = g_strdup(id);
+
+	g_object_notify_by_pspec(G_OBJECT(contact), properties[PROP_ID]);
+}
+
 const gchar *
 purple_contact_get_username(PurpleContact *contact) {
 	g_return_val_if_fail(PURPLE_IS_CONTACT(contact), NULL);
