diff --git a/core/src/main/java/org/keycloak/OAuth2Constants.java b/core/src/main/java/org/keycloak/OAuth2Constants.java index 2ef01cbb1f..c73d7dc83e 100644 --- a/core/src/main/java/org/keycloak/OAuth2Constants.java +++ b/core/src/main/java/org/keycloak/OAuth2Constants.java @@ -55,6 +55,8 @@ public interface OAuth2Constants { String IMPLICIT = "implicit"; + String USERNAME="username"; + String PASSWORD = "password"; String CLIENT_CREDENTIALS = "client_credentials"; diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidateOTP.java b/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidateOTP.java index 3e1d949906..ede22e01eb 100755 --- a/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidateOTP.java +++ b/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidateOTP.java @@ -60,6 +60,9 @@ public class ValidateOTP extends AbstractDirectGrantAuthenticator implements Cre String otp = inputData.getFirst("otp"); + // KEYCLOAK-12908 Backwards compatibility. If paramter "otp" is null, then assign "totp". + otp = (otp == null) ? inputData.getFirst("totp") : otp; + // Always use default OTP credential in case of direct grant authentication String credentialId = getCredentialProvider(context.getSession()) .getDefaultCredential(context.getSession(), context.getRealm(), context.getUser()).getId(); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/LoginTotpTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/LoginTotpTest.java index 0a4b17f29e..37761ce059 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/LoginTotpTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/LoginTotpTest.java @@ -21,7 +21,9 @@ import org.junit.Assert; import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import org.keycloak.OAuth2Constants; import org.keycloak.events.Details; +import org.keycloak.models.Constants; import org.keycloak.models.utils.TimeBasedOTP; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.UserRepresentation; @@ -32,11 +34,21 @@ import org.keycloak.testsuite.pages.AppPage.RequestType; import org.keycloak.testsuite.pages.LoginPage; import org.keycloak.testsuite.pages.LoginTotpPage; import org.keycloak.testsuite.util.GreenMailRule; +import org.keycloak.testsuite.util.OAuthClient; import org.keycloak.testsuite.util.RealmRepUtil; import org.keycloak.testsuite.util.UserBuilder; +import javax.ws.rs.client.Client; +import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.client.Entity; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.Form; +import javax.ws.rs.core.Response; +import java.io.IOException; import java.net.MalformedURLException; +import static org.keycloak.testsuite.auth.page.AuthRealm.TEST; + /** * @author Stian Thorgersen * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc. @@ -182,4 +194,38 @@ public class LoginTotpTest extends AbstractTestRealmKeycloakTest { loginPage.assertCurrent(); } + + //KEYCLOAK-12908 + @Test + public void loginWithTotp_getToken_checkCompatibilityCLI() throws IOException { + Client httpClient = ClientBuilder.newClient(); + try { + WebTarget exchangeUrl = httpClient.target(OAuthClient.AUTH_SERVER_ROOT) + .path("/realms") + .path(TEST) + .path("protocol/openid-connect/token"); + + Form form = new Form() + .param(OAuth2Constants.GRANT_TYPE, OAuth2Constants.PASSWORD) + .param(OAuth2Constants.USERNAME, "test-user@localhost") + .param(OAuth2Constants.PASSWORD, "password") + .param(OAuth2Constants.CLIENT_ID, Constants.ADMIN_CLI_CLIENT_ID); + + // Compatibility between "otp" and "totp" + Response response = exchangeUrl.request() + .post(Entity.form(form.param("otp", totp.generateTOTP("totpSecret")))); + + Assert.assertEquals(200, response.getStatus()); + response.close(); + + response = exchangeUrl.request() + .post(Entity.form(form.param("totp", totp.generateTOTP("totpSecret")))); + + Assert.assertEquals(200, response.getStatus()); + response.close(); + + } finally { + httpClient.close(); + } + } }