Using OAuth 2.0 with my Windows Service to manage my Adwords account

2,197 views
Skip to first unread message

giles bodger

unread,
Oct 15, 2013, 8:44:13 AM10/15/13
to adwor...@googlegroups.com
Hi all,

So we have a Windows Service which we use to manage our AdWords accounts and am now trying to convert to using OAuth2 from ClientLogin as the Authorization Method.
The situation is such that I don't have a need to send a user to a redirect page to verify access to their data, I simply need MY Windows service to be able to access MY Adwords account data.

reading up on the following page ......

Because we have a hierarchy of Advertiser accounts all linked under a single master MCC, I think I am right in saying that I can create the necessary ClientID and the Client Secret by using the "OAuth2TokenGenerator.exe" application, and indeed I went through the following the steps......
  1. Navigated to the page https://code.google.com/apis/console/ 
  2. Log in to the adwords account
  3. Create a new Project (although I don't really know what a project is or what it is for ?)
  4. Then, not in the new "cloud console" dashboard but the old one, i was able to click on "API Access" and then "Create an OAuth2 client Id"
  5. I then entered this data along with the already entered OAuth2 Scope in the OAuth2TokenGenerator
  6. A browser window then opened to a localhost domain with the following url , http://localhost:8080/?code=[REMOVED FOR SECURITY PURPOSES] , I am presuming that this browser window opened in order for me to verify that i, as the adwords account user, will allow my windows service (that i am creating the OAuth2 configuration for) to have access to. As I was logged into adwords we had some sort of redirection and an Authentication Code "code" was given as you can see in the querystring.
  7. The OAuth2TokenGenerator then presented me with the OAuth2 configuration that I will need

<add key='AuthorizationMethod' value='OAuth2' />
<add key='OAuth2ClientId' value='[VALUE]' />
<add key='OAuth2ClientSecret' value='[VALUE]' />
<add key='OAuth2RefreshToken' value='[VALUE]' />

So I add these to my app.config, and then whenever I make a request via the Adwords API to access my account, I am creating the following AdWordsUser ....


/* c# code START */
var adWordsUser = new AdWordsUser();

adWordsUser.Config.OAuth2ClientId = myConfig._oAuth2ClientId;
adWordsUser.Config.OAuth2ClientSecret = myConfig._oAuth2ClientSecret;
adWordsUser.Config.OAuth2RefreshToken = myConfig._oAuth2RefreshToken; // what is this used for ?

return adWordsUser;
/* c# code END */


So this then seems to work and I appear to be able to access the data in my Adwords account, which is great, but can I ask the following

  1. Is this the correct method for setting up OAuth2 access to an Adwords account for use within a Windows Service? - I have no user other than myself that would need to verify that my Windows Service is allowed access to my Adwords account data.
  2. Do I have any need for the Authorisation Code as given in the browser window as described in point 6 above as I explained the OAuth2TokenGenerator process ?
  3. When I create my AdWordsUser object, do I need to utilise the OAuth2RefreshToken? Is this RefreshToken relevent to my set up?
  4. Is my ClientId and ClientSecret about to expire at any point in the future, like an Access Token might expire with a mobile application style user access scenario?
  5. We currently deploy more than one Windows Service to access the same Adwords account. Can more than 1 Windows service use these same credentials at the same time?
  6. When logged into my account at https://code.google.com/apis/console/b/0/, I can see my "ClientId For Installed Applications" within which I see my Client ID and Client Secret - I also see RedirectURI's. Do I have any need for these?


A lot of questions i know, so many thanks in advance

Giles Bodger


 



 

Anash P. Oommen (AdWords API Team)

unread,
Oct 15, 2013, 10:08:15 AM10/15/13
to adwor...@googlegroups.com
Hi Giles,

That's a lot of questions, I'll try to get all your questions answered. Feel free to follow up if you have more questions.

At a low level, OAuth flow involves 
    (a) generating an authorization url
    (b) Navigating the user to the url and asking user to authorize it.
    (c) Obtaining an authorization code from server once the user authorizes the access (This is the authorization code you see on the url)
    (d) Exchanging the authorization code for an access token (for authorizing your API calls, short lived, typically expires in 1 hour) and optionally a refresh token (for obtaining a new access token once the old one expires, won't expire until the user explicitly revokes this token)
    (e) Making calls with access token in OAuth2 Authorization header.
    (f) Detecting when the access token expires (either track remaining lifetime of the token, or examine the errorcode thrown by the server)
    (g) Use refresh token to get a new access token, repeat (e) and (f)

If you have just one customer, you can use OAuthTokenGenerator.exe to do steps (a) to (d). If you have an application where users login interactively, you need to implement (a) to (d) as shown in the OAuth ASP.NET code example. The client library takes care of (e) to (g) provided you mention the configuration in app.config or you load them into AdWordsUser at runtime.

To answer your specific questions,

1. Yes, you are right in thinking that you can generate the configuration one-time using OAuthTokenGenerator.exe and use that with your Windows service.
2. No, it is used only for obtaining the access and refresh tokens.
3. Yes, it is used to refresh the access token automatically once it expires. Since your service is a Windows service, you will definitely need this token.
4. ClientId and ClientSecret won't expire. Only access token will expire.
5. Yes, you can.
6. Yes, you need to keep the project on the Google API Console in tact, since the access and refresh tokens are issued against the Google API Console.

Hope this helps. Let me know if you have more questions,

Cheers,
Anash P. Oommen,
AdWords API Advisor

giles bodger

unread,
Oct 15, 2013, 10:45:14 AM10/15/13
to adwor...@googlegroups.com
Hi Anash,

Thanks very much for that speedy response - hugely appreciated. 

So it looks like I am good to go with using the configuration I generated as long as I use the OAuth2RefreshToken, which as you have said will auto refresh the access token as and when required

I understand the OAuth2 flow as you describe, but have one last question.

Do I ever see this "access token", or is it hidden within the client library framework's communication - ie within steps (e) - (g)

many thanks in advance

Giles

giles bodger

unread,
Nov 3, 2013, 2:42:32 PM11/3/13
to adwor...@googlegroups.com
Hi Anash,

So we put our OAuth2 enabled application into our production environment, which caters for synching data, updating data and processing reports. We immediately ran into hundreds of the following errors .....

........
<ExceptionType>Google.Api.Ads.Common.Lib.AdsOAuthException</ExceptionType>
    <StackTrace>An unhandled exception occurred.
[Google.Api.Ads.Common.Lib.AdsOAuthException]: Failed to refresh access token.
&lt;HTML&gt;
&lt;HEAD&gt;
&lt;TITLE&gt;User Rate Limit Exceeded&lt;/TITLE&gt;
&lt;/HEAD&gt;
&lt;BODY BGCOLOR="#FFFFFF" TEXT="#000000"&gt;
&lt;H1&gt;User Rate Limit Exceeded&lt;/H1&gt;
&lt;H2&gt;Error 403&lt;/H2&gt;
&lt;/BODY&gt;
&lt;/HTML&gt;


Stack Trace: 
   at Google.Api.Ads.Common.Lib.OAuth2ProviderForApplications.RefreshAccessTokenInOfflineMode()
   at Google.Api.Ads.Common.Lib.OAuth2ProviderBase.GetAuthHeader()
   at Google.Api.Ads.AdWords.Lib.AdWordsSoapClient.InitForCall(String methodName, Object[] parameters)
      .........


I thought from your explanation, that the access token is refreshed automatically - under the hood by the client library framework - when we supply the refresh token at run time - which is what i am doing.

We have hit rate limit exceeded errors previously and have built in support to back off to not keep incurring them (appreciate that we don't incur a penalty for requests made when developer / account level usage is in breach) and making unnecessary requests.

If you could shed any light on this I would be most appreciative

many thanks in advance

Giles


On Tuesday, October 15, 2013 3:08:15 PM UTC+1, Anash P. Oommen (AdWords API Team) wrote:

Anash P. Oommen (AdWords API Team)

unread,
Nov 5, 2013, 6:54:29 AM11/5/13
to adwor...@googlegroups.com
Hi Giles,

The error suggests that you are refreshing the access token very frequently (or rather, the library is doing this very frequently for some reason). Could you provide more details on how your code works? Is it multi threaded? Are you creating mutliple AdWordsUsers? How are you retrieving OAuth2 tokens? Feel free to reply to author and ping this thread in case you don't want to discuss some of these details in the public forum.

Cheers,
Anash P. Oommen,
AdWords API Advisor.

giles bodger

unread,
Nov 5, 2013, 1:03:45 PM11/5/13
to adwor...@googlegroups.com
Hi Anash,

Our Windows service looks after all of our synching from AW, pushing updates to AW and downloading reports from AW. We have sql jobs that kick off these requests for all of our accounts multiple times per hour. Everything was working fine with Client Login - not to say that it wont with OAuth i am sure :)

At the time of the errors, we would have been running up to 15 threads on Synch, 15 threads on Updates and 25 threads on Reports. Every request we make to the API has a new AdWordsUser object created as follows ....

/* c# code START */
var adWordsUser = new AdWordsUser();

adWordsUser.Config.OAuth2ClientId = myConfig._oAuth2ClientId;
adWordsUser.Config.OAuth2ClientSecret = myConfig._oAuth2ClientSecret;
adWordsUser.Config.OAuth2RefreshToken = myConfig._oAuth2RefreshToken;

return adWordsUser;
/* c# code END */

....
(appreciate that i don't actually have to explicitly set these values as they are retrieved from the config at runtime)


within each account synch or update push or report request, we call any or all of the following AW services ...

ManagedCustomerService
CampaignService
BudgetService
AdGroupService
AdGroupAdService
AdParamService
CampaignCriterionService
AdGroupCriterionService
CustomerSyncService
ReportUtilitiesService
AdGroupBidModifierService
FeedService
FeedItemService
FeedMappingService
CampaignFeedService

...... 

We were at the AdWords workshop yesterday in London and brought this up during the OAuth presentation and Paul (cant remember his surname - too many Pauls!) said that it might be because we are creating a new AdWordsUser object for each request, and that this may have caused the library to have made too many refresh attempts ?

As to how we are retrieving OAuth2 tokens, I have created a refresh token using the OAuthTokenGenerator.exe and am simply using this along with my ClientId and Secret within the configuration. My understanding from our conversation so far was that the actual access token is created and used under the hood by the client library and that the refresh token simply refreshes this token.

Just as a side note - We also have a wrapper around all our requests to catch exceptions and to apply the necessary wait times if we are Rate Limited - however we have only been managing ACCOUNT and DEVELOPER scope levels of Rate Limit and not the USER level as we were getting with these particular Rate Limit exceptions.

I know that the ClientId and Secret and Refresh Token do work because i can make requests of our production AW accounts from my local workstation with the same OAuth credentials that were failing - it appears to be the multiple threads of requests that we are getting stuck on.

Many thanks in advance

giles bodger

unread,
Nov 7, 2013, 6:13:00 AM11/7/13
to adwor...@googlegroups.com
.... I mentioned the scope of the error as being USER - but have just noted that this is not the scope - no scope is returned - the USER string was simply part of the error message -  

Any how - do you have any thoughts on what i can do to solve the problem?

Anash P. Oommen (AdWords API Team)

unread,
Nov 7, 2013, 3:28:35 PM11/7/13
to adwor...@googlegroups.com
Hi Giles,

I'll look at this a bit more closely later today, but I agree that the problem is about your 40 threads all trying to refresh the access token at the same time. The workaround would be to share the access token among the various threads.

A simple workaround would be to ensure that the AdsOAuthProvider is shared among all the AdWordsUsers. Something like:

AdWordsAppConfig config = new AdWordsAppConfig();
// Set all the necessary property overrides here.

AdsOAuthProvider provider = new OAuth2ProviderForApplications(config);
// Optional: Fetch an access token before initalizing the users
provider.RefreshAccessToken();

// Now create the users. Make sure you share the provider amongst them.
AdWordsUser user1 = new AdWordsUser();
AdWordsUser user2 = new AdWordsUser();

user1.OAuthProvider = user2.OAuthProvider = provider;

This way, the users won't race in the beginning to refresh access tokens all at once.

I'll make sure that this functionality is clearer / cleaner in a future version of the client library.

Cheers,
Anash P. Oommen,
AdWords API Advisor.

giles bodger

unread,
Nov 8, 2013, 6:55:12 AM11/8/13
to adwor...@googlegroups.com
Hi Anash,

I tried that and it looks to have solved the problem :-) 

many thanks for you help on this one

Giles
Reply all
Reply to author
Forward
0 new messages