Search code examples
c#winformsoauthgoogle-oauthgoogle-calendar-api

Viewing Google Calendar in WinForms Browser Control


I want to show my Google Calendar in a WebBrowser control from WinForms.

I tried to follow a tutorial https://www.daimto.com/google-api-and-oath2/

But unfortunately i still dont get the auth to get access to my google calendar, "the browser is not safe ...".

I know i somehow have to use the acces but i dont know how.

The Code:

 public partial class Kalender : UserControl
{
    public string clientId = "897492813624-glvfpm6heik1fqr7s2mjtl6eme5ad8bq.apps.googleusercontent.com";
    public string redirectUri = "https://calendar.google.com/";
    public string clientSecret = "HYGFBEd_BZPcWcrPopsX4uZr";
    public string redirectURI = "https://calendar.google.com/";
    public AuthResponse access;

    public Kalender()
    {
        InitializeComponent();
        webKalender.Navigate(AuthResponse.GetAutenticationURI(clientId, redirectUri));
    }

    private void webKalender_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
    {
        
        string Mytitle = ((WebBrowser)sender).DocumentTitle.ToLower();
        if (Mytitle.IndexOf("success code=") > -1)
        {
            webKalender.Visible = false;

            // searching the body for our code
            string AuthCode = webKalender.DocumentTitle.Replace("Success code=", "");
            string webText = ((WebBrowser)sender).DocumentText;
            int start = webText.IndexOf("id=\"code\"");
            start = webText.IndexOf(AuthCode, start);
            int end = webText.IndexOf('"', start);
            string authCode = webText.Substring(start, end - start);

            //Exchange the code for Access token and refreshtoken.
            access = AuthResponse.Exchange(authCode, clientId, clientSecret, redirectURI);

            //processAccess();
        }
    }

The class from the Tutorial:

 public class AuthResponse
    {
        private string access_token;
        public string Access_token
        {
            get
            {
                // Access token lasts an hour if its expired we get a new one.
                if (DateTime.Now.Subtract(created).Hours > 1)
                {
                    refresh();
                }
                return access_token;
            }
            set { access_token = value; }
        }
        public string refresh_token { get; set; }
        public string clientId { get; set; }
        public string secret { get; set; }
        public string expires_in { get; set; }
        public DateTime created { get; set; }


       

         //"{\n  \"access_token\" : \"ya29.kwFUj-la2lATSkrqFlJXBqQjCIZiTg51GYpKt8Me8AJO5JWf0Sx6-0ZWmTpxJjrBrxNS_JzVw969LA\",\n  \"token_type\" : \"Bearer\",\n  \"expires_in\" : 3600,\n  \"refresh_token\" : \"1/ejoPJIyBAhPHRXQ7pHLxJX2VfDBRz29hqS_i5DuC1cQ\"\n}"
      

        public static AuthResponse get(string response)
        {
            AuthResponse result = (AuthResponse)JsonConvert.DeserializeObject(response);
            result.created = DateTime.Now;   // DateTime.Now.Add(new TimeSpan(-2, 0, 0)); //For testing force refresh.
            return result;
        }


        public void refresh()
        {
            var request = (HttpWebRequest)WebRequest.Create("https://accounts.google.com/o/oauth2/token");
            string postData = string.Format("client_id={0}&client_secret={1}&refresh_token={2}&grant_type=refresh_token", this.clientId, this.secret, this.refresh_token);
            var data = Encoding.ASCII.GetBytes(postData);

            request.Method = "POST";
            request.ContentType = "application/x-www-form-urlencoded";
            request.ContentLength = data.Length;

            using (var stream = request.GetRequestStream())
            {
                stream.Write(data, 0, data.Length);
            }

            var response = (HttpWebResponse)request.GetResponse();
            var responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();
            var refreshResponse = AuthResponse.get(responseString);
            this.access_token = refreshResponse.access_token;
            this.created = DateTime.Now;
        }


        public static AuthResponse Exchange(string authCode, string clientid, string secret, string redirectURI)
        {

            var request = (HttpWebRequest)WebRequest.Create("https://accounts.google.com/o/oauth2/token");

            string postData = string.Format("code={0}&client_id={1}&client_secret={2}&redirect_uri={3}&grant_type=authorization_code", authCode, clientid, secret, redirectURI);
            var data = Encoding.ASCII.GetBytes(postData);

            request.Method = "POST";
            request.ContentType = "application/x-www-form-urlencoded";
            request.ContentLength = data.Length;

            using (var stream = request.GetRequestStream())
            {
                stream.Write(data, 0, data.Length);
            }

            var response = (HttpWebResponse)request.GetResponse();

            var responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();

            var x = AuthResponse.get(responseString);

            x.clientId = clientid;
            x.secret = secret;

            return x;

        }



        public static Uri GetAutenticationURI(string clientId, string redirectUri)
        {
            string scopes = "https://www.googleapis.com/auth/calendar";

            if (string.IsNullOrEmpty(redirectUri))
            {
                redirectUri = "urn:ietf:wg:oauth:2.0:oob";
            }
            string oauth = string.Format("https://accounts.google.com/o/oauth2/auth?client_id={0}&redirect_uri={1}&scope={2}&response_type=code", clientId, redirectUri, scopes);
            return new Uri(oauth);
        }

    }

After Email input an google login

After i press yes on the previous error message

Sorry for the German but it says something like :"Brower or Application my not be safe. Use different Browser."


Solution

  • I want to show my Google Calendar in a webbrowser control from winforms.

    The first thing you should understand is if what you are trying to do is show the Google calendar web application inside your application then its not going to work not using that code or any code.

    What you can do is use the Google calendar api to access Google calendar data and then create your own display of that data in your web form.

    Thats what that code you are using was designed to do. However its more then nine years old.

    If at all possible you should be using the Google .net client library. That being said the code you are using was originally written for use with .Net 3.5 over nine years ago. The google .net client library did not support .net 3.5. (I am not referring to .Net core 3.1 we are talking much older.)

    There have been a lot of changes to security since that time. If Google is detecting that you are opening that in a webview then its probably not going to work. The only way the auth screen will work these days is if the oauth window is opened in the installed browser on the machine.

    The .net client library is designed to do just that.

    Here is some authorization code to get you started.

    public static class Oauth2Example
        {
            /// <summary>
            /// ** Installed Aplication only ** 
            /// This method requests Authentcation from a user using Oauth2.  
            /// </summary>
            /// <param name="clientSecretJson">Path to the client secret json file from Google Developers console.</param>
            /// <param name="userName">Identifying string for the user who is being authentcated.</param>
            /// <param name="scopes">Array of Google scopes</param>
            /// <returns>CalendarService used to make requests against the Calendar API</returns>
            public static CalendarService GetCalendarService(string clientSecretJson, string userName, string[] scopes)
            {
                try
                {
                    if (string.IsNullOrEmpty(userName))
                        throw new ArgumentNullException("userName");
                    if (string.IsNullOrEmpty(clientSecretJson))
                        throw new ArgumentNullException("clientSecretJson");
                    if (!File.Exists(clientSecretJson))
                        throw new Exception("clientSecretJson file does not exist.");
    
                    var cred = GetUserCredential(clientSecretJson, userName, scopes);
                    return GetService(cred);
    
                }
                catch (Exception ex)
                {
                    throw new Exception("Get Calendar service failed.", ex);
                }
            }
    
            /// <summary>
            /// ** Installed Aplication only ** 
            /// This method requests Authentcation from a user using Oauth2.  
            /// Credentials are stored in System.Environment.SpecialFolder.Personal
            /// Documentation https://developers.google.com/accounts/docs/OAuth2
            /// </summary>
            /// <param name="clientSecretJson">Path to the client secret json file from Google Developers console.</param>
            /// <param name="userName">Identifying string for the user who is being authentcated.</param>
            /// <param name="scopes">Array of Google scopes</param>
            /// <returns>authencated UserCredential</returns>
            private static UserCredential GetUserCredential(string clientSecretJson, string userName, string[] scopes)
            {
                try
                {
                    if (string.IsNullOrEmpty(userName))
                        throw new ArgumentNullException("userName");
                    if (string.IsNullOrEmpty(clientSecretJson))
                        throw new ArgumentNullException("clientSecretJson");
                    if (!File.Exists(clientSecretJson))
                        throw new Exception("clientSecretJson file does not exist.");
    
                    // These are the scopes of permissions you need. It is best to request only what you need and not all of them               
                    using (var stream = new FileStream(clientSecretJson, FileMode.Open, FileAccess.Read))
                    {
                        string credPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
                        credPath = Path.Combine(credPath, ".credentials/", System.Reflection.Assembly.GetExecutingAssembly().GetName().Name);
    
                        // Requesting Authentication or loading previously stored authentication for userName
                        var credential = GoogleWebAuthorizationBroker.AuthorizeAsync(GoogleClientSecrets.Load(stream).Secrets,
                                                                                 scopes,
                                                                                 userName,
                                                                                 CancellationToken.None,
                                                                                 new FileDataStore(credPath, true)).Result;
    
                        credential.GetAccessTokenForRequestAsync();
                        return credential;
                    }
                }
                catch (Exception ex)
                {
                    throw new Exception("Get user credentials failed.", ex);
                }
            }
    
            /// <summary>
            /// This method get a valid service
            /// </summary>
            /// <param name="credential">Authecated user credentail</param>
            /// <returns>CalendarService used to make requests against the Calendar API</returns>
            private static CalendarService GetService(UserCredential credential)
            {
                try
                {
                    if (credential == null)
                        throw new ArgumentNullException("credential");
    
                    // Create Calendar API service.
                    return new CalendarService(new BaseClientService.Initializer()
                    {
                        HttpClientInitializer = credential,
                        ApplicationName = "Calendar Oauth2 Authentication Sample"
                    });
                }
                catch (Exception ex)
                {
                    throw new Exception("Get Calendar service failed.", ex);
                }
            }
        }