Byron / google-apis-rs

A binding and CLI generator for all Google APIs
http://byron.github.io/google-apis-rs
Other
1.02k stars 136 forks source link

google_drive3 scope error #229

Closed smokytheangel0 closed 5 years ago

smokytheangel0 commented 5 years ago

While I was trying this out with this code:

extern crate hyper;
extern crate hyper_rustls;
extern crate yup_oauth2 as oauth2;
extern crate google_drive3 as drive3;
use std::fs;
use std::path::Path;
use drive3::{Result, Error};
use std::default::Default;
use oauth2::{Authenticator, DefaultAuthenticatorDelegate, ApplicationSecret, MemoryStorage};
use drive3::Drive;

fn main() {

    let secret = yup_oauth2::read_application_secret(Path::new("credentials.json")).expect("failed to read secrets file");
    let auth = Authenticator::new(&secret, DefaultAuthenticatorDelegate,
                                    hyper::Client::with_connector(hyper::net::HttpsConnector::new(hyper_rustls::TlsClient::new())),
                                    <MemoryStorage as Default>::default(), None);

    let mut hub = Drive::new(hyper::Client::with_connector(hyper::net::HttpsConnector::new(hyper_rustls::TlsClient::new())), auth);

    let result = hub.files().list().doit();
    println!("{:?}", result);
}

I got this error: Err(MissingToken(StringError { error: "Invalid Scope: \'no description provided\'\n" }))

this may be because I was supposed to specify the scope somewhere, but this is absent from the examples I've come across. I also read in the google_drive3 cli repo or docs that the scopes are automatically generated (somehow) when not configured (http://byron.github.io/google-apis-rs/google_drive3_cli/index.html). The only other places I encountered this error on google are about people who had more than one space in between the scopes they sent to the server or other concatenation issues.

Kleptine commented 5 years ago

Did you ever figure this out?

Byron commented 5 years ago

Something that comes to my mind is to use the CLI's debugging features to see which scope is actually sent to the server. The CLI can also be used to provide different scopes as the user sees fit, and will handle (re)authentication against these scopes accordingly.

The API indeed comes pre-configured and guesses the correct scope to use based on the JSON schema from google. From my experience, this worked well.

Please feel free to leave comments here even though the issue is closed. There is an option to reopen it if it turns out to be an issue of this library.

Similar to #224 .

rshatch commented 5 years ago

I ran into this today, and discovered I had to manually specify the flow to Authenticator to be yup_oauth2::FlowType::InstalledRedirect or yup_oauth2::FlowType::InstalledInteractive - presumably it's incorrectly trying to use the Device flow even though that doesn't work for the Drive APIs. Probably an issue in yup_oauth2, which may or may not be fixed in later versions than what this crate is pinned to.

That said, I uncovered another scope issue (which I can open a new ticket for if necessary) - it looks like it requests a token for the broadest scope possible (https://www.googleapis.com/auth/drive) which Google is making restricted and subjecting apps that use it to very stringent (and expensive) security checks - https://developers.google.com/drive/api/v3/about-auth?hl=en_US#select_scopes_for_a_new_app

Byron commented 5 years ago

@rshatch If I remember correctly, it is hard-coded to use one specific flow. This might as well be the device flow, not knowing that individual APIs require a different one.

The scope issue you ran into regarding the drive API is an unfortunate side-effect of it guessing the scope if it's not provided. This is meant as convenience, but can more often than not be surprising as there is no way this can be predicted correctly in all cases.

The next iteration of these crates does not repeat that mistake.

cutler-scott-newrelic commented 4 years ago

If anyone else is reading this, I ran into the same issue and solved it with per-request scopes and specifying the flow type...

// Start with GDrive auth - based on example code from drive3 API and yup-oauth2
    let secret: ApplicationSecret =  yup_oauth2::read_application_secret(Path::new("clientsecret.json"))
        .expect("clientsecret.json");
    let token_storage = DiskTokenStorage::new(&String::from("temp_token")).unwrap();
    let mut auth = Authenticator::new(&secret, DefaultAuthenticatorDelegate,
                                      hyper::Client::with_connector(hyper::net::HttpsConnector::new(hyper_rustls::TlsClient::new())),
                                      token_storage, Some(FlowType::InstalledInteractive));

    let mut hub = DriveHub::new(hyper::Client::with_connector(hyper::net::HttpsConnector::new(hyper_rustls::TlsClient::new())), auth);

    // get some initial info about the file
    let fields = "kind, id, name, mimeType, webViewLink, modifiedTime, parents";
    let fileid = arg_matches.value_of("GDRIVEID").unwrap(); // using clap library
    let hub_result = hub.files().get(fileid).add_scope(Scope::Readonly).param("fields",fields).doit();
    let (file_response,file_object) = match hub_result {
        Ok(x) => x,
        Err(e) => return Err(SimpleError::new(format!("failed accessing Google Metadata API {:?}", e)))
    };