This library complements the java-security
project with testing utilities.
It includes for example a JwtGenerator
that generates JSON Web Tokens (JWT) that can be used for JUnit tests, as well as for integration testing.
By default the generated token is Base64 encoded and signed with a generated RSA key.
- Java 8
- maven 3.3.9 or later
<dependency>
<groupId>com.sap.cloud.security.xsuaa</groupId>
<artifactId>java-security-test</artifactId>
<version>2.4.1-SNAPSHOT</version>
<scope>test</scope>
</dependency>
Find an example on how to use the test utilities here.
Using JwtGenerator
you can create tokens of type Token
, which offers you a getAccessToken()
method that returns the encoded and signed Jwt token. By default its signed with a random RSA key pair. In case you like to provide the token via Authorization
header to your application use getBearerAccessToken()
to get the access token prefixed with Bearer
.
Token token = JwtGenerator.getInstance(Service.XSUAA)
.withHeaderParameter(TokenHeader.KEY_ID, "key-id") // optional
.withClaimValue(TokenClaims.XSUAA.CLIENT_ID, clientId) // optional
.createToken();
String authorizationHeaderValue = token.getBearerAccessToken();
In case you want to test your secured web application as part of your JUnit tests you need to generate JWT tokens and in order to validate the token you need also to mock the jwks endpoint of the identity service e.g. xsuaa.
The SecurityTestRule
uses third-party library WireMock to stub outgoing calls to the identity service. Furthermore it pre-configures the JwtGenerator
, so that the token is signed with a private key which matches the public key provided by the jwks endpoint (on behalf of WireMock). Furthermore you can specify the clientId
for token generation, that it can be validated by the predefined set of Jwt validators.
Optionally, you can configure the SecurityTestRule
to start an embedded Jetty servlet container that comes equipped with an authenticator. The authenticator checks whether a request is done by an authenticated AND authorized party. You can also add your own servlets to the container. Only requests that contain a valid authorization header will be passed through to the servlet. See the following test code that triggers HTTP requests against the servlet container. One request does not contain the token inside the authorization header and is expected to result in HTTP 401 (unauthenticated). The other contains a valid token and is expected to succeed.
public class HelloJavaServletTest {
private static Properties oldProperties;
@ClassRule
public static SecurityTestRule rule = SecurityTestRule.getInstance(XSUAA)
.useApplicationServer() // optionally customize application server, e.g. port
.addApplicationServlet(TestServlet.class, "/hi"); // add servlet to be tested to application server
@After
public void tearDown() {
SecurityContext.clearToken();
}
@Test
public void requestWithoutAuthorizationHeader_unauthenticated() throws IOException {
HttpGet request = createGetRequest(null);
try (CloseableHttpResponse response = HttpClients.createDefault().execute(request)) {
assertThat(response.getStatusLine().getStatusCode()).isEqualTo(HttpStatus.SC_UNAUTHORIZED); // 401
}
}
@Test
public void requestWithValidToken_ok() throws IOException {
String jwt = rule.getPreconfiguredJwtGenerator()
.withScopes(SecurityTestRule.DEFAULT_APP_ID + ".Read")
.createToken()
.getBearerAccessToken();
HttpGet request = createGetRequest(jwt);
try (CloseableHttpResponse response = HttpClients.createDefault().execute(request)) {
String responseBody = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
assertThat(response.getStatusLine().getStatusCode()).isEqualTo(HttpStatus.SC_OK);
}
}
private HttpGet createGetRequest(String bearerToken) {
HttpGet httpGet = new HttpGet(rule.getApplicationServerUri() + HelloJavaServlet.ENDPOINT);
if(bearerToken != null) {
httpGet.setHeader(HttpHeaders.AUTHORIZATION, bearerToken);
}
return httpGet;
}
}