Skip to content

Commit

Permalink
add refresh token phase to AuthorizationCode scenario (#434)
Browse files Browse the repository at this point in the history
Co-authored-by: Kamesh Akella <[email protected]>
Co-authored-by: Alexander Schwartz <[email protected]>
  • Loading branch information
3 people authored Aug 1, 2023
1 parent 0fc9257 commit d5fcbdc
Show file tree
Hide file tree
Showing 7 changed files with 71 additions and 3 deletions.
2 changes: 1 addition & 1 deletion benchmark/src/main/content/bin/kcb.sh
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ DIRNAME=$(dirname "$RESOLVED_NAME")

# Default values
JAVA_OPTS="-server"
JAVA_OPTS="${JAVA_OPTS} -Xmx1G -XX:+HeapDumpOnOutOfMemoryError"
JAVA_OPTS="${JAVA_OPTS} -Xmx4G -XX:+HeapDumpOnOutOfMemoryError"

DEBUG_MODE="${DEBUG:-false}"
DEBUG_PORT="${DEBUG_PORT:-8787}"
Expand Down
11 changes: 11 additions & 0 deletions benchmark/src/main/java/org/keycloak/benchmark/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,17 @@ public class Config {
public static final int refreshTokenPeriod = Integer.getInteger("refresh-token-period", 0);
public static final boolean filterResults = Boolean.getBoolean("filter-results"); // filter out results outside of measurementPeriod

/**
* Used by the AuthorizationCode scenario to specify how many times the token should be refreshed before the user logs out.
*/
public static final int refreshTokenCount = Integer.getInteger("refresh-token-count", 0);

/**
* Whether to share TCP connections among the multiple users in the test scenario, possibly helpful
* when the local testing system is overtaxed. But this will induce a lesser load than real world.
*/
public static final boolean shareConnections = Boolean.getBoolean("share-connections");

// Computed timestamps
public static final long simulationStartTime = System.currentTimeMillis();
public static final long warmUpStartTime = simulationStartTime + rampUpPeriod * 1000L;
Expand Down
15 changes: 15 additions & 0 deletions benchmark/src/main/scala/keycloak/scenario/CommonSimulation.scala
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,21 @@ abstract class CommonSimulation extends Simulation {
.acceptHeader("application/json")
.disableFollowRedirect

if (Config.shareConnections) {
// When a local system cannot handle a large number of connections, using shared connections
// may help by sharing existing connections among multiple users. However, this sharing will
// occur if while there are pauses within a scenario, allowing such an opportunity.
default = default.shareConnections
// when sharing connections across users, the number of connections in the pool shouldn't be limited
// to the default of 6 connections when using HTTP/1.1.
default = default.maxConnectionsPerHost(9999)
}

// since the test may involve tens of thousands of connections from a single testing
// system to a single host:port on a server, we may need to add additional addresses to
// increase the number of TCP connections we can make and this enables the use of those.
default = default.useAllLocalAddresses

if (Config.inferHtmlResources) {
default.inferHtmlResources()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,8 +243,10 @@ class KeycloakScenarioBuilder {
.formParam("code", "${code}")
.check(
status.is(200),
// other elements like the access token not captured as we don't need them in the current scenarios and want to save memory
jsonPath("$..id_token").find.saveAs("idToken"),
jsonPath("$..access_token").find.saveAs("accessToken"),
jsonPath("$..refresh_token").find.saveAs("refreshToken"),
jsonPath("$..expires_in").find.saveAs("expiresIn"),
)
)
.exec(session => (session.removeAll("code")))
Expand Down Expand Up @@ -720,5 +722,33 @@ class KeycloakScenarioBuilder {
// 5 seconds before expiry is time to refresh
lastRefresh + sess("expiresIn").as[String].toInt * 1000 - 5000 < System.currentTimeMillis()
}

private def refreshToken(): ChainBuilder = {
exec(http("RefreshToken")
.post(TOKEN_ENDPOINT)
.headers(UI_HEADERS)
.formParam("grant_type", "refresh_token")
.formParam("refresh_token", "${refreshToken}")
.formParam("client_id", "${clientId}")
.formParam("client_secret", "${clientSecret}")
.formParam("redirect_uri", "${redirectUri}")
.check(
status.is(200),
jsonPath("$..id_token").find.saveAs("idToken"),
jsonPath("$..access_token").find.saveAs("accessToken"),
jsonPath("$..refresh_token").find.saveAs("refreshToken"),
jsonPath("$..expires_in").find.saveAs("expiresIn"),
)
)
.exitHereIfFailed
}

def repeatRefresh(): KeycloakScenarioBuilder = {
chainBuilder = chainBuilder
.repeat(Config.refreshTokenCount, "refresh_i") {
refreshToken().pause(Config.refreshTokenPeriod)
}
this
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ class AuthorizationCode extends CommonSimulation {
.openLoginPage(true)
.loginUsernamePassword()
.exchangeCode()
.repeatRefresh()
.logout(true))

}
}
9 changes: 9 additions & 0 deletions doc/benchmark/modules/ROOT/pages/configuration.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,15 @@ Specify multiple URLs separated with a blank to run the test against multiple se

`+--server-url="http://host1:8080/ http://host2:8080/"+`

| [.nowrap]`--share-connections`
| false
| Enable sharing of connections among concurrent users within a scenario.
This can be useful if the load generator host cannot handle as many TCP connections as are necessary.
Note, using this option will produce less load on the target system than real world.
Note, only scenarios that pause between steps allow sharing of connections.

`+--share-connections=true+`

| [.nowrap]`--scenario`
| ClientSecret scenario
| The full classname of the scenario to run, for example `keycloak.scenario.authentication.AuthorizationCode`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ bin/kcb.sh \

To create offline sessions, set the parameter xref:configuration.adoc#scope[`--scope`] to a value including `offline_access`, for example, `openid profile offline_access`.

To test repeated refreshing of tokens between authenticating and logging out, pass `--refresh-token-count=<count>` and `--refresh-token-period=<seconds>`.

== Error messages

Invalid parameter: redirect_uri::
Expand Down

0 comments on commit d5fcbdc

Please sign in to comment.