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
- Create a app in Google Apis Console
- APIs & auth -> APIs -> Enable Drive API and Drive SDK.
- APIs & auth -> credentials. Create a new Client Id -> installed Application type other.
- Take a note of your Client ID and Client secret
- 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
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
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
Thank you for the effor. I wonder, how to feed it the stored refresh token (for Installed Application, youtube), too…
I was able to figure this out. I just haven’t had time to add it to the post. You need to create your own implementation of IDataStore.
Can you Please add the code block feed the stored refresh token in your example.
What is the implementation or contents of the StoredResponse object. I see the encapsulated property, but what resides inside the field?
StoredResponse myStoredResponse = new StoredResponse(tbRefreshToken.Text);
sends the RefreshToken
I just uploaded a sample project shows how to do both methods.
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 .
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
I have done some testing with regard to “User” you can now find those comments in the Tutorial.
Your method GenerateClientSecretsStream, can be easily replaced by the native functionality
var secrets = new ClientSecrets { ClientId = “put id here”, ClientSecret = “put secret here” };
Josef
I tested this and it works perfectly. I’m in the process of updating all of the tutorials and sample projects to reflect this, and a few others have commented on. Thank you for the tip.
Tutorial updated to make use of this change
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
Ok,
I have added a new client_id but for “Installed application” instead of “Web application” and it works.
However, we must have consent screen every time we connect ?
Thanks
Fred
That is set up in the google apis console for your app.
No the concent screen should only show the first time. Are you using filedata store or your own?
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?
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.
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.
You need to create your own implementation of IdataStore check under Stored refresh-Token in the tutorial.
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.
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
are you using the same user as you where before? It sounds like an issue with your reauthToken to me.
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.
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.
Very good, a kiss for you.
lol thanks, I am glad i could be of help. 🙂
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.
Hello my friend! I wish to say that this article
is amazing, great written and come with approximately all
vital infos. I would like to peer more posts like this .
How to log out user from my application?
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.
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#
One does not actually log out of Google, Once you have access you always have access until the user revokes your access or you delete the refresh-token.
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…
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.
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.
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.
Error: redirect_uri_mismatch.The redirect URI in the request: http://localhost:3078/authorize/ did not match a registered redirect URI. Why.I’ve followed your instructions
Probably because you made a Client ID for web application instead of a Client ID for native application If you intend this to be a web application then you need to have redirect URI set to the file that Google Should return authentication to. It must be a file and not just the directory.
Thank you so much
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
In the Google Developer console if you created a Client ID for native application this should set the redirect URI to “urn:ietf:wg:oauth:2.0:oob” which will enable it to redirect.
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
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
Post your code on stackoverflow i will have a look at it.
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
Go to APIs & Auths -> Consent screen
Make sure you have added an email address and a Product name.
It works only for one user but my scenerio is I have userid and passwords of users email account and I want to make events for them.How to switch users?Previously I am using Gdata(google version 1).Now I m updating to version 3.
FileDataStore(“Drive.Auth.Store”) stores the information in the %AppData% directory the naming is based upon “user”. Which API are you trying to access I may have a newer tutorial
I m access google calendar api v3
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);
well caught thank you 🙂
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!!!
Nope you are stuck with it opening a new window
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!!!
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?
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!
Good question. I have no idea i never really checked that. I will look into it.
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.
can you post the full error message?
Hi Linda.
It error “Error: redirect_uri_mismatch……The redirect URI in the request: http://localhost:1550/authorize/ did not match a registered redirect URI”
Please help !
Thanks so much.
You need to fix your Visual studio and get it to launch debugger with out changing the port number. The Redirect_uri must match exactly what you have in developer console. in your case http://localhost:1550/authorize/ but the probably is that Visual studio may be changing this randomly.
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
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.
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?
Go to the Google Developers console its part of the Client ID for web application on the credentials screen.
Note: you can add more the one Redirect uri for your application. Just put them on different lines.
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.
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.
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!
Hi Linda,
Will your code work (with minor adjustments) with Adsense Management API?
For automatic reporting purposes
pretty much start with: Install-Package Google.Apis.AdSense.v1_2 you will need to change the scopes, and probably uses an absence service. That is what is nice about the Google .net client library, its the same for all of the APIs
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
change “user” to something else. If its already saved it wont request access again.
I changed the “user” (many times) and cleaned the cache, but it didn’t work yet!
it’s a web application running on IIS 7!
please…. I’m already going crazy!!!
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;
Hi Guilherme,
did you find a way to solve the IIS problem?
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
Its a web application .
Your question is really to long for this forum. I think you should try posting it to http://stackoverflow.com/
Hi ,
Please see my question on Stack overflow.
http://stackoverflow.com/questions/29623247/google-drive-fileupload-using-saved-tokens-in-c-sharp
Thanks,
Sandeep
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.
await GoogleWebAuthorizationBroker.AuthorizeAsync(
new Uri(“ms-appx:///Assets/client_secrets.json”),
new[] { DriveService.Scope.DriveReadonly },
“user”,
CancellationToken.None);
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.
I am not a windows phone developer I think you should post that question on StackOverflow with the Google-api-dotnet-client tag. I am sure someone there will be able to help.
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
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).
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.
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.
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.
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.
What do you mean by fetch what command exactly are you using?
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);
I think you should look though how I authenticate the other APIs, there is something wrong with your authentication. you cant just pass token like that.
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.
did you change the redirect uri to the one on the server? Are you sure IIS has access to %appdata%? Are you sure your not blocking Googles Authentication servers?
redirect url is set. Appdata is something that I have struggled but finally sorted out. Is there a way to set a local folder for data storage instead of appdata folder?
I do not think I am blocking Googles authentication servers (where do I check to make sure?).
check this localfiledatastore.cs
Update: I have implemented your Localdatasource solution to avoid app data accessibility problem.
I can see the folder is created but it is empty and the Google conscent window does not come up.
Any idea?
I think this is getting to long for here, take your issue to http://stackoverflow.com its much easier to help with issues there.
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
Hi Paul,
Can you provide a sample of source code about impersonation ?
Sorry I dont have any sample code for impersonation currently.
Hi Daimto,
I am very much thankful and greatful to you for your all post on google as due to that only I am able to integrate Google Prov API but when I published on production server it hangs forever. Please see my post below as you suggested to post on stackoverflow and let me know your thoughts.
http://stackoverflow.com/questions/31818458/google-api-consent-screen-not-showing-up-on-after-publishing-to-server
I have replied there want to keep this kind of question on stack.
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
No, using Login and password is called client login, Client login was shutdown in May 2015. You cant use login password or email and password to access a Google API.
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
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.
Thank you Linda for the answer, do you know if there is a way to use the service account with JavaScript ?
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.
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)
unfortunately i have very little experience with proxy servers. Try and post your question on Stackoverflow.com maybe someone there will be able to answer your question.
I couldn’t resist commenting. Very well written!
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
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.
You have a few options.
I am new developer and have to work on the google calender api using c#…please help
It was really a good Post. Now I am working on Google Cloud Print and Wondering whether i can Use this code For Google Cloud Print or Not??
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.
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
access denied sounds like an issue with where it is saving the credentials. default is %appData%
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.
It works fine on IIS I suspect the problem has something to do with IIS having permissions to wherever you are saving the datastore.
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?
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.
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?
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
Make sure you have included the library correctly?
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.
If you are running this on IIS then you should check this tutorial Web Applications
Thanks for such simple and detail explanation of each and every parameters.
Hello Linda,
Nice article.
Could you please point me to a complete gmail API send example?
Thanks a lot
Regards
Xavier
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
Hi Linda,
Thanks for the great article.
The sample project git url has broken.Could you please provide the working url.
Thanks,
Sateesh
Sorry about that i have changed the directory structure of the git project. I have updated the link. Google Drive samples
For what type of applications can you use this? does it work for mobile applications like xamarin forms or xamarin native?
Web, and native apps the google .net client library doesn’t support Xamarin
Hi Linda,
Can I create a new google spreadsheet on ASP.NET MVC?
sure go though the google sheets api.
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
GoogleWebAuthorizationBroker.AuthorizeAsync is for installed applications not web applications it wont work hosted on a server.
could you advise what will work for applications hosted on server?
[web applications asp .net](https://developers.google.com/api-client-library/dotnet/guide/aaa_oauth#web-applications-asp.net-mvc) will work on a web server. Or you can dig around in the tests on the Google api .net client library itself and see how its done with asp .net core.
Hi. Are you still doing inside this google stuff?
I’ve developed a solution and I’m having trouble when deploying it to live or staging.
Let me know if you have time. Thanks.
Yes i am still working with google apis.