Hide

OAuth 2.0

This document describes OAuth 2.0, when to use it, how to acquire client IDs, and how to use it with the Google API Client Library for .NET.

OAuth 2.0 Protocol

OAuth 2.0 is the authorization protocol used by Google APIs. You should get familiar with the protocol by reading the following links:

Acquiring client IDs and secrets

You can get client IDs and secrets on the Google Developers Console. There are different types of client IDs, so be sure to get the correct type for your application:

In each of the code snippets below (except the Service account one), you have to download the client secret and store it as client_secrets.json in your project.

Credentials

User credentials

UserCredential is a thread-safe helper class for using an access token to access protected resources. An access token typically expires after 1 hour, after which you will get an error if you try to use it.

UserCredential and AuthorizationCodeFlow take care of automatically "refreshing" the token, which simply means getting a new access token. This is done using a long-lived refresh token, which you receive along with the access token if you use the access_type=offline parameter during the authorization code flow.

In most applications, it is advisable to store the credential's access token and refresh token in persistent storage. Otherwise, you will need to present the end user with an authorization page in the browser every hour, because the access token expires an hour after you've received it.

To make sure the access and refresh tokens persist, you can provide your own implementation of IDataStore, or you can use one of the following implementations provided by the library:

ServiceAccountCredential

ServiceAccountCredential is similar to UserCredential, but it serves a different purpose. Google OAuth 2.0 supports server-to-server interactions such as those between a web application and Google Cloud Storage. The requesting application has to prove its own identity to gain access to an API, and an end user doesn't have to be involved. ServiceAccountCredential stores a private key, which is used to sign a request to get a new access token.

Both UserCredential and ServiceAccountCredential implement IConfigurableHttpClientInitializer so you can register each of these as:
  • An unsuccessful response handler, so it will refresh the token if it receives an HTTP 401 status code.
  • An interceptor, to intercept the Authorization header on every request.

Installed applications

Sample code using the Books API:
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

using Google.Apis.Auth.OAuth2;
using Google.Apis.Books.v1;
using Google.Apis.Books.v1.Data;
using Google.Apis.Services;
using Google.Apis.Util.Store;

namespace Books.ListMyLibrary
{
    /// <summary>
    /// Sample which demonstrates how to use the Books API.
    /// https://developers.google.com/books/docs/v1/getting_started
    /// <summary>
    internal class Program
    {
        [STAThread]
        static void Main(string[] args)
        {
            Console.WriteLine("Books API Sample: List MyLibrary");
            Console.WriteLine("================================");
            try
            {
                new Program().Run().Wait();
            }
            catch (AggregateException ex)
            {
                foreach (var e in ex.InnerExceptions)
                {
                    Console.WriteLine("ERROR: " + e.Message);
                }
            }
            Console.WriteLine("Press any key to continue...");
            Console.ReadKey();
        }

        private async Task Run()
        {
            UserCredential credential;
            using (var stream = new FileStream("client_secrets.json", FileMode.Open, FileAccess.Read))
            {
                credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
                    GoogleClientSecrets.Load(stream).Secrets,
                    new[] { BooksService.Scope.Books },
                    "user", CancellationToken.None, new FileDataStore("Books.ListMyLibrary"));
            }

            // Create the service.
            var service = new BooksService(new BaseClientService.Initializer()
                {
                    HttpClientInitializer = credential,
                    ApplicationName = "Books API Sample",
                });

            var bookshelves = await service.Mylibrary.Bookshelves.List().ExecuteAsync();
            ...
        }
    }
}
  • In this sample code a new UserCredential instance is created by calling the GoogleWebAuthorizationBroker.AuthorizeAsync method. This static method gets the following:

    • The client secret (or a stream to the client secret).
    • The required scopes.
    • The user identifier.
    • The cancellation token for cancelling an operation.
    • An optional data store. If the data store is not specified, the default is a FileDataStore with a default “Google.Apis.Auth” folder. The folder is created in Environment.SpecialFolder.ApplicationData.

  • The UserCredential that is returned by this method is set as a HttpClientInitializer on the BooksService (using the initializer). As explained above, UserCredential implements an HTTP client initializer.

  • Notice that in the above sample code, the client secret information is loaded from a file, but you can also do the following:

    credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
        new ClientSecrets
        {
            ClientId = "PUT_CLIENT_ID_HERE",
            ClientSecret = "PUT_CLIENT_SECRETS_HERE"
        },
        new[] { BooksService.Scope.Books },
        "user",
        CancellationToken.None,
        new FileDataStore("Books.ListMyLibrary"));
    Take a look at our Books sample.

Web applications (ASP.NET MVC)

Google APIs support OAuth 2.0 for Web Server Applications. In order to run the following code successfully, you must first add a redirect URI to your project in the Google Developers Console. Since you will use FlowMetadata and its default settings, set the redirect URI to your_site/AuthCallback/IndexAsync.

To find the redirect URIs for your OAuth 2.0 credentials, do the following:

  1. Open the Credentials page.
  2. If you haven't done so already, create your OAuth 2.0 credentials by clicking Add credentials > OAuth 2.0 client ID.
  3. After you create your credentials, view or edit the redirect URLs by clicking the client ID in the OAuth 2.0 client IDs section.

After creating a new web application project in your IDE, add the right Google.Apis NuGet package for Drive, YouTube, or the other service you want to use. Then, add the Google.Apis.Auth.MVC package. The following code demonstrates an ASP.NET MVC application that queries a Google API service.

  1. Add your own implementation of FlowMetadata.
    using System;
    using System.Web.Mvc;
    
    using Google.Apis.Auth.OAuth2;
    using Google.Apis.Auth.OAuth2.Flows;
    using Google.Apis.Auth.OAuth2.Mvc;
    using Google.Apis.Drive.v2;
    using Google.Apis.Util.Store;
    
    namespace Google.Apis.Sample.MVC4
    {
        public class AppFlowMetadata : FlowMetadata
        {
            private static readonly IAuthorizationCodeFlow flow =
                new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
                    {
                        ClientSecrets = new ClientSecrets
                        {
                            ClientId = "PUT_CLIENT_ID_HERE",
                            ClientSecret = "PUT_CLIENT_SECRET_HERE"
                        },
                        Scopes = new[] { DriveService.Scope.Drive },
                        DataStore = new FileDataStore("Drive.Api.Auth.Store")
                    });
    
            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.
                var user = controller.Session["user"];
                if (user == null)
                {
                    user = Guid.NewGuid();
                    controller.Session["user"] = user;
                }
                return user.ToString();
    
            }
    
            public override IAuthorizationCodeFlow Flow
            {
                get { return flow; }
            }
        }
    }

    FlowMetadata is an abstract class that contains your own logic for retrieving the user identifier and the IAuthorizationCodeFlow you are using.

    In the above sample code a new GoogleAuthorizationCodeFlow is created with the right scopes, client secrets, and the data store. Consider adding your own implementation of IDataStore, for example you could write one that uses EntityFramework.

  2. Implement your own controller that uses a Google API service. The following sample uses a DriveService:
    using System.Linq;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Web.Mvc;
    
    using Google.Apis.Auth.OAuth2.Mvc;
    using Google.Apis.Drive.v2;
    using Google.Apis.Services;
    
    using Google.Apis.Sample.MVC4;
    
    namespace Google.Apis.Sample.MVC4.Controllers
    {
        public class HomeController : Controller
        {
            public async Task IndexAsync(CancellationToken cancellationToken)
            {
                var result = await new AuthorizationCodeMvcApp(this, new AppFlowMetadata()).
                    AuthorizeAsync(cancellationToken);
    
                if (result.Credential != null)
                {
                    var service = new DriveService(new BaseClientService.Initializer
                        {
                            HttpClientInitializer = result.Credential,
                            ApplicationName = "ASP.NET MVC Sample"
                        });
    
                    // YOUR CODE SHOULD BE HERE..
                    // SAMPLE CODE:
                    var list = await service.Files.List().ExecuteAsync();
                    ViewBag.Message = "FILE COUNT IS: " + list.Items.Count();
                    return View();
                }
                else
                {
                    return new RedirectResult(result.RedirectUri);
                }
            }
        }
    }
  3. Implement your own callback controller. The implementation should be something like this:
    using Google.Apis.Sample.MVC4;
    
    namespace Google.Apis.Sample.MVC4.Controllers
    {
        public class AuthCallbackController : Google.Apis.Auth.OAuth2.Mvc.Controllers.AuthCallbackController
        {
            protected override Google.Apis.Auth.OAuth2.Mvc.FlowMetadata FlowData
            {
                get { return new AppFlowMetadata(); }
            }
        }
    }

Service account

Google APIs also support Service accounts. Unlike the scenario in which a client application requests access to an end-user's data, service accounts provide access to the client application's own data.

Your client application signs the request for an access token using a private key downloaded from the Google Developers Console. After creating a new client ID, you should choose a “Service Account” application type and then you can download the private key. Take a look at our service account sample using Google Plus API.

using System;
using System.Security.Cryptography.X509Certificates;

using Google.Apis.Auth.OAuth2;
using Google.Apis.Plus.v1;
using Google.Apis.Plus.v1.Data;
using Google.Apis.Services;

namespace Google.Apis.Samples.PlusServiceAccount
{
    /// <summary>
    /// This sample demonstrates the simplest use case for a Service Account service.
    /// The certificate needs to be downloaded from the Google Developers Console
    /// <see cref="https://console.developers.google.com/">
    ///   "Create another client ID..." -> "Service Account" -> Download the certificate,
    ///   rename it as "key.p12" and add it to the project. Don't forget to change the Build action
    ///   to "Content" and the Copy to Output Directory to "Copy if newer".
    /// </summary>
    public class Program
    {
        // A known public activity.
        private static String ACTIVITY_ID = "z12gtjhq3qn2xxl2o224exwiqruvtda0i";

        public static void Main(string[] args)
        {
            Console.WriteLine("Plus API - Service Account");
            Console.WriteLine("==========================");

            String serviceAccountEmail = "SERVICE_ACCOUNT_EMAIL_HERE";

            var certificate = new X509Certificate2(@"key.p12", "notasecret", X509KeyStorageFlags.Exportable);

            ServiceAccountCredential credential = new ServiceAccountCredential(
               new ServiceAccountCredential.Initializer(serviceAccountEmail)
               {
                   Scopes = new[] { PlusService.Scope.PlusMe }
               }.FromCertificate(certificate));

            // Create the service.
            var service = new PlusService(new BaseClientService.Initializer()
            {
                HttpClientInitializer = credential,
                ApplicationName = "Plus API Sample",
            });

            Activity activity = service.Activities.Get(ACTIVITY_ID).Execute();
            Console.WriteLine("  Activity: " + activity.Object.Content);
            Console.WriteLine("  Video: " + activity.Object.Attachments[0].Url);

            Console.WriteLine("Press any key to continue...");
            Console.ReadKey();
        }
    }
}

The above sample code creates a ServiceAccountCredential. The required scopes are set and there is a call to FromCertificate, which loads the private key from the given X509Certificate2. As in all other samples code, the credential is set as HttpClientInitializer.

Windows Phone 8.0 (WP)

The following sample code is very similar to the .NET 4 application OAuth flow. The following sample creates a new DriveService using the GoogleWebAuthorizationBroker implementation for WP. By default, StorageDataStore is used to store the access token and the refresh token.

using System;
using System.Windows;
using System.IO;
using System.Threading;
using System.Text;
using Microsoft.Phone.Controls;

using Google.Apis.Drive.v2;
using Google.Apis.Services;
using Google.Apis.Auth.OAuth2;

namespace Drive.WP.Sample
{
    public partial class MainPage : PhoneApplicationPage
    {
        public MainPage()
        {
            InitializeComponent();
        }

        private async void Button_Click(object sender, RoutedEventArgs e)
        {
            var credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
                new FileStream("client_secrets.json", FileMode.Open, FileAccess.Read),
                new[] { DriveService.Scope.Drive },
                "user",
                CancellationToken.None);

            var initializer = new BaseClientService.Initializer()
            {
                HttpClientInitializer = credential,
                ApplicationName = "WP Drive Sample Application",
            };

            var service = new DriveService(initializer);
            var list = await service.Files.List().ExecuteAsync();
            foreach (var file in list.Items)
            {
                // You can get data from the file (using file.Title for example)
                // and append it to a TextBox, List, etc.
            }
        }
    }
}

Windows Phone 8.1

The Windows Phone 8.1 authentication API is not compatible with the Windows Phone 8.0 authentication API. The WebAuthenticationBroker.AuthenticateAsync method, used in Windows Phone 8.0, is not supported anymore. Starting with Windows 8.1, Microsoft supports several AndContinue methods, one of them is WebAuthenticationBroker.AuthenticateAndContinue.

When you call an AndContinue method, the app is deactivated until the operation completes. You can read more in How to continue your Windows Phone Store app after calling an AndContinue method. So, in order to support the OAuth 2.0 protocol for Windows Phone 8.1 Store apps, you will have to do the following:

  1. After creating a new Windows Phone (or Universal) project in your IDE, add the right Google.Apis NuGet package for Drive, YouTube, or the other service you want to use. The following snippets use the Drive API.
  2. If you are not using version 1.9.1 of Google.Apis and Google.Apis.Auth or later, run the following commands in the Package Manager Console:
    Update-Package Google.Apis
    Update-Package Google.Apis.Auth
  3. Create a new class in the Common folder (if a solution Common folder doesn't exist, create a one) and name it ContinuationManager. Replace the class content with the following implementation:
    using Windows.ApplicationModel.Activation;
    /// <summary>
    /// ContinuationManager is used to detect if the most recent activation was due to a
    /// authentication continuation.
    ///
    /// Note: To keep this sample as simple as possible, the content of the file was changed to support
    /// WebAuthenticationBrokerContinuation ONLY.
    /// Take a look in http://msdn.microsoft.com/en-us/library/dn631755.aspx
    /// for a full documentation on how to support continuation in other cases.
    /// </summary>
    public class ContinuationManager
    {
        /// <summary>
        /// Sets the ContinuationArgs for this instance.
        /// Should be called by the main activation handling code in App.xaml.cs.
        /// </summary>
        /// <param name="args">The activation args.</param>
        internal void Continue(IContinuationActivatedEventArgs args)
        {
            if (args.Kind == ActivationKind.WebAuthenticationBrokerContinuation)
            {
                var page = MainPage.Current as IWebAuthenticationContinuable;
                if (page != null)
                {
                    page.ContinueWebAuthentication(
                        args as WebAuthenticationBrokerContinuationEventArgs);
                }
            }
        }
    }
    
    /// <summary>Implement this interface if your page invokes the web authentication broker.</summary>
    interface IWebAuthenticationContinuable
    {
        /// <summary>
        /// This method is invoked when the web authentication broker returns with the authentication result.
        /// </summary>
        /// <param name="args">Activated event args object that contains returned authentication token.</param>
        void ContinueWebAuthentication(WebAuthenticationBrokerContinuationEventArgs args);
    }
    This light implementation targets authentication ONLY. A full implementation is available at: http://msdn.microsoft.com/en-us/library/dn631755.aspx.
  4. Add a static Current property to the MainPage class. The new property returns the MainPage and it should be set in the constructor.
    Change the class to inherit from the Common.IWebAuthenticationContinuable interface, and keep the ContinueWebAuthentication method empty for now.
    At this point, the MainPage class should look like the following:
    public sealed partial class MainPage : Page, Common.IWebAuthenticationContinuable
    {
        public static MainPage Current { get; private set; }
        public MainPage()
        {
            this.InitializeComponent();
    
            this.NavigationCacheMode = NavigationCacheMode.Required;
            Current = this;
        }
    
        public void ContinueWebAuthentication(
            Windows.ApplicationModel.Activation.WebAuthenticationBrokerContinuationEventArgs args)
        {
            // WILL BE IMPLEMENTED LATER ON.
        }
    
        // MORE CODE HERE
    }
  5. Change the project main entry point, the App class, by editing (or adding) the OnActivated method. OnActivated is called when the app is activated again, which might be be after the authorization flow was called and an authorization code was received. The implementation should look as the following:
    /// <summary>
    /// On activated callback. It is used in order to continue the application
    /// after the user authenticated.
    /// </summary>
    protected override void OnActivated(IActivatedEventArgs e)
    {
        base.OnActivated(e);
        var continuationManager = new Common.ContinuationManager();
        var continuationEventArgs = e as IContinuationActivatedEventArgs;
        if (continuationEventArgs != null)
        {
            // Call ContinuationManager to handle continuation activation.
            // The default ContinuationManager (which is documented in
            // http://msdn.microsoft.com/en-us/library/dn631755.aspx) was changed to
            /// handle ONLY authentication.
            // It supports now current window and NOT current active frame.
            continuationManager.Continue(continuationEventArgs);
        }
        Window.Current.Activate();
    }
        
  6. Return to the MainPage class and follow these final steps:
    1. Add the following using statements:
      using System.Collections.ObjectModel;
      using System.Threading;
      using System.Threading.Tasks;
      
      using Windows.ApplicationModel.Core;
      using Windows.UI.Core;
      
      using Google.Apis.Auth.OAuth2;
      using Google.Apis.Util.Store;
      using Google.Apis.Services;
      
      using Google.Apis.Drive.v2; // Or the exact service you are using.
      using Google.Apis.Drive.v2.Data;
    2. Add the following fields and properties:
      private UserCredential credential;
      private DriveService service;
      
      public ObservableCollection<File> Files { get; private set; }
    3. Add the following methods:
      private async Task AuthenticateAsync()
      {
          if (service != null)
              return;
      
          credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
              new Uri("ms-appx:///Assets/client_secrets.json"),
              new[] { DriveService.Scope.DriveReadonly },
              "user",
              CancellationToken.None);
      
          var initializer = new BaseClientService.Initializer()
          {
              HttpClientInitializer = credential,
              ApplicationName = "WindowsPhoneSample",
          };
      
          service = new DriveService(initializer);
      }
      
      public async Task GetFilesAsync()
      {
          await AuthenticateAsync();
          var list = await service.Files.List().ExecuteAsync();
      
          // Update the Files collection from the thread it was created (the UI thread).
          await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(
              CoreDispatcherPriority.Normal,
              () =>
              {
                  Files.Clear();
                  foreach (var file in list.Items)
                  {
                      Files.Add(file);
                  }
              });
      }
      
      private async void GetFiles_Click(object sender, RoutedEventArgs e)
      {
          await GetFilesAsync();
      }
    4. Change the implementation of ContinueWebAuthentication as the following, note that the method is async now.
      public async void ContinueWebAuthentication(
          Windows.ApplicationModel.Activation.WebAuthenticationBrokerContinuationEventArgs args)
      {
          await PasswordVaultDataStore.Default.StoreAsync<SerializableWebAuthResult>(
              SerializableWebAuthResult.Name,
              new SerializableWebAuthResult(args.WebAuthenticationResult));
      
          await GetFilesAsync();
      
          await PasswordVaultDataStore.Default.DeleteAsync<SerializableWebAuthResult>(
              SerializableWebAuthResult.Name);
      }
      Note that the code snippet above uses PasswordVaultDataStore to store the authentication result. PasswordVaultDataStore is the library data store for Windows Phone 8.1, and we use it here to store the authentication result using the SerializableWebAuthResult.Name key.
      GoogleWebAuthorizationBroker.AuthorizeAsync internal implementation fetches this key to verify if an authorization code was already received by the app and it can replace it with an access token, or a new authorization page should be displayed to the user.
    5. Add the following two statement in the end of MainPage constructor:
      Files = new ObservableCollection<File>();
      this.DataContext = this;
    6. And the last step is a UI change to MainPage.xaml in order to add a button and a list. Please add the following content inside the main Grid component:
      <Grid.RowDefinitions>
          <RowDefinition Height="Auto" />
          <RowDefinition />
      </Grid.RowDefinitions>
      
      <Button Content="Get Files" Click="GetFiles_Click" />
      <ListBox Grid.Row="1" ItemsSource="{Binding Files}">
          <ListBox.ItemTemplate>
              <DataTemplate>
                  <TextBlock Text="{Binding Title}" />
              </DataTemplate>
          </ListBox.ItemTemplate>
      </ListBox>
  7. Lastly, download the client secret, rename it to client_secrets.json, and add it to the project in the Assets folder, as suggested in Acquiring client IDs and secrets.
A full Universal App sample (which works both for Windows 8.1 and Windows Phone 8.1) using MVVM best practices, is available in the samples repository. Take a look in Blogger.Sample for more information.

Windows 8.1 applications

The code for a Windows 8.1 application is very similar to the Windows Phone sample. It creates a new CalendarService using a GoogleWebAuthorizationBroker for Windows 8.1 application. By default, StorageDataStore is used to store the access token and the refresh token.

var credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
    new Uri("ms-appx:///Assets/client_secrets.json"),
    new[] { Uri.EscapeUriString(CalendarService.Scope.Calendar) },
    "user",
    CancellationToken.None);
var calendarService = new CalendarService(new BaseClientService.Initializer
    {
        HttpClientInitializer = credential,
        ApplicationName = "Windows 8.1 Calendar sample"
    });
var calendarListResource = await calendarService.CalendarList.List().ExecuteAsync();