keycloak-aplcache
Changes
services/src/main/java/org/keycloak/forms/account/freemarker/FreeMarkerAccountProvider.java 2(+1 -1)
Details
diff --git a/services/src/main/java/org/keycloak/forms/account/freemarker/FreeMarkerAccountProvider.java b/services/src/main/java/org/keycloak/forms/account/freemarker/FreeMarkerAccountProvider.java
index 893e816..58c39ec 100755
--- a/services/src/main/java/org/keycloak/forms/account/freemarker/FreeMarkerAccountProvider.java
+++ b/services/src/main/java/org/keycloak/forms/account/freemarker/FreeMarkerAccountProvider.java
@@ -188,7 +188,7 @@ public class FreeMarkerAccountProvider implements AccountProvider {
switch (page) {
case TOTP:
- attributes.put("totp", new TotpBean(session, realm, user, baseUri));
+ attributes.put("totp", new TotpBean(session, realm, user));
break;
case FEDERATED_IDENTITY:
attributes.put("federatedIdentity", new AccountFederatedIdentityBean(session, realm, user, uriInfo.getBaseUri(), stateChecker));
diff --git a/services/src/main/java/org/keycloak/forms/account/freemarker/model/TotpBean.java b/services/src/main/java/org/keycloak/forms/account/freemarker/model/TotpBean.java
old mode 100755
new mode 100644
index 1312e5d..197f985
--- a/services/src/main/java/org/keycloak/forms/account/freemarker/model/TotpBean.java
+++ b/services/src/main/java/org/keycloak/forms/account/freemarker/model/TotpBean.java
@@ -14,12 +14,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.keycloak.forms.account.freemarker.model;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.Base32;
+import org.keycloak.models.utils.HmacOTP;
+import org.keycloak.utils.TotpUtils;
import java.io.UnsupportedEncodingException;
import java.net.URI;
@@ -34,35 +37,15 @@ public class TotpBean {
private final String totpSecret;
private final String totpSecretEncoded;
+ private final String totpSecretQrCode;
private final boolean enabled;
- private final String contextUrl;
- private final String keyUri;
- public TotpBean(KeycloakSession session, RealmModel realm, UserModel user, URI baseUri) {
+ public TotpBean(KeycloakSession session, RealmModel realm, UserModel user) {
this.enabled = session.users().configuredForCredentialType(realm.getOTPPolicy().getType(), realm, user);
- this.contextUrl = baseUri.getPath();
-
- this.totpSecret = randomString(20);
- this.totpSecretEncoded = Base32.encode(totpSecret.getBytes());
- this.keyUri = realm.getOTPPolicy().getKeyURI(realm, user, this.totpSecret);
- }
-
- private static String randomString(int length) {
- String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVW1234567890";
- StringBuilder sb = new StringBuilder();
- for (int i = 0; i < length; i++) {
- char c = chars.charAt(random.nextInt(chars.length()));
- sb.append(c);
- }
- return sb.toString();
- }
-
- private static final SecureRandom random;
- static
- {
- random = new SecureRandom();
- random.nextInt();
+ this.totpSecret = HmacOTP.generateSecret(20);
+ this.totpSecretEncoded = TotpUtils.encode(totpSecret);
+ this.totpSecretQrCode = TotpUtils.qrCode(totpSecret, realm, user);
}
public boolean isEnabled() {
@@ -74,19 +57,11 @@ public class TotpBean {
}
public String getTotpSecretEncoded() {
- StringBuilder sb = new StringBuilder();
- for (int i = 0; i < totpSecretEncoded.length(); i += 4) {
- sb.append(totpSecretEncoded.substring(i, i + 4 < totpSecretEncoded.length() ? i + 4 : totpSecretEncoded.length()));
- if (i + 4 < totpSecretEncoded.length()) {
- sb.append(" ");
- }
- }
- return sb.toString();
+ return totpSecretEncoded;
}
- public String getTotpSecretQrCodeUrl() throws UnsupportedEncodingException {
- String contents = URLEncoder.encode(keyUri, "utf-8");
- return contextUrl + "qrcode" + "?size=246x246&contents=" + contents;
+ public String getTotpSecretQrCode() {
+ return totpSecretQrCode;
}
}
diff --git a/services/src/main/java/org/keycloak/forms/login/freemarker/FreeMarkerLoginFormsProvider.java b/services/src/main/java/org/keycloak/forms/login/freemarker/FreeMarkerLoginFormsProvider.java
index 1a81870..44ee44d 100755
--- a/services/src/main/java/org/keycloak/forms/login/freemarker/FreeMarkerLoginFormsProvider.java
+++ b/services/src/main/java/org/keycloak/forms/login/freemarker/FreeMarkerLoginFormsProvider.java
@@ -279,7 +279,7 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
switch (page) {
case LOGIN_CONFIG_TOTP:
- attributes.put("totp", new TotpBean(realm, user, baseUri));
+ attributes.put("totp", new TotpBean(realm, user));
break;
case LOGIN_UPDATE_PROFILE:
UpdateProfileContext userCtx = (UpdateProfileContext) attributes.get(LoginFormsProvider.UPDATE_PROFILE_CONTEXT_ATTR);
diff --git a/services/src/main/java/org/keycloak/forms/login/freemarker/model/TotpBean.java b/services/src/main/java/org/keycloak/forms/login/freemarker/model/TotpBean.java
index a403176..d1e1d9c 100755
--- a/services/src/main/java/org/keycloak/forms/login/freemarker/model/TotpBean.java
+++ b/services/src/main/java/org/keycloak/forms/login/freemarker/model/TotpBean.java
@@ -20,6 +20,7 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.Base32;
import org.keycloak.models.utils.HmacOTP;
+import org.keycloak.utils.TotpUtils;
import java.io.UnsupportedEncodingException;
import java.net.URI;
@@ -32,17 +33,15 @@ public class TotpBean {
private final String totpSecret;
private final String totpSecretEncoded;
+ private final String totpSecretQrCode;
private final boolean enabled;
- private final String contextUrl;
- private final String keyUri;
- public TotpBean(RealmModel realm, UserModel user, URI baseUri) {
+ public TotpBean(RealmModel realm, UserModel user) {
this.enabled = user.isOtpEnabled();
- this.contextUrl = baseUri.getPath();
-
+
this.totpSecret = HmacOTP.generateSecret(20);
- this.totpSecretEncoded = Base32.encode(totpSecret.getBytes());
- this.keyUri = realm.getOTPPolicy().getKeyURI(realm, user, this.totpSecret);
+ this.totpSecretEncoded = TotpUtils.encode(totpSecret);
+ this.totpSecretQrCode = TotpUtils.qrCode(totpSecret, realm, user);
}
public boolean isEnabled() {
@@ -54,19 +53,11 @@ public class TotpBean {
}
public String getTotpSecretEncoded() {
- StringBuilder sb = new StringBuilder();
- for (int i = 0; i < totpSecretEncoded.length(); i += 4) {
- sb.append(totpSecretEncoded.substring(i, i + 4 < totpSecretEncoded.length() ? i + 4 : totpSecretEncoded.length()));
- if (i + 4 < totpSecretEncoded.length()) {
- sb.append(" ");
- }
- }
- return sb.toString();
+ return totpSecretEncoded;
}
- public String getTotpSecretQrCodeUrl() throws UnsupportedEncodingException {
- String contents = URLEncoder.encode(keyUri, "utf-8");
- return contextUrl + "qrcode" + "?size=246x246&contents=" + contents;
+ public String getTotpSecretQrCode() {
+ return totpSecretQrCode;
}
}
diff --git a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
index 4de67ed..e979465 100644
--- a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
+++ b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
@@ -82,7 +82,6 @@ public class KeycloakApplication extends Application {
singletons.add(new ServerVersionResource());
singletons.add(new RealmsResource());
singletons.add(new AdminRoot());
- classes.add(QRCodeResource.class);
classes.add(ThemeResource.class);
classes.add(JsResource.class);
diff --git a/services/src/main/java/org/keycloak/utils/TotpUtils.java b/services/src/main/java/org/keycloak/utils/TotpUtils.java
new file mode 100644
index 0000000..f9f3b60
--- /dev/null
+++ b/services/src/main/java/org/keycloak/utils/TotpUtils.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.utils;
+
+import com.google.zxing.BarcodeFormat;
+import com.google.zxing.client.j2se.MatrixToImageWriter;
+import com.google.zxing.common.BitMatrix;
+import com.google.zxing.qrcode.QRCodeWriter;
+import org.keycloak.common.util.Base64;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.Base32;
+
+import java.io.ByteArrayOutputStream;
+import java.net.URLEncoder;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class TotpUtils {
+
+ public static String encode(String totpSecret) {
+ String encoded = Base32.encode(totpSecret.getBytes());
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < encoded.length(); i += 4) {
+ sb.append(encoded.substring(i, i + 4 < encoded.length() ? i + 4 : encoded.length()));
+ if (i + 4 < encoded.length()) {
+ sb.append(" ");
+ }
+ }
+ return sb.toString();
+ }
+
+ public static String qrCode(String totpSecret, RealmModel realm, UserModel user) {
+ try {
+ String keyUri = realm.getOTPPolicy().getKeyURI(realm, user, totpSecret);
+
+ int width = 246;
+ int height = 246;
+
+ QRCodeWriter writer = new QRCodeWriter();
+ final BitMatrix bitMatrix = writer.encode(keyUri, BarcodeFormat.QR_CODE, width, height);
+
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ MatrixToImageWriter.writeToStream(bitMatrix, "png", bos);
+ bos.close();
+
+ return Base64.encodeBytes(bos.toByteArray());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+}
diff --git a/themes/src/main/resources/theme/base/account/totp.ftl b/themes/src/main/resources/theme/base/account/totp.ftl
index 1cfefba..8ab36fa 100755
--- a/themes/src/main/resources/theme/base/account/totp.ftl
+++ b/themes/src/main/resources/theme/base/account/totp.ftl
@@ -30,7 +30,7 @@
</li>
<li>
<p>${msg("totpStep2")}</p>
- <img src="${totp.totpSecretQrCodeUrl}" alt="Figure: Barcode"><br/>
+ <img src="data:image/png;base64, ${totp.totpSecretQrCode}" alt="Figure: Barcode"><br/>
<span class="code">${totp.totpSecretEncoded}</span>
</li>
<li>
diff --git a/themes/src/main/resources/theme/base/login/login-config-totp.ftl b/themes/src/main/resources/theme/base/login/login-config-totp.ftl
index 649a833..07a8801 100755
--- a/themes/src/main/resources/theme/base/login/login-config-totp.ftl
+++ b/themes/src/main/resources/theme/base/login/login-config-totp.ftl
@@ -34,7 +34,7 @@
</li>
<li>
<p>${msg("loginTotpStep2")}</p>
- <img src="${totp.totpSecretQrCodeUrl}" alt="Figure: Barcode"><br/>
+ <img src="data:image/png;base64, ${totp.totpSecretQrCode}" alt="Figure: Barcode"><br/>
<span class="code">${totp.totpSecretEncoded}</span>
</li>
<li>
diff --git a/themes/src/main/resources/theme/keycloak/account/resources/css/account.css b/themes/src/main/resources/theme/keycloak/account/resources/css/account.css
index c1353d7..be0c31e 100644
--- a/themes/src/main/resources/theme/keycloak/account/resources/css/account.css
+++ b/themes/src/main/resources/theme/keycloak/account/resources/css/account.css
@@ -204,17 +204,20 @@ ol li {
ol li img {
margin-top: 15px;
- width: 180px;
margin-bottom: 5px;
border: 1px solid #eee;
}
ol li span {
- bottom: 80px;
- left: 200px;
+ padding: 15px;
+ background-color: #f5f5f5;
+ border: 1px solid #eee;
+ top: 46px;
+ left: 270px;
+ right: 50px;
position: absolute;
font-family: courier, monospace;
- font-size: 13px;
+ font-size: 25px;
}
hr + .form-horizontal {