[java-idp-plugin-totp] branch main updated: Add URL and QR code generation to CLI.

Scott Cantor cantor.2 at osu.edu
Thu Aug 13 00:27:30 UTC 2020


This is an automated email from the git hooks/post-receive script.

scantor pushed a commit to branch main
in repository java-idp-plugin-totp.

View the commit online:
http://git.shibboleth.net/view/?p=java-idp-plugin-totp.git;a=commit;h=2251f26d6c99fea2a5bd95cf79d4af81b5ac7933

The following commit(s) were added to refs/heads/main by this push:
       new  2251f26   Add URL and QR code generation to CLI.
2251f26 is described below

commit 2251f26d6c99fea2a5bd95cf79d4af81b5ac7933
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Wed Aug 12 20:27:20 2020 -0400

    Add URL and QR code generation to CLI.
---
 .../plugin/totp/impl/GoogleTOTPAuthenticator.java  | 38 ++++++++++++++++++++--
 .../idp/plugin/totp/impl/TOTPAuthenticator.java    | 14 +++++++-
 .../totp/impl/TOTPAuthenticatorArguments.java      | 31 ++++++++++++++++++
 .../idp/plugin/totp/impl/TOTPAuthenticatorCLI.java |  7 +++-
 4 files changed, 86 insertions(+), 4 deletions(-)

diff --git a/totp-impl/src/main/java/net/shibboleth/idp/plugin/totp/impl/GoogleTOTPAuthenticator.java b/totp-impl/src/main/java/net/shibboleth/idp/plugin/totp/impl/GoogleTOTPAuthenticator.java
index 3972b5d..9558174 100644
--- a/totp-impl/src/main/java/net/shibboleth/idp/plugin/totp/impl/GoogleTOTPAuthenticator.java
+++ b/totp-impl/src/main/java/net/shibboleth/idp/plugin/totp/impl/GoogleTOTPAuthenticator.java
@@ -21,6 +21,7 @@ import java.security.GeneralSecurityException;
 import java.util.Collection;
 
 import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
 
 import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
 import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
@@ -33,10 +34,12 @@ import net.shibboleth.utilities.java.support.component.AbstractInitializableComp
 import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
 import net.shibboleth.utilities.java.support.component.ComponentSupport;
 import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.utilities.java.support.primitive.StringSupport;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.net.UrlEscapers;
 import com.warrenstrange.googleauth.GoogleAuthenticator;
 import com.warrenstrange.googleauth.GoogleAuthenticatorConfig;
 import com.warrenstrange.googleauth.GoogleAuthenticatorKey;
@@ -79,19 +82,26 @@ public class GoogleTOTPAuthenticator extends AbstractInitializableComponent impl
     }
 
     /** {@inheritDoc} */
-    public TOTPCredential createCredential() throws GeneralSecurityException {
+    public TOTPCredential createCredential(@Nullable @NotEmpty final String issuer,
+            @Nullable @NotEmpty final String accountName) throws GeneralSecurityException {
         
         final byte[] secret;
+        final String encodedSecret;
         final GoogleAuthenticatorKey cred = authenticator.createCredentials();
         
+        final String trimmedIssuer = StringSupport.trimOrNull(issuer);
+        final String trimmedName = StringSupport.trimOrNull(accountName);
+        
         try {
             switch (authconfig.getKeyRepresentation()) {
                 case BASE32:
                     secret = Base32Support.decode(cred.getKey());
+                    encodedSecret = cred.getKey();
                     break;
                     
                 case BASE64:
                     secret = Base64Support.decode(cred.getKey());
+                    encodedSecret = Base32Support.encode(secret, false);
                     break;
                     
                 default:
@@ -102,12 +112,36 @@ public class GoogleTOTPAuthenticator extends AbstractInitializableComponent impl
                 public byte[] getKey() {
                     return secret;
                 }
+                
+                public String getTOTPURL() {
+                    final String label;
+                    if (trimmedName != null) {
+                        if (trimmedIssuer != null) {
+                            label = trimmedIssuer + ":" + trimmedName;
+                        } else {
+                            label = trimmedName;
+                        }
+                        
+                    } else {
+                        label = null;
+                    }
+                    
+                    final StringBuilder url = new StringBuilder();
+                    url.append("otpauth://totp/")
+                        .append(UrlEscapers.urlPathSegmentEscaper().escape(label))
+                        .append("?secret=")
+                        .append(UrlEscapers.urlFormParameterEscaper().escape(encodedSecret));
+                    if (trimmedIssuer != null) {
+                        url.append("&issuer=").append(UrlEscapers.urlFormParameterEscaper().escape(issuer));
+                    }
+                    return url.toString();
+                }
     
                 public Collection<Integer> getScratchCodes() {
                     return cred.getScratchCodes();
                 }
             };
-        } catch (final DecodingException e) {
+        } catch (final EncodingException|DecodingException e) {
             throw new GeneralSecurityException(e);
         }
     }
diff --git a/totp-impl/src/main/java/net/shibboleth/idp/plugin/totp/impl/TOTPAuthenticator.java b/totp-impl/src/main/java/net/shibboleth/idp/plugin/totp/impl/TOTPAuthenticator.java
index cde579a..2668145 100644
--- a/totp-impl/src/main/java/net/shibboleth/idp/plugin/totp/impl/TOTPAuthenticator.java
+++ b/totp-impl/src/main/java/net/shibboleth/idp/plugin/totp/impl/TOTPAuthenticator.java
@@ -21,6 +21,7 @@ import java.security.GeneralSecurityException;
 import java.util.Collection;
 
 import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
 
 import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
 import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
@@ -35,11 +36,15 @@ public interface TOTPAuthenticator {
     /**
      * Generate a new credential.
      * 
+     * @param issuer TOTP credential issuer
+     * @param accountName TOTP account holder
+     * 
      * @return a new credential
      * 
      * @throws GeneralSecurityException if unable to generate the credential
      */
-    @Nonnull TOTPCredential createCredential() throws GeneralSecurityException;
+    @Nonnull TOTPCredential createCredential(@Nullable @NotEmpty final String issuer,
+            @Nullable @NotEmpty final String accountName) throws GeneralSecurityException;
     
     /**
      * Validate a secret and code.
@@ -61,6 +66,13 @@ public interface TOTPAuthenticator {
          */
         @Nonnull @NotEmpty byte[] getKey();
 
+        /**
+         * Get the TOTP URL associated with the credential.
+         * 
+         * @return TOTP URL
+         */
+        @Nonnull @NotEmpty String getTOTPURL();
+        
         /**
          * Get the list of scratch codes.
          *
diff --git a/totp-impl/src/main/java/net/shibboleth/idp/plugin/totp/impl/TOTPAuthenticatorArguments.java b/totp-impl/src/main/java/net/shibboleth/idp/plugin/totp/impl/TOTPAuthenticatorArguments.java
index 2e04202..a8d19cf 100644
--- a/totp-impl/src/main/java/net/shibboleth/idp/plugin/totp/impl/TOTPAuthenticatorArguments.java
+++ b/totp-impl/src/main/java/net/shibboleth/idp/plugin/totp/impl/TOTPAuthenticatorArguments.java
@@ -27,6 +27,7 @@ import net.shibboleth.ext.spring.cli.AbstractCommandLineArguments;
 import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
 import net.shibboleth.utilities.java.support.codec.Base32Support;
 import net.shibboleth.utilities.java.support.codec.DecodingException;
+import net.shibboleth.utilities.java.support.primitive.StringSupport;
 
 /**
  * Arguments for {@link TOTPAuthenticatorCLI}.
@@ -39,6 +40,14 @@ public class TOTPAuthenticatorArguments extends AbstractCommandLineArguments {
     @Parameter(names = "--authenticator")
     @Nullable private String authenticatorName;
 
+    /** Credential issuer. */
+    @Parameter(names = "--issuer")
+    @Nullable private String issuer;
+    
+    /** Credential account name. */
+    @Parameter(names = "--account")
+    @Nullable private String account;
+    
     /** Raw token seed when verifying codes. */
     @Nullable private byte[] seed;
     
@@ -63,6 +72,24 @@ public class TOTPAuthenticatorArguments extends AbstractCommandLineArguments {
         return seed;
     }
 
+    /**
+     * Get the token issuer.
+     * 
+     * @return token issuer
+     */
+    @Nullable @NotEmpty public String getIssuer() {
+        return StringSupport.trimOrNull(issuer);
+    }
+
+    /**
+     * Get the token account name.
+     * 
+     * @return token account name
+     */
+    @Nullable @NotEmpty public String getAccountName() {
+        return StringSupport.trimOrNull(account);
+    }
+
     /**
      * Get token code to verify.
      * 
@@ -103,6 +130,10 @@ public class TOTPAuthenticatorArguments extends AbstractCommandLineArguments {
         out.println();
         out.println(String.format("  --%-20s %s", "authenticator",
                 "Specifies a non-default TOTPAuthenticator bean to use."));
+        out.println(String.format("  --%-20s %s", "issuer",
+                "Specifies a token issuer when generating a new credential."));
+        out.println(String.format("  --%-20s %s", "account",
+                "Specifies a token account name when generating a new credential."));
         out.println();
     }
 
diff --git a/totp-impl/src/main/java/net/shibboleth/idp/plugin/totp/impl/TOTPAuthenticatorCLI.java b/totp-impl/src/main/java/net/shibboleth/idp/plugin/totp/impl/TOTPAuthenticatorCLI.java
index 7178491..6e80e0d 100644
--- a/totp-impl/src/main/java/net/shibboleth/idp/plugin/totp/impl/TOTPAuthenticatorCLI.java
+++ b/totp-impl/src/main/java/net/shibboleth/idp/plugin/totp/impl/TOTPAuthenticatorCLI.java
@@ -23,6 +23,8 @@ import javax.annotation.Nullable;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.net.UrlEscapers;
+
 import net.shibboleth.ext.spring.cli.AbstractCommandLine;
 import net.shibboleth.idp.Version;
 import net.shibboleth.idp.plugin.totp.impl.TOTPAuthenticator.TOTPCredential;
@@ -86,8 +88,11 @@ public class TOTPAuthenticatorCLI extends AbstractCommandLine<TOTPAuthenticatorA
             }
             
             // Create a new token.
-            final TOTPCredential tc = authenticator.createCredential();
+            final TOTPCredential tc = authenticator.createCredential(args.getIssuer(), args.getAccountName());
             System.out.println("Seed: " + Base32Support.encode(tc.getKey(), false));
+            System.out.println("URL: " + tc.getTOTPURL());
+            System.out.println("QR Code: https://api.qrserver.com/v1/create-qr-code/?data=" +
+                    UrlEscapers.urlFormParameterEscaper().escape(tc.getTOTPURL()) + "&size=200x200&ecc=M&margin=0");
             
         } catch (final Exception e) {
             if (args.isVerboseOutput()) {

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list