Skip to content
This repository has been archived by the owner on Sep 26, 2019. It is now read-only.

Commit

Permalink
password in jwt payload (#823)
Browse files Browse the repository at this point in the history
* make sure we don't put the password in the jwt payload

* Made sure password doesn't appear in jwt payload
  • Loading branch information
Errorific authored and lucassaldanha committed Feb 11, 2019
1 parent e2b87b0 commit b1e8df2
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ public class JsonRpcHttpService {
private final LabelledMetric<OperationTimer> requestTimer;

@VisibleForTesting public final Optional<JWTAuth> jwtAuthProvider;
@VisibleForTesting public final Optional<JWTAuthOptions> jwtAuthOptions;
private final Optional<AuthProvider> credentialAuthProvider;

private HttpServer httpServer;
Expand Down Expand Up @@ -212,6 +213,7 @@ private JsonRpcHttpService(
this.vertx = vertx;
this.jsonRpcMethods = methods;
this.credentialAuthProvider = credentialAuthProvider;
this.jwtAuthOptions = jwtOptions;
jwtAuthProvider = jwtOptions.map(options -> JWTAuth.create(vertx, options));
}

Expand Down Expand Up @@ -441,7 +443,11 @@ private void handleLogin(final RoutingContext routingContext) {

final JWTOptions options =
new JWTOptions().setExpiresInMinutes(5).setAlgorithm("RS256");
final String token = jwtAuthProvider.get().generateToken(user.principal(), options);
final JsonObject jwtContents =
new JsonObject()
.put("permissions", user.principal().getValue("permissions"))
.put("username", user.principal().getValue("username"));
final String token = jwtAuthProvider.get().generateToken(jwtContents, options);

final JsonObject responseBody = new JsonObject().put("token", token);
final HttpServerResponse response = routingContext.response();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,20 +31,35 @@
import tech.pegasys.pantheon.ethereum.privacy.PrivateTransactionHandler;
import tech.pegasys.pantheon.metrics.noop.NoOpMetricsSystem;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Paths;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

import io.vertx.core.Vertx;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.auth.KeyStoreOptions;
import io.vertx.ext.auth.PubSecKeyOptions;
import io.vertx.ext.auth.SecretOptions;
import io.vertx.ext.auth.User;
import io.vertx.ext.auth.jwt.JWTAuth;
import io.vertx.ext.auth.jwt.JWTAuthOptions;
import io.vertx.ext.auth.jwt.impl.JWTAuthProviderImpl;
import io.vertx.ext.jwt.JWK;
import io.vertx.ext.jwt.JWT;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
Expand Down Expand Up @@ -227,4 +242,89 @@ public void loginWithGoodCredentialsAndPermissions() throws IOException {
});
}
}

private JWT makeJwt(final JWTAuthOptions config)
throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException {
final KeyStoreOptions keyStoreOptions = config.getKeyStore();
if (keyStoreOptions != null) {
final KeyStore ks = KeyStore.getInstance(keyStoreOptions.getType());

// synchronize on the class to avoid the case where multiple file accesses will overlap
synchronized (JWTAuthProviderImpl.class) {
final Buffer keystore = vertx.fileSystem().readFileBlocking(keyStoreOptions.getPath());

try (InputStream in = new ByteArrayInputStream(keystore.getBytes())) {
ks.load(in, keyStoreOptions.getPassword().toCharArray());
}
}

return new JWT(ks, keyStoreOptions.getPassword().toCharArray());
} else {
// no key file attempt to load pem keys
final JWT jwt = new JWT();

final List<PubSecKeyOptions> keys = config.getPubSecKeys();

if (keys != null) {
for (final PubSecKeyOptions pubSecKey : config.getPubSecKeys()) {
if (pubSecKey.isSymmetric()) {
jwt.addJWK(new JWK(pubSecKey.getAlgorithm(), pubSecKey.getPublicKey()));
} else {
jwt.addJWK(
new JWK(
pubSecKey.getAlgorithm(),
pubSecKey.isCertificate(),
pubSecKey.getPublicKey(),
pubSecKey.getSecretKey()));
}
}
}

// TODO: remove once the deprecation ends!
final List<SecretOptions> secrets = config.getSecrets();

if (secrets != null) {
for (final SecretOptions secret : secrets) {
jwt.addSecret(secret.getType(), secret.getSecret());
}
}

final List<JsonObject> jwks = config.getJwks();

if (jwks != null) {
for (final JsonObject jwk : jwks) {
jwt.addJWK(new JWK(jwk));
}
}
return jwt;
}
}

@Test
public void loginDoesntPopulateJWTPayloadWithPassword()
throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException {
final RequestBody body =
RequestBody.create(JSON, "{\"username\":\"user\",\"password\":\"pegasys\"}");
final Request request = new Request.Builder().post(body).url(baseUrl + "/login").build();
try (final Response resp = client.newCall(request).execute()) {
assertThat(resp.code()).isEqualTo(200);
assertThat(resp.message()).isEqualTo("OK");
assertThat(resp.body().contentType()).isNotNull();
assertThat(resp.body().contentType().type()).isEqualTo("application");
assertThat(resp.body().contentType().subtype()).isEqualTo("json");
final String bodyString = resp.body().string();
assertThat(bodyString).isNotNull();
assertThat(bodyString).isNotBlank();

final JsonObject respBody = new JsonObject(bodyString);
final String token = respBody.getString("token");
assertThat(token).isNotNull();
final JWT jwt = makeJwt(service.jwtAuthOptions.get());

final JsonObject jwtPayload = jwt.decode(token);
final String jwtPayloadString = jwtPayload.encode();
assertThat(jwtPayloadString.contains("password")).isFalse();
assertThat(jwtPayloadString.contains("pegasys")).isFalse();
}
}
}

0 comments on commit b1e8df2

Please sign in to comment.