diff --git a/addressbook/backends/google/e-book-backend-google.c b/addressbook/backends/google/e-book-backend-google.c
index 7e106c5..fc30395 100644
--- a/addressbook/backends/google/e-book-backend-google.c
+++ b/addressbook/backends/google/e-book-backend-google.c
@@ -84,40 +84,7 @@ data_book_error_from_gdata_error (GError **error,
 	g_return_if_fail (gdata_error != NULL);
 
 	/* Authentication errors */
-	if (gdata_error->domain == GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR) {
-		switch (gdata_error->code) {
-		case GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR_BAD_AUTHENTICATION:
-			g_set_error_literal (
-				error, E_CLIENT_ERROR,
-				E_CLIENT_ERROR_AUTHENTICATION_FAILED,
-				e_client_error_to_string (
-				E_CLIENT_ERROR_AUTHENTICATION_FAILED));
-			break;
-		case GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR_NOT_VERIFIED:
-		case GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR_TERMS_NOT_AGREED:
-		case GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR_CAPTCHA_REQUIRED:
-		case GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR_ACCOUNT_DELETED:
-		case GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR_ACCOUNT_DISABLED:
-			g_set_error_literal (
-				error, E_CLIENT_ERROR,
-				E_CLIENT_ERROR_PERMISSION_DENIED,
-				e_client_error_to_string (
-				E_CLIENT_ERROR_PERMISSION_DENIED));
-			break;
-		case GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR_SERVICE_DISABLED:
-			g_set_error_literal (
-				error, E_CLIENT_ERROR,
-				E_CLIENT_ERROR_REPOSITORY_OFFLINE,
-				e_client_error_to_string (
-				E_CLIENT_ERROR_REPOSITORY_OFFLINE));
-			break;
-		default:
-			use_fallback = TRUE;
-			break;
-		}
-
-	/* General service errors */
-	} else if (gdata_error->domain == GDATA_SERVICE_ERROR) {
+	if (gdata_error->domain == GDATA_SERVICE_ERROR) {
 		switch (gdata_error->code) {
 		case GDATA_SERVICE_ERROR_UNAVAILABLE:
 			g_set_error_literal (
@@ -1012,7 +979,8 @@ get_groups (EBookBackend *backend,
 
 static void
 get_groups_sync (EBookBackend *backend,
-                 GCancellable *cancellable)
+                 GCancellable *cancellable,
+		 GError **error)
 {
 	EBookBackendGooglePrivate *priv;
 	GDataQuery *query;
@@ -1033,7 +1001,7 @@ get_groups_sync (EBookBackend *backend,
 		cancellable,
 		(GDataQueryProgressCallback) process_group,
 		backend,
-		NULL);
+		error);
 
 	if (feed)
 		g_object_unref (feed);
@@ -1201,38 +1169,26 @@ connect_without_password (EBookBackend *backend,
 			  GCancellable *cancellable,
 			  GError **error)
 {
+	ESource *source;
+	ESourceAuthentication *extension;
+	EGDataOAuth2Authorizer *authorizer;
+	gchar *method;
+	gboolean is_oauth2_method;
 	EBookBackendGooglePrivate *priv;
 
 	priv = E_BOOK_BACKEND_GOOGLE_GET_PRIVATE (backend);
 
+	source = e_backend_get_source (E_BACKEND (backend));
+	extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
+	method = e_source_authentication_dup_method (extension);
+	is_oauth2_method = g_strcmp0 (method, "OAuth2") == 0;
+	g_free (method);
+
 	/* Make sure we have the GDataService configured
 	 * before requesting authorization. */
 
 	if (priv->authorizer == NULL) {
-		ESource *source;
-		ESourceAuthentication *extension;
-		EGDataOAuth2Authorizer *authorizer;
-		const gchar *extension_name;
-		gchar *method;
-
-		extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
-		source = e_backend_get_source (E_BACKEND (backend));
-		extension = e_source_get_extension (source, extension_name);
-		method = e_source_authentication_dup_method (extension);
-
-		if (g_strcmp0 (method, "OAuth2") == 0) {
-			authorizer = e_gdata_oauth2_authorizer_new (source);
-			priv->authorizer = GDATA_AUTHORIZER (authorizer);
-		}
-
-		g_free (method);
-	}
-
-	if (priv->authorizer == NULL) {
-		GDataClientLoginAuthorizer *authorizer;
-
-		authorizer = gdata_client_login_authorizer_new (
-			CLIENT_ID, GDATA_TYPE_CONTACTS_SERVICE);
+		authorizer = e_gdata_oauth2_authorizer_new (source);
 		priv->authorizer = GDATA_AUTHORIZER (authorizer);
 	}
 
@@ -1261,10 +1217,10 @@ connect_without_password (EBookBackend *backend,
 	/* If we're using OAuth tokens, then as far as the backend
 	 * is concerned it's always authorized.  The GDataAuthorizer
 	 * will take care of everything in the background. */
-	if (!GDATA_IS_CLIENT_LOGIN_AUTHORIZER (priv->authorizer))
+	if (is_oauth2_method)
 		return TRUE;
 
-	/* Otherwise it's up to us to obtain a login secret. */
+	/* Otherwise it's up to us to obtain an OAuth 2 token. */
 	return FALSE;
 }
 
@@ -1812,7 +1768,7 @@ book_backend_google_create_contacts_sync (EBookBackend *backend,
 
 	/* Ensure the system groups have been fetched. */
 	if (g_hash_table_size (priv->system_groups_by_id) == 0)
-		get_groups_sync (backend, cancellable);
+		get_groups_sync (backend, cancellable, NULL);
 
 	/* Build the GDataEntry from the vCard */
 	contact = e_contact_new_from_vcard (vcards[0]);
@@ -1987,7 +1943,7 @@ book_backend_google_modify_contacts_sync (EBookBackend *backend,
 
 	/* Ensure the system groups have been fetched. */
 	if (g_hash_table_size (priv->system_groups_by_id) == 0)
-		get_groups_sync (backend, cancellable);
+		get_groups_sync (backend, cancellable, NULL);
 
 	/* Update the old GDataEntry from the new contact. */
 	gdata_entry_update_from_e_contact (
@@ -2329,58 +2285,39 @@ book_backend_google_authenticate_sync (EBackend *backend,
 				       GCancellable *cancellable,
 				       GError **error)
 {
+	EBookBackend *book_backend = E_BOOK_BACKEND (backend);
 	EBookBackendGooglePrivate *priv;
-	ESourceAuthentication *auth_extension;
 	ESourceAuthenticationResult result;
-	ESource *source;
-	const gchar *username;
-	gchar *user;
+	EGDataOAuth2Authorizer *authorizer;
 	GError *local_error = NULL;
 
 	g_debug (G_STRFUNC);
 
 	/* We should not have gotten here if we're offline. */
-	g_return_val_if_fail (
-		e_backend_get_online (E_BACKEND (backend)),
-		E_SOURCE_AUTHENTICATION_ERROR);
-
-	/* Nor should we have gotten here if we're already authorized. */
-	g_return_val_if_fail (
-		!backend_is_authorized (E_BOOK_BACKEND (backend)),
-		E_SOURCE_AUTHENTICATION_ERROR);
+	g_return_val_if_fail (e_backend_get_online (backend), E_SOURCE_AUTHENTICATION_ERROR);
 
 	priv = E_BOOK_BACKEND_GOOGLE (backend)->priv;
 
-	source = e_backend_get_source (backend);
-	auth_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
-	user = e_source_authentication_dup_user (auth_extension);
+	g_return_val_if_fail (E_IS_GDATA_OAUTH2_AUTHORIZER (priv->authorizer), E_SOURCE_AUTHENTICATION_ERROR);
 
-	username = e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_USERNAME);
-	if (!username || !*username)
-		username = user;
+	authorizer = E_GDATA_OAUTH2_AUTHORIZER (priv->authorizer);
+	e_gdata_oauth2_authorizer_set_credentials (authorizer, credentials);
 
-	if (gdata_client_login_authorizer_authenticate (GDATA_CLIENT_LOGIN_AUTHORIZER (priv->authorizer),
-		user, e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_PASSWORD), cancellable, &local_error)) {
-		EBookBackend *book_backend = E_BOOK_BACKEND (backend);
+	get_groups_sync (E_BOOK_BACKEND (backend), cancellable, &local_error);
+
+	if (local_error == NULL) {
+		result = E_SOURCE_AUTHENTICATION_ACCEPTED;
 
 		if (backend_is_authorized (book_backend)) {
 			e_book_backend_set_writable (book_backend, TRUE);
 			cache_refresh_if_needed (book_backend);
 		}
-	}
-
-	g_free (user);
-
-	if (local_error == NULL) {
-		result = E_SOURCE_AUTHENTICATION_ACCEPTED;
-
-	} else if (g_error_matches (
-		local_error, GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR,
-		GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR_BAD_AUTHENTICATION)) {
-
+	} else if (g_error_matches (local_error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_AUTHENTICATION_REQUIRED)) {
+		if (!e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_PASSWORD))
+			result = E_SOURCE_AUTHENTICATION_REQUIRED;
+		else
+			result = E_SOURCE_AUTHENTICATION_REJECTED;
 		g_clear_error (&local_error);
-		result = E_SOURCE_AUTHENTICATION_REJECTED;
-
 	} else {
 		g_propagate_error (error, local_error);
 		result = E_SOURCE_AUTHENTICATION_ERROR;
diff --git a/addressbook/backends/google/e-gdata-oauth2-authorizer.c b/addressbook/backends/google/e-gdata-oauth2-authorizer.c
index a8e384b..3012ca6 100644
--- a/addressbook/backends/google/e-gdata-oauth2-authorizer.c
+++ b/addressbook/backends/google/e-gdata-oauth2-authorizer.c
@@ -27,6 +27,7 @@ struct _EGDataOAuth2AuthorizerPrivate {
 	/* These members are protected by the global mutex. */
 	gchar *access_token;
 	GHashTable *authorization_domains;
+	ENamedParameters *credentials;
 };
 
 enum {
@@ -137,6 +138,8 @@ gdata_oauth2_authorizer_finalize (GObject *object)
 	g_hash_table_destroy (priv->authorization_domains);
 	g_weak_ref_clear (&priv->source);
 
+	e_named_parameters_free (priv->credentials);
+
 	/* Chain up to parent's finalize() method. */
 	G_OBJECT_CLASS (e_gdata_oauth2_authorizer_parent_class)->
 		finalize (object);
@@ -240,8 +243,8 @@ gdata_oauth2_authorizer_refresh_authorization (GDataAuthorizer *authorizer,
 	g_free (*ptr_access_token);
 	*ptr_access_token = NULL;
 
-	success = e_source_get_oauth2_access_token_sync (
-		source, cancellable, ptr_access_token, NULL, error);
+	success = e_util_get_source_oauth2_access_token_sync (source, oauth2_authorizer->priv->credentials,
+		ptr_access_token, NULL, cancellable, error);
 
 	g_mutex_unlock (&mutex);
 
@@ -323,3 +326,36 @@ e_gdata_oauth2_authorizer_ref_source (EGDataOAuth2Authorizer *authorizer)
 	return g_weak_ref_get (&authorizer->priv->source);
 }
 
+void
+e_gdata_oauth2_authorizer_set_credentials (EGDataOAuth2Authorizer *authorizer,
+					   const ENamedParameters *credentials)
+{
+	g_return_if_fail (E_IS_GDATA_OAUTH2_AUTHORIZER (authorizer));
+
+	g_mutex_lock (&mutex);
+
+	e_named_parameters_free (authorizer->priv->credentials);
+	if (credentials)
+		authorizer->priv->credentials = e_named_parameters_new_clone (credentials);
+	else
+		authorizer->priv->credentials = NULL;
+
+	g_mutex_unlock (&mutex);
+}
+
+ENamedParameters *
+e_gdata_oauth2_authorizer_clone_credentials (EGDataOAuth2Authorizer *authorizer)
+{
+	ENamedParameters *credentials = NULL;
+
+	g_return_val_if_fail (E_IS_GDATA_OAUTH2_AUTHORIZER (authorizer), NULL);
+
+	g_mutex_lock (&mutex);
+
+	if (authorizer->priv->credentials)
+		credentials = e_named_parameters_new_clone (authorizer->priv->credentials);
+
+	g_mutex_unlock (&mutex);
+
+	return credentials;
+}
diff --git a/addressbook/backends/google/e-gdata-oauth2-authorizer.h b/addressbook/backends/google/e-gdata-oauth2-authorizer.h
index ef908f6..0dfc582 100644
--- a/addressbook/backends/google/e-gdata-oauth2-authorizer.h
+++ b/addressbook/backends/google/e-gdata-oauth2-authorizer.h
@@ -62,6 +62,12 @@ EGDataOAuth2Authorizer *
 					(ESource *source);
 ESource *	e_gdata_oauth2_authorizer_ref_source
 					(EGDataOAuth2Authorizer *authorizer);
+void		e_gdata_oauth2_authorizer_set_credentials
+					(EGDataOAuth2Authorizer *authorizer,
+					 const ENamedParameters *credentials);
+ENamedParameters *
+		e_gdata_oauth2_authorizer_clone_credentials
+					(EGDataOAuth2Authorizer *authorizer);
 
 G_END_DECLS
 
diff --git a/addressbook/libebook/e-book-client-view.c b/addressbook/libebook/e-book-client-view.c
index 0393617..be4df5c 100644
--- a/addressbook/libebook/e-book-client-view.c
+++ b/addressbook/libebook/e-book-client-view.c
@@ -647,6 +647,8 @@ book_client_view_complete_cb (EGdbusBookView *object,
 		g_weak_ref_init (&signal_closure->client_view, client_view);
 		e_gdbus_templates_decode_error (
 			in_error_strv, &signal_closure->error);
+		if (signal_closure->error)
+			g_dbus_error_strip_remote_error (signal_closure->error);
 
 		main_context = book_client_view_ref_main_context (client_view);
 
diff --git a/calendar/backends/caldav/e-cal-backend-caldav.c b/calendar/backends/caldav/e-cal-backend-caldav.c
index 51ec4ba..4d1a389 100644
--- a/calendar/backends/caldav/e-cal-backend-caldav.c
+++ b/calendar/backends/caldav/e-cal-backend-caldav.c
@@ -95,8 +95,7 @@ struct _ECalBackendCalDAVPrivate {
 	gchar *uri;
 
 	/* Authentication info */
-	gchar *username; /* not NULL only as override */
-	gchar *password;
+	ENamedParameters *credentials;
 	gboolean auth_required;
 
 	/* support for 'getctag' extension */
@@ -322,6 +321,92 @@ static void put_server_comp_to_cache (ECalBackendCalDAV *cbdav, icalcomponent *i
 /* ************************************************************************* */
 /* Misc. utility functions */
 
+static gboolean
+caldav_setup_bearer_auth (ECalBackendCalDAV *cbdav,
+			  ESoupAuthBearer *bearer,
+			  GCancellable *cancellable,
+			  GError **error)
+{
+	ESource *source;
+	gchar *access_token = NULL;
+	gint expires_in_seconds = -1;
+	gboolean success = FALSE;
+
+	g_return_val_if_fail (E_IS_CAL_BACKEND_CALDAV (cbdav), FALSE);
+	g_return_val_if_fail (E_IS_SOUP_AUTH_BEARER (bearer), FALSE);
+
+	source = e_backend_get_source (E_BACKEND (cbdav));
+
+	success = e_util_get_source_oauth2_access_token_sync (source, cbdav->priv->credentials,
+		&access_token, &expires_in_seconds, cancellable, error);
+
+	if (success)
+		e_soup_auth_bearer_set_access_token (bearer, access_token, expires_in_seconds);
+
+	g_free (access_token);
+
+	return success;
+}
+
+static gboolean
+caldav_maybe_prepare_bearer_auth (ECalBackendCalDAV *cbdav,
+				  GCancellable *cancellable,
+				  GError **error)
+{
+	ESourceWebdav *extension;
+	ESource *source;
+	SoupSessionFeature *feature;
+	SoupAuth *soup_auth;
+	SoupURI *soup_uri;
+	gchar *auth_method = NULL;
+	gboolean success;
+
+	g_return_val_if_fail (E_IS_CAL_BACKEND_CALDAV (cbdav), FALSE);
+
+	source = e_backend_get_source (E_BACKEND (cbdav));
+
+	if (e_source_has_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION)) {
+		ESourceAuthentication *extension;
+
+		extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
+		auth_method = e_source_authentication_dup_method (extension);
+	} else {
+		return TRUE;
+	}
+
+	if (g_strcmp0 (auth_method, "OAuth2") != 0 && g_strcmp0 (auth_method, "Google") != 0) {
+		g_free (auth_method);
+		return TRUE;
+	}
+
+	g_free (auth_method);
+
+	/* Preload the SoupAuthManager with a valid "Bearer" token
+	 * when using OAuth 2.0. This avoids an extra unauthorized
+	 * HTTP round-trip, which apparently Google doesn't like. */
+
+	feature = soup_session_get_feature (cbdav->priv->session, SOUP_TYPE_AUTH_MANAGER);
+
+	extension = e_source_get_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND);
+	soup_uri = e_source_webdav_dup_soup_uri (extension);
+
+	soup_auth = g_object_new (
+		E_TYPE_SOUP_AUTH_BEARER,
+		SOUP_AUTH_HOST, soup_uri->host, NULL);
+
+	success = caldav_setup_bearer_auth (cbdav, E_SOUP_AUTH_BEARER (soup_auth), cancellable, error);
+	if (success) {
+		soup_auth_manager_use_auth (
+			SOUP_AUTH_MANAGER (feature),
+			soup_uri, soup_auth);
+	}
+
+	g_object_unref (soup_auth);
+	soup_uri_free (soup_uri);
+
+	return success;
+}
+
 static void
 update_slave_cmd (ECalBackendCalDAVPrivate *priv,
                   SlaveCommand slave_cmd)
@@ -994,20 +1079,9 @@ soup_authenticate_bearer (SoupSession *session,
                           SoupAuth *auth,
                           ECalBackendCalDAV *cbdav)
 {
-	ESource *source;
-	gchar *access_token = NULL;
-	gint expires_in_seconds = -1;
 	GError *local_error = NULL;
 
-	source = e_backend_get_source (E_BACKEND (cbdav));
-
-	e_source_get_oauth2_access_token_sync (
-		source, NULL, &access_token,
-		&expires_in_seconds, &local_error);
-
-	e_soup_auth_bearer_set_access_token (
-		E_SOUP_AUTH_BEARER (auth),
-		access_token, expires_in_seconds);
+	caldav_setup_bearer_auth (cbdav, E_SOUP_AUTH_BEARER (auth), NULL, &local_error);
 
 	/* Stash the error to be picked up by caldav_credentials_required_sync().
 	 * There's no way to explicitly propagate a GError directly
@@ -1026,8 +1100,6 @@ soup_authenticate_bearer (SoupSession *session,
 
 		g_mutex_unlock (&cbdav->priv->bearer_auth_error_lock);
 	}
-
-	g_free (access_token);
 }
 
 static void
@@ -1063,14 +1135,15 @@ soup_authenticate (SoupSession *session,
 
 		auth_user = e_source_authentication_dup_user (auth_extension);
 
-		username = cbdav->priv->username;
+		username = cbdav->priv->credentials ? e_named_parameters_get (cbdav->priv->credentials, E_SOURCE_CREDENTIAL_USERNAME) : NULL;
 		if (!username || !*username)
 			username = auth_user;
 
-		if (!username || !*username || !cbdav->priv->password)
+		if (!username || !*username || !cbdav->priv->credentials ||
+		    !e_named_parameters_exists (cbdav->priv->credentials, E_SOURCE_CREDENTIAL_PASSWORD))
 			soup_message_set_status (msg, SOUP_STATUS_FORBIDDEN);
 		else
-			soup_auth_authenticate (auth, username, cbdav->priv->password);
+			soup_auth_authenticate (auth, username, e_named_parameters_get (cbdav->priv->credentials, E_SOURCE_CREDENTIAL_PASSWORD));
 
 		g_free (auth_user);
 	}
@@ -1315,7 +1388,9 @@ caldav_credentials_required_sync (ECalBackendCalDAV *cbdav,
 
 	if (success) {
 		success = e_backend_credentials_required_sync (E_BACKEND (cbdav),
-			(first_attempt || !cbdav->priv->password) ? E_SOURCE_CREDENTIALS_REASON_REQUIRED :
+			(first_attempt || !cbdav->priv->credentials ||
+			 !e_named_parameters_exists (cbdav->priv->credentials, E_SOURCE_CREDENTIAL_PASSWORD))
+			? E_SOURCE_CREDENTIALS_REASON_REQUIRED :
 			E_SOURCE_CREDENTIALS_REASON_REJECTED,
 			NULL, 0, NULL, cancellable, error);
 	}
@@ -2983,6 +3058,9 @@ open_calendar_wrapper (ECalBackendCalDAV *cbdav,
 
 	g_return_val_if_fail (cbdav != NULL, FALSE);
 
+	if (!caldav_maybe_prepare_bearer_auth (cbdav, cancellable, error))
+		return FALSE;
+
 	success = caldav_server_open_calendar (cbdav, &server_unreachable, out_certificate_pem, out_certificate_errors, cancellable, &local_error);
 
 	if (first_attempt && g_error_matches (local_error, E_DATA_CAL_ERROR, AuthenticationFailed)) {
@@ -3033,6 +3111,7 @@ caldav_do_open (ECalBackendSync *backend,
 {
 	ECalBackendCalDAV *cbdav;
 	ESourceWebdav *webdav_extension;
+	ESourceAuthentication *auth_extension;
 	ESource *source;
 	gboolean online;
 
@@ -3042,6 +3121,7 @@ caldav_do_open (ECalBackendSync *backend,
 
 	source = e_backend_get_source (E_BACKEND (cbdav));
 	webdav_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND);
+	auth_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
 	e_source_webdav_unset_temporary_ssl_trust (webdav_extension);
 
 	/* let it decide the 'getctag' extension availability again */
@@ -3065,11 +3145,14 @@ caldav_do_open (ECalBackendSync *backend,
 	cbdav->priv->is_google = FALSE;
 
 	if (online) {
-		gchar *certificate_pem = NULL;
+		gchar *certificate_pem = NULL, *auth_method;
 		GTlsCertificateFlags certificate_errors = 0;
 		GError *local_error = NULL;
 
-		if (!open_calendar_wrapper (cbdav, cancellable, &local_error, TRUE, NULL, &certificate_pem, &certificate_errors) &&
+		auth_method = e_source_authentication_dup_method (auth_extension);
+
+		if ((g_strcmp0 (auth_method, "Google") == 0 ||
+		    !open_calendar_wrapper (cbdav, cancellable, &local_error, TRUE, NULL, &certificate_pem, &certificate_errors)) &&
 		    !g_cancellable_is_cancelled (cancellable)) {
 			ESourceCredentialsReason reason = E_SOURCE_CREDENTIALS_REASON_REQUIRED;
 			GError *local_error2 = NULL;
@@ -3090,7 +3173,8 @@ caldav_do_open (ECalBackendSync *backend,
 				/* These errors are treated through the authentication */
 				g_clear_error (&local_error);
 			} else {
-				g_propagate_error (perror, local_error);
+				if (local_error)
+					g_propagate_error (perror, local_error);
 				local_error = NULL;
 			}
 			g_clear_error (&local_error2);
@@ -3098,6 +3182,7 @@ caldav_do_open (ECalBackendSync *backend,
 
 		g_clear_error (&local_error);
 		g_free (certificate_pem);
+		g_free (auth_method);
 	} else {
 		e_cal_backend_set_writable (E_CAL_BACKEND (cbdav), FALSE);
 	}
@@ -5405,23 +5490,14 @@ caldav_authenticate_sync (EBackend *backend,
 {
 	ECalBackendCalDAV *cbdav;
 	ESourceAuthenticationResult result;
-	const gchar *username;
 	GError *local_error = NULL;
 
 	cbdav = E_CAL_BACKEND_CALDAV (backend);
 
 	g_mutex_lock (&cbdav->priv->busy_lock);
 
-	g_free (cbdav->priv->username);
-	cbdav->priv->username = NULL;
-
-	g_free (cbdav->priv->password);
-	cbdav->priv->password = g_strdup (e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_PASSWORD));
-
-	username = e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_USERNAME);
-	if (username && *username) {
-		cbdav->priv->username = g_strdup (username);
-	}
+	e_named_parameters_free (cbdav->priv->credentials);
+	cbdav->priv->credentials = e_named_parameters_new_clone (credentials);
 
 	open_calendar_wrapper (cbdav, cancellable, &local_error, FALSE, NULL, out_certificate_pem, out_certificate_errors);
 
@@ -5432,8 +5508,11 @@ caldav_authenticate_sync (EBackend *backend,
 		g_cond_signal (&cbdav->priv->cond);
 	} else if (g_error_matches (local_error, E_DATA_CAL_ERROR, AuthenticationFailed) ||
 		   g_error_matches (local_error, E_DATA_CAL_ERROR, AuthenticationRequired)) {
+		const gchar *username;
 		gchar *auth_user = NULL;
 
+		username = e_named_parameters_get (cbdav->priv->credentials, E_SOURCE_CREDENTIAL_USERNAME);
+
 		if (!username || !*username) {
 			ESource *source;
 			ESourceAuthentication *auth_extension;
@@ -5500,8 +5579,8 @@ e_cal_backend_caldav_finalize (GObject *object)
 	g_cond_clear (&priv->slave_gone_cond);
 
 	g_free (priv->uri);
-	g_free (priv->username);
-	g_free (priv->password);
+	e_named_parameters_free (priv->credentials);
+	priv->credentials = NULL;
 	g_free (priv->schedule_outbox_url);
 
 	if (priv->ctag_to_store) {
@@ -5523,73 +5602,16 @@ caldav_backend_initable_init (GInitable *initable,
 {
 	ECalBackendCalDAVPrivate *priv;
 	SoupSessionFeature *feature;
-	ESource *source;
-	const gchar *extension_name;
-	gchar *auth_method = NULL;
 	gboolean success = TRUE;
 
 	priv = E_CAL_BACKEND_CALDAV_GET_PRIVATE (initable);
 
-	feature = soup_session_get_feature (
-		priv->session, SOUP_TYPE_AUTH_MANAGER);
+	feature = soup_session_get_feature (priv->session, SOUP_TYPE_AUTH_MANAGER);
 
 	/* Add the "Bearer" auth type to support OAuth 2.0. */
 	soup_session_feature_add_feature (feature, E_TYPE_SOUP_AUTH_BEARER);
 	g_mutex_init (&priv->bearer_auth_error_lock);
 
-	/* Preload the SoupAuthManager with a valid "Bearer" token
-	 * when using OAuth 2.0.  This avoids an extra unauthorized
-	 * HTTP round-trip, which apparently Google doesn't like. */
-
-	source = e_backend_get_source (E_BACKEND (initable));
-
-	extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
-	if (e_source_has_extension (source, extension_name)) {
-		ESourceAuthentication *extension;
-
-		extension = e_source_get_extension (source, extension_name);
-		auth_method = e_source_authentication_dup_method (extension);
-	}
-
-	e_backend_ensure_online_state_updated (E_BACKEND (initable), cancellable);
-
-	if (g_strcmp0 (auth_method, "OAuth2") == 0 &&
-	    e_backend_get_online (E_BACKEND (initable))) {
-		ESourceWebdav *extension;
-		SoupAuth *soup_auth;
-		SoupURI *soup_uri;
-		gchar *access_token = NULL;
-		gint expires_in_seconds = -1;
-
-		extension_name = E_SOURCE_EXTENSION_WEBDAV_BACKEND;
-		extension = e_source_get_extension (source, extension_name);
-		soup_uri = e_source_webdav_dup_soup_uri (extension);
-
-		soup_auth = g_object_new (
-			E_TYPE_SOUP_AUTH_BEARER,
-			SOUP_AUTH_HOST, soup_uri->host, NULL);
-
-		success = e_source_get_oauth2_access_token_sync (
-			source, cancellable, &access_token,
-			&expires_in_seconds, error);
-
-		if (success) {
-			e_soup_auth_bearer_set_access_token (
-				E_SOUP_AUTH_BEARER (soup_auth),
-				access_token, expires_in_seconds);
-
-			soup_auth_manager_use_auth (
-				SOUP_AUTH_MANAGER (feature),
-				soup_uri, soup_auth);
-		}
-
-		g_free (access_token);
-		g_object_unref (soup_auth);
-		soup_uri_free (soup_uri);
-	}
-
-	g_free (auth_method);
-
 	return success;
 }
 
diff --git a/calendar/backends/gtasks/e-cal-backend-gtasks.c b/calendar/backends/gtasks/e-cal-backend-gtasks.c
index 912d122..88cc408 100644
--- a/calendar/backends/gtasks/e-cal-backend-gtasks.c
+++ b/calendar/backends/gtasks/e-cal-backend-gtasks.c
@@ -168,7 +168,8 @@ ecb_gtasks_is_authorized (ECalBackend *backend)
 {
 	ECalBackendGTasks *gtasks = E_CAL_BACKEND_GTASKS (backend);
 
-	if (!gtasks->priv->service)
+	if (!gtasks->priv->service ||
+	    !gtasks->priv->tasklist)
 		return FALSE;
 
 	return gdata_service_is_authorized (GDATA_SERVICE (gtasks->priv->service));
@@ -181,23 +182,24 @@ ecb_gtasks_prepare_tasklist (ECalBackendGTasks *gtasks,
 {
 	ESourceResource *resource;
 	ESource *source;
+	GDataFeed *feed;
+	GDataQuery *query;
 	gchar *id;
 
 	g_return_if_fail (E_IS_CAL_BACKEND_GTASKS (gtasks));
-	g_return_if_fail (ecb_gtasks_is_authorized (E_CAL_BACKEND (gtasks)));
+	g_return_if_fail (gtasks->priv->service != NULL);
+	g_return_if_fail (gdata_service_is_authorized (GDATA_SERVICE (gtasks->priv->service)));
 
 	source = e_backend_get_source (E_BACKEND (gtasks));
 	resource = e_source_get_extension (source, E_SOURCE_EXTENSION_RESOURCE);
 	id = e_source_resource_dup_identity (resource);
 
-	/* If the tasklist ID is not set, then pick the first from the list, most likely the "Default List" */
-	if (!id || !*id) {
-		GDataFeed *feed;
-		GDataQuery *query;
-
-		query = gdata_query_new_with_limits (NULL, 0, 1);
-		feed = gdata_tasks_service_query_all_tasklists (gtasks->priv->service, query, cancellable, NULL, NULL, error);
-		if (feed) {
+	query = gdata_query_new_with_limits (NULL, 0, 1);
+	/* This also verifies that the service can connect to the server with given credentials */
+	feed = gdata_tasks_service_query_all_tasklists (gtasks->priv->service, query, cancellable, NULL, NULL, error);
+	if (feed) {
+		/* If the tasklist ID is not set, then pick the first from the list, most likely the "Default List" */
+		if (!id || !*id) {
 			GList *entries;
 
 			entries = gdata_feed_get_entries (feed);
@@ -208,10 +210,10 @@ ecb_gtasks_prepare_tasklist (ECalBackendGTasks *gtasks,
 					id = g_strdup (gdata_entry_get_id (entry));
 				}
 			}
-			g_clear_object (&feed);
 		}
-		g_object_unref (query);
 	}
+	g_clear_object (&feed);
+	g_object_unref (query);
 
 	if (!id || !*id) {
 		/* But the tests for change will not work */
@@ -641,6 +643,7 @@ ecb_gtasks_time_to_refresh_data_cb (ESource *source,
 
 static gboolean
 ecb_gtasks_request_authorization (ECalBackend *backend,
+				  const ENamedParameters *credentials,
 				  GCancellable *cancellable,
 				  GError **error)
 {
@@ -651,21 +654,17 @@ ecb_gtasks_request_authorization (ECalBackend *backend,
 
 	if (!gtasks->priv->authorizer) {
 		ESource *source;
-		ESourceAuthentication *extension;
 		EGDataOAuth2Authorizer *authorizer;
-		const gchar *extension_name;
-		gchar *method;
 
-		extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
 		source = e_backend_get_source (E_BACKEND (backend));
-		extension = e_source_get_extension (source, extension_name);
-		method = e_source_authentication_dup_method (extension);
 
 		/* Only OAuth2 is supported with Google Tasks */
 		authorizer = e_gdata_oauth2_authorizer_new (source);
 		gtasks->priv->authorizer = GDATA_AUTHORIZER (authorizer);
+	}
 
-		g_free (method);
+	if (E_IS_GDATA_OAUTH2_AUTHORIZER (gtasks->priv->authorizer)) {
+		e_gdata_oauth2_authorizer_set_credentials (E_GDATA_OAUTH2_AUTHORIZER (gtasks->priv->authorizer), credentials);
 	}
 
 	if (!gtasks->priv->service) {
@@ -742,6 +741,71 @@ ecb_gtasks_get_backend_property (ECalBackend *backend,
 }
 
 static void
+ecb_gtasks_update_connection_sync (ECalBackendGTasks *gtasks,
+				   const ENamedParameters *credentials,
+				   GCancellable *cancellable,
+				   GError **error)
+{
+	ECalBackend *backend;
+	gboolean success;
+	GError *local_error = NULL;
+
+	g_return_if_fail (E_IS_CAL_BACKEND_GTASKS (gtasks));
+
+	backend = E_CAL_BACKEND (gtasks);
+
+	success = ecb_gtasks_request_authorization (backend, credentials, cancellable, &local_error);
+	if (success)
+		success = gdata_authorizer_refresh_authorization (gtasks->priv->authorizer, cancellable, &local_error);
+
+	if (success) {
+		e_cal_backend_set_writable (backend, TRUE);
+
+		ecb_gtasks_prepare_tasklist (gtasks, cancellable, &local_error);
+		if (!local_error)
+			ecb_gtasks_start_update (gtasks);
+	} else {
+		e_cal_backend_set_writable (backend, FALSE);
+	}
+
+	if (local_error)
+		g_propagate_error (error, local_error);
+}
+
+static ESourceAuthenticationResult
+ecb_gtasks_authenticate_sync (EBackend *backend,
+			      const ENamedParameters *credentials,
+			      gchar **out_certificate_pem,
+			      GTlsCertificateFlags *out_certificate_errors,
+			      GCancellable *cancellable,
+			      GError **error)
+{
+	ECalBackendGTasks *gtasks;
+	ESourceAuthenticationResult result;
+	GError *local_error = NULL;
+
+	gtasks = E_CAL_BACKEND_GTASKS (backend);
+
+	ecb_gtasks_update_connection_sync (gtasks, credentials, cancellable, &local_error);
+
+	if (local_error == NULL) {
+		result = E_SOURCE_AUTHENTICATION_ACCEPTED;
+
+	} else if (g_error_matches (local_error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_AUTHENTICATION_REQUIRED)) {
+		if (!e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_PASSWORD))
+			result = E_SOURCE_AUTHENTICATION_REQUIRED;
+		else
+			result = E_SOURCE_AUTHENTICATION_REJECTED;
+		g_clear_error (&local_error);
+	} else {
+		result = E_SOURCE_AUTHENTICATION_ERROR;
+		g_propagate_error (error, local_error);
+	}
+
+	return result;
+}
+
+static void
 ecb_gtasks_open (ECalBackend *backend,
 		 EDataCal *cal,
 		 guint32 opid,
@@ -766,19 +830,27 @@ ecb_gtasks_open (ECalBackend *backend,
 	ecb_gtasks_take_cancellable (gtasks, g_cancellable_new ());
 
 	if (e_backend_get_online (E_BACKEND (backend))) {
-		gboolean success;
+		ESource *source;
+		gchar *auth_method = NULL;
+
+		source = e_backend_get_source (E_BACKEND (backend));
 
-		success = ecb_gtasks_request_authorization (backend, cancellable, &local_error);
-		if (success)
-			success = gdata_authorizer_refresh_authorization (gtasks->priv->authorizer, cancellable, &local_error);
+		if (e_source_has_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION)) {
+			ESourceAuthentication *auth_extension;
 
-		if (success) {
-			e_cal_backend_set_writable (backend, TRUE);
+			auth_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
+			auth_method = e_source_authentication_dup_method (auth_extension);
+		}
 
-			ecb_gtasks_prepare_tasklist (gtasks, cancellable, &local_error);
-			if (!local_error)
-				ecb_gtasks_start_update (gtasks);
+		if (g_strcmp0 (auth_method, "Google") == 0) {
+			e_backend_credentials_required_sync (
+				E_BACKEND (backend), E_SOURCE_CREDENTIALS_REASON_REQUIRED,
+				NULL, 0, NULL, cancellable, &local_error);
+		} else {
+			ecb_gtasks_update_connection_sync (gtasks, NULL, cancellable, &local_error);
 		}
+
+		g_free (auth_method);
 	}
 
 	e_data_cal_respond_open (cal, opid, local_error);
@@ -1435,32 +1507,35 @@ static void
 e_cal_backend_gtasks_class_init (ECalBackendGTasksClass *class)
 {
 	GObjectClass *object_class;
-	ECalBackendClass *backend_class;
-
-	object_class = (GObjectClass *) class;
-	backend_class = (ECalBackendClass *) class;
+	EBackendClass *backend_class;
+	ECalBackendClass *cal_backend_class;
 
 	g_type_class_add_private (class, sizeof (ECalBackendGTasksPrivate));
 
+	object_class = (GObjectClass *) class;
 	object_class->constructed = ecb_gtasks_constructed;
 	object_class->dispose = ecb_gtasks_dispose;
 	object_class->finalize = ecb_gtasks_finalize;
 
-	backend_class->get_backend_property = ecb_gtasks_get_backend_property;
-	backend_class->open = ecb_gtasks_open;
-	backend_class->refresh = ecb_gtasks_refresh;
-	backend_class->get_object = ecb_gtasks_get_object;
-	backend_class->get_object_list = ecb_gtasks_get_object_list;
-	backend_class->get_free_busy = ecb_gtasks_get_free_busy;
-	backend_class->create_objects = ecb_gtasks_create_objects;
-	backend_class->modify_objects = ecb_gtasks_modify_objects;
-	backend_class->remove_objects = ecb_gtasks_remove_objects;
-	backend_class->receive_objects = ecb_gtasks_receive_objects;
-	backend_class->send_objects = ecb_gtasks_send_objects;
-	backend_class->get_attachment_uris = ecb_gtasks_get_attachment_uris;
-	backend_class->discard_alarm = ecb_gtasks_discard_alarm;
-	backend_class->start_view = ecb_gtasks_start_view;
-	backend_class->stop_view = ecb_gtasks_stop_view;
-	backend_class->add_timezone = ecb_gtasks_add_timezone;
-	backend_class->shutdown = ecb_gtasks_shutdown;
+	backend_class = (EBackendClass *) class;
+	backend_class->authenticate_sync = ecb_gtasks_authenticate_sync;
+
+	cal_backend_class = (ECalBackendClass *) class;
+	cal_backend_class->get_backend_property = ecb_gtasks_get_backend_property;
+	cal_backend_class->open = ecb_gtasks_open;
+	cal_backend_class->refresh = ecb_gtasks_refresh;
+	cal_backend_class->get_object = ecb_gtasks_get_object;
+	cal_backend_class->get_object_list = ecb_gtasks_get_object_list;
+	cal_backend_class->get_free_busy = ecb_gtasks_get_free_busy;
+	cal_backend_class->create_objects = ecb_gtasks_create_objects;
+	cal_backend_class->modify_objects = ecb_gtasks_modify_objects;
+	cal_backend_class->remove_objects = ecb_gtasks_remove_objects;
+	cal_backend_class->receive_objects = ecb_gtasks_receive_objects;
+	cal_backend_class->send_objects = ecb_gtasks_send_objects;
+	cal_backend_class->get_attachment_uris = ecb_gtasks_get_attachment_uris;
+	cal_backend_class->discard_alarm = ecb_gtasks_discard_alarm;
+	cal_backend_class->start_view = ecb_gtasks_start_view;
+	cal_backend_class->stop_view = ecb_gtasks_stop_view;
+	cal_backend_class->add_timezone = ecb_gtasks_add_timezone;
+	cal_backend_class->shutdown = ecb_gtasks_shutdown;
 }
diff --git a/calendar/backends/gtasks/e-gdata-oauth2-authorizer.c b/calendar/backends/gtasks/e-gdata-oauth2-authorizer.c
index c1c5632..094b30b 100644
--- a/calendar/backends/gtasks/e-gdata-oauth2-authorizer.c
+++ b/calendar/backends/gtasks/e-gdata-oauth2-authorizer.c
@@ -27,6 +27,7 @@ struct _EGDataOAuth2AuthorizerPrivate {
 	/* These members are protected by the global mutex. */
 	gchar *access_token;
 	GHashTable *authorization_domains;
+	ENamedParameters *credentials;
 };
 
 enum {
@@ -137,6 +138,8 @@ gdata_oauth2_authorizer_finalize (GObject *object)
 	g_hash_table_destroy (priv->authorization_domains);
 	g_weak_ref_clear (&priv->source);
 
+	e_named_parameters_free (priv->credentials);
+
 	/* Chain up to parent's finalize() method. */
 	G_OBJECT_CLASS (e_gdata_oauth2_authorizer_parent_class)->
 		finalize (object);
@@ -240,8 +243,8 @@ gdata_oauth2_authorizer_refresh_authorization (GDataAuthorizer *authorizer,
 	g_free (*ptr_access_token);
 	*ptr_access_token = NULL;
 
-	success = e_source_get_oauth2_access_token_sync (
-		source, cancellable, ptr_access_token, NULL, error);
+	success = e_util_get_source_oauth2_access_token_sync (source, oauth2_authorizer->priv->credentials,
+		ptr_access_token, NULL, cancellable, error);
 
 	g_mutex_unlock (&mutex);
 
@@ -323,3 +326,36 @@ e_gdata_oauth2_authorizer_ref_source (EGDataOAuth2Authorizer *authorizer)
 	return g_weak_ref_get (&authorizer->priv->source);
 }
 
+void
+e_gdata_oauth2_authorizer_set_credentials (EGDataOAuth2Authorizer *authorizer,
+					   const ENamedParameters *credentials)
+{
+	g_return_if_fail (E_IS_GDATA_OAUTH2_AUTHORIZER (authorizer));
+
+	g_mutex_lock (&mutex);
+
+	e_named_parameters_free (authorizer->priv->credentials);
+	if (credentials)
+		authorizer->priv->credentials = e_named_parameters_new_clone (credentials);
+	else
+		authorizer->priv->credentials = NULL;
+
+	g_mutex_unlock (&mutex);
+}
+
+ENamedParameters *
+e_gdata_oauth2_authorizer_clone_credentials (EGDataOAuth2Authorizer *authorizer)
+{
+	ENamedParameters *credentials = NULL;
+
+	g_return_val_if_fail (E_IS_GDATA_OAUTH2_AUTHORIZER (authorizer), NULL);
+
+	g_mutex_lock (&mutex);
+
+	if (authorizer->priv->credentials)
+		credentials = e_named_parameters_new_clone (authorizer->priv->credentials);
+
+	g_mutex_unlock (&mutex);
+
+	return credentials;
+}
diff --git a/calendar/backends/gtasks/e-gdata-oauth2-authorizer.h b/calendar/backends/gtasks/e-gdata-oauth2-authorizer.h
index ef908f6..0dfc582 100644
--- a/calendar/backends/gtasks/e-gdata-oauth2-authorizer.h
+++ b/calendar/backends/gtasks/e-gdata-oauth2-authorizer.h
@@ -62,6 +62,12 @@ EGDataOAuth2Authorizer *
 					(ESource *source);
 ESource *	e_gdata_oauth2_authorizer_ref_source
 					(EGDataOAuth2Authorizer *authorizer);
+void		e_gdata_oauth2_authorizer_set_credentials
+					(EGDataOAuth2Authorizer *authorizer,
+					 const ENamedParameters *credentials);
+ENamedParameters *
+		e_gdata_oauth2_authorizer_clone_credentials
+					(EGDataOAuth2Authorizer *authorizer);
 
 G_END_DECLS
 
diff --git a/camel/providers/imapx/camel-imapx-command.c b/camel/providers/imapx/camel-imapx-command.c
index 0677736..0388c7d 100644
--- a/camel/providers/imapx/camel-imapx-command.c
+++ b/camel/providers/imapx/camel-imapx-command.c
@@ -390,6 +390,8 @@ camel_imapx_command_add_part (CamelIMAPXCommand *ic,
 		/* we presume we'll need to get additional data only if we're not authenticated yet */
 		g_object_ref (ob);
 		mechanism = camel_sasl_get_mechanism (CAMEL_SASL (ob));
+		if (g_strcmp0 (mechanism, "Google") == 0)
+			mechanism = "XOAUTH2";
 		g_string_append (buffer, mechanism);
 		if (!camel_sasl_get_authenticated ((CamelSasl *) ob))
 			type |= CAMEL_IMAPX_COMMAND_CONTINUATION;
diff --git a/camel/providers/imapx/camel-imapx-server.c b/camel/providers/imapx/camel-imapx-server.c
index 6f74fc0..7fa98d1 100644
--- a/camel/providers/imapx/camel-imapx-server.c
+++ b/camel/providers/imapx/camel-imapx-server.c
@@ -2874,7 +2874,8 @@ camel_imapx_server_authenticate_sync (CamelIMAPXServer *is,
 	g_object_unref (settings);
 
 	if (mechanism != NULL) {
-		if (is->priv->cinfo && !g_hash_table_lookup (is->priv->cinfo->auth_types, mechanism)) {
+		if (is->priv->cinfo && !g_hash_table_lookup (is->priv->cinfo->auth_types, mechanism) && (
+		    !g_str_equal (mechanism, "Google") || !g_hash_table_lookup (is->priv->cinfo->auth_types, "XOAUTH2"))) {
 			g_set_error (
 				error, CAMEL_SERVICE_ERROR,
 				CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
diff --git a/camel/providers/smtp/camel-smtp-transport.c b/camel/providers/smtp/camel-smtp-transport.c
index cba3c8b..4d5e746 100644
--- a/camel/providers/smtp/camel-smtp-transport.c
+++ b/camel/providers/smtp/camel-smtp-transport.c
@@ -526,7 +526,7 @@ smtp_transport_connect_sync (CamelService *service,
 
 		session = camel_service_ref_session (service);
 
-		if (g_hash_table_lookup (transport->authtypes, mechanism)) {
+		if (g_hash_table_lookup (transport->authtypes, g_strcmp0 (mechanism, "Google") == 0 ? "XOAUTH2" : mechanism)) {
 			success = camel_session_authenticate_sync (
 				session, service, mechanism,
 				cancellable, error);
@@ -633,7 +633,7 @@ smtp_transport_authenticate_sync (CamelService *service,
 	if (challenge) {
 		auth_challenge = TRUE;
 		cmdbuf = g_strdup_printf (
-			"AUTH %s %s\r\n", mechanism, challenge);
+			"AUTH %s %s\r\n", g_strcmp0 (mechanism, "Google") == 0 ? "XOAUTH2" : mechanism, challenge);
 		g_free (challenge);
 	} else if (local_error) {
 		d (fprintf (stderr, "[SMTP] SASL challenge failed: %s", local_error->message));
diff --git a/configure.ac b/configure.ac
index cea0a47..8938af4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -58,6 +58,8 @@ m4_define([goa_minimum_version], [3.8])
 m4_define([gweather_minimum_version], [3.10])
 m4_define([libaccounts_glib_minimum_version], [1.4])
 m4_define([libsignon_glib_minimum_version], [1.8])
+m4_define([webkitgtk_minimum_version], [2.4.9])
+m4_define([json_glib_minimum_version], [1.0.4])
 
 AC_SUBST([BASE_VERSION],[base_version])
 AC_SUBST([API_VERSION],[api_version])
@@ -471,6 +473,42 @@ if test "x$enable_gtk" = xyes; then
 fi
 AM_CONDITIONAL(HAVE_GTK, [test x$enable_gtk = xyes])
 
+dnl **********************************
+dnl Check for WebKitGTK+ and json-glib
+dnl **********************************
+AC_ARG_ENABLE([google-auth],
+	[AS_HELP_STRING([--enable-google-auth],
+	[enable Google authentication (default=yes)])],
+	[enable_google_auth=$enableval], [enable_google_auth=yes])
+AC_MSG_CHECKING([if Google authentication support is enabled])
+AC_MSG_RESULT([$enable_google_auth])
+if test "x$enable_google_auth" = "xyes"; then
+	PKG_CHECK_MODULES(
+		[GOOGLE_AUTH], [webkitgtk-3.0 >= webkitgtk_minimum_version
+				json-glib-1.0 >= json_glib_minimum_version],,
+		[AC_MSG_ERROR([
+
+	Libraries to support Google authentication prompts not found,
+	or versions not new enough.
+
+	If you want to disable Google authentication support,
+	please append --disable-google-auth to configure.
+
+	])])
+
+	AC_DEFINE(ENABLE_GOOGLE_AUTH, 1, [Define to 1 if Google autentication support is enabled.])
+	AC_ARG_WITH(google-client-id, [AS_HELP_STRING([--with-google-client-id], [Google OAuth 2.0 client id])], [], [])
+	if test "x$with_google_client_id" = "x"; then
+		with_google_client_id=590402290962-2i0b7rqma8b9nmtfrcp7fa06g6cf7g74.apps.googleusercontent.com
+	fi
+	AC_ARG_WITH(google-client-secret, [AS_HELP_STRING([--with-google-client-secret], [Google OAuth 2.0 client secret])], [], [])
+	if test "x$with_google_client_secret" = "x"; then
+		with_google_client_secret=mtfUe5W8Aal9DcgVipOY1T9G
+	fi
+	AC_DEFINE_UNQUOTED(GOOGLE_CLIENT_ID, ["$with_google_client_id"], [Define Google OAuth 2.0 Client ID to use])
+	AC_DEFINE_UNQUOTED(GOOGLE_CLIENT_SECRET, ["$with_google_client_secret"], [Define Google OAuth 2.0 Client Secret to use])
+fi
+AM_CONDITIONAL(ENABLE_GOOGLE_AUTH, [test x$enable_google_auth = xyes])
 
 dnl ******************************************
 dnl Check whether to build examples/demos
@@ -1884,6 +1922,7 @@ echo "
 	Vala bindings:		$enable_vala_bindings
 	GNOME Online Accounts	$enable_goa
 	Ubuntu Online Accounts	$enable_uoa
+	Google Authentication	$enable_google_auth
 	Google Contacts		$enable_google
 	Google Tasks		$have_gdata_0_15_1
 	GTK+:  			$enable_gtk
diff --git a/libedataserver/Makefile.am b/libedataserver/Makefile.am
index f19fdfc..57870d7 100644
--- a/libedataserver/Makefile.am
+++ b/libedataserver/Makefile.am
@@ -38,6 +38,7 @@ SHARED_COMPILER_FLAGS = \
 	$(SOUP_CFLAGS) \
 	$(CODE_COVERAGE_CFLAGS) \
 	$(ICU_CFLAGS) \
+	$(GOOGLE_AUTH_CFLAGS) \
 	$(NULL)
 
 libedataserver_1_2_la_CPPFLAGS = \
@@ -82,6 +83,7 @@ libedataserver_1_2_la_SOURCES = \
 	e-source-contacts.c \
 	e-source-credentials-provider.c \
 	e-source-credentials-provider-impl.c \
+	e-source-credentials-provider-impl-google.c \
 	e-source-credentials-provider-impl-password.c \
 	e-source-goa.c \
 	e-source-ldap.c \
@@ -129,6 +131,7 @@ libedataserver_1_2_la_LIBADD = \
 	$(SOCKET_LIBS) \
 	$(SOUP_LIBS) \
 	$(ICU_LIBS) \
+	$(GOOGLE_AUTH_LIBS) \
 	$(NULL)
 
 libedataserver_1_2_la_LDFLAGS = \
@@ -173,6 +176,7 @@ libedataserverinclude_HEADERS = \
 	e-source-contacts.h \
 	e-source-credentials-provider.h \
 	e-source-credentials-provider-impl.h \
+	e-source-credentials-provider-impl-google.h \
 	e-source-credentials-provider-impl-password.h \
 	e-source-enums.h \
 	e-source-enumtypes.h \
diff --git a/libedataserver/e-data-server-util.c b/libedataserver/e-data-server-util.c
index 60ce101..7dc777c 100644
--- a/libedataserver/e-data-server-util.c
+++ b/libedataserver/e-data-server-util.c
@@ -33,6 +33,8 @@
 #include <glib-object.h>
 
 #include "e-source.h"
+#include "e-source-authentication.h"
+#include "e-source-credentials-provider-impl-google.h"
 #include "e-source-enumtypes.h"
 #include "e-source-registry.h"
 #include "camel/camel.h"
@@ -2834,3 +2836,39 @@ e_util_get_source_full_name (ESourceRegistry *registry,
 
 	return g_string_free (fullname, FALSE);
 }
+
+gboolean
+e_util_get_source_oauth2_access_token_sync (struct _ESource *source,
+					    const ENamedParameters *credentials,
+					    gchar **out_access_token,
+					    gint *out_expires_in_seconds,
+					    GCancellable *cancellable,
+					    GError **error)
+{
+	gchar *auth_method = NULL;
+	gboolean success = FALSE;
+
+	g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+
+	if (e_source_has_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION)) {
+		ESourceAuthentication *extension;
+
+		extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
+		auth_method = e_source_authentication_dup_method (extension);
+	}
+
+	if (g_strcmp0 (auth_method, "OAuth2") == 0) {
+		success = e_source_get_oauth2_access_token_sync (
+			source, cancellable, out_access_token,
+			out_expires_in_seconds, error);
+	} else if (g_strcmp0 (auth_method, "Google") == 0) {
+		success = TRUE;
+
+		e_source_credentials_google_util_extract_from_credentials (
+			credentials, out_access_token, out_expires_in_seconds);
+	}
+
+	g_free (auth_method);
+
+	return success;
+}
diff --git a/libedataserver/e-data-server-util.h b/libedataserver/e-data-server-util.h
index 42115a2..6b26f12 100644
--- a/libedataserver/e-data-server-util.h
+++ b/libedataserver/e-data-server-util.h
@@ -273,7 +273,13 @@ void		e_type_traverse			(GType parent_type,
 
 gchar *		e_util_get_source_full_name	(struct _ESourceRegistry *registry,
 						 struct _ESource *source);
-
+gboolean	e_util_get_source_oauth2_access_token_sync
+						(struct _ESource *source,
+						 const ENamedParameters *credentials,
+						 gchar **out_access_token,
+						 gint *out_expires_in_seconds,
+						 GCancellable *cancellable,
+						 GError **error);
 G_END_DECLS
 
 #endif /* E_DATA_SERVER_UTIL_H */
diff --git a/libedataserver/e-source-credentials-provider-impl-google.c b/libedataserver/e-source-credentials-provider-impl-google.c
new file mode 100644
index 0000000..5761a17
--- /dev/null
+++ b/libedataserver/e-source-credentials-provider-impl-google.c
@@ -0,0 +1,475 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
+ *
+ * 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.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+
+#include <libedataserver/libedataserver.h>
+
+#ifdef ENABLE_GOOGLE_AUTH
+#include <json-glib/json-glib.h>
+#endif
+
+#include "e-source-credentials-provider-impl-google.h"
+
+struct _ESourceCredentialsProviderImplGooglePrivate {
+	gboolean dummy;
+};
+
+G_DEFINE_TYPE (ESourceCredentialsProviderImplGoogle, e_source_credentials_provider_impl_google, E_TYPE_SOURCE_CREDENTIALS_PROVIDER_IMPL)
+
+static gboolean
+e_source_credentials_provider_impl_google_can_process (ESourceCredentialsProviderImpl *provider_impl,
+						       ESource *source)
+{
+	g_return_val_if_fail (E_IS_SOURCE_CREDENTIALS_PROVIDER_IMPL_GOOGLE (provider_impl), FALSE);
+	g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+
+	return e_source_credentials_google_util_generate_secret_uid (source, NULL);
+}
+
+static gboolean
+e_source_credentials_provider_impl_google_can_store (ESourceCredentialsProviderImpl *provider_impl)
+{
+	g_return_val_if_fail (E_IS_SOURCE_CREDENTIALS_PROVIDER_IMPL_GOOGLE (provider_impl), FALSE);
+
+	return TRUE;
+}
+
+static gboolean
+e_source_credentials_provider_impl_google_can_prompt (ESourceCredentialsProviderImpl *provider_impl)
+{
+	g_return_val_if_fail (E_IS_SOURCE_CREDENTIALS_PROVIDER_IMPL_GOOGLE (provider_impl), FALSE);
+
+	return TRUE;
+}
+
+static gboolean
+e_source_credentials_google_util_get_access_token_from_secret (const gchar *secret,
+							       gchar **out_access_token)
+{
+	gchar *expires_after = NULL, *access_token = NULL;
+	gboolean success = FALSE;
+
+	if (!secret)
+		return FALSE;
+
+	if (!e_source_credentials_google_util_decode_from_secret (secret,
+		E_GOOGLE_SECRET_ACCESS_TOKEN, &access_token,
+		E_GOOGLE_SECRET_EXPIRES_AFTER, &expires_after,
+		NULL))
+		return FALSE;
+
+	if (access_token && expires_after &&
+	    g_ascii_strtoll (expires_after, NULL, 10) > g_get_real_time () / G_USEC_PER_SEC) {
+		success = TRUE;
+
+		if (out_access_token) {
+			*out_access_token = access_token;
+			access_token = NULL;
+		}
+	}
+
+	g_free (expires_after);
+	g_free (access_token);
+
+	return success;
+}
+
+static gboolean
+e_source_credentials_provider_impl_google_lookup_sync (ESourceCredentialsProviderImpl *provider_impl,
+						       ESource *source,
+						       GCancellable *cancellable,
+						       ENamedParameters **out_credentials,
+						       GError **error)
+{
+	gchar *uid = NULL, *secret = NULL, *access_token = NULL;
+
+	g_return_val_if_fail (E_IS_SOURCE_CREDENTIALS_PROVIDER_IMPL_GOOGLE (provider_impl), FALSE);
+	g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+	g_return_val_if_fail (out_credentials != NULL, FALSE);
+
+	*out_credentials = NULL;
+
+	if (!e_source_credentials_google_is_supported ()) {
+		g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+			_("Google authentication is not supported"));
+		return FALSE;
+	}
+
+	if (!e_source_credentials_google_util_generate_secret_uid (source, &uid)) {
+		g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+			/* Translators: The first %s is a display name of the source, the second is its UID. */
+			_("Source '%s' (%s) is not a valid Google source"),
+			e_source_get_display_name (source),
+			e_source_get_uid (source));
+		return FALSE;
+	}
+
+	if (!e_secret_store_lookup_sync (uid, &secret, cancellable, error)) {
+		g_free (uid);
+		return FALSE;
+	}
+
+	g_free (uid);
+
+	if (!secret) {
+		g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, _("Google secret not found"));
+		return FALSE;
+	}
+
+	if (e_source_credentials_google_util_get_access_token_from_secret (secret, &access_token)) {
+		*out_credentials = e_named_parameters_new ();
+		e_named_parameters_set (*out_credentials, E_SOURCE_CREDENTIAL_GOOGLE_SECRET, secret);
+		e_named_parameters_set (*out_credentials, E_SOURCE_CREDENTIAL_PASSWORD, access_token);
+	}
+
+	e_util_safe_free_string (access_token);
+	e_util_safe_free_string (secret);
+
+	return *out_credentials != NULL;
+}
+
+static gboolean
+e_source_credentials_provider_impl_google_store_sync (ESourceCredentialsProviderImpl *provider_impl,
+						      ESource *source,
+						      const ENamedParameters *credentials,
+						      gboolean permanently,
+						      GCancellable *cancellable,
+						      GError **error)
+{
+	gchar *uid = NULL, *label;
+	gboolean success;
+
+	g_return_val_if_fail (E_IS_SOURCE_CREDENTIALS_PROVIDER_IMPL_GOOGLE (provider_impl), FALSE);
+	g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+	g_return_val_if_fail (credentials != NULL, FALSE);
+	g_return_val_if_fail (e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_GOOGLE_SECRET) != NULL, FALSE);
+
+	if (!e_source_credentials_google_is_supported ()) {
+		g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+			_("Google authentication is not supported"));
+		return FALSE;
+	}
+
+	if (!e_source_credentials_google_util_generate_secret_uid (source, &uid)) {
+		g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+			/* Translators: The first %s is a display name of the source, the second is its UID. */
+			_("Source '%s' (%s) is not a valid Google source"),
+			e_source_get_display_name (source),
+			e_source_get_uid (source));
+		return FALSE;
+	}
+
+	label = g_strdup_printf ("Evolution Data Source - %s", strstr (uid, "::") + 2);
+
+	success = e_secret_store_store_sync (uid,
+		e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_GOOGLE_SECRET),
+		label, permanently, cancellable, error);
+
+	g_free (label);
+	g_free (uid);
+
+	return success;
+}
+
+static gboolean
+e_source_credentials_provider_impl_google_delete_sync (ESourceCredentialsProviderImpl *provider_impl,
+							 ESource *source,
+							 GCancellable *cancellable,
+							 GError **error)
+{
+	gchar *uid = NULL;
+	gboolean success;
+
+	g_return_val_if_fail (E_IS_SOURCE_CREDENTIALS_PROVIDER_IMPL_GOOGLE (provider_impl), FALSE);
+	g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+
+	if (!e_source_credentials_google_is_supported ()) {
+		g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+			_("Google authentication is not supported"));
+		return FALSE;
+	}
+
+	if (!e_source_credentials_google_util_generate_secret_uid (source, &uid)) {
+		g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+			/* Translators: The first %s is a display name of the source, the second is its UID. */
+			_("Source '%s' (%s) is not a valid Google source"),
+			e_source_get_display_name (source),
+			e_source_get_uid (source));
+		return FALSE;
+	}
+
+	success = e_secret_store_delete_sync (uid, cancellable, error);
+
+	g_free (uid);
+
+	return success;
+}
+
+static void
+e_source_credentials_provider_impl_google_class_init (ESourceCredentialsProviderImplGoogleClass *klass)
+{
+	ESourceCredentialsProviderImplClass *impl_class;
+
+	g_type_class_add_private (klass, sizeof (ESourceCredentialsProviderImplGooglePrivate));
+
+	impl_class = E_SOURCE_CREDENTIALS_PROVIDER_IMPL_CLASS (klass);
+	impl_class->can_process = e_source_credentials_provider_impl_google_can_process;
+	impl_class->can_store = e_source_credentials_provider_impl_google_can_store;
+	impl_class->can_prompt = e_source_credentials_provider_impl_google_can_prompt;
+	impl_class->lookup_sync = e_source_credentials_provider_impl_google_lookup_sync;
+	impl_class->store_sync = e_source_credentials_provider_impl_google_store_sync;
+	impl_class->delete_sync = e_source_credentials_provider_impl_google_delete_sync;
+}
+
+static void
+e_source_credentials_provider_impl_google_init (ESourceCredentialsProviderImplGoogle *provider_impl)
+{
+	provider_impl->priv = G_TYPE_INSTANCE_GET_PRIVATE (provider_impl,
+		E_TYPE_SOURCE_CREDENTIALS_PROVIDER_IMPL_GOOGLE, ESourceCredentialsProviderImplGooglePrivate);
+}
+
+gboolean
+e_source_credentials_google_is_supported (void)
+{
+#ifdef ENABLE_GOOGLE_AUTH
+	return TRUE;
+#else
+	return FALSE;
+#endif
+}
+
+gboolean
+e_source_credentials_google_util_generate_secret_uid (ESource *source,
+						      gchar **out_uid)
+{
+	ESourceAuthentication *authentication_extension;
+	gchar *auth_method, *user;
+	gboolean is_google_auth;
+
+	g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+
+	if (out_uid)
+		*out_uid = NULL;
+
+	if (!e_source_has_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION))
+		return FALSE;
+
+	authentication_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
+	auth_method = e_source_authentication_dup_method (authentication_extension);
+	is_google_auth = g_strcmp0 (auth_method, "Google") == 0;
+
+	g_free (auth_method);
+
+	if (!is_google_auth)
+		return FALSE;
+
+	user = e_source_authentication_dup_user (authentication_extension);
+	if (!user || !*user) {
+		g_free (user);
+		return FALSE;
+	}
+
+	if (out_uid)
+		*out_uid = g_strdup_printf ("OAuth2::Google[%s]", user);
+
+	g_free (user);
+
+	return TRUE;
+}
+
+gboolean
+e_source_credentials_google_util_encode_to_secret (gchar **out_secret,
+						   const gchar *key1_name,
+						   const gchar *value1,
+						   ...)
+{
+#ifdef ENABLE_GOOGLE_AUTH
+	JsonBuilder *builder;
+	JsonNode *node;
+	const gchar *key, *value;
+	va_list va;
+
+	g_return_val_if_fail (out_secret != NULL, FALSE);
+	g_return_val_if_fail (key1_name != NULL, FALSE);
+	g_return_val_if_fail (value1 != NULL, FALSE);
+
+	*out_secret = NULL;
+
+	builder = json_builder_new ();
+
+	va_start (va, value1);
+	key = key1_name;
+	value = value1;
+
+	json_builder_begin_object (builder);
+
+	while (key && value) {
+		json_builder_set_member_name (builder, key);
+		json_builder_add_string_value (builder, value);
+
+		key = va_arg (va, const gchar *);
+		if (!key)
+			break;
+
+		value = va_arg (va, const gchar *);
+		g_warn_if_fail (value != NULL);
+	}
+
+	va_end (va);
+
+	json_builder_end_object (builder);
+	node = json_builder_get_root (builder);
+
+	g_object_unref (builder);
+
+	if (node) {
+		JsonGenerator *generator;
+
+		generator = json_generator_new ();
+		json_generator_set_root (generator, node);
+
+		*out_secret = json_generator_to_data (generator, NULL);
+
+		g_object_unref (generator);
+		json_node_free (node);
+	}
+
+	return *out_secret != NULL;
+#else
+	return FALSE;
+#endif
+}
+
+gboolean
+e_source_credentials_google_util_decode_from_secret (const gchar *secret,
+						     const gchar *key1_name,
+						     gchar **out_value1,
+						     ...)
+{
+#ifdef ENABLE_GOOGLE_AUTH
+	JsonParser *parser;
+	JsonReader *reader;
+	const gchar *key;
+	gchar **out_value;
+	va_list va;
+	GError *error = NULL;
+
+	g_return_val_if_fail (key1_name != NULL, FALSE);
+	g_return_val_if_fail (out_value1 != NULL, FALSE);
+
+	if (!secret)
+		return FALSE;
+
+	parser = json_parser_new ();
+	if (!json_parser_load_from_data (parser, secret, -1, &error)) {
+		g_object_unref (parser);
+
+		g_debug ("%s: Failed to parse secret '%s': %s", G_STRFUNC, secret, error ? error->message : "Unknown error");
+		g_clear_error (&error);
+
+		return FALSE;
+	}
+
+	reader = json_reader_new (json_parser_get_root (parser));
+	key = key1_name;
+	out_value = out_value1;
+
+	va_start (va, out_value1);
+
+	while (key && out_value) {
+		*out_value = NULL;
+
+		if (json_reader_read_member (reader, key)) {
+			*out_value = g_strdup (json_reader_get_string_value (reader));
+			if (!*out_value) {
+				const GError *reader_error = json_reader_get_error (reader);
+
+				if (g_error_matches (reader_error, JSON_READER_ERROR, JSON_READER_ERROR_INVALID_TYPE)) {
+					gint64 iv64;
+
+					json_reader_end_member (reader);
+
+					iv64 = json_reader_get_int_value (reader);
+
+					if (!json_reader_get_error (reader))
+						*out_value = g_strdup_printf ("%" G_GINT64_FORMAT, iv64);
+				}
+			}
+		}
+
+		json_reader_end_member (reader);
+
+		key = va_arg (va, const gchar *);
+		if (!key)
+			break;
+
+		out_value = va_arg (va, gchar **);
+		g_warn_if_fail (out_value != NULL);
+	}
+
+	g_object_unref (reader);
+	g_object_unref (parser);
+	va_end (va);
+
+	return TRUE;
+#else
+	return FALSE;
+#endif
+}
+
+gboolean
+e_source_credentials_google_util_extract_from_credentials (const ENamedParameters *credentials,
+							   gchar **out_access_token,
+							   gint *out_expires_in_seconds)
+{
+	const gchar *secret;
+	gchar *expires_after = NULL;
+	gint64 expires_after_tm, now;
+
+	if (!credentials ||
+	    !e_named_parameters_exists (credentials, E_SOURCE_CREDENTIAL_GOOGLE_SECRET) ||
+	    !e_named_parameters_exists (credentials, E_SOURCE_CREDENTIAL_PASSWORD))
+		return FALSE;
+
+	secret = e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_GOOGLE_SECRET);
+
+	if (!e_source_credentials_google_util_decode_from_secret (secret, E_GOOGLE_SECRET_EXPIRES_AFTER, &expires_after, NULL) ||
+	    !expires_after)
+		return FALSE;
+
+	expires_after_tm = g_ascii_strtoll (expires_after, NULL, 10);
+	g_free (expires_after);
+
+	if (out_access_token)
+		*out_access_token = g_strdup (e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_PASSWORD));
+
+	if (out_expires_in_seconds) {
+		now = g_get_real_time () / G_USEC_PER_SEC;
+		if (now < expires_after_tm)
+			now = expires_after_tm;
+
+		*out_expires_in_seconds = (gint) (expires_after_tm - now);
+	}
+
+	return TRUE;
+}
diff --git a/libedataserver/e-source-credentials-provider-impl-google.h b/libedataserver/e-source-credentials-provider-impl-google.h
new file mode 100644
index 0000000..4181c69
--- /dev/null
+++ b/libedataserver/e-source-credentials-provider-impl-google.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
+ *
+ * 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.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#if !defined (__LIBEDATASERVER_H_INSIDE__) && !defined (LIBEDATASERVER_COMPILATION)
+#error "Only <libedataserver/libedataserver.h> should be included directly."
+#endif
+
+#ifndef E_SOURCE_CREDENTIALS_PROVIDER_IMPL_GOOGLE_H
+#define E_SOURCE_CREDENTIALS_PROVIDER_IMPL_GOOGLE_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <libedataserver/e-source.h>
+#include <libedataserver/e-source-credentials-provider-impl.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SOURCE_CREDENTIALS_PROVIDER_IMPL_GOOGLE \
+	(e_source_credentials_provider_impl_google_get_type ())
+#define E_SOURCE_CREDENTIALS_PROVIDER_IMPL_GOOGLE(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_SOURCE_CREDENTIALS_PROVIDER_IMPL_GOOGLE, ESourceCredentialsProviderImplGoogle))
+#define E_SOURCE_CREDENTIALS_PROVIDER_IMPL_GOOGLE_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_SOURCE_CREDENTIALS_PROVIDER_IMPL_GOOGLE, ESourceCredentialsProviderImplGoogleClass))
+#define E_IS_SOURCE_CREDENTIALS_PROVIDER_IMPL_GOOGLE(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_SOURCE_CREDENTIALS_PROVIDER_IMPL_GOOGLE))
+#define E_IS_SOURCE_CREDENTIALS_PROVIDER_IMPL_GOOGLE_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_SOURCE_CREDENTIALS_PROVIDER_IMPL_GOOGLE))
+#define E_SOURCE_CREDENTIALS_PROVIDER_IMPL_GOOGLE_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_SOURCE_CREDENTIALS_PROVIDER_IMPL_GOOGLE, ESourceCredentialsProviderImplGoogleClass))
+
+/* The key names used in crendetials ENamedParameters structure */
+#define E_SOURCE_CREDENTIAL_GOOGLE_SECRET "Google-secret"
+
+/* Secret key names, saved by the code; not the names returned by the Google server */
+#define E_GOOGLE_SECRET_REFRESH_TOKEN "refresh_token"
+#define E_GOOGLE_SECRET_ACCESS_TOKEN "access_token"
+#define E_GOOGLE_SECRET_EXPIRES_AFTER "expires_after"
+
+G_BEGIN_DECLS
+
+typedef struct _ESourceCredentialsProviderImplGoogle ESourceCredentialsProviderImplGoogle;
+typedef struct _ESourceCredentialsProviderImplGoogleClass ESourceCredentialsProviderImplGoogleClass;
+typedef struct _ESourceCredentialsProviderImplGooglePrivate ESourceCredentialsProviderImplGooglePrivate;
+
+/**
+ * ESourceCredentialsProviderImplGoogle:
+ *
+ * Google based credentials provider implementation.
+ *
+ * Since: 3.20
+ **/
+struct _ESourceCredentialsProviderImplGoogle {
+	ESourceCredentialsProviderImpl parent;
+	ESourceCredentialsProviderImplGooglePrivate *priv;
+};
+
+struct _ESourceCredentialsProviderImplGoogleClass {
+	ESourceCredentialsProviderImplClass parent_class;
+};
+
+GType		e_source_credentials_provider_impl_google_get_type	(void);
+
+gboolean	e_source_credentials_google_is_supported		(void);
+gboolean	e_source_credentials_google_util_generate_secret_uid	(ESource *source,
+									 gchar **out_uid);
+gboolean	e_source_credentials_google_util_encode_to_secret	(gchar **out_secret,
+									 const gchar *key1_name,
+									 const gchar *value1,
+									 ...) G_GNUC_NULL_TERMINATED;
+gboolean	e_source_credentials_google_util_decode_from_secret	(const gchar *secret,
+									 const gchar *key1_name,
+									 gchar **out_value1,
+									 ...) G_GNUC_NULL_TERMINATED;
+gboolean	e_source_credentials_google_util_extract_from_credentials
+									(const ENamedParameters *credentials,
+									 gchar **out_access_token,
+									 gint *out_expires_in_seconds);
+G_END_DECLS
+
+#endif /* E_SOURCE_CREDENTIALS_PROVIDER_IMPL_GOOGLE_H */
diff --git a/libedataserver/e-source-credentials-provider.c b/libedataserver/e-source-credentials-provider.c
index 2d3abfc..93a466e 100644
--- a/libedataserver/e-source-credentials-provider.c
+++ b/libedataserver/e-source-credentials-provider.c
@@ -35,6 +35,7 @@
 
 /* built-in source credentials provider implementations */
 #include "e-source-credentials-provider-impl-password.h"
+#include "e-source-credentials-provider-impl-google.h"
 
 struct _ESourceCredentialsProviderPrivate {
 	GWeakRef registry; /* The property can hold both client and server-side registry */
@@ -201,6 +202,7 @@ e_source_credentials_provider_class_init (ESourceCredentialsProviderClass *class
 
 	/* Ensure built-in credential providers implementation types */
 	g_type_ensure (E_TYPE_SOURCE_CREDENTIALS_PROVIDER_IMPL_PASSWORD);
+	g_type_ensure (E_TYPE_SOURCE_CREDENTIALS_PROVIDER_IMPL_GOOGLE);
 }
 
 static void
diff --git a/libedataserver/e-webdav-discover.c b/libedataserver/e-webdav-discover.c
index 0fc13bf..5ff6b35 100644
--- a/libedataserver/e-webdav-discover.c
+++ b/libedataserver/e-webdav-discover.c
@@ -30,6 +30,7 @@
 #include "e-soup-auth-bearer.h"
 #include "e-soup-ssl-trust.h"
 #include "e-source-authentication.h"
+#include "e-source-credentials-provider-impl-google.h"
 #include "e-source-webdav.h"
 #include "e-webdav-discover.h"
 
@@ -337,6 +338,31 @@ e_webdav_discover_get_xpath_string (xmlXPathContextPtr xp_ctx,
 	return string;
 }
 
+static gboolean
+e_webdav_discover_setup_bearer_auth (ESource *source,
+				     const ENamedParameters *credentials,
+				     ESoupAuthBearer *bearer,
+				     GCancellable *cancellable,
+				     GError **error)
+{
+	gchar *access_token = NULL;
+	gint expires_in_seconds = -1;
+	gboolean success = FALSE;
+
+	g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+	g_return_val_if_fail (credentials != NULL, FALSE);
+
+	success = e_util_get_source_oauth2_access_token_sync (source, credentials,
+		&access_token, &expires_in_seconds, cancellable, error);
+
+	if (success)
+		e_soup_auth_bearer_set_access_token (bearer, access_token, expires_in_seconds);
+
+	g_free (access_token);
+
+	return success;
+}
+
 typedef struct _AuthenticateData {
 	ESource *source;
 	const ENamedParameters *credentials;
@@ -357,26 +383,16 @@ e_webdav_discover_authenticate_cb (SoupSession *session,
 		return;
 
 	if (E_IS_SOUP_AUTH_BEARER (auth)) {
-		gchar *access_token = NULL;
-		gint expires_in_seconds = -1;
 		GError *local_error = NULL;
 
-		e_source_get_oauth2_access_token_sync (
-			auth_data->source, NULL, &access_token,
-			&expires_in_seconds, &local_error);
-
-		e_soup_auth_bearer_set_access_token (
-			E_SOUP_AUTH_BEARER (auth),
-			access_token, expires_in_seconds);
+		e_webdav_discover_setup_bearer_auth (auth_data->source, auth_data->credentials,
+			E_SOUP_AUTH_BEARER (auth), NULL, &local_error);
 
 		if (local_error != NULL) {
 			soup_message_set_status_full (msg, SOUP_STATUS_FORBIDDEN, local_error->message);
 
 			g_error_free (local_error);
 		}
-
-		g_free (access_token);
-
 	} else {
 		gchar *auth_user = NULL;
 
@@ -1792,28 +1808,20 @@ e_webdav_discover_sources_sync (ESource *source,
 		auth_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
 		auth_method = e_source_authentication_dup_method (auth_extension);
 
-		if (g_strcmp0 (auth_method, "OAuth2") == 0) {
+		if (g_strcmp0 (auth_method, "OAuth2") == 0 || g_strcmp0 (auth_method, "Google") == 0) {
 			SoupAuth *soup_auth;
-			gchar *access_token = NULL;
-			gint expires_in_seconds = -1;
 
 			soup_auth = g_object_new (E_TYPE_SOUP_AUTH_BEARER, SOUP_AUTH_HOST, soup_uri->host, NULL);
 
-			success = e_source_get_oauth2_access_token_sync (
-				source, cancellable, &access_token,
-				&expires_in_seconds, error);
+			success = e_webdav_discover_setup_bearer_auth (source, credentials,
+				E_SOUP_AUTH_BEARER (soup_auth), cancellable, error);
 
 			if (success) {
-				e_soup_auth_bearer_set_access_token (
-					E_SOUP_AUTH_BEARER (soup_auth),
-					access_token, expires_in_seconds);
-
 				soup_auth_manager_use_auth (
 					SOUP_AUTH_MANAGER (feature),
 					soup_uri, soup_auth);
 			}
 
-			g_free (access_token);
 			g_object_unref (soup_auth);
 		}
 
diff --git a/libedataserver/libedataserver.h b/libedataserver/libedataserver.h
index 8410d81..4c6e053 100644
--- a/libedataserver/libedataserver.h
+++ b/libedataserver/libedataserver.h
@@ -54,6 +54,7 @@
 #include <libedataserver/e-source-contacts.h>
 #include <libedataserver/e-source-credentials-provider.h>
 #include <libedataserver/e-source-credentials-provider-impl.h>
+#include <libedataserver/e-source-credentials-provider-impl-google.h>
 #include <libedataserver/e-source-credentials-provider-impl-password.h>
 #include <libedataserver/e-source-enums.h>
 #include <libedataserver/e-source-enumtypes.h>
diff --git a/libedataserverui/Makefile.am b/libedataserverui/Makefile.am
index 51578fd..21beed4 100644
--- a/libedataserverui/Makefile.am
+++ b/libedataserverui/Makefile.am
@@ -16,6 +16,7 @@ libedataserverui_1_2_la_CPPFLAGS = \
 	$(GCR_BASE_CFLAGS) \
 	$(GCR_CFLAGS) \
 	$(GTK_CFLAGS) \
+	$(GOOGLE_AUTH_CFLAGS) \
 	$(NULL)
 
 libedataserverui_1_2_la_LIBADD = \
@@ -30,6 +31,7 @@ libedataserverui_1_2_la_LIBADD = \
 	$(GCR_BASE_LIBS) \
 	$(GCR_LIBS) \
 	$(GTK_LIBS) \
+	$(GOOGLE_AUTH_LIBS) \
 	$(NULL)
 
 libedataserverui_1_2_la_LDFLAGS = \
@@ -44,6 +46,7 @@ libedataserveruiinclude_HEADERS = \
 	e-cell-renderer-color.h \
 	e-credentials-prompter.h \
 	e-credentials-prompter-impl.h \
+	e-credentials-prompter-impl-google.h \
 	e-credentials-prompter-impl-password.h \
 	e-trust-prompt.h \
 	e-webdav-discover-widget.h \
@@ -54,6 +57,7 @@ libedataserverui_1_2_la_SOURCES = \
 	e-cell-renderer-color.c \
 	e-credentials-prompter.c \
 	e-credentials-prompter-impl.c \
+	e-credentials-prompter-impl-google.c \
 	e-credentials-prompter-impl-password.c \
 	e-trust-prompt.c \
 	e-webdav-discover-widget.c \
diff --git a/libedataserverui/e-credentials-prompter-impl-google.c b/libedataserverui/e-credentials-prompter-impl-google.c
new file mode 100644
index 0000000..4fadef5
--- /dev/null
+++ b/libedataserverui/e-credentials-prompter-impl-google.c
@@ -0,0 +1,1127 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
+ *
+ * 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.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+
+#include <gtk/gtk.h>
+
+#include <libedataserver/libedataserver.h>
+
+#include "e-credentials-prompter.h"
+#include "e-credentials-prompter-impl-google.h"
+
+#ifdef ENABLE_GOOGLE_AUTH
+#include <webkit/webkit.h>
+
+/* https://developers.google.com/identity/protocols/OAuth2InstalledApp */
+#define GOOGLE_AUTH_URI "https://accounts.google.com/o/oauth2/auth"
+#define GOOGLE_TOKEN_URI "https://www.googleapis.com/oauth2/v3/token"
+#define GOOGLE_REDIRECT_URI "urn:ietf:wg:oauth:2.0:oob"
+
+static const gchar *GOOGLE_SCOPE =
+	/* GMail IMAP and SMTP access */
+	"https://mail.google.com/ "
+	/* Google Calendar API (CalDAV and GData) */
+	"https://www.googleapis.com/auth/calendar "
+	/* Google Contacts API (GData) */
+	"https://www.google.com/m8/feeds/ "
+	/* Google Contacts API (CardDAV) - undocumented */
+	"https://www.googleapis.com/auth/carddav "
+	/* Google Tasks - undocumented */
+	"https://www.googleapis.com/auth/tasks";
+#endif /* ENABLE_GOOGLE_AUTH */
+
+struct _ECredentialsPrompterImplGooglePrivate {
+	GMutex property_lock;
+
+	gpointer prompt_id;
+	ESource *auth_source;
+	ESource *cred_source;
+	gchar *error_text;
+	ENamedParameters *credentials;
+
+	GtkDialog *dialog;
+#ifdef ENABLE_GOOGLE_AUTH
+	WebKitWebView *web_view;
+#endif
+	gulong show_dialog_idle_id;
+
+	GCancellable *cancellable;
+};
+
+G_DEFINE_TYPE (ECredentialsPrompterImplGoogle, e_credentials_prompter_impl_google, E_TYPE_CREDENTIALS_PROMPTER_IMPL)
+
+#ifdef ENABLE_GOOGLE_AUTH
+static gchar *
+cpi_google_create_auth_uri (ESource *source)
+{
+	GHashTable *form;
+	SoupURI *soup_uri;
+	gchar *uri, *user = NULL;
+
+	g_return_val_if_fail (E_IS_SOURCE (source), NULL);
+
+	soup_uri = soup_uri_new (GOOGLE_AUTH_URI);
+	form = g_hash_table_new (g_str_hash, g_str_equal);
+
+	#define add_to_form(name, value) g_hash_table_insert (form, (gpointer) name, (gpointer) value)
+
+	add_to_form ("response_type", "code");
+	add_to_form ("client_id", GOOGLE_CLIENT_ID);
+	add_to_form ("redirect_uri", GOOGLE_REDIRECT_URI);
+	add_to_form ("scope", GOOGLE_SCOPE);
+
+	if (e_source_has_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION)) {
+		ESourceAuthentication *auth_extension;
+
+		auth_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
+		user = e_source_authentication_dup_user (auth_extension);
+
+		if (user && *user)
+			add_to_form ("login_hint", user);
+	}
+
+	add_to_form ("include_granted_scopes", "false");
+
+	#undef add_to_form
+
+	soup_uri_set_query_from_form (soup_uri, form);
+
+	uri = soup_uri_to_string (soup_uri, FALSE);
+
+	soup_uri_free (soup_uri);
+	g_hash_table_destroy (form);
+	g_free (user);
+
+	return uri;
+}
+
+static gchar *
+cpi_google_create_token_post_data (const gchar *authorization_code)
+{
+	g_return_val_if_fail (authorization_code != NULL, NULL);
+
+	return soup_form_encode (
+		"code", authorization_code,
+		"client_id", GOOGLE_CLIENT_ID,
+		"client_secret", GOOGLE_CLIENT_SECRET,
+		"redirect_uri", GOOGLE_REDIRECT_URI,
+		"grant_type", "authorization_code",
+		NULL);
+}
+
+static gchar *
+cpi_google_create_refresh_token_post_data (const gchar *refresh_token)
+{
+	g_return_val_if_fail (refresh_token != NULL, NULL);
+
+	return soup_form_encode (
+		"refresh_token", refresh_token,
+		"client_id", GOOGLE_CLIENT_ID,
+		"client_secret", GOOGLE_CLIENT_SECRET,
+		"grant_type", "refresh_token",
+		NULL);
+}
+
+static void
+cpi_google_setup_proxy_resolver (SoupSession *session,
+				 ESourceRegistry *registry,
+				 ESource *cred_source)
+{
+	ESourceAuthentication *auth_extension;
+	ESource *source = NULL;
+	gchar *uid;
+
+	g_return_if_fail (SOUP_IS_SESSION (session));
+	g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
+	g_return_if_fail (E_IS_SOURCE (cred_source));
+
+	if (!e_source_has_extension (cred_source, E_SOURCE_EXTENSION_AUTHENTICATION))
+		return;
+
+	auth_extension = e_source_get_extension (cred_source, E_SOURCE_EXTENSION_AUTHENTICATION);
+	uid = e_source_authentication_dup_proxy_uid (auth_extension);
+	if (uid != NULL) {
+		source = e_source_registry_ref_source (registry, uid);
+
+		g_free (uid);
+	}
+
+	if (source != NULL) {
+		GProxyResolver *proxy_resolver;
+
+		proxy_resolver = G_PROXY_RESOLVER (source);
+		if (g_proxy_resolver_is_supported (proxy_resolver))
+			g_object_set (session, SOUP_SESSION_PROXY_RESOLVER, proxy_resolver, NULL);
+
+		g_object_unref (source);
+	}
+}
+
+static void
+cpi_google_abort_session_cb (GCancellable *cancellable,
+			     SoupSession *session)
+{
+	soup_session_abort (session);
+}
+
+static guint
+cpi_google_post_data_sync (const gchar *uri,
+			   const gchar *post_data,
+			   ESourceRegistry *registry,
+			   ESource *cred_source,
+			   GCancellable *cancellable,
+			   gchar **out_response_data)
+{
+	SoupSession *session;
+	SoupMessage *message;
+	guint status_code = SOUP_STATUS_CANCELLED;
+
+	g_return_val_if_fail (uri != NULL, SOUP_STATUS_MALFORMED);
+	g_return_val_if_fail (post_data != NULL, SOUP_STATUS_MALFORMED);
+	g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), SOUP_STATUS_MALFORMED);
+	g_return_val_if_fail (E_IS_SOURCE (cred_source), SOUP_STATUS_MALFORMED);
+	g_return_val_if_fail (out_response_data != NULL, SOUP_STATUS_MALFORMED);
+
+	*out_response_data = NULL;
+
+	message = soup_message_new (SOUP_METHOD_POST, uri);
+	g_return_val_if_fail (message != NULL, SOUP_STATUS_MALFORMED);
+
+	soup_message_set_request (message, "application/x-www-form-urlencoded",
+		SOUP_MEMORY_TEMPORARY, post_data, strlen (post_data));
+
+	e_soup_ssl_trust_connect (message, cred_source);
+
+	session = soup_session_new ();
+	g_object_set (
+		session,
+		SOUP_SESSION_TIMEOUT, 90,
+		SOUP_SESSION_SSL_STRICT, TRUE,
+		SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE, TRUE,
+		NULL);
+
+	cpi_google_setup_proxy_resolver (session, registry, cred_source);
+
+	soup_message_headers_append (message->request_headers, "Connection", "close");
+
+	if (!g_cancellable_is_cancelled (cancellable)) {
+		gulong cancel_handler_id = 0;
+
+		if (cancellable)
+			cancel_handler_id = g_cancellable_connect (cancellable, G_CALLBACK (cpi_google_abort_session_cb), session, NULL);
+
+		status_code = soup_session_send_message (session, message);
+
+		if (cancel_handler_id)
+			g_cancellable_disconnect (cancellable, cancel_handler_id);
+	}
+
+	if (SOUP_STATUS_IS_SUCCESSFUL (status_code)) {
+		if (message->response_body) {
+			*out_response_data = g_strndup (message->response_body->data, message->response_body->length);
+		} else {
+			status_code = SOUP_STATUS_MALFORMED;
+		}
+	}
+
+	g_object_unref (message);
+	g_object_unref (session);
+
+	return status_code;
+}
+
+static gboolean
+cpi_google_update_prompter_credentials (GWeakRef *prompter_google_wr,
+					const gchar *refresh_token,
+					const gchar *access_token,
+					const gchar *expires_in,
+					GCancellable *cancellable)
+{
+	ECredentialsPrompterImplGoogle *prompter_google;
+	gint64 expires_after_tm;
+	gchar *expires_after;
+	gboolean success = FALSE;
+
+	g_return_val_if_fail (prompter_google_wr != NULL, FALSE);
+
+	if (!refresh_token || !access_token || !expires_in)
+		return FALSE;
+
+	if (g_cancellable_is_cancelled (cancellable))
+		return FALSE;
+
+	expires_after_tm = g_get_real_time () / G_USEC_PER_SEC;
+	expires_after_tm += g_ascii_strtoll (expires_in, NULL, 10);
+	expires_after = g_strdup_printf ("%" G_GINT64_FORMAT, expires_after_tm);
+
+	prompter_google = g_weak_ref_get (prompter_google_wr);
+	if (prompter_google && !g_cancellable_is_cancelled (cancellable)) {
+		gchar *secret = NULL;
+
+		if (e_source_credentials_google_util_encode_to_secret (&secret,
+			E_GOOGLE_SECRET_REFRESH_TOKEN, refresh_token,
+			E_GOOGLE_SECRET_ACCESS_TOKEN, access_token,
+			E_GOOGLE_SECRET_EXPIRES_AFTER, expires_after, NULL)) {
+			e_named_parameters_set (prompter_google->priv->credentials,
+				E_SOURCE_CREDENTIAL_GOOGLE_SECRET, secret);
+			e_named_parameters_set (prompter_google->priv->credentials,
+				E_SOURCE_CREDENTIAL_PASSWORD, access_token);
+
+			success = TRUE;
+		}
+
+		g_object_unref (prompter_google);
+		g_free (secret);
+	}
+
+	g_free (expires_after);
+
+	return success;
+}
+
+static void
+e_credentials_prompter_impl_google_show_html (WebKitWebView *web_view,
+					      const gchar *title,
+					      const gchar *body_text)
+{
+	gchar *html;
+
+	g_return_if_fail (WEBKIT_IS_WEB_VIEW (web_view));
+	g_return_if_fail (title != NULL);
+	g_return_if_fail (body_text != NULL);
+
+	html = g_strdup_printf (
+		"<html>"
+		"<head><title>%s</title></head>"
+		"<body><div style=\"font-size:12pt; font-family:Helvetica,Arial;\">%s</div></body>"
+		"</html>",
+		title,
+		body_text);
+	webkit_web_view_load_string (web_view, html, "text/html", "UTF-8", "none-local://");
+	g_free (html);
+}
+
+static gboolean
+e_credentials_prompter_impl_google_finish_dialog_idle_cb (gpointer user_data)
+{
+	ECredentialsPrompterImplGoogle *prompter_google = user_data;
+
+	if (g_source_is_destroyed (g_main_current_source ()))
+		return FALSE;
+
+	g_return_val_if_fail (E_IS_CREDENTIALS_PROMPTER_IMPL_GOOGLE (prompter_google), FALSE);
+
+	if (g_source_get_id (g_main_current_source ()) == prompter_google->priv->show_dialog_idle_id) {
+		prompter_google->priv->show_dialog_idle_id = 0;
+
+		g_warn_if_fail (prompter_google->priv->dialog != NULL);
+
+		if (e_named_parameters_exists (prompter_google->priv->credentials, E_SOURCE_CREDENTIAL_PASSWORD)) {
+			gtk_dialog_response (prompter_google->priv->dialog, GTK_RESPONSE_OK);
+		} else if (prompter_google->priv->error_text) {
+			e_credentials_prompter_impl_google_show_html (prompter_google->priv->web_view,
+				"Finished with error", prompter_google->priv->error_text);
+		}
+	}
+
+	return FALSE;
+}
+
+typedef struct {
+	GWeakRef *prompter_google; /* ECredentialsPrompterImplGoogle * */
+	GCancellable *cancellable;
+	ESource *cred_source;
+	ESourceRegistry *registry;
+	gchar *authorization_code;
+} AccessTokenThreadData;
+
+static void
+access_token_thread_data_free (gpointer user_data)
+{
+	AccessTokenThreadData *td = user_data;
+
+	if (td) {
+		e_weak_ref_free (td->prompter_google);
+		g_clear_object (&td->cancellable);
+		g_clear_object (&td->cred_source);
+		g_clear_object (&td->registry);
+		g_free (td->authorization_code);
+		g_free (td);
+	}
+}
+
+static gpointer
+cpi_google_get_access_token_thread (gpointer user_data)
+{
+	AccessTokenThreadData *td = user_data;
+	ECredentialsPrompterImplGoogle *prompter_google;
+	GCancellable *cancellable;
+	gchar *post_data, *response_json = NULL;
+	guint soup_status;
+	gboolean success = FALSE;
+
+	g_return_val_if_fail (td != NULL, NULL);
+
+	cancellable = td->cancellable;
+
+	if (g_cancellable_is_cancelled (cancellable))
+		goto exit;
+
+
+	post_data = cpi_google_create_token_post_data (td->authorization_code);
+	g_warn_if_fail (post_data != NULL);
+	if (!post_data)
+		goto exit;
+
+	soup_status = cpi_google_post_data_sync (GOOGLE_TOKEN_URI, post_data,
+		td->registry, td->cred_source, cancellable, &response_json);
+
+	if (SOUP_STATUS_IS_SUCCESSFUL (soup_status) && response_json) {
+		gchar *access_token = NULL, *refresh_token = NULL, *expires_in = NULL, *token_type = NULL;
+
+		if (e_source_credentials_google_util_decode_from_secret (response_json,
+			"access_token", &access_token,
+			"refresh_token", &refresh_token,
+			"expires_in", &expires_in,
+			"token_type", &token_type,
+			NULL) && access_token && refresh_token && expires_in && token_type) {
+
+			g_warn_if_fail (g_str_equal (token_type, "Bearer"));
+
+			if (!cpi_google_update_prompter_credentials (td->prompter_google,
+				refresh_token, access_token, expires_in, cancellable)) {
+				soup_status = SOUP_STATUS_MALFORMED;
+			}
+		} else {
+			soup_status = SOUP_STATUS_MALFORMED;
+		}
+
+		g_free (access_token);
+		g_free (refresh_token);
+		g_free (expires_in);
+		g_free (token_type);
+	}
+
+	g_free (response_json);
+	g_free (post_data);
+
+ exit:
+	prompter_google = g_weak_ref_get (td->prompter_google);
+	if (prompter_google && !g_cancellable_is_cancelled (cancellable)) {
+		if (!success) {
+			g_free (prompter_google->priv->error_text);
+			prompter_google->priv->error_text = NULL;
+
+			prompter_google->priv->error_text = g_strdup_printf (
+				_("Failed to obtain access token from address '%s'. Error code %d (%s)"),
+				GOOGLE_TOKEN_URI, soup_status, soup_status_get_phrase (soup_status));
+		}
+
+		prompter_google->priv->show_dialog_idle_id = g_idle_add (
+			e_credentials_prompter_impl_google_finish_dialog_idle_cb,
+			prompter_google);
+	}
+
+	g_clear_object (&prompter_google);
+
+	access_token_thread_data_free (td);
+
+	return NULL;
+}
+
+static void
+cpi_google_document_load_finished_cb (WebKitWebView *web_view,
+				      WebKitWebFrame *frame,
+				      ECredentialsPrompterImplGoogle *prompter_google)
+{
+	const gchar *title;
+
+	g_return_if_fail (WEBKIT_IS_WEB_VIEW (web_view));
+	g_return_if_fail (E_IS_CREDENTIALS_PROMPTER_IMPL_GOOGLE (prompter_google));
+
+	if (frame != webkit_web_view_get_main_frame (web_view))
+		return;
+
+	title = webkit_web_frame_get_title (frame);
+	if (!title)
+		return;
+
+	if (g_ascii_strncasecmp (title, "Denied ", 7) == 0) {
+		g_cancellable_cancel (prompter_google->priv->cancellable);
+		gtk_dialog_response (prompter_google->priv->dialog, GTK_RESPONSE_CANCEL);
+		return;
+	}
+
+	if (g_ascii_strncasecmp (title, "Success code=", 13) == 0) {
+		ECredentialsPrompter *prompter;
+		ECredentialsPrompterImpl *prompter_impl;
+		AccessTokenThreadData *td;
+		GThread *thread;
+
+		e_credentials_prompter_impl_google_show_html (web_view,
+			"Checking returned code", _("Requesting access token, please wait..."));
+
+		gtk_widget_set_sensitive (GTK_WIDGET (web_view), FALSE);
+
+		e_named_parameters_set (prompter_google->priv->credentials, E_SOURCE_CREDENTIAL_PASSWORD, NULL);
+
+		prompter_impl = E_CREDENTIALS_PROMPTER_IMPL (prompter_google);
+		prompter = e_credentials_prompter_impl_get_credentials_prompter (prompter_impl);
+
+		td = g_new0 (AccessTokenThreadData, 1);
+		td->prompter_google = e_weak_ref_new (prompter_google);
+		td->cancellable = g_object_ref (prompter_google->priv->cancellable);
+		td->cred_source = g_object_ref (prompter_google->priv->cred_source);
+		td->registry = g_object_ref (e_credentials_prompter_get_registry (prompter));
+		td->authorization_code = g_strdup (title + 13);
+
+		thread = g_thread_new (G_STRFUNC, cpi_google_get_access_token_thread, td);
+		g_thread_unref (thread);
+
+		return;
+	}
+}
+
+static void
+cpi_google_notify_progress_cb (WebKitWebView *web_view,
+			       GParamSpec *param,
+			       GtkProgressBar *progress_bar)
+{
+	gboolean visible;
+	gdouble progress;
+
+	g_return_if_fail (GTK_IS_PROGRESS_BAR (progress_bar));
+
+	progress = webkit_web_view_get_progress (web_view);
+	visible = progress > 1e-9 && progress < 1 - 1e-9;
+
+	gtk_progress_bar_set_fraction (progress_bar, visible ? progress : 0.0);
+}
+
+static void
+credentials_prompter_impl_google_get_prompt_strings (ESourceRegistry *registry,
+						     ESource *source,
+						     gchar **prompt_title,
+						     GString **prompt_description)
+{
+	GString *description;
+	const gchar *message;
+	gchar *display_name;
+
+	/* Known types */
+	enum {
+		TYPE_UNKNOWN,
+		TYPE_AMBIGUOUS,
+		TYPE_ADDRESS_BOOK,
+		TYPE_CALENDAR,
+		TYPE_MAIL_ACCOUNT,
+		TYPE_MAIL_TRANSPORT,
+		TYPE_MEMO_LIST,
+		TYPE_TASK_LIST
+	} type = TYPE_UNKNOWN;
+
+	if (e_source_has_extension (source, E_SOURCE_EXTENSION_ADDRESS_BOOK)) {
+		type = TYPE_ADDRESS_BOOK;
+	}
+
+	if (e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR)) {
+		if (type == TYPE_UNKNOWN)
+			type = TYPE_CALENDAR;
+		else
+			type = TYPE_AMBIGUOUS;
+	}
+
+	if (e_source_has_extension (source, E_SOURCE_EXTENSION_MAIL_ACCOUNT)) {
+		if (type == TYPE_UNKNOWN)
+			type = TYPE_MAIL_ACCOUNT;
+		else
+			type = TYPE_AMBIGUOUS;
+	}
+
+	if (e_source_has_extension (source, E_SOURCE_EXTENSION_MAIL_TRANSPORT)) {
+		if (type == TYPE_UNKNOWN)
+			type = TYPE_MAIL_TRANSPORT;
+		else
+			type = TYPE_AMBIGUOUS;
+	}
+
+	if (e_source_has_extension (source, E_SOURCE_EXTENSION_MEMO_LIST)) {
+		if (type == TYPE_UNKNOWN)
+			type = TYPE_MEMO_LIST;
+		else
+			type = TYPE_AMBIGUOUS;
+	}
+
+	if (e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST)) {
+		if (type == TYPE_UNKNOWN)
+			type = TYPE_TASK_LIST;
+		else
+			type = TYPE_AMBIGUOUS;
+	}
+
+	switch (type) {
+		case TYPE_ADDRESS_BOOK:
+			message = _("Google Address book authentication request");
+			break;
+		case TYPE_CALENDAR:
+			message = _("Google Calendar authentication request");
+			break;
+		case TYPE_MEMO_LIST:
+			message = _("Google Memo List authentication request");
+			break;
+		case TYPE_TASK_LIST:
+			message = _("Google Task List authentication request");
+			break;
+		case TYPE_MAIL_ACCOUNT:
+		case TYPE_MAIL_TRANSPORT:
+			message = _("Google Mail authentication request");
+			break;
+		default:  /* generic account prompt */
+			message = _("Google account authentication request");
+			break;
+	}
+
+	display_name = e_util_get_source_full_name (registry, source);
+	description = g_string_sized_new (256);
+
+	g_string_append_printf (description, "<big><b>%s</b></big>\n\n", message);
+	switch (type) {
+		case TYPE_ADDRESS_BOOK:
+			g_string_append_printf (description,
+				_("Login to your Google account and accept conditions in order to access your address book \"%s\"."), display_name);
+			break;
+		case TYPE_CALENDAR:
+			g_string_append_printf (description,
+				_("Login to your Google account and accept conditions in order to access your calendar \"%s\"."), display_name);
+			break;
+		case TYPE_MAIL_ACCOUNT:
+			g_string_append_printf (description,
+				_("Login to your Google account and accept conditions in order to access your mail account \"%s\"."), display_name);
+			break;
+		case TYPE_MAIL_TRANSPORT:
+			g_string_append_printf (description,
+				_("Login to your Google account and accept conditions in order to access your mail transport \"%s\"."), display_name);
+			break;
+		case TYPE_MEMO_LIST:
+			g_string_append_printf (description,
+				_("Login to your Google account and accept conditions in order to access your memo list \"%s\"."), display_name);
+			break;
+		case TYPE_TASK_LIST:
+			g_string_append_printf (description,
+				_("Login to your Google account and accept conditions in order to access your task list \"%s\"."), display_name);
+			break;
+		default:  /* generic account prompt */
+			g_string_append_printf (description,
+				_("Login to your Google account and accept conditions in order to access your account \"%s\"."), display_name);
+			break;
+	}
+
+	*prompt_title = g_strdup (message);
+	*prompt_description = description;
+
+	g_free (display_name);
+}
+#endif /* ENABLE_GOOGLE_AUTH */
+
+static gboolean
+e_credentials_prompter_impl_google_show_dialog (ECredentialsPrompterImplGoogle *prompter_google)
+{
+#ifdef ENABLE_GOOGLE_AUTH
+	GtkWidget *dialog, *content_area, *widget, *progress_bar, *vbox;
+	GtkGrid *grid;
+	GtkScrolledWindow *scrolled_window;
+	GtkWindow *dialog_parent;
+	ECredentialsPrompter *prompter;
+	gchar *title, *uri;
+	GString *info_markup;
+	gint row = 0;
+	gboolean success;
+
+	g_return_val_if_fail (E_IS_CREDENTIALS_PROMPTER_IMPL_GOOGLE (prompter_google), FALSE);
+	g_return_val_if_fail (prompter_google->priv->prompt_id != NULL, FALSE);
+	g_return_val_if_fail (prompter_google->priv->dialog == NULL, FALSE);
+
+	prompter = e_credentials_prompter_impl_get_credentials_prompter (E_CREDENTIALS_PROMPTER_IMPL (prompter_google));
+	g_return_val_if_fail (prompter != NULL, FALSE);
+
+	dialog_parent = e_credentials_prompter_get_dialog_parent (prompter);
+
+	credentials_prompter_impl_google_get_prompt_strings (e_credentials_prompter_get_registry (prompter),
+		prompter_google->priv->auth_source, &title, &info_markup);
+	if (prompter_google->priv->error_text && *prompter_google->priv->error_text) {
+		gchar *escaped = g_markup_printf_escaped ("%s", prompter_google->priv->error_text);
+
+		g_string_append_printf (info_markup, "\n\n%s", escaped);
+		g_free (escaped);
+	}
+
+	dialog = gtk_dialog_new_with_buttons (title, dialog_parent, GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+		_("_Cancel"), GTK_RESPONSE_CANCEL,
+		NULL);
+
+	gtk_window_set_default_size (GTK_WINDOW (dialog), 320, 480);
+
+	prompter_google->priv->dialog = GTK_DIALOG (dialog);
+	gtk_window_set_resizable (GTK_WINDOW (dialog), TRUE);
+	if (dialog_parent)
+		gtk_window_set_transient_for (GTK_WINDOW (dialog), dialog_parent);
+	gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER_ON_PARENT);
+	gtk_container_set_border_width (GTK_CONTAINER (dialog), 12);
+
+	content_area = gtk_dialog_get_content_area (prompter_google->priv->dialog);
+
+	/* Override GtkDialog defaults */
+	gtk_box_set_spacing (GTK_BOX (content_area), 12);
+	gtk_container_set_border_width (GTK_CONTAINER (content_area), 0);
+
+	grid = GTK_GRID (gtk_grid_new ());
+	gtk_grid_set_column_spacing (grid, 12);
+	gtk_grid_set_row_spacing (grid, 6);
+
+	gtk_box_pack_start (GTK_BOX (content_area), GTK_WIDGET (grid), FALSE, TRUE, 0);
+
+	/* Info Label */
+	widget = gtk_label_new (NULL);
+	gtk_label_set_line_wrap (GTK_LABEL (widget), TRUE);
+	gtk_label_set_markup (GTK_LABEL (widget), info_markup->str);
+	g_object_set (
+		G_OBJECT (widget),
+		"hexpand", TRUE,
+		"vexpand", FALSE,
+		"halign", GTK_ALIGN_FILL,
+		"valign", GTK_ALIGN_CENTER,
+		"width-chars", 60,
+		"max-width-chars", 80,
+		"xalign", 0.0,
+		NULL);
+
+	gtk_grid_attach (grid, widget, 0, row, 1, 1);
+	row++;
+
+	vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 1);
+	g_object_set (
+		G_OBJECT (vbox),
+		"hexpand", TRUE,
+		"vexpand", TRUE,
+		"halign", GTK_ALIGN_FILL,
+		"valign", GTK_ALIGN_FILL,
+		NULL);
+
+	gtk_grid_attach (grid, vbox, 0, row, 1, 1);
+
+	widget = gtk_scrolled_window_new (NULL, NULL);
+	g_object_set (
+		G_OBJECT (widget),
+		"hexpand", TRUE,
+		"vexpand", TRUE,
+		"halign", GTK_ALIGN_FILL,
+		"valign", GTK_ALIGN_FILL,
+		"hscrollbar-policy", GTK_POLICY_AUTOMATIC,
+		"vscrollbar-policy", GTK_POLICY_AUTOMATIC,
+		NULL);
+
+	gtk_box_pack_start (GTK_BOX (vbox), widget, TRUE, TRUE, 0);
+
+	scrolled_window = GTK_SCROLLED_WINDOW (widget);
+
+	widget = webkit_web_view_new ();
+	g_object_set (
+		G_OBJECT (widget),
+		"hexpand", TRUE,
+		"vexpand", TRUE,
+		"halign", GTK_ALIGN_FILL,
+		"valign", GTK_ALIGN_FILL,
+		NULL);
+	gtk_container_add (GTK_CONTAINER (scrolled_window), widget);
+
+	prompter_google->priv->web_view = WEBKIT_WEB_VIEW (widget);
+
+	progress_bar = gtk_progress_bar_new ();
+	g_object_set (
+		G_OBJECT (progress_bar),
+		"hexpand", TRUE,
+		"vexpand", FALSE,
+		"halign", GTK_ALIGN_FILL,
+		"valign", GTK_ALIGN_START,
+		"orientation", GTK_ORIENTATION_HORIZONTAL,
+		"ellipsize", PANGO_ELLIPSIZE_END,
+		"fraction", 0.0,
+		NULL);
+	gtk_style_context_add_class (gtk_widget_get_style_context (progress_bar), GTK_STYLE_CLASS_OSD);
+
+	gtk_box_pack_start (GTK_BOX (vbox), progress_bar, FALSE, FALSE, 0);
+
+	gtk_widget_show_all (GTK_WIDGET (grid));
+
+	uri = cpi_google_create_auth_uri (prompter_google->priv->cred_source);
+	if (!uri) {
+		success = FALSE;
+	} else {
+		WebKitWebView *web_view = prompter_google->priv->web_view;
+		gulong load_finished_handler_id, progress_handler_id;
+
+		load_finished_handler_id = g_signal_connect (web_view, "document-load-finished",
+			G_CALLBACK (cpi_google_document_load_finished_cb), prompter_google);
+		progress_handler_id = g_signal_connect (web_view, "notify::progress",
+			G_CALLBACK (cpi_google_notify_progress_cb), progress_bar);
+
+		webkit_web_view_load_uri (web_view, uri);
+
+		success = gtk_dialog_run (prompter_google->priv->dialog) == GTK_RESPONSE_OK;
+
+		if (load_finished_handler_id)
+			g_signal_handler_disconnect (web_view, load_finished_handler_id);
+		if (progress_handler_id)
+			g_signal_handler_disconnect (web_view, progress_handler_id);
+	}
+
+	if (prompter_google->priv->cancellable)
+		g_cancellable_cancel (prompter_google->priv->cancellable);
+
+	prompter_google->priv->web_view = NULL;
+	prompter_google->priv->dialog = NULL;
+	gtk_widget_destroy (dialog);
+
+	g_string_free (info_markup, TRUE);
+	g_free (title);
+
+	return success;
+#else /* ENABLE_GOOGLE_AUTH */
+	return FALSE;
+#endif /* ENABLE_GOOGLE_AUTH */
+}
+
+static void
+e_credentials_prompter_impl_google_free_prompt_data (ECredentialsPrompterImplGoogle *prompter_google)
+{
+	g_return_if_fail (E_IS_CREDENTIALS_PROMPTER_IMPL_GOOGLE (prompter_google));
+
+	prompter_google->priv->prompt_id = NULL;
+
+	g_clear_object (&prompter_google->priv->auth_source);
+	g_clear_object (&prompter_google->priv->cred_source);
+
+	g_free (prompter_google->priv->error_text);
+	prompter_google->priv->error_text = NULL;
+
+	e_named_parameters_free (prompter_google->priv->credentials);
+	prompter_google->priv->credentials = NULL;
+}
+
+static gboolean
+e_credentials_prompter_impl_google_manage_dialog_idle_cb (gpointer user_data)
+{
+	ECredentialsPrompterImplGoogle *prompter_google = user_data;
+
+	if (g_source_is_destroyed (g_main_current_source ()))
+		return FALSE;
+
+	g_return_val_if_fail (E_IS_CREDENTIALS_PROMPTER_IMPL_GOOGLE (prompter_google), FALSE);
+
+	if (g_source_get_id (g_main_current_source ()) == prompter_google->priv->show_dialog_idle_id) {
+		gboolean success;
+
+		prompter_google->priv->show_dialog_idle_id = 0;
+
+		g_warn_if_fail (prompter_google->priv->dialog == NULL);
+
+		if (e_named_parameters_exists (prompter_google->priv->credentials, E_SOURCE_CREDENTIAL_PASSWORD))
+			success = TRUE;
+		else
+			success = e_credentials_prompter_impl_google_show_dialog (prompter_google);
+
+		e_credentials_prompter_impl_prompt_finish (
+			E_CREDENTIALS_PROMPTER_IMPL (prompter_google),
+			prompter_google->priv->prompt_id,
+			success ? prompter_google->priv->credentials : NULL);
+
+		e_credentials_prompter_impl_google_free_prompt_data (prompter_google);
+	}
+
+	return FALSE;
+}
+
+#ifdef ENABLE_GOOGLE_AUTH
+typedef struct {
+	GWeakRef *prompter_google; /* ECredentialsPrompterImplGoogle * */
+	GCancellable *cancellable;
+	ESource *cred_source;
+	ESourceRegistry *registry;
+	gchar *secret;
+} CheckExistingThreadData;
+
+static void
+check_existing_thread_data_free (gpointer user_data)
+{
+	CheckExistingThreadData *td = user_data;
+
+	if (td) {
+		e_weak_ref_free (td->prompter_google);
+		g_clear_object (&td->cancellable);
+		g_clear_object (&td->cred_source);
+		g_clear_object (&td->registry);
+		g_free (td->secret);
+		g_free (td);
+	}
+}
+
+static gpointer
+cpi_google_check_existing_token_thread (gpointer user_data)
+{
+	CheckExistingThreadData *td = user_data;
+	ECredentialsPrompterImplGoogle *prompter_google;
+	GCancellable *cancellable;
+	gchar *refresh_token = NULL;
+
+	g_return_val_if_fail (td != NULL, NULL);
+
+	cancellable = td->cancellable;
+
+	if (g_cancellable_is_cancelled (cancellable))
+		goto exit;
+
+	if (!e_source_credentials_google_util_decode_from_secret (td->secret, E_GOOGLE_SECRET_REFRESH_TOKEN, &refresh_token, NULL))
+		goto exit;
+
+	if (refresh_token) {
+		gchar *post_data, *response_json = NULL;
+		guint soup_status;
+
+		post_data = cpi_google_create_refresh_token_post_data (refresh_token);
+		g_warn_if_fail (post_data != NULL);
+		if (!post_data)
+			goto exit;
+
+		soup_status = cpi_google_post_data_sync (GOOGLE_TOKEN_URI, post_data,
+			td->registry, td->cred_source, cancellable, &response_json);
+
+		if (SOUP_STATUS_IS_SUCCESSFUL (soup_status) && response_json) {
+			gchar *access_token = NULL, *expires_in = NULL;
+
+			if (e_source_credentials_google_util_decode_from_secret (response_json,
+				"access_token", &access_token,
+				"expires_in", &expires_in,
+				NULL) && access_token && expires_in) {
+				cpi_google_update_prompter_credentials (td->prompter_google,
+					refresh_token, access_token, expires_in, cancellable);
+			}
+
+			g_free (access_token);
+			g_free (expires_in);
+		}
+
+		g_free (response_json);
+		g_free (post_data);
+	}
+
+ exit:
+	prompter_google = g_weak_ref_get (td->prompter_google);
+	if (prompter_google && !g_cancellable_is_cancelled (cancellable)) {
+		prompter_google->priv->show_dialog_idle_id = g_idle_add (
+			e_credentials_prompter_impl_google_manage_dialog_idle_cb,
+			prompter_google);
+	}
+
+	g_clear_object (&prompter_google);
+	g_free (refresh_token);
+
+	check_existing_thread_data_free (td);
+
+	return NULL;
+}
+#endif /* ENABLE_GOOGLE_AUTH */
+
+static void
+e_credentials_prompter_impl_google_process_prompt (ECredentialsPrompterImpl *prompter_impl,
+						   gpointer prompt_id,
+						   ESource *auth_source,
+						   ESource *cred_source,
+						   const gchar *error_text,
+						   const ENamedParameters *credentials)
+{
+	ECredentialsPrompterImplGoogle *prompter_google;
+
+	g_return_if_fail (E_IS_CREDENTIALS_PROMPTER_IMPL_GOOGLE (prompter_impl));
+
+	prompter_google = E_CREDENTIALS_PROMPTER_IMPL_GOOGLE (prompter_impl);
+	g_return_if_fail (prompter_google->priv->prompt_id == NULL);
+	g_return_if_fail (prompter_google->priv->show_dialog_idle_id == 0);
+
+	prompter_google->priv->prompt_id = prompt_id;
+	prompter_google->priv->auth_source = g_object_ref (auth_source);
+	prompter_google->priv->cred_source = g_object_ref (cred_source);
+	prompter_google->priv->error_text = g_strdup (error_text);
+	prompter_google->priv->credentials = e_named_parameters_new_clone (credentials);
+	prompter_google->priv->cancellable = g_cancellable_new ();
+
+#ifdef ENABLE_GOOGLE_AUTH
+	e_named_parameters_set (prompter_google->priv->credentials, E_SOURCE_CREDENTIAL_PASSWORD, NULL);
+
+	/* Read stored secret directly from the secret store, if not provided, because
+	   e_source_credentials_provider_impl_google_lookup_sync() returns the secret
+	   only if the stored access token is not expired.
+	 */
+	if (!e_named_parameters_exists (prompter_google->priv->credentials, E_SOURCE_CREDENTIAL_GOOGLE_SECRET)) {
+		gchar *secret_uid = NULL;
+
+		if (e_source_credentials_google_util_generate_secret_uid (cred_source, &secret_uid)) {
+			gchar *secret = NULL;
+
+			if (e_secret_store_lookup_sync (secret_uid, &secret, prompter_google->priv->cancellable, NULL)) {
+				e_named_parameters_set (prompter_google->priv->credentials,
+					E_SOURCE_CREDENTIAL_GOOGLE_SECRET, secret);
+			}
+
+			g_free (secret);
+		}
+
+		g_free (secret_uid);
+	}
+
+	if (e_named_parameters_exists (prompter_google->priv->credentials, E_SOURCE_CREDENTIAL_GOOGLE_SECRET)) {
+		ECredentialsPrompter *prompter;
+		CheckExistingThreadData *td;
+		GThread *thread;
+
+		prompter = e_credentials_prompter_impl_get_credentials_prompter (prompter_impl);
+
+		td = g_new0 (CheckExistingThreadData, 1);
+		td->prompter_google = e_weak_ref_new (prompter_google);
+		td->cancellable = g_object_ref (prompter_google->priv->cancellable);
+		td->secret = g_strdup (e_named_parameters_get (prompter_google->priv->credentials, E_SOURCE_CREDENTIAL_GOOGLE_SECRET));
+		td->cred_source = g_object_ref (cred_source);
+		td->registry = g_object_ref (e_credentials_prompter_get_registry (prompter));
+
+		thread = g_thread_new (G_STRFUNC, cpi_google_check_existing_token_thread, td);
+		g_thread_unref (thread);
+	} else {
+#endif /* ENABLE_GOOGLE_AUTH */
+		prompter_google->priv->show_dialog_idle_id = g_idle_add (
+			e_credentials_prompter_impl_google_manage_dialog_idle_cb,
+			prompter_google);
+#ifdef ENABLE_GOOGLE_AUTH
+	}
+#endif /* ENABLE_GOOGLE_AUTH */
+}
+
+static void
+e_credentials_prompter_impl_google_cancel_prompt (ECredentialsPrompterImpl *prompter_impl,
+						  gpointer prompt_id)
+{
+	ECredentialsPrompterImplGoogle *prompter_google;
+
+	g_return_if_fail (E_IS_CREDENTIALS_PROMPTER_IMPL_GOOGLE (prompter_impl));
+
+	prompter_google = E_CREDENTIALS_PROMPTER_IMPL_GOOGLE (prompter_impl);
+	g_return_if_fail (prompter_google->priv->prompt_id == prompt_id);
+
+	if (prompter_google->priv->cancellable)
+		g_cancellable_cancel (prompter_google->priv->cancellable);
+
+	/* This also closes the dialog. */
+	if (prompter_google->priv->dialog)
+		gtk_dialog_response (prompter_google->priv->dialog, GTK_RESPONSE_CANCEL);
+}
+
+static void
+e_credentials_prompter_impl_google_dispose (GObject *object)
+{
+	ECredentialsPrompterImplGoogle *prompter_google = E_CREDENTIALS_PROMPTER_IMPL_GOOGLE (object);
+
+	if (prompter_google->priv->show_dialog_idle_id) {
+		g_source_remove (prompter_google->priv->show_dialog_idle_id);
+		prompter_google->priv->show_dialog_idle_id = 0;
+	}
+
+	if (prompter_google->priv->cancellable) {
+		g_cancellable_cancel (prompter_google->priv->cancellable);
+		g_clear_object (&prompter_google->priv->cancellable);
+	}
+
+	g_warn_if_fail (prompter_google->priv->prompt_id == NULL);
+	g_warn_if_fail (prompter_google->priv->dialog == NULL);
+
+	e_credentials_prompter_impl_google_free_prompt_data (prompter_google);
+
+	/* Chain up to parent's method. */
+	G_OBJECT_CLASS (e_credentials_prompter_impl_google_parent_class)->dispose (object);
+}
+
+static void
+e_credentials_prompter_impl_google_finalize (GObject *object)
+{
+	ECredentialsPrompterImplGoogle *prompter_google = E_CREDENTIALS_PROMPTER_IMPL_GOOGLE (object);
+
+	g_mutex_clear (&prompter_google->priv->property_lock);
+
+	/* Chain up to parent's method. */
+	G_OBJECT_CLASS (e_credentials_prompter_impl_google_parent_class)->finalize (object);
+}
+
+static void
+e_credentials_prompter_impl_google_class_init (ECredentialsPrompterImplGoogleClass *class)
+{
+	static const gchar *authentication_methods[] = {
+		"Google",
+		NULL
+	};
+
+	GObjectClass *object_class;
+	ECredentialsPrompterImplClass *prompter_impl_class;
+
+	g_type_class_add_private (class, sizeof (ECredentialsPrompterImplGooglePrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->dispose = e_credentials_prompter_impl_google_dispose;
+	object_class->finalize = e_credentials_prompter_impl_google_finalize;
+
+	prompter_impl_class = E_CREDENTIALS_PROMPTER_IMPL_CLASS (class);
+	prompter_impl_class->authentication_methods = (const gchar * const *) authentication_methods;
+	prompter_impl_class->process_prompt = e_credentials_prompter_impl_google_process_prompt;
+	prompter_impl_class->cancel_prompt = e_credentials_prompter_impl_google_cancel_prompt;
+}
+
+static void
+e_credentials_prompter_impl_google_init (ECredentialsPrompterImplGoogle *prompter_google)
+{
+	prompter_google->priv = G_TYPE_INSTANCE_GET_PRIVATE (prompter_google,
+		E_TYPE_CREDENTIALS_PROMPTER_IMPL_GOOGLE, ECredentialsPrompterImplGooglePrivate);
+
+	g_mutex_init (&prompter_google->priv->property_lock);
+}
+
+/**
+ * e_credentials_prompter_impl_google_new:
+ *
+ * Creates a new instance of an #ECredentialsPrompterImplGoogle.
+ *
+ * Returns: (transfer full): a newly created #ECredentialsPrompterImplGoogle,
+ *    which should be freed with g_object_unref() when no longer needed.
+ *
+ * Since: 3.20
+ **/
+ECredentialsPrompterImpl *
+e_credentials_prompter_impl_google_new (void)
+{
+	return g_object_new (E_TYPE_CREDENTIALS_PROMPTER_IMPL_GOOGLE, NULL);
+}
diff --git a/libedataserverui/e-credentials-prompter-impl-google.h b/libedataserverui/e-credentials-prompter-impl-google.h
new file mode 100644
index 0000000..c4f3aea
--- /dev/null
+++ b/libedataserverui/e-credentials-prompter-impl-google.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
+ *
+ * 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.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#if !defined (__LIBEDATASERVERUI_H_INSIDE__) && !defined (LIBEDATASERVERUI_COMPILATION)
+#error "Only <libedataserverui/libedataserverui.h> should be included directly."
+#endif
+
+#ifndef E_CREDENTIALS_PROMPTER_IMPL_GOOGLE_H
+#define E_CREDENTIALS_PROMPTER_IMPL_GOOGLE_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <libedataserverui/e-credentials-prompter-impl.h>
+
+/* Standard GObject macros */
+#define E_TYPE_CREDENTIALS_PROMPTER_IMPL_GOOGLE \
+	(e_credentials_prompter_impl_google_get_type ())
+#define E_CREDENTIALS_PROMPTER_IMPL_GOOGLE(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_CREDENTIALS_PROMPTER_IMPL_GOOGLE, ECredentialsPrompterImplGoogle))
+#define E_CREDENTIALS_PROMPTER_IMPL_GOOGLE_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_CREDENTIALS_PROMPTER_IMPL_GOOGLE, ECredentialsPrompterImplGoogleClass))
+#define E_IS_CREDENTIALS_PROMPTER_IMPL_GOOGLE(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_CREDENTIALS_PROMPTER_IMPL_GOOGLE))
+#define E_IS_CREDENTIALS_PROMPTER_IMPL_GOOGLE_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_CREDENTIALS_PROMPTER_IMPL_GOOGLE))
+#define E_CREDENTIALS_PROMPTER_IMPL_GOOGLE_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_CREDENTIALS_PROMPTER_IMPL_GOOGLE, ECredentialsPrompterImplGoogleClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ECredentialsPrompterImplGoogle ECredentialsPrompterImplGoogle;
+typedef struct _ECredentialsPrompterImplGoogleClass ECredentialsPrompterImplGoogleClass;
+typedef struct _ECredentialsPrompterImplGooglePrivate ECredentialsPrompterImplGooglePrivate;
+
+/**
+ * ECredentialsPrompterImplGoogle:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ *
+ * Since: 3.20
+ **/
+struct _ECredentialsPrompterImplGoogle {
+	ECredentialsPrompterImpl parent;
+	ECredentialsPrompterImplGooglePrivate *priv;
+};
+
+struct _ECredentialsPrompterImplGoogleClass {
+	ECredentialsPrompterImplClass parent_class;
+};
+
+GType		e_credentials_prompter_impl_google_get_type	(void) G_GNUC_CONST;
+ECredentialsPrompterImpl *
+		e_credentials_prompter_impl_google_new	(void);
+
+G_END_DECLS
+
+#endif /* E_CREDENTIALS_PROMPTER_IMPL_GOOGLE_H */
diff --git a/libedataserverui/e-credentials-prompter-impl-password.c b/libedataserverui/e-credentials-prompter-impl-password.c
index 582d167..512f32f 100644
--- a/libedataserverui/e-credentials-prompter-impl-password.c
+++ b/libedataserverui/e-credentials-prompter-impl-password.c
@@ -53,7 +53,8 @@ password_dialog_map_event_cb (GtkWidget *dialog,
 }
 
 static void
-credentials_prompter_impl_password_get_prompt_strings (ESource *source,
+credentials_prompter_impl_password_get_prompt_strings (ESourceRegistry *registry,
+						       ESource *source,
 						       gchar **prompt_title,
 						       GString **prompt_description)
 {
@@ -80,7 +81,7 @@ credentials_prompter_impl_password_get_prompt_strings (ESource *source,
 	 *     if the result is ambiguous, just refer to the data source as
 	 *     an "account". */
 
-	display_name = e_source_dup_display_name (source);
+	display_name = e_util_get_source_full_name (registry, source);
 
 	if (e_source_has_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION)) {
 		ESourceAuthentication *extension;
@@ -217,7 +218,9 @@ e_credentials_prompter_impl_password_show_dialog (ECredentialsPrompterImplPasswo
 
 	dialog_parent = e_credentials_prompter_get_dialog_parent (prompter);
 
-	credentials_prompter_impl_password_get_prompt_strings (prompter_password->priv->auth_source, &title, &info_markup);
+	credentials_prompter_impl_password_get_prompt_strings (
+		e_credentials_prompter_get_registry (prompter),
+		prompter_password->priv->auth_source, &title, &info_markup);
 	if (prompter_password->priv->error_text && *prompter_password->priv->error_text) {
 		gchar *escaped = g_markup_printf_escaped ("%s", prompter_password->priv->error_text);
 
diff --git a/libedataserverui/e-credentials-prompter.c b/libedataserverui/e-credentials-prompter.c
index 3c3a072..40c39a7 100644
--- a/libedataserverui/e-credentials-prompter.c
+++ b/libedataserverui/e-credentials-prompter.c
@@ -29,6 +29,7 @@
 
 /* built-in credentials prompter implementations */
 #include "e-credentials-prompter-impl-password.h"
+#include "e-credentials-prompter-impl-google.h"
 
 typedef struct _ProcessPromptData {
 	GWeakRef *prompter;
@@ -1032,6 +1033,7 @@ e_credentials_prompter_class_init (ECredentialsPrompterClass *class)
 
 	/* Ensure built-in credential providers implementation types */
 	g_type_ensure (E_TYPE_CREDENTIALS_PROMPTER_IMPL_PASSWORD);
+	g_type_ensure (E_TYPE_CREDENTIALS_PROMPTER_IMPL_GOOGLE);
 }
 
 static void
diff --git a/libedataserverui/libedataserverui.h b/libedataserverui/libedataserverui.h
index 3223492..062eb09 100644
--- a/libedataserverui/libedataserverui.h
+++ b/libedataserverui/libedataserverui.h
@@ -23,6 +23,7 @@
 #include <libedataserverui/e-cell-renderer-color.h>
 #include <libedataserverui/e-credentials-prompter.h>
 #include <libedataserverui/e-credentials-prompter-impl.h>
+#include <libedataserverui/e-credentials-prompter-impl-google.h>
 #include <libedataserverui/e-credentials-prompter-impl-password.h>
 #include <libedataserverui/e-trust-prompt.h>
 #include <libedataserverui/e-webdav-discover-widget.h>
diff --git a/modules/google-backend/module-google-backend.c b/modules/google-backend/module-google-backend.c
index 6492b77..8ab4b2f 100644
--- a/modules/google-backend/module-google-backend.c
+++ b/modules/google-backend/module-google-backend.c
@@ -101,6 +101,90 @@ G_DEFINE_DYNAMIC_TYPE (
 	e_google_backend_factory,
 	E_TYPE_COLLECTION_BACKEND_FACTORY)
 
+static gboolean
+google_backend_can_use_google_auth (ESource *source)
+{
+	ESourceRegistryServer *registry;
+	gboolean res;
+
+	g_return_val_if_fail (E_IS_SERVER_SIDE_SOURCE (source), FALSE);
+
+	if (!e_source_credentials_google_is_supported ())
+		return FALSE;
+
+	registry = e_server_side_source_get_server (E_SERVER_SIDE_SOURCE (source));
+	g_object_ref (source);
+
+	while (source && e_source_get_parent (source)) {
+		ESource *adept_source;
+
+		adept_source = e_source_registry_server_ref_source (registry, e_source_get_parent (source));
+		if (adept_source) {
+			g_object_unref (source);
+			source = adept_source;
+		} else {
+			break;
+		}
+	}
+
+	res = !e_source_has_extension (source, E_SOURCE_EXTENSION_GOA) &&
+	      !e_source_has_extension (source, E_SOURCE_EXTENSION_UOA);
+
+	g_object_unref (source);
+
+	return res;
+}
+
+static gboolean
+host_ends_with (const gchar *host,
+		const gchar *ends_with)
+{
+	gint host_len, ends_with_len;
+
+	if (!host || !ends_with)
+		return FALSE;
+
+	host_len = strlen (host);
+	ends_with_len = strlen (ends_with);
+
+	if (host_len <= ends_with_len)
+		return FALSE;
+
+	return g_ascii_strcasecmp (host + host_len - ends_with_len, ends_with) == 0;
+}
+
+static void
+google_backend_mail_update_auth_method (ESource *source)
+{
+	EOAuth2Support *oauth2_support;
+	const gchar *method;
+
+	oauth2_support = e_server_side_source_ref_oauth2_support (E_SERVER_SIDE_SOURCE (source));
+
+	if (oauth2_support != NULL) {
+		method = "XOAUTH2";
+	} else if (google_backend_can_use_google_auth (source)) {
+		method = "Google";
+	} else {
+		method = NULL;
+	}
+
+	if (method) {
+		ESourceAuthentication *auth_extension;
+		gchar *host;
+
+		auth_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
+		host = e_source_authentication_dup_host (auth_extension);
+
+		if (host && (host_ends_with (host, ".gmail.com") || host_ends_with (host, ".googlemail.com")))
+			e_source_authentication_set_method (auth_extension, method);
+
+		g_free (host);
+	}
+
+	g_clear_object (&oauth2_support);
+}
+
 static void
 google_backend_calendar_update_auth_method (ESource *source)
 {
@@ -110,11 +194,10 @@ google_backend_calendar_update_auth_method (ESource *source)
 
 	oauth2_support = e_server_side_source_ref_oauth2_support (E_SERVER_SIDE_SOURCE (source));
 
-	/* The host name and WebDAV resource path depend on the
-	 * authentication method used, so update those here too. */
-
 	if (oauth2_support != NULL) {
 		method = "OAuth2";
+	} else if (google_backend_can_use_google_auth (source)) {
+		method = "Google";
 	} else {
 		method = "plain/password";
 	}
@@ -138,7 +221,15 @@ google_backend_contacts_update_auth_method (ESource *source)
 
 	extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
 	extension = e_source_get_extension (source, extension_name);
-	method = (oauth2_support != NULL) ? "OAuth2" : "ClientLogin";
+
+	if (oauth2_support != NULL)
+		method = "OAuth2";
+	else /* if (google_backend_can_use_google_auth (source)) */
+		method = "Google";
+	/* "ClientLogin" for Contacts is not supported anymore, thus
+	   force "Google" method regardless the evolution-data-server
+	   was compiled with it or not. */
+
 	e_source_authentication_set_method (extension, method);
 
 	g_clear_object (&oauth2_support);
@@ -325,6 +416,7 @@ google_backend_authenticate_sync (EBackend *backend,
 				  GError **error)
 {
 	ECollectionBackend *collection = E_COLLECTION_BACKEND (backend);
+	ESourceAuthentication *auth_extension = NULL;
 	ESourceCollection *collection_extension;
 	ESourceGoa *goa_extension = NULL;
 	ESource *source;
@@ -343,6 +435,8 @@ google_backend_authenticate_sync (EBackend *backend,
 	collection_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_COLLECTION);
 	if (e_source_has_extension (source, E_SOURCE_EXTENSION_GOA))
 		goa_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_GOA);
+	if (e_source_has_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION))
+		auth_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
 
 	g_return_val_if_fail (e_source_collection_get_calendar_enabled (collection_extension) ||
 		e_source_collection_get_contacts_enabled (collection_extension), E_SOURCE_AUTHENTICATION_ERROR);
@@ -362,11 +456,20 @@ google_backend_authenticate_sync (EBackend *backend,
 
 	google_backend_calendar_update_auth_method (source);
 
-	if (goa_extension)
+	if (goa_extension) {
 		calendar_url = e_source_goa_get_calendar_url (goa_extension);
-	else
+	} else {
 		calendar_url = "https://www.google.com/calendar/dav/";
 
+		if (auth_extension) {
+			gchar *method;
+
+			method = e_source_authentication_dup_method (auth_extension);
+			if (g_strcmp0 (method, "Google") == 0)
+				calendar_url = "https://apidata.googleusercontent.com/caldav/v2/";
+		}
+	}
+
 	if (e_source_collection_get_calendar_enabled (collection_extension) && calendar_url &&
 	    e_webdav_discover_sources_sync (source, calendar_url, E_WEBDAV_DISCOVER_SUPPORTS_NONE,
 		credentials, out_certificate_pem, out_certificate_errors,
@@ -452,8 +555,9 @@ google_backend_add_tasks (ECollectionBackend *backend)
 
 	collection_source = e_backend_get_source (E_BACKEND (backend));
 
-	/* Tasks require OAuth2, which is supported only through GOA */
-	if (!e_source_has_extension (collection_source, E_SOURCE_EXTENSION_GOA))
+	/* Tasks require OAuth2 */
+	if (!e_source_has_extension (collection_source, E_SOURCE_EXTENSION_GOA) &&
+	    !e_source_credentials_google_is_supported ())
 		return;
 
 	resource_id = GOOGLE_TASKS_RESOURCE_ID;
@@ -477,7 +581,10 @@ google_backend_add_tasks (ECollectionBackend *backend)
 	extension = e_source_get_extension (source, extension_name);
 
 	e_source_authentication_set_host (E_SOURCE_AUTHENTICATION (extension), "www.google.com");
-	e_source_authentication_set_method (E_SOURCE_AUTHENTICATION (extension), "OAuth2");
+	if (google_backend_can_use_google_auth (collection_source))
+		e_source_authentication_set_method (E_SOURCE_AUTHENTICATION (extension), "Google");
+	else
+		e_source_authentication_set_method (E_SOURCE_AUTHENTICATION (extension), "OAuth2");
 
 	e_binding_bind_property (
 		collection_extension, "identity",
@@ -589,15 +696,6 @@ google_backend_populate (ECollectionBackend *backend)
 		ESource *source = link->data;
 
 		have_tasks = have_tasks || e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST);
-		if (have_tasks) {
-			source = e_backend_get_source (E_BACKEND (backend));
-
-			/* Tasks require OAuth2, which is supported only through GOA */
-			if (!e_source_has_extension (source, E_SOURCE_EXTENSION_GOA)) {
-				e_source_remove_sync (source, NULL, NULL);
-				have_tasks = FALSE;
-			}
-		}
 	}
 	g_list_free_full (list, (GDestroyNotify) g_object_unref);
 
@@ -699,6 +797,15 @@ google_backend_child_added (ECollectionBackend *backend,
 			e_source_authentication_set_user (
 				auth_child_extension,
 				collection_identity);
+
+		if (e_source_has_extension (child_source, E_SOURCE_EXTENSION_MAIL_ACCOUNT) ||
+		    e_source_has_extension (child_source, E_SOURCE_EXTENSION_MAIL_TRANSPORT)) {
+			google_backend_mail_update_auth_method (child_source);
+			g_signal_connect (
+				child_source, "notify::oauth2-support",
+				G_CALLBACK (google_backend_mail_update_auth_method),
+				NULL);
+		}
 	}
 
 	/* Keep the calendar authentication method up-to-date.
diff --git a/modules/secret-monitor/module-secret-monitor.c b/modules/secret-monitor/module-secret-monitor.c
index de94992..07d12b0 100644
--- a/modules/secret-monitor/module-secret-monitor.c
+++ b/modules/secret-monitor/module-secret-monitor.c
@@ -131,6 +131,10 @@ secret_monitor_scan_secrets_thread (gpointer user_data)
 		if (uid == NULL)
 			continue;
 
+		/* These are special keys, not referencing any real ESource */
+		if (g_str_has_prefix (uid, "OAuth2::"))
+			continue;
+
 		source = e_source_registry_server_ref_source (server, uid);
 
 		/* If we find a matching ESource, update the SecretItem's
diff --git a/po/POTFILES.in b/po/POTFILES.in
index ea550a5..a01d86c 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -186,6 +186,7 @@ libedataserver/e-categories.c
 libedataserver/e-client.c
 libedataserver/e-source.c
 libedataserver/e-source-credentials-provider-impl.c
+libedataserver/e-source-credentials-provider-impl-google.c
 libedataserver/e-source-credentials-provider-impl-password.c
 libedataserver/e-source-mail-signature.c
 libedataserver/e-source-proxy.c
@@ -194,6 +195,7 @@ libedataserver/e-source-webdav.c
 libedataserver/e-time-utils.c
 libedataserver/e-webdav-discover.c
 libedataserverui/e-credentials-prompter.c
+libedataserverui/e-credentials-prompter-impl-google.c
 libedataserverui/e-credentials-prompter-impl-password.c
 libedataserverui/e-trust-prompt.c
 libedataserverui/e-webdav-discover-widget.c
