htmlWriter = delegate (TextWriter textWriter)
+ {
+ textWriter.WriteLine("Token retrieval completed successfully!
");
+ textWriter.WriteLine($"Access token: {Credentials.AccessToken}
");
+ textWriter.WriteLine($"Refresh token: {Credentials.RefreshToken}
");
+ textWriter.WriteLine($"Tokens saved to: {CredentialsJsonPath}
");
+ };
+ return new WebServer(o => o
+ .WithUrlPrefix(_baseUrl)
+ .WithMode(HttpListenerMode.EmbedIO))
+ .WithLocalSessionManager()
+ .WithModule(new ActionModule("/Reddit.NET/oauthRedirect", HttpVerbs.Any, ctx =>
+ {
+ Credentials = RetrieveToken(ctx.GetRequestQueryData()).Result;
+ CredentialsJsonPath = WriteCredentialsToJson(Credentials);
+ _completedAuthCallback?.Invoke(Credentials);
+ return ctx.SendStandardHtmlAsync(200, htmlWriter);
+ }));
+ }
+
+ private async Task RetrieveToken(NameValueCollection queryData)
+ {
+ if (!string.IsNullOrWhiteSpace(queryData["error"]))
+ {
+ throw new Exception($"Reddit returned error regarding authorisation. Error value: {queryData["error"]}");
+ }
+
+ if (queryData["state"] != _state)
+ {
+ throw new Exception($"State returned by Reddit does not match state sent.");
+ }
+
+ _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes($"{_appId}:{_appSecret}")));
+ string code = queryData["code"];
+ var tokenRequestData = new Dictionary()
+ {
+ { "grant_type", "authorization_code" },
+ { "code", code },
+ { "redirect_uri", _redirectUrl }
+ };
+ HttpResponseMessage tokenResponse = await _httpClient.PostAsync("https://www.reddit.com/api/v1/access_token", new FormUrlEncodedContent(tokenRequestData));
+ if (!tokenResponse.IsSuccessStatusCode)
+ {
+ throw new Exception("Reddit returned non-success status code when getting access token.");
+ }
+ string tokenResponseContent = await tokenResponse.Content.ReadAsStringAsync();
+ if (tokenResponseContent.Contains("error"))
+ {
+ throw new Exception($"Reddit returned error when getting access token. JSON response: {tokenResponseContent}");
+ }
+ var credentials = JsonConvert.DeserializeObject(tokenResponseContent);
+ return credentials;
+ }
+
+ private string WriteCredentialsToJson(OAuthToken oAuthToken)
+ {
+ string fileExt = "." + _appId + "." + (!string.IsNullOrWhiteSpace(_appSecret) ? _appSecret + "." : "") + "json";
+
+ string tokenPath = Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar
+ + "RDNOauthToken_" + DateTime.Now.ToString("yyyyMMddHHmmssffff") + fileExt;
+
+ File.WriteAllText(tokenPath, JsonConvert.SerializeObject(oAuthToken));
+ return tokenPath;
+ }
+
+ public void Dispose()
+ {
+ _webServer.Dispose();
+ _textWriter.Dispose();
+ _memoryStream.Dispose();
+ }
+ }
+
+ [Obsolete("This class has been deprecated in favour of " + nameof(AuthTokenRetrieverServer) + ".")]
public class AuthTokenRetrieverLib
{
///
@@ -21,7 +169,6 @@ public class AuthTokenRetrieverLib
internal string AppId
{
get;
- private set;
}
///
@@ -30,7 +177,6 @@ internal string AppId
internal string AppSecret
{
get;
- private set;
}
///
@@ -39,10 +185,9 @@ internal string AppSecret
internal int Port
{
get;
- private set;
}
- internal HttpServer HttpServer
+ internal AuthTokenRetrieverServer AuthServer
{
get;
private set;
@@ -50,14 +195,18 @@ internal HttpServer HttpServer
public string AccessToken
{
- get;
- private set;
+ get
+ {
+ return AuthServer.Credentials.AccessToken;
+ }
}
public string RefreshToken
{
- get;
- private set;
+ get
+ {
+ return AuthServer.Credentials.RefreshToken;
+ }
}
///
@@ -76,94 +225,18 @@ public AuthTokenRetrieverLib(string appId = null, string appSecret = null, int p
public void AwaitCallback()
{
- using (HttpServer = new HttpServer(new HttpRequestProvider()))
- {
- HttpServer.Use(new TcpListenerAdapter(new TcpListener(IPAddress.Loopback, Port)));
-
- HttpServer.Use((context, next) =>
- {
- string code = null;
- string state = null;
- try
- {
- code = context.Request.QueryString.GetByName("code");
- state = context.Request.QueryString.GetByName("state"); // This app formats state as: AppId + ":" [+ AppSecret]
- }
- catch (KeyNotFoundException)
- {
- context.Response = new uhttpsharp.HttpResponse(HttpResponseCode.Ok, Encoding.UTF8.GetBytes("ERROR: No code and/or state received!"), false);
- throw new Exception("ERROR: Request received without code and/or state!");
- }
-
- if (!string.IsNullOrWhiteSpace(code)
- && !string.IsNullOrWhiteSpace(state))
- {
- // Send request with code and JSON-decode the return for token retrieval. --Kris
- RestRequest restRequest = new RestRequest("/api/v1/access_token", Method.POST);
-
- restRequest.AddHeader("Authorization", "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes(state)));
- restRequest.AddHeader("Content-Type", "application/x-www-form-urlencoded");
-
- restRequest.AddParameter("grant_type", "authorization_code");
- restRequest.AddParameter("code", code);
- restRequest.AddParameter("redirect_uri",
- "http://localhost:" + Port.ToString() + "/Reddit.NET/oauthRedirect"); // This must be an EXACT match in the app settings on Reddit! --Kris
-
- OAuthToken oAuthToken = JsonConvert.DeserializeObject(ExecuteRequest(restRequest));
-
- AccessToken = oAuthToken.AccessToken;
- RefreshToken = oAuthToken.RefreshToken;
-
- string[] sArr = state.Split(':');
- if (sArr == null || sArr.Length == 0)
- {
- throw new Exception("State must consist of 'appId:appSecret'!");
- }
-
- string appId = sArr[0];
- string appSecret = (sArr.Length >= 2 ? sArr[1] : null);
-
- string fileExt = "." + appId + "." + (!string.IsNullOrWhiteSpace(appSecret) ? appSecret + "." : "") + "json";
-
- string tokenPath = Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar
- + "RDNOauthToken_" + DateTime.Now.ToString("yyyyMMddHHmmssffff") + fileExt;
-
- File.WriteAllText(tokenPath, JsonConvert.SerializeObject(oAuthToken));
-
- string html;
- using (Stream stream = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream("AuthTokenRetrieverLib.Templates.Success.html"))
- {
- using (StreamReader streamReader = new StreamReader(stream))
- {
- html = streamReader.ReadToEnd();
- }
- }
-
- html = html.Replace("REDDIT_OAUTH_ACCESS_TOKEN", oAuthToken.AccessToken);
- html = html.Replace("REDDIT_OAUTH_REFRESH_TOKEN", oAuthToken.RefreshToken);
- html = html.Replace("LOCAL_TOKEN_PATH", tokenPath);
-
- context.Response = new uhttpsharp.HttpResponse(HttpResponseCode.Ok, Encoding.UTF8.GetBytes(html), false);
- }
-
- return Task.Factory.GetCompleted();
- });
-
- HttpServer.Start();
- }
+ AuthServer = new AuthTokenRetrieverServer(AppId, AppSecret, Port);
}
public void StopListening()
{
- HttpServer.Dispose();
+ AuthServer.Dispose();
}
public string AuthURL(string scope = "creddits%20modcontributors%20modmail%20modconfig%20subscribe%20structuredstyles%20vote%20wikiedit%20mysubreddits%20submit%20modlog%20modposts%20modflair%20save%20modothers%20read%20privatemessages%20report%20identity%20livemanage%20account%20modtraffic%20wikiread%20edit%20modwiki%20modself%20history%20flair")
{
- return "https://www.reddit.com/api/v1/authorize?client_id=" + AppId + "&response_type=code"
- + "&state=" + AppId + ":" + AppSecret
- + "&redirect_uri=http://localhost:" + Port.ToString() + "/Reddit.NET/oauthRedirect&duration=permanent"
- + "&scope=" + scope;
+ AuthServer.Scope = scope;
+ return AuthServer.AuthorisationUrl;
}
public string ExecuteRequest(RestRequest restRequest)
diff --git a/src/AuthTokenRetrieverLib/AuthTokenRetrieverLib.csproj b/src/AuthTokenRetrieverLib/AuthTokenRetrieverLib.csproj
index dde35dd6..984fc2c8 100644
--- a/src/AuthTokenRetrieverLib/AuthTokenRetrieverLib.csproj
+++ b/src/AuthTokenRetrieverLib/AuthTokenRetrieverLib.csproj
@@ -26,17 +26,9 @@
-
-
-
-
-
-
-
-
+
-
diff --git a/src/AuthTokenRetrieverLib/Templates/Success.html b/src/AuthTokenRetrieverLib/Templates/Success.html
deleted file mode 100644
index 5c88be14..00000000
--- a/src/AuthTokenRetrieverLib/Templates/Success.html
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
- Token Retrieval Successful!
-
-
- Token retrieval completed successfully!
-
-
-
- | Access Token: |
- |
- REDDIT_OAUTH_ACCESS_TOKEN |
-
-
- | Refresh Token: |
- |
- REDDIT_OAUTH_REFRESH_TOKEN |
-
-
-
-
-
-
- Token Saved to: LOCAL_TOKEN_PATH
-
-
-
-
- You may now close this window whenever you're ready.
-
-