Oauth 2.0 for Fusion Tables API in .NET - c#

To automate updating a Google Fusion Table, we have created a .NET console application to which we have attached the file clientsecrets.json generated and downloaded from the Google administration console.
When I run the application locally, a browser window opens to authorize the use of the API with OAuth 2.0. Once it authorized the process executed properly.
However, when we run the application on the server where we want to schedule your daily execution, does not open the browser window and stating "One or more errors ocurred".
The server is a Windows Server 2012. The application is built on the .NET 4.5.1 and authorizing the code is as follows:
...
var service = new FusiontablesService(new BaseClientService.Initializer
{
ApplicationName = "Fusion Tables Sample",
HttpClientInitializer = Utils.Google.GetCredential().Result
});
...
public static async Task<UserCredential> GetCredential()
{
using (var stream = new FileStream("client_secrets.json", FileMode.Open, FileAccess.Read))
{
return await GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
new[] { FusiontablesService.Scope.Fusiontables },
"XXXXXXXXXXXX[user]", CancellationToken.None);
}
}

Seems you need a service account that can be created from Google "IAM and Admin' Console: https://console.cloud.google.com/iam-admin/serviceaccounts. Once the service account is created, save its private key locally (preferably as JSON-file) and then create BaseClientService.Initializer from the credentials obtained from this file. Something like:
var scopes = new[] { FusiontablesService.Scope.Fusiontables };
using (var stream = new FileStream(keyFilePath, FileMode.Open, FileAccess.Read))
{
return GoogleCredential.FromStream(stream)
.CreateScoped(scopes);
}
Having such Initializer, just create the instance of FusiontablesService almost as you did:
service = new FusiontablesService(
new BaseClientService.Initializer()
{
HttpClientInitializer = creds,
ApplicationName = "Street Parking",
});

Related

What could have possibly gone wrong with Google Calendar v3 app in a .NET 5 Core API that it would suddenly stop working after a year+ of uptime?

I've first verified that the failure occurs directly as a result of executing the following ListRequest by stopping any resulting processing by the app after the request is executed:
EventsResource.ListRequest(builder.GetCalendarService(), "user#domain.com") { Key = "API_KEY", TimeMin = DateTime.Now.AddDays(-3), TimeMax = DateTime.Now.AddDays(21), ShowDeleted = false, SingleEvents = true, OrderBy = EventsResource.ListRequest.OrderByEnum.StartTime };
All other Requests have stopped working also. Building the GoogleCredential and CalendarService is executing without error unless it doesn't execute until the actual Request execution:
string[] Scopes = {
CalendarService.Scope.Calendar,
CalendarService.Scope.CalendarEvents,
CalendarService.Scope.CalendarEventsReadonly};
FileStream stream = new FileStream(AppDomain.CurrentDomain.BaseDirectory + "auth\\apiinfofile.json", FileMode.Open, FileAccess.Read);
GoogleCredential credential;
credential = GoogleCredential.FromStream(stream).CreateScoped(Scopes).CreateWithUser("user#appname.iam.gserviceaccount.com");
CalendarService service = new CalendarService(new BaseClientService.Initializer { HttpClientInitializer = credential, ApplicationName = "appname" });
Was there a recent change to the API that I'm unaware of?
I was using ver 1.49 of the Google API packages and tried updating to ver 1.55 since that's the only thing that's changed as far as I can tell but no luck.
Is there anything that will expire or something along those lines that need to be updated?

Always prompt user to select an account on application start

I'm using the code from the code samples to authenticate user on application start.
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 },
"user",
CancellationToken.None,
new FileDataStore(this.GetType().ToString())
);
}
var youtubeService = new YouTubeService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = this.GetType().ToString()
});
Everything works fine except that I want to force the Select Account screen to show every time (now it only shows the first time, and then after that the user is remembered) because the application is supposed to allow different users to log in.
Looks like that I'm supposed to set the prompt request parameter to select_account, but I don't know how am I supposed to do this, AuthorizeAsync method doesn't accept any additional arguments.
You are correct that this is currently not possible to do in a simple way.
I've filed a bug to fix this: https://github.com/googleapis/google-api-dotnet-client/issues/1322

C# WebApi 2 Google Drive authentication on server side

I'm developing a web API that uses the google drive API, on my local machine, first run it redirected me to a google page to authenticate and then worked fine. Now I want to publish my code to Azure, and after I've published it the same function fails, because I could authenticate. How do I perform this authentication on the server?
I've followed the .NET QuickStart (https://developers.google.com/drive/api/v3/quickstart/dotnet), here is my code:
using (var stream =
new FileStream($#"{dir}\client_secret.json", FileMode.Open, FileAccess.Read))
{
var cred = GoogleClientSecrets.Load(stream).Secrets;
Credentials = GoogleWebAuthorizationBroker.AuthorizeAsync(
cred,
Scopes,
"user",
CancellationToken.None).Result;
}
service = new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = Credentials,
ApplicationName = ApplicationName,
});
}
The tutorial you are following is for a .NET console application this is a native application not a web application. GoogleWebAuthorizationBroker is for use with installed applications. It works on localhost because its able to spawn the browser window on your machine. This wont work in a hosted environment because it cant spawn a web browser on the server.
You should be following this example Web applications
using System;
using System.Web.Mvc;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Auth.OAuth2.Flows;
using Google.Apis.Auth.OAuth2.Mvc;
using Google.Apis.Drive.v2;
using Google.Apis.Util.Store;
namespace Google.Apis.Sample.MVC4
{
public class AppFlowMetadata : FlowMetadata
{
private static readonly IAuthorizationCodeFlow flow =
new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets
{
ClientId = "PUT_CLIENT_ID_HERE",
ClientSecret = "PUT_CLIENT_SECRET_HERE"
},
Scopes = new[] { DriveService.Scope.Drive },
DataStore = new FileDataStore("Drive.Api.Auth.Store")
});
public override string GetUserId(Controller controller)
{
// In this sample we use the session to store the user identifiers.
// That's not the best practice, because you should have a logic to identify
// a user. You might want to use "OpenID Connect".
// You can read more about the protocol in the following link:
// https://developers.google.com/accounts/docs/OAuth2Login.
var user = controller.Session["user"];
if (user == null)
{
user = Guid.NewGuid();
controller.Session["user"] = user;
}
return user.ToString();
}
public override IAuthorizationCodeFlow Flow
{
get { return flow; }
}
}
}
Note about azure you may have to change the location where filedatastore stores the credentials it depends upon where you have write access to. That can be done by supplying a path to the folder you want to store it in.
new FileDataStore(#"c:\datastore",true)

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.

YouTube v3 API caption download using SDK nuget package

I'm trying to download a caption track using YouTube API v3 (https://developers.google.com/youtube/v3/docs/captions/download) and official .NET SDK nuget package (https://www.nuget.org/packages/Google.Apis.YouTube.v3/, version 1.9.0.1360).
Returned stream contains the following text:
"The OAuth token was received in the query string, which this API forbids for response formats other than JSON or XML. If possible, try sending the OAuth token in the Authorization header instead."
instead of the SRT plain text content which I just uploaded and verified manually through YouTube.com UI.
I found the type of error: lockedDomainCreationFailure
My code:
...
_service = new YTApi.YouTubeService(new BaseClientService.Initializer {
ApplicationName = config.AppName,
ApiKey = config.DeveloperKey
});
...
public Stream CaptionsDownload(
string accessToken,
string trackId
)
{
var request = _service.Captions.Download(trackId);
request.OauthToken = accessToken;
request.Tfmt = YTApi.CaptionsResource.DownloadRequest.TfmtEnum.Srt;
var trackStream = new MemoryStream();
request.Download(trackStream);
trackStream.Position = 0;
return trackStream;
}
I cannot seem to find the way to set any headers on _service.HttpClient, and I guess I shouldn't do it manually. I expect that DownloadRequest (or YouTubeBaseServiceRequest) will put
/// <summary>
/// OAuth 2.0 token for the current user.
/// </summary>
[RequestParameter("oauth_token", RequestParameterType.Query)]
public virtual string OauthToken { get; set; }
into a correct authorization header. I don't see this implemented in the version 1.9.0.1360.
Maybe I'm overlooking something? Any help is greatly appreciated.
Note: I use other caption-related methods with this SDK, and 'download' is the only one I'm having a trouble with.
You initialed the service WITHOUT the user credential (you only used the API key). Take a look in one of the samples in our developers guide, (and pick the right flow... are you using installed application, windows phone, etc.?)
You will have to change the way you create your service to do something like the following:
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.<THE_RIGHT_SCOPE_HERE> },
"user", CancellationToken.None);
}
// Create the service.
_service = new YouTubeService(new BaseClientService.Initializer {
ApplicationName = config.AppName,
HttpClientInitializer = credential,
ApplicationName = "Books API Sample",
});
Then, for each request to the youtube service, your OAuth access token will be included as an additional header on the HTTP request itself.

Categories