diff --git a/.github/workflows/scp-deploy.yml b/.github/workflows/scp-deploy.yml index e282af732..a923e51ae 100644 --- a/.github/workflows/scp-deploy.yml +++ b/.github/workflows/scp-deploy.yml @@ -32,16 +32,16 @@ jobs: - name: Deploy powerauth-webflow.war shell: bash run: | - scp -i ~/.ssh/id_rsa **/target/powerauth-webflow-*.war ${{ secrets.SCP_USERNAME }}@${{ secrets.SCP_HOST }}:/opt/apache-tomcat/webapps/powerauth-webflow.war + scp -i ~/.ssh/id_rsa powerauth-webflow/target/powerauth-webflow-*.war ${{ secrets.SCP_USERNAME }}@${{ secrets.SCP_HOST }}:/opt/apache-tomcat/webapps/powerauth-webflow.war - name: Deploy powerauth-nextstep.war shell: bash run: | - scp -i ~/.ssh/id_rsa **/target/powerauth-nextstep-*.war ${{ secrets.SCP_USERNAME }}@${{ secrets.SCP_HOST }}:/opt/apache-tomcat/webapps/powerauth-nextstep.war + scp -i ~/.ssh/id_rsa powerauth-nextstep/target/powerauth-nextstep-*.war ${{ secrets.SCP_USERNAME }}@${{ secrets.SCP_HOST }}:/opt/apache-tomcat/webapps/powerauth-nextstep.war - name: Deploy powerauth-webflow-client.war shell: bash run: | - scp -i ~/.ssh/id_rsa **/target/webflow-client-*.war ${{ secrets.SCP_USERNAME }}@${{ secrets.SCP_HOST }}:/opt/apache-tomcat/webapps/webflow-client.war + scp -i ~/.ssh/id_rsa powerauth-webflow-client/target/powerauth-webflow-client-*.war ${{ secrets.SCP_USERNAME }}@${{ secrets.SCP_HOST }}:/opt/apache-tomcat/webapps/powerauth-webflow-client.war - name: Deploy powerauth-tpp-engine.war shell: bash run: | - scp -i ~/.ssh/id_rsa **/target/powerauth-tpp-engine-*.war ${{ secrets.SCP_USERNAME }}@${{ secrets.SCP_HOST }}:/opt/apache-tomcat/webapps/powerauth-tpp-engine.war + scp -i ~/.ssh/id_rsa powerauth-tpp-engine/target/powerauth-tpp-engine-*.war ${{ secrets.SCP_USERNAME }}@${{ secrets.SCP_HOST }}:/opt/apache-tomcat/webapps/powerauth-tpp-engine.war diff --git a/docs/Basic-Definitions.md b/docs/Basic-Definitions.md index c876e9d88..f867748cb 100644 --- a/docs/Basic-Definitions.md +++ b/docs/Basic-Definitions.md @@ -221,7 +221,7 @@ When the user identity is managed by the Next Step application, Next Step provid ### Next Step -- credential hashing -Next Step application hashes the user credentials using the Argon2 hashing algorithm. The credential verification is performed by comparing the hash of the credential with the stored hash. The hashing algorithm parameters can be changed and in this case the credential hash is recreated with new parameters during the next user authentication and stored in the database. +Next Step application hashes the user credentials using the Argon2 or Bcrypt hashing algorithms. The credential verification is performed by comparing the hash of the credential with the stored hash. For Argon2, the hashing algorithm parameters can be changed to provide strong hashing. In this case the credential hash is recreated with new parameters during the next user authentication and stored in the database. We recommend to use Argon2 instead of Bcrypt, which was added mainly for compatibility reasons and does not support hashing strength configuration. ### Next Step -- database record encryption diff --git a/docs/Migration-Instructions.md b/docs/Migration-Instructions.md index 50e22cd23..649fcf524 100644 --- a/docs/Migration-Instructions.md +++ b/docs/Migration-Instructions.md @@ -2,6 +2,7 @@ This page contains PowerAuth Web Flow migration instructions. +- [PowerAuth Web Flow 1.9.0](./Web-Flow-1.9.0.md) - [PowerAuth Web Flow 1.8.0](./Web-Flow-1.8.0.md) - [PowerAuth Web Flow 1.7.0](./Web-Flow-1.7.0.md) - [PowerAuth Web Flow 1.6.0](./Web-Flow-1.6.0.md) diff --git a/docs/Next-Step-Server-REST-API-Reference.md b/docs/Next-Step-Server-REST-API-Reference.md index 006457ace..e71b63963 100644 --- a/docs/Next-Step-Server-REST-API-Reference.md +++ b/docs/Next-Step-Server-REST-API-Reference.md @@ -5701,6 +5701,8 @@ The list of expected status codes: } ``` +Possible algorithm names: `ARGON_2D`, `ARGON_2I`, `ARGON_2ID`, `BCRYPT`. For `BCRYPT` empty parameters should be used as this algorithm does not support hashing algorithm parameterization. + #### Response 200 - Headers: diff --git a/docs/Web-Flow-1.9.0.md b/docs/Web-Flow-1.9.0.md new file mode 100644 index 000000000..37a8e6a8f --- /dev/null +++ b/docs/Web-Flow-1.9.0.md @@ -0,0 +1,5 @@ +# Migration from 1.8.0 to 1.9.0 + +This guide contains instructions for migration from PowerAuth WebFlow version `1.8.x` to version `1.9.0`. + +There are no database changes needed for this version. diff --git a/pom.xml b/pom.xml index 1604b20f1..85d998f4e 100644 --- a/pom.xml +++ b/pom.xml @@ -8,13 +8,13 @@ io.getlime.security powerauth-webflow-parent - 1.8.0 + 1.9.0 pom org.springframework.boot spring-boot-starter-parent - 3.3.2 + 3.3.4 @@ -93,21 +93,21 @@ 17 1.78.1 3.5.3 - 1.6.4 + 1.6.6 2.6.0 - 2.2.22 + 2.2.25 1.4.4 20240325.1 - 7.4 + 8.0 - 1.10.0 - 1.8.0 - 1.8.0 - 1.8.0 + 1.11.0 + 1.9.0 + 1.9.0 + 1.9.0 @@ -345,7 +345,7 @@ de.skuzzle.enforcer restrict-imports-enforcer-rule - 2.5.0 + 2.6.0 diff --git a/powerauth-data-adapter-client/pom.xml b/powerauth-data-adapter-client/pom.xml index 2c117cbb8..abeb0de16 100644 --- a/powerauth-data-adapter-client/pom.xml +++ b/powerauth-data-adapter-client/pom.xml @@ -28,7 +28,7 @@ powerauth-webflow-parent io.getlime.security - 1.8.0 + 1.9.0 diff --git a/powerauth-data-adapter-model/pom.xml b/powerauth-data-adapter-model/pom.xml index 7eba0a47c..053e7555e 100644 --- a/powerauth-data-adapter-model/pom.xml +++ b/powerauth-data-adapter-model/pom.xml @@ -28,7 +28,7 @@ powerauth-webflow-parent io.getlime.security - 1.8.0 + 1.9.0 diff --git a/powerauth-mtoken-model/pom.xml b/powerauth-mtoken-model/pom.xml index 07e8279fb..475952411 100644 --- a/powerauth-mtoken-model/pom.xml +++ b/powerauth-mtoken-model/pom.xml @@ -27,7 +27,7 @@ powerauth-webflow-parent io.getlime.security - 1.8.0 + 1.9.0 diff --git a/powerauth-nextstep-client/pom.xml b/powerauth-nextstep-client/pom.xml index 89d0978ae..93f5f8394 100644 --- a/powerauth-nextstep-client/pom.xml +++ b/powerauth-nextstep-client/pom.xml @@ -25,7 +25,7 @@ powerauth-webflow-parent io.getlime.security - 1.8.0 + 1.9.0 powerauth-nextstep-client diff --git a/powerauth-nextstep-model/pom.xml b/powerauth-nextstep-model/pom.xml index 5519b7197..786ce4b08 100644 --- a/powerauth-nextstep-model/pom.xml +++ b/powerauth-nextstep-model/pom.xml @@ -29,7 +29,7 @@ io.getlime.security powerauth-webflow-parent - 1.8.0 + 1.9.0 diff --git a/powerauth-nextstep-model/src/main/java/io/getlime/security/powerauth/lib/nextstep/model/entity/enumeration/HashAlgorithm.java b/powerauth-nextstep-model/src/main/java/io/getlime/security/powerauth/lib/nextstep/model/entity/enumeration/HashAlgorithm.java index 6a06a6636..c076fdc30 100644 --- a/powerauth-nextstep-model/src/main/java/io/getlime/security/powerauth/lib/nextstep/model/entity/enumeration/HashAlgorithm.java +++ b/powerauth-nextstep-model/src/main/java/io/getlime/security/powerauth/lib/nextstep/model/entity/enumeration/HashAlgorithm.java @@ -17,11 +17,14 @@ */ package io.getlime.security.powerauth.lib.nextstep.model.entity.enumeration; +import lombok.Getter; + /** * Enumeration representing hashing algorithms. * * @author Roman Strobl, roman.strobl@wultra.com */ +@Getter public enum HashAlgorithm { /** @@ -37,35 +40,30 @@ public enum HashAlgorithm { /** * Algorithm argon2id. */ - ARGON_2ID("argon2id", 2); + ARGON_2ID("argon2id", 2), + + BCRYPT("bcrypt"); private final String name; - private final int id; + private final Integer id; /** * Hash algorithm constructor. - * @param name Algorithm name for Modular Crypt Format. - * @param id Algorithm ID in Bouncy Castle library. + * @param name Algorithm name. */ - HashAlgorithm(String name, int id) { + HashAlgorithm(String name) { this.name = name; - this.id = id; + this.id = null; } /** - * Get algorithm name for Modular Crypt Format. - * @return Algorithm name. - */ - public String getName() { - return name; - } - - /** - * Get algorithm ID in Bouncy Castle library. - * @return Algorithm ID. + * Hash algorithm constructor for Argon family of algorithms. + * @param name Algorithm name for Modular Crypt Format. + * @param id Algorithm ID in Bouncy Castle library (Argon algorithms only). */ - public int getId() { - return id; + HashAlgorithm(String name, int id) { + this.name = name; + this.id = id; } } diff --git a/powerauth-nextstep/pom.xml b/powerauth-nextstep/pom.xml index c7ef55d63..71f1e44c3 100644 --- a/powerauth-nextstep/pom.xml +++ b/powerauth-nextstep/pom.xml @@ -28,7 +28,7 @@ io.getlime.security powerauth-webflow-parent - 1.8.0 + 1.9.0 diff --git a/powerauth-nextstep/src/main/java/io/getlime/security/powerauth/app/nextstep/service/CredentialProtectionService.java b/powerauth-nextstep/src/main/java/io/getlime/security/powerauth/app/nextstep/service/CredentialProtectionService.java index 52b82e1f6..55ba2ff9c 100644 --- a/powerauth-nextstep/src/main/java/io/getlime/security/powerauth/app/nextstep/service/CredentialProtectionService.java +++ b/powerauth-nextstep/src/main/java/io/getlime/security/powerauth/app/nextstep/service/CredentialProtectionService.java @@ -37,6 +37,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.crypto.bcrypt.BCrypt; import org.springframework.stereotype.Service; import java.io.IOException; @@ -100,6 +101,10 @@ public CredentialValue protectCredential(String credentialValue, CredentialEntit final String hashedValue = argon2Hash.toString(); return credentialValueConverter.toDBValue(hashedValue, userId, credentialDefinition); } + case BCRYPT -> { + String hashedValue = BCrypt.hashpw(credentialValue, BCrypt.gensalt()); + return credentialValueConverter.toDBValue(hashedValue, userId, credentialDefinition); + } default -> throw new InvalidConfigurationException("Unsupported hashing algorithm: " + algorithm); } } @@ -132,6 +137,13 @@ public boolean verifyCredential(String credentialValue, CredentialEntity credent } return succeeded; } + case BCRYPT -> { + boolean succeeded = BCrypt.checkpw(credentialValue, decryptedCredentialValue); + if (succeeded) { + updateStoredCredentialValueIfRequired(credentialValue, credential); + } + return succeeded; + } default -> throw new InvalidConfigurationException("Unsupported hashing algorithm: " + algorithm); } } @@ -154,6 +166,7 @@ public boolean verifyCredentialHistory(String credentialValue, CredentialHistory final HashAlgorithm algorithm = hashingConfig.getAlgorithm(); return switch (algorithm) { case ARGON_2I, ARGON_2D, ARGON_2ID -> verifyCredentialUsingArgon2(credentialValue, algorithm, decryptedCredentialValue); + case BCRYPT -> BCrypt.checkpw(credentialValue, decryptedCredentialValue); }; } @@ -335,7 +348,7 @@ private void updateStoredCredentialValueIfRequired(String credentialValue, Crede updateRequired = true; } else { // Check actual argon2 parameters from the hash in the database and compare them with credential definition - updateRequired = updateRequired || !argon2ParamMatch(extractCredentialValue(credential), credentialDefinition.getHashingConfig().getAlgorithm(), credential.getHashingConfig().getParameters()); + updateRequired = updateRequired || (credentialDefinition.getHashingConfig().getAlgorithm() != HashAlgorithm.BCRYPT && !argon2ParamMatch(extractCredentialValue(credential), credentialDefinition.getHashingConfig().getAlgorithm(), credential.getHashingConfig().getParameters())); } } diff --git a/powerauth-nextstep/src/test/java/io/getlime/security/powerauth/app/nextstep/service/CredentialProtectionServiceTest.java b/powerauth-nextstep/src/test/java/io/getlime/security/powerauth/app/nextstep/service/CredentialProtectionServiceTest.java new file mode 100644 index 000000000..32fda9718 --- /dev/null +++ b/powerauth-nextstep/src/test/java/io/getlime/security/powerauth/app/nextstep/service/CredentialProtectionServiceTest.java @@ -0,0 +1,97 @@ +/* + * PowerAuth Web Flow and related software components + * Copyright (C) 2024 Wultra s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package io.getlime.security.powerauth.app.nextstep.service; + +import com.fasterxml.jackson.databind.ObjectMapper; +import io.getlime.security.powerauth.app.nextstep.NextStepTest; +import io.getlime.security.powerauth.app.nextstep.repository.model.entity.CredentialDefinitionEntity; +import io.getlime.security.powerauth.app.nextstep.repository.model.entity.CredentialEntity; +import io.getlime.security.powerauth.app.nextstep.repository.model.entity.HashConfigEntity; +import io.getlime.security.powerauth.app.nextstep.repository.model.entity.UserIdentityEntity; +import io.getlime.security.powerauth.lib.nextstep.model.entity.CredentialValue; +import io.getlime.security.powerauth.lib.nextstep.model.entity.enumeration.EncryptionAlgorithm; +import io.getlime.security.powerauth.lib.nextstep.model.entity.enumeration.HashAlgorithm; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Tests for password hashing. + * + * @author Roman Strobl, roman.strobl@wultra.com + */ +class CredentialProtectionServiceTest extends NextStepTest { + + @Autowired + private CredentialProtectionService credentialProtectionService; + + @Test + void testBcrypt() throws Exception { + final CredentialEntity credentialEntity = new CredentialEntity(); + final UserIdentityEntity user = new UserIdentityEntity(); + final HashConfigEntity hashConfig = new HashConfigEntity(); + final CredentialDefinitionEntity credentialDefinition = new CredentialDefinitionEntity(); + credentialDefinition.setEncryptionAlgorithm(EncryptionAlgorithm.NO_ENCRYPTION); + credentialDefinition.setHashingConfig(hashConfig); + hashConfig.setAlgorithm(HashAlgorithm.BCRYPT); + hashConfig.setParameters("{}"); + user.setUserId("test"); + credentialEntity.setUser(user); + credentialEntity.setCredentialDefinition(credentialDefinition); + credentialEntity.setHashingConfig(hashConfig); + final CredentialValue hashed = credentialProtectionService.protectCredential("test", credentialEntity); + credentialEntity.setValue(hashed.getValue()); + assertEquals(EncryptionAlgorithm.NO_ENCRYPTION, hashed.getEncryptionAlgorithm()); + assertNotEquals("test", hashed.getValue()); + assertTrue(hashed.getValue().startsWith("$2a$10$")); + assertTrue(credentialProtectionService.verifyCredential("test", credentialEntity)); + } + + @Test + void testArgon2() throws Exception { + final CredentialEntity credentialEntity = new CredentialEntity(); + final UserIdentityEntity user = new UserIdentityEntity(); + final HashConfigEntity hashConfig = new HashConfigEntity(); + final CredentialDefinitionEntity credentialDefinition = new CredentialDefinitionEntity(); + credentialDefinition.setEncryptionAlgorithm(EncryptionAlgorithm.NO_ENCRYPTION); + credentialDefinition.setHashingConfig(hashConfig); + hashConfig.setAlgorithm(HashAlgorithm.ARGON_2I); + final Map params = Map.of( + "version", "19", + "iterations", "4", + "memory", "16", + "parallelism", "2", + "outputLength", "32" + ); + hashConfig.setParameters(new ObjectMapper().writeValueAsString(params)); + user.setUserId("test"); + credentialEntity.setUser(user); + credentialEntity.setCredentialDefinition(credentialDefinition); + credentialEntity.setHashingConfig(hashConfig); + final CredentialValue hashed = credentialProtectionService.protectCredential("test", credentialEntity); + credentialEntity.setValue(hashed.getValue()); + assertEquals(EncryptionAlgorithm.NO_ENCRYPTION, hashed.getEncryptionAlgorithm()); + assertNotEquals("test", hashed.getValue()); + assertTrue(hashed.getValue().startsWith("$argon2i$v=19$m=65536,t=4,p=2$")); + assertTrue(credentialProtectionService.verifyCredential("test", credentialEntity)); + } + +} \ No newline at end of file diff --git a/powerauth-tpp-engine-client/pom.xml b/powerauth-tpp-engine-client/pom.xml index 50e5472c3..aefaf2257 100644 --- a/powerauth-tpp-engine-client/pom.xml +++ b/powerauth-tpp-engine-client/pom.xml @@ -25,7 +25,7 @@ powerauth-webflow-parent io.getlime.security - 1.8.0 + 1.9.0 powerauth-tpp-engine-client diff --git a/powerauth-tpp-engine-model/pom.xml b/powerauth-tpp-engine-model/pom.xml index 9eadfebc3..aaa74dd52 100644 --- a/powerauth-tpp-engine-model/pom.xml +++ b/powerauth-tpp-engine-model/pom.xml @@ -24,7 +24,7 @@ powerauth-webflow-parent io.getlime.security - 1.8.0 + 1.9.0 powerauth-tpp-engine-model diff --git a/powerauth-tpp-engine/pom.xml b/powerauth-tpp-engine/pom.xml index 259ef8fa3..0516ad74f 100644 --- a/powerauth-tpp-engine/pom.xml +++ b/powerauth-tpp-engine/pom.xml @@ -28,7 +28,7 @@ io.getlime.security powerauth-webflow-parent - 1.8.0 + 1.9.0 diff --git a/powerauth-webflow-authentication-approval-sca/pom.xml b/powerauth-webflow-authentication-approval-sca/pom.xml index 448f641fd..862554c93 100644 --- a/powerauth-webflow-authentication-approval-sca/pom.xml +++ b/powerauth-webflow-authentication-approval-sca/pom.xml @@ -25,7 +25,7 @@ powerauth-webflow-parent io.getlime.security - 1.8.0 + 1.9.0 powerauth-webflow-authentication-approval-sca diff --git a/powerauth-webflow-authentication-consent/pom.xml b/powerauth-webflow-authentication-consent/pom.xml index 3390ece85..54bd360a1 100644 --- a/powerauth-webflow-authentication-consent/pom.xml +++ b/powerauth-webflow-authentication-consent/pom.xml @@ -25,7 +25,7 @@ powerauth-webflow-parent io.getlime.security - 1.8.0 + 1.9.0 powerauth-webflow-authentication-consent diff --git a/powerauth-webflow-authentication-form/pom.xml b/powerauth-webflow-authentication-form/pom.xml index e93b3dc18..df28c5799 100644 --- a/powerauth-webflow-authentication-form/pom.xml +++ b/powerauth-webflow-authentication-form/pom.xml @@ -25,7 +25,7 @@ powerauth-webflow-parent io.getlime.security - 1.8.0 + 1.9.0 powerauth-webflow-authentication-form diff --git a/powerauth-webflow-authentication-init/pom.xml b/powerauth-webflow-authentication-init/pom.xml index a85824923..62755a3b7 100644 --- a/powerauth-webflow-authentication-init/pom.xml +++ b/powerauth-webflow-authentication-init/pom.xml @@ -25,7 +25,7 @@ powerauth-webflow-parent io.getlime.security - 1.8.0 + 1.9.0 powerauth-webflow-authentication-init diff --git a/powerauth-webflow-authentication-login-sca/pom.xml b/powerauth-webflow-authentication-login-sca/pom.xml index fa0f331b4..701b8e12d 100644 --- a/powerauth-webflow-authentication-login-sca/pom.xml +++ b/powerauth-webflow-authentication-login-sca/pom.xml @@ -25,7 +25,7 @@ powerauth-webflow-parent io.getlime.security - 1.8.0 + 1.9.0 powerauth-webflow-authentication-login-sca diff --git a/powerauth-webflow-authentication-mtoken/pom.xml b/powerauth-webflow-authentication-mtoken/pom.xml index 0b0f8be1b..88e072da6 100644 --- a/powerauth-webflow-authentication-mtoken/pom.xml +++ b/powerauth-webflow-authentication-mtoken/pom.xml @@ -25,7 +25,7 @@ powerauth-webflow-parent io.getlime.security - 1.8.0 + 1.9.0 powerauth-webflow-authentication-mtoken diff --git a/powerauth-webflow-authentication-mtoken/src/main/java/io/getlime/security/powerauth/lib/webflow/authentication/mtoken/controller/MessageController.java b/powerauth-webflow-authentication-mtoken/src/main/java/io/getlime/security/powerauth/lib/webflow/authentication/mtoken/controller/MessageController.java index 33e4cff3f..a04247957 100644 --- a/powerauth-webflow-authentication-mtoken/src/main/java/io/getlime/security/powerauth/lib/webflow/authentication/mtoken/controller/MessageController.java +++ b/powerauth-webflow-authentication-mtoken/src/main/java/io/getlime/security/powerauth/lib/webflow/authentication/mtoken/controller/MessageController.java @@ -18,8 +18,9 @@ package io.getlime.security.powerauth.lib.webflow.authentication.mtoken.controller; import io.getlime.security.powerauth.lib.webflow.authentication.model.request.WebSocketRegistrationRequest; -import io.getlime.security.powerauth.lib.webflow.authentication.service.WebSocketMessageService; +import io.getlime.security.powerauth.lib.webflow.authentication.service.websocket.WebSocketMessageService; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.messaging.handler.annotation.MessageMapping; import org.springframework.messaging.simp.SimpMessageHeaderAccessor; import org.springframework.stereotype.Controller; @@ -30,6 +31,7 @@ * @author Roman Strobl */ @Controller +@ConditionalOnProperty(name = "powerauth.webflow.websockets.enabled", havingValue = "true") public class MessageController { private final WebSocketMessageService webSocketMessageService; diff --git a/powerauth-webflow-authentication-mtoken/src/main/java/io/getlime/security/powerauth/lib/webflow/authentication/mtoken/controller/MobileAppApiController.java b/powerauth-webflow-authentication-mtoken/src/main/java/io/getlime/security/powerauth/lib/webflow/authentication/mtoken/controller/MobileAppApiController.java index 871003fb4..43baccb87 100644 --- a/powerauth-webflow-authentication-mtoken/src/main/java/io/getlime/security/powerauth/lib/webflow/authentication/mtoken/controller/MobileAppApiController.java +++ b/powerauth-webflow-authentication-mtoken/src/main/java/io/getlime/security/powerauth/lib/webflow/authentication/mtoken/controller/MobileAppApiController.java @@ -55,7 +55,7 @@ import io.getlime.security.powerauth.lib.webflow.authentication.mtoken.model.response.MobileTokenAuthenticationResponse; import io.getlime.security.powerauth.lib.webflow.authentication.service.AuthMethodQueryService; import io.getlime.security.powerauth.lib.webflow.authentication.service.PowerAuthOperationService; -import io.getlime.security.powerauth.lib.webflow.authentication.service.WebSocketMessageService; +import io.getlime.security.powerauth.lib.webflow.authentication.service.websocket.WebSocketMessageService; import io.getlime.security.powerauth.rest.api.spring.annotation.PowerAuth; import io.getlime.security.powerauth.rest.api.spring.annotation.PowerAuthToken; import io.getlime.security.powerauth.rest.api.spring.authentication.PowerAuthActivation; @@ -65,6 +65,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -95,6 +96,12 @@ public class MobileAppApiController extends AuthMethodController getOperationConfigs(List getOperationConfigs(List powerauth-webflow-parent io.getlime.security - 1.8.0 + 1.9.0 powerauth-webflow-authentication-operation-review diff --git a/powerauth-webflow-authentication-sms/pom.xml b/powerauth-webflow-authentication-sms/pom.xml index d51dda749..c0fd90771 100644 --- a/powerauth-webflow-authentication-sms/pom.xml +++ b/powerauth-webflow-authentication-sms/pom.xml @@ -25,7 +25,7 @@ powerauth-webflow-parent io.getlime.security - 1.8.0 + 1.9.0 powerauth-webflow-authentication-sms diff --git a/powerauth-webflow-authentication/pom.xml b/powerauth-webflow-authentication/pom.xml index 063b582f1..a9a5b43f5 100644 --- a/powerauth-webflow-authentication/pom.xml +++ b/powerauth-webflow-authentication/pom.xml @@ -25,7 +25,7 @@ powerauth-webflow-parent io.getlime.security - 1.8.0 + 1.9.0 io.getlime.security diff --git a/powerauth-webflow-authentication/src/main/java/io/getlime/security/powerauth/lib/webflow/authentication/configuration/WebFlowServicesConfiguration.java b/powerauth-webflow-authentication/src/main/java/io/getlime/security/powerauth/lib/webflow/authentication/configuration/WebFlowServicesConfiguration.java index 124fc7e33..db5cb7729 100644 --- a/powerauth-webflow-authentication/src/main/java/io/getlime/security/powerauth/lib/webflow/authentication/configuration/WebFlowServicesConfiguration.java +++ b/powerauth-webflow-authentication/src/main/java/io/getlime/security/powerauth/lib/webflow/authentication/configuration/WebFlowServicesConfiguration.java @@ -77,6 +77,12 @@ public class WebFlowServicesConfiguration { @Value("${powerauth.webflow.pa.operations.enabled}") private boolean powerAuthOperationSupportEnabled; + /** + * WebSocket support configuration. + */ + @Value("${powerauth.webflow.websockets.enabled:true}") + private boolean webSocketSupportEnabled; + /** * Authentication type which configures how username and password is transferred for verification. */ @@ -219,6 +225,14 @@ public boolean isPowerAuthOperationSupportEnabled() { return powerAuthOperationSupportEnabled; } + /** + * Get whether WebSocket support is enabled. + * @return Whether WebSocket support is enabled. + */ + public boolean isWebSocketSupportEnabled() { + return webSocketSupportEnabled; + } + /** * Get authentication type which configures how username and password is transferred for verification. * @return Authentication type. diff --git a/powerauth-webflow-authentication/src/main/java/io/getlime/security/powerauth/lib/webflow/authentication/listener/WebSocketDisconnectListener.java b/powerauth-webflow-authentication/src/main/java/io/getlime/security/powerauth/lib/webflow/authentication/listener/WebSocketDisconnectListener.java index 4659fd122..20812305d 100644 --- a/powerauth-webflow-authentication/src/main/java/io/getlime/security/powerauth/lib/webflow/authentication/listener/WebSocketDisconnectListener.java +++ b/powerauth-webflow-authentication/src/main/java/io/getlime/security/powerauth/lib/webflow/authentication/listener/WebSocketDisconnectListener.java @@ -23,6 +23,7 @@ import io.getlime.security.powerauth.lib.webflow.authentication.service.OperationCancellationService; import io.getlime.security.powerauth.lib.webflow.authentication.service.OperationSessionService; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; import org.springframework.web.socket.CloseStatus; @@ -34,6 +35,7 @@ * @author Roman Strobl, roman.strobl@wultra.com */ @Component +@ConditionalOnProperty(name = "powerauth.webflow.websockets.enabled", havingValue = "true") public class WebSocketDisconnectListener implements ApplicationListener { private final OperationSessionService operationSessionService; diff --git a/powerauth-webflow-authentication/src/main/java/io/getlime/security/powerauth/lib/webflow/authentication/service/websocket/WebSocketMessageService.java b/powerauth-webflow-authentication/src/main/java/io/getlime/security/powerauth/lib/webflow/authentication/service/websocket/WebSocketMessageService.java new file mode 100644 index 000000000..9d94f4c7c --- /dev/null +++ b/powerauth-webflow-authentication/src/main/java/io/getlime/security/powerauth/lib/webflow/authentication/service/websocket/WebSocketMessageService.java @@ -0,0 +1,63 @@ +/* + * Copyright 2024 Wultra s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package io.getlime.security.powerauth.lib.webflow.authentication.service.websocket; + +import io.getlime.security.powerauth.lib.nextstep.model.enumeration.AuthResult; + +/** + * Interface for WebSocket messages. + * + * @author Roman Strobl, roman.strobl@wultra.com + */ +public interface WebSocketMessageService { + + /** + * Notification of clients about completed authorization. + * + * @param operationId Operation ID. + * @param authResult Authorization result. + */ + void notifyAuthorizationComplete(String operationId, AuthResult authResult); + + /** + * Sends a message about successful websocket registration to the user. + * + * @param operationHash Operation hash. + * @param sessionId Session ID. + * @param registrationSucceeded Whether Web Socket registration was successful. + */ + void sendRegistrationMessage(String operationHash, String sessionId, boolean registrationSucceeded); + + /** + * Get Web Socket session ID for given operation hash. + * + * @param operationHash Operation hash. + * @return Web Socket session ID. + */ + String lookupWebSocketSessionId(String operationHash); + + /** + * Store a mapping for new web socket identifier to the Web Socket session with given ID. + * + * @param operationHash Operation hash. + * @param webSocketSessionId Web Socket Session ID. + * @param clientIpAddress Remote client IP address. + * @return Whether Web Socket registration was successful. + */ + boolean registerWebSocketSession(String operationHash, String webSocketSessionId, String clientIpAddress); + +} diff --git a/powerauth-webflow-authentication/src/main/java/io/getlime/security/powerauth/lib/webflow/authentication/service/websocket/WebSocketMessageServiceDummy.java b/powerauth-webflow-authentication/src/main/java/io/getlime/security/powerauth/lib/webflow/authentication/service/websocket/WebSocketMessageServiceDummy.java new file mode 100644 index 000000000..0f027984d --- /dev/null +++ b/powerauth-webflow-authentication/src/main/java/io/getlime/security/powerauth/lib/webflow/authentication/service/websocket/WebSocketMessageServiceDummy.java @@ -0,0 +1,56 @@ +/* + * Copyright 2024 Wultra s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package io.getlime.security.powerauth.lib.webflow.authentication.service.websocket; + +import io.getlime.security.powerauth.lib.nextstep.model.enumeration.AuthResult; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Service; + +/** + * Dummy WebSocket service. + * + * @author Roman Strobl, roman.strobl@wultra.com + */ +@Service +@Slf4j +@ConditionalOnProperty(name = "powerauth.webflow.websockets.enabled", havingValue = "false", matchIfMissing = true) +public class WebSocketMessageServiceDummy implements WebSocketMessageService { + + { + logger.info("WebSocketMessageServiceDummy was initialized."); + } + + @Override + public void notifyAuthorizationComplete(String operationId, AuthResult authResult) { + } + + @Override + public void sendRegistrationMessage(String operationHash, String sessionId, boolean registrationSucceeded) { + } + + @Override + public String lookupWebSocketSessionId(String operationHash) { + return null; + } + + @Override + public boolean registerWebSocketSession(String operationHash, String webSocketSessionId, String clientIpAddress) { + return false; + } + +} diff --git a/powerauth-webflow-authentication/src/main/java/io/getlime/security/powerauth/lib/webflow/authentication/service/WebSocketMessageService.java b/powerauth-webflow-authentication/src/main/java/io/getlime/security/powerauth/lib/webflow/authentication/service/websocket/WebSocketMessageServiceImpl.java similarity index 78% rename from powerauth-webflow-authentication/src/main/java/io/getlime/security/powerauth/lib/webflow/authentication/service/WebSocketMessageService.java rename to powerauth-webflow-authentication/src/main/java/io/getlime/security/powerauth/lib/webflow/authentication/service/websocket/WebSocketMessageServiceImpl.java index a7c7761eb..1ea2495f9 100644 --- a/powerauth-webflow-authentication/src/main/java/io/getlime/security/powerauth/lib/webflow/authentication/service/WebSocketMessageService.java +++ b/powerauth-webflow-authentication/src/main/java/io/getlime/security/powerauth/lib/webflow/authentication/service/websocket/WebSocketMessageServiceImpl.java @@ -14,12 +14,15 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package io.getlime.security.powerauth.lib.webflow.authentication.service; +package io.getlime.security.powerauth.lib.webflow.authentication.service.websocket; import io.getlime.security.powerauth.lib.nextstep.model.enumeration.AuthResult; import io.getlime.security.powerauth.lib.webflow.authentication.model.response.WebSocketAuthorizationResponse; import io.getlime.security.powerauth.lib.webflow.authentication.model.response.WebSocketRegistrationResponse; +import io.getlime.security.powerauth.lib.webflow.authentication.service.OperationSessionService; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.messaging.MessageHeaders; import org.springframework.messaging.simp.SimpMessageHeaderAccessor; import org.springframework.messaging.simp.SimpMessageType; @@ -33,7 +36,9 @@ * @author Petr Dvorak, petr@wultra.com */ @Service -public class WebSocketMessageService { +@Slf4j +@ConditionalOnProperty(name = "powerauth.webflow.websockets.enabled", havingValue = "true") +public class WebSocketMessageServiceImpl implements WebSocketMessageService { private final SimpMessagingTemplate websocket; private final OperationSessionService operationSessionService; @@ -44,9 +49,10 @@ public class WebSocketMessageService { * @param operationSessionService Operation to session mapping service. */ @Autowired - public WebSocketMessageService(SimpMessagingTemplate websocket, OperationSessionService operationSessionService) { + public WebSocketMessageServiceImpl(SimpMessagingTemplate websocket, OperationSessionService operationSessionService) { this.websocket = websocket; this.operationSessionService = operationSessionService; + logger.info("WebSocketMessageServiceImpl was initialized."); } /** @@ -62,12 +68,7 @@ private MessageHeaders createHeaders(String webSocketSessionId) { return headerAccessor.getMessageHeaders(); } - /** - * Notification of clients about completed authorization. - * - * @param operationId Operation ID. - * @param authResult Authorization result. - */ + @Override public void notifyAuthorizationComplete(String operationId, AuthResult authResult) { final String webSocketId = operationSessionService.generateOperationHash(operationId); final String sessionId = lookupWebSocketSessionId(webSocketId); @@ -79,13 +80,7 @@ public void notifyAuthorizationComplete(String operationId, AuthResult authResul } } - /** - * Sends a message about successful websocket registration to the user. - * - * @param operationHash Operation hash. - * @param sessionId Session ID. - * @param registrationSucceeded Whether Web Socket registration was successful. - */ + @Override public void sendRegistrationMessage(String operationHash, String sessionId, boolean registrationSucceeded) { WebSocketRegistrationResponse registrationResponse = new WebSocketRegistrationResponse(); registrationResponse.setWebSocketId(operationHash); @@ -93,24 +88,12 @@ public void sendRegistrationMessage(String operationHash, String sessionId, bool websocket.convertAndSendToUser(sessionId, "/topic/registration", registrationResponse, createHeaders(sessionId)); } - /** - * Get Web Socket session ID for given operation hash. - * - * @param operationHash Operation hash. - * @return Web Socket session ID. - */ + @Override public String lookupWebSocketSessionId(String operationHash) { return operationSessionService.lookupWebSocketSessionIdByOperationHash(operationHash); } - /** - * Store a mapping for new web socket identifier to the Web Socket session with given ID. - * - * @param operationHash Operation hash. - * @param webSocketSessionId Web Socket Session ID. - * @param clientIpAddress Remote client IP address. - * @return Whether Web Socket registration was successful. - */ + @Override public boolean registerWebSocketSession(String operationHash, String webSocketSessionId, String clientIpAddress) { return operationSessionService.registerWebSocketSession(operationHash, webSocketSessionId, clientIpAddress); } diff --git a/powerauth-webflow-client/pom.xml b/powerauth-webflow-client/pom.xml index eb1da0096..20bcc4a70 100644 --- a/powerauth-webflow-client/pom.xml +++ b/powerauth-webflow-client/pom.xml @@ -30,7 +30,7 @@ powerauth-webflow-parent io.getlime.security - 1.8.0 + 1.9.0 diff --git a/powerauth-webflow-i18n/pom.xml b/powerauth-webflow-i18n/pom.xml index 288f266bb..7a4b16641 100644 --- a/powerauth-webflow-i18n/pom.xml +++ b/powerauth-webflow-i18n/pom.xml @@ -28,7 +28,7 @@ io.getlime.security powerauth-webflow-parent - 1.8.0 + 1.9.0 diff --git a/powerauth-webflow-resources/pom.xml b/powerauth-webflow-resources/pom.xml index b17262fd0..b3692668b 100644 --- a/powerauth-webflow-resources/pom.xml +++ b/powerauth-webflow-resources/pom.xml @@ -25,7 +25,7 @@ powerauth-webflow-parent io.getlime.security - 1.8.0 + 1.9.0 io.getlime.security diff --git a/powerauth-webflow/pom.xml b/powerauth-webflow/pom.xml index 6cc8dee00..847fc2f69 100644 --- a/powerauth-webflow/pom.xml +++ b/powerauth-webflow/pom.xml @@ -24,7 +24,7 @@ io.getlime.security powerauth-webflow-parent - 1.8.0 + 1.9.0 powerauth-webflow @@ -206,7 +206,7 @@ com.github.eirslett frontend-maven-plugin - 1.15.0 + 1.15.1 target @@ -242,6 +242,39 @@ + + websockets-enabled + + true + + + + org.springframework + spring-messaging + + + org.springframework + spring-websocket + + + + + + websockets-disabled + + + org.springframework + spring-messaging + provided + + + org.springframework + spring-websocket + provided + + + + test-repository diff --git a/powerauth-webflow/src/main/java/io/getlime/security/powerauth/app/webflow/configuration/WebFlowServerConfiguration.java b/powerauth-webflow/src/main/java/io/getlime/security/powerauth/app/webflow/configuration/WebFlowServerConfiguration.java index 642fafe71..1406ac561 100644 --- a/powerauth-webflow/src/main/java/io/getlime/security/powerauth/app/webflow/configuration/WebFlowServerConfiguration.java +++ b/powerauth-webflow/src/main/java/io/getlime/security/powerauth/app/webflow/configuration/WebFlowServerConfiguration.java @@ -187,6 +187,12 @@ public class WebFlowServerConfiguration { @Value("${powerauth.webflow.approval.certificate.signer.ica.extensionInstallURLFirefox:}") private String icaExtensionInstallURLFirefox; + /** + * WebSocket support configuration. + */ + @Value("${powerauth.webflow.websockets.enabled:true}") + private boolean webSocketSupportEnabled; + /** * Configuration constructor. * @param auditFactory Audit factory. @@ -392,6 +398,14 @@ public String getIcaExtensionInstallURLFirefox() { return icaExtensionInstallURLFirefox; } + /** + * Get whether WebSocket support is enabled. + * @return Whether WebSocket support is enabled. + */ + public boolean isWebSocketSupportEnabled() { + return webSocketSupportEnabled; + } + /** * Prepare audit interface. * @return Audit interface. diff --git a/powerauth-webflow/src/main/java/io/getlime/security/powerauth/app/webflow/configuration/WebMvcConfiguration.java b/powerauth-webflow/src/main/java/io/getlime/security/powerauth/app/webflow/configuration/WebMvcConfiguration.java index 5b4353193..6521e5398 100644 --- a/powerauth-webflow/src/main/java/io/getlime/security/powerauth/app/webflow/configuration/WebMvcConfiguration.java +++ b/powerauth-webflow/src/main/java/io/getlime/security/powerauth/app/webflow/configuration/WebMvcConfiguration.java @@ -26,6 +26,7 @@ import io.getlime.security.powerauth.rest.api.spring.annotation.support.PowerAuthEncryptionArgumentResolver; import io.getlime.security.powerauth.rest.api.spring.annotation.support.PowerAuthWebArgumentResolver; import io.getlime.security.powerauth.rest.api.spring.filter.PowerAuthRequestFilter; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; @@ -52,6 +53,7 @@ * @author Petr Dvorak, petr@wultra.com */ @Configuration +@Slf4j public class WebMvcConfiguration implements WebMvcConfigurer { @Autowired @@ -140,7 +142,12 @@ public ReloadableResourceBundleMessageSourceWithListing messageSource() { @Bean public Resource languageSettingSource() throws MalformedURLException { - return resourceLoader.getResource(configuration.getResourcesLocation() + "lang.json"); + Resource resource = resourceLoader.getResource(configuration.getResourcesLocation() + "lang.json"); + if (!resource.exists()) { + logger.info("The lang.json file was not found in {}, using default location", configuration.getResourcesLocation()); + resource = resourceLoader.getResource("classpath:/static/resources/lang.json"); + } + return resource; } @Override diff --git a/powerauth-webflow/src/main/java/io/getlime/security/powerauth/app/webflow/configuration/WebSocketConfiguration.java b/powerauth-webflow/src/main/java/io/getlime/security/powerauth/app/webflow/configuration/WebSocketConfiguration.java index a9e5c6ae7..d961d1398 100644 --- a/powerauth-webflow/src/main/java/io/getlime/security/powerauth/app/webflow/configuration/WebSocketConfiguration.java +++ b/powerauth-webflow/src/main/java/io/getlime/security/powerauth/app/webflow/configuration/WebSocketConfiguration.java @@ -20,8 +20,9 @@ import io.getlime.security.powerauth.lib.webflow.authentication.configuration.WebFlowServicesConfiguration; import io.getlime.security.powerauth.lib.webflow.authentication.interceptor.WebSocketHandshakeInterceptor; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Configuration; import org.springframework.messaging.simp.config.MessageBrokerRegistry; -import org.springframework.stereotype.Component; import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; import org.springframework.web.socket.config.annotation.StompEndpointRegistry; import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; @@ -31,8 +32,9 @@ * * @author Roman Strobl, roman.strobl@wultra.com */ -@Component +@Configuration @EnableWebSocketMessageBroker +@ConditionalOnProperty(name = "powerauth.webflow.websockets.enabled", havingValue = "true") public class WebSocketConfiguration implements WebSocketMessageBrokerConfigurer { public static final String MESSAGE_PREFIX = "/topic"; diff --git a/powerauth-webflow/src/main/java/io/getlime/security/powerauth/app/webflow/controller/HomeController.java b/powerauth-webflow/src/main/java/io/getlime/security/powerauth/app/webflow/controller/HomeController.java index 6063148de..c33d8fc7a 100644 --- a/powerauth-webflow/src/main/java/io/getlime/security/powerauth/app/webflow/controller/HomeController.java +++ b/powerauth-webflow/src/main/java/io/getlime/security/powerauth/app/webflow/controller/HomeController.java @@ -210,6 +210,7 @@ public String authenticate(Map model, HttpServletRequest request model.put("icaExtensionIDEdge", webFlowConfig.getIcaExtensionIDEdge()); model.put("icaExtensionIDFirefox", webFlowConfig.getIcaExtensionIDFirefox()); model.put("icaExtensionInstallURLFirefox", webFlowConfig.getIcaExtensionInstallURLFirefox()); + model.put("webSocketSupportEnabled", webFlowConfig.isWebSocketSupportEnabled()); logger.info("The /authenticate request succeeded"); return "index"; } diff --git a/powerauth-webflow/src/main/js/websocket-client.js b/powerauth-webflow/src/main/js/websocket-client.js index 10e89d048..c13443ddb 100644 --- a/powerauth-webflow/src/main/js/websocket-client.js +++ b/powerauth-webflow/src/main/js/websocket-client.js @@ -27,6 +27,9 @@ require('stompjs'); * @param webSocketId Web Socket ID. */ function register(registrations, webSocketId) { + if (!webSocketSupportEnabled) { + return; + } try { var msie = document.documentMode; if (msie && msie < 11) { @@ -60,6 +63,9 @@ function register(registrations, webSocketId) { * @param callback Callback function to call on an event. */ function subscribe(route, callback) { + if (!webSocketSupportEnabled) { + return; + } try { if (client !== undefined) { client.subscribe(route, callback); @@ -75,6 +81,9 @@ function subscribe(route, callback) { * @param route Web Socket route. */ function unsubscribe(route) { + if (!webSocketSupportEnabled) { + return; + } try { if (client !== undefined) { client.unsubscribe(route); @@ -92,6 +101,9 @@ function unsubscribe(route) { * @param message Text of the message as JSON. */ function send(destination, params, message) { + if (!webSocketSupportEnabled) { + return; + } try { if (client !== undefined) { client.send(destination, params, message); @@ -106,6 +118,9 @@ function send(destination, params, message) { * Disconnect the WebSocket. */ function disconnect() { + if (!webSocketSupportEnabled) { + return; + } try { if (client !== undefined) { client.disconnect(); diff --git a/powerauth-webflow/src/main/resources/application.properties b/powerauth-webflow/src/main/resources/application.properties index c20569a7d..13b4f957f 100644 --- a/powerauth-webflow/src/main/resources/application.properties +++ b/powerauth-webflow/src/main/resources/application.properties @@ -52,6 +52,9 @@ powerauth.webflow.offlineMode.available=true # Enable or disable operations support in PowerAuth server powerauth.webflow.pa.operations.enabled=false +# Enable or disable WebSocket support +powerauth.webflow.websockets.enabled=true + # Configuration of Android Security Warning powerauth.webflow.android.showSecurityWarning=true diff --git a/powerauth-webflow/src/main/resources/templates/index.html b/powerauth-webflow/src/main/resources/templates/index.html index 8cc5ceb70..c4415da72 100644 --- a/powerauth-webflow/src/main/resources/templates/index.html +++ b/powerauth-webflow/src/main/resources/templates/index.html @@ -70,6 +70,8 @@ const extensionIDFirefox = [[${icaExtensionIDFirefox}]]; const extensionInstallURLFirefox = [[${icaExtensionInstallURLFirefox}]]; + const webSocketSupportEnabled = [[${webSocketSupportEnabled}]]; + window.onbeforeunload = function() { // Modern browsers do not allow custom messages, so text will be only shown in old browsers. // Note that the onbeforeunload dialog does not appear in case user does no action, see: