6

I want to use Google Analytics API using OAuth.

I am using this library: http://code.google.com/p/google-api-dotnet-client/

The following code is used for authentication:

var credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
    new ClientSecrets { ClientId = "...", ClientSecret = "..." },
    new[] {Google.Apis.Analytics.v3.AnalyticsService.Scope.AnalyticsReadonly},
    "user",
    CancellationToken.None,
    new FileDataStore("Analytics.Auth.Store")).Result;

var service = new Google.Apis.Analytics.v3.AnalyticsService(
    new BaseClientService.Initializer
    {
        HttpClientInitializer = credential,
        ApplicationName = "...",
    });

Can I use this with a refresh_token so that I do not have to accept the authorization request every few days?

Something like in the answer of this question: Service Account Google Analytics OAuth AccessType = Offline C#

Community
  • 1
  • 1
Dennis
  • 1,027
  • 1
  • 14
  • 30

2 Answers2

12

I know only one way: you need to override GoogleAuthorizationCodeRequestUrl, but I have no idea how to use this with AuthorizationBroker.

internal class ForceOfflineGoogleAuthorizationCodeFlow : GoogleAuthorizationCodeFlow
{
    public ForceOfflineGoogleAuthorizationCodeFlow(AuthorizationCodeFlow.Initializer initializer) : base(initializer) { }

    public override AuthorizationCodeRequestUrl CreateAuthorizationCodeRequest(string redirectUri)
    {
        return new GoogleAuthorizationCodeRequestUrl(new Uri(AuthorizationServerUrl))
                {
                    ClientId = ClientSecrets.ClientId,
                    Scope = string.Join(" ", Scopes),
                    RedirectUri = redirectUri,
                    AccessType = "offline",
                    ApprovalPrompt = "force"
                };
    }
};

Looks like they create Flow inside the broker: GoogleWebAuthorizationBroker.cs

And I didn't see any way to pass params or replace AuthorizationCodeFlow

Darida
  • 196
  • 1
  • 8
  • We are now using a service account with certificate which was easier to set up, so I did not try this approach. I am marking it as the answer though. – Dennis Mar 04 '14 at 10:52
  • Dennis, do you have instructions on how to set up the "service account with certificate" that you mentioned. I've been trying with not much luck. – Jarvis Mar 07 '14 at 21:18
  • You saved my day. Great help. – Hugo Hilário Dec 02 '14 at 10:47
  • Very very helpful, thanks a lot. It's very unfortunate that "AccessType" cannot be configured from GoogleAuthorizationCodeFlow as an attribute like clientid, scope etc. (https://developers.google.com/accounts/docs/OAuth2WebServer#overview) – Dhanuka777 Dec 29 '14 at 08:40
  • 1
    Btw, you cannot create a constructor with "AuthorizationCodeFlow.Initializer initializer", it has to be a "GoogleAuthorizationCodeFlow.Initialier". – Dhanuka777 Dec 29 '14 at 08:42
8

Apparently I can't comment, but to extend on Darida's answer:

Make your custom CodeFlow

public class CustomAuthorizationCodeFlow : GoogleAuthorizationCodeFlow
{
    public CustomAuthorizationCodeFlow(GoogleAuthorizationCodeFlow.Initializer initializer) : base(initializer) { }

    public override AuthorizationCodeRequestUrl CreateAuthorizationCodeRequest(String redirectUri)
    {
        return new GoogleAuthorizationCodeRequestUrl(new Uri(AuthorizationServerUrl))
        {
            ClientId = ClientSecrets.ClientId,
            Scope = string.Join(" ", Scopes),
            RedirectUri = redirectUri,
            AccessType = "online",
            ApprovalPrompt = "auto"
        };
    }
}

Then make a custom FlowMetadata

public class AppFlowMetadata : FlowMetadata
{
    private static readonly IAuthorizationCodeFlow flow =
        new CustomAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
        {
            ClientSecrets = new ClientSecrets
            {
                ClientId = "...",
                ClientSecret = "..."
            },
            Scopes = new String[] { AnalyticsService.Scope.AnalyticsReadonly },
            DataStore = new EFDataStore(),
        });

    public override IAuthorizationCodeFlow Flow
    {
        get { return flow; }
    }

    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.

        return String.Format("user-{0}", WebSecurity.GetUserId(controller.User.Identity.Name));
    }
}

and then in the Controller

public ActionResult Sample()
{
    var result = await new AuthorizationCodeMvcApp(this, new AppFlowMetadata()).AuthorizeAsync(cancellationToken);

    if (result.Credential != null)
    {
        var service = new AnalyticsService(new BaseClientService.Initializer()
        {
            HttpClientInitializer = result.Credential,
            ApplicationName = APPLICATION_NAME
        });
   }
}
Peter Dolkens
  • 133
  • 1
  • 7
  • How can you do this without MVC? It seems like every example uses MVC instead of just Web API – Rob L Dec 22 '16 at 14:24