How to get Google Analytics data using OAuth? - c#

Hy guys, we are developing a system which will provide users with access to Google Analytics. I'm trying to implement it in the way so user don't need to enter their Google login credentials on our site, so trying to get it work using their login.
I have a solution which gets analytics using user's email and password. I'm looking for a solution which will not require user's email and password but can not find anything.
How can it be done? any advices or links will be appreciated.
thanks

Ok, guys, after a few days of struggle I finally figured this out. There is no documentation on the Internet and people who had done it before did not want to share their success by some reason. I found this discussion which helped me.
To make it work you will need DotNetOpenAuth from http://www.dotnetopenauth.net/ and gdata from http://code.google.com/p/google-gdata/
so
using DotNetOpenAuth.ApplicationBlock;
using DotNetOpenAuth.OAuth;
using Google.GData.Client;
using Google.GData.Analytics;
In DotNetOpenAuth there is sample project named OAuthConsumer which you need.
Change it to requiest authorization for Analytics:
GoogleConsumer.RequestAuthorization(google, GoogleConsumer.Applications.Analytics);
This will get you Token and Token secret.
You can use them like this:
GOAuthRequestFactory requestFactory = new GOAuthRequestFactory("cp", TokenManager.ConsumerKey); //ConsumerKey actually is the name of web application
requestFactory.ConsumerKey = TokenManager.ConsumerKey;
requestFactory.ConsumerSecret = TokenManager.ConsumerSecret;
requestFactory.Token = AccessToken;
requestFactory.TokenSecret = TokenManager.GetTokenSecret(AccessToken);
requestFactory.UseSSL = true;
AnalyticsService service = new AnalyticsService(requestFactory.ApplicationName); // acually the same as ConsumerKey
service.RequestFactory = requestFactory;
const string dataFeedUrl = "https://www.google.com/analytics/feeds/data";
DataQuery query1 = new DataQuery(dataFeedUrl);
This service you can use like here or here
And the last thing, you WILL NOT be available to try and test it on localhost so you will need a domain which MUST be registered with Google here in order to get consumer key and secret

There's a .NET/C# class for Google Data authentication that can be used to access the Google Analytics Data Export API (since the API is part of the Google Data standard, though you might need to make Google Analytics specific adjustments.)*
The authentication is best managed by creating a Google Registered Application, as this allows you to make the authentication without security warnings (and, for that matter, security lapses).
There are three forms of supported authentication; the 'secure'/passwordless ones are OAuth and AuthSub (which is the Google-proprietary version of OAuth); the hardcoded username and password version is referred to by Google as 'ClientLogin', and is not considered secure or ideal for multiple-user applications.
*(Since you tagged the question .netc#)
Edit: More details on using AuthSub or OAuth with the .NET library:
AuthSubSupport: http://code.google.com/p/google-gdata/wiki/AuthSubSupport
Code Samples on how to use the libraries for OAuth authentication: http://code.google.com/apis/gdata/docs/auth/oauth.html#2LeggedOAuth (Click the .NET tab).

Basics of working with OAuth are here: http://code.google.com/apis/accounts/docs/OpenID.html#working
Authenticating with OAuth: http://code.google.com/apis/accounts/docs/OAuth.html
After you authenticate a user with OAuth, you will have the request token that works like the one you get back from Google's login API. From there, it should be the same as username/password.

I don't think you need to mess with OAuth.
The google analytics api lets you pass credentials. Just start from this data feed example.
http://code.google.com/p/google-gdata/source/browse/trunk/clients/cs/samples/Analytics_DataFeed_Sample/dataFeed.cs
// Configure GA API and do client login Authorization.
AnalyticsService asv = new AnalyticsService("gaExportAPI_acctSample_v2.0");
asv.setUserCredentials(clientUser, clientPass);
Download the client library here
http://code.google.com/apis/analytics/docs/gdata/gdataLibraries.html
To get a feel for data queries, play with this and then copy the values into the example above
http://code.google.com/apis/analytics/docs/gdata/gdataExplorer.html

Related

Write data to Google Spreadsheet using API Key (C#)

I have an app that allow me to read the data from Google Spreadsheet using API Key. I just make HTTP GET to this address and get a response with data.
https://sheets.googleapis.com/v4/spreadsheets/18soCZy9H4ZGuu**********BeHlNY1lD8at-Pbjmf8c/values/Sheet1!A1?key=AIzaSyAYJ***********pB-4iKZjYf4y0vhXP8OM
But when I try to do same to write data using HTTP PUT to address
https://sheets.googleapis.com/v4/spreadsheets/18soCZy9H4ZGuu**********BeHlNY1lD8at-Pbjmf8c/values/Sheet1!A4?valueInputOption=RAW?key=AIzaSyAYJ***********pB-4iKZjYf4y0vhXP8OM
its gives me 401 error.
Code to make PUT request:
using (WebClient wc = new WebClient())
{
byte[] res = wc.UploadData(link, "PUT", Encoding.ASCII.GetBytes(textBox1.Text));
MessageBox.Show(Encoding.Default.GetString(res));
}
Also spreadsheet is fully public with permission to read and write by anyone without auth. My guess is that I can't use API Key to write data to spreadsheet, and only way to do this is using OAuth.
UPDATE:
So i've just tryed Google.Apis.Sheets.v4 to write values, and now i'm almost 100% sure that API Key can't be used to write data to Google Spreadsheet. Well, then I'll use OAuth 2.0.
Well, maybe you are correct and the problem here is the API_KEY itself.
If you check the Sheets API documentation, it is stated that every request your application sends to the Google Sheets API needs to identify your application to Google. There are two ways to identify your application: using an OAuth 2.0 token (which also authorizes the request) and/or using the application's API key. Here's how to determine which of those options to use:
If the request requires authorization (such as a request for an individual's private data), then the application must provide an OAuth 2.0 token with the request. The application may also provide the API key, but it doesn't have to.
If the request doesn't require authorization (such as a request for public data), then the application must provide either the API key or an OAuth 2.0 token, or both—whatever option is most convenient for you.
So meaning either the OAuth 2.0 token or API key will work in your case since the file is public. But the problem is in the PUT request that you are doing, we can assume here that the API key is not working with it. But, we have alternatives for it, and that is the OAuth.
I also found here a related SO question that might help you.
For anyone still hoping for a simple answer, it seems there won't be one - any writing to a sheet, irrespective of the sheets permissions, will require OAuth2:
'This is intentional behavior. While public sheets are anonymously readable, anonymous edits aren't currently supported for a variety of reasons.
In this context, "anyone" == anyone with a google account.' HERE
One option that wasn't mentioned here is to use a service account instead. Service accounts are like users, but without being attached to a person. Instead, they're attached to a project.
Service accounts have an email address as well as a private key. Both can be used to create a JWTClientAuth, and this can be used to authenticate the API while it's being instantiated or to authenticate each and every request.
The advantage of the service account is that it works like an API KEY -- no need to ask a user to copy a URL to the browser and then copy a code back into the application -- but because it can act as an authenticated user, the service account email address can be added to the Google Sheet as an editor. With this in place, the application has full write access to the sheet but without having to deal with authorization codes and refresh tokens and copy/pasting.
You can see a Python example, Python With Google Sheets Service Account Step By Step, and a Node.js example, Accessing Google APIs Using Service Account in Node.js. I followed these examples to get setup.
Since you're using C#, you may find Writing to Google Sheets API Using .NET and a Service Account to be helpful.
This method reads the service account credentials from the JSON file to then instantiate the SheetsService:
private void ConnectToGoogle() {
GoogleCredential credential;
// Put your credentials json file in the root of the solution and make sure copy to output dir property is set to always copy
using (var stream = new FileStream(Path.Combine(HttpRuntime.BinDirectory, "credentials.json"),
FileMode.Open, FileAccess.Read)) {
credential = GoogleCredential.FromStream(stream).CreateScoped(_scopes);
}
// Create Google Sheets API service.
_sheetsService = new SheetsService(new BaseClientService.Initializer() {
HttpClientInitializer = credential, ApplicationName = _applicationName
});
}
Afterwards, you can use the Google Sheets .NET Client Library to write the data.

How do I setup a valid on-premise ADFS URI?

I have a .NET 4.6.2 Windows client application which needs to get an authentication token from our on-premise ADFS server and use it to call an ASP.NET Core REST API. It's client name, id (GUID) and re-direct URI have been registered with ADFS. I am using the latest ADAL (v3.13) library to facilitate the authentication. I am attempting to get a token as demonstrated in the ADAL sample code like this:
AuthenticationContext authenticationContext = new AuthenticationContext("https://<adfs-sts-server>/<rest-api-host>", false);
var result = authenticationContext.AcquireTokenAsync(<rest-api-resource-uri>, clientId, redirectUri, new PlatformParameters(PromptBehavior.Auto));
The AcquireTokenAsync call returns an error, saying: The browser based authentication dialog failed to complete. Reason: The server has not found anything matching the requested URI (Uniform Resource Identifier).
Can anyone tell me:
Is the "requested URI" refered to in the error the https://<adfs-sts-server>/<rest-api-host> or <rest-api-resource-uri>?
Do I need to register <rest-api-host> or <rest-api-resource-uri> with ADFS in some way, and if so how?
Any other information I need to get this to work?
Thanks!
Peter
Using Active Directory Federation Services (ADFS) to provide authentication for on-premise endpoints from a Windows Client
Configuring ADFS
There are 2 parts to configuring ADFS.
Register the client application with ADFS
ADFS needs to be able to identify the application requesting user authentication, whether it be a service, WPF application, Web client or Office Add-in. I have gone generic and added the following client, which we can use for most of our C# requests; we may need to register a new client with different callback for Web clients.
Use one of the many tools out there to generate a GUID for the client ID.
* CLIENT_ID and APP_NAME should be unique.
* For a web client the redirect URI is where the auth service will redirect your call after authenticating the user. It should be an endpoint where you can process the token and continue with your client application. The redirect URI is not really used with rich clients/services/add-ins.
CLIENT_ID = 26E54EC9-7988-4DAE-A527-483A8A78B1C6
APP_NAME = Investplus
DESCRIPTION = Invest+ rich client suite
REDIRECT_URI = https://server/redirect-adfs.html
Instructions for Client registration
(may be possible in a wizard, but this is what I found on the web and it worked fo us)
Log on to the AD FS server as administrator and open a Windows PowerShell command window.
Enter the following command. In Windows PowerShell
Add-AdfsClient -ClientId <CLIENT_ID> -Name <APP_NAME> -RedirectUri <REDIRECT_URI>
Register the resource to be accessed ('Relying Party' in ADFS speak)
I found this link useful, it takes you through the steps of the wizard for setting up a relying party.
Instructions for Relying Party registration
The administrator on the server team will need to use the ADFS Add Relying Party Trust Wizard, and under the "Select Data Source" step select Enter data about the relying party manually.
Values you need to supply for this wizard:
DISPLAY_NAME = "MyInvestApi" (Unique display name for this Relying party)
PROFILE = "AD FS Profile"
ENABLE_SUPPORT_FOR_WS-FEDERATION_PASSIVE_PROTOCOL = true
URL = "https://server/api" (Unique URL for this RP)
ADD_ONE_OR_MORE_IDENTIFIERS = eg. "urn:myInvestApi" and "https://server/api"
ACCEPT_REMAINING_DEFAULTS
when given the opportunity, Add Claim Rules:
SEND_LDAP_ATTRIBUTES_AS_CLAIMS = true
ATTRIBUTE_STORE = Active Directory
SELECT_USEFUL_ATTRIBUTES = User-Principal-Name; Email; Display-Name
Configuring/Coding the Client application
Microsoft provides Active Directory Authentication Libraries (ADAL) for a range of platforms and languages from C# to Javascript, and from iOS to Cordova to Node.
The API exposed has changed significantly in each major version: I am using the latest C# library, currently 3.13.5.
The library makes the coding very simple, just a few lines; where I had problems was:
I couldn't find an explanation of what URL to use for the ADFS
Secure Token Service (STS)
I couldn't find documentation of the whole process as I am doing here (most documentation focussed on Azure FS), I struggled to work out
how the values provided to ADFS for Client and Relying party mapped
to the values used in the code.
What is the ADFS endpoint/URL to use in code?
Microsoft's best practice is to name your ADFS/STS server URL https://sts.domain.com (some people use https://adfs.domain.com, ask your server admins). However, if you try to hit this from a browser you'll get a 404 - Not found and trying to retrieve a token in the code, the ADAL library reports:
The browser based authentication dialog failed to complete. Reason: The server has not found anything matching the requested URI (Uniform Resource Identifier).
This is how I found the endpoint to use:
ADFS pubishes federation metadata at 'https://sts.domain.com/federationmetadata/2007-06/federationmetadata.xml'
Extract this file and open in a text editor.
When configuring the Relying Party, we specified "Enable Support for WS-Federation Passive Protocol" when specifying our resource endpoint, so search the XML for PassiveRequestorEndpoint.
Use the <Address> from this node - in my case https://sts.domain.com/adfs/ls/. I don't know if this will always be the value, or if it is specified when ADFS is setup and therefore potentially different per site.
What other values to use in the code?
We want our client app to retrieve a JSON Web Token (JWT) from ADFS which we can pass to our protected resource for authentication/authorization purposes.
At its most simple, the access token can be retrieved in 3 lines of code + configuration, and this will show how to translate what we have configured in ADFS to the values required by ADAL:
var stsEndpoint = "https://sts.domain.com/adfs/ls/";
var relyingPartyIdentifier = "urn:myInvestApi"; // Tenant in Azure AD speak, but this is an on-premise service
var authority = stsEndpoint + relyingPartyIdentifier;
var restResourceUrl = "https://server/api";
var redirectUri = "https://server/redirect-adfs.html";
const string CLIENT_ID = "26E54EC9-7988-4DAE-A527-483A8A78B1C6";
AuthenticationContext authenticationContext = new AuthenticationContext(authority, false);
var asyncRequest = authenticationContext.AcquireTokenAsync(restResourceUrl, CLIENT_ID, redirectUri, new PlatformParameters(PromptBehavior.Auto));
var accessToken = asyncRequest.Result.AccessToken;
Useful references
ASP.NET Core Token Authentication Guide
ADAL - Native App to REST service - Authentication with ACS via Browser Dialog
Create a line-of-business Azure app with AD FS authentication
OAuth 2 Simplified
To issue the token for the web API, we need to make the ADFS to aware it by creating a relying party trust for the web API. And when we add a replying party we need to specify the identifiers for the replying party like figure below(Windows Server 2012 R2):
Then we can use this identifiers as the resource URI to acquire the token for this replying party. Please ensure that the resource URI is correct as you config like figure above.
And here is an article about developing with ADFS using OAuth:
Developing Modern Applications using OAuth and Active Directory Federation Services
Depending on the version of asdf, you may be able to use 'discovery' to obtain the endpoints to use.
Have a look at this post for more details: http://www.cloudidentity.com/blog/2015/08/21/openid-connect-web-sign-on-with-adfs-in-windows-server-2016-tp3/

Can I use Chrome Web Store payments with OAuth 2.0

I've written a hosted Chrome Web App which authenticates the user with OAuth 2.0 using the Google APIs Client Library for .NET. Now I want to add payments to our application using the in-built Chrome Web Store Payments.
Looking at the documentation it appears that I need an OpenID URL in order to check for payment.
How can I get this UserID/OpenID URL since I'm using OAuth instead of OpenID authentication?
var service = new Google.Apis.Oauth2.v2.Oauth2Service(
new BaseClientService.Initializer
{
HttpClientInitializer = userCredential,
ApplicationName = "My App Name",
}
);
HttpResponseMessage message = await service.HttpClient.GetAsync(
string.Format("https://www.googleapis.com/chromewebstore/v1/licenses/{0}/{1}",
appId,
fedId // Where do I get this??
)
);
I wanted to leave my own experience here so that others can see that it's not just a matter of using the existing Id returned from an authorised request to the user profile endpoint, as this is not the ID required by the Chrome Payments API...
Short Answer
It's not possible to use only OAuth2.0 for a hosted app. The only options for a hosted app are:
Use the deprecated OpenID (see detailed answer)
Use In-app Payments using Google Wallet for Digital Goods
Long Answer
We still have to use OpenID, however Google has provided a migration path for OpenID users to OAuth2.0 called OpenID Connect. The aim of this migration is to map the old fedId field to new the Google+ User Ids.
This allows us to retrieve an OpenID identifier using an existing OAuth 2.0 process.
Caveat: The Google .NET Client APIs do not support this migration path. So authentication must be done manually or using a 3rd party OAuth library.
Howto:
As per usual OAuth flow, direct the user to the Authenticate endpoint (https://accounts.google.com/o/oauth2/auth) with the following variables:
openid.realm=http://localhost ** Required, where http://localhost matches your redirect_uri variable
scope=openid profile https://www.googleapis.com/auth/chromewebstore.readonly ** Both openid and profile scopes are required in order to retrieve the OpenID identifier. The chromewebstore scope is required to query the payments API.
Then exchange the code for an access token from the Token endpoint (https://accounts.google.com/o/oauth2/token)
At this point you will receive the standard access_token, refresh_token, etc variables but also an additional id_token variable.
This id_token is a JWT-encoded string containing the OpenID information.
Decoding this JWT-encoded (you can use this C# JWT Library) string will give you a JSON string in the following format:
{
"aud": "<googleuserid>.apps.googleusercontent.com",
"at_hash": "<hashcode>",
"iss": "accounts.google.com",
"openid_id": "<!! The fedId we require !!>",
"exp": <id>,
"azp": "<googleuserid>.apps.googleusercontent.com",
"iat": <id>,
"sub": "<googleuserid>"
}
At this stage we've finally found what we're looking for, the openid_id. This can be used to communicate with the Chrome Payments API
While still using the same OAuth credentials, make a signed request to the following URL:
https://www.googleapis.com/chromewebstore/v1/licenses/{appId}/{openId}
{appId} is the ID of your app within the Chrome Web Store
{openId} is the openid_id from the JWT response
This should give you what you need:
https://developers.google.com/accounts/docs/OAuth2
Its a complete overview of OAuth2.0.
Helped me with a problem I was having with a webapp setup, hope it can do the same.
P.S - Im not sure but this may be exactly what your looking for:
https://developers.google.com/accounts/docs/OAuth2InstalledApp

How to use OAuth accesstoken to acquire profile images from various providers using DotNetOpenAuth.AspNet and Microsoft.AspNet.Membership.OpenAuth?

I've created a web application that uses the OAuth authentication and universal connectors as explained in this tutorial, and started to fiddle around a little to add support for other providers like Yahoo and LinkedIn. So the authentication part works and users are created in the asp.net Membership provider. Also, all the providers return the accesstoken which I supposedly can use to retrieve more information regarding the user.
I'd really like to acquire the profile image, but it seems every provider has a different way of requesting this information. Twitter even describes a way to authorise every request by changing the HTTP header information.
Whilst reading this information on the websites of the various providers I was wondering whether this functionality isn't also already included somewhere in DotNetOpenAuth.AspNet or Microsoft.AspNet.Membership.OpenAuth implementation.
How can I use DotNetOpenAuth.AspNet and/or Microsoft.AspNet.Membership.OpenAuth to request the profile image of the loggedin user using the just acquired accesstoken?
UPDATE in response to Leo's answer
I use the following code to make a call on LinkedIn's API.
string accessToken = extraData["accesstoken"]; // Extra Data received from OAuth containing the accesstoken.
WebRequest request = WebRequest.Create("https://api.linkedin.com/v1/people/~:(id,first-name,last-name,date-of-birth,email-address,picture-url)?oauth2_access_token=" + accessToken);
using (WebResponse response = request.GetResponse())
{
// do something with response here.
}
Error message is "The remote server returned an error: (401) Unauthorized.".
What am I doing wrong?
The answer is simple...you can't use any of these. These are wrappers of OAuth and OAuth only specifies how you can authenticate a user. Now, to request the user's profile photo you will need to use the external provider's own API and you will need most likely a valid access token. So, you will need to use one of these implementations of OAuth to authenticate a user and the recieve an access token, store the access token somewhere (usually a cookie) and then use the access token to make sub-sequent calls to the provider's APIs. Examples and links....
Facebook's Graph API allows you to retrieve users profiles
https://developers.facebook.com/docs/graph-api/quickstart/
notice that all examples in the link above will require you to include the access token in a parameter named access_token, for example
https://graph.facebook.com/me?method=GET&format=json&suppress_http_code=1&access_token={your-access-token}
Google...
https://www.googleapis.com/oauth2/v3/userinfo?access_token={your-access-token}
LinkedIn...
https://api.linkedin.com/v1/people/~:(id,first-name,last-name,date-of-birth,email-address,picture-url)?oauth2_access_token={your-access-token}
You can get more specific information from these providers' websites
Let me know if you have any other doubts I might be able to help you since I have implemented stuff like these before.
Cheers, Leo

Login Remotely To Google Analytics with ASP.NET

I'm trying to login directly to Google Analytics. To explain, I have an account system and I'd like when you select an ASP.NET button for instance it re-directs you - via a silent login - to a specified Google Analytics account.
I've looked long and hard at Dave Cullen's ASP.NET library and although I can login 'silently' using HttpWebRequest, I can't then stick the user on that page. I'm having allsorts of dramas with a 'Cannot send a content-body with this verb-type' error too.
Here is the very basic code I have currently based on Dave's library;
string token = GoogleAnalytics.getSessionTokenClientLogin(username, password);
NameValueCollection profiles = GoogleAnalytics.getAccountInfo(token, GoogleAnalytics.mode.ClientLogin);
HttpWebRequest theRequest = (HttpWebRequest)WebRequest.Create("https://www.google.com/analytics/settings/?et=reset&hl=en_uk&et=reset&hl=en-US&et=reset&hl=en-GB");
theRequest.Headers.Add("Authorization: GoogleLogin auth=" + token);
Stream responseBody = theRequest.GetRequestStream();
Encoding encode = System.Text.Encoding.GetEncoding("utf-8");
StreamReader readStream = new StreamReader(responseBody, encode);
My question therefore is; 1. can this be done? and 2. is this even the right way to do this?
Advice welcomed!
I'm not sure what the overall goal of signing someone in to Google Analytics automatically is, but if its just to display some of the data that is in Google Analytics you might want to consider using the Google Data API to pull the information that you want from Google Analytics. You could create a simple dashboard of what they really need to see without giving access to other things in Google Analytics, by logging them in you are most likely giving them access to data and tools that they just don't need?
Check out the API if it doesn't fit your needs maybe provide some more information on the overall goal is of this functionality.
http://code.google.com/apis/analytics/
Unless you're willing to implement a proxy server to proxy google analytics, I don't think you're going to be able to do this because you can't assign cookies to the client for another domain.
If the auth tokens are stored in cookies you can add the cookies to your ASP.NET response - then host the google page in an IFRAME just by setting the src (no inlining). That IFRAME will "inherit" the cookies from your parent page and the page will think it's authenticated.

Categories

Resources