Skip to content

Commit

Permalink
Merge pull request #44 from granthoff1107/master
Browse files Browse the repository at this point in the history
OnRefresh callback support.
  • Loading branch information
bchavez authored Nov 21, 2018
2 parents aa45138 + 71a3883 commit b1ba217
Show file tree
Hide file tree
Showing 9 changed files with 119 additions and 22 deletions.
3 changes: 2 additions & 1 deletion Source/Coinbase.Tests/Coinbase.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@
<Compile Include="CoinbaseApiKeyTests.cs" />
<Compile Include="Endpoints\AccountTests.cs" />
<Compile Include="Endpoints\NotificationTests.cs" />
<Compile Include="Endpoints\OAuthHelperTests.cs" />
<Compile Include="OAuthTests\OAuthHelperTests.cs" />
<Compile Include="Integration\DataTests.cs" />
<Compile Include="Endpoints\PaymentMethodTest.cs" />
<Compile Include="Endpoints\WithdrawlTests.cs" />
Expand All @@ -86,6 +86,7 @@
<Compile Include="Examples.cs" />
<Compile Include="ExtensionsForTesting.cs" />
<Compile Include="Integration\IntegrationTests.cs" />
<Compile Include="OAuthTests\TokenTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ServerTest.cs" />
<Compile Include="Endpoints\UserTests.cs" />
Expand Down
4 changes: 2 additions & 2 deletions Source/Coinbase.Tests/Examples.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public static string UserWithNameChange(string name)
"wallet:user:read",
"wallet:user:email"
},
OauthMeta = new Dictionary<string, JToken>()
OAuthMeta = new Dictionary<string, JToken>()
};

public const string Account1 = @"{
Expand Down Expand Up @@ -1494,4 +1494,4 @@ public static string Address1WithName(string name)
}


}
}
4 changes: 2 additions & 2 deletions Source/Coinbase.Tests/Integration/IntegrationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,8 @@ public async Task convert_code_to_token()
[Test]
public async Task run_expired_token()
{
this.client = new CoinbaseClient(new OAuthConfig { AccessToken = secrets.OAuthAccessToken })
.WithAutomaticOAuthTokenRefresh(secrets.OAuthClientId, secrets.OAuthClientSecret, secrets.OAuthRefreshToken);
this.client = new CoinbaseClient(new OAuthConfig { AccessToken = secrets.OAuthAccessToken, RefreshToken = secrets.OAuthRefreshToken })
.WithAutomaticOAuthTokenRefresh(secrets.OAuthClientId, secrets.OAuthClientSecret);

var authInfo = await this.client.Users.GetAuthInfoAsync();
authInfo.Dump();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
using System;
using System.Globalization;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using FluentAssertions;
using Flurl;
using Flurl.Http;
using Flurl.Http.Testing;
using NUnit.Framework;
using Z.ExtensionMethods;

namespace Coinbase.Tests.Endpoints
namespace Coinbase.Tests.OAuthTests
{
public class OAuthHelperTests
{
Expand Down
90 changes: 90 additions & 0 deletions Source/Coinbase.Tests/OAuthTests/TokenTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using FluentAssertions;
using NUnit.Framework;

namespace Coinbase.Tests.OAuthTests
{
public class TokenTests : OAuthServerTest
{

[Test]
public async Task auto_refresh_token()
{
//simulate the expired response.
var expiredResponse = @"{""errors"":[{""id"":""expired_token"",""message"":""The access token is expired""}]}";
server.RespondWith(expiredResponse, status:401);


//simulate the refresh response.
var refreshResponse = @"{
""access_token"":""aaa"",
""token_type"":""bearer"",
""expires_in"":7200,
""refresh_token"":""bbb"",
""scope"":""all"",
""created_at"":1542649114
}";
server.RespondWith(refreshResponse, status: 200);

//simulate the actual successful response after token refresh.
SetupServerSingleResponse(Examples.User);

//enable automatic refresh
client.WithAutomaticOAuthTokenRefresh("clientId", "clientSecret");

var response = await client.Users.GetCurrentUserAsync();

response.Dump();

var config = client.Config as OAuthConfig;
config.AccessToken.Should().Be("aaa");
config.RefreshToken.Should().Be("bbb");

Examples.UserModel.Should().BeEquivalentTo(response.Data);
}

[Test]
public async Task auto_refresh_token_with_callback()
{
//simulate the expired response.
var expiredResponse = @"{""errors"":[{""id"":""expired_token"",""message"":""The access token is expired""}]}";
server.RespondWith(expiredResponse, status: 401);

//simulate the refresh response.
var refreshResponse = @"{
""access_token"":""aaa"",
""token_type"":""bearer"",
""expires_in"":7200,
""refresh_token"":""bbb"",
""scope"":""all"",
""created_at"":1542649114
}";
server.RespondWith(refreshResponse, status: 200);

//simulate the actual successful response after token refresh.
SetupServerSingleResponse(Examples.User);

//enable automatic refresh
client.WithAutomaticOAuthTokenRefresh("clientId", "clientSecret", o =>
{
o.AccessToken.Should().Be("aaa");
o.RefreshToken.Should().Be("bbb");
return Task.FromResult(0);
});

var response = await client.Users.GetCurrentUserAsync();

response.Dump();

var config = client.Config as OAuthConfig;
config.AccessToken.Should().Be("aaa");
config.RefreshToken.Should().Be("bbb");

Examples.UserModel.Should().BeEquivalentTo(response.Data);
}

}
}
23 changes: 18 additions & 5 deletions Source/Coinbase/CoinbaseClient.OAuth.cs
Original file line number Diff line number Diff line change
Expand Up @@ -188,9 +188,17 @@ public static class AutoRefreshTokenHelper
{
public const string ExpiredToken = "expired_token";

public static CoinbaseClient WithAutomaticOAuthTokenRefresh(this CoinbaseClient client, string clientId, string clientSecret, string refreshToken)
/// <summary>
/// Setup the CoinbaseClient to use automatic token refresh.
/// </summary>
/// <param name="clientId">The OAuth Application Client ID</param>
/// <param name="clientSecret">The OAuth Application Secret</param>
/// <param name="onRefresh">Callback function to invoke when the OAuth token is refreshed.</param>
public static CoinbaseClient WithAutomaticOAuthTokenRefresh(this CoinbaseClient client, string clientId, string clientSecret, Func<OAuthResponse, Task> onRefresh = null)
{
if (client.Config is OAuthConfig config) { }
if( client.Config is OAuthConfig config )
{
}
else throw new InvalidOperationException($"Client must be using an {nameof(OAuthConfig)}");

async Task TokenExpiredErrorHandler(HttpCall call)
Expand All @@ -201,9 +209,14 @@ async Task TokenExpiredErrorHandler(HttpCall call)
var errorResponse = await ex.GetResponseJsonAsync<JsonResponse>().ConfigureAwait(false);
if (errorResponse.Errors.Any(x => x.Id == ExpiredToken))
{
var refresh = await OAuthHelper.RenewAccessAsync(refreshToken, clientId, clientSecret).ConfigureAwait(false);
config.AccessToken = refresh.AccessToken;
refreshToken = refresh.RefreshToken;
var refreshResponse = await OAuthHelper.RenewAccessAsync(config.RefreshToken, clientId, clientSecret).ConfigureAwait(false);
config.AccessToken = refreshResponse.AccessToken;
config.RefreshToken = refreshResponse.RefreshToken;

if( onRefresh is null )
{
}
else await onRefresh(refreshResponse).ConfigureAwait(false);

call.Response = await call.FlurlRequest.SendAsync(call.Request.Method, call.Request.Content).ConfigureAwait(false);
call.ExceptionHandled = true;
Expand Down
8 changes: 1 addition & 7 deletions Source/Coinbase/CoinbaseClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,10 @@ public partial class CoinbaseClient : FlurlClient, ICoinbaseClient
protected internal Url TimeEndpoint => this.Config.ApiUrl.AppendPathSegment("time");
protected internal Url NotificationsEndpoint => this.Config.ApiUrl.AppendPathSegment("notifications");

public CoinbaseClient(OAuthConfig config): this(config as Config){}

public CoinbaseClient(ApiKeyConfig config) : this(config as Config){}

public CoinbaseClient() : this(null as Config){}

/// <summary>
/// The main class for making Coinbase API calls.
/// </summary>
protected CoinbaseClient(Config config)
public CoinbaseClient(Config config = null)
{
this.Config = config ?? new Config();
this.Config.EnsureValid();
Expand Down
1 change: 1 addition & 0 deletions Source/Coinbase/Config.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ protected internal virtual void Configure(CoinbaseClient client)
public class OAuthConfig : Config
{
public string AccessToken { get; set; }
public string RefreshToken { get; set; }

internal override void EnsureValid()
{
Expand Down
4 changes: 2 additions & 2 deletions Source/Coinbase/Models/Objects.cs
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,7 @@ public partial class Auth : Json
public string[] Scopes { get; set; }

[JsonProperty("oauth_meta")]
public IDictionary<string, JToken> OauthMeta { get; set; }
public IDictionary<string, JToken> OAuthMeta { get; set; }
}


Expand Down Expand Up @@ -680,4 +680,4 @@ public partial class Notification : Entity
}


}
}

0 comments on commit b1ba217

Please sign in to comment.