I must be stupid I think. Using OAuth.
Have the following piece of code
// Authenticate with Google
using (MemoryStream stream =
new MemoryStream(GetSecrets()))
{
string credPath = "token.json";
this.userCredential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
scopes,
"user",
CancellationToken.None,
new FileDataStore(credPath, true)).Result;
Console.WriteLine("Credential file saved to: " + credPath);
}
Works fine and sets this.userCredential as expected
Now I want to create a StorageClient
But StorageClient cannot be created directly from UserCredential.
The only way I can see is to create from the AccessToken
GoogleCredential googleCredential = GoogleCredential.FromAccessToken(this.userCredential.Token.AccessToken);
return StorageClient.Create(googleCredential);
Problem is this token expires after an hour.
However, in C# I cannot for the life of me find a way to pass in the refresh token (which I have in the UserCredential) and have it refresh itself as needed
It seems possible in Java though, using the .Builder
Does anyone know if this is possible ?
The simplest way of achieving this is to create the StorageService yourself, and then pass that to the StorageClientImpl constructor:
var service = new StorageService(new BaseClientService.Initializer
{
HttpClientInitializer = userCredential,
ApplicationName = StorageClientImpl.ApplicationName,
});
StorageClient client = new StorageClientImpl(service);
We might consider adding an overload of Create that takes an IConfigurableHttpClientInitializer instead of a GoogleCredential, in the future.
Related
I'm new to C# and I'm creating a WPF C# app. I want use Google Drive Rest API to send backup file to Google Drive. But this isn't problem. When I wrote Android app in Java, getting account name was very easy. But in C# and my desktop app, I don't now how do this. I read many topic and Google documentation, but I didn't find answer.
Here is my code for Helper class. I only want to get the account name or address and display it in the main window.
I found a solution with Google+ API, but now it doesn't work because Google+ doesn't exist.
Is there any way to do this?
UserCredential credential;
public DriveService GetGoogleDriveService()
{
using (var stream =
new FileStream("credentials.json", FileMode.Open, FileAccess.Read))
{
// The file token.json stores the user's access and refresh tokens, and is created
// automatically when the authorization flow completes for the first time.
string credPath = System.Environment.GetFolderPath(
System.Environment.SpecialFolder.Personal);
credPath = Path.Combine(credPath, ".credentials/menadzer_zespolow_token.json");
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
Scopes,
"user",
CancellationToken.None,
new FileDataStore(credPath, true)).Result;
var service = new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = ApplicationName,
});
return service;
}
}
public String GetAccountEmail()
{
// code to get account name or email there
return accountNameOrEmail;
}
I get credentials using code
static string[] Scopes = { "https://www.googleapis.com/auth/userinfo.email" };
private static UserCredential GenerateCredential()
{
UserCredential credential;
using (var stream = new FileStream("credentials.json", FileMode.Open, FileAccess.Read))
{
// The file token.json stores the user's access and refresh tokens, and is created
// automatically when the authorization flow completes for the first time.
string credPath = "token.json";
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
Scopes,
"user",
CancellationToken.None,
new FileDataStore(credPath, true)).Result;
Console.WriteLine("Credential file saved to: " + credPath);
}
return credential;
}
How to get email from this credential? I've tried code
private string GetEmailFromCredentials(UserCredential credential)
{
var plusService = new PlusService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "My Application",
});
var me = plusService.People.Get("me").Execute();
var useremail = me.Emails.FirstOrDefault().Value;
return useremail;
}
but it looks like that People.Get("me") is not possibe anymore. I'm getting error "Google.Apis.Requests.RequestError
Legacy People API has not been used in project 618254727025 before or it is disabled"
solution is to get access token and try https://www.googleapis.com/oauth2/v2/userinfo?access_token=
In your scopes variable. Try and just use the value "email" not the
full https address. Scope keywords in the web link are separated by spaces. Here is a way that I do this to obtain the scopes: profile email openid.
To test this approach, you can manually paste the below weblink into a browser after obtaining the access code:
https://www.googleapis.com/oauth2/v2/userinfo?access_token=[PASTE ACCESS CODE HERE]&[PASTE VALUE FROM THE BELOW VARIABLE authorizationRequest HERE]
fyi: I was ammending the demonstration code available: https://github.com/googlesamples/oauth-apps-for-windows.
// Creates the OAuth 2.0 authorization request.
string authorizationRequest = string.Format("{0}?response_type=code&scope=openid%20profile%20email&redirect_uri={1}&client_id={2}&state={3}&code_challenge={4}&code_challenge_method={5}",
authorizationEndpoint,
System.Uri.EscapeDataString(redirectURI),
clientID,
state,
code_challenge,
code_challenge_method);
You can use the users.getprofile it will return the email address of the user who is currently authenticated.
request
var service = new GmailService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "My Application",
});
var user = service.Users.GetProfile("me").Execute;
response
{
"emailAddress": "xxxx1#gmail.com",
"messagesTotal": 8394,
"threadsTotal": 1494,
"historyId": "605503"
}
People me
The correct usage of people.get is "people/me"
var request = plusService.People.Get("people/me")
request.PersonFields("emailAddresses");
var response = request.Execute();
I have been stuck on this issue for several days. And finally, thanks to this link (Check if user is already logged in), I learned that the parameter input, "user", to be the key issue. This "user" should be the windows login user (you can use Enviroment.Username), not the programmer or APP user. The GoogleWebAuthorizationBroker.AuthorizeAsync uses this username to save its credential in the location:
C:\Users[username]\AppData\Roaming\Google.Apis.Auth\Google.Apis.Auth.OAuth2.Responses.TokenResponse-[username]
(something like this).
So if you feed "user" to AuthorizeAsync, the credential saving could be a problem, and your app will hang or lag seriously. And, later when you want to use the cred file to get userinfo and email, it will be problematic (lag seriously). In my case, user info will be all missing, leaving only an email address. Also, you have to include the required two scopes: "https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/userinfo.profile". Hope these helps.
Add 'https://www.googleapis.com/auth/userinfo.email' in scopes.
In callback you will code. Get tokens json from this code using
oauth2Client.
This json contains id_token which is basically a jwt
token, parse it u will get email.
I'm trying the code below, obtained from their docs here. I'm unable to compile, I get an exception message:
Maybe I'm not understanding the TeamDrive concept correctly?
I'm not sure where the issue is. Below is the code snippet I have:
var teamDriveMetadata = new TeamDrive()
{
Name = "Project Resources"
};
var requestId = System.Guid.NewGuid().ToString();
var request = service.Teamdrives.Create(teamDriveMetadata, requestId);
request.Fields = "id";
var teamDrive = request.Execute();
Console.WriteLine("Team Drive ID: " + teamDrive.Id);
Console.WriteLine("Done.");
Console.Read();
Below is the auth setup:
using (var stream =
new FileStream("client_secret.json", FileMode.Open, FileAccess.ReadWrite))
{
string credPath = System.Environment.GetFolderPath(
System.Environment.SpecialFolder.Personal);
credPath = Path.Combine(credPath, ".credentials/drive-dotnet-quickstart.json");
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
Scopes,
"MyKey",
CancellationToken.None,
new FileDataStore(credPath, true)).Result;
Console.WriteLine("Credential file saved to: " + credPath);
}
// Create Drive API service.
var service = new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = ApplicationName
});
FYI: I ran the sample code linked without issue. I have a test project that has the proper permissions already established. I pasted the linked code into that project, and had to resolve "var request" conflicting with the same in my existing code. Once resolved the code ran without issue.
As previously suggested I would recommend focusing on resolving your permissions/scopes.
I haven't been able to create an Spreadsheet in GoogleDocs using new SpreadSheet API, since it is now supported instead using Google Drive API.
All the examples I've found are for creating and modifying Sheets, not the main spreadsheet.
static string[] Scopes = { SheetsService.Scope.Spreadsheets };
var credential = GoogleWebAuthorizationBroker.AuthorizeAsync(new ClientSecrets {
ClientId = clientId, // FROM JSON
ClientSecret = clientSecret // FROM JSON
},
Scopes, Environment.UserName, CancellationToken.None,
new FileDataStore("xxIDxx.GoogleDrive.Auth.Store")).Result;
var service = new SheetsService(new BaseClientService.Initializer() {
HttpClientInitializer = credential,
ApplicationName = "Google Sheets API Project",
});
string SpreadSheetID = "AVeryLongAndRandomStringID";
Spreadsheet SpSheet = new Spreadsheet();
SpSheet.Properties = new SpreadsheetProperties();
SpSheet.SpreadsheetId = SpreadSheetID;
SpSheet.Properties.Title = "I HATE THIS SPREADSHEET";
Sheet MySheet = new Sheet();
MySheet.Properties = new SheetProperties();
MySheet.Properties.Title = "MySheet";
MySheet.Properties.SheetId = 34213312;
MySheet.Properties.SheetType = "GRID";
var SheetSet = new List<Sheet>();
SheetSet.Add(MySheet);
SpSheet.Sheets = SheetSet;
var MyNewSpreadSheet = service.Spreadsheets.Create(SpSheet).Execute();
Thanks!
UPDATE:
The small version "var MyNewSpreadSheet" indeed worked (my last attempts also did) but... I haven't realized it was saving the document in MY googleDrive instead my client's account.
What I was trying to accomplish was to create an App where anyone with a google account could create or alter a Spreadsheet document in a "repository" account.
The file "client_secret.json" was generated from my client's account, I don't know why the code creates the Spreadsheet on the logged gmail account.
Any ideas?
Thanks!
You may want to try sending HTTP request with this format:
POST https://sheets.googleapis.com/v4/spreadsheets
As mentioned in Method: spreadsheets.create, if request is successful, the response body contains a newly created instance of Spreadsheet.
Also, please note that using this method requires on of the following OAuth scopes:
https://www.googleapis.com/auth/drive
https://www.googleapis.com/auth/spreadsheets
Furthermore, please try to also check this documentation if it can help. It's about the Sheets API using C#.
All day long I'm trying to delete/add video to my YouTube playlists. I'm using the YouTube Data API v.3.0. for .NET C#.
I have already created a project in Google Developer Console and got my client secrets JSON file. Also my code for obtaining list items is working ok which means that only the PUT operations are not working as expected. I have used almost the same code as in the google developers site code examples.
Authentication method:
private async Task<YouTubeService> GetYouTubeService(string userEmail)
{
UserCredential credential;
using (var stream = new FileStream("client_secrets.json", FileMode.Open, FileAccess.Read))
{
credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
new[]
{
YouTubeService.Scope.Youtube,
YouTubeService.Scope.Youtubepartner,
YouTubeService.Scope.YoutubeUpload,
YouTubeService.Scope.YoutubepartnerChannelAudit,
YouTubeService.Scope.YoutubeReadonly
},
userEmail,
CancellationToken.None,
new FileDataStore(this.GetType().ToString()));
}
var youtubeService = new YouTubeService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = this.GetType().ToString()
});
return youtubeService;
}
Add video to playlist code:
private async Task AddSongToPlaylistAsync(string userEmail, string songId, string playlistId)
{
var youtubeService = await this.GetYouTubeService(userEmail);
var newPlaylistItem = new PlaylistItem();
newPlaylistItem.Snippet = new PlaylistItemSnippet();
newPlaylistItem.Snippet.PlaylistId = playlistId;
newPlaylistItem.Snippet.ResourceId = new ResourceId();
newPlaylistItem.Snippet.ResourceId.Kind = "youtube#video";
newPlaylistItem.Snippet.ResourceId.VideoId = songId;
newPlaylistItem = await youtubeService.PlaylistItems.Insert(newPlaylistItem, "snippet").ExecuteAsync();
}
this is the message that I receive when I try to add a new video to the specified playlist:
Google.Apis.Requests.RequestError
Insufficient Permission [403]
Errors [
Message[Insufficient Permission] Location[ - ] Reason[insufficientPermissions] Domain[global]
]
I'll really appreciate any available help because I didn't find anything useful googling.
Thank you in advance!
I encountered the same issue but if anyone is having issue with the inefficient permission when adding a video to a playlist, you will need to have the YouTubeService.Scope.Youtube (which looks like you already have).
var scopes = new[]
{
YouTubeService.Scope.Youtube
};
If you however added the scope after you already have given the permission, you will need to revoke the client by going to this page manage permissions. You will have to look for your specific client. After you've done that, you can rerun you app and request for permission once again.
Another option is to create a new clientId and clientSecret again and make sure you have the right scope to begin with. I hope this helps.