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);
}