Skip to content

Commit

Permalink
Added support for proxy configs in auth flows, closes #360
Browse files Browse the repository at this point in the history
  • Loading branch information
JohnnyCrazy committed Jul 17, 2019
1 parent 0a9499c commit 4933dea
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 45 deletions.
5 changes: 4 additions & 1 deletion SpotifyAPI.Web.Auth/AuthorizationCodeAuth.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ namespace SpotifyAPI.Web.Auth
public class AuthorizationCodeAuth : SpotifyAuthServer<AuthorizationCode>
{
public string SecretId { get; set; }

public ProxyConfig ProxyConfig { get; set; }

public AuthorizationCodeAuth(string redirectUri, string serverUri, Scope scope = Scope.None, string state = "")
: base("code", "AuthorizationCodeAuth", redirectUri, serverUri, scope, state)
Expand Down Expand Up @@ -53,7 +55,8 @@ public async Task<Token> RefreshToken(string refreshToken)
new KeyValuePair<string, string>("refresh_token", refreshToken)
};

HttpClient client = new HttpClient();
HttpClientHandler handler = ProxyConfig.CreateClientHandler(ProxyConfig);
HttpClient client = new HttpClient(handler);
client.DefaultRequestHeaders.Add("Authorization", GetAuthHeader());
HttpContent content = new FormUrlEncodedContent(args);

Expand Down
5 changes: 4 additions & 1 deletion SpotifyAPI.Web.Auth/CredentialsAuth.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ public class CredentialsAuth
public string ClientSecret { get; set; }

public string ClientId { get; set; }

public ProxyConfig ProxyConfig { get; set; }

public CredentialsAuth(string clientId, string clientSecret)
{
Expand All @@ -29,7 +31,8 @@ public async Task<Token> GetToken()
new KeyValuePair<string, string>("grant_type", "client_credentials")
};

HttpClient client = new HttpClient();
HttpClientHandler handler = ProxyConfig.CreateClientHandler(ProxyConfig);
HttpClient client = new HttpClient(handler);
client.DefaultRequestHeaders.Add("Authorization", $"Basic {auth}");
HttpContent content = new FormUrlEncodedContent(args);

Expand Down
57 changes: 34 additions & 23 deletions SpotifyAPI.Web.Auth/TokenSwapAuth.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,30 +22,34 @@ namespace SpotifyAPI.Web.Auth
/// </summary>
public class TokenSwapAuth : SpotifyAuthServer<AuthorizationCode>
{
string exchangeServerUri;
readonly string _exchangeServerUri;

/// <summary>
/// The HTML to respond with when the callback server (serverUri) is reached. The default value will close the window on arrival.
/// </summary>
public string HtmlResponse { get; set; } = "<script>window.close();</script>";

/// <summary>
/// If true, will time how long it takes for access to expire. On expiry, the <see cref="OnAccessTokenExpired"/> event fires.
/// </summary>
public bool TimeAccessExpiry { get; set; }

public ProxyConfig ProxyConfig { get; set; }

/// <param name="exchangeServerUri">The URI to an exchange server that will perform the key exchange.</param>
/// <param name="serverUri">The URI to host the server at that your exchange server should return the authorization code to by GET request. (e.g. http://localhost:4002)</param>
/// <param name="scope"></param>
/// <param name="state">Stating none will randomly generate a state parameter.</param>
/// <param name="htmlResponse">The HTML to respond with when the callback server (serverUri) is reached. The default value will close the window on arrival.</param>
public TokenSwapAuth(string exchangeServerUri, string serverUri, Scope scope = Scope.None, string state = "", string htmlResponse = "") : base("code", "", "", serverUri, scope, state)
public TokenSwapAuth(string exchangeServerUri, string serverUri, Scope scope = Scope.None, string state = "",
string htmlResponse = "") : base("code", "", "", serverUri, scope, state)
{
if (!string.IsNullOrEmpty(htmlResponse))
{
HtmlResponse = htmlResponse;
}

this.exchangeServerUri = exchangeServerUri;
_exchangeServerUri = exchangeServerUri;
}

protected override void AdaptWebServer(WebServer webServer)
Expand All @@ -55,7 +59,7 @@ protected override void AdaptWebServer(WebServer webServer)

public override string GetUri()
{
StringBuilder builder = new StringBuilder(exchangeServerUri);
StringBuilder builder = new StringBuilder(_exchangeServerUri);
builder.Append("?");
builder.Append("response_type=code");
builder.Append("&state=" + State);
Expand All @@ -64,8 +68,6 @@ public override string GetUri()
return Uri.EscapeUriString(builder.ToString());
}

static readonly HttpClient httpClient = new HttpClient();

/// <summary>
/// The maximum amount of times to retry getting a token.
/// <para/>
Expand All @@ -85,26 +87,32 @@ public override string GetUri()
/// <param name="refreshToken">This needs to be defined if "grantType" is "refresh_token".</param>
/// <param name="currentRetries">Does not need to be defined. Used internally for retry attempt recursion.</param>
/// <returns>Attempts to return a full <see cref="Token"/>, but after retry attempts, may return a <see cref="Token"/> with no <see cref="Token.AccessToken"/>, or null.</returns>
async Task<Token> GetToken(string grantType, string authorizationCode = "", string refreshToken = "", int currentRetries = 0)
async Task<Token> GetToken(string grantType, string authorizationCode = "", string refreshToken = "",
int currentRetries = 0)
{
var content = new FormUrlEncodedContent(new Dictionary<string, string>
{
{ "grant_type", grantType },
{ "code", authorizationCode },
{ "refresh_token", refreshToken }
});
FormUrlEncodedContent content = new FormUrlEncodedContent(new Dictionary<string, string>
{
{"grant_type", grantType},
{"code", authorizationCode},
{"refresh_token", refreshToken}
});

try
{
var siteResponse = await httpClient.PostAsync(exchangeServerUri, content);
HttpClientHandler handler = ProxyConfig.CreateClientHandler(ProxyConfig);
HttpClient client = new HttpClient(handler);
HttpResponseMessage siteResponse = await client.PostAsync(_exchangeServerUri, content);

Token token = JsonConvert.DeserializeObject<Token>(await siteResponse.Content.ReadAsStringAsync());
// Don't need to check if it was null - if it is, it will resort to the catch block.
if (!token.HasError() && !string.IsNullOrEmpty(token.AccessToken))
{
return token;
}
}
catch { }
catch
{
}

if (currentRetries >= MaxGetTokenRetries)
{
Expand All @@ -120,7 +128,8 @@ async Task<Token> GetToken(string grantType, string authorizationCode = "", stri
}
}

System.Timers.Timer accessTokenExpireTimer;
System.Timers.Timer _accessTokenExpireTimer;

/// <summary>
/// When Spotify authorization has expired. Will only trigger if <see cref="TimeAccessExpiry"/> is true.
/// </summary>
Expand All @@ -134,19 +143,19 @@ void SetAccessExpireTimer(Token token)
{
if (!TimeAccessExpiry) return;

if (accessTokenExpireTimer != null)
if (_accessTokenExpireTimer != null)
{
accessTokenExpireTimer.Stop();
accessTokenExpireTimer.Dispose();
_accessTokenExpireTimer.Stop();
_accessTokenExpireTimer.Dispose();
}

accessTokenExpireTimer = new System.Timers.Timer
_accessTokenExpireTimer = new System.Timers.Timer
{
Enabled = true,
Interval = token.ExpiresIn * 1000,
AutoReset = false
};
accessTokenExpireTimer.Elapsed += (sender, e) => OnAccessTokenExpired?.Invoke(this, EventArgs.Empty);
_accessTokenExpireTimer.Elapsed += (sender, e) => OnAccessTokenExpired?.Invoke(this, EventArgs.Empty);
}

/// <summary>
Expand All @@ -161,6 +170,7 @@ public async Task<Token> ExchangeCodeAsync(string authorizationCode)
{
SetAccessExpireTimer(token);
}

return token;
}

Expand All @@ -176,6 +186,7 @@ public async Task<Token> RefreshAuthAsync(string refreshToken)
{
SetAccessExpireTimer(token);
}

return token;
}
}
Expand Down Expand Up @@ -204,7 +215,7 @@ public Task<bool> GetAuth()
Code = code,
Error = error
}));
return HttpContext.HtmlResponseAsync(((TokenSwapAuth)auth).HtmlResponse);
return HttpContext.HtmlResponseAsync(((TokenSwapAuth) auth).HtmlResponse);
}
}
}
}
20 changes: 20 additions & 0 deletions SpotifyAPI.Web/ProxyConfig.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Net;
using System.Net.Http;

namespace SpotifyAPI.Web
{
Expand Down Expand Up @@ -73,5 +74,24 @@ public WebProxy CreateWebProxy()

return proxy;
}

public static HttpClientHandler CreateClientHandler(ProxyConfig proxyConfig = null)
{
HttpClientHandler clientHandler = new HttpClientHandler
{
PreAuthenticate = false,
UseDefaultCredentials = true,
UseProxy = false
};

if (string.IsNullOrWhiteSpace(proxyConfig?.Host)) return clientHandler;
WebProxy proxy = proxyConfig.CreateWebProxy();
clientHandler.UseProxy = true;
clientHandler.Proxy = proxy;
clientHandler.UseDefaultCredentials = proxy.UseDefaultCredentials;
clientHandler.PreAuthenticate = proxy.UseDefaultCredentials;

return clientHandler;
}
}
}
21 changes: 1 addition & 20 deletions SpotifyAPI.Web/SpotifyWebClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ internal class SpotifyWebClient : IClient

public SpotifyWebClient(ProxyConfig proxyConfig = null)
{
HttpClientHandler clientHandler = CreateClientHandler(proxyConfig);
HttpClientHandler clientHandler = ProxyConfig.CreateClientHandler(proxyConfig);
_client = new HttpClient(clientHandler);
}

Expand Down Expand Up @@ -200,24 +200,5 @@ private void AddHeaders(Dictionary<string,string> headers)
_client.DefaultRequestHeaders.TryAddWithoutValidation(headerPair.Key, headerPair.Value);
}
}

private static HttpClientHandler CreateClientHandler(ProxyConfig proxyConfig = null)
{
HttpClientHandler clientHandler = new HttpClientHandler
{
PreAuthenticate = false,
UseDefaultCredentials = true,
UseProxy = false
};

if (string.IsNullOrWhiteSpace(proxyConfig?.Host)) return clientHandler;
WebProxy proxy = proxyConfig.CreateWebProxy();
clientHandler.UseProxy = true;
clientHandler.Proxy = proxy;
clientHandler.UseDefaultCredentials = proxy.UseDefaultCredentials;
clientHandler.PreAuthenticate = proxy.UseDefaultCredentials;

return clientHandler;
}
}
}

0 comments on commit 4933dea

Please sign in to comment.