Google.GData.Client.GDataRequestException - Authentication suddenly fails in old code - c#

I'm suddenly starting to get the following exception when attempting to authenticate and access a spreadsheet on Google drive:
Unhandled Exception: Google.GData.Client.GDataRequestException:
Execution of aut hentication request returned unexpected
result: 404
at Google.GData.Client.Utilities.getAuthException(TokenCollection tokens,
Htt pWebResponse response)
at Google.GData.Client.Utilities.QueryClientLoginToken(GDataCredentials
gc, S tring serviceName, String applicationName, Boolean
fUseKeepAlive, IWebProxy prox yServer, Uri
clientLoginHandler)
at Google.GData.Client.GDataGAuthRequest.QueryAuthToken(GDataCredentials
gc)
at Google.GData.Client.GDataGAuthRequest.EnsureCredentials()
at Google.GData.Client.GDataRequest.EnsureWebRequest()
at Google.GData.Client.GDataGAuthRequest.EnsureWebRequest()
at Google.GData.Client.GDataRequest.Execute()
at Google.GData.Client.GDataGAuthRequest.Execute(Int32 retryCounter)
at Google.GData.Client.GDataGAuthRequest.Execute()
at Google.GData.Client.Service.Query(Uri queryUri, DateTime ifModifiedSince, String etag, Int64& contentLength)
at Google.GData.Client.Service.Query(FeedQuery feedQuery)
at Google.GData.Documents.DocumentsService.Query(DocumentsListQuery
feedQuery )
at GoogleLogger.GoogleService.getLastXECLogRows(String folderName, String fileName, Int32 rows)
This is in code that has been running for two years without any problems. I first thought that I may have lost access permissions on my production system but Google drive loads fine in my web browser. Tried it on several other systems and am getting the very same.
Did they change something in the Google API today? This can't be coincidence!

Google has retired their older authentication API. OAuth 2.0 should be used instead.
I spent too much time to figure out how to use newer Auth API with older GData API grabbing bits and pieces of information here and there from the Internet.
I decided to share all the the details with screenshots to save your time.
Go to https://console.developers.google.com/project
Hit Create Project button
Create project. Type in some name.
Go to API & Auth > Credentials and hit Create new Client ID button. It will create JSON key for you automatically - ignore that.
Hit Generate new P12 key
File download will start automatically. Remember the password, you will need it to open the file you just downloaded.
Rename downloaded file to Key.p12 and add it to your solution. Make sure you set Build Action and Copy to Output Directory accordingly.
Install Google API Auth using Nuget. Type the following in the Package Manager Console
Install-Package Google.Apis.Auth
Copy service account email address that was generated in Step #4.
Grant appropriate permission to this user in your Google Spreadsheet.
Use the following code to query the spreadsheet. Replace email and Google spreadsheet URL address in the code below.
const string ServiceAccountEmail = "452351479-q41ce1720qd9l94s8847mhc0toao1fed#developer.gserviceaccount.com";
var certificate = new X509Certificate2("Key.p12", "notasecret", X509KeyStorageFlags.Exportable);
var serviceAccountCredentialInitializer =
new ServiceAccountCredential.Initializer(ServiceAccountEmail)
{
Scopes = new[] { "https://spreadsheets.google.com/feeds" }
}.FromCertificate(certificate);
var credential = new ServiceAccountCredential(serviceAccountCredentialInitializer);
if (!credential.RequestAccessTokenAsync(System.Threading.CancellationToken.None).Result)
throw new InvalidOperationException("Access token request failed.");
var requestFactory = new GDataRequestFactory(null);
requestFactory.CustomHeaders.Add("Authorization: Bearer " + credential.Token.AccessToken);
var service = new SpreadsheetsService(null) { RequestFactory = requestFactory };
var query = new ListQuery("https://spreadsheets.google.com/feeds/list/0ApZkobM61WIrdGRYshh345523VNsLWc/1/private/full");
var feed = service.Query(query);
var rows = feed.Entries
.Cast<ListEntry>()
.Select(arg =>
new
{
Field0 = arg.Elements[0].Value,
Field1 = arg.Elements[1].Value
})
.ToList();

I've managed to solve this by using this solution with Service Account with oAuth2.0
Accessing older GData APIs (Spreadsheet API) using OAuth 2 and a service account
The solution:
1. Create Project and Google Service Account in https://console.developers.google.com/project
Generate your p12 key.
Allow APIs in Developer console you want to use (basically we are going to use old API, so you can skip this step, but just in case)
Use the code below (.NET Framework 4.5!)
Also don't forget to grant "youraccount#developer.gserviceaccount.com" access to your spreadsheet document as you grant permissions for usual users by pressing Share.
Code:
using System.Security.Cryptography.X509Certificates;
using Google.GData.Client;
using Google.GData.Extensions;
using Google.GData.Spreadsheets;
using Google.Apis.Auth.OAuth2;
string keyFilePath = #"C:\key.p12"; // found in developer console
string serviceAccountEmail = "youraccount#developer.gserviceaccount.com"; // found in developer console
var certificate = new X509Certificate2(keyFilePath, "notasecret", X509KeyStorageFlags.Exportable);
ServiceAccountCredential credential = new ServiceAccountCredential(new ServiceAccountCredential.Initializer(serviceAccountEmail) //create credential using certificate
{
Scopes = new[] { "https://spreadsheets.google.com/feeds/" } //this scopr is for spreadsheets, check google scope FAQ for others
}.FromCertificate(certificate));
credential.RequestAccessTokenAsync(System.Threading.CancellationToken.None).Wait(); //request token
var requestFactory = new GDataRequestFactory("Some Name");
requestFactory.CustomHeaders.Add(string.Format("Authorization: Bearer {0}", credential.Token.AccessToken));
SpreadsheetsService myService = new SpreadsheetsService("You App Name"); //create your old service
myService.RequestFactory = requestFactory; //add new request factory to your old service
SpreadsheetQuery query = new SpreadsheetQuery(); //do the job as you done it before
SpreadsheetFeed feed = myService.Query(query);

Alright, I figured it out. Step by step instructions as follows - also see the code I provided below. FYI, this runs in .Net 3.5 and unlike the solution offered previously there are no new dependencies. You should be up and running in no time.
If you haven't yet create your OAuth 2.0 credentials - I assume you already know how to get those but here:
a) Log into your Google developer console: http://console.developers.google.com
b) Create a project
c) Create your credentials - use 'installed application'
d) add the APIs that you need - I think Drive API is definitely required. I also added Drive SDK just in case.
Copy the code below into VS and edit the first Main() method with your client key and secret key.
Run the app and copy both the new access token and the refresh token. Put those and your remaining credentials into the second Main() method below.
You now should be able to run the second Main() method (just reverse the naming). From now on that will be all you need - there is no need to re-run the first Main() method.
BTW, the first Main() method below was found here: https://developers.google.com/google-apps/spreadsheets/authorize
I did however add the missing token type as well the access type. Those are needed, so use the code below:
using System;
using Google.GData.Client;
using Google.GData.Spreadsheets;
using Google.GData.Documents;
using System.Configuration;
using System.Collections.Specialized;
namespace GoogleSpreadsheet
{
class GoogleOAutho2
{
private static String folderName = "crazy.ivan";
static void Main(string[] args)
{
////////////////////////////////////////////////////////////////////////////
// STEP 1: Configure how to perform OAuth 2.0
////////////////////////////////////////////////////////////////////////////
// TODO: Update the following information with that obtained from
// https://code.google.com/apis/console. After registering
// your application, these will be provided for you.
string CLIENT_ID = "your_client_id";
// This is the OAuth 2.0 Client Secret retrieved
// above. Be sure to store this value securely. Leaking this
// value would enable others to act on behalf of your application!
string CLIENT_SECRET = "your_secret_key"
// Space separated list of scopes for which to request access.
string SCOPE = "https://www.googleapis.com/auth/drive https://spreadsheets.google.com/feeds https://docs.google.com/feeds";
// This is the Redirect URI for installed applications.
// If you are building a web application, you have to set your
// Redirect URI at https://code.google.com/apis/console.
string REDIRECT_URI = "urn:ietf:wg:oauth:2.0:oob";
string TOKEN_TYPE = "refresh";
////////////////////////////////////////////////////////////////////////////
// STEP 2: Set up the OAuth 2.0 object
////////////////////////////////////////////////////////////////////////////
// OAuth2Parameters holds all the parameters related to OAuth 2.0.
OAuth2Parameters parameters = new OAuth2Parameters();
// Set your OAuth 2.0 Client Id (which you can register at
// https://code.google.com/apis/console).
parameters.ClientId = CLIENT_ID;
// Set your OAuth 2.0 Client Secret, which can be obtained at
// https://code.google.com/apis/console.
parameters.ClientSecret = CLIENT_SECRET;
// Set your Redirect URI, which can be registered at
// https://code.google.com/apis/console.
parameters.RedirectUri = REDIRECT_URI;
////////////////////////////////////////////////////////////////////////////
// STEP 3: Get the Authorization URL
////////////////////////////////////////////////////////////////////////////
// Set the scope for this particular service.
parameters.Scope = SCOPE;
parameters.AccessType = "offline"; // IMPORTANT and was missing in the original
parameters.TokenType = TOKEN_TYPE; // IMPORTANT and was missing in the original
// Get the authorization url. The user of your application must visit
// this url in order to authorize with Google. If you are building a
// browser-based application, you can redirect the user to the authorization
// url.
string authorizationUrl = OAuthUtil.CreateOAuth2AuthorizationUrl(parameters);
Console.WriteLine(authorizationUrl);
Console.WriteLine("Please visit the URL above to authorize your OAuth "
+ "request token. Once that is complete, type in your access code to "
+ "continue...");
parameters.AccessCode = Console.ReadLine();
////////////////////////////////////////////////////////////////////////////
// STEP 4: Get the Access Token
////////////////////////////////////////////////////////////////////////////
// Once the user authorizes with Google, the request token can be exchanged
// for a long-lived access token. If you are building a browser-based
// application, you should parse the incoming request token from the url and
// set it in OAuthParameters before calling GetAccessToken().
OAuthUtil.GetAccessToken(parameters);
string accessToken = parameters.AccessToken;
string refreshToken = parameters.RefreshToken;
Console.WriteLine("OAuth Access Token: " + accessToken + "\n");
Console.WriteLine("OAuth Refresh Token: " + refreshToken + "\n");
////////////////////////////////////////////////////////////////////////////
// STEP 5: Make an OAuth authorized request to Google
////////////////////////////////////////////////////////////////////////////
// Initialize the variables needed to make the request
GOAuth2RequestFactory requestFactory =
new GOAuth2RequestFactory(null, "MySpreadsheetIntegration-v1", parameters);
SpreadsheetsService service = new SpreadsheetsService("MySpreadsheetIntegration-v1");
service.RequestFactory = requestFactory;
// Make the request to Google
// See other portions of this guide for code to put here...
// Instantiate a SpreadsheetQuery object to retrieve spreadsheets.
Google.GData.Spreadsheets.SpreadsheetQuery query = new Google.GData.Spreadsheets.SpreadsheetQuery();
// Make a request to the API and get all spreadsheets.
SpreadsheetFeed feed = service.Query(query);
// Iterate through all of the spreadsheets returned
foreach (SpreadsheetEntry entry in feed.Entries)
{
// Print the title of this spreadsheet to the screen
Console.WriteLine(entry.Title.Text);
}
Console.ReadLine();
}
// once you copied your access and refresh tokens
// then you can run this method directly from now on...
static void MainX(string[] args)
{
GOAuth2RequestFactory requestFactory = RefreshAuthenticate();
SpreadsheetsService service = new SpreadsheetsService("MySpreadsheetIntegration-v1");
service.RequestFactory = requestFactory;
// Instantiate a SpreadsheetQuery object to retrieve spreadsheets.
Google.GData.Spreadsheets.SpreadsheetQuery query = new Google.GData.Spreadsheets.SpreadsheetQuery();
// Make a request to the API and get all spreadsheets.
SpreadsheetFeed feed = service.Query(query);
// Iterate through all of the spreadsheets returned
foreach (SpreadsheetEntry entry in feed.Entries)
{
// Print the title of this spreadsheet to the screen
Console.WriteLine(entry.Title.Text);
}
Console.ReadLine();
public static GOAuth2RequestFactory RefreshAuthenticate() {
OAuth2Parameters parameters = new OAuth2Parameters(){
RefreshToken = "the_refresh_token_you_copied_from_the_CLI_running_the_first_method";
AccessToken = "the_access_token_you_copied_from_the_CLI_running_the_first_method";
ClientId = "your_client_id";
ClientSecret = "your_dirty_little_secret";
Scope = "https://www.googleapis.com/auth/drive https://spreadsheets.google.com/feeds",
AccessType = "offline",
TokenType = "refresh"
};
string authUrl = OAuthUtil.CreateOAuth2AuthorizationUrl(parameters);
return new GOAuth2RequestFactory(null, "evilspeculator", parameters);
}
}
}
Hope that works for you guys - best of luck!

Andrew I was wondering how you got the google.apis.auth.oauth2 dll. Im trying to impliment your fix and I cant find the correct way to install the library.
I feel like I may be able to get this to work after I have that part.

Related

Access to a private Google Spreadsheet with C#

I have an AngularJS + C#.NET OnePage Application Website. The goal is to retrieve a JSON from a private Google Spreadsheet that I own with C# (not with AngularJS). I read various Google Sheets Documentation and API, OAuth 2.0 etc. and tried some examples, but none of them seem to work for me. I see there are different ways to access and retrieve data from a Google Spreadsheet, still, they don't work for my case.
Can anyone help me?
Thank you.
Edit: I managed to get a token by creating an Other Application type under Google Developers Console=>Credentials=>Create Client ID. Here's the C# Console Application:
using System;
using Google.GData.Client;
using Google.GData.Spreadsheets;
namespace MySpreadsheetIntegration
{
class Program
{
static void Main(string[] args)
{
////////////////////////////////////////////////////////////////////////////
// STEP 1: Configure how to perform OAuth 2.0
////////////////////////////////////////////////////////////////////////////
// TODO: Update the following information with that obtained from
// https://code.google.com/apis/console. After registering
// your application, these will be provided for you.
string CLIENT_ID = "12345678.apps.googleusercontent.com";
// This is the OAuth 2.0 Client Secret retrieved
// above. Be sure to store this value securely. Leaking this
// value would enable others to act on behalf of your application!
string CLIENT_SECRET = "Gc0230jdsah01jqpowpgff";
// Space separated list of scopes for which to request access.
string SCOPE = "https://spreadsheets.google.com/feeds https://docs.google.com/feeds";
// This is the Redirect URI for installed applications.
// If you are building a web application, you have to set your
// Redirect URI at https://code.google.com/apis/console.
string REDIRECT_URI = "urn:ietf:wg:oauth:2.0:oob";
////////////////////////////////////////////////////////////////////////////
// STEP 2: Set up the OAuth 2.0 object
////////////////////////////////////////////////////////////////////////////
// OAuth2Parameters holds all the parameters related to OAuth 2.0.
OAuth2Parameters parameters = new OAuth2Parameters();
// Set your OAuth 2.0 Client Id (which you can register at
// https://code.google.com/apis/console).
parameters.ClientId = CLIENT_ID;
// Set your OAuth 2.0 Client Secret, which can be obtained at
// https://code.google.com/apis/console.
parameters.ClientSecret = CLIENT_SECRET;
// Set your Redirect URI, which can be registered at
// https://code.google.com/apis/console.
parameters.RedirectUri = REDIRECT_URI;
////////////////////////////////////////////////////////////////////////////
// STEP 3: Get the Authorization URL
////////////////////////////////////////////////////////////////////////////
// Set the scope for this particular service.
parameters.Scope = SCOPE;
// Get the authorization url. The user of your application must visit
// this url in order to authorize with Google. If you are building a
// browser-based application, you can redirect the user to the authorization
// url.
string authorizationUrl = OAuthUtil.CreateOAuth2AuthorizationUrl(parameters);
Console.WriteLine(authorizationUrl);
Console.WriteLine("Please visit the URL above to authorize your OAuth "
+ "request token. Once that is complete, type in your access code to "
+ "continue...");
parameters.AccessCode = Console.ReadLine();
////////////////////////////////////////////////////////////////////////////
// STEP 4: Get the Access Token
////////////////////////////////////////////////////////////////////////////
// Once the user authorizes with Google, the request token can be exchanged
// for a long-lived access token. If you are building a browser-based
// application, you should parse the incoming request token from the url and
// set it in OAuthParameters before calling GetAccessToken().
OAuthUtil.GetAccessToken(parameters);
string accessToken = parameters.AccessToken;
Console.WriteLine("OAuth Access Token: " + accessToken);
////////////////////////////////////////////////////////////////////////////
// STEP 5: Make an OAuth authorized request to Google
////////////////////////////////////////////////////////////////////////////
// Initialize the variables needed to make the request
GOAuth2RequestFactory requestFactory =
new GOAuth2RequestFactory(null, "MySpreadsheetIntegration-v1", parameters);
SpreadsheetsService service = new SpreadsheetsService("MySpreadsheetIntegration-v1");
service.RequestFactory = requestFactory;
Console.ReadLine();
}
}
}
With this code I have to copy the link I get and paste it in a browser in order to get a token. Is there a way to get this token directly in my app without the need to copy the link manually?
I found another working solution where you don't have to open a browser window which is similar to the solution with the p12-key above.
First, create in the Google Developer Console under Create Credentials a Service account key.
Then, you have to choose a service account type, you can choose App Engine Default Service account and you can download a json file that contains the private key.
With this file, you can create programmatically Google credentials (where "sheets-test.json" is the downloaded json file):
var credential = GoogleCredential.FromStream(new FileStream("Sheets-test.json", FileMode.Open)).CreateScoped(Scopes);
Give the service user (you can find it in the json file in the client_email field) access to your spreadsheet.
Here is the full code that enables you to append a value to the first free row in a spreadsheet, I modified the google .NET quickstart project:
using System;
using System.Collections.Generic;
using System.IO;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Services;
using Google.Apis.Sheets.v4;
using Google.Apis.Sheets.v4.Data;
namespace SheetApiTest
{
public class SheetApiWithGoogleCredentials
{
static string[] Scopes = { SheetsService.Scope.Spreadsheets };
static string ApplicationName = "Google Sheets API .NET Quickstart";
public void AppendData()
{
// the downloaded jsonn file with private key
var credential = GoogleCredential.FromStream(new FileStream("Sheets-test.json", FileMode.Open)).CreateScoped(Scopes);
var service = new SheetsService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = ApplicationName,
});
// spreadsheet id - your own spreadsheet id
var spreadsheetId = "11AwV7d1pEPq4x-rx9WeZHNwGJa0ehfRhh760";
var valueRange = new ValueRange { Values = new List<IList<object>> { new List<object>() } };
valueRange.Values[0].Add(DateTime.Now.ToLongTimeString());
// insert here the name of your spreadsheet table
var rangeToWrite = "Tabellenblatt1";
var appendRequest = service.Spreadsheets.Values.Append(valueRange, spreadsheetId, rangeToWrite);
appendRequest.ValueInputOption = SpreadsheetsResource.ValuesResource.AppendRequest.ValueInputOptionEnum.USERENTERED;
var appendReponse = appendRequest.Execute();
}
}
}
There is a way for server-to-server communication using certificate instead of copy/paste the provided by Google AccessCode for the communication to be established.
First you will need to get your certificate from Google Console.
If you haven't already created a project do so and give it a name. Then:
Go to "Credentials" section (found on the left menu) and
Click on the button "Create Credentials" and choose "Service account key"
Choose the service account to be "App Engine default service account" and
Choose the "key type" to be "p.12" which is the certificate type.
Image for reference:
After these steps are completed, the certificate will be automatically downloaded. Upload it in some folder in your project and use it as shown below.
Note: for the sake of simplicity the code is placed in the controller.
public async Task<ActionResult> ServerAuth()
{
ViewBag.Message = "Server to server authentication";
List<string> records = new List<string>();
const string ServiceAccountEmail = "your-account#appspot.gserviceaccount.com";
string fullKeyPath = HttpContext.Server.MapPath("~/Key/MyProjectKey.p12"); // The certificate generated by Google and uploaded in the project.
var certificate = new X509Certificate2(fullKeyPath, "notasecret", X509KeyStorageFlags.Exportable); // "notasecret" is the password for the certificate
var serviceAccountCredentialInitializer = new ServiceAccountCredential.Initializer(ServiceAccountEmail)
{
Scopes = new[] { "https://spreadsheets.google.com/feeds", "http://spreadsheets.google.com/feeds/spreadsheets/private/full" }
}.FromCertificate(certificate);
var credential = new ServiceAccountCredential(serviceAccountCredentialInitializer);
if (!await credential.RequestAccessTokenAsync(System.Threading.CancellationToken.None))
{
throw new InvalidOperationException("Access token request failed.");
}
var requestFactory = new GDataRequestFactory(null);
requestFactory.CustomHeaders.Add("Authorization: Bearer " + credential.Token.AccessToken);
var service = new SpreadsheetsService(null) { RequestFactory = requestFactory };
SpreadsheetQuery query = new SpreadsheetQuery();
query.Title = "Test Sheet"; // The exact name of the sheet you want to read
query.Exact = true;
var feed = service.Query(query);
foreach (SpreadsheetEntry entry in feed.Entries)
{
foreach (WorksheetEntry worksheet in entry.Worksheets.Entries.Cast<WorksheetEntry>())
{
CellQuery cellQuery = new CellQuery(worksheet.CellFeedLink);
CellFeed cellFeed = service.Query(cellQuery);
foreach (CellEntry cell in cellFeed.Entries)
{
records.Add(cell.InputValue);
}
}
}
return View(records);
}

Error when calling any method on Service Management API

I'm looking to start an Azure runbook from a c# application which will be hosted on an Azure web app.
I'm using certificate authentication (in an attempt just to test that I can connect and retrieve some data)
Here's my code so far:
var cert = ConfigurationManager.AppSettings["mgmtCertificate"];
var creds = new Microsoft.Azure.CertificateCloudCredentials("<my-sub-id>",
new X509Certificate2(Convert.FromBase64String(cert)));
var client = new Microsoft.Azure.Management.Automation.AutomationManagementClient(creds, new Uri("https://management.core.windows.net/"));
var content = client.Runbooks.List("<resource-group-id>", "<automation-account-name>");
Every time I run this, no matter what certificate I use I get the same error:
An unhandled exception of type 'Hyak.Common.CloudException' occurred in Microsoft.Threading.Tasks.dll
Additional information: ForbiddenError: The server failed to authenticate the request. Verify that the certificate is valid and is associated with this subscription.
I've tried downloading the settings file which contains the automatically generated management certificate you get when you spin up the Azure account... nothing I do will let me talk to any of the Azure subscription
Am I missing something fundamental here?
Edit: some additional info...
So I decided to create an application and use the JWT authentication method.
I've added an application, given the application permissions to the Azure Service Management API and ensured the user is a co-administrator and I still get the same error, even with the token...
const string tenantId = "xx";
const string clientId = "xx";
var context = new AuthenticationContext(string.Format("https://login.windows.net/{0}", tenantId));
var user = "<user>";
var pwd = "<pass>";
var userCred = new UserCredential(user, pwd);
var result = context.AcquireToken("https://management.core.windows.net/", clientId, userCred);
var token = result.CreateAuthorizationHeader().Substring("Bearer ".Length); // Token comes back fine and I can inspect and see that it's valid for 1 hour - all looks ok...
var sub = "<subscription-id>";
var creds = new TokenCloudCredentials(sub, token);
var client = new AutomationManagementClient(creds, new Uri("https://management.core.windows.net/"));
var content = client.Runbooks.List("<resource-group>", "<automation-id>");
I've also tried using other Azure libs (like auth, datacentre etc) and I get the same error:
ForbiddenError: The server failed to authenticate the request. Verify that the certificate is valid and is associated with this subscription.
I'm sure it's just 1 tickbox I need to tick buried somewhere in that monolithic Management Portal but I've followed a few tutorials on how to do this and they all end up with this error...
public async Task StartAzureRunbook()
{
try
{
var subscriptionId = "azure subscription Id";
string base64cer = "****long string here****"; //taken from http://stackoverflow.com/questions/24999518/azure-api-the-server-failed-to-authenticate-the-request
var cert = new X509Certificate2(Convert.FromBase64String(base64cer));
var client = new Microsoft.Azure.Management.Automation.AutomationManagementClient(new CertificateCloudCredentials(subscriptionId, cert));
var ct = new CancellationToken();
var content = await client.Runbooks.ListByNameAsync("MyAutomationAccountName", "MyRunbookName", ct);
var firstOrDefault = content?.Runbooks.FirstOrDefault();
if (firstOrDefault != null)
{
var operation = client.Runbooks.Start("MyAutomationAccountName", new RunbookStartParameters(firstOrDefault.Id));
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
Also in portal:
1) Application is multitenant
2) Permissions to other applications section - Windows Azure Service Manager - Delegated permissions "Access Azure Service Management(preview)"
Ensure that your Management certificate has private key and was not made from the .CER file. The fact that you're not supplying a password when generating the X509Certificate object makes me think you're using public key only
Ensure that your Managemnet's certificate public key (.CER file) has been uploaded to the Azure management portal (legacy version, Management Certificate area)
Use CertificateCloudCredentials and not any other credential type of an object
Ok, stupid really but one of the tutorials I followed suggested installing the prerelease version of the libs.
Installing the preview (0.15.2-preview) has fixed the issue!

Dropnet getting access token cant peform 3rd step of 0auth

Hi guys im trying to use dropnet as means to be using dropbox as a cloud storage for my application, and following the 3 step process using the normal 0auth
1.Get Request Token[done]
2.Send user for authorization, and get back verifier[done]
3.Get Access token using the original Request Token and the verifier[issue!]
if i understand the api correctly since i want to use a single account for my storage i need api key,api secret,token and secret.the token and secret seems to be accessible from the third steps and its my trouble.
from the second step i get this url
https://www.dropbox.com/1/oauth/authorize?oauth_token=xxxxxxxxxx
before pressing authenticate to allow my app to use dropbox
from the documentation i read that you need to use this method
UserLogin GetAccessToken(string code, string redirectUri);
i am assuming here xxxxx is the string code
to validate that is the original
so i made code as follows
var accessToken = client.GetAccessToken("xxxxxxx","https://www.dropbox.com/1/oauth/authorize?oauth_token=xxxxxxxxxx);
var ats =accessToken.Secret;
var att = accessToken.Token;
Console.Writeline(ats);
Console.Writeline(att):
in hopes of getting the console to print my secret and token for my acct but it dosent work ?Giving me the error of
An unhandled exception of type 'DropNet.Exceptions.DropboxRestException' occurred in DropNet.dll
help please !
Solved the problem myself,here is the full code
DropNetClient client = new DropNetClient(variable.ApiKey, variable.ApiSecret);
]
var response =client.GetToken();
var t = response.Token;
var s = response.Secret;
Console.WriteLine(s);
Console.WriteLine(t);
var authorizeUrl = client.BuildAuthorizeUrl(new DropNet.Models.UserLogin
{
Secret = s,
Token = t
}
);
DropNetClient client2= new DropNetClient(variable.ApiKey, variable.ApiSecret,t,s);
// Prompt for user to auth
Process.Start(authorizeUrl);
// PRESS KEY AFTER authorization AFTER
Console.ReadKey();
// If the user authed, let's get that token
try
{
var Token = client2.GetAccessToken();
var userToken = Token.Token;
var userSecret = Token.Secret;
Console.WriteLine(userSecret);//ACCESS TOKEN SECRET
Console.WriteLine(userToken);//ACCESS TOKEN
Console.ReadKey();
}
catch (Exception e)
{
Console.WriteLine("Exception! " + e.Message);
Console.ReadKey();
}
// save for later

Google Analytics throws 403 error

I am attempting to download metric data from Google Analytics using C# and am performing user authentication with OAuth 2.0. I'm using the Installed Application authorisation flow, which requires logging into Google and copy-and-pasting a code into the application. I'm following the code taken from google-api-dotnet-client:
private void DownloadData()
{
Service = new AnalyticsService(new BaseClientService.Initializer() {
Authenticator = CreateAuthenticator(),
});
var request = service.Data.Ga.Get(AccountID, StartDate, EndDate, Metrics);
request.Dimensions = Dimensions;
request.StartIndex = 1;
request.MaxResults = 10000;
var response = request.Execute(); // throws Google.GoogleApiException
}
private IAuthenticator CreateAuthenticator()
{
var provider = new NativeApplicationClient(GoogleAuthenticationServer.Description) {
ClientIdentifier = "123456789012.apps.googleusercontent.com",
ClientSecret = "xxxxxxxxxxxxxxxxxxxxxxxx",
};
return new OAuth2Authenticator<NativeApplicationClient>(provider, Login);
}
private static IAuthorizationState Login(NativeApplicationClient arg)
{
// Generate the authorization URL.
IAuthorizationState state = new AuthorizationState(new[] { AnalyticsService.Scopes.AnalyticsReadonly.GetStringValue() });
state.Callback = new Uri(NativeApplicationClient.OutOfBandCallbackUrl);
Uri authUri = arg.RequestUserAuthorization(state);
// Request authorization from the user by opening a browser window.
Process.Start(authUri.ToString());
Console.Write("Google Authorization Code: ");
string authCode = Console.ReadLine();
// Retrieve the access token by using the authorization code.
state = arg.ProcessUserAuthorization(authCode, state);
return state;
}
The Google account xxxxxx#gmail.com registered the Client ID and secret. The same account has full administration rights in Google Analytics. When I try to pull data from Google Analytics, it goes through the authorisation process, which appears to work properly. Then it fails with:
Google.GoogleApiException
Google.Apis.Requests.RequestError
User does not have sufficient permissions for this profile. [403]
Errors [
Message[User does not have sufficient permissions for this profile.] Location[ - ] Reason [insufficientPermissions] Domain[global]
]
I've been struggling with this for a few hours. I've double checked that the correct user is being used, and is authorised on Google Analytics. I'm at a loss as to what is misconfigured. Any ideas as to what requires configuring or changing?
If auth seems to be working working then my suggestion is that you make sure you're providing the correct ID because based on your code snippet:
var request = service.Data.Ga.Get(AccountID, StartDate, EndDate, Metrics);
one can only assume that you're using the Account ID. If so, that is incorrect and you'd receive the error you've encountered. You need to query with the Profile ID.
If you login to Google Analytics using the web interface you'll see the following pattern in URL of the browser's address bar:
/a12345w654321p9876543/
The number following the p is the profile ID, so 9876543 in the example above. Make sure you're using that and actually you should be using the table id which would be ga:9876543.
If it isn't an ID issue then instead query the Management API to list accounts and see what you have access to and to verify auth is working correctly.
This can help : https://developers.google.com/analytics/devguides/reporting/core/v3/coreErrors, look error 403.
//Thanks for this post. The required profile id can be read from the account summaries.
Dictionary profiles = new Dictionary();
var accounts = service.Management.AccountSummaries.List().Execute();
foreach (var account in accounts.Items)
{
var profileId = account.WebProperties[0].Profiles[0].Id;
profiles.Add("ga:" + profileId, account.Name);
}

Google Analytics Api on Azure

I want to use the google analytics api in my MVC website, im authenticating using the api service account and oauth2 with have no issues on my localhost but as soon as I deploy to Azure i get a 502 error:
"502 - Web server received an invalid response while acting as a
gateway or proxy server. There is a problem with the page you are
looking for, and it cannot be displayed. When the Web server (while
acting as a gateway or proxy) contacted the upstream content server,
it received an invalid response from the content server."
heres my code:
const string ServiceAccountUser = "xxxxxxxxxx-cpla4j8focrebami0l87mbcto09j9j6k#developer.gserviceaccount.com";
AssertionFlowClient client = new AssertionFlowClient(
GoogleAuthenticationServer.Description,
new X509Certificate2(System.Web.Hosting.HostingEnvironment.MapPath("/Areas/Admin/xxxxxxxxxxxxxxxxxx-privatekey.p12"),
"notasecret", X509KeyStorageFlags.Exportable))
{
Scope = AnalyticsService.Scopes.AnalyticsReadonly.GetStringValue(),
ServiceAccountId = ServiceAccountUser //Bug, why does ServiceAccountUser have to be assigned to ServiceAccountId
//,ServiceAccountUser = ServiceAccountUser
};
OAuth2Authenticator<AssertionFlowClient> authenticator = new OAuth2Authenticator<AssertionFlowClient>(client, AssertionFlowClient.GetState);
I cant figure out whats causing it? Am im missing something within Azure?
Thanks for any help.
I also ran into the same issue but passing X509KeyStorageFlags.MachineKeySet into the constructor as well fixed the issue for me.
X509Certificate2 certificate = new X509Certificate2(file, "key", X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet);
After hours of pain on this exact same problem, I found a work around by piecing together various sources of info.
The problem arises from trying to read the p12 file from the Azure web site, i.e. this line in my code fails
var key = new X509Certificate2(keyFile, keyPassword, X509KeyStorageFlags.Exportable);
No idea why, but it works if you split the file into a cer and key.xml file?
Firstly, extract these files, (I just used a console app)
// load pfx/p12 as "exportable"
var p12Cert = new X509Certificate2(#"c:\Temp\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-privatekey.p12", "notasecret", X509KeyStorageFlags.Exportable);
// export .cer from .pfx/.p12
File.WriteAllBytes(#"C:\Temp\MyCert.cer", p12Cert.Export(X509ContentType.Cert));
// export private key XML
string privateKeyXml = p12Cert.PrivateKey.ToXmlString(true);
File.WriteAllText(#"C:\Temp\PrivateKey.xml", privateKeyXml);
Then copy them to your website then load them in like so
//Store the authentication description
AuthorizationServerDescription desc = GoogleAuthenticationServer.Description;
//Create a certificate object to use when authenticating
var rsaCryptoServiceProvider = new RSACryptoServiceProvider();
rsaCryptoServiceProvider.FromXmlString(File.ReadAllText(keyFile));
var key = new X509Certificate2(certFile) {PrivateKey = rsaCryptoServiceProvider};
//Now, we will log in and authenticate, passing in the description
//and key from above, then setting the accountId and scope
var client = new AssertionFlowClient(desc, key)
{
//cliendId is your SERVICE ACCOUNT Email Address from Google APIs Console
//looks something like 12345-randomstring#developer.gserviceaccount.com
//~IMPORTANT~: this email address has to be added to your Google Analytics profile
// and given Read & Analyze permissions
ServiceAccountId = clientId,
Scope = "https://www.googleapis.com/auth/analytics.readonly"
};
//Finally, complete the authentication process
//NOTE: This is the first change from the update above
var auth = new OAuth2Authenticator<AssertionFlowClient>(client, AssertionFlowClient.GetState);
//First, create a new service object
//NOTE: this is the second change from the update
//above. Thanks to James for pointing this out
var gas = new AnalyticsService(new BaseClientService.Initializer { Authenticator = auth });
This now works for me and I hope it helps you.

Categories

Resources