How to: OAuth 2.0 Flows using the Google+ .NET API Client Libraries 1.7+ in C#
There have recently been significant changes to the .NET Libraries for Google APIs. As such, it’s a great time to show my favorite scenarios for OAuth 2. In this brief post I’ll show you a basic API query and then some more complicated examples such as code exchange.
Using a Basic API key
Before we begin, I want to add that for making basic API calls, you do not need a client id / client secret. You first start by creating a project in the Google Developer Console and then constructing a service object as follows:
PlusService plusService = new PlusService( new Google.Apis.Services.BaseClientService.Initializer() { ApiKey = API_KEY });
You are now ready to make API calls using the service object as follows:
Person me = plusService.People.Get(@"+GusClass").Execute();
If you inspect the me variable, you will see all of the data for the user +GusClass in your data. This is the easy one so if you want to make sure your configuration is working, start with a basic API such as Plus.People.Get as shown above or Plus.Activities.Search. Note, for performance, you should use the async methods:
Person me = await plusService.People.Get(@"+GusClass").ExecuteAsync();
Pre-steps for OAuth 2.0 Flows in C# / .NET
You must get API keys from the Google Developer Console. For code exchange and refresh flows, where your application is authorized to perform operations on a user’s data, you will also need to provide a client ID and secret, which now be loaded from JSON as described in the next section. I’m not going to get into very deep detail about much beyond how the client (service) is authenticated.
Building the objects for performing flows
Constructing a TokenInfo object:
/// <summary>Retrieves the Client Configuration from the server path.</summary> /// <returns>Client secrets that can be used for API calls.</returns> public static GoogleClientSecrets GetClientConfiguration() { using (var stream = new FileStream("client_secrets.json", FileMode.Open, FileAccess.Read)) { return GoogleClientSecrets.Load(stream); } }
Constructing a service given a TokenInfo object:
/// <summary> /// Gets a Google+ service object for making authorized API calls to Google+ endpoints. /// </summary> /// <param name="credentials">The OAuth 2 credentials to use to authorize the client.</param> /// <returns> /// A <see cref="PlusService">PlusService</see>"/> object for API calls authorized to the /// user who corresponds with the credentials. /// </returns> public static PlusService GetPlusService(TokenResponse credentials) { IAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow( new GoogleAuthorizationCodeFlow.Initializer { ClientSecrets = GetClientConfiguration().Secrets, Scopes = new string[] { PlusService.Scope.PlusLogin } }); UserCredential credential = new UserCredential(flow, "me", credentials); return new PlusService( new Google.Apis.Services.BaseClientService.Initializer() { ApplicationName = "Haikunamatata", HttpClientInitializer = credential }); }
OAuth 2.0 Flows examples
Again, the key flows are:
- Basic API Key – You want to make an API call that does not require user authorization.
- Code exchange – You have an authorization code from a redirect or the Sign-In Button and want an access / refresh token combo.
- Refresh – You have a refresh token, your access token is expired, and you want to have a new access token.
Code Exchange
Code exchange is the first type of authenticated OAuth 2.0 flows I want to demonstrate. The authorization code typically is passed to your client from an authorization server and is only valid for exchange once.
response = null; // Use the code exchange flow to get an access and refresh token. IAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer { ClientSecrets = GetClientConfiguration().Secrets, Scopes = new string[] { PlusService.Scope.PlusLogin } }); TokenResponse response = flow.ExchangeCodeForTokenAsync("", code, "postmessage", CancellationToken.None).Result; // response.accessToken now should be populated
You can now perform authenticated queries on a service object using the TokenResponse object and can securely store the refresh token if it was returned from the code exchange.
Refresh Token
When you have a valid refresh token and want to make authenticated API calls to Google, you will not be able to directly use the refresh token to make API calls. However, you can get a temporary access token by exchanging the refresh token with the server. The following code shows a refresh token flow:
TokenResponse token = new TokenResponse { AccessToken = this.GoogleAccessToken, RefreshToken = this.GoogleRefreshToken }; IAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer { ClientSecrets = PlusHelper.GetClientConfiguration().Secrets, Scopes = new string[] { PlusService.Scope.PlusLogin } }); UserCredential credential = new UserCredential(flow, "me", token); bool success = credential.RefreshTokenAsync(CancellationToken.None).Result; token = credential.Token;
Making API calls once you have TokenResponse
You are now ready to make authenticated API calls with the response:
PlusService plusService = new PlusService( new Google.Apis.Services.BaseClientService.Initializer() { ApplicationName = "Haikunamata", HttpClientInitializer = credential });
I have put together an update to the Google+ Quickstart that demonstrates the new way of doing things.
See Also
If you’re using ASP.NET MVC:
Why are you linking a dead example, google disabled authentication, everybody is screwed and have to deal with google plus. And your examples don’t work, i can’t get email from request.Execute()
I think your API client (configured from https://console.developers.google.com) is misconfigured because the port is changing on your ASP.NET web server. The port needs to match whatever you have the port and protocol correctly set to in the client, e.g. https://example.com is different from http://example.com is different from http://example.com:8001 – the server built into Visual Studio chooses a random port. To correct this, set it manually within your project properties (right-click your project) and then set it to a specific port.
my port is not changing and my API config is OK
for one google+ account I can see the email, for another no
If the sample is a Web project, you need to set the port for authorization. If the project is a console project, you’ll be using the installed application flow and can get a random port on the browser when the user authorizes.
Have a look, these guys also stuck https://stackoverflow.com/questions/24283902/google-signin-api-and-emails
So is it google+ bug officially?
OK I had duplicate data-scope attribut in the button. This solution works: https://stackoverflow.com/questions/24283902/google-signin-api-and-emails?answertab=active#tab-top
The scope should work in my github code. At least it works for me. If you have detailed repro steps, I might be able to better understand what’s going on.
Hi, sorry for my bad english. :o) I hope you can help me to understand. In the Pre Step section you have created a function that returns a PlusService. Where or how can i obtain the TokenResponse object you passed as parameter?
Thank you so much!
There are a few places where the auth token is retrieved – make sure to try downloading the sample. Here’s the code for getting the authorization token from a code exchange:
// Code exchange
token = flow.ExchangeCodeForTokenAsync(“”, code, “postmessage”,
CancellationToken.None).Result;
// Token Refresh
UserCredential credential = new UserCredential(flow, “me”, token);
bool success = credential.RefreshTokenAsync(CancellationToken.None).Resul
Hi, thank you for your reply. Another question. How i can obtain the code to call ExchangeCodeForTokenAsync function? I saw the quickstart code but i would get the code without javascript and ajax. It’s possible to abtain it by server side?
Another thing. My application is configured to authenticate users with Google as external provider. I think that in order to authenticate a user the server must already have a token provided by Google…is it right? If yes there’s a way to get it without calling Google again via javascript?
In this particular case, the authorization code is retrieved client-side using the JavaScript client library. If you want to do this server-side or using an installed application, do so as described here:
https://gusclass.com/blog/2014/07/29/using-google-apis-from-console-apps-in-net/
Gus,
I am having a difficult time with 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.
I can’t look at this at this time; please post your question in Stack Overflow to investigate a response and it will be easier for the community to find too if others are encountering the same thing. http://stackoverflow.com/questions/tagged/google-plus Oh, and link it here in case I get a chance to look at it 🙂
Hi:
I am trying to access user from the Gapps domain account. I am authenticating using admin account of the domain.
Its throwing following exception while executing following statement
DirService.Users.Get(“testu@XXXXXX.com”).Execute()
The service admin has thrown an exception: Google.GoogleApiException: Google.Apis.Requests.RequestError
Insufficient Permission [403]
Errors [
Message[Insufficient Permission] Location[ – ] Reason[insufficientPermissions] Domain[global]
]
Any help on this is greatly appreciated.
Hi, I wanna include a Google Calendar API in my Web project, but I’m getting an exception when I try to execute the ExchangeCodeForTokenAsync method, and it is caused because I don’t know how to get the “code” parameter of that method.
Would you help me, please?. How can I get that code?.
Ooof, it’s been a while since I last looked into this. Sorry you’re having trouble but you might want to try stack overflow.
replace this line:
static string[] Scopes = { SheetsService.Scope.Spreadsheets.ReadOnly };
with this:
static string[] Scopes = { SheetsService.Scope.Spreadsheets };
and if you did not pass, delete (token.json) folder and try again