Skip to content
This repository was archived by the owner on Mar 11, 2025. It is now read-only.

Commit 3f14353

Browse files
jskeetWilliamDenniss
authored andcommitted
Update console application
- Follow .NET naming conventions - Await tasks appropriately - Remove client ID and client secret from source code
1 parent 05432f2 commit 3f14353

File tree

1 file changed

+87
-82
lines changed

1 file changed

+87
-82
lines changed

OAuthConsoleApp/OAuthConsoleApp/Program.cs

Lines changed: 87 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2016 Google Inc.
1+
// Copyright 2016 Google Inc.
22
//
33
// Licensed under the Apache License, Version 2.0 (the "License");
44
// you may not use this file except in compliance with the License.
@@ -12,23 +12,34 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
using Newtonsoft.Json;
1516
using System;
1617
using System.Collections.Generic;
17-
using System.Text;
18-
using System.Threading.Tasks;
19-
using Newtonsoft.Json;
18+
using System.Diagnostics;
2019
using System.IO;
2120
using System.Net;
2221
using System.Net.Sockets;
23-
using System.Security.Cryptography;
2422
using System.Runtime.InteropServices;
23+
using System.Security.Cryptography;
24+
using System.Text;
25+
using System.Threading.Tasks;
2526

2627
namespace OAuthConsoleApp
2728
{
2829
class Program
2930
{
30-
static void Main(string[] args)
31+
const string AuthorizationEndpoint = "https://accounts.google.com/o/oauth2/v2/auth";
32+
33+
static async Task<int> Main(string[] args)
3134
{
35+
if (args.Length != 2)
36+
{
37+
Console.WriteLine("Required command line arguments: client-id client-secret");
38+
return 1;
39+
}
40+
string clientId = args[0];
41+
string clientSecret = args[1];
42+
3243
Console.WriteLine("+-----------------------+");
3344
Console.WriteLine("| Sign in with Google |");
3445
Console.WriteLine("+-----------------------+");
@@ -37,18 +48,13 @@ static void Main(string[] args)
3748
Console.ReadKey();
3849

3950
Program p = new Program();
40-
p.doOAuth();
51+
await p.DoOAuthAsync(clientId, clientSecret);
4152

53+
Console.WriteLine("Press any key to exit...");
4254
Console.ReadKey();
55+
return 0;
4356
}
4457

45-
// client configuration
46-
const string clientID = "581786658708-elflankerquo1a6vsckabbhn25hclla0.apps.googleusercontent.com";
47-
const string clientSecret = "3f6NggMbPtrmIBpgx-MK2xXK";
48-
const string authorizationEndpoint = "https://accounts.google.com/o/oauth2/v2/auth";
49-
const string tokenEndpoint = "https://www.googleapis.com/oauth2/v4/token";
50-
const string userInfoEndpoint = "https://www.googleapis.com/oauth2/v3/userinfo";
51-
5258
// ref http://stackoverflow.com/a/3978040
5359
public static int GetRandomUnusedPort()
5460
{
@@ -59,35 +65,35 @@ public static int GetRandomUnusedPort()
5965
return port;
6066
}
6167

62-
private async void doOAuth()
68+
private async Task DoOAuthAsync(string clientId, string clientSecret)
6369
{
6470
// Generates state and PKCE values.
65-
string state = randomDataBase64url(32);
66-
string code_verifier = randomDataBase64url(32);
67-
string code_challenge = base64urlencodeNoPadding(sha256(code_verifier));
68-
const string code_challenge_method = "S256";
71+
string state = GenerateRandomDataBase64url(32);
72+
string codeVerifier = GenerateRandomDataBase64url(32);
73+
string codeChallenge = Base64UrlEncodeNoPadding(Sha256Ascii(codeVerifier));
74+
const string codeChallengeMethod = "S256";
6975

7076
// Creates a redirect URI using an available port on the loopback address.
71-
string redirectURI = string.Format("http://{0}:{1}/", IPAddress.Loopback, GetRandomUnusedPort());
72-
output("redirect URI: " + redirectURI);
77+
string redirectUri = $"http://{IPAddress.Loopback}:{GetRandomUnusedPort()}/";
78+
Log("redirect URI: " + redirectUri);
7379

7480
// Creates an HttpListener to listen for requests on that redirect URI.
7581
var http = new HttpListener();
76-
http.Prefixes.Add(redirectURI);
77-
output("Listening..");
82+
http.Prefixes.Add(redirectUri);
83+
Log("Listening..");
7884
http.Start();
7985

8086
// Creates the OAuth 2.0 authorization request.
8187
string authorizationRequest = string.Format("{0}?response_type=code&scope=openid%20profile&redirect_uri={1}&client_id={2}&state={3}&code_challenge={4}&code_challenge_method={5}",
82-
authorizationEndpoint,
83-
System.Uri.EscapeDataString(redirectURI),
84-
clientID,
88+
AuthorizationEndpoint,
89+
Uri.EscapeDataString(redirectUri),
90+
clientId,
8591
state,
86-
code_challenge,
87-
code_challenge_method);
92+
codeChallenge,
93+
codeChallengeMethod);
8894

8995
// Opens request in the browser.
90-
System.Diagnostics.Process.Start(authorizationRequest);
96+
Process.Start(authorizationRequest);
9197

9298
// Waits for the OAuth authorization response.
9399
var context = await http.GetContextAsync();
@@ -97,71 +103,71 @@ private async void doOAuth()
97103

98104
// Sends an HTTP response to the browser.
99105
var response = context.Response;
100-
string responseString = string.Format("Please return to the app.");
101-
var buffer = System.Text.Encoding.UTF8.GetBytes(responseString);
106+
string responseString = "Please return to the app.";
107+
byte[] buffer = Encoding.UTF8.GetBytes(responseString);
102108
response.ContentLength64 = buffer.Length;
103109
var responseOutput = response.OutputStream;
104-
Task responseTask = responseOutput.WriteAsync(buffer, 0, buffer.Length).ContinueWith((task) =>
105-
{
106-
responseOutput.Close();
107-
http.Stop();
108-
Console.WriteLine("HTTP server stopped.");
109-
});
110+
await responseOutput.WriteAsync(buffer, 0, buffer.Length);
111+
responseOutput.Close();
112+
http.Stop();
113+
Log("HTTP server stopped.");
110114

111115
// Checks for errors.
112-
if (context.Request.QueryString.Get("error") != null)
116+
string error = context.Request.QueryString.Get("error");
117+
if (error is object)
113118
{
114-
output(String.Format("OAuth authorization error: {0}.", context.Request.QueryString.Get("error")));
119+
Log($"OAuth authorization error: {error}.");
115120
return;
116121
}
117-
if (context.Request.QueryString.Get("code") == null
118-
|| context.Request.QueryString.Get("state") == null)
122+
if (context.Request.QueryString.Get("code") is null
123+
|| context.Request.QueryString.Get("state") is null)
119124
{
120-
output("Malformed authorization response. " + context.Request.QueryString);
125+
Log($"Malformed authorization response. {context.Request.QueryString}");
121126
return;
122127
}
123128

124129
// extracts the code
125130
var code = context.Request.QueryString.Get("code");
126-
var incoming_state = context.Request.QueryString.Get("state");
131+
var incomingState = context.Request.QueryString.Get("state");
127132

128133
// Compares the receieved state to the expected value, to ensure that
129134
// this app made the request which resulted in authorization.
130-
if (incoming_state != state)
135+
if (incomingState != state)
131136
{
132-
output(String.Format("Received request with invalid state ({0})", incoming_state));
137+
Log($"Received request with invalid state ({incomingState})");
133138
return;
134139
}
135-
output("Authorization code: " + code);
140+
Log("Authorization code: " + code);
136141

137142
// Starts the code exchange at the Token Endpoint.
138-
performCodeExchange(code, code_verifier, redirectURI);
143+
await ExchangeCodeForTokensAsync(code, codeVerifier, redirectUri, clientId, clientSecret);
139144
}
140145

141-
async void performCodeExchange(string code, string code_verifier, string redirectURI)
146+
async Task ExchangeCodeForTokensAsync(string code, string codeVerifier, string redirectUri, string clientId, string clientSecret)
142147
{
143-
output("Exchanging code for tokens...");
148+
Log("Exchanging code for tokens...");
144149

145150
// builds the request
146-
string tokenRequestURI = "https://www.googleapis.com/oauth2/v4/token";
151+
string tokenRequestUri = "https://www.googleapis.com/oauth2/v4/token";
147152
string tokenRequestBody = string.Format("code={0}&redirect_uri={1}&client_id={2}&code_verifier={3}&client_secret={4}&scope=&grant_type=authorization_code",
148153
code,
149-
System.Uri.EscapeDataString(redirectURI),
150-
clientID,
151-
code_verifier,
154+
Uri.EscapeDataString(redirectUri),
155+
clientId,
156+
codeVerifier,
152157
clientSecret
153158
);
154159

155160
// sends the request
156-
HttpWebRequest tokenRequest = (HttpWebRequest)WebRequest.Create(tokenRequestURI);
161+
HttpWebRequest tokenRequest = (HttpWebRequest)WebRequest.Create(tokenRequestUri);
157162
tokenRequest.Method = "POST";
158163
tokenRequest.ContentType = "application/x-www-form-urlencoded";
159164
tokenRequest.Accept = "Accept=text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
160-
byte[] _byteVersion = Encoding.ASCII.GetBytes(tokenRequestBody);
161-
tokenRequest.ContentLength = _byteVersion.Length;
162-
Stream stream = tokenRequest.GetRequestStream();
163-
await stream.WriteAsync(_byteVersion, 0, _byteVersion.Length);
164-
stream.Close();
165+
byte[] tokenRequestBodyBytes = Encoding.ASCII.GetBytes(tokenRequestBody);
166+
tokenRequest.ContentLength = tokenRequestBodyBytes.Length;
167+
using (Stream requestStream = tokenRequest.GetRequestStream())
168+
{
169+
await requestStream.WriteAsync(tokenRequestBodyBytes, 0, tokenRequestBodyBytes.Length);
170+
}
165171

166172
try
167173
{
@@ -176,8 +182,8 @@ async void performCodeExchange(string code, string code_verifier, string redirec
176182
// converts to dictionary
177183
Dictionary<string, string> tokenEndpointDecoded = JsonConvert.DeserializeObject<Dictionary<string, string>>(responseText);
178184

179-
string access_token = tokenEndpointDecoded["access_token"];
180-
userinfoCall(access_token);
185+
string accessToken = tokenEndpointDecoded["access_token"];
186+
await RequestUserInfoAsync(accessToken);
181187
}
182188
}
183189
catch (WebException ex)
@@ -187,31 +193,30 @@ async void performCodeExchange(string code, string code_verifier, string redirec
187193
var response = ex.Response as HttpWebResponse;
188194
if (response != null)
189195
{
190-
output("HTTP: " + response.StatusCode);
196+
Log("HTTP: " + response.StatusCode);
191197
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
192198
{
193199
// reads response body
194200
string responseText = await reader.ReadToEndAsync();
195-
output(responseText);
201+
Log(responseText);
196202
}
197203
}
198204

199205
}
200206
}
201207
}
202208

203-
204-
async void userinfoCall(string access_token)
209+
private async Task RequestUserInfoAsync(string accessToken)
205210
{
206-
output("Making API Call to Userinfo...");
211+
Log("Making API Call to Userinfo...");
207212

208213
// builds the request
209-
string userinfoRequestURI = "https://www.googleapis.com/oauth2/v3/userinfo";
214+
string userinfoRequestUri = "https://www.googleapis.com/oauth2/v3/userinfo";
210215

211216
// sends the request
212-
HttpWebRequest userinfoRequest = (HttpWebRequest)WebRequest.Create(userinfoRequestURI);
217+
HttpWebRequest userinfoRequest = (HttpWebRequest)WebRequest.Create(userinfoRequestUri);
213218
userinfoRequest.Method = "GET";
214-
userinfoRequest.Headers.Add(string.Format("Authorization: Bearer {0}", access_token));
219+
userinfoRequest.Headers.Add(string.Format("Authorization: Bearer {0}", accessToken));
215220
userinfoRequest.ContentType = "application/x-www-form-urlencoded";
216221
userinfoRequest.Accept = "Accept=text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
217222

@@ -221,15 +226,15 @@ async void userinfoCall(string access_token)
221226
{
222227
// reads response body
223228
string userinfoResponseText = await userinfoResponseReader.ReadToEndAsync();
224-
output(userinfoResponseText);
229+
Log(userinfoResponseText);
225230
}
226231
}
227232

228233
///
229234
/// Appends the given string to the on-screen log, and the debug console.
230235
///
231-
/// string to be appended
232-
public void output(string output)
236+
/// String to be logged
237+
private void Log(string output)
233238
{
234239
Console.WriteLine(output);
235240
}
@@ -239,32 +244,32 @@ public void output(string output)
239244
///
240245
/// Input length (nb. output will be longer)
241246
///
242-
public static string randomDataBase64url(uint length)
247+
private static string GenerateRandomDataBase64url(uint length)
243248
{
244249
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
245250
byte[] bytes = new byte[length];
246251
rng.GetBytes(bytes);
247-
return base64urlencodeNoPadding(bytes);
252+
return Base64UrlEncodeNoPadding(bytes);
248253
}
249254

250255
///
251-
/// Returns the SHA256 hash of the input string.
256+
/// Returns the SHA256 hash of the input string, which is assumed to be ASCII.
252257
///
253-
///
254-
///
255-
public static byte[] sha256(string inputStirng)
258+
private static byte[] Sha256Ascii(string text)
256259
{
257-
byte[] bytes = Encoding.ASCII.GetBytes(inputStirng);
258-
SHA256Managed sha256 = new SHA256Managed();
259-
return sha256.ComputeHash(bytes);
260+
byte[] bytes = Encoding.ASCII.GetBytes(text);
261+
using (SHA256Managed sha256 = new SHA256Managed())
262+
{
263+
return sha256.ComputeHash(bytes);
264+
}
260265
}
261266

262267
///
263268
/// Base64url no-padding encodes the given input buffer.
264269
///
265270
///
266271
///
267-
public static string base64urlencodeNoPadding(byte[] buffer)
272+
private static string Base64UrlEncodeNoPadding(byte[] buffer)
268273
{
269274
string base64 = Convert.ToBase64String(buffer);
270275

0 commit comments

Comments
 (0)