Alexa skill w/ Account Linking and gmail API .NET - Authentication - c#

I have created an Alexa skill and enabled account linking. Setup my skills configuration to point to googles authorization and token servers, the skill successfully links. Then alexa sends POST Requests to my service that include the access token and state from Google. I use this to validate the token and what I want to do is use the token to access gmail api and begin calling actions.
However, the only examples online I can find include re-building the gmailservice by using the client secret and client id again. I figure since I have the access token already, why do I need to include the secret and ID again? How can i make gmail api calls with the access token I already have, or is this not possible? Here's the code I'm trying to use:
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/gmail-dotnet-quickstart.json");
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
Scopes,
"user",
CancellationToken.None,
new FileDataStore(credPath, true)).Result;
Console.WriteLine("Credential file saved to: " + credPath);
}
// Create Gmail API service.
var service = new GmailService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = ApplicationName,
});
// Define parameters of request.
UsersResource.LabelsResource.ListRequest request = service.Users.Labels.List("me");
// List labels.
IList<Label> labels= request.Execute().Labels;
Console.WriteLine("Labels:");
if (labels != null && labels.Count > 0)
{
foreach (var labelItem in labels)
{
Console.WriteLine("{0}", labelItem.Name);
}
}
else
{
Console.WriteLine("No labels found.");
}
Console.Read();
I don't want to include the client_secret.json file because I already was authorized by google's oauth process in the alexa skill's account linking. Help ?

Related

How to get logged email via OAuth 2.0 in C#

I'm using Google Sheet API V4 in C#.
My source will create new Excel file, write data to that file.
When execute, Google API will open a new OAuth tab on the browser, User will choose/login to an email on this tab, and grant Read/Write/All permission for my App.
My question is: How can I get Logged/permitted email address in C# source?
I want to know, what email did the user log in to create myfile.xlsx.
Thanks in advance.
Code snippet:
string[] Scopes = { SheetsService.Scope.DriveFile };
string ApplicationName = "Google Sheets API";
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,
"UserName",
CancellationToken.None,
new FileDataStore(credPath, true)).Result;
Console.WriteLine("Credential file saved to: " + credPath);
}
var fileName = "myfile.xlsx";
var myNewSheet = new Spreadsheet();
myNewSheet.Properties = new SpreadsheetProperties();
myNewSheet.Properties.Title = fileName;
var sheet = new Sheet();
sheet.Properties = new SheetProperties();
sheet.Properties.Title = $"data";
myNewSheet.Sheets = new List<Sheet>() { sheet };
var newSheet = service.Spreadsheets.Create(myNewSheet).Execute();
var spreadSheetId = newSheet.SpreadsheetId;
...........
// Write data to file source
Google Indentiy platform describes the procedure of how to authenticate a user to obtain user information and more specifically user profile information
The documentation does not specify how exactly to do it in C#, but basically
Make a GET request to https://www.googleapis.com/oauth2/v3/userinfo
Provide the scopes https://www.googleapis.com/auth/userinfo.profile and https://www.googleapis.com/auth/userinfo.email
There is a good sample on Stackoverflow how to do it in PHP - I hope it helps you with the implementation in C#
There is also a workaround authenticating users with Gmail

"Google.GoogleApiException: Google.Apis.Requests.RequestError Request is missing required authentication credential" when using ScriptService

Using .net, I'm trying to make a request to the Google ScriptService, however I keep getting this error "Request is missing required authentication credential", although I am including the credential. In fact I use the same credential not too long before to successfully make a request to the YoutubeService.
Below is my code, it actually used to work, so I'm not sure what has changed:
Scopes = new string[] { ScriptService.Scope.Forms, ScriptService.Scope.Spreadsheets,
ScriptService.Scope.Drive, YouTubeService.Scope.YoutubeUpload, YouTubeService.Scope.Youtube };
UserCredential credential;
using (var stream = new FileStream(#"Resources\client_secret.json", FileMode.Open, FileAccess.Read))
{
var credPath = Path.Combine(parentDir, ".credentials/" + folderName);
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
Scopes,
"user",
CancellationToken.None,
new FileDataStore(credPath, true)).Result;
}
// Create Google Apps Script Execution API service.
var service = new ScriptService(new BaseClientService.Initializer()
{
HttpClientInitializer = this.credential,
ApplicationName = Properties.Resources.ApplicationName,
});
// Create an execution request object.
ExecutionRequest request = new ExecutionRequest();
request.Function = "createForm";
request.Parameters = new List<object>();
request.Parameters.Add(this.id);
request.Parameters.Add(name);
request.Parameters.Add(email);
request.Parameters.Add(this.link);
ScriptsResource.RunRequest runReq = service.Scripts.Run(request, Globals.Script_ID);
try
{
// Make the API request.
Operation op = runReq.Execute();
catch (Google.GoogleApiException e)
{
Debug.WriteLine("Error calling API:\n{0}", e.ToString());
}
I have enabled the API and generated OAuth 2.0 credentials for my platform in the developer console. The client_secret.json is the OAuth 2.0 credential that I downloaded from my console.
Any thoughts on what could be going wrong? I recall having a similar issue after updating my google packages, however in this instance I did not do so. I also tried updating the packages and still got the same issue.
The issue was that I did not have the correct authorization scopes. Originally I had:
Scopes = new string[] { ScriptService.Scope.Forms, ScriptService.Scope.Spreadsheets,
ScriptService.Scope.Drive, YouTubeService.Scope.YoutubeUpload, YouTubeService.Scope.Youtube };
However, upon checking the "Scopes" tab in my apps script Project Properties, I was missing the "userinfo.email" scope. And so, I updated my code in the following way:
Scopes = new string[] { ScriptService.Scope.Forms, ScriptService.Scope.Spreadsheets,
ScriptService.Scope.Drive, ScriptService.Scope.UserinfoEmail, YouTubeService.Scope.YoutubeUpload, YouTubeService.Scope.Youtube };
My guess is that the API had been updated since I originally wrote my script some months ago, as I was able to successfully run it at that time with the three original authorizations that I had.

Google Spreadsheets Api Key example

I'm developing an application wich needs to access a "simple database" (google spreadsheet) with users IDs.
I try with Oauth2.0 But that's not what I need.
I'm using this code to get access to the sheet:
private static SheetsService AuthorizeGoogleApp()
{
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/sheets.googleapis.com-dotnet-quickstart.json");
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
Scopes,
"user",
CancellationToken.None,
new FileDataStore(credPath, true)).Result;
Console.WriteLine("Credential file saved to: " + credPath);
}
// Create Google Sheets API service.
var service = new SheetsService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = ApplicationName,
});
return service;
}
But with this code the C# application open a browser to ask the user to login in his google account.
I need to have access via API, so it will be transparent to the user.
I already have the api key, but I don't know how to use and I don't find any documentation on Google sites.
Can you help me with some kind of example to read a simple column?
Thanks in advance!
Here is the documentation for the API.
It also covers authorization.

Using OAuth2 to Authenticate with a Google API in C#

I have created an console application that uses OAuth2 to authenticate with the GoogleAnalyticsApiV4 to query some data. The application works as intended but we would like to automate the process so the application can be scheduled to run once a day. The problem here is the application would be hosted on azure and there is no way for a user to accept the authentication request with google that pops up in a browser the first time the application runs.
Following posts online and googles documentation my current solution to authenticate is this
try
{
var credential = GetCredential().Result;
using (var svc = new AnalyticsReportingService(
new BaseClientService.Initializer
{
HttpClientInitializer = credential,
ApplicationName = "Google Analytics API Console"
}))
{
///// Query some data/////
}
static async Task<UserCredential> GetCredential()
{
using (var stream = new FileStream("client_secret.json",
FileMode.Open, FileAccess.Read))
{
string loginEmailAddress = ConfigurationManager.AppSettings["GoogleUsername"];
return await GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
new[] { AnalyticsReportingService.Scope.Analytics },
loginEmailAddress, CancellationToken.None,
new FileDataStore("GoogleAnalyticsApiConsole"));
}
}
This solution works perfectly well to authenticate with Google as long as a user is available to input credentials and accept the authentication request. Unfortunately as soon as the application is moved to another machine it needs to re-authenticate and a user needs to input credentials again and accept the request.
I have been searching for a way to take the User out of the process so the application can run on azure but have not found anything clear on how to do this in c#.
Please can someone either describe how i can authenticate my application with google without a user, or point me in the direction of documentation that accurately covers the process.
An help or examples would be greatly appreciated.
You have a couple of options.
Is this an account you have access to. If it is then you can use a service account. Service accounts are preauthorized the you take the service account email address and add it as a user in Google analytics admin at the account level and the service account will be able to access the account for as long as it is valid. No pop up window is required. I have some sample code on how to authenticate with a service account here
/// <summary>
/// Authenticating to Google using a Service account
/// Documentation: https://developers.google.com/accounts/docs/OAuth2#serviceaccount
/// </summary>
/// <param name="serviceAccountEmail">From Google Developer console https://console.developers.google.com</param>
/// <param name="serviceAccountCredentialFilePath">Location of the .p12 or Json Service account key file downloaded from Google Developer console https://console.developers.google.com</param>
/// <returns>AnalyticsService used to make requests against the Analytics API</returns>
public static AnalyticsReportingService AuthenticateServiceAccount(string serviceAccountEmail, string serviceAccountCredentialFilePath)
{
try
{
if (string.IsNullOrEmpty(serviceAccountCredentialFilePath))
throw new Exception("Path to the service account credentials file is required.");
if (!File.Exists(serviceAccountCredentialFilePath))
throw new Exception("The service account credentials file does not exist at: " + serviceAccountCredentialFilePath);
if (string.IsNullOrEmpty(serviceAccountEmail))
throw new Exception("ServiceAccountEmail is required.");
// These are the scopes of permissions you need. It is best to request only what you need and not all of them
string[] scopes = new string[] { AnalyticsReportingService.Scope.Analytics }; // View your Google Analytics data
// For Json file
if (Path.GetExtension(serviceAccountCredentialFilePath).ToLower() == ".json")
{
GoogleCredential credential;
using (var stream = new FileStream(serviceAccountCredentialFilePath, FileMode.Open, FileAccess.Read))
{
credential = GoogleCredential.FromStream(stream)
.CreateScoped(scopes);
}
// Create the Analytics service.
return new AnalyticsReportingService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "AnalyticsReporting Service account Authentication Sample",
});
}
else if (Path.GetExtension(serviceAccountCredentialFilePath).ToLower() == ".p12")
{ // If its a P12 file
var certificate = new X509Certificate2(serviceAccountCredentialFilePath, "notasecret", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable);
var credential = new ServiceAccountCredential(new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
Scopes = scopes
}.FromCertificate(certificate));
// Create the AnalyticsReporting service.
return new AnalyticsReportingService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "AnalyticsReporting Authentication Sample",
});
}
else
{
throw new Exception("Unsupported Service accounts credentials.");
}
}
catch (Exception ex)
{
Console.WriteLine("Create service account AnalyticsReportingService failed" + ex.Message);
throw new Exception("CreateServiceAccountAnalyticsReportingFailed", ex);
}
}
If this isn't something you can do. Then you should be aware of the fact that filedatastore() by default stores your credentials in %appData% you could simply copy that file onto the new server along with the code.
You can also move the location to some were other then %appData% by using the following code:
new FileDataStore(#"c:\datastore",true)
I have a tutorial on how filedatastore works. here File datastore demystified
Preauthorizing service account to Google Analytics. Admin section of the Google analytics website. Grant it read access should be more then enough.

Unable To upload file to google drive - using c#

All code runs without errors, but when I check my Google Drive account I can't find the file I am uploading ("document.txt").
Also it has asked me for Authentication again.
UserCredential credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
new ClientSecrets
{
ClientId = "Here my clientid",
ClientSecret = "client secret",
},
new[] { DriveService.Scope.Drive },
"user",
CancellationToken.None).Result;
// Create the service.
var service = new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "Drive API Sample",
});
File body = new File();
body.Title = "My document";
body.Description = "A test document";
body.MimeType = "text/plain";
byte[] byteArray = System.IO.File.ReadAllBytes("document.txt");
System.IO.MemoryStream stream = new System.IO.MemoryStream(byteArray);
FilesResource.InsertMediaUpload request = service.Files.Insert(body, stream, "text/plain");
request.Upload();
File file = request.ResponseBody;
Questions:
Why cant I find my uploaded file, and how can I get it to remember my authentication.
I think you are forgetting body.Parent so it doesn't know what directory to place the file into.
parents[] list Collection of parent folders which contain this file.
Setting this field will put the file in all of the provided folders.
On insert, if no folders are provided, the file will be placed in the
default root folder.
example:
body.Parents = new List<ParentReference>() { new ParentReference() { Id = 'root' } };
You are getting asked for authentication again because you aren't saving authentication.
//Scopes for use with the Google Drive API
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.GoogleDrive.Auth.Store")
).Result;
FileDataStore stores the authentication data in the %appdata% directory.
More detailed information can be found in the tutorial Google Drive API with C# .net – Upload
Update For the following error:
"The API is not enabled for your project, or there is a per-IP or
per-Referer restriction configured on your API key and the request
does not match these restrictions. Please use the Google Developers
Console to update your configuration. [403]"
Go to Developer console for your project here Under APIs & auth -> APIs enable Google drive API and sdk. Also go to credentials and make sure you added a product name and email.

Categories