From 0044dcc5ff8a806b65a76e01f194e4e71423c69d Mon Sep 17 00:00:00 2001 From: Michael Mroz Date: Tue, 23 May 2017 16:20:39 +1000 Subject: [PATCH 1/2] Exposed OTP secret for not-yet serialised Transaction entities --- .../java/com/auth0/guardian/Transaction.java | 27 ++++++++++++----- .../java/com/auth0/guardian/GuardianTest.java | 2 ++ .../com/auth0/guardian/TransactionTest.java | 29 +++++++++++++++++++ 3 files changed, 50 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/auth0/guardian/Transaction.java b/src/main/java/com/auth0/guardian/Transaction.java index d22e0fd..28acf72 100644 --- a/src/main/java/com/auth0/guardian/Transaction.java +++ b/src/main/java/com/auth0/guardian/Transaction.java @@ -40,12 +40,12 @@ public class Transaction implements Serializable { private String transactionToken; private String recoveryCode; - private transient String otpSecret; + private transient String totpSecret; - Transaction(String transactionToken, String recoveryCode, String otpSecret) { + Transaction(String transactionToken, String recoveryCode, String totpSecret) { this.transactionToken = transactionToken; this.recoveryCode = recoveryCode; - this.otpSecret = otpSecret; + this.totpSecret = totpSecret; } public String getTransactionToken() { @@ -56,6 +56,21 @@ public String getRecoveryCode() { return recoveryCode; } + /** + * Returns the TOTP secret to be encoded in a URI for QR code generation, + * or manually entered if a camera is not available to the enrollment device. + * + * @return the TOTP secret + * @throws IllegalStateException when there is no OTP secret + */ + public String getTotpSecret() throws IllegalStateException { + if (totpSecret == null) { + throw new IllegalStateException("There is no OTP Secret for this transaction"); + } + + return totpSecret; + } + /** * Returns the TOTP enrollment URI to be displayed in the QR code * @@ -65,16 +80,12 @@ public String getRecoveryCode() { * @throws IllegalStateException when there is no OTP secret */ public String totpURI(String user, String issuer) throws IllegalStateException { - if (otpSecret == null) { - throw new IllegalStateException("There is no OTP Secret for this transaction"); - } - // use HttpUrl to build the URI, java.net.URL really sucks return new HttpUrl.Builder() .scheme("https") // HttpUrl only allows http/https so I replace it afterwards .host("totp") .addPathSegment(String.format("%s:%s", issuer, user)) - .addQueryParameter("secret", otpSecret) + .addQueryParameter("secret", getTotpSecret()) .addQueryParameter("issuer", issuer) .build() .toString() diff --git a/src/test/java/com/auth0/guardian/GuardianTest.java b/src/test/java/com/auth0/guardian/GuardianTest.java index 0835b01..80b43f8 100644 --- a/src/test/java/com/auth0/guardian/GuardianTest.java +++ b/src/test/java/com/auth0/guardian/GuardianTest.java @@ -98,6 +98,7 @@ public void shouldRequestEnrollWithTOTP() throws Exception { assertThat(transaction, is(notNullValue())); assertThat(transaction.getTransactionToken(), is(equalTo("THE_TRANSACTION_TOKEN"))); assertThat(transaction.getRecoveryCode(), is(equalTo("THE_RECOVERY_CODE"))); + assertThat(transaction.getTotpSecret(), is(equalTo("THE_OTP_SECRET"))); assertThat(transaction.totpURI("user", "issuer"), is(equalTo("otpauth://totp/issuer:user?secret=THE_OTP_SECRET&issuer=issuer"))); } @@ -130,6 +131,7 @@ public void shouldRequestEnrollWithSMS() throws Exception { assertThat(transaction, is(notNullValue())); assertThat(transaction.getTransactionToken(), is(equalTo("THE_TRANSACTION_TOKEN"))); assertThat(transaction.getRecoveryCode(), is(equalTo("THE_RECOVERY_CODE"))); + assertThat(transaction.getTotpSecret(), is(equalTo("THE_OTP_SECRET"))); assertThat(transaction.totpURI("user", "issuer"), is(equalTo("otpauth://totp/issuer:user?secret=THE_OTP_SECRET&issuer=issuer"))); } diff --git a/src/test/java/com/auth0/guardian/TransactionTest.java b/src/test/java/com/auth0/guardian/TransactionTest.java index aa451a1..d0ae716 100644 --- a/src/test/java/com/auth0/guardian/TransactionTest.java +++ b/src/test/java/com/auth0/guardian/TransactionTest.java @@ -46,6 +46,7 @@ public void shouldCreateCorrectly() throws Exception { assertThat(transaction.getTransactionToken(), is(equalTo("TRANSACTION_TOKEN"))); assertThat(transaction.getRecoveryCode(), is(equalTo("RECOVERY_CODE"))); + assertThat(transaction.getTotpSecret(), is(equalTo("OTP_SECRET"))); assertThat(transaction.totpURI("username", "company"), is(equalTo("otpauth://totp/company:username?secret=OTP_SECRET&issuer=company"))); } @@ -55,6 +56,7 @@ public void shouldCreateCorrectlyWithSpaces() throws Exception { assertThat(transaction.getTransactionToken(), is(equalTo("TRANSACTION_TOKEN"))); assertThat(transaction.getRecoveryCode(), is(equalTo("RECOVERY_CODE"))); + assertThat(transaction.getTotpSecret(), is(equalTo("OTP_SECRET"))); assertThat(transaction.totpURI("user name", "company name"), is(equalTo("otpauth://totp/company%20name:user%20name?secret=OTP_SECRET&issuer=company%20name"))); } @@ -64,6 +66,7 @@ public void shouldCreateCorrectlyWithWeirdCharacters() throws Exception { assertThat(transaction.getTransactionToken(), is(equalTo("TRANSACTION_TOKEN"))); assertThat(transaction.getRecoveryCode(), is(equalTo("RECOVERY_CODE"))); + assertThat(transaction.getTotpSecret(), is(equalTo("OTP_SECRET"))); assertThat(transaction.totpURI("user%name", "compaƱy?!"), is(equalTo("otpauth://totp/compa%C3%B1y%3F!:user%25name?secret=OTP_SECRET&issuer=compa%C3%B1y?!"))); } @@ -91,6 +94,32 @@ public void shouldSerializeCorrectly() throws Exception { assertThat(restoredTransaction.getRecoveryCode(), is(equalTo("RECOVERY_CODE"))); } + @Test + public void shouldThrowWhenRequestingOtpSecretAfterSerialization() throws Exception { + exception.expect(IllegalStateException.class); + exception.expectMessage("There is no OTP Secret for this transaction"); + + Transaction transaction = new Transaction("TRANSACTION_TOKEN", "RECOVERY_CODE", "OTP_SECRET"); + + // save + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream); + objectOutputStream.writeObject(transaction); + objectOutputStream.close(); + outputStream.close(); + + byte[] binaryData = outputStream.toByteArray(); + + // restore + ByteArrayInputStream inputStream = new ByteArrayInputStream(binaryData); + ObjectInputStream objectInputStream = new ObjectInputStream(inputStream); + Transaction restoredTransaction = (Transaction) objectInputStream.readObject(); + objectInputStream.close(); + inputStream.close(); + + restoredTransaction.getTotpSecret(); + } + @Test public void shouldThrowWhenRequestingTotpUriAfterSerialization() throws Exception { exception.expect(IllegalStateException.class); From b8b8f15fe46a11dcc4ebdaef76b48403b08a17b4 Mon Sep 17 00:00:00 2001 From: Nicolas Ulrich Date: Mon, 29 May 2017 11:51:15 -0300 Subject: [PATCH 2/2] Release 0.3.0 --- CHANGELOG.md | 6 ++++++ README.md | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index af5e3ac..348af61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Change Log +## [0.3.0](https://github.com/auth0/Guardian.java/tree/0.3.0) (2017-05-29) +[Full Changelog](https://github.com/auth0/Guardian.java/compare/0.2.0...0.3.0) + +**Added** +- Exposed OTP secret for not-yet serialised Transaction entities [\#8](https://github.com/auth0/Guardian.java/pull/8) ([mirichan](https://github.com/mirichan)) + ## [0.2.0](https://github.com/auth0/Guardian.java/tree/0.2.0) (2017-04-06) [Full Changelog](https://github.com/auth0/Guardian.java/compare/0.1.0...0.2.0) diff --git a/README.md b/README.md index 3421585..95dc667 100644 --- a/README.md +++ b/README.md @@ -16,14 +16,14 @@ Get Guardian Java via Maven: com.auth0 guardian - 0.2.0 + 0.3.0 ``` or Gradle: ```gradle -compile 'com.auth0:guardian:0.2.0' +compile 'com.auth0:guardian:0.3.0' ``` ## Usage