Skip to content

Commit 87b0abc

Browse files
Add fix to be able to use NKey + Seed auth handler and username + password information combined. (#1138)
1 parent 1de9fa3 commit 87b0abc

File tree

3 files changed

+93
-41
lines changed

3 files changed

+93
-41
lines changed

src/main/java/io/nats/client/Options.java

Lines changed: 39 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2399,51 +2399,50 @@ public CharBuffer buildProtocolConnectOptionsString(String serverURI, boolean in
23992399
appendOption(connectString, Options.OPTION_SIG, encodedSig, true, true);
24002400
appendOption(connectString, Options.OPTION_JWT, jwt, true, true);
24012401
}
2402-
else {
2403-
String uriUser = null;
2404-
String uriPass = null;
2405-
String uriToken = null;
2406-
2407-
// Values from URI override options
2408-
try {
2409-
URI uri = this.createURIForServer(serverURI);
2410-
String userInfo = uri.getRawUserInfo();
2411-
if (userInfo != null) {
2412-
int at = userInfo.indexOf(":");
2413-
if (at == -1) {
2414-
uriToken = uriDecode(userInfo);
2415-
}
2416-
else {
2417-
uriUser = uriDecode(userInfo.substring(0, at));
2418-
uriPass = uriDecode(userInfo.substring(at + 1));
2419-
}
2402+
2403+
String uriUser = null;
2404+
String uriPass = null;
2405+
String uriToken = null;
2406+
2407+
// Values from URI override options
2408+
try {
2409+
URI uri = this.createURIForServer(serverURI);
2410+
String userInfo = uri.getRawUserInfo();
2411+
if (userInfo != null) {
2412+
int at = userInfo.indexOf(":");
2413+
if (at == -1) {
2414+
uriToken = uriDecode(userInfo);
2415+
}
2416+
else {
2417+
uriUser = uriDecode(userInfo.substring(0, at));
2418+
uriPass = uriDecode(userInfo.substring(at + 1));
24202419
}
24212420
}
2422-
catch (URISyntaxException e) {
2423-
// the createURIForServer call is the one that potentially throws this
2424-
// uriUser, uriPass and uriToken will already be null
2425-
}
2421+
}
2422+
catch (URISyntaxException e) {
2423+
// the createURIForServer call is the one that potentially throws this
2424+
// uriUser, uriPass and uriToken will already be null
2425+
}
24262426

2427-
if (uriUser != null) {
2428-
appendOption(connectString, Options.OPTION_USER, uriUser, true, true);
2429-
}
2430-
else if (this.username != null) {
2431-
appendOption(connectString, Options.OPTION_USER, this.username, true, true);
2432-
}
2427+
if (uriUser != null) {
2428+
appendOption(connectString, Options.OPTION_USER, uriUser, true, true);
2429+
}
2430+
else if (this.username != null) {
2431+
appendOption(connectString, Options.OPTION_USER, this.username, true, true);
2432+
}
24332433

2434-
if (uriPass != null) {
2435-
appendOption(connectString, Options.OPTION_PASSWORD, uriPass, true, true);
2436-
}
2437-
else if (this.password != null) {
2438-
appendOption(connectString, Options.OPTION_PASSWORD, this.password, true, true);
2439-
}
2434+
if (uriPass != null) {
2435+
appendOption(connectString, Options.OPTION_PASSWORD, uriPass, true, true);
2436+
}
2437+
else if (this.password != null) {
2438+
appendOption(connectString, Options.OPTION_PASSWORD, this.password, true, true);
2439+
}
24402440

2441-
if (uriToken != null) {
2442-
appendOption(connectString, Options.OPTION_AUTH_TOKEN, uriToken, true, true);
2443-
}
2444-
else if (this.token != null) {
2445-
appendOption(connectString, Options.OPTION_AUTH_TOKEN, this.token, true, true);
2446-
}
2441+
if (uriToken != null) {
2442+
appendOption(connectString, Options.OPTION_AUTH_TOKEN, uriToken, true, true);
2443+
}
2444+
else if (this.token != null) {
2445+
appendOption(connectString, Options.OPTION_AUTH_TOKEN, this.token, true, true);
24472446
}
24482447
}
24492448

src/test/java/io/nats/client/AuthHandlerForTesting.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,21 @@
1818

1919
public class AuthHandlerForTesting implements AuthHandler {
2020
private final NKey nkey;
21+
private final char[] jwt;
2122

2223
public AuthHandlerForTesting(NKey nkey) {
2324
this.nkey = nkey;
25+
this.jwt = null;
26+
}
27+
28+
public AuthHandlerForTesting(NKey nkey, char[] jwt) {
29+
this.nkey = nkey;
30+
this.jwt = jwt;
2431
}
2532

2633
public AuthHandlerForTesting() throws Exception {
2734
this.nkey = NKey.createUser(null);
35+
this.jwt = null;
2836
}
2937

3038
public NKey getNKey() {
@@ -48,6 +56,6 @@ public byte[] sign(byte[] nonce) {
4856
}
4957

5058
public char[] getJWT() {
51-
return null;
59+
return this.jwt;
5260
}
5361
}

src/test/java/io/nats/client/OptionsTests.java

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -694,6 +694,51 @@ public void testNKeyConnectOptions() throws Exception {
694694
assertEquals(expectedWithAuth, o.buildProtocolConnectOptionsString("nats://localhost:4222", true, nonce).toString(), "auth connect options");
695695
}
696696

697+
// Test for auth handler from nkey, option JWT and user info
698+
@Test
699+
public void testNKeyJWTAndUserInfoOptions() throws Exception {
700+
// "jwt" is encoded from:
701+
// Header: {"alg":"HS256"}
702+
// Payload: {"jti":"","iat":2000000000,"iss":"","name":"user_jwt","sub":"","nats":{"pub":{"deny":[">"]},
703+
// "sub":{"deny":[">"]},"subs":-1,"data":-1,"payload":-1,"type":"user","version":2}}
704+
String jwt = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIiLCJpYXQiOjIwMDAwMDAwMDAsImlzcyI6IiIsIm5hbWUiOiJ1c2VyX2p3"
705+
+ "dCIsInN1YiI6IiIsIm5hdHMiOnsicHViIjp7ImRlbnkiOlsiPiJdfSwic3ViIjp7ImRlbnkiOlsiPiJdfSwic3VicyI6LTEsImRh"
706+
+ "dGEiOi0xLCJwYXlsb2FkIjotMSwidHlwZSI6InVzZXIiLCJ2ZXJzaW9uIjoyfX0";
707+
NKey nkey = NKey.createUser(null);
708+
String username = "username";
709+
String password = "password";
710+
AuthHandlerForTesting th = new AuthHandlerForTesting(nkey, jwt.toCharArray());
711+
byte[] nonce = "abcdefg".getBytes(StandardCharsets.UTF_8);
712+
String sig = Base64.getUrlEncoder().withoutPadding().encodeToString(th.sign(nonce));
713+
714+
// Assert that no auth and user info is given
715+
Options options = new Options.Builder().authHandler(th)
716+
.userInfo(username.toCharArray(), password.toCharArray()).build();
717+
String expectedWithoutAuth = "{\"lang\":\"java\",\"version\":\"" + Nats.CLIENT_VERSION + "\""
718+
+ ",\"protocol\":1,\"verbose\":false,\"pedantic\":false,\"tls_required\":false,\"echo\":true,"
719+
+ "\"headers\":true,\"no_responders\":true}";
720+
String actualWithoutAuth = options
721+
.buildProtocolConnectOptionsString("nats://localhost:4222", false, nonce).toString();
722+
assertEquals(expectedWithoutAuth, actualWithoutAuth);
723+
724+
// Assert that auth and user info is given via options
725+
String expectedWithAuth = "{\"lang\":\"java\",\"version\":\"" + Nats.CLIENT_VERSION + "\""
726+
+ ",\"protocol\":1,\"verbose\":false,\"pedantic\":false,\"tls_required\":false,\"echo\":true,"
727+
+ "\"headers\":true,\"no_responders\":true,\"nkey\":\"" + new String(th.getID()) + "\",\"sig\":\""
728+
+ sig + "\",\"jwt\":\"" + jwt + "\",\"user\":\"" + username + "\",\"pass\":\"" + password + "\"}";
729+
String actualWithAuthInOptions = options
730+
.buildProtocolConnectOptionsString("nats://localhost:4222", true, nonce).toString();
731+
assertEquals(expectedWithAuth, actualWithAuthInOptions);
732+
733+
// Assert that auth is given via options and user info is given via server URI
734+
Options optionsWithoutUserInfo = new Options.Builder().authHandler(th).build();
735+
String serverUriWithAuth = "nats://" + username + ":" + password + "@localhost:4222";
736+
String actualWithAuthInServerUri = optionsWithoutUserInfo
737+
.buildProtocolConnectOptionsString(serverUriWithAuth, true, nonce).toString();
738+
assertEquals(expectedWithAuth, actualWithAuthInServerUri);
739+
}
740+
741+
697742
@Test
698743
public void testDefaultDataPort() {
699744
Options o = new Options.Builder().socketWriteTimeout(null).build();

0 commit comments

Comments
 (0)