tdf / libcmis

C/C++ CMIS client library
GNU General Public License v2.0
21 stars 20 forks source link

Failure logging into some Google Drive accounts, but not into others #22

Open stbergmann opened 5 years ago

stbergmann commented 5 years ago

Maybe somebody here can help me with the following problem: Using LibreOffice (which internally uses libcmis) to log into Google Drive (in LibreOffice, "File - Open Remote... - Add service - Type: Google Drive", enter "User" and "Password" and click "OK"), with two different @googlemail.com accounts, it consistently succeeds for one of them while it consistently fails with the other. (This is with https://github.com/tdf/libcmis/pull/20 "Properly encode OAuth2 credentials" included, so it is unlikely that this is due to differences like e.g. the exact password spelling.) Debugging into OAuth2Providers::OAuth2Gdrive in src/libcmis/oauth2-providers.cxx (with passing verbose = true into SessionFactory::createSession in src/libcmis/session-factory.cxx, plus some printf), the two cases proceed the same up until sending loginPasswdPost as POST request (in // STEP 3: password page). In both cases, loginPasswdPost looks like (broken up for better readability)

Page=PasswordSeparationSignIn &GALX=XXXXXXXXXXX &gxf=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXQ%3AXXXXXXXXXXXXX &continue=https%3A%2F%2Faccounts.google.com%2Fo%2Foauth2%2Fauth%3Fscope%3Dhttps%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive%26redirect_uri%3Durn%3Aietf%3Awg%3Aoauth%3A2.0%3Aoob%26response_type%3Dcode%26client_id%3D280867422816-9jc83t6phkfb2q8p94dtk8kr5fa8af3r.apps.googleusercontent.com%26from_login%3D1%26as%3DXXXXXXXXXXXXXXXXXXXXXX &followup=https%3A%2F%2Faccounts.google.com%2Fo%2Foauth2%2Fauth%3Fscope%3Dhttps%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive%26redirect_uri%3Durn%3Aietf%3Awg%3Aoauth%3A2.0%3Aoob%26response_type%3Dcode%26client_id%3D280867422816-9jc83t6phkfb2q8p94dtk8kr5fa8af3r.apps.googleusercontent.com%26from_login%3D1%26as%3DXXXXXXXXXXXXXXXXXXXXXX &ltmpl=nosignup &scc=1 &sarp=1 &oauth=1 &flowName=GlifWebSignIn &ProfileInformation=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX &SessionState=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX &_utf8=%E2%98%83 &bgresponse=js_disabled &Email=XXXXXXXXXXXXXXXXXXXXXXXXXX%40googlemail.com &signIn=Anmelden &PersistentCookie=yes &Passwd=XXXXXXXXXX

However, the responses from the server differ. In the successful case, we get a 302 Moved Temporarily response with Location: https://accounts.google.de/accounts/SetSID?ssdc=..., ultimately GETing a /o/oauth2/auth?scope=https://www.googleapis.com/auth/drive&redirect_uri=urn:ietf:wg:oauth:2.0:oob&response_type=code&client_id=... HTML page titled "Request for Permission" and which is apparently as expected by the next call into parseResponse, causing OAuth2Providers::OAuth2Gdrive to return successfully with some non-empty code. But in the failing case, we directly get a 200 OK HTML page titled "Anmelden – Google Konten" (stuff is apparently localized on the server end, I'm doing this from Germany) which is apparently not as expected by the next call into parseResponse, and which causes OAuth2Providers::OAuth2Gdrive to fail returning an empty code. (And when deliberately trying with a bad user name, or with the otherwise successful user name and a wrong password, behavior is different still, ruling out the assumption that the failing case's user name or password is bad: For a bad user name, the HTML page retrieved in // STEP 2: send email already is different, containing some has-error stuff. And for a bad password, the HTML page retrieved in // STEP 3: password page contains such has-error stuff.) This may not even be an issue with libcmis. It smells like something is set up differently for those two Google accounts, but I have no idea what that could be. (And I get reports from other people too that their accounts don't work, in the same way.) Does anybody have an idea what might go wrong?

ferdnyc commented 5 years ago

Apologies for butting in on this, hopefully this info is helpful and not merely me saying things that are already well known.

I was surprised, when I encountered this today in LibreOffice 6.1.5.2 on Fedora 29. Not that authentication didn't work, but that LibreOffice was trying to use its own username/password dialog to do it. My experience has been that all such methods stopped working quite a while ago, especially where Google Drive is concerned. And the comments in gdata/gdata-oauth2-authorizer.c from libgdata would seem to bear that out.

libgdata is the library that powers Gnome Online Accounts' functionality for Google services, and the authentication process there is just as they describe in the code: You go into the Online Accounts pane of the Settings application, indicate that you want to add a new Google account to your online accounts list, and the app immediately pops up an embedded web browser. Google's page handles all of the interaction with the user until authentication is complete.

image

Their comments sketch out the flow, for more detail see the complete gdata-oauth2-authorizer.c source.

/* #GDataOAuth2Authorizer provides an implementation of the #GDataAuthorizer
 * interface for authentication and authorization using the
 * <ulink type="http" url="https://developers.google.com/accounts/docs/OAuth2InstalledApp">OAuth 2.0</ulink>
 * process, which is Google’s currently preferred authentication and
 * authorization process.
 *
 * OAuth 2.0 replaces the deprecated OAuth 1.0 and ClientLogin processes. One of
 * the main reasons for this is to allow two-factor authentication to be
 * supported, by moving the authentication interface to a web page under
 * Google’s control.
 *
 * The OAuth 2.0 process as implemented by Google follows the
 * <ulink type="http" url="http://tools.ietf.org/html/rfc6749">OAuth 2.0
 * protocol as specified by IETF in RFC 6749</ulink>, with a few additions to
 * support scopes (implemented in libgdata by #GDataAuthorizationDomains),
 * locales and custom domains. Briefly, the process is initiated by building an
 * authentication URI (using gdata_oauth2_authorizer_build_authentication_uri())
 * and opening it in the user’s web browser. The user authenticates and
 * authorizes the requested scopes on Google’s website, then an authorization
 * code is returned (via #GDataOAuth2Authorizer:redirect-uri) to the
 * application, which then converts the code into an access and refresh token
 * (using gdata_oauth2_authorizer_request_authorization()). The access token is
 * then attached to all future requests to the online service, and the refresh
 * token can be used in future (with gdata_authorizer_refresh_authorization())
 * to refresh authorization after the access token expires.
 *
 * The refresh token may also be accessed as
 * #GDataOAuth2Authorizer:refresh-token and saved by the application. It may
 * later be set on a new instance of #GDataOAuth2Authorizer, and
 * gdata_authorizer_refresh_authorization_async() called to establish a new
 * access token without requiring the user to re-authenticate unless they have
 * explicitly revoked the refresh token.
 *
 * For an overview of the standard OAuth 2.0 flow, see
 * <ulink type="http" url="http://tools.ietf.org/html/rfc6749#section-1.2">RFC 6749</ulink>.
 */

```c
/* <example>
 *  <title>Authenticating Asynchronously Using OAuth 2.0</title>
 *  <programlisting>
 *  GDataSomeService *service;
 *  GDataOAuth2Authorizer *authorizer;
 *  gchar *authentication_uri, *authorization_code;
 *
 *  /<!-- -->* Create an authorizer and authenticate and authorize the service we're using, asynchronously. *<!-- -->/
 *  authorizer = gdata_oauth2_authorizer_new ("some-client-id", "some-client-secret",
 *                                            GDATA_OAUTH2_REDIRECT_URI_OOB, GDATA_TYPE_SOME_SERVICE);
 *  authentication_uri = gdata_oauth2_authorizer_build_authentication_uri (authorizer, NULL, FALSE);
 *
 *  /<!-- -->* (Present the page at the authentication URI to the user, either in an embedded or stand-alone web browser, and
 *   * ask them to grant access to the application and return the code Google gives them.) *<!-- -->/
 *  authorization_code = ask_user_for_code (authentication_uri);
 *
 *  gdata_oauth2_authorizer_request_authorization_async (authorizer, authorization_code, cancellable,
 *                                                       (GAsyncReadyCallback) request_authorization_cb, user_data);
 */

...Etc. Google have really locked down their authentication flow in recent years, and any application that's imposing itself into the password-collection process (and therefore potentially capturing or misusing users' credentials) doesn't seem to have much chance of remaining able to authenticate — even if it works for some users today, Google would prefer that it not in the future.

papermache commented 4 years ago

Confirming 6-digit pin bug still exists in 6.3.3.

Freso commented 3 years ago

FWIW, I still get this in 7.0.4.2 00(Build:2).

MeggyCal commented 3 years ago

Hi, I see it is still open... you can find some recent patches addressing this issue in https://github.com/LibreOffice/core/tree/master/external/libcmis, it would be very helpful if you merged them upstream (for reference see https://bugs.documentfoundation.org/show_bug.cgi?id=101630).