0
votes

I can get access by getting auth token from browser and using "Fetch..." later. But I want to do it without user intervention. I decided to use Service account. But when i'm trying to get access, I always receive "access denied". I'm using

var oAuth2Provider =
                (user.OAuthProvider as AdsOAuthProviderForServiceAccounts);
        oAuth2Provider.GenerateAccessTokenForServiceAccount();

The properties of oAuth2Provider are:

JwtCertificatePassword "notasecret"
JwtCertificatePath "D://xxxxx-privatekey.p12"
PrnEmail "[email protected]" //it's my mcc account with approved dev token
ServiceAccountEmail "22222222222-ckvf630rcccn3j7jjuf4fmujnbiotett@developer.gserviceaccount.com"

Here are stack trace of error.

    at Google.Api.Ads.Common.Lib.OAuth2ProviderForServiceAccounts.GenerateAccessTokenForServiceAccount()
   at AdwordsStat.GetReports.DoAuth2Authorization(AdsUser user) in D:\Adwords_API\Jooble_Adwords\GetReports.cs:line 48
   at AdwordsStat.Form1.Form1_Shown(Object sender, EventArgs e) in D:\Adwords_API\Jooble_Adwords\Form1.cs:line 87
   at System.Windows.Forms.Form.OnShown(EventArgs e)
   at System.Windows.Forms.Form.CallShownEvent()
   at System.Windows.Forms.Control.InvokeMarshaledCallbackDo(ThreadMethodEntry tme)
   at System.Windows.Forms.Control.InvokeMarshaledCallbackHelper(Object obj)
   at System.Threading.ExecutionContext.runTryCode(Object userData)
   at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Windows.Forms.Control.InvokeMarshaledCallback(ThreadMethodEntry tme)
   at System.Windows.Forms.Control.InvokeMarshaledCallbacks()
   at System.Windows.Forms.Control.WndProc(Message& m)
   at System.Windows.Forms.ScrollableControl.WndProc(Message& m)
   at System.Windows.Forms.Form.WndProc(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
   at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
   at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
   at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr dwComponentID, Int32 reason, Int32 pvLoopData)
   at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
   at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
   at System.Windows.Forms.Application.Run(Form mainForm)
   at AdwordsStat.Program.Main() in D:\Adwords_API\Jooble_Adwords\Program.cs:line 16
   at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
   at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
   at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
   at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ThreadHelper.ThreadStart()

Then I've read about neediness of google apps account. I've registered,added to it's account cliend id from my service account that looks like

2222222222222-ckvf630rcccn3j7jjuf4fmujnbiotett.apps.googleusercontent.com scope : https://adwords.google.com/api/adwords/

But I get access denied again.

Then i've tried to use another lib which looks like `

public static Dictionary<string, string> GetAccessToken(string clientIdEMail, string keyFilePath, string scope)
        {
            // certificate
            var certificate = new X509Certificate2(keyFilePath, "notasecret");

        // header
        var header = new { typ = "JWT", alg = "RS256" };

        // claimset
        var times = GetExpiryAndIssueDate();
        var claimset = new
        {
            iss = clientIdEMail,
            scope = scope,
            aud = "https://accounts.google.com/o/oauth2/token",
            iat = times[0],
            exp = times[1],
        };

        JavaScriptSerializer ser = new JavaScriptSerializer();

        // encoded header
        var headerSerialized = ser.Serialize(header);
        var headerBytes = Encoding.UTF8.GetBytes(headerSerialized);
        var headerEncoded = Convert.ToBase64String(headerBytes);

        // encoded claimset
        var claimsetSerialized = ser.Serialize(claimset);
        var claimsetBytes = Encoding.UTF8.GetBytes(claimsetSerialized);
        var claimsetEncoded = Convert.ToBase64String(claimsetBytes);

        // input
        var input = headerEncoded + "." + claimsetEncoded;
        var inputBytes = Encoding.UTF8.GetBytes(input);

        // signiture
        var rsa = certificate.PrivateKey as RSACryptoServiceProvider;
        var cspParam = new CspParameters
        {
            KeyContainerName = rsa.CspKeyContainerInfo.KeyContainerName,
            KeyNumber = rsa.CspKeyContainerInfo.KeyNumber == KeyNumber.Exchange ? 1 : 2
        };
        var aescsp = new RSACryptoServiceProvider(cspParam) { PersistKeyInCsp = false };
        var signatureBytes = aescsp.SignData(inputBytes, "SHA256");
        var signatureEncoded = Convert.ToBase64String(signatureBytes);

        // jwt
        var jwt = headerEncoded + "." + claimsetEncoded + "." + signatureEncoded;

        var client = new WebClient();
        client.Encoding = Encoding.UTF8;
        var uri = "https://accounts.google.com/o/oauth2/token";
        var content = new NameValueCollection();

        content["assertion"] = jwt;
        content["grant_type"] = "urn:ietf:params:oauth:grant-type:jwt-bearer";

        string response = Encoding.UTF8.GetString(client.UploadValues(uri, "POST", content));

        var result = ser.Deserialize<Dictionary<string, string>>(response);

        return result;
    }

    private static int[] GetExpiryAndIssueDate()
    {
        var utc0 = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
        var issueTime = DateTime.UtcNow;

        var iat = (int)issueTime.Subtract(utc0).TotalSeconds;
        var exp = (int)issueTime.AddMinutes(55).Subtract(utc0).TotalSeconds;

        return new[] { iat, exp };
    }`

And after that-i've receiving access token and expiry time.

var result = GetAccessToken(SERVICE_ACCOUNT_EMAIL, SERVICE_ACCOUNT_PKCS12_FILE_PATH, SCOPE_ANALYTICS_READONLY);
            string access_token;
            result.TryGetValue("access_token", out access_token);

then I'm trying to

oAuth2Provider.AccessToken = access_token;

But again receiving access denied

I've been trying to solve this problem for a week.

1
Is someone can help me with my problem???Aidanpraid
Have you found a solution yet?Hnatt
i solved it. i'm using google apps account to retrieve access to api now. And i've registered this account as my client center.Aidanpraid
Me too, but had a different problem: set the scope as https://adwords.google.com/api/adwords without the trailing slash, like it says here. It turns out that the trailing slash is a big deal.Hnatt

1 Answers

0
votes

It’s not 100% obvious what you’re really trying to accomplish, but if you need to have offline access on behalf of a user, what you probably want is a “refresh token”, so you can get a new access token when your initial one expires. The info is at https://developers.google.com/accounts/docs/OAuth2WebServer