Refactor Google Login

FeroxRev [2016-07-20 18:35:25]
Refactor Google Login
Filename
PokemonGo/RocketAPI/Client.cs
PokemonGo/RocketAPI/Console/Program.cs
PokemonGo/RocketAPI/Helpers/HttpClientHelper.cs
PokemonGo/RocketAPI/Login/GoogleLogin.cs
PokemonGo/RocketAPI/PokemonGo.RocketAPI.csproj
PokemonGo/RocketAPI/Settings.cs
diff --git a/PokemonGo/RocketAPI/Client.cs b/PokemonGo/RocketAPI/Client.cs
index e8fe500..138f2d3 100644
--- a/PokemonGo/RocketAPI/Client.cs
+++ b/PokemonGo/RocketAPI/Client.cs
@@ -20,6 +20,7 @@ using PokemonGo.RocketAPI.GeneratedCode;
 using PokemonGo.RocketAPI.Helpers;
 using PokemonGo.RocketAPI.Extensions;
 using System.Threading;
+using PokemonGo.RocketAPI.Login;

 namespace PokemonGo.RocketAPI
 {
@@ -46,7 +47,7 @@ namespace PokemonGo.RocketAPI
             };
             _httpClient = new HttpClient(new RetryHandler(handler));
             _httpClient.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", "Niantic App");
-                //"Dalvik/2.1.0 (Linux; U; Android 5.1.1; SM-G900F Build/LMY48G)");
+            //"Dalvik/2.1.0 (Linux; U; Android 5.1.1; SM-G900F Build/LMY48G)");
             _httpClient.DefaultRequestHeaders.ExpectContinue = false;
             _httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Connection", "keep-alive");
             _httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Accept", "*/*");
@@ -60,155 +61,22 @@ namespace PokemonGo.RocketAPI
             _currentLng = lng;
         }

-        public async Task LoginGoogle(string deviceId, string email, string refreshToken)
+        public async Task DoGoogleLogin()
         {
-            var handler = new HttpClientHandler()
+            if (Settings.GoogleRefreshToken == string.Empty)
             {
-                AutomaticDecompression = DecompressionMethods.GZip,
-                AllowAutoRedirect = false
-            };
-
-            using (var tempHttpClient = new HttpClient(handler))
-            {
-                tempHttpClient.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent",
-                    "GoogleAuth/1.4 (kltexx LMY48G); gzip");
-                tempHttpClient.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("gzip"));
-                tempHttpClient.DefaultRequestHeaders.Add("device", deviceId);
-                tempHttpClient.DefaultRequestHeaders.Add("app", "com.nianticlabs.pokemongo");
-
-                var response = await tempHttpClient.PostAsync(Resources.GoogleGrantRefreshAccessUrl,
-                    new FormUrlEncodedContent(
-                        new[]
-                        {
-                            new KeyValuePair<string, string>("androidId", deviceId),
-                            new KeyValuePair<string, string>("lang", "nl_NL"),
-                            new KeyValuePair<string, string>("google_play_services_version", "9256238"),
-                            new KeyValuePair<string, string>("sdk_version", "22"),
-                            new KeyValuePair<string, string>("device_country", "nl"),
-                            new KeyValuePair<string, string>("client_sig", Settings.ClientSig),
-                            new KeyValuePair<string, string>("caller_sig", Settings.ClientSig),
-                            new KeyValuePair<string, string>("Email", email),
-                            new KeyValuePair<string, string>("service",
-                                "audience:server:client_id:848232511240-7so421jotr2609rmqakceuu1luuq0ptb.apps.googleusercontent.com"),
-                            new KeyValuePair<string, string>("app", "com.nianticlabs.pokemongo"),
-                            new KeyValuePair<string, string>("check_email", "1"),
-                            new KeyValuePair<string, string>("token_request_options", ""),
-                            new KeyValuePair<string, string>("callerPkg", "com.nianticlabs.pokemongo"),
-                            new KeyValuePair<string, string>("Token", refreshToken)
-                        }));
-
-                var content = await response.Content.ReadAsStringAsync();
-                _accessToken = content.Split(new[] {"Auth=", "issueAdvice"}, StringSplitOptions.RemoveEmptyEntries)[0];
-                _authType = AuthType.Google;
-            }
-        }
-
-        public async Task LoginGoogle()
-        {
-
-            String OAUTH_ENDPOINT = "https://accounts.google.com/o/oauth2/device/code";
-            String CLIENT_ID = "848232511240-73ri3t7plvk96pj4f85uj8otdat2alem.apps.googleusercontent.com";
-
-            var handler = new HttpClientHandler()
-            {
-                AutomaticDecompression = DecompressionMethods.GZip,
-                AllowAutoRedirect = false
-            };
-
-            using (var tempHttpClient = new HttpClient(handler))
-            {
-                var response = await tempHttpClient.PostAsync(OAUTH_ENDPOINT,
-                    new FormUrlEncodedContent(
-                        new[]
-                        {
-                            new KeyValuePair<string, string>("client_id", CLIENT_ID),
-                            new KeyValuePair<string, string>("scope", "openid email https://www.googleapis.com/auth/userinfo.email")
-                        }));
-
-                var content = await response.Content.ReadAsStringAsync();
-                JToken token = JObject.Parse(content);
-                JToken token2;
-                Console.WriteLine("Please visit " + token.SelectToken("verification_url") + " and enter " + token.SelectToken("user_code"));
-                while ((token2 = poll(token)) == null)
-                {
-                    Thread.Sleep(Convert.ToInt32(token.SelectToken("interval")) * 1000);
-                }
-                string authToken = token2.SelectToken("id_token").ToString();
-                Console.WriteLine("Sucessfully receieved token.");
-                _accessToken = authToken;
-                _authType = AuthType.Google;
-            }
-        }
-
-
-        private JToken poll(JToken json)
-        {
-            var handler = new HttpClientHandler()
-            {
-                AutomaticDecompression = DecompressionMethods.GZip,
-                AllowAutoRedirect = false
-            };
-
-            String OAUTH_TOKEN_ENDPOINT = "https://www.googleapis.com/oauth2/v4/token";
-            String SECRET = "NCjF1TLi2CcY6t5mt0ZveuL7";
-            String CLIENT_ID = "848232511240-73ri3t7plvk96pj4f85uj8otdat2alem.apps.googleusercontent.com";
-
-            using (var tempHttpClient = new HttpClient(handler))
-            {
-                var response = tempHttpClient.PostAsync(OAUTH_TOKEN_ENDPOINT,
-                    new FormUrlEncodedContent(
-                        new[]
-                        {
-                            new KeyValuePair<string, string>("client_id", CLIENT_ID),
-                            new KeyValuePair<string, string>("client_secret", SECRET),
-                            new KeyValuePair<string, string>("code", json.SelectToken("device_code").ToString()),
-                            new KeyValuePair<string, string>("grant_type", "http://oauth.net/grant_type/device/1.0"),
-                            new KeyValuePair<string, string>("scope", "openid email https://www.googleapis.com/auth/userinfo.email")
-                        }));
-
-                string content = response.Result.Content.ReadAsStringAsync().Result;
-                JToken token = JObject.Parse(content);
-                if (token.SelectToken("error") == null)
-                {
-                    return token;
-                }
-                else
-                {
-                    return null;
-                }
+                var tokenResponse = await GoogleLogin.GetAccessToken();
+                _accessToken = tokenResponse.id_token;
+                Settings.GoogleRefreshToken = tokenResponse.access_token;
+                Console.WriteLine($"Put RefreshToken in settings for direct login: {Settings.GoogleRefreshToken}");
             }
-        }
-
-        public async void GoogleLoginByRefreshToken(string refreshToken)
-        {
-            var handler = new HttpClientHandler() {
-                AutomaticDecompression = DecompressionMethods.GZip,
-                AllowAutoRedirect = false
-            };
-
-            using (var tempHttpClient = new HttpClient(handler))
+            else
             {
-                var response = tempHttpClient.PostAsync("https://www.googleapis.com/oauth2/v4/token",
-                    new FormUrlEncodedContent(
-                        new[]
-                        {
-                            new KeyValuePair<string, string>("client_id", "848232511240-73ri3t7plvk96pj4f85uj8otdat2alem.apps.googleusercontent.com"),
-                            new KeyValuePair<string, string>("client_secret", "NCjF1TLi2CcY6t5mt0ZveuL7"),
-                            new KeyValuePair<string, string>("refresh_token", refreshToken),
-                            new KeyValuePair<string, string>("grant_type", "refresh_token"),
-                            new KeyValuePair<string, string>("scope", "openid email https://www.googleapis.com/auth/userinfo.email")
-                        }));
-
-                var content = await response.Result.Content.ReadAsStringAsync();
-
-                JToken token = JObject.Parse(content);
-                string authToken = token.SelectToken("id_token").ToString();
-                Console.WriteLine("Sucessfully receieved token.");
-                _accessToken = authToken;
-                _authType = AuthType.Google;
+                var tokenResponse = await GoogleLogin.GetAccessToken(Settings.GoogleRefreshToken);
+                _accessToken = tokenResponse.id_token;
             }
         }
-
+
         public async Task LoginPtc(string username, string password)
         {
             //Get session cookie
diff --git a/PokemonGo/RocketAPI/Console/Program.cs b/PokemonGo/RocketAPI/Console/Program.cs
index 4eecbc5..76aded9 100644
--- a/PokemonGo/RocketAPI/Console/Program.cs
+++ b/PokemonGo/RocketAPI/Console/Program.cs
@@ -30,7 +30,7 @@ namespace PokemonGo.RocketAPI.Console
             }
             else
             {
-                await client.LoginGoogle(Settings.DeviceId, Settings.Email, Settings.LongDurationToken);
+                await client.DoGoogleLogin();
             }
             var serverResponse = await client.GetServer();
             var profile = await client.GetProfile();
diff --git a/PokemonGo/RocketAPI/Helpers/HttpClientHelper.cs b/PokemonGo/RocketAPI/Helpers/HttpClientHelper.cs
new file mode 100644
index 0000000..f3d8df4
--- /dev/null
+++ b/PokemonGo/RocketAPI/Helpers/HttpClientHelper.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Net.Http;
+using System.Text;
+using System.Threading.Tasks;
+using Newtonsoft.Json.Linq;
+
+namespace PokemonGo.RocketAPI.Helpers
+{
+    public class HttpClientHelper
+    {
+        public static async Task<TResponse> PostFormEncodedAsync<TResponse>(string url, params KeyValuePair<string, string>[] keyValuePairs)
+        {
+            var handler = new HttpClientHandler()
+            {
+                AutomaticDecompression = DecompressionMethods.GZip,
+                AllowAutoRedirect = false
+            };
+
+            using (var tempHttpClient = new HttpClient(handler))
+            {
+                var response = await tempHttpClient.PostAsync(url, new FormUrlEncodedContent(keyValuePairs));
+                return await response.Content.ReadAsAsync<TResponse>();
+            }
+        }
+    }
+}
diff --git a/PokemonGo/RocketAPI/Login/GoogleLogin.cs b/PokemonGo/RocketAPI/Login/GoogleLogin.cs
new file mode 100644
index 0000000..05f412d
--- /dev/null
+++ b/PokemonGo/RocketAPI/Login/GoogleLogin.cs
@@ -0,0 +1,92 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Net.Http;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Newtonsoft.Json.Linq;
+using PokemonGo.RocketAPI.Enums;
+using PokemonGo.RocketAPI.Helpers;
+
+namespace PokemonGo.RocketAPI.Login
+{
+    internal static class GoogleLogin
+    {
+        private const string OauthTokenEndpoint = "https://www.googleapis.com/oauth2/v4/token";
+        private const string OauthEndpoint = "https://accounts.google.com/o/oauth2/device/code";
+        private const string ClientId = "848232511240-73ri3t7plvk96pj4f85uj8otdat2alem.apps.googleusercontent.com";
+        private const string ClientSecret = "NCjF1TLi2CcY6t5mt0ZveuL7";
+
+        internal static async Task<TokenResponseModel> GetAccessToken()
+        {
+            var deviceCodeResponse = await GetDeviceCode();
+            Console.WriteLine("Please visit " + deviceCodeResponse.verification_url + " and enter " + deviceCodeResponse.user_code);
+
+            //Poll until user submitted code..
+            TokenResponseModel tokenResponse;
+            do
+            {
+                await Task.Delay(2000);
+                tokenResponse = await PollSubmittedToken(deviceCodeResponse.device_code);
+            } while (tokenResponse.access_token == null || tokenResponse.refresh_token == null);
+
+            return tokenResponse;
+        }
+
+        private static async Task<DeviceCodeModel> GetDeviceCode()
+        {
+            return await HttpClientHelper.PostFormEncodedAsync<DeviceCodeModel>(OauthEndpoint,
+                new KeyValuePair<string, string>("client_id", ClientId),
+                new KeyValuePair<string, string>("scope", "openid email https://www.googleapis.com/auth/userinfo.email"));
+        }
+
+        private static async Task<TokenResponseModel> PollSubmittedToken(string deviceCode)
+        {
+            return await HttpClientHelper.PostFormEncodedAsync<TokenResponseModel>(OauthTokenEndpoint,
+                new KeyValuePair<string, string>("client_id", ClientId),
+                new KeyValuePair<string, string>("client_secret", ClientSecret),
+                new KeyValuePair<string, string>("code", deviceCode),
+                new KeyValuePair<string, string>("grant_type", "http://oauth.net/grant_type/device/1.0"),
+                new KeyValuePair<string, string>("scope", "openid email https://www.googleapis.com/auth/userinfo.email"));
+        }
+
+        public static async Task<TokenResponseModel> GetAccessToken(string refreshToken)
+        {
+            return await HttpClientHelper.PostFormEncodedAsync<TokenResponseModel>(OauthTokenEndpoint,
+                new KeyValuePair<string, string>("client_id", ClientId),
+                new KeyValuePair<string, string>("client_secret", ClientSecret),
+                new KeyValuePair<string, string>("refresh_token", refreshToken),
+                new KeyValuePair<string, string>("grant_type", "refresh_token"),
+                new KeyValuePair<string, string>("scope", "openid email https://www.googleapis.com/auth/userinfo.email"));
+        }
+
+
+        internal class ErrorResponseModel
+        {
+            public string error { get; set; }
+            public string error_description { get; set; }
+        }
+
+        internal class TokenResponseModel
+        {
+            public string access_token { get; set; }
+            public string token_type { get; set; }
+            public int expires_in { get; set; }
+            public string refresh_token { get; set; }
+            public string id_token { get; set; }
+        }
+
+
+        public class DeviceCodeModel
+        {
+            public string verification_url { get; set; }
+            public int expires_in { get; set; }
+            public int interval { get; set; }
+            public string device_code { get; set; }
+            public string user_code { get; set; }
+        }
+
+    }
+}
diff --git a/PokemonGo/RocketAPI/PokemonGo.RocketAPI.csproj b/PokemonGo/RocketAPI/PokemonGo.RocketAPI.csproj
index f61df95..02cf8f8 100644
--- a/PokemonGo/RocketAPI/PokemonGo.RocketAPI.csproj
+++ b/PokemonGo/RocketAPI/PokemonGo.RocketAPI.csproj
@@ -88,11 +88,13 @@
     <Compile Include="GeneratedCode\PlayerUpdateResponse.cs" />
     <Compile Include="GeneratedCode\Request.cs" />
     <Compile Include="GeneratedCode\SettingsResponse.cs" />
+    <Compile Include="Helpers\HttpClientHelper.cs" />
     <Compile Include="Helpers\JsonHelper.cs" />
     <Compile Include="Helpers\ProtoHelper.cs" />
     <Compile Include="Helpers\RetryHandler.cs" />
     <Compile Include="Helpers\S2Helper.cs" />
     <Compile Include="Helpers\Utils.cs" />
+    <Compile Include="Login\GoogleLogin.cs" />
     <Compile Include="Settings.cs" />
     <None Include="app.config" />
     <Compile Include="Client.cs" />
diff --git a/PokemonGo/RocketAPI/Settings.cs b/PokemonGo/RocketAPI/Settings.cs
index 87b81db..e01aa12 100644
--- a/PokemonGo/RocketAPI/Settings.cs
+++ b/PokemonGo/RocketAPI/Settings.cs
@@ -9,13 +9,10 @@ namespace PokemonGo.RocketAPI
     public static class Settings
     {
         //Fetch these settings from intercepting the /auth call in headers and body (only needed for google auth)
-        public const bool UsePTC = true;
+        public const bool UsePTC = false;
         public const string PtcUsername = "User";
         public const string PtcPassword = "alligator2";
-        public const string DeviceId = "cool-device-id";
-        public const string Email = "fake@gmail.com";
-        public const string ClientSig = "fake";
-        public const string LongDurationToken = "fakeid";
+        public static string GoogleRefreshToken = string.Empty;
         public const double DefaultLatitude = 10;
         public const double DefaultLongitude = 10;
You may download the files in Public Git.