diff --git a/libpurple/core.c b/libpurple/core.c
--- a/libpurple/core.c
+++ b/libpurple/core.c
@@ -149,12 +149,13 @@
 	purple_cmds_init();
 	purple_protocols_init();
 
+	purple_credential_manager_startup(); /* before accounts */
+
 	/* Since plugins get probed so early we should probably initialize their
 	 * subsystem right away too.
 	 */
 	purple_plugins_init();
 
-	purple_credential_manager_startup(); /* before accounts */
 	purple_keyring_init(); /* before accounts */
 	purple_theme_manager_init();
 
diff --git a/libpurple/plugins/keyrings/secretservice.c b/libpurple/plugins/keyrings/secretservice.c
--- a/libpurple/plugins/keyrings/secretservice.c
+++ b/libpurple/plugins/keyrings/secretservice.c
@@ -1,24 +1,19 @@
-/* purple
- * @file secretservice.c Secret Service password storage
- * @ingroup plugins
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
+/*
+ * Purple - Internet Messaging Library
+ * Copyright (C) Pidgin Developers <devel@pidgin.im>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * 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 of the License, or (at your option) any later version.
  *
- * This program is distributed in the hope that it will be useful,
+ * 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 General Public License for more details.
+ * 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 General Public License
- * along with this program ; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <https://www.gnu.org/licenses/>.
  */
 
 /* TODO
@@ -43,15 +38,16 @@
    project name. It may not be appropriate to translate this string, but
    transliterating to your alphabet is reasonable. More info about the
    project can be found at https://wiki.gnome.org/Projects/Libsecret */
-#define SECRETSERVICE_NAME        N_("Secret Service")
-#define SECRETSERVICE_ID          "keyring-libsecret"
-#define SECRETSERVICE_DOMAIN      (g_quark_from_static_string(SECRETSERVICE_ID))
+#define SECRETSERVICE_NAME N_("Secret Service")
+#define SECRETSERVICE_ID "secret-service"
 
-static PurpleKeyring *keyring_handler = NULL;
-static GCancellable *keyring_cancellable = NULL;
+/******************************************************************************
+ * Globals
+ *****************************************************************************/
+static PurpleCredentialProvider *instance = NULL;
 
-static const SecretSchema purple_schema = {
-	"im.pidgin.Purple", SECRET_SCHEMA_NONE,
+static const SecretSchema purple_secret_service_schema = {
+	"im.pidgin.Purple3", SECRET_SCHEMA_NONE,
 	{
 		{"user", SECRET_SCHEMA_ATTRIBUTE_STRING},
 		{"protocol", SECRET_SCHEMA_ATTRIBUTE_STRING},
@@ -61,263 +57,232 @@
 	0, 0, 0, 0, 0, 0, 0, 0
 };
 
-typedef struct _InfoStorage InfoStorage;
+#define PURPLE_TYPE_SECRET_SERVICE (purple_secret_service_get_type())
+G_DECLARE_FINAL_TYPE(PurpleSecretService, purple_secret_service,
+                     PURPLE, SECRET_SERVICE, PurpleCredentialProvider)
 
-struct _InfoStorage
-{
-	PurpleAccount *account;
-	gpointer cb;
-	gpointer user_data;
+struct _PurpleSecretService {
+	PurpleCredentialProvider parent;
 };
 
-/***********************************************/
-/*     Keyring interface                       */
-/***********************************************/
-static void
-ss_g_error_to_keyring_error(GError **error, PurpleAccount *account)
-{
-	GError *old_error;
-	GError *new_error = NULL;
-
-	g_return_if_fail(error != NULL);
-
-	old_error = *error;
+G_DEFINE_DYNAMIC_TYPE(PurpleSecretService, purple_secret_service,
+                      PURPLE_TYPE_CREDENTIAL_PROVIDER)
 
-	if (g_error_matches(old_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
-		new_error = g_error_new_literal(PURPLE_KEYRING_ERROR,
-		                                PURPLE_KEYRING_ERROR_CANCELLED,
-		                                _("Operation cancelled."));
-	} else if (g_error_matches(old_error, G_DBUS_ERROR,
-				G_DBUS_ERROR_SPAWN_SERVICE_NOT_FOUND) ||
-			g_error_matches(old_error, G_DBUS_ERROR,
-				G_DBUS_ERROR_IO_ERROR)) {
-		purple_debug_info("keyring-libsecret",
-			"Failed to communicate with Secret "
-			"Service (account: %s (%s)).\n",
-			purple_account_get_username(account),
-			purple_account_get_protocol_id(account));
-		new_error = g_error_new(PURPLE_KEYRING_ERROR,
-			PURPLE_KEYRING_ERROR_BACKENDFAIL,
-			"Failed to communicate with Secret "
-			"Service (account: %s).",
-			purple_account_get_username(account));
-	} else if (g_error_matches(old_error, SECRET_ERROR,
-			SECRET_ERROR_IS_LOCKED)) {
-		purple_debug_info("keyring-libsecret",
-			"Secret Service is locked (account: %s (%s)).\n",
-			purple_account_get_username(account),
-			purple_account_get_protocol_id(account));
-		new_error = g_error_new(PURPLE_KEYRING_ERROR,
-			PURPLE_KEYRING_ERROR_ACCESSDENIED,
-			"Secret Service is locked (account: %s).",
-			purple_account_get_username(account));
+/******************************************************************************
+ * Callbacks
+ *****************************************************************************/
+static void
+purple_secret_service_read_password_callback(GObject *obj,
+                                             GAsyncResult *result,
+                                             gpointer data)
+{
+	GTask *task = G_TASK(data);
+	GError *error = NULL;
+	gchar *password = NULL;
+
+	password = secret_password_lookup_finish(result, &error);
+
+	if(error != NULL) {
+		g_task_return_error(task, error);
 	} else {
-		purple_debug_error("keyring-libsecret",
-			"Unknown error (account: %s (%s), "
-			"domain: %s, code: %d): %s.\n",
-			purple_account_get_username(account),
-			purple_account_get_protocol_id(account),
-			g_quark_to_string(old_error->domain),
-			old_error->code, old_error->message);
-		new_error = g_error_new(PURPLE_KEYRING_ERROR,
-			PURPLE_KEYRING_ERROR_BACKENDFAIL,
-			"Unknown error (account: %s).",
-			purple_account_get_username(account));
+		g_task_return_pointer(task, password, g_free);
 	}
 
-	g_clear_error(error);
-	g_propagate_error(error, new_error);
+	g_object_unref(G_OBJECT(task));
 }
 
 static void
-ss_read_continue(GObject *object, GAsyncResult *result, gpointer data)
+purple_secret_service_write_password_callback(GObject *obj,
+                                              GAsyncResult *result,
+                                              gpointer data)
 {
-	InfoStorage *storage = data;
-	PurpleAccount *account = storage->account;
-	PurpleKeyringReadCallback cb = storage->cb;
-	char *password;
+	GTask *task = G_TASK(data);
 	GError *error = NULL;
+	gboolean ret = FALSE;
 
-	password = secret_password_lookup_finish(result, &error);
+	ret = secret_password_store_finish(result, &error);
 
-	if (error != NULL) {
-		ss_g_error_to_keyring_error(&error, account);
-	} else if (password == NULL) {
-		error = g_error_new(PURPLE_KEYRING_ERROR,
-			PURPLE_KEYRING_ERROR_NOPASSWORD,
-			"No password found for account: %s",
-			purple_account_get_username(account));
+	if(error != NULL) {
+		g_task_return_error(task, error);
+	} else {
+		g_task_return_boolean(task, ret);
 	}
 
-	if (cb != NULL) {
-		cb(account, password, error, storage->user_data);
-	}
-
-	g_clear_error(&error);
-	g_free(storage);
+	g_object_unref(G_OBJECT(task));
 }
 
 static void
-ss_read(PurpleAccount *account, PurpleKeyringReadCallback cb, gpointer data)
+purple_secret_service_clear_password_callback(GObject *obj,
+                                              GAsyncResult *result,
+                                              gpointer data)
 {
-	InfoStorage *storage = g_new0(InfoStorage, 1);
+	GTask *task = G_TASK(data);
+	GError *error = NULL;
+	gboolean ret = FALSE;
+
+	ret = secret_password_clear_finish(result, &error);
+
+	if(error != NULL) {
+		g_task_return_error(task, error);
+	} else {
+		g_task_return_boolean(task, ret);
+	}
+
+	g_object_unref(G_OBJECT(task));
+}
 
-	storage->account = account;
-	storage->cb = cb;
-	storage->user_data = data;
+/******************************************************************************
+ * PurpleCredentialProvider Implementation
+ *****************************************************************************/
+static void
+purple_secret_service_read_password_async(PurpleCredentialProvider *provider,
+                                          PurpleAccount *account,
+                                          GCancellable *cancellable,
+                                          GAsyncReadyCallback callback,
+                                          gpointer data)
+{
+	GTask *task = g_task_new(G_OBJECT(provider), cancellable, callback, data);
 
-	secret_password_lookup(&purple_schema, keyring_cancellable,
-		ss_read_continue, storage,
-		"user", purple_account_get_username(account),
-		"protocol", purple_account_get_protocol_id(account), NULL);
+	secret_password_lookup(&purple_secret_service_schema, cancellable,
+                           purple_secret_service_read_password_callback, task,
+                           "user", purple_account_get_username(account),
+                           "protocol", purple_account_get_protocol_id(account),
+                           NULL);
+}
+
+static gchar *
+purple_secret_service_read_password_finish(PurpleCredentialProvider *provider,
+                                           GAsyncResult *result,
+                                           GError **error)
+{
+	g_return_val_if_fail(PURPLE_IS_CREDENTIAL_PROVIDER(provider), FALSE);
+	g_return_val_if_fail(G_IS_ASYNC_RESULT(result), FALSE);
+
+	return g_task_propagate_pointer(G_TASK(result), error);
 }
 
 static void
-ss_save_continue(GError *error, gpointer data)
+purple_secret_service_write_password_async(PurpleCredentialProvider *provider,
+                                           PurpleAccount *account,
+                                           const gchar *password,
+                                           GCancellable *cancellable,
+                                           GAsyncReadyCallback callback,
+                                           gpointer data)
 {
-	InfoStorage *storage = data;
-	PurpleKeyringSaveCallback cb;
-	PurpleAccount *account;
-
-	account = storage->account;
-	cb = storage->cb;
+	GTask *task = NULL;
+	gchar *label = NULL;
+	const gchar *username = NULL;
 
-	if (error != NULL) {
-		ss_g_error_to_keyring_error(&error, account);
-	} else {
-		purple_debug_info("keyring-libsecret", "Password for %s updated.\n",
-			purple_account_get_username(account));
-	}
+	task = g_task_new(G_OBJECT(provider), cancellable, callback, data);
+	username = purple_account_get_username(account);
 
-	if (cb != NULL)
-		cb(account, error, storage->user_data);
-
-	g_clear_error(&error);
-	g_free(storage);
+	label = g_strdup_printf(_("libpurple password for account %s"), username);
+	secret_password_store(&purple_secret_service_schema,
+	                      SECRET_COLLECTION_DEFAULT, label, password,
+	                      cancellable,
+	                      purple_secret_service_write_password_callback, task,
+	                      "user", username,
+	                      "protocol", purple_account_get_protocol_id(account),
+	                      NULL);
+	g_free(label);
 }
 
-static void
-ss_store_continue(GObject *object, GAsyncResult *result, gpointer data)
+static gboolean
+purple_secret_service_write_password_finish(PurpleCredentialProvider *provider,
+                                            GAsyncResult *result,
+                                            GError **error)
 {
-	GError *error = NULL;
-
-	secret_password_store_finish(result, &error);
+	g_return_val_if_fail(PURPLE_IS_CREDENTIAL_PROVIDER(provider), FALSE);
+	g_return_val_if_fail(G_IS_ASYNC_RESULT(result), FALSE);
 
-	ss_save_continue(error, data);
-}
-
-static void
-ss_clear_continue(GObject *object, GAsyncResult *result, gpointer data)
-{
-	GError *error = NULL;
-
-	secret_password_clear_finish(result, &error);
-
-	ss_save_continue(error, data);
+	return g_task_propagate_boolean(G_TASK(result), error);
 }
 
 static void
-ss_save(PurpleAccount *account,
-         const gchar *password,
-         PurpleKeyringSaveCallback cb,
-         gpointer data)
+purple_secret_service_clear_password_async(PurpleCredentialProvider *provider,
+                                           PurpleAccount *account,
+                                           GCancellable *cancellable,
+                                           GAsyncReadyCallback callback,
+                                           gpointer data)
 {
-	InfoStorage *storage = g_new0(InfoStorage, 1);
+	GTask *task = g_task_new(G_OBJECT(provider), cancellable, callback, data);
 
-	storage->account = account;
-	storage->cb = cb;
-	storage->user_data = data;
-
-	if (password != NULL && *password != '\0') {
-		const char *username = purple_account_get_username(account);
-		char *label;
-
-		purple_debug_info("keyring-libsecret",
-			"Updating password for account %s (%s).\n",
-			username, purple_account_get_protocol_id(account));
+	secret_password_clear(&purple_secret_service_schema, cancellable,
+	                      purple_secret_service_clear_password_callback, task,
+	                      "user", purple_account_get_username(account),
+	                      "protocol", purple_account_get_protocol_id(account),
+	                      NULL);
+}
 
-		label = g_strdup_printf(_("Pidgin IM password for account %s"), username);
-		secret_password_store(&purple_schema, SECRET_COLLECTION_DEFAULT,
-			label, password, keyring_cancellable,
-			ss_store_continue, storage,
-			"user", username,
-			"protocol", purple_account_get_protocol_id(account),
-			NULL);
-		g_free(label);
+static gboolean
+purple_secret_service_clear_password_finish(PurpleCredentialProvider *provider,
+                                            GAsyncResult *result,
+                                            GError **error)
+{
+	g_return_val_if_fail(PURPLE_IS_CREDENTIAL_PROVIDER(provider), FALSE);
+	g_return_val_if_fail(G_IS_ASYNC_RESULT(result), FALSE);
 
-	} else {	/* password == NULL, delete password. */
-		purple_debug_info("keyring-libsecret",
-			"Forgetting password for account %s (%s).\n",
-			purple_account_get_username(account),
-			purple_account_get_protocol_id(account));
+	return g_task_propagate_boolean(G_TASK(result), error);
+}
 
-		secret_password_clear(&purple_schema, keyring_cancellable,
-			ss_clear_continue, storage,
-			"user", purple_account_get_username(account),
-			"protocol", purple_account_get_protocol_id(account),
-			NULL);
-	}
+/******************************************************************************
+ * GObject Implementation
+ *****************************************************************************/
+static void
+purple_secret_service_init(PurpleSecretService *ss) {
 }
 
 static void
-ss_cancel(void)
-{
-	g_cancellable_cancel(keyring_cancellable);
+purple_secret_service_class_init(PurpleSecretServiceClass *klass) {
+	PurpleCredentialProviderClass *provider_class = NULL;
 
-	/* Swap out cancelled cancellable for new one for further operations */
-	g_clear_object(&keyring_cancellable);
-	keyring_cancellable = g_cancellable_new();
+	provider_class = PURPLE_CREDENTIAL_PROVIDER_CLASS(klass);
+	provider_class->read_password_async =
+		purple_secret_service_read_password_async;
+	provider_class->read_password_finish =
+		purple_secret_service_read_password_finish;
+	provider_class->write_password_async =
+		purple_secret_service_write_password_async;
+	provider_class->write_password_finish =
+		purple_secret_service_write_password_finish;
+	provider_class->clear_password_async =
+		purple_secret_service_clear_password_async;
+	provider_class->clear_password_finish =
+		purple_secret_service_clear_password_finish;
 }
 
 static void
-ss_close(void)
-{
-	ss_cancel();
+purple_secret_service_class_finalize(PurpleSecretServiceClass *klass) {
 }
 
-static gboolean
-ss_init(GError **error)
-{
-	keyring_cancellable = g_cancellable_new();
-
-	keyring_handler = purple_keyring_new();
-
-	purple_keyring_set_name(keyring_handler, _(SECRETSERVICE_NAME));
-	purple_keyring_set_id(keyring_handler, SECRETSERVICE_ID);
-	purple_keyring_set_read_password(keyring_handler, ss_read);
-	purple_keyring_set_save_password(keyring_handler, ss_save);
-	purple_keyring_set_cancel_requests(keyring_handler, ss_cancel);
-	purple_keyring_set_close_keyring(keyring_handler, ss_close);
-
-	purple_keyring_register(keyring_handler);
-
-	return TRUE;
+/******************************************************************************
+ * API
+ *****************************************************************************/
+static PurpleCredentialProvider *
+purple_secret_service_new(void) {
+	return PURPLE_CREDENTIAL_PROVIDER(g_object_new(
+		PURPLE_TYPE_SECRET_SERVICE,
+		"id", SECRETSERVICE_ID,
+		"name", _(SECRETSERVICE_NAME),
+		NULL
+	));
 }
 
-static void
-ss_uninit(void)
-{
-	ss_close();
-	purple_keyring_unregister(keyring_handler);
-	purple_keyring_free(keyring_handler);
-	keyring_handler = NULL;
+/******************************************************************************
+ * Plugin Exports
+ *****************************************************************************/
+G_MODULE_EXPORT GPluginPluginInfo *gplugin_query(GPluginPlugin *plugin, GError **error);
+G_MODULE_EXPORT gboolean gplugin_load(GPluginPlugin *plugin, GError **error);
+G_MODULE_EXPORT gboolean gplugin_unload(GPluginPlugin *plugin, GError **error);
 
-	g_clear_object(&keyring_cancellable);
-}
-
-/***********************************************/
-/*     Plugin interface                        */
-/***********************************************/
-
-static PurplePluginInfo *
-plugin_query(GError **error)
-{
+G_MODULE_EXPORT GPluginPluginInfo *
+gplugin_query(GPluginPlugin *plugin, GError **error) {
 	const gchar * const authors[] = {
 		"Elliott Sales de Andrade (qulogic[at]pidgin.im)",
 		NULL
 	};
 
-	return purple_plugin_info_new(
+	return GPLUGIN_PLUGIN_INFO(purple_plugin_info_new(
 		"id",           SECRETSERVICE_ID,
 		"name",         SECRETSERVICE_NAME,
 		"version",      DISPLAY_VERSION,
@@ -327,29 +292,39 @@
 		"authors",      authors,
 		"website",      PURPLE_WEBSITE,
 		"abi-version",  PURPLE_ABI_VERSION,
-		"flags",        PURPLE_PLUGIN_INFO_FLAGS_INTERNAL,
+		"flags",        PURPLE_PLUGIN_INFO_FLAGS_INTERNAL |
+		                PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD,
 		NULL
-	);
-}
-
-static gboolean
-plugin_load(PurplePlugin *plugin, GError **error)
-{
-	return ss_init(error);
+	));
 }
 
-static gboolean
-plugin_unload(PurplePlugin *plugin, GError **error)
-{
-	if (purple_keyring_get_inuse() == keyring_handler) {
-		g_set_error(error, SECRETSERVICE_DOMAIN, 0, "The keyring is currently "
-			"in use.");
-		return FALSE;
+G_MODULE_EXPORT gboolean
+gplugin_load(GPluginPlugin *plugin, GError **error) {
+	PurpleCredentialManager *manager = NULL;
+
+	purple_secret_service_register_type(G_TYPE_MODULE(plugin));
+
+	manager = purple_credential_manager_get_default();
+
+	instance = purple_secret_service_new();
+
+	return purple_credential_manager_register_provider(manager, instance,
+	                                                   error);
+}
+
+G_MODULE_EXPORT gboolean
+gplugin_unload(GPluginPlugin *plugin, GError **error) {
+	PurpleCredentialManager *manager = NULL;
+	gboolean ret = FALSE;
+
+	manager = purple_credential_manager_get_default();
+	ret = purple_credential_manager_unregister_provider(manager, instance,
+	                                                    error);
+	if(!ret) {
+		return ret;
 	}
 
-	ss_uninit();
+	g_clear_object(&instance);
 
 	return TRUE;
 }
-
-PURPLE_PLUGIN_INIT(secret_service, plugin_query, plugin_load, plugin_unload);
diff --git a/libpurple/purplecredentialmanager.c b/libpurple/purplecredentialmanager.c
--- a/libpurple/purplecredentialmanager.c
+++ b/libpurple/purplecredentialmanager.c
@@ -19,7 +19,12 @@
 #include <glib/gi18n-lib.h>
 
 #include "purplecredentialmanager.h"
+
+#include "core.h"
+#include "debug.h"
+#include "prefs.h"
 #include "purpleprivate.h"
+#include "util.h"
 
 enum {
 	SIG_PROVIDER_REGISTERED,
@@ -112,6 +117,23 @@
 	g_object_unref(G_OBJECT(task));
 }
 
+/******************************************************************************
+ * Purple Callbacks
+ *****************************************************************************/
+static void
+purple_credential_manager_core_init_cb(gpointer data) {
+	PurpleCredentialManager *manager = PURPLE_CREDENTIAL_MANAGER(data);
+	PurpleCredentialManagerPrivate *priv = NULL;
+
+	priv = purple_credential_manager_get_instance_private(manager);
+	if(!PURPLE_IS_CREDENTIAL_PROVIDER(priv->active_provider)) {
+		purple_notify_error(NULL, _("Credential Manager"),
+		                    _("Failed to load the selected credential "
+		                      "provider."),
+		                    _("Check your system configuration or select "
+		                      "another one in the preferences dialog."), NULL);
+	}
+}
 
 /******************************************************************************
  * GObject Implementation
@@ -138,6 +160,13 @@
 
 	priv->providers = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
 	                                        g_object_unref);
+
+	/* Connect to the core-initialized signal so we can alert the user if we
+	 * were unable to find their credential provider.
+	 */
+	purple_signal_connect(purple_get_core(), "core-initialized", manager,
+	                      PURPLE_CALLBACK(purple_credential_manager_core_init_cb),
+	                      manager);
 }
 
 static void
@@ -267,6 +296,20 @@
 	g_signal_emit(G_OBJECT(manager), signals[SIG_PROVIDER_REGISTERED], 0,
 	              provider);
 
+	/* If we don't currently have an active provider, check if the newly
+	 * registered provider has the id of the stored provider in preferences.
+	 * If it is, go ahead and make it the active provider.
+	 */
+	if(!PURPLE_IS_CREDENTIAL_PROVIDER(priv->active_provider)) {
+		const gchar *wanted = NULL;
+
+		wanted = purple_prefs_get_string("/purple/credentials/active-provider");
+
+		if(purple_strequal(wanted, id)) {
+			purple_credential_manager_set_active_provider(manager, id, error);
+		}
+	}
+
 	return TRUE;
 }
 
@@ -337,6 +380,9 @@
 
 	g_clear_object(&old);
 
+	purple_debug_info("PurpleCredentialProvider",
+	                  "set active provider to '%s'", id);
+
 	return TRUE;
 }
 
