Google OAuth2 C# 147


Google Drive API

Have you been trying to access Google Drive with C#? Do you need to upload files to Google Drive with .net? In this post, we will be looking into accessing Google Drive using OAuth2 with C# using the Google .net client library.   You can change the scope easily to connect to any of the other API’s I am just using drive as an example.   If you are interested in learning how OAuth2 works without using Google’s library’s I have a post that explains it all from the start. Google API and Oath2


Update: I have a new tutorial series that walks you through using the Google Drive API it starts with Google Drive API C#, I also have one for using Google Analytics it starts with Google Analytics API C# you will find them both more up to date then this tutorial.

Requirements

Create Google Apis app

  1. Create a app in Google Apis Console
  2. APIs & auth -> APIs -> Enable Drive API and Drive SDK.
  3. APIs & auth -> credentials.  Create a new Client Id -> installed Application type other.
  4. Take a note of your Client ID and Client secret
  5. APIs & auth -> consent screen Make sure to set an email address and a product name.

Visual studio project

Create project in Visual Studio, I recommend either a console application or a win forms application for testing.   Install the following NuGet package to your project.

pm> install-package google.apis.drive.v2

OAuth2

Oauth2RequestBrowser

In order to access most of Google’s APIs you need to be authenticated, we use OAuth to authentcate.     Open Authentication is a way for your users to allow your program to access there data for them.   One thing that I want to note is that you are giving OAuth2 access via your Google Account.   Your web browser needs to be logged into the Google Account that has access to the data you want to request.  (If you are releasing this application to the public its a good idea to put a note about this some place or you will get support calls.  Been there done that)

There are two ways of requesting user access with Open Authentication.   The first is to use something called FileDataStore this will store the RefreshToken on the PC in a file in the %AppData% directory.   The other way of doing it is to create your own implantation of iDataStore, this will enable you to save the RefreshToken to a database for example.

Code Using FileDatastore

 var credential = GoogleWebAuthorizationBroker.AuthorizeAsync(new ClientSecrets { ClientId = "YourClientId",
                                                                              ClientSecret = "YourClientSecret" },
                                                          new[] { DriveService.Scope.Drive,
                                                                  DriveService.Scope.DriveFile },
                                                          "user",
                                                          CancellationToken.None,
                                                          new FileDataStore("Drive.Auth.Store")).Result; 

ClientId and Secret

The first thing we are doing is sending Google the information about our application.    Google likes to keep track of our applications so that they know who is accessing there API and how much they have accessed it. This information was found in the Google Developer console when you registered your application.

Scopes

All of the API’s have different scopes depending on what information you would like to access.  The user will be told what information you need and they can decided at that time if they want to let you have access to the information or not.   If they don’t then they can’t use your program.

Tip: Make sure that you only request the permissions you actually need.

Users are becoming more protective of there data, they may not grant you application access and you will loose users.   Google Drive Scope Reference

FileDataStore

new FileDataStore(“Drive.Auth.Store”)

Once the user gives you access the information is saved on there machine so you wont have to ask them again.   You can find it here:

%AppData%\Roaming\Drive.Auth.Store\Google.Apis.Auth.OAuth2.Responses.TokenResponse-user

The file format is Json so you can open it in a text editor if you want.

{"access_token":"",
"token_type":"Bearer",
"expires_in":3600,
"refresh_token":"",
"Issued":""
}

Expires in is used by the system to calculate when the the access token has expired.

That’s it really that’s all you need to know to get OAuth2 working with any of the Google API’s just change the scope you need depending on the API you are accessing.     In my next tutorial we will look more into the Google Drive API, check back for Google Analytic s API tutorials coming soon .

User

If I send  ”Linda” using FileDataStore and authorize the access the next time I load the application I don’t have to authorize it again because the file exists in my %appdata% with my stored refresh Token.  If I change that to say LindaLawton the next time I load it will ask me to authorize it again.   This is how you keep different users authentication organized by there login.   If you have more then one user using the same machine they will then each have there own information stored in %appData%.

In this picture we can see that we are now authenticated and can go on to creating a Google drive service for accessing Google drive data. Drive Access tutorial
RefreshToken

Stored refresh-Token

In order to load a stored refresh token someplace other then %appdata% you need to create your own implementation of IDataStore. I have a few examples for you in the GitHub project Google-Dotnet-Samples Authentication.

 /// 
 /// Saved data store that implements . 
 /// This Saved data store stores a StoredResponse object.
 /// 
    class SavedDataStore : IDataStore
    {
        public StoredResponse _storedResponse { get; set; }
        /// 
        /// Constructs Load previously saved StoredResponse.
        /// 
        ///Stored response
        public SavedDataStore(StoredResponse pResponse)
        {
            this._storedResponse = pResponse;
        }
        public SavedDataStore()
        {
            this._storedResponse = new StoredResponse();
        }
        /// 
        /// Stores the given value. into storedResponse
        /// .
        /// 
        ///The type to store in the data store
        ///The key
        ///The value to store in the data store
        public Task StoreAsync(string key, T value)
        {
            var serialized = NewtonsoftJsonSerializer.Instance.Serialize(value);
            JObject jObject = JObject.Parse(serialized);
            // storing access token
            var test = jObject.SelectToken("access_token");
            if (test != null)
            {
                this._storedResponse.access_token = (string)test;
            }
            // storing token type
            test = jObject.SelectToken("token_type");
            if (test != null)
            {
                this._storedResponse.token_type = (string)test;
            }
            test = jObject.SelectToken("expires_in");
            if (test != null)
            {
                this._storedResponse.expires_in = (long?)test;
            }
            test = jObject.SelectToken("refresh_token");
            if (test != null)
            {
                this._storedResponse.refresh_token = (string)test;
            }
            test = jObject.SelectToken("Issued");
            if (test != null)
            {
                this._storedResponse.Issued = (string)test;
            }
            return TaskEx.Delay(0);
        }

        /// 
        /// Deletes StoredResponse.
        /// 
        ///The key to delete from the data store
        public Task DeleteAsync(string key)
        {
            this._storedResponse = new StoredResponse();
            return TaskEx.Delay(0);
        }

        /// 
        /// Returns the stored value for_storedResponse      
        ///The type to retrieve
        ///The key to retrieve from the data store
        /// The stored object
        public Task GetAsync(string key)
        {
            TaskCompletionSource tcs = new TaskCompletionSource();
            try
            {
                string JsonData = Newtonsoft.Json.JsonConvert.SerializeObject(this._storedResponse);
                tcs.SetResult(Google.Apis.Json.NewtonsoftJsonSerializer.Instance.Deserialize(JsonData));
            }
            catch (Exception ex)
            {
                tcs.SetException(ex);
            }
            return tcs.Task;
        }

        /// 
        /// Clears all values in the data store. 
        /// 
        public Task ClearAsync()
        {
            this._storedResponse = new StoredResponse();
            return TaskEx.Delay(0);
        }

        ///// Creates a unique stored key based on the key and the class type.
        /////The object key
        /////The type to store or retrieve
        //public static string GenerateStoredKey(string key, Type t)
        //{
        //    return string.Format("{0}-{1}", t.FullName, key);
        //}
    }

Then to use it you have something like this

  //Now we load our saved refreshToken.
  StoredResponse myStoredResponse = new StoredResponse(tbRefreshToken.Text);
 // Now we pass a SavedDatastore with our StoredResponse.

    credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
              new ClientSecrets { ClientId = "YourClientId", ClientSecret = "YourClientSecret" },
              new[] { DriveService.Scope.Drive,
                DriveService.Scope.DriveFile },
              "user",
              CancellationToken.None,
               new SavedDataStore(myStoredResponse)).Result; }

Sample Project

I have created a small sample project for this. Google .net Samples Drive

Conclusion

Accessing Google APIs using OAuth2 is a matter of Creating your application on Google Developer console, telling the user what access you need buy supplying the scope. The google .net client library comes with filedatastore by default. We can change where the authentication information is stored by creating or own version of idatastore. I have a sample project that shows you how to change fileDataStore for storing information in the database as well as storing the files in the current working directory. Google .net Samples Authentication


About Linda Lawton

My name is Linda Lawton I have more than 20 years experience working as an application developer and a database expert. I have also been working with Google APIs since 2012 and I have been contributing to the Google .Net client library since 2013. In 2013 I became a a Google Developer Experts for Google Analytics.

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

147 thoughts on “Google OAuth2 C#

  • Macy

    Thank you so much for this usfull post.
    But still I have some questions.

    1)What are GoogleWebAuthorizationBroker.Folder = “Tasks.Auth.Store” and FileDataStore(“Drive.Auth.Store”) for?
    2)Should I use them even if I want to store my refresh_token, client_Id and secret_Id in database and create credentials based on these stored data?
    3)what is “user” ? is it an identical string like client_id ?
    Thanks .

    • Daimto

      GoogleWebAuthorizationBroker.Folder = “Tasks.Auth.Store”
      I just did some testing and you don’t need it. I must have copied it from the sample code some place. Nice spotting.
      FileDataStore(“Drive.Auth.Store”) Is used when you want to save the refreshtoken %appData% directory on the pc. Not if you want to load stored data. If you want to load your refreshtoken from the database you will need to make your own implementation of Idatastore. Check under the “Stored refresh-Token” section of the tutorial and download the sample project it will be easier to understand how it works.

      “User” mind you i’m not sure about this. But i think the user string is used to keep track of who is logging in. If you are storing the refreshtoken in %appData% but have more then one user using the PC you need to be able to store more then one refreshtoken and know who is who. I haven’t tested this. When working with a refresh token that’s stored in the database it doesn’t matter what you put in the string it can be just “” if you want. It doesn’t appear to be used.

      What I hope they are doing with “User” is something else. I haven’t had time to dig around in the source code for the dll to be sure, because if there not i will probably send in a feature request. My hope is that they are using this to fill in “QuotaUser”. I haven’t found any where else that you can add quota user to the requests.

      https://developers.google.com/analytics/devguides/reporting/core/v3/reference#quotaUser

  • Josef Kos

    Your method GenerateClientSecretsStream, can be easily replaced by the native functionality

    var secrets = new ClientSecrets { ClientId = “put id here”, ClientSecret = “put secret here” };

    Josef

  • Frederic

    Hello,
    I would like to use your tutorial but I have a question : where are you giving the redirect URL to google in the “GoogleWebAuthorizationBroker.AuthorizeAsync()” ?
    Because when I run the code I have a “redirect_uri_mismatch” error.

    Thanks

    Fred

  • rasmus christensen

    Hello,

    First of all great blog post. When the token should be renew do you do anything special? I can see that the get method is being called, but how is the actual Refresh and followed store of the new access token happen?

    • Daimto

      The library handels that for you. Make a few requests to the api then wait a hour. Run it through debug you can see it will get you a new authToken if it needs one.

  • Jimmy

    Hi

    Thanks for your tutorials, they are great.
    I have a question. I have created a webpage in .net that sends the user to
    https://accounts.google.com/o/oauth2/auth?client_id=xxxxxx.apps.googleusercontent.com&redirect_uri={url}&scope=https://www.googleapis.com/auth/youtube.upload&response_type=code&access_type=offline

    When the user acceps, I retrieve the token and refresh token and saves it in a database. I also have the refresh token rutine in place. This is all good 🙂

    I now want to write a .net program that is running on the server. The program will upload video’es to youtube to the users youtube channel, using the saved tokens.

    The piece of code in my .net program that I need to change looks something like this

    credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
    new ClientSecrets { ClientId = “xxxx”, ClientSecret = “xxx” },
    new[] { YouTubeService.Scope.YoutubeUpload },
    “user”,
    CancellationToken.None
    );

    My question is, how to use the already requested token in the above code.

    Hope you understand my question, and can give me a hint.
    Thanks, and thanks for at great Blog 🙂

    Best regards.

  • Hubert Nguyen

    Great post Linda, I spent a few hours looking at different ways to connect to Google Services, and your article and code sample are impeccable — it was actually the only one I found that just worked out of the box.

  • Alberto Begliardo

    Great post Linda, many many thanks!
    I’m using these for calendar and task. On consolle application it works well. Now i’m doing it on a windows service and, with the same code working on consolle, i receive an error on calendar part

    Insufficient Permission [403]
    Errors [
    Message[Insufficient Permission] Location[ – ] Reason[insufficientPermissions] Domain[global]
    ]
    System.Collections.ListDictionaryInternal Google.Apis

    Tasks part work as well as on consolle app. The code is simple:

    f (!File.Exists(AppDomain.CurrentDomain.BaseDirectory.ToString() + “client_reauth_token.txt”)) throw new Exception(“client_reauth_token.txt Not found”, new FileNotFoundException());
    string reauthToken = File.ReadAllText(AppDomain.CurrentDomain.BaseDirectory.ToString() + “client_reauth_token.txt”);

    StoredResponse myStoredResponse = new StoredResponse(reauthToken);

    UserCredential credentialTEST;
    using (var stream = new FileStream(“client_secrets.json”, FileMode.Open, FileAccess.Read))
    {
    credentialTEST = GoogleWebAuthorizationBroker.AuthorizeAsync(
    GoogleClientSecrets.Load(stream).Secrets,
    new[] { TasksService.Scope.Tasks, CalendarService.Scope.Calendar },
    “user”, CancellationToken.None, new SavedDataStore(myStoredResponse)).Result;
    }

    var serviceGTLTest = new TasksService(new BaseClientService.Initializer()
    {
    HttpClientInitializer = credentialTEST,
    ApplicationName = “BagubitsCalendarSyncronizer”,
    });

    TaskLists resultsTLtest = serviceGTLTest.Tasklists.List().Execute();
    Console.WriteLine(“\tTaskList:”);

    foreach (TaskList task in resultsTLtest.Items)
    {
    string title = Regex.Replace(task.Title.ToString(), @”\t|\n|\r”, “”);
    Console.WriteLine(“\t\t” + title);
    }

    var serviceGCtest = new CalendarService(new BaseClientService.Initializer()
    {
    HttpClientInitializer = credentialTEST,
    ApplicationName = “BagubitsCalendarSyncronizer”,
    });

    CalendarList resultsCLtest = serviceGCtest.CalendarList.List().Execute();
    Console.WriteLine(“\tCalendars:”);

    foreach (CalendarListEntry calendar in resultsCLtest.Items)
    {
    string title = Regex.Replace(calendar.Summary.ToString(), @”\t|\n|\r”, “”);
    Console.WriteLine(“\t\t” + title);
    }

    why on windows service calendar part is throw an

    Error: Google.Apis.Requests.RequestError
    Insufficient Permission [403]
    Errors [
    Message[Insufficient Permission] Location[ – ] Reason[insufficientPermissions] Domain[global]
    ]
    System.Collections.ListDictionaryInternal Google.Apis

    Many thanks, my best regards

    Alberto

  • Himadri

    Hi,
    Thanks for this very helpful article. I could use Youtube APIs with the help of your article from a console program. I want to use Youtube APIs from web application. I created a client Id in google console for the web application but, I am always getting Error: redirect_uri_mismatch. I did set a redirect uri in the client id but, the application sending different redirect_uri. I appreciate your help.

    • Linda Lawton

      I haven’t tried working with C# and a web application yet. But i know from PHP that the Redirect URI needs to be the path to the file on the server that handles the Authentication flow. For a windows application its just localhost, you need to change that. I will see if i can find some more information for you.

  • Glenn

    I’ve found these guides to be very useful, so thank you very much!

    I need to use iDataStore as I’m connecting to many user’s Google calendars from a VB.NET web application. I have converted the SaveDataStore.cs class to .vb but I’m encountering an error which I cannot understand.

    I’ve posted this query on StackOverflow with my code and the specific error message. I’ve spent hours trying to implement this but I’m just hitting a brick wall, I hope you don’t mind taking a quick look and offering any guidance.

    http://stackoverflow.com/questions/24224645/authorizing-google-api-using-stored-refresh-token-and-idatastore-in-vb-net

    Sorry to bother you with this.

    • Linda Lawton

      Depends upon how your application is written. But deleting the Access token will remove access until you use the refresh-token to get a new one. Deleting the Refresh-token will require them to authenticate you again which really isn’t what you want to do.

      • Armugam

        Hi Linda,

        My Question is token automatically expires in 3600,before that how can i logout from google account from my Windows application ?

        Yes only through Token we have an access to Drive,but when i tried to make the “Authentication.credentials.Token =null; “==>throwing null reference bcz the “Authentication.credentials” itself is null when i tried for the above.

        When i was worked on Onedrive,below code helped me to logged out from my account.
        OneDriveMethods odm = new OneDriveMethods();

        if (Session.instance != null)
        {
        this.signOutWebBrowser.Navigate(odm.AuthClient.GetLogoutUrl());
        odm.AuthClient = null;
        }
        Where AuthClient contains session here.

        But when i have tried the same in GDrive,i haven’t find LogoutUrl property anywhere….

        Can you plz… help me in logging out from my Google account through my windows application using c#

          • Armugam

            Thank You Soo.. Much for your quick Reply 🙂

            Is it possible with our own Web browser in Windows Application using C#??

            If so, plz provide help me…

          • Linda Lawton

            If you mean open the authentication window in a web-browser control. No the client library doesn’t support that, but it was something i was looking into. Its a good idea.

  • JR

    Linda,

    I am having a difficult time with similarities to this. For one, I do not understand why Google requires the user to log in using their username and password to be able to get the authorization code instead of the application providing the client id and client secret. I am using the Google Analytics Embed API to generate google charts with data from each client’s account in my admin dashboard for each of my client’s ASP.NET MVC websites. However, since I am not behind a secure connection (https), I cannot use their authorize component to receive the tokens. Therefore, I have installed the nuget packages for the OAuth2 and Google OAuth2 Api’s (v3) in my project. But consider this setting: A client has an existing google account that they want to use for the google analytics api access in their site, in case they ever want to look into google analytics itself further, etc. I can get their client id and client secret from them, but since the application is not behind a secure ssl connection, how do I get the authorization code with this info to then request the refresh tokens without having them log in through C# code in my controller, etc.? I have already setup the proper domains in the javascript origins in the Developer Console and have the following code, but I believe my scope may be off or I am not providing something or using the CreateAuthorizationCodeRequest method wrong? I am simply trying to get back the authorization code through C# instead of the login postback that you have done such as with the google plus sign in button.
    My Code:

    public ActionResult Index()
    {
    IAuthorizationCodeFlow flow =
    new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
    {
    ClientSecrets = new ClientSecrets
    {
    ClientId = “GoogleAnalyticClientId,
    ClientSecret = “GoogleAnalyticClientSecret”
    },
    Scopes = new string[] { “https://www.googleapis.com/auth/analytics” }

    });
    flow.CreateAuthorizationCodeRequest(“http://www.mydomain.com/Admin/Dashboard”);
    return View();
    }

    When I deploy and navigate to page, I get back an error in the dev console of chrome that has an Object with errors ‘message: “immediate_failed”‘ and ‘reason: “invalidParameter”‘.

    So, I know I am missing something initially in that but also wondering if CreateAuthorizationCodeRequest will work for this reason?

    Thank you for your time.

    • Linda Lawton

      Google Requires that you login with your Google account which is hooked to your Google Analytics account. you cant give access to your Google Analytics account with out being logged into it first. If the user is already loged into there Google account they should not be prompted to login to Google before authenticating your application.

  • BS

    hi linda
    your tutorial is the first very useful for work with google api,
    i have a simple question, i have a installed application i created key and im able autenticate.
    i dont understand how i can manage the redirect after that client’s accept.
    It’s start always a browser page with localhost and obviously with error message,
    in the project i cant specify anything for the installed application.
    can you help me?
    thanx

      • BS

        yes in my console i have my id client id for native application with
        urn:ietf:wg:oauth:2.0:oob
        http://localhost
        but i dont know how can i choose the first using the api c#
        and i cannot (or i dont know) modify the console

        this is the code that i use
        UserCredential credential=GoogleWebAuthorizationBroker.AuthorizeAsync
        (
        new ClientSecrets()
        {
        ClientId = “XXXXXXX”,
        ClientSecret= “XXXXXX”
        },
        new[] { CalendarService.Scope.Calendar },
        “xxxxxxx@gmail.com”,
        CancellationToken.None,new FileDataStore(“Calendar.Auth.Store”)).Result;

        // Create the service.
        var service = new CalendarService(new BaseClientService.Initializer()
        {
        HttpClientInitializer = credential,
        ApplicationName = “Calendar API Sample”,
        });

        thanx

  • Hugo Hilário

    Hi Linda,

    I’ve implemented the IDataStore interface to store the user credentials inside my database using Entity Framework. Everything works fine except on the first redirect, I get the following error on the GetAsync:

    Google.Apis.Auth.OAuth2.Responses.TokenResponseException: Error:”State is invalid”, Description:””, Uri:””

    Any ideas?

    Thanks a lot

  • omairalam

    I m creating Client ID for native application in google developer console and update
    client_secrets.json and run sample project and get this error in browser

    401. That’s an error.

    Error: invalid_client

    no support email

    Request Details
    scope=https://www.googleapis.com/auth/drive https://www.googleapis.com/auth/drive.file
    response_type=code
    redirect_uri=http://localhost:64061/authorize/
    access_type=offline
    client_id=685022597165-c71lrnhb7vj7nap1drdk29q82o88pfqp.apps.googleusercontent.com

    That’s all we know

  • nullreference

    This works a treat and saved me a lot of time, many thanks for the post. One thing I did run into was the serialisation and de-serialisation in the saveddatastore. It worked fine locally, but when we deployed to another country it would throw an exception. We needed to add a date format setting as per below.

    JsonSerializerSettings microsoftDateFormatSettings = new JsonSerializerSettings
    {
    DateFormatHandling = DateFormatHandling.MicrosoftDateFormat
    };
    string JsonData = Newtonsoft.Json.JsonConvert.SerializeObject(this._storedResponse, microsoftDateFormatSettings);

  • Rabeeh

    Hi Linda,

    Thanks for saving a lot of time.. Your example is very informative and explained in a very simple manner.

    The only problem i am having is the authentication is opening a new tab in browsers like chrome and firefox. Also if there is mulitple browsers, no matter where we open the page, the authentication page is opened in the default browser.

    Is there any way to avoid authentication to open new tab and open in the same page????

    Please help!!! Thanks in Advance!!!

  • Yaugen

    Hi Linda!

    Thanks a lot for the post and for the sample project!
    One thing I still to want to ask you… I have found out that the code is only works when the project platform target set to “x86”. Is this requirement a “must” or there are tricks that can make the project working under “AnyCPU” platform target?

    Thanks a lot!!!

    • Yaugen

      Has to correct my question. My assumption about “target platform” was wrong. I just missed that I worked with a saved response file. But what is still an issue I cannot get the app working on “.NET Framework 4.5”. after calling GoogleWebAuthorizationBroker.AuthorizeAsync i see the log in window, enter there my credentials, pressing Accept. then I am getting the following error message:
      The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel.

      Could you please tell me. What can be wrong?

      • Yaugen

        Sorry Linda 🙂
        Everething works,
        the reason of my troubles was System.Net.ServicePointManager.ServerCertificateValidationCallback wich was defined somewhere in the guts of the code.
        You can delete entire thread I had opened. Its not an issue 🙂
        Sorry for disturb and thanks again!

  • DucKham

    Hi Linda, Thanks so much about tutorial.
    I’m problem with authentication, when you run debug, it is ok, but when run localhost it is error “400……”.
    Please help ! Thanks so much.

  • Virgo Sun

    Hello Linda,

    Thank you for your expertize code. It works like a charm. Actually I am trying to integrate OAuth2 to my PC and Android inter-communication app. Your code is the only native working that I’ve googled so far. For any next stuff, please make it reusable by class or some other kind.

    Thanks

  • Keir

    Thanks Linda for this – I’ve struggled with complexity of the Google provided examples (particularly pushing me towards DrEdit) and this seems a lot more straight forward. Looking forward to adapting this to my project later today.

  • Justin

    Hi, I implemented your code, however I am getting the following error.

    The redirect URI in the request: http://localhost:55421/authorize/ did not match a registered redirect URI.

    Learn more

    Request Details
    scope=https://www.googleapis.com/auth/calendar https://www.googleapis.com/auth/calendar.readonly
    response_type=code
    redirect_uri=http://localhost:55421/authorize/
    access_type=offline
    client_id=106111114487443-h9sdfsdfpakm5rnrjlosdldsj1el1fjecsu8at.apps.googleusercontent.com

    This is a web application I am working on. However the default redirect uri, has a different port number from the actual deployment.

    How do I go about customizing my redirect_uri for my Request?

  • Josh

    Hi Linda,

    This is a great post and is the most detailed I have found online regarding Google API’s and .NET. I am fairly new to oAuth 2.0, and I have a working implementation that is very similar to what you have in this post.

    My question is that I have a web app with a scheduled job that connects out to Google Calendar once per day to post events. The first time the job runs it has me log in to the connected Google Account to authenticate and allow access. Will this be the one and only time this happens? I want to make sure that the job will run continuously without issues.

    Thank you for your time.

    • Linda Lawton

      It depends on how you do it really. if you are using filedatastore then the authentication is stored in %appData% directory under the name you gave it, with the user also that you gave it. As long as you have your scheduled job to use this same file then it wont ask you again.

  • J Soares

    I have been tasked with implementing Google integration with our existing Application. None of our developers were familiar with OAuth2 so I needed to learn all there was to know about it. Our application is a Windows desktop app but we also have a web implementation, and our clientele are in the financial and insurance industries so we have to comply with very specific security requirements.

    That said, I just wanted to let you know that your blog has been instrumental in my understanding and implementing of OAuth2 and the Google API’s.

    Thank you so Much!

  • Guilherme

    Hi Linda, it’s a great post…
    But I am having a problem in the request “GoogleWebAuthorizationBroker.AuthorizeAsync” that does not open the Google login windows.
    Anyone had this problem?

    ps.: works correctly in IIS Express.

    []’s Guilherme

      • Guilherme

        string[] scopes = new string[] { Google.Apis.Oauth2.v2.Oauth2Service.Scope.UserinfoEmail,
        Google.Apis.Oauth2.v2.Oauth2Service.Scope.UserinfoProfile,
        Google.Apis.Oauth2.v2.Oauth2Service.Scope.PlusLogin, // Get basic User Info
        AnalyticsService.Scope.Analytics, // view and manage your analytics data
        AnalyticsService.Scope.AnalyticsEdit, // edit management actives
        AnalyticsService.Scope.AnalyticsManageUsers, // manage users
        AnalyticsService.Scope.AnalyticsReadonly }; // View analytics data

        string clientId = “XXXXXXXXXXXX”;
        string clientSecret = “ZZZZZZZZZZZZZ”;

        MemoryDataStore mData = new MemoryDataStore();
        UserCredential credential;

        using (var cts = new CancellationTokenSource())
        {
        cts.CancelAfter(TimeSpan.FromSeconds(30));
        credential = GoogleWebAuthorizationBroker.AuthorizeAsync(new ClientSecrets { ClientId = clientId, ClientSecret = clientSecret }
        , scopes
        , “F2F”
        , cts.Token
        , mData).Result;
        // ==> dont open a popup logon… and I get timeout!
        }

        return credential.Token.RefreshToken;

  • sandeep

    Hi ,
    Thanks for your great Posts .
    I have created the DatabaseDataStore which implements the IDataStore.
    In my application i have a register User page where i ask user to click the register button and he gets redirected to the Google authentication.
    this is the code for that.

    var result = await new AuthorizationCodeMvcApp(this, new AppFlowMetadata()). AuthorizeAsync(cancellationToken);

    and this is AppFlowMetadata :

    private static readonly IAuthorizationCodeFlow flow =
    new CustomAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
    {
    ClientSecrets = new ClientSecrets
    {
    ClientId = “my client id”,
    ClientSecret = “my client secrets”
    },
    Scopes = new[] { DriveService.Scope.Drive },
    DataStore = new DatabaseDataStore(“LocalDB”,””,””,””,””)
    //DataStore = new FileDataStore(“Drive.Api.Auth.Store”)
    });

    I pass null empty values to the idatastore as I have hardcoded the connection string .
    And after successful Oauth Flow I can see the data being saved in my database in GoogleUser Table.
    The google usertable saves this information automatically in refreshtoken field along with user id.
    {“access_token”:”xxxxxx”,”token_type”:”Bearer”,”expires_in”:3600,”refresh_token”:”xxxxxx”,”Issued”:”2015-04-08T18:17:01.233+05:30″}

    Now next step is that I want to user to go to another page in application where he will select the file to upload.
    And then he should be able to upload the file as he has already authenticated.
    Here I did something like this.

    private static readonly IAuthorizationCodeFlow flow =
    new CustomAuthorizationCodeFlowSaved(new GoogleAuthorizationCodeFlow.Initializer
    {
    ClientSecrets = new ClientSecrets
    {
    ClientId = “My client id”,
    ClientSecret = “my client Secret”
    },
    Scopes = new[] { DriveService.Scope.Drive },
    DataStore = new DatabaseDataStore(getStream())
    // DataStore = new SavedDataStore()

    } );
    private static StoredResponse getStream()
    {
    DataAccess objDA = new DataAccess();
    DataRow dr = objDA.getSingleDataRow(“Select RefreshToken from dbo.GoogleUser where userid=1 and username=’user Name'”);
    StoredResponse myStoredResponse = new StoredResponse(dr[“RefreshToken”].ToString());
    return myStoredResponse;
    }

    I have creted the class for StoredResponse as below. ( I just refered the article for savedDatastore.)
    public class StoredResponse
    {
    public string access_token { get; set; }
    public string token_type { get; set; }
    public long? expires_in { get; set; }
    public string refresh_token { get; set; }
    public string Issued { get; set; }

    public StoredResponse(string pRefreshToken)
    {
    //this.access_token = pAccessToken;
    // this.token_type = pTokenType;
    //this.expires_in = pExpiresIn;
    this.refresh_token = pRefreshToken;
    //this.Issued = pIssued;
    this.Issued = DateTime.MinValue.ToString();
    }
    public StoredResponse()
    {
    this.Issued = DateTime.MinValue.ToString();
    }

    }

    This doesnot prompt for login , but credentials are null.
    Can you please help me with this ?
    Thanks,
    Sandeep

  • Seena

    Hi Landa, Thanks for your great post , I am currently working on mobile app(windows phone 8.1) , But I am having a problem in the request “ GoogleWebAuthorizationBroker.AuthorizeAsync” i am getting error “An exception of type ‘System.IO.FileNotFoundException’ occurred in mscorlib.ni.dll but was not handled in user code” For last three day i am facing this issue kindly help me.

    • Seena

      await GoogleWebAuthorizationBroker.AuthorizeAsync(
      new Uri(“ms-appx:///Assets/client_secrets.json”),
      new[] { DriveService.Scope.DriveReadonly },
      “user”,
      CancellationToken.None);

      • srnux

        Did you try to set Uri like this:
        new Uri(@”ms-appdata:///local/client_secret.json”)
        Add client_secret.json to root folder or change path in uri
        Change deployment setting on the file – Copy to output directory : Copy always
        I’m working on Windows 10 Universal Application for Devices and this worked for me.

  • nill

    I need to open my google drive excel file in c# window form for database use plz help me to access path of google drive path for every palteform and every user of my application

  • Plopes

    Hi, thanks for your post, it’s simple and concise, but I think it’s a bit incomplete, because it’s missing the refresh token. I bet it works great as-is with Drive service, but I use Google Cloud Print service, which I’ve not found any C# API, and so I had to implement the refresh token myself.

    The GoogleWebAuthorizationBroker.AuthorizeAsync method does not refresh the token, I think that the refresh is made in the DriveService, that’s why it didn’t work for me (since I don’t use this service).

    • Linda Lawton

      The client library handles the refresh token for you. Filedatastore stores the Refresh-token in the %appdata% directory. The client library detects on its own when it needs a new access token and uses the refresh-token to get a new one for you.

      • Richard Mikesell

        Are you saying that the javascript library will retrieve the refresh_token? I had read that the client code does not provide this for security reasons; or is there some way to actually fetch the refresh_token during a client/javascript login? If so, would you mind expanding on this a bit and direct me to any code samples you may have? Thank you. Your website is extremely helpful.

        • Linda Lawton Post author

          Actually the JavaScript library to my knowledge will not return a refresh token. The Google Library are all written with best practice in mind. Using a Refresh token in JavaScript would be bad for security reasons, because of this i think Google Decided that the Java script library should just not return a Refresh Token. So to answer the first part of your question its probably not possible to get a refresh token back from the JavaScript client library with out hacking the library code.

          Now if you consider the authentication server, which has no way of knowing what language you are sending the request in. You can authenticate using any language that is capable of sending HTTP posts and HTTP gets. So technically speaking you should be able to do it all manually in JavaScript get a refresh-token back. Then refresh the refresh-token and using the access token in the JavaScript client library. I have never tried it but in theory it should be possible. Assuming you want to give this a try Google 3 legged Oauth2 flow might get you started.

  • Gopi

    I am fetching the uplaoded video from youtube from my channel in C#. but while fetching i am facing the below issue.

    Google.Apis.Requests.RequestError
    Insufficient Permission [403]
    Errors [
    Message[Insufficient Permission] Location[ – ] Reason[insufficientPermissions] Domain[global]
    ]

    Kindly help if any configuration is required or not.

    I am able to upload videos sucessfully.

      • Gopi

        i am using below command for that.

        var channelsListRequest = youtubeService.Channels.List(“contentDetails”);
        channelsListRequest.Mine = true;

        // Retrieve the contentDetails part of the channel resource for the authenticated user’s channel.
        var channelsListResponse = channelsListRequest.Execute();

        For credential i am using below command.

        ClientSecrets secrets = new ClientSecrets()
        {
        ClientId = “xxxxx.apps.googleusercontent.com”,
        ClientSecret = “xxxxxxxxxx”
        };

        var token = new TokenResponse { RefreshToken = “xxxxxxxxx” };
        credentials = new UserCredential(new GoogleAuthorizationCodeFlow(
        new GoogleAuthorizationCodeFlow.Initializer
        {
        ClientSecrets = secrets,
        DataStore = new FileDataStore(Assembly.GetExecutingAssembly().GetName().Name),
        Scopes = new[] { YouTubeService.Scope.YoutubeReadonly }

        }),
        “xxxxxx@gmail.com”,
        token);

  • prasad

    Everything working great locally. But on the server (windows 2008; IIS), the conscent screen is not coming up and webpage hangs, any idea what the problem could be?

    Thanks a lot in advance,
    Prasad.

  • Paul

    Hi Linda,
    just wanted to thank you for a great documentation and an awesome sample project. I would like to add few words about Impersonation (Delegated Authorization), because there is not much ressources about this topic on the internet.
    If you are implementing an enterprise solution with more than 1 user, it is almost the main task, to upload ressources by an application as an particular user. For this reason you have to use the service account. Just add one for your project, generate a p12 file and add release the service account ID (these without the “at” inside) to be used for impersonation (https://www.youtube.com/watch?v=iK14bfd6qhs)
    To use impersonation in your code, you’ll need only to add “User={yourUserEmailAddressToImpersonateTo}” in instantiation of ServiceAccountCredential claim, after scopes in “ServiceAccount”- version of Authenticaton Method.

    Have a nice Time,
    Paul

  • Fereidoon

    Hi Dear Friend
    In this case(OAuth2 ), we have e-mail and password for approved users get access, is it really reasonable?
    Is there a way to authenticate without providing a password and email?
    thanks

  • Samy

    Hi Linda,

    Is there a way to upload files in the same drive for all the users ? I want to use this method for an app for a company. This app will allow to multiple employee to upload files in the drive of the company.

    Thanks,
    Samy

    • Linda Lawton Post author

      Yes you can create a service account. Service accounts have there own drive account. You can also grant the service account access to the company’s google drive account by adding it as a user on a folder on Google drive. I should turn this into a blog post its a very good idea and a question I see often.

        • Linda Lawton Post author

          I have read that it is possible. JavaScript is client side which means that anyone that views the source in there web browser will have enough information to access your account. I would not personally try it due to security reasons. Service accounts in JavaScript in my opinion would be a security nightmare and open you up to all kinds of risk.

          At the very least they could damage your data. They could also spam Googles servers so much that Google not only shuts down your project but your whole Google developers console account.

          Thank you for your comment its a very good question.

  • Mikhail

    How i can use proxy server, when trying to get access token.

    var credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
    cred,
    Scopes,
    “user”,
    CancellationToken.None).Result;

    Now i getting http error 407.

    in System.Net.HttpWebRequest.EndGetRequestStream(IAsyncResult asyncResult, ref TransportContext context)
    in System.Net.Http.HttpClientHandler.GetRequestStreamCallback(IAsyncResult ar)

  • Pawan

    thanks for this post .
    i am trying to implement it. presently i am working on localhost right now.
    i am getting new redirect every time during autho process which causes uri mismatch.
    can u tell me google pick the the redirect url or any function or class send it in autho url.
    or this is visual stdio problem

    • Linda Lawton Post author

      Its a pain i know. Create a native client instead one for installed applications. Test on localhost with that. Just don’t release a web application with that client id. I will see about writing a walk though on how to set up visual studio for web applications.

    • Linda Lawton Post author

      I dont think so. The Google Cloud print API isn’t a discovery API there for its not supported by the current version of the .net client library. You could probably use the code to get an access token but sending the requests you are going to have to code manually.

      I have looked at the cloud print API but i dont have access to a printer capable of it so have been unable to test it.

  • henry tehrani

    I followed your great code and works great on my local machine but fails on the farmed staging server. It throws access denied exemption and fails to start the browser to ask user for their permission. Do you have an example of an actual staged code?
    thanks

  • shibasis sengupta

    Thanks for the awesome post.
    One problem though. The use of “GoogleWebAuthorizationBroker.AuthorizeAsync” does not work in IIS. It bcasically never takes the user to the google authorization page if you are running this on IIS.

  • Robel selemun

    hey i have an c# asp.net mvc application one of its function is to find a specific file in my google drive. The method looks like this:

    public bool checkOrderconformation(Project project)
    {
    DriveService service = GetDriveService();
    List result = new List();
    FilesResource.ListRequest request = service.Files.List();

    do
    {
    try
    {
    //all customers folder
    request.PageSize = 1000;
    request.Q = string.Format(“‘{0}’ in parents”, “0ByfIxiGuAxCEWVljS29NX2xoN1E”);
    FileList files = request.Execute();
    result.AddRange(files.Files);

    //customer folder
    File customer = result.Where(f => f.Name == project.clientName).FirstOrDefault();
    request.Q = string.Format(“‘{0}’ in parents”, customer.Id);
    FileList customerFolder = request.Execute();
    result = customerFolder.Files.ToList();

    //produktions folder
    File produktion = result.Where(f => f.Name == “Produktion”).FirstOrDefault();
    request.Q = string.Format(“‘{0}’ in parents”, produktion.Id);
    FileList produktionsFolder = request.Execute();
    result = produktionsFolder.Files.ToList();

    //ongoing Projects folder
    File pagaendeProjekt = result.Where(f => f.Name == “Pågående projekt”).FirstOrDefault();
    request.Q = string.Format(“‘{0}’ in parents”, pagaendeProjekt.Id);
    FileList pagaendeProjektFolder = request.Execute();
    result = pagaendeProjektFolder.Files.ToList();

    //Project folder
    File Project = result.Where(f => f.Name.Contains(project.name)).FirstOrDefault();
    request.Q = string.Format(“‘{0}’ in parents”, Project.Id);
    FileList ProjektFolder = request.Execute();
    result = ProjektFolder.Files.ToList();

    //Admin folder
    File admin = result.Where(f => f.Name == “Admin”).FirstOrDefault();
    request.Q = string.Format(“‘{0}’ in parents”, admin.Id);
    FileList AdminFolder = request.Execute();
    result = AdminFolder.Files.ToList();

    project.order_url = string.Format(“http://www.drive.google.com/drive/folders/{0}”, admin.Id);
    foreach (var item in result)
    {
    if (item.Name.Contains(“orderconformation”))
    {

    return true;
    }
    }

    request.PageToken = files.NextPageToken;

    }
    catch (Exception e)
    {
    Console.WriteLine(“An error occurred: ” + e.Message);
    request.PageToken = null;
    }
    } while (!String.IsNullOrEmpty(request.PageToken));

    return false;

    }

    Now this method works fine in my localhost but when i publish it to my azure site i get this error in the link(‘https://gyazo.com/fc54647baf864fbe52f16dff8373df93’)

    because it works in localhost i dont think there is any problem with the code itself so i have no clue why it wont work do you have any ide?

    • Linda Lawton Post author

      Azure is a pain, i suspect its an issue with the credentials that’s what it normally is. How exactly are you authenticating can you post your question on Stackoverflow? This forum isnt very good for large chunks of code.

    • Ilan

      Did you got any answer? I have the same issue. It works ok in localhost, but when I try it on Azure y get an exception on GoogleWebAuthorizeBroker.AuthorizeAsync. Have you published on stackoverflow?

  • Palash Hazra

    I have got error v var credential = GoogleWebAuthorizationBroker.AuthorizeAsync(new ClientSecrets
    {
    ClientId = “YourClientId”,
    ClientSecret = “YourClientSecret”
    },
    new[] { DriveService.Scope.Drive,
    DriveService.Scope.DriveFile },
    “user”,
    CancellationToken.None,
    new FileDataStore(“Drive.Auth.Store”)).Result;
    }
    here mention GoogleWebAuthorizationBroker does not exists in the current context please give me sugges
    tion

  • Vids

    Hello Mam,

    I use the code below :

    UserCredential credential;

    string credPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
    credPath = Path.Combine(credPath, “.credentials/gdrivecreds.json”);

    credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
    new ClientSecrets
    {
    ClientId = “dcs2en66p.apps.googleusercontent.com”,
    ClientSecret = “hfhfh”
    },
    Scopes,
    “user”,
    CancellationToken.None,
    new FileDataStore(credPath, true)).Result;

    This works absolutely fine on local system. but throws below error on server :

    at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
    ↵ at System.IO.Directory.InternalCreateDirectory(String fullPath, String path, Object dirSecurityObj, Boolean checkHost)
    ↵ at System.IO.Directory.InternalCreateDirectoryHelper(String path, Boolean checkHost)
    ↵ at System.IO.Directory.CreateDirectory(String path)
    ↵ at Google.Apis.Util.Store.FileDataStore..ctor(String folder, Boolean fullPath)

    Please help asap.

    • Linda Lawton Post author

      This is part of my new Samples project its generated so if you find any errors please let me know. Also remember service accounts only work with admin directory accounts not with normal Gmail user accounts Good luck GmailSample please let me know if you find any bugs

  • Gokulnath.VM

    credential does not work after deployed on the remote server

    Issue :
    Failed to launch browser with “https://accounts.google.com/o/oauth2/v2/auth?access_type=offline&response_type=code&client_id=479062508736-q6s2shhe6n65r8dtr97r5fegc7fh7hne.apps.googleusercontent.com&redirect_uri=http%3A%2F%2Flocalhost%3A63391%2Fauthorize%2F&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive.file” for authorization. See inner exception for details. —> System.ComponentModel.Win32Exception: Access is denied