diff --git a/README.md b/README.md index d1d8c54..e8c3693 100644 --- a/README.md +++ b/README.md @@ -30,23 +30,23 @@ The following features are supported: com.github.bastiaanjansen otp-java - 2.0.1 + 2.0.2 ``` ### Gradle ```gradle -implementation 'com.github.bastiaanjansen:otp-java:2.0.1' +implementation 'com.github.bastiaanjansen:otp-java:2.0.2' ``` ### Scala SBT ```scala -libraryDependencies += "com.github.bastiaanjansen" % "otp-java" % "2.0.1" +libraryDependencies += "com.github.bastiaanjansen" % "otp-java" % "2.0.2" ``` ### Apache Ivy ```xml - + ``` Or you can download the source from the [GitHub releases page](https://github.com/BastiaanJansen/OTP-Java/releases). diff --git a/pom.xml b/pom.xml index 05514bb..f4809c5 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ com.github.bastiaanjansen otp-java - 2.0.1 + 2.0.2 OTP-Java A small and easy-to-use one-time password generator for Java according to RFC 4226 (HOTP) and RFC 6238 (TOTP). diff --git a/src/main/java/com/bastiaanjansen/otp/HOTPGenerator.java b/src/main/java/com/bastiaanjansen/otp/HOTPGenerator.java index ae8ade0..aefdc82 100644 --- a/src/main/java/com/bastiaanjansen/otp/HOTPGenerator.java +++ b/src/main/java/com/bastiaanjansen/otp/HOTPGenerator.java @@ -120,7 +120,7 @@ public URI getURI(final String type, final String issuer, final String account, query.put(URIHelper.SECRET, new String(secret, StandardCharsets.UTF_8)); query.put(URIHelper.ISSUER, issuer); - String path = account.isEmpty() ? issuer : String.format("%s:%s", issuer, account); + String path = account.isEmpty() ? URIHelper.encode(issuer) : String.format("%s:%s", URIHelper.encode(issuer), URIHelper.encode(account)); return URIHelper.createURI(URL_SCHEME, type, path, query); } diff --git a/src/main/java/com/bastiaanjansen/otp/helpers/URIHelper.java b/src/main/java/com/bastiaanjansen/otp/helpers/URIHelper.java index b90acaa..4e3dc45 100644 --- a/src/main/java/com/bastiaanjansen/otp/helpers/URIHelper.java +++ b/src/main/java/com/bastiaanjansen/otp/helpers/URIHelper.java @@ -8,6 +8,7 @@ import java.nio.charset.StandardCharsets; import java.util.LinkedHashMap; import java.util.Map; +import java.util.stream.Collectors; /** * A URI utility class with helper methods @@ -61,20 +62,20 @@ public static Map queryItems(URI uri) { * @throws URISyntaxException when URI cannot be created */ public static URI createURI(String scheme, String host, String path, Map query) throws URISyntaxException { - StringBuilder uriString = new StringBuilder(String.format("%s://%s/%s", scheme, host, path)); - String[] queryKeys = query.keySet().toArray(new String[0]); + String uriString = String.format("%s://%s/%s?", scheme, host, path); + String uri = query.keySet().stream() + .map(key -> String.format("%s=%s", key, encode(query.get(key)))) + .collect(Collectors.joining("&", uriString, "")); + + return new URI(uri); + } + + public static String encode(String value) { try { - for (int i = 0; i < queryKeys.length; i++) { - String sign = i == 0 ? "?" : "&"; - String key = queryKeys[i]; - uriString.append(String.format("%s%s=%s", sign, key, URLEncoder.encode(query.get(key), StandardCharsets.UTF_8.toString()))); - } + return URLEncoder.encode(value, StandardCharsets.UTF_8.toString()); } catch (UnsupportedEncodingException e) { - // Highly unlikely throw new IllegalArgumentException(e); } - - return new URI(uriString.toString()); } } diff --git a/src/test/java/com/bastiaanjansen/otp/HOTPGeneratorTest.java b/src/test/java/com/bastiaanjansen/otp/HOTPGeneratorTest.java index d49e29a..a293f95 100644 --- a/src/test/java/com/bastiaanjansen/otp/HOTPGeneratorTest.java +++ b/src/test/java/com/bastiaanjansen/otp/HOTPGeneratorTest.java @@ -106,6 +106,22 @@ void getURIWithIssuer_doesNotThrow() { }); } + @Test + void getURIWithIssuerWithSpace_doesNotThrow() { + HOTPGenerator generator = new HOTPGenerator.Builder(secret.getBytes()).build(); + + assertDoesNotThrow(() -> generator.getURI(10, "issuer with space")); + } + + @Test + void getURIWithIssuerWithSpace_doesEscapeIssuer() throws URISyntaxException { + HOTPGenerator generator = new HOTPGenerator.Builder(secret.getBytes()).build(); + + String url = generator.getURI(10, "issuer with space").toString(); + + assertThat(url, is("otpauth://hotp/issuer+with+space?digits=6&counter=10&secret=vv3kox7uqj4kyakohmzpph3us4cjimh6f3zknb5c2oobq6v2kiyhm27q&issuer=issuer+with+space&algorithm=SHA1")); + } + @Test void getURIWithIssuer() throws URISyntaxException { HOTPGenerator generator = new HOTPGenerator.Builder(secret.getBytes()).build(); @@ -119,7 +135,7 @@ void getURIWithIssuerWithUrlUnsafeCharacters() throws URISyntaxException { HOTPGenerator generator = new HOTPGenerator.Builder(secret.getBytes()).build(); URI uri = generator.getURI(10, "mac&cheese"); - assertThat(uri.toString(), is("otpauth://hotp/mac&cheese?digits=6&counter=10&secret=" + secret + "&issuer=mac%26cheese&algorithm=SHA1")); + assertThat(uri.toString(), is("otpauth://hotp/mac%26cheese?digits=6&counter=10&secret=" + secret + "&issuer=mac%26cheese&algorithm=SHA1")); } @@ -145,7 +161,8 @@ void getURIWithIssuerAndAccountWithUrlUnsafeCharacters() throws URISyntaxExcepti HOTPGenerator generator = new HOTPGenerator.Builder(secret.getBytes()).build(); URI uri = generator.getURI(100, "mac&cheese", "ac@cou.nt"); - assertThat(uri.toString(), is("otpauth://hotp/mac&cheese:ac@cou.nt?digits=6&counter=100&secret=" + secret + "&issuer=mac%26cheese&algorithm=SHA1")); + + assertThat(uri.toString(), is("otpauth://hotp/mac%26cheese:ac%40cou.nt?digits=6&counter=100&secret=" + secret + "&issuer=mac%26cheese&algorithm=SHA1")); } @Test diff --git a/src/test/java/com/bastiaanjansen/otp/TOTPGeneratorTest.java b/src/test/java/com/bastiaanjansen/otp/TOTPGeneratorTest.java index c9a94c2..ae32145 100644 --- a/src/test/java/com/bastiaanjansen/otp/TOTPGeneratorTest.java +++ b/src/test/java/com/bastiaanjansen/otp/TOTPGeneratorTest.java @@ -165,7 +165,7 @@ void getURIWithIssuerWithUrlUnsafeCharacters() throws URISyntaxException { TOTPGenerator generator = new TOTPGenerator.Builder(secret.getBytes()).build(); URI uri = generator.getURI("mac&cheese"); - assertThat(uri.toString(), is("otpauth://totp/mac&cheese?period=30&digits=6&secret=" + secret + "&issuer=mac%26cheese&algorithm=SHA1")); + assertThat(uri.toString(), is("otpauth://totp/mac%26cheese?period=30&digits=6&secret=" + secret + "&issuer=mac%26cheese&algorithm=SHA1")); } @Test @@ -192,7 +192,7 @@ void getURIWithIssuerAndAccountWithUrlUnsafeCharacters() throws URISyntaxExcepti URI uri = generator.getURI("mac&cheese", "ac@cou.nt"); - assertThat(uri.toString(), is("otpauth://totp/mac&cheese:ac@cou.nt?period=30&digits=6&secret=" + secret + "&issuer=mac%26cheese&algorithm=SHA1")); + assertThat(uri.toString(), is("otpauth://totp/mac%26cheese:ac%40cou.nt?period=30&digits=6&secret=" + secret + "&issuer=mac%26cheese&algorithm=SHA1")); } @Test