How do I set return_uri for GoogleWebAuthorizationBroker.AuthorizeAsync?

11,958

Solution 1

You can use this code: (original idea from http://coderissues.com/questions/27512300/how-to-append-login-hint-usergmail-com-to-googlewebauthorizationbroker)

dsAuthorizationBroker.RedirectUri = "my localhost redirect uri";
UserCredential credential = await dsAuthorizationBroker.AuthorizeAsync(...

dsAuthorizationBroker.cs

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Auth.OAuth2.Flows;
using Google.Apis.Auth.OAuth2.Requests;
using Google.Apis.Util.Store;

namespace OAuth2
{    
    public class dsAuthorizationBroker : GoogleWebAuthorizationBroker
    {
        public static string RedirectUri;

        public new static async Task<UserCredential> AuthorizeAsync(
            ClientSecrets clientSecrets,
            IEnumerable<string> scopes,
            string user,
            CancellationToken taskCancellationToken,
            IDataStore dataStore = null)
        {
            var initializer = new GoogleAuthorizationCodeFlow.Initializer
            {
                ClientSecrets = clientSecrets,
            };
            return await AuthorizeAsyncCore(initializer, scopes, user,
                taskCancellationToken, dataStore).ConfigureAwait(false);
        }

        private static async Task<UserCredential> AuthorizeAsyncCore(
            GoogleAuthorizationCodeFlow.Initializer initializer,
            IEnumerable<string> scopes,
            string user,
            CancellationToken taskCancellationToken,
            IDataStore dataStore)
        {
            initializer.Scopes = scopes;
            initializer.DataStore = dataStore ?? new FileDataStore(Folder);
            var flow = new dsAuthorizationCodeFlow(initializer);
            return await new AuthorizationCodeInstalledApp(flow, 
                new LocalServerCodeReceiver())
                .AuthorizeAsync(user, taskCancellationToken).ConfigureAwait(false);
        }
    }


    public class dsAuthorizationCodeFlow : GoogleAuthorizationCodeFlow
    {
        public dsAuthorizationCodeFlow(Initializer initializer)
            : base(initializer) { }

        public override AuthorizationCodeRequestUrl
                       CreateAuthorizationCodeRequest(string redirectUri)
        {
            return base.CreateAuthorizationCodeRequest(dsAuthorizationBroker.RedirectUri);
        }
    }    
}

Solution 2

If you are trying to use GoogleWebAuthorizationBroker.AuthorizeAsync in a .NET application NON-web server application i.e. C# Console App command line program, it's crucial when creating the Google OAuth profile (https://console.developers.google.com/apis) in the credentials to select the following. It's hidden and if you don't do it this way, it has to go through an approval process if you choose the radio button "Other". Also note by just copying the contents of the JSON parameters created in the steps below and replacing your client_id/secret with a web app version will still fail. Make a new OAuth client profile for your Google API console.

CLICK "HELP ME CHOOSE"

Step 1

CHOOSE YOUR INTENDED API LIBRARY ie (Google Calendar API) Select "User Data"

Step 2

"Yeah -NO AUTHORIZATION REQUIRED FILEDS" ie Javascript & Redirect Now you have a profile without the authorization

enter image description here

Use the "Download JSON" and save it to your application to reference in the code below. When you look inside this file, you will notice a different set of parameters as well to tell the broker this is an application. In this example, I am accessing the scope Calendar API. Just change the scope to whatever API you are trying to access.

   string[] Scopes = { CalendarService.Scope.Calendar }; //requires full scope to get ACL list..
                string ApplicationName = "Name Of Your Application In Authorization Screen";

                //just reference the namespaces in your using block

                using (var stream = new FileStream("other_client_id.json", FileMode.Open, FileAccess.Read))
                {
                    // The file token.json stores the user's access and refresh tokens, and is created
                    // automatically when the authorization flow completes for the first time.
                    string credPath = "other_token.json";
                    credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
                        GoogleClientSecrets.Load(stream).Secrets,
                        Scopes,
                        "user",
                        CancellationToken.None,
                        new FileDataStore(credPath, true)).Result;               
                }

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

                //Then your ready to grab data from here using the methods mentioned in Google Calendar API docs

Solution 3

selecting "other" while creating oAuth Client ID helped me resolve the redirection issue for me. (Having the "Web Application" option tries to redirect to some url with random port, which is very annoying)

Now my Gmail API works like a charm :)

Share:
11,958

Related videos on Youtube

Bob Kaufman
Author by

Bob Kaufman

ASP.NET and C# developer in Salt Lake City.

Updated on June 16, 2022

Comments

  • Bob Kaufman
    Bob Kaufman almost 2 years

    I am trying to use the Google Calendar API in my non-MVC .NET Web Application. (This appears to be an important distinction.)

    I’ve tried to use code from this example at Google and this example at Daimto along with some helpful hints from a number of related posts here.

    I have written the following method:

    public void GetUserCredential( String userName )
    {
        String clientId = ConfigurationManager.AppSettings[ "Google.ClientId" ];            //From Google Developer console https://console.developers.google.com
        String clientSecret = ConfigurationManager.AppSettings[ "Google.ClientSecret" ];    //From Google Developer console https://console.developers.google.com
        String[] scopes = new string[] {
                Google.Apis.Calendar.v3.CalendarService.Scope.Calendar          
        };
    
        // here is where we Request the user to give us access, or use the Refresh Token that was previously stored in %AppData%
        UserCredential credential = GoogleWebAuthorizationBroker.AuthorizeAsync( new ClientSecrets
        {
            ClientId = clientId,
            ClientSecret = clientSecret             
        }, scopes, userName, CancellationToken.None, new FileDataStore( "c:\\temp" ) ).Result;
    
        // TODO: Replace FileDataStore with DatabaseDataStore
    }
    

    Problem is, when Google’s OAuth2 page is called, redirect_uri keeps getting set to http://localhost:<some-random-port>/authorize. I have no idea how to set this to something else, as in the following example URL generated by AuthorizeAsync:

    https://accounts.google.com/o/oauth2/auth?access_type=offline
        &response_type=code
        &client_id=********.apps.googleusercontent.com
        &redirect_uri=http:%2F%2Flocalhost:40839%2Fauthorize%2F
        &scope=https:%2F%2Fwww.googleapis.com%2Fauth%2Fcalendar
    

    Google responds with a redirect_uri_mismatch error page with the message:

    “The redirect URI in the request: http://localhost:XXXXX/authorize/ did not match a registered redirect URI”

    I can only register so many Redirect URI’s in my Google Developer’s Console Credentials page. I’m not inclined to register 65535 ports, and I want to use a page other than /authorize on my site. Specifically, I want to use, during development, http://localhost:888/Pages/GoogleApiRedirect but have no clue as to where I would set this, beyond what I've done in the Developer’s Console.

    How do I explicitly set the value of redirect_uri? I am also open to a response in the form “This approach is completely wrong.”

    EDIT:

    After playing with this over the past day, I've discovered that by using the Client ID/Client Secret for the Native Application rather than the Web Application, I can at least get to Google's web authorization page without it complaining about a redirect_uri_mismatch. This is still unacceptable, because it still returns to http://localhost:<some-random-port>/authorize, which is outside the control of my web application.

    • Gerardo
      Gerardo almost 9 years
      Have you tried configuring visual studio to use a predefined port for your web app project? check this documentation, so you can define a port and use it in your developer console. msdn.microsoft.com/en-us/library/ms178109(v=vs.140).aspx
    • Bob Kaufman
      Bob Kaufman almost 9 years
      Thanks for the suggestion, @Gerardo. Yes, it's already preconfigured for port 888. I've also tried "unprivileged" ports above 1024. I've seen this suggestion in a few situations that are similar to mine. If it did work, I suppose I could live with /authorize being my redirect page. Not an ideal solution, but one I could live with.
    • Gerardo
      Gerardo almost 9 years
      You could also try to deploy the project directly in IIS, from there you can have more control on the port for your web app. msdn.microsoft.com/en-us/library/vstudio/…
    • Bob Kaufman
      Bob Kaufman almost 9 years
      Another good suggestion, @Gerardo. Thing is, redirect_uri is just a parameter for goodness sake! Why is it being arbitrarily and uncontrollably set as it is? I'm both surprised and frustrated by the utter lack of documentation on this issue. Which leads me to wonder, as I've posed towards the end of my question, if I'm approaching this entirely incorrectly and I should not be using AuthorizeAsync at all.
    • Bob Kaufman
      Bob Kaufman over 8 years
      @Peter - I've tested both on my dev machine (localhost:888) and on my beta machine beta.??????.com and the example below works well on both.
    • Piotr Stulinski
      Piotr Stulinski over 8 years
      @BobKaufman Great, have you had any challenges with LocalServerCodeReceiver throwing a Win32 unauthorised exception? seems to want to open up some sort of port which under IIS Express works, but the minute i host in IIS it doesnt have the right IIS permissions. If you look at the code under google-api-dotnet-client/Src/GoogleApis.Auth.DotNet4/OAuth2/‌​LocalServerCodeRecei‌​ver.cs there is some funky stuff going on with trying to find unused ports etc.
    • Bob Kaufman
      Bob Kaufman over 8 years
      @Peter - not yet. At the moment, I'm just too busy being thrilled that it seamlessly adds and reads things from my one calendar. But that is likely to change over the next few days. As I'm in this code, I'll be paying particular attention to your comment.
    • Piotr Stulinski
      Piotr Stulinski over 8 years
      @BobKaufman i tried using GoogleWebAuthorizationBroker and deep down in the code base is causes a failure in a class called LocalServerCode Receiver where IIS attempts to call Process.Run(url). The problem is that IIS doesnt have permission to access Process.Run() whilst IISExpress does because it is running as a user account. Interesting that you do not have this problem... I am having issues on my Windows 10 machine with IIS installed. I think GoogleWebAuthorizationBroker might be for standard applications not for web applications - well not under the standard IIS configuration.
    • Piotr Stulinski
      Piotr Stulinski over 8 years
    • FrenkyB
      FrenkyB almost 8 years
      The problem for me now is that I don't receive user credentials after successful authentication. I think something similar to @PiotrStulinski. If I use desktop application (with slightly different code than below), user credentials return to method public Task StoreAsync<T>(string key, T value) and are stored in T value. With method below I receive response to my return uri, but no credentials. Method StoreAsync is never called.
  • user275801
    user275801 over 7 years
    This answer doesn't really help at all. nothing is explained... simply waving a magic wand and making it work in some arcane and inscrutable way doesn't really answer the original question, which is "why doesn't the original code work?"
  • Boinst
    Boinst over 7 years
    I don't think that this answer helps. Nowhere is the RedirectUri actually set.
  • Rushik
    Rushik about 5 years
    Thanks for this help. But one issue, not close authentication window . How to close this authendicaton window and then redirect to current working project page?
  • moto_geek
    moto_geek almost 5 years
    This does not return a TOKEN! I posted a reply to hopefully help people past this problem.
  • MortenMoulder
    MortenMoulder about 4 years
    Just wanted to say thank you so much for this hidden reply. Helped me out yesterday.
  • Roman Borovets
    Roman Borovets almost 3 years
    I don't have an option "Where you will be calling the API from?".... Solution doesn't work or outdated...
  • Jamie
    Jamie about 2 years
    I tried this and got the following error in my Windows desktop app: System.AggregateException: One or more errors occurred. ---> Google.Apis.Auth.OAuth2.Responses.TokenResponseException: Error:"invalid_client", Description:"Unauthorized", Uri:"" at Google.Apis.Auth.OAuth2.Responses.TokenResponse Can you help?