diff --git a/server-spi/src/main/java/org/keycloak/models/OTPPolicy.java b/server-spi/src/main/java/org/keycloak/models/OTPPolicy.java index 2f820f1198..0acf02d335 100755 --- a/server-spi/src/main/java/org/keycloak/models/OTPPolicy.java +++ b/server-spi/src/main/java/org/keycloak/models/OTPPolicy.java @@ -112,24 +112,41 @@ public class OTPPolicy implements Serializable { this.period = period; } + /** + * Constructs the otpauth:// URI based on the Key-Uri-Format. + * @param realm + * @param user + * @param secret + * @return the otpauth:// URI + */ public String getKeyURI(RealmModel realm, UserModel user, String secret) { + try { + String displayName = realm.getDisplayName() != null && !realm.getDisplayName().isEmpty() ? realm.getDisplayName() : realm.getName(); - String uri; - uri = "otpauth://" + type + "/" + URLEncoder.encode(user.getUsername(), "UTF-8") + "?secret=" + - Base32.encode(secret.getBytes()) + "&digits=" + digits + "&algorithm=" + algToKeyUriAlg.get(algorithm); + String accountName = URLEncoder.encode(user.getUsername(), "UTF-8"); + String issuerName = URLEncoder.encode(displayName, "UTF-8") .replaceAll("\\+", "%20"); - uri += "&issuer=" + URLEncoder.encode(displayName, "UTF-8"); + /* + * The issuerName component in the label is usually shown in a authenticator app, such as + * Google Authenticator or FreeOTP, as a hint for the user to which system an username + * belongs to. + */ + String label = issuerName + ":" + accountName; + + String parameters = "secret=" + Base32.encode(secret.getBytes()) // + + "&digits=" + digits // + + "&algorithm=" + algToKeyUriAlg.get(algorithm) // + + "&issuer=" + issuerName; if (type.equals(UserCredentialModel.HOTP)) { - uri += "&counter=" + initialCounter; - } - if (type.equals(UserCredentialModel.TOTP)) { - uri += "&period=" + period; + parameters += "&counter=" + initialCounter; + } else if (type.equals(UserCredentialModel.TOTP)) { + parameters += "&period=" + period; } - return uri; + return "otpauth://" + type + "/" + label+ "?" + parameters; } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); }