Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I set return_uri for GoogleWebAuthorizationBroker.AuthorizeAsync?

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.

like image 902
Bob Kaufman Avatar asked Jul 03 '15 14:07

Bob Kaufman


4 Answers

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);
        }
    }    
}
like image 116
Cristian Avatar answered Oct 22 '22 04:10

Cristian


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
like image 33
moto_geek Avatar answered Oct 22 '22 06:10

moto_geek


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 :)

like image 2
Venkat pv Avatar answered Oct 22 '22 05:10

Venkat pv


If you are struggling to build a console app that would be able to authenticate to Gmail to send mail, here is what worked. LocalServerCodeReceiver was the key to listening to access token returned from the browser. This code also would obtain a refresh token if needed, and will cache the token on the file system for future use.

var clientSecrets = new ClientSecrets
{
    ClientId = YOUR_CLIENTID,
    ClientSecret = YOUR_CLIENT_SECRET
};

var codeFlow = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
    DataStore = new FileDataStore(AppDomain.CurrentDomain.BaseDirectory),
    Scopes = new[] { GmailService.Scope.MailGoogleCom },
    ClientSecrets = clientSecrets
});

var codeReceiver = new LocalServerCodeReceiver();
var authCode = new AuthorizationCodeInstalledApp(codeFlow, codeReceiver);
var credential = await authCode.AuthorizeAsync(EmailUser, CancellationToken.None);

if (authCode.ShouldRequestAuthorizationCode(credential.Token))
{
    await credential.RefreshTokenAsync(CancellationToken.None);
}

Note:

  • in my experience, Firefox didn't handle the browser redirect correctly, so I had to switch my default Windows browser to Chrome in order for this to work
  • even though this code is part of a console app, it is not entirely set up to run unattended; unless the token is cached on the filesystem from prior runs, you would need to manually go through the authentication flow in the browser popup
  • the OAuth client ID configuration in the Developers console should be set to "Desktop"
like image 1
July.Tech Avatar answered Oct 22 '22 04:10

July.Tech