Skip to content
This repository has been archived by the owner on Nov 19, 2024. It is now read-only.

Commit

Permalink
Merge pull request #46 from hybrid2102/refreshtoken-after-refresh
Browse files Browse the repository at this point in the history
after refreshing the token, if the response does not contain a new Re…
  • Loading branch information
josephdecock authored May 5, 2024
2 parents 5d114aa + b5eaa59 commit f398940
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ public async Task<UserToken> RefreshAccessTokenAsync(
token.Expiration = response.ExpiresIn == 0
? DateTimeOffset.MaxValue
: DateTimeOffset.UtcNow.AddSeconds(response.ExpiresIn);
token.RefreshToken = response.RefreshToken;
token.RefreshToken = response.RefreshToken ?? userToken.RefreshToken;
token.Scope = response.Scope;
}

Expand Down
126 changes: 87 additions & 39 deletions test/Tests/UserTokenManagementTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public class UserTokenManagementTests : IntegrationTestBase
{
public UserTokenManagementTests() : base("web")
{ }

[Fact]
public async Task Anonymous_user_should_return_user_token_error()
{
Expand All @@ -21,7 +21,7 @@ public async Task Anonymous_user_should_return_user_token_error()

token!.IsError.ShouldBeTrue();
}

[Fact]
public async Task Anonymous_user_should_return_client_token()
{
Expand All @@ -31,7 +31,7 @@ public async Task Anonymous_user_should_return_client_token()
token!.AccessToken.ShouldNotBeNull();
token.AccessTokenType.ShouldBe("Bearer");
token.Expiration.ShouldNotBe(DateTimeOffset.MaxValue);

token.IsError.ShouldBeFalse();
}

Expand All @@ -40,7 +40,7 @@ public async Task Standard_initial_token_response_should_return_expected_values(
{
var mockHttp = new MockHttpMessageHandler();
AppHost.IdentityServerHttpHandler = mockHttp;

var initialTokenResponse = new
{
id_token = IdentityServerHost.CreateIdToken("1", "web"),
Expand All @@ -49,12 +49,12 @@ public async Task Standard_initial_token_response_should_return_expected_values(
expires_in = 3600,
refresh_token = "initial_refresh_token",
};

// response for re-deeming code
mockHttp.When("/connect/token")
.WithFormData("grant_type", "authorization_code")
.Respond("application/json", JsonSerializer.Serialize(initialTokenResponse));

await AppHost.InitializeAsync();
await AppHost.LoginAsync("alice");

Expand All @@ -68,7 +68,7 @@ public async Task Standard_initial_token_response_should_return_expected_values(
token.AccessTokenType.ShouldBe("token_type");
token.RefreshToken.ShouldBe("initial_refresh_token");
token.Expiration.ShouldNotBe(DateTimeOffset.MaxValue);

// 2nd request should not trigger a token request
response = await AppHost.BrowserClient.GetAsync(AppHost.Url("/user_token"));
token = await response.Content.ReadFromJsonAsync<UserToken>();
Expand All @@ -80,26 +80,26 @@ public async Task Standard_initial_token_response_should_return_expected_values(
token.RefreshToken.ShouldBe("initial_refresh_token");
token.Expiration.ShouldNotBe(DateTimeOffset.MaxValue);
}

[Fact]
public async Task Missing_expires_in_should_result_in_long_lived_token()
{
var mockHttp = new MockHttpMessageHandler();
AppHost.IdentityServerHttpHandler = mockHttp;

var initialTokenResponse = new
{
id_token = IdentityServerHost.CreateIdToken("1", "web"),
access_token = "initial_access_token",
token_type = "token_type",
refresh_token = "initial_refresh_token",
};

// response for re-deeming code
mockHttp.When("/connect/token")
.WithFormData("grant_type", "authorization_code")
.Respond("application/json", JsonSerializer.Serialize(initialTokenResponse));

await AppHost.InitializeAsync();
await AppHost.LoginAsync("alice");

Expand All @@ -113,26 +113,26 @@ public async Task Missing_expires_in_should_result_in_long_lived_token()
token.RefreshToken.ShouldBe("initial_refresh_token");
token.Expiration.ShouldBe(DateTimeOffset.MaxValue);
}

[Fact]
public async Task Missing_initial_refresh_token_response_should_return_access_token()
{
var mockHttp = new MockHttpMessageHandler();
AppHost.IdentityServerHttpHandler = mockHttp;

var initialTokenResponse = new
{
id_token = IdentityServerHost.CreateIdToken("1", "web"),
access_token = "initial_access_token",
token_type = "token_type",
expires_in = 3600
};

// response for re-deeming code
mockHttp.When("/connect/token")
.WithFormData("grant_type", "authorization_code")
.Respond("application/json", JsonSerializer.Serialize(initialTokenResponse));

await AppHost.InitializeAsync();
await AppHost.LoginAsync("alice");

Expand All @@ -146,26 +146,26 @@ public async Task Missing_initial_refresh_token_response_should_return_access_to
token.RefreshToken.ShouldBeNull();
token.Expiration.ShouldNotBe(DateTimeOffset.MaxValue);
}

[Fact]
public async Task Missing_initial_refresh_token_and_expired_access_token_should_return_initial_access_token()
{
var mockHttp = new MockHttpMessageHandler();
AppHost.IdentityServerHttpHandler = mockHttp;

var initialTokenResponse = new
{
id_token = IdentityServerHost.CreateIdToken("1", "web"),
access_token = "initial_access_token",
token_type = "token_type",
expires_in = 10
};

// response for re-deeming code
mockHttp.When("/connect/token")
.WithFormData("grant_type", "authorization_code")
.Respond("application/json", JsonSerializer.Serialize(initialTokenResponse));

await AppHost.InitializeAsync();
await AppHost.LoginAsync("alice");

Expand All @@ -179,13 +179,13 @@ public async Task Missing_initial_refresh_token_and_expired_access_token_should_
token.RefreshToken.ShouldBeNull();
token.Expiration.ShouldNotBe(DateTimeOffset.MaxValue);
}

[Fact]
public async Task Short_token_lifetime_should_trigger_refresh()
{
var mockHttp = new MockHttpMessageHandler();
AppHost.IdentityServerHttpHandler = mockHttp;

// short token lifetime should trigger refresh on 1st use
var initialTokenResponse = new
{
Expand All @@ -195,12 +195,12 @@ public async Task Short_token_lifetime_should_trigger_refresh()
expires_in = 10,
refresh_token = "initial_refresh_token",
};

// response for re-deeming code
mockHttp.When("/connect/token")
.WithFormData("grant_type", "authorization_code")
.Respond("application/json", JsonSerializer.Serialize(initialTokenResponse));

// short token lifetime should trigger refresh on 1st use
var refreshTokenResponse = new
{
Expand All @@ -209,13 +209,13 @@ public async Task Short_token_lifetime_should_trigger_refresh()
expires_in = 10,
refresh_token = "refreshed1_refresh_token",
};

// response for refresh 1
mockHttp.When("/connect/token")
.WithFormData("grant_type", "refresh_token")
.WithFormData("refresh_token", "initial_refresh_token")
.Respond("application/json", JsonSerializer.Serialize(refreshTokenResponse));

// short token lifetime should trigger refresh on 2nd use
var refreshTokenResponse2 = new
{
Expand All @@ -224,18 +224,18 @@ public async Task Short_token_lifetime_should_trigger_refresh()
expires_in = 3600,
refresh_token = "refreshed2_refresh_token",
};

// response for refresh 1
mockHttp.When("/connect/token")
.WithFormData("grant_type", "refresh_token")
.WithFormData("refresh_token", "refreshed1_refresh_token")
.Respond("application/json", JsonSerializer.Serialize(refreshTokenResponse2));


// setup host
await AppHost.InitializeAsync();
await AppHost.LoginAsync("alice");

// first request should trigger refresh
var response = await AppHost.BrowserClient.GetAsync(AppHost.Url("/user_token"));
var token = await response.Content.ReadFromJsonAsync<UserToken>();
Expand All @@ -246,7 +246,7 @@ public async Task Short_token_lifetime_should_trigger_refresh()
token.AccessTokenType.ShouldBe("token_type1");
token.RefreshToken.ShouldBe("refreshed1_refresh_token");
token.Expiration.ShouldNotBe(DateTimeOffset.MaxValue);

// second request should trigger refresh
response = await AppHost.BrowserClient.GetAsync(AppHost.Url("/user_token"));
token = await response.Content.ReadFromJsonAsync<UserToken>();
Expand All @@ -257,7 +257,7 @@ public async Task Short_token_lifetime_should_trigger_refresh()
token.AccessTokenType.ShouldBe("token_type2");
token.RefreshToken.ShouldBe("refreshed2_refresh_token");
token.Expiration.ShouldNotBe(DateTimeOffset.MaxValue);

// third request should not trigger refresh
response = await AppHost.BrowserClient.GetAsync(AppHost.Url("/user_token"));
token = await response.Content.ReadFromJsonAsync<UserToken>();
Expand All @@ -275,7 +275,7 @@ public async Task Resources_get_distinct_tokens()
{
var mockHttp = new MockHttpMessageHandler();
AppHost.IdentityServerHttpHandler = mockHttp;

// no resource specified
var initialTokenResponse = new
{
Expand All @@ -288,7 +288,7 @@ public async Task Resources_get_distinct_tokens()
mockHttp.When("/connect/token")
.WithFormData("grant_type", "authorization_code")
.Respond("application/json", JsonSerializer.Serialize(initialTokenResponse));

// resource 1 specified
var resource1TokenResponse = new
{
Expand All @@ -301,7 +301,7 @@ public async Task Resources_get_distinct_tokens()
.WithFormData("grant_type", "refresh_token")
.WithFormData("resource", "urn:api1")
.Respond("application/json", JsonSerializer.Serialize(resource1TokenResponse));

// resource 2 specified
var resource2TokenResponse = new
{
Expand All @@ -314,11 +314,11 @@ public async Task Resources_get_distinct_tokens()
.WithFormData("grant_type", "refresh_token")
.WithFormData("resource", "urn:api2")
.Respond("application/json", JsonSerializer.Serialize(resource2TokenResponse));

// setup host
await AppHost.InitializeAsync();
await AppHost.LoginAsync("alice");

// first request - no resource
var response = await AppHost.BrowserClient.GetAsync(AppHost.Url("/user_token"));
var token = await response.Content.ReadFromJsonAsync<UserToken>();
Expand All @@ -328,7 +328,7 @@ public async Task Resources_get_distinct_tokens()
token.AccessToken.ShouldBe("access_token_without_resource");
token.RefreshToken.ShouldBe("initial_refresh_token");
token.Expiration.ShouldNotBe(DateTimeOffset.MaxValue);

// second request - with resource api1
response = await AppHost.BrowserClient.GetAsync(AppHost.Url("/user_token_with_resource/urn:api1"));
token = await response.Content.ReadFromJsonAsync<UserToken>();
Expand All @@ -346,7 +346,55 @@ public async Task Resources_get_distinct_tokens()
token.ShouldNotBeNull();
token.IsError.ShouldBeFalse();
token.AccessToken.ShouldBe("urn:api2_access_token");
token.RefreshToken.ShouldBe("initial_refresh_token");
token.RefreshToken.ShouldBe("initial_refresh_token");
token.Expiration.ShouldNotBe(DateTimeOffset.MaxValue);
}
}

[Fact]
public async Task Refresh_responses_without_refresh_token_use_old_refresh_token()
{
var mockHttp = new MockHttpMessageHandler();
AppHost.IdentityServerHttpHandler = mockHttp;

// short token lifetime should trigger refresh on 1st use
var initialTokenResponse = new
{
id_token = IdentityServerHost.CreateIdToken("1", "web"),
access_token = "initial_access_token",
token_type = "token_type",
expires_in = 10,
refresh_token = "initial_refresh_token",
};

// response for re-deeming code
mockHttp.When("/connect/token")
.WithFormData("grant_type", "authorization_code")
.Respond("application/json", JsonSerializer.Serialize(initialTokenResponse));

// note lack of refresh_token
var refreshTokenResponse = new
{
access_token = "refreshed1_access_token",
token_type = "token_type1",
expires_in = 3600,
};

// response for refresh
mockHttp.When("/connect/token")
.WithFormData("grant_type", "refresh_token")
.WithFormData("refresh_token", "initial_refresh_token")
.Respond("application/json", JsonSerializer.Serialize(refreshTokenResponse));

// setup host
await AppHost.InitializeAsync();
await AppHost.LoginAsync("alice");

// first request should trigger refresh
var response = await AppHost.BrowserClient.GetAsync(AppHost.Url("/user_token"));
var token = await response.Content.ReadFromJsonAsync<UserToken>();

token.ShouldNotBeNull();
token.IsError.ShouldBeFalse();
token.RefreshToken.ShouldBe("initial_refresh_token");
}
}

0 comments on commit f398940

Please sign in to comment.