[evolution-data-server] Bug 749974 - Use OAuth2 for Google sources



commit 3ed35881531088dd9eceb1a800390daf3fb214b3
Author: Milan Crha <mcrha redhat com>
Date:   Mon Sep 21 15:36:32 2015 +0200

    Bug 749974 - Use OAuth2 for Google sources

 .../backends/google/e-book-backend-google.c        |  133 +--
 .../backends/google/e-gdata-oauth2-authorizer.c    |   40 +-
 .../backends/google/e-gdata-oauth2-authorizer.h    |    6 +
 addressbook/libebook/e-book-client-view.c          |    2 +
 calendar/backends/caldav/e-cal-backend-caldav.c    |  210 ++--
 calendar/backends/gtasks/e-cal-backend-gtasks.c    |  173 +++-
 .../backends/gtasks/e-gdata-oauth2-authorizer.c    |   40 +-
 .../backends/gtasks/e-gdata-oauth2-authorizer.h    |    6 +
 camel/providers/imapx/camel-imapx-command.c        |    2 +
 camel/providers/imapx/camel-imapx-server.c         |    3 +-
 camel/providers/smtp/camel-smtp-transport.c        |    4 +-
 configure.ac                                       |   39 +
 libedataserver/Makefile.am                         |    4 +
 libedataserver/e-data-server-util.c                |   38 +
 libedataserver/e-data-server-util.h                |    8 +-
 .../e-source-credentials-provider-impl-google.c    |  475 ++++++++
 .../e-source-credentials-provider-impl-google.h    |   99 ++
 libedataserver/e-source-credentials-provider.c     |    2 +
 libedataserver/e-webdav-discover.c                 |   54 +-
 libedataserver/libedataserver.h                    |    1 +
 libedataserverui/Makefile.am                       |    4 +
 .../e-credentials-prompter-impl-google.c           | 1127 ++++++++++++++++++++
 .../e-credentials-prompter-impl-google.h           |   78 ++
 .../e-credentials-prompter-impl-password.c         |    9 +-
 libedataserverui/e-credentials-prompter.c          |    2 +
 libedataserverui/libedataserverui.h                |    1 +
 modules/google-backend/module-google-backend.c     |  143 +++-
 modules/secret-monitor/module-secret-monitor.c     |    4 +
 po/POTFILES.in                                     |    2 +
 29 files changed, 2416 insertions(+), 293 deletions(-)
---
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 e70fd95..0d5356d 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 9c459ba..a91443f 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


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]