I am trying to learn about the Google's Drive API while I am writing a little program. I am using GoogleWebAuthorizationBroker class for asking the user to give the app permissions, but the drawback is that it is automatically opening a new browser tab in Chrome (as an example):
const string ApplicationName = "TestApp";
string[] scopes = { DriveService.Scope.Drive };
UserCredential credential;
using (var stream =
new FileStream("client_secret.json", FileMode.Open, FileAccess.Read))
{
string credPath = System.Environment.GetFolderPath(
System.Environment.SpecialFolder.Personal);
credPath = Path.Combine(credPath, ".credentials/drive-dotnet-quickstart");
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
scopes,
"user",
CancellationToken.None,
new FileDataStore(credPath, true)).Result;
Console.WriteLine("Credential file saved to: " + credPath);
}
I would like to know if there is a way to get the AuthorizationURL so I can show a browser dialog with it, and therefore get the respective token.
How can that be done? Thanks in advance.
Currently the way the client library is written it will automatically open it in the default browser.
There is an issue request already for this feature. GoogleWebAuthorizationBroker in embedded browser? #547
Answer: There is currently no way to get the Authorization url so that you can sow the dialog on your own. The client library is an open source project so you can add it.
Related
I'm working on a .NET desktop app that needs interaction with Youtube APIs.
I'm following the first sample at https://developers.google.com/youtube/v3/code_samples/dotnet
My code runs fine, but I want to force the user to select the right Google account every time the application starts.
I've tried the following modification of the above sample:
UserCredential credential;
using (var stream = new FileStream("Assets\\client_secret.json", FileMode.Open, FileAccess.Read))
{
credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecretsStream = stream,
Prompt = "select_account" // <-- This line does nothing...
},
new[] { YouTubeService.Scope.YoutubeReadonly },
"user",
CancellationToken.None
);
}
To my understanding, based on this old similar question, adding the Prompt = "select_account" parameter should do the trick but in reality it doesn't work as expected.
Infact it appears that the credentials are somewhat cached and the desired prompt never appears.
I've even tried to recompile the code in this way:
//omissis...
new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets(), // <- no reference to the stream!
Prompt = "select_account"
},
and with my big surprise the app still work without any reference to the client_secret.json stream! Authentication is skipped completely after the user successfully logs in for the first time
I'm curious how authentication on a Linux headless server works without an internet browser in C# for their Sheetsv4 API. All the code snippets they have on their getting started page have reference to GoogleWebAuthorizationBroker, which doesn't work without a web-browser. I've seen references to GoogleAuthorizationCodeFlow, but I'm not sure if this is what I'm looking for - nor how you would be intended to use it. My current code is bellow-
UserCredential Credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(Stream).Secrets,
new string[1] { SheetsService.Scope.SpreadsheetsReadonly },
"user",
CancellationToken.None,
new FileDataStore(GreetFurConfiguration.TokenFile, true)
).Result;
What I would want is for a link to be generated and, from there, to be able to paste the token into the console, as this is how other Google APIs handle this authentication (Go, NodeJS) which I have used before and seem to work well.
If there is any better way to handle this authentication though that is more suitable for a .NET workflow, that would be suitable as well. I can't manage to find any examples of how you'd get a OAuth2 token for the life of me though without having access to a web browser on the host machine.
EDIT: I'd be looking for https://github.com/googleapis/google-api-dotnet-client/blob/master/Src/Support/Google.Apis.Auth/OAuth2/PromptCodeReceiver.cs
However, I can't find any documentation on how to use this class.
The GoogleWebAuthorizationBroker.AuthorizeAsync allows you to send which code receiver you want The one you are looking for is PromptCodeReceiver.
private const string PathToCredentialFile = "/home/linda/development/creds/client.json";
static async Task Main(string[] args)
{
var scope = new[] {AnalyticsReportingService.Scope.AnalyticsReadonly};
var credPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
await using (var stream = new FileStream(PathToCredentialFile, FileMode.Open, FileAccess.Read))
{
// Requesting Authentication or loading previously stored authentication for userName
var credential = GoogleWebAuthorizationBroker.AuthorizeAsync(GoogleClientSecrets.Load(stream).Secrets,
scope,
"userName",
CancellationToken.None,
new FileDataStore(credPath, true),
new PromptCodeReceiver()).Result;
Console.WriteLine($"AccessToken: {credential.Token.AccessToken}");
}
Output:
Please visit the following URL in a web browser, then enter the code shown after authorization:
https://accounts.google.com/o/oauth2/v2/auth?access_type=offline&response_type=code&client_id=1XXXXX.apps.googleusercontent.com&redirect_uri=urn%3Aietf%3Awg%3Aoauth%3A2.0%3Aoob&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fanalytics.readonly
Please enter code:
4/1AY0e-g6L3ASB0lEhWNkh4lDc4nl5k0xV177o38taWFzEzKBv3H24ZC4zQAM
Access toekn: ya29.a0AfH6SMB8ZhpZJgKkpMfbiflxeOF_o6Gzs6fxIuPI25Vewbp7NgVAfJp8EX6K5zgielRrYaSFjqwKIY8MoCuCDbPeF5-2w6_WRnauWqtpleqk2zjqmkHgpfNwbpO8n7VmHVSF9Mgn3YOZRl
The credentials authorizing access to gmailAPI seem to be cached somehow. When I change the client ID and secret, and email address, nothing changes. Also, when I change the scope, nothing changes. I am trying to force the credentials to refresh. Is there a way to force the credentials to reprompt the user?
Is there a file I can find and delete?
I am using c# and the gmail api package from nuget. The code for credentialing is:
_emailAddress = Settings.EmailAddress;
string clientSecret = Settings.ClientSecret;
string clientId = Settings.ClientId;
ClientSecrets clientSecrets = new ClientSecrets {ClientId = clientId, ClientSecret = clientSecret};
UserCredential credential;
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
clientSecrets,
Scopes,
"user",
CancellationToken.None).Result;
_service = new GmailService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "Draft Sender", //applicationName,
});
Also, once I credential, my scope is set: cannot change scope until I delete and renew the secret and clientID. Also, it takes two run-throughs in order to generate the credentials, the first resulting in an error, but the second resulting in a prompt to authorize.
I'm pretty sure that this is happening because the credentials are cached somewhere, somehow, even though I haven't specified any storage...but I don't know for sure. Either way, the issue seems to depend on code inside the gmail api package, but I am having trouble finding a solution for what I am dealing with.
Ultimately, I need to deploy a functionality to an ASP website that will access the gmail account as a repository for emails, so I need to have this authorization thing figured out so that the server will have access to the email account upon deployment (or easily after). It is not really necessary (but desired) that it happen without the intermediate error.
Is there a solution (easy or hard) to control this caching process and handle the permissions?
If you don't specify where you are storing the response token, it's stored on:
C:\Users\'Your user'\AppData\Roaming\Google.Apis.Auth
If you want to specify the directory look at this:
(note: you just need to create the directory App_Data in your project)
string path = HostingEnvironment.MapPath("~/App_Data");
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
new ClientSecrets
{
ClientId = "Client_ID",
ClientSecret = "Secret_Client"
},
Scopes,
"me",
CancellationToken.None,
new FileDataStore(path)
).Result;
I am using a code example at http://www.daimto.com/google-oauth2-csharp/ which would allow me to connect to the google drive api and get authorized using oauth2. I downloaded the .json file that has my client id and client secret and put it in the \bin\debug output folder (and a few other places out of desperation) and when the code progresses to opening that file, it causes an exception which says 'stream handle' threw an exception of type 'System.Object.DisposeException'
The code throws an exception here (at the credential line) when stepping through:
using (var stream = new FileStream("client_secrets.json", FileMode.Open, FileAccess.Read))
{
GoogleWebAuthorizationBroker.Folder = "Tasks.Auth.Store";
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
new[] { DriveService.Scope.Drive,
DriveService.Scope.DriveFile },
"user",
CancellationToken.None,
new FileDataStore("Drive.Auth.Store")).Result;
}
This is probably the 4th or 5th code example I have tried, and none of them seem to work. It is quite disheartening.
I can see that that tutorial is very out of date. I am not sure why you are unable to use stream reader to read the file. But I think that you will find this code much cleaner and easer to understand.
string _client_id = "from apis console";
string _client_secret = "frome apis console";
string[] scopes = new string[] {DriveService.Scope.Drive,
DriveService.Scope.DriveFile };
// here is where we Request the user to give us access,
// or use the Refresh Token that was previously stored in %AppData%
UserCredential credential =
GoogleWebAuthorizationBroker.AuthorizeAsync(new ClientSecrets {ClientId = _client_id,
ClientSecret = _client_secret },
scopes,
Environment.UserName,
CancellationToken.None,
new FileDataStore("Daimto.GooglePlus.Auth.Store")
).Result;
I will update that tutorial ASAP.
You are using an Async method to do your read. This is normally fine, but that means the code is still executing when you get to the end of your using statement. When the end bracket of the using is reached, the filestream is disposed while your async method is still trying to use it.
Try removing your using statement and instead disposing of the stream once your authorization is finished.
I am retriving a token through an website, then I save the token, and refresh token in a database. Then I am writing .net program that uploads video's located on our server to youtube. My problem is to get the program I am making, use the stored token. I am using one of google's examples to upload the video. But the program should use the already saved token, instead os asking for new credentials.
The current code is this.
UserCredential credential;
//credential.UserCredential(,"",)
using (var stream = new FileStream("client_secrets.json", FileMode.Open, FileAccess.Read))
{
credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
// This OAuth 2.0 access scope allows an application to upload files to the
// authenticated user's YouTube channel, but doesn't allow other types of access.
new[] { YouTubeService.Scope.YoutubeUpload },
"user",
CancellationToken.None
);
}
Console.WriteLine("HER");
var youtubeService = new YouTubeService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = Assembly.GetExecutingAssembly().GetName().Name
});
So instead I would like to use an already saved token.
Any help, or a pointer in the right direction would be great. Thanks.
You would use GoogleAuthorizationCodeFlow instead of GoogleWebAuthorizationBroker in order to use the refresh token. See the answer here.