From 32062fc79f87eb57d65fc7395f9e6fca6e43f273 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Petr=20Dvo=C5=99=C3=A1k?=
Date: Wed, 17 May 2023 15:34:05 +0200
Subject: [PATCH 001/146] First commit for FIDO2 support
---
.../20230430-add-columns-external-id.xml | 52 +
.../1.5.x/db.changelog-version.xml | 1 +
pom.xml | 1 +
.../client/model/entity/Activation.java | 5 +
.../client/model/enumeration/Protocols.java | 38 +
.../model/request/InitActivationRequest.java | 2 +
powerauth-fido2/pom.xml | 105 +
.../Fido2AuthenticationFailedException.java | 40 +
.../rest/controller/AssertionController.java | 77 +
.../controller/RegistrationController.java | 83 +
.../AssertionChallengeConverter.java | 52 +
.../model/converter/AssertionConverter.java | 59 +
.../RegistrationChallengeConverter.java | 51 +
.../converter/RegistrationConverter.java | 87 +
.../AttestationObjectDeserializer.java | 67 +
.../AttestationStatementDeserializer.java | 65 +
.../AuthenticatorDataDeserializer.java | 145 ++
.../Base64ToByteArrayDeserializer.java | 49 +
.../Base64ToStringDeserializer.java | 54 +
.../CollectedClientDataDeserializer.java | 68 +
.../fido2/rest/model/entity/AaguidList.java | 195 ++
.../rest/model/entity/AssertionChallenge.java | 35 +
.../rest/model/entity/AttestationObject.java | 41 +
.../model/entity/AttestationStatement.java | 31 +
.../model/entity/AttestedCredentialData.java | 33 +
.../AuthenticatorAssertionResponse.java | 48 +
.../AuthenticatorAttestationResponse.java | 41 +
.../rest/model/entity/AuthenticatorData.java | 40 +
.../model/entity/AuthenticatorDetail.java | 52 +
.../model/entity/CollectedClientData.java | 43 +
.../rest/model/entity/EllipticCurvePoint.java | 30 +
.../fido2/rest/model/entity/Flags.java | 38 +
.../rest/model/entity/PublicKeyObject.java | 35 +
.../model/entity/RegistrationChallenge.java | 34 +
.../rest/model/enumeration/CurveType.java | 27 +
.../fido2/rest/model/enumeration/Fmt.java | 45 +
.../model/enumeration/SignatureAlgorithm.java | 27 +
.../request/AssertionChallengeRequest.java | 43 +
.../rest/model/request/AssertionRequest.java | 50 +
.../RegisteredAuthenticatorsRequest.java | 34 +
.../request/RegistrationChallengeRequest.java | 36 +
.../model/request/RegistrationRequest.java | 56 +
.../response/AssertionChallengeResponse.java | 33 +
.../AssertionVerificationResponse.java | 45 +
.../RegisteredAuthenticatorsResponse.java | 35 +
.../RegistrationChallengeResponse.java | 33 +
.../model/response/RegistrationResponse.java | 50 +
.../validator/AssertionRequestValidator.java | 91 +
.../RegistrationRequestValidator.java | 125 ++
.../fido2/service/AssertionService.java | 103 +
.../fido2/service/AuthenticatorProvider.java | 38 +
.../fido2/service/ChallengeProvider.java | 90 +
.../fido2/service/CryptographyService.java | 37 +
.../fido2/service/RegistrationService.java | 109 +
powerauth-java-server/pom.xml | 5 +
.../configuration/OpenApiConfiguration.java | 5 +-
.../PowerAuthAuditConfiguration.java | 2 +-
.../controller/RESTControllerAdvice.java | 18 +
.../model/entity/ActivationRecordEntity.java | 126 +-
.../model/entity/OperationEntity.java | 2 +-
.../repository/ActivationRepository.java | 11 +
.../app/server/service/PowerAuthService.java | 3 +
.../tasks/ActivationServiceBehavior.java | 61 +-
.../tasks/OperationServiceBehavior.java | 10 +-
.../fido2/PowerAuthAuthenticatorProvider.java | 344 +++
.../fido2/PowerAuthChallengeProvider.java | 162 ++
.../fido2/PowerAuthCryptographyService.java | 78 +
.../request/ActivationLayer2Request.java | 30 +-
.../src/main/resources/xsd/PowerAuth-2.0.xsd | 270 ---
.../src/main/resources/xsd/PowerAuth-3.0.xsd | 1910 -----------------
70 files changed, 3657 insertions(+), 2284 deletions(-)
create mode 100644 docs/db/changelog/changesets/powerauth-java-server/1.5.x/20230430-add-columns-external-id.xml
create mode 100644 powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/enumeration/Protocols.java
create mode 100644 powerauth-fido2/pom.xml
create mode 100644 powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/errorhandling/Fido2AuthenticationFailedException.java
create mode 100644 powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/AssertionController.java
create mode 100644 powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/RegistrationController.java
create mode 100644 powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/AssertionChallengeConverter.java
create mode 100644 powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/AssertionConverter.java
create mode 100644 powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/RegistrationChallengeConverter.java
create mode 100644 powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/RegistrationConverter.java
create mode 100644 powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AttestationObjectDeserializer.java
create mode 100644 powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AttestationStatementDeserializer.java
create mode 100644 powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AuthenticatorDataDeserializer.java
create mode 100644 powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/Base64ToByteArrayDeserializer.java
create mode 100644 powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/Base64ToStringDeserializer.java
create mode 100644 powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/CollectedClientDataDeserializer.java
create mode 100644 powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AaguidList.java
create mode 100644 powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AssertionChallenge.java
create mode 100644 powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AttestationObject.java
create mode 100644 powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AttestationStatement.java
create mode 100644 powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AttestedCredentialData.java
create mode 100644 powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AuthenticatorAssertionResponse.java
create mode 100644 powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AuthenticatorAttestationResponse.java
create mode 100644 powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AuthenticatorData.java
create mode 100644 powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AuthenticatorDetail.java
create mode 100644 powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/CollectedClientData.java
create mode 100644 powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/EllipticCurvePoint.java
create mode 100644 powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/Flags.java
create mode 100644 powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/PublicKeyObject.java
create mode 100644 powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/RegistrationChallenge.java
create mode 100644 powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/enumeration/CurveType.java
create mode 100644 powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/enumeration/Fmt.java
create mode 100644 powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/enumeration/SignatureAlgorithm.java
create mode 100644 powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/AssertionChallengeRequest.java
create mode 100644 powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/AssertionRequest.java
create mode 100644 powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/RegisteredAuthenticatorsRequest.java
create mode 100644 powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/RegistrationChallengeRequest.java
create mode 100644 powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/RegistrationRequest.java
create mode 100644 powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/response/AssertionChallengeResponse.java
create mode 100644 powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/response/AssertionVerificationResponse.java
create mode 100644 powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/response/RegisteredAuthenticatorsResponse.java
create mode 100644 powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/response/RegistrationChallengeResponse.java
create mode 100644 powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/response/RegistrationResponse.java
create mode 100644 powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/validator/AssertionRequestValidator.java
create mode 100644 powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/validator/RegistrationRequestValidator.java
create mode 100644 powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/AssertionService.java
create mode 100644 powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/AuthenticatorProvider.java
create mode 100644 powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/ChallengeProvider.java
create mode 100644 powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/CryptographyService.java
create mode 100644 powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/RegistrationService.java
create mode 100644 powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAuthenticatorProvider.java
create mode 100644 powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthChallengeProvider.java
create mode 100644 powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthCryptographyService.java
delete mode 100644 powerauth-java-server/src/main/resources/xsd/PowerAuth-2.0.xsd
delete mode 100644 powerauth-java-server/src/main/resources/xsd/PowerAuth-3.0.xsd
diff --git a/docs/db/changelog/changesets/powerauth-java-server/1.5.x/20230430-add-columns-external-id.xml b/docs/db/changelog/changesets/powerauth-java-server/1.5.x/20230430-add-columns-external-id.xml
new file mode 100644
index 000000000..ba56c0200
--- /dev/null
+++ b/docs/db/changelog/changesets/powerauth-java-server/1.5.x/20230430-add-columns-external-id.xml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+ Add external_id column
+
+
+
+
+
+
+
+
+
+
+
+ Add protocol column
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/db/changelog/changesets/powerauth-java-server/1.5.x/db.changelog-version.xml b/docs/db/changelog/changesets/powerauth-java-server/1.5.x/db.changelog-version.xml
index 447a035c3..637e48247 100644
--- a/docs/db/changelog/changesets/powerauth-java-server/1.5.x/db.changelog-version.xml
+++ b/docs/db/changelog/changesets/powerauth-java-server/1.5.x/db.changelog-version.xml
@@ -4,5 +4,6 @@
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.9.xsd">
+
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 458741fa1..7c5a7a3eb 100644
--- a/pom.xml
+++ b/pom.xml
@@ -75,6 +75,7 @@
powerauth-client-model
powerauth-rest-client-spring
+ powerauth-fido2
powerauth-java-server
powerauth-admin
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/Activation.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/Activation.java
index cfd3f2e21..118ba244f 100644
--- a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/Activation.java
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/Activation.java
@@ -36,7 +36,9 @@ public class Activation {
private ActivationStatus activationStatus;
private String blockedReason;
private String activationName;
+ private String externalId;
private String extras;
+ private String protocol;
private String platform;
private String deviceInfo;
private List activationFlags = new ArrayList<>();
@@ -46,6 +48,9 @@ public class Activation {
private String userId;
private String applicationId;
private String applicationName;
+ private long failedAttempts;
+ private long maxFailedAttempts;
+ private String devicePublicKeyBase64;
private long version;
}
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/enumeration/Protocols.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/enumeration/Protocols.java
new file mode 100644
index 000000000..4a1f73612
--- /dev/null
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/enumeration/Protocols.java
@@ -0,0 +1,38 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.security.powerauth.client.model.enumeration;
+
+/**
+ * @author Petr Dvorak, petr@wultra.com
+ */
+public enum Protocols {
+ POWERAUTH("powerauth"),
+ FIDO2("fido2");
+
+ private final String protocol;
+
+ Protocols(String protocol) {
+ this.protocol = protocol;
+ }
+
+ @Override
+ public String toString() {
+ return protocol;
+ }
+}
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/InitActivationRequest.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/InitActivationRequest.java
index b3448a35b..d2859c19e 100644
--- a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/InitActivationRequest.java
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/InitActivationRequest.java
@@ -19,6 +19,7 @@
package com.wultra.security.powerauth.client.model.request;
import com.wultra.security.powerauth.client.model.enumeration.ActivationOtpValidation;
+import com.wultra.security.powerauth.client.model.enumeration.Protocols;
import lombok.Data;
import lombok.ToString;
@@ -32,6 +33,7 @@
@Data
public class InitActivationRequest {
+ private Protocols protocol = Protocols.POWERAUTH;
private String userId;
private String applicationId;
private Date timestampActivationExpire;
diff --git a/powerauth-fido2/pom.xml b/powerauth-fido2/pom.xml
new file mode 100644
index 000000000..793a80870
--- /dev/null
+++ b/powerauth-fido2/pom.xml
@@ -0,0 +1,105 @@
+
+
+
+ 4.0.0
+
+ powerauth-fido2
+ powerauth-fido2
+ powerauth-fido2
+
+
+ io.getlime.security
+ powerauth-server-parent
+ 1.5.0-SNAPSHOT
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
+
+ org.apache.tomcat.embed
+ tomcat-embed-el
+
+
+
+
+ org.springdoc
+ springdoc-openapi-starter-webmvc-ui
+ 2.0.4
+
+
+ org.springframework.boot
+ spring-boot-starter-tomcat
+ provided
+
+
+
+ com.fasterxml.jackson.dataformat
+ jackson-dataformat-cbor
+ 2.13.4
+
+
+ org.postgresql
+ postgresql
+ runtime
+
+
+ org.projectlombok
+ lombok
+ true
+
+
+
+ io.getlime.security
+ powerauth-java-crypto
+ 1.5.0-SNAPSHOT
+
+
+ io.getlime.core
+ rest-client-base
+ 1.7.0-SNAPSHOT
+
+
+ io.getlime.security
+ powerauth-client-model
+ 1.5.0-SNAPSHOT
+
+
+
+ org.bouncycastle
+ bcprov-jdk18on
+ ${bcprov-jdk18on.version}
+ provided
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/errorhandling/Fido2AuthenticationFailedException.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/errorhandling/Fido2AuthenticationFailedException.java
new file mode 100644
index 000000000..d3a59a633
--- /dev/null
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/errorhandling/Fido2AuthenticationFailedException.java
@@ -0,0 +1,40 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.powerauth.fido2.errorhandling;
+
+import java.io.Serial;
+
+/**
+ * Exception related to FIDO2 authentication failure.
+ *
+ * @author Petr Dvorak, petr@wultra.com
+ */
+public class Fido2AuthenticationFailedException extends Exception {
+
+ @Serial
+ private static final long serialVersionUID = -3214199555928548491L;
+
+ public Fido2AuthenticationFailedException(String message) {
+ super(message);
+ }
+
+ public Fido2AuthenticationFailedException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/AssertionController.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/AssertionController.java
new file mode 100644
index 000000000..510fc4612
--- /dev/null
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/AssertionController.java
@@ -0,0 +1,77 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.powerauth.fido2.rest.controller;
+
+import com.wultra.powerauth.fido2.errorhandling.Fido2AuthenticationFailedException;
+import com.wultra.powerauth.fido2.rest.model.request.AssertionChallengeRequest;
+import com.wultra.powerauth.fido2.rest.model.request.AssertionRequest;
+import com.wultra.powerauth.fido2.rest.model.response.AssertionChallengeResponse;
+import com.wultra.powerauth.fido2.rest.model.response.AssertionVerificationResponse;
+import com.wultra.powerauth.fido2.rest.model.validator.AssertionRequestValidator;
+import com.wultra.powerauth.fido2.service.AssertionService;
+import io.getlime.core.rest.model.base.request.ObjectRequest;
+import io.getlime.core.rest.model.base.response.ObjectResponse;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.validation.Valid;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * Controller responsible for FIDO2 assertion handling.
+ *
+ * @author Petr Dvorak, petr@wultra.com
+ */
+@Validated
+@RestController
+@RequestMapping("assertions")
+@Slf4j
+@Tag(name = "FIDO2 Assertions Controller")
+public class AssertionController {
+
+ private final AssertionRequestValidator assertionRequestValidator;
+ private final AssertionService assertionService;
+
+ @Autowired
+ public AssertionController(AssertionRequestValidator assertionRequestValidator, AssertionService assertionService) {
+ this.assertionRequestValidator = assertionRequestValidator;
+ this.assertionService = assertionService;
+ }
+
+ @PostMapping("challenge")
+ @CrossOrigin(origins = "http://localhost:8081")
+ public ObjectResponse requestAssertionChallenge(@Valid @RequestBody ObjectRequest request) throws Exception {
+ final AssertionChallengeRequest requestObject = request.getRequestObject();
+ final AssertionChallengeResponse assertionChallengeResponse = assertionService.requestAssertionChallenge(requestObject);
+ return new ObjectResponse<>(assertionChallengeResponse);
+ }
+
+ @PostMapping
+ @CrossOrigin(origins = "http://localhost:8081")
+ public ObjectResponse authenticate(@Valid @RequestBody ObjectRequest request) throws Fido2AuthenticationFailedException {
+ final AssertionRequest requestObject = request.getRequestObject();
+ final String error = assertionRequestValidator.validate(requestObject);
+ if (error != null) {
+ throw new Fido2AuthenticationFailedException(error);
+ }
+ final AssertionVerificationResponse signatureResponse = assertionService.authenticate(requestObject);
+ return new ObjectResponse<>(signatureResponse);
+ }
+
+}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/RegistrationController.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/RegistrationController.java
new file mode 100644
index 000000000..9d0cf6041
--- /dev/null
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/RegistrationController.java
@@ -0,0 +1,83 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.powerauth.fido2.rest.controller;
+
+import com.wultra.powerauth.fido2.rest.model.entity.AaguidList;
+import com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorDetail;
+import com.wultra.powerauth.fido2.rest.model.request.RegisteredAuthenticatorsRequest;
+import com.wultra.powerauth.fido2.rest.model.request.RegistrationChallengeRequest;
+import com.wultra.powerauth.fido2.rest.model.request.RegistrationRequest;
+import com.wultra.powerauth.fido2.rest.model.response.RegisteredAuthenticatorsResponse;
+import com.wultra.powerauth.fido2.rest.model.response.RegistrationChallengeResponse;
+import com.wultra.powerauth.fido2.rest.model.response.RegistrationResponse;
+import com.wultra.powerauth.fido2.service.AuthenticatorProvider;
+import com.wultra.powerauth.fido2.service.RegistrationService;
+import io.getlime.core.rest.model.base.request.ObjectRequest;
+import io.getlime.core.rest.model.base.response.ObjectResponse;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.validation.Valid;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * Controller responsible for FIDO2 authenticator registration handling.
+ *
+ * @author Petr Dvorak, petr@wultra.com
+ */
+@Validated
+@RestController
+@RequestMapping("registrations")
+@Slf4j
+@Tag(name = "FIDO2 Registration Controller")
+public class RegistrationController {
+
+ private final RegistrationService registrationService;
+
+ @Autowired
+ public RegistrationController(RegistrationService registrationService) {
+ this.registrationService = registrationService;
+ }
+
+ @PostMapping("list")
+ @CrossOrigin(origins = "http://localhost:8081")
+ public ObjectResponse registeredAuthenticators(@Valid @RequestBody ObjectRequest request) throws Exception {
+ final RegisteredAuthenticatorsRequest requestObject = request.getRequestObject();
+ final RegisteredAuthenticatorsResponse responseObject = registrationService.registrationsForUser(requestObject.getUserId(), requestObject.getApplicationId());
+ return new ObjectResponse<>(responseObject);
+ }
+
+ @PostMapping("challenge")
+ @CrossOrigin(origins = "http://localhost:8081")
+ public ObjectResponse requestRegistrationChallenge(@Valid @RequestBody ObjectRequest request) throws Exception {
+ final RegistrationChallengeRequest requestObject = request.getRequestObject();
+ final RegistrationChallengeResponse responseObject = registrationService.requestRegistrationChallenge(requestObject.getUserId(), requestObject.getApplicationId());
+ return new ObjectResponse<>(responseObject);
+ }
+
+ @PostMapping
+ @CrossOrigin(origins = "http://localhost:8081")
+ public ObjectResponse register(@Valid @RequestBody ObjectRequest request) throws Exception {
+ final RegistrationRequest requestObject = request.getRequestObject();
+ final RegistrationResponse responseObject = registrationService.register(requestObject);
+ return new ObjectResponse<>(responseObject);
+ }
+
+}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/AssertionChallengeConverter.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/AssertionChallengeConverter.java
new file mode 100644
index 000000000..5c1451483
--- /dev/null
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/AssertionChallengeConverter.java
@@ -0,0 +1,52 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.powerauth.fido2.rest.model.converter;
+
+import com.wultra.powerauth.fido2.rest.model.entity.AssertionChallenge;
+import com.wultra.powerauth.fido2.rest.model.response.AssertionChallengeResponse;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+/**
+ * Converter for assertion challenge values.
+ *
+ * @author Petr Dvorak, petr@wultra.com
+ */
+@Component
+@Slf4j
+public class AssertionChallengeConverter {
+
+ /**
+ * Convert a new assertion challenge response from a provided challenge.
+ *
+ * @param source Challenge.
+ * @return Assertion challenge response.
+ */
+ public AssertionChallengeResponse fromChallenge(AssertionChallenge source) {
+ if (source == null) {
+ return null;
+ }
+ final AssertionChallengeResponse destination = new AssertionChallengeResponse();
+ destination.setUserId(source.getUserId());
+ destination.setApplicationIds(source.getApplicationIds());
+ destination.setChallenge(source.getChallenge());
+ return destination;
+ }
+
+}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/AssertionConverter.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/AssertionConverter.java
new file mode 100644
index 000000000..27d54689c
--- /dev/null
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/AssertionConverter.java
@@ -0,0 +1,59 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.powerauth.fido2.rest.model.converter;
+
+import com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorDetail;
+import com.wultra.powerauth.fido2.rest.model.response.AssertionVerificationResponse;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+/**
+ * Converter between assertion verification result to signature verification.
+ *
+ * @author Petr Dvorak, petr@wultra.com
+ */
+@Component
+@Slf4j
+public class AssertionConverter {
+
+ public AssertionVerificationResponse fromAuthenticatorDetail(AuthenticatorDetail source, boolean assertionValid) {
+ if (source == null) {
+ return null;
+ }
+
+ if (!assertionValid) { // return empty object for invalid assertions
+ final AssertionVerificationResponse destination = new AssertionVerificationResponse();
+ destination.setAssertionValid(false);
+ return destination;
+ } else {
+ final AssertionVerificationResponse destination = new AssertionVerificationResponse();
+ destination.setAssertionValid(assertionValid);
+ destination.setUserId(source.getUserId());
+ destination.setActivationId(source.getActivationId());
+ destination.setApplicationId(source.getApplicationId());
+ destination.setActivationStatus(source.getActivationStatus());
+ destination.setBlockedReason(source.getBlockedReason());
+ destination.setRemainingAttempts(source.getMaxFailedAttempts() - source.getFailedAttempts());
+ destination.setApplicationRoles(source.getApplicationRoles());
+ destination.setActivationFlags(source.getActivationFlags());
+ return destination;
+ }
+ }
+
+}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/RegistrationChallengeConverter.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/RegistrationChallengeConverter.java
new file mode 100644
index 000000000..c15314c3d
--- /dev/null
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/RegistrationChallengeConverter.java
@@ -0,0 +1,51 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.powerauth.fido2.rest.model.converter;
+
+import com.wultra.powerauth.fido2.rest.model.entity.RegistrationChallenge;
+import com.wultra.powerauth.fido2.rest.model.response.RegistrationChallengeResponse;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author Petr Dvorak, petr@wultra.com
+ */
+@Component
+@Slf4j
+public class RegistrationChallengeConverter {
+
+ /**
+ * Convert a new assertion challenge response from a provided challenge.
+ *
+ * @param source Challenge.
+ * @return Assertion challenge response.
+ */
+ public RegistrationChallengeResponse fromChallenge(RegistrationChallenge source) {
+ if (source == null) {
+ return null;
+ }
+ final RegistrationChallengeResponse destination = new RegistrationChallengeResponse();
+ destination.setUserId(source.getUserId());
+ destination.setActivationId(source.getActivationId());
+ destination.setApplicationId(source.getApplicationId());
+ destination.setChallenge(source.getChallenge());
+ return destination;
+ }
+
+}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/RegistrationConverter.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/RegistrationConverter.java
new file mode 100644
index 000000000..002078848
--- /dev/null
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/RegistrationConverter.java
@@ -0,0 +1,87 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.powerauth.fido2.rest.model.converter;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.wultra.powerauth.fido2.rest.model.entity.AaguidList;
+import com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorDetail;
+import com.wultra.powerauth.fido2.rest.model.entity.RegistrationChallenge;
+import com.wultra.powerauth.fido2.rest.model.request.RegistrationRequest;
+import com.wultra.powerauth.fido2.rest.model.response.RegistrationResponse;
+import com.wultra.security.powerauth.client.model.enumeration.ActivationStatus;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+
+/**
+ * Converter class for registration related objects.
+ *
+ * @author Petr Dvorak, petr@wultra.com
+ */
+@Component
+@Slf4j
+public class RegistrationConverter {
+
+ private final AaguidList aaguidRegistry = new AaguidList();
+
+ public AuthenticatorDetail convert(RegistrationChallenge challenge, RegistrationRequest requestObject, byte[] aaguid, byte[] publicKey) {
+ try {
+ final AuthenticatorDetail authenticatorDetail = new AuthenticatorDetail();
+ authenticatorDetail.setUserId(challenge.getUserId());
+ authenticatorDetail.setActivationId(challenge.getActivationId());
+ authenticatorDetail.setApplicationId(challenge.getApplicationId());
+
+ authenticatorDetail.setExternalId(requestObject.getId());
+ authenticatorDetail.setExtras(new ObjectMapper().writeValueAsString(requestObject.getResponse().getTransports()));
+ authenticatorDetail.setActivationName(requestObject.getActivationName());
+ authenticatorDetail.setPlatform(requestObject.getAuthenticatorAttachment());
+ authenticatorDetail.setDeviceInfo(aaguidRegistry.vendorName(aaguid));
+ authenticatorDetail.setActivationStatus(ActivationStatus.ACTIVE);
+ authenticatorDetail.setActivationFlags(new ArrayList<>());
+ authenticatorDetail.setApplicationRoles(new ArrayList<>());
+ authenticatorDetail.setPublicKeyBytes(publicKey);
+ authenticatorDetail.setFailedAttempts(0L);
+ authenticatorDetail.setMaxFailedAttempts(5L);
+ return authenticatorDetail;
+ } catch (JsonProcessingException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public RegistrationResponse convertRegistrationResponse(AuthenticatorDetail source) {
+ final RegistrationResponse result = new RegistrationResponse();
+ result.setUserId(source.getUserId());
+ result.setActivationId(source.getActivationId());
+ result.setApplicationId(source.getApplicationId());
+ result.setExternalId(source.getExternalId());
+ result.setExtras(source.getExtras());
+ result.setActivationName(source.getActivationName());
+ result.setPlatform(source.getPlatform());
+ result.setDeviceInfo(source.getDeviceInfo());
+ result.setActivationStatus(source.getActivationStatus());
+ result.setActivationFlags(source.getActivationFlags());
+ result.setApplicationRoles(source.getApplicationRoles());
+ result.setPublicKeyBytes(source.getPublicKeyBytes());
+ result.setFailedAttempts(source.getFailedAttempts());
+ result.setMaxFailedAttempts(source.getMaxFailedAttempts());
+ return result;
+ }
+}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AttestationObjectDeserializer.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AttestationObjectDeserializer.java
new file mode 100644
index 000000000..0ce540c74
--- /dev/null
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AttestationObjectDeserializer.java
@@ -0,0 +1,67 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.powerauth.fido2.rest.model.converter.serialization;
+
+import com.fasterxml.jackson.core.JacksonException;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+import com.fasterxml.jackson.dataformat.cbor.databind.CBORMapper;
+import com.wultra.powerauth.fido2.rest.model.entity.AttestationObject;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+import java.io.Serial;
+import java.util.Base64;
+
+/**
+ * @author Petr Dvorak, petr@wultra.com
+ */
+@Component
+@Slf4j
+public class AttestationObjectDeserializer extends StdDeserializer {
+
+ @Serial
+ private static final long serialVersionUID = -5549850902593127253L;
+
+ private final CBORMapper cborMapper = new CBORMapper();
+
+ public AttestationObjectDeserializer() {
+ this(null);
+ }
+
+ public AttestationObjectDeserializer(Class> vc) {
+ super(vc);
+ }
+
+ @Override
+ public AttestationObject deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException {
+ try {
+ final String originalTextValue = jsonParser.getText();
+ final byte[] decodedAttestationObject = Base64.getDecoder().decode(originalTextValue);
+ final AttestationObject attestationObject = cborMapper.readValue(decodedAttestationObject, AttestationObject.class);
+ attestationObject.setEncoded(originalTextValue);
+ return attestationObject;
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AttestationStatementDeserializer.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AttestationStatementDeserializer.java
new file mode 100644
index 000000000..2308f49f7
--- /dev/null
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AttestationStatementDeserializer.java
@@ -0,0 +1,65 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.powerauth.fido2.rest.model.converter.serialization;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+import com.wultra.powerauth.fido2.rest.model.entity.AttestationStatement;
+import com.wultra.powerauth.fido2.rest.model.enumeration.SignatureAlgorithm;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+import java.io.Serial;
+import java.util.Map;
+
+/**
+ * @author Petr Dvorak, petr@wultra.com
+ */
+@Component
+@Slf4j
+public class AttestationStatementDeserializer extends StdDeserializer {
+
+ @Serial
+ private static final long serialVersionUID = -3598363993363470844L;
+
+ public AttestationStatementDeserializer() {
+ this(null);
+ }
+
+ public AttestationStatementDeserializer(Class vc) {
+ super(vc);
+ }
+
+ @Override
+ public AttestationStatement deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
+ final Map map = jsonParser.readValueAs(new TypeReference<>() {});
+ final AttestationStatement result = new AttestationStatement();
+ final Integer alg = (Integer) map.get("alg");
+ if (alg != null && -7 == alg) {
+ result.setAlgorithm(SignatureAlgorithm.ES256);
+ } else {
+ result.setAlgorithm(SignatureAlgorithm.UNKNOWN);
+ }
+ result.setSignature((byte[]) map.get("sig"));
+ return result;
+ }
+}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AuthenticatorDataDeserializer.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AuthenticatorDataDeserializer.java
new file mode 100644
index 000000000..5a8ecb00c
--- /dev/null
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AuthenticatorDataDeserializer.java
@@ -0,0 +1,145 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.powerauth.fido2.rest.model.converter.serialization;
+
+import com.fasterxml.jackson.core.JacksonException;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+import com.fasterxml.jackson.dataformat.cbor.databind.CBORMapper;
+import com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorData;
+import com.wultra.powerauth.fido2.rest.model.entity.Flags;
+import com.wultra.powerauth.fido2.rest.model.entity.PublicKeyObject;
+import com.wultra.powerauth.fido2.rest.model.enumeration.CurveType;
+import com.wultra.powerauth.fido2.rest.model.enumeration.SignatureAlgorithm;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+import java.io.Serial;
+import java.nio.ByteBuffer;
+import java.util.Map;
+
+/**
+ * @author Petr Dvorak, petr@wultra.com
+ */
+@Component
+@Slf4j
+public class AuthenticatorDataDeserializer extends StdDeserializer {
+
+ @Serial
+ private static final long serialVersionUID = -7644582864083436208L;
+
+ private final CBORMapper cborMapper = new CBORMapper();
+
+ public AuthenticatorDataDeserializer() {
+ this(null);
+ }
+ private AuthenticatorDataDeserializer(Class> vc) {
+ super(vc);
+ }
+
+ @Override
+ public AuthenticatorData deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException {
+ final AuthenticatorData result = new AuthenticatorData();
+
+ // Serialize Auth Data
+ final byte[] authData = jsonParser.getBinaryValue();
+ result.setEncoded(authData);
+
+ // Get RP ID Hash
+ final byte[] rpIdHash = new byte[32];
+ System.arraycopy(authData, 0, rpIdHash,0, 32);
+ result.setRpIdHash(rpIdHash);
+
+ // Get Flags
+ final byte flagByte = authData[32];
+ final Flags flags = result.getFlags();
+
+ flags.setUserPresent(isFlagOn(flagByte, 0));
+ flags.setReservedBit2(isFlagOn(flagByte, 1));
+ flags.setUserVerified(isFlagOn(flagByte, 2));
+ flags.setBackupEligible(isFlagOn(flagByte, 3));
+ flags.setBackupState(isFlagOn(flagByte, 4));
+ flags.setReservedBit6(isFlagOn(flagByte, 5));
+ flags.setAttestedCredentialsIncluded(isFlagOn(flagByte,6));
+ flags.setExtensionDataInlcuded(isFlagOn(flagByte,7));
+
+ // Get Signature Counter
+ final byte[] signCountBytes = new byte[4];
+ System.arraycopy(authData, 33, signCountBytes, 0, 4);
+ final int signCount = ByteBuffer.wrap(signCountBytes).getInt(); // big-endian by default
+ result.setSignCount(signCount);
+
+ if (authData.length > 37) { // get info about the credentials
+
+ // Get AAGUID
+ final byte[] aaguid = new byte[16];
+ System.arraycopy(authData, 37, aaguid, 0, 16);
+ result.getAttestedCredentialData().setAaguid(aaguid);
+
+ // Get credential ID length
+ final byte[] credentialIdLength = new byte[2];
+ System.arraycopy(authData, 53, credentialIdLength, 0, 2);
+ final ByteBuffer wrapped = ByteBuffer.wrap(credentialIdLength); // big-endian by default
+ short credentialIdLengthValue = wrapped.getShort();
+
+ // Get credentialId
+ final byte[] credentialId = new byte[credentialIdLengthValue];
+ System.arraycopy(authData, 55, credentialId, 0, credentialIdLengthValue);
+ result.getAttestedCredentialData().setCredentialId(credentialId);
+
+ // Get credentialPublicKey
+ final int remainingLength = authData.length - (55 + credentialIdLengthValue);
+ final byte[] credentialPublicKey = new byte[remainingLength];
+ System.arraycopy(authData, 55 + credentialIdLengthValue, credentialPublicKey, 0, remainingLength);
+ final Map credentialPublicKeyMap = cborMapper.readValue(credentialPublicKey, new TypeReference<>() {
+ });
+
+ final PublicKeyObject publicKeyObject = new PublicKeyObject();
+ final Integer algorithm = (Integer) credentialPublicKeyMap.get("3");
+ if (algorithm != null && -7 == algorithm) {
+ publicKeyObject.setAlgorithm(SignatureAlgorithm.ES256);
+ } else {
+ throw new RuntimeException("Unsupported algorithm: " + algorithm);
+ }
+ final Integer curveType = (Integer) credentialPublicKeyMap.get("-1");
+ if (curveType != null && 1 == curveType) {
+ publicKeyObject.setCurveType(CurveType.P256);
+ } else {
+ throw new RuntimeException("Unsupported curve type: " + curveType);
+ }
+
+ final byte[] xBytes = (byte[]) credentialPublicKeyMap.get("-2");
+ final byte[] yBytes = (byte[]) credentialPublicKeyMap.get("-3");
+ publicKeyObject.getPoint().setX(xBytes);
+ publicKeyObject.getPoint().setY(yBytes);
+
+ result.getAttestedCredentialData().setPublicKeyObject(publicKeyObject);
+ }
+
+ return result;
+ }
+
+ private boolean isFlagOn(byte flags, int position) {
+ return ((flags >> position) & 1) == 1;
+ }
+
+}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/Base64ToByteArrayDeserializer.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/Base64ToByteArrayDeserializer.java
new file mode 100644
index 000000000..fe0801665
--- /dev/null
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/Base64ToByteArrayDeserializer.java
@@ -0,0 +1,49 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.powerauth.fido2.rest.model.converter.serialization;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+
+import java.io.IOException;
+import java.io.Serial;
+import java.util.Base64;
+
+/**
+ * @author Petr Dvorak, petr@wultra.com
+ */
+public class Base64ToByteArrayDeserializer extends StdDeserializer {
+
+ @Serial
+ private static final long serialVersionUID = 4519714786533202920L;
+
+ public Base64ToByteArrayDeserializer() {
+ this(null);
+ }
+
+ public Base64ToByteArrayDeserializer(Class> vc) {
+ super(vc);
+ }
+
+ @Override
+ public byte[] deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
+ return Base64.getDecoder().decode(jsonParser.getText());
+ }
+}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/Base64ToStringDeserializer.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/Base64ToStringDeserializer.java
new file mode 100644
index 000000000..31d61449f
--- /dev/null
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/Base64ToStringDeserializer.java
@@ -0,0 +1,54 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.powerauth.fido2.rest.model.converter.serialization;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+import java.io.Serial;
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+
+/**
+ * @author Petr Dvorak, petr@wultra.com
+ */
+@Component
+@Slf4j
+public class Base64ToStringDeserializer extends StdDeserializer {
+
+ @Serial
+ private static final long serialVersionUID = 2540966716709142276L;
+
+ public Base64ToStringDeserializer() {
+ this(null);
+ }
+
+ public Base64ToStringDeserializer(Class> vc) {
+ super(vc);
+ }
+
+ @Override
+ public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
+ return new String(Base64.getDecoder().decode(jsonParser.getText()), StandardCharsets.UTF_8);
+ }
+}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/CollectedClientDataDeserializer.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/CollectedClientDataDeserializer.java
new file mode 100644
index 000000000..5b53b39c1
--- /dev/null
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/CollectedClientDataDeserializer.java
@@ -0,0 +1,68 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.powerauth.fido2.rest.model.converter.serialization;
+
+import com.fasterxml.jackson.core.JacksonException;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+import com.wultra.powerauth.fido2.rest.model.entity.CollectedClientData;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+import java.io.Serial;
+import java.util.Base64;
+
+/**
+ * @author Petr Dvorak, petr@wultra.com
+ */
+@Component
+@Slf4j
+public class CollectedClientDataDeserializer extends StdDeserializer {
+
+ @Serial
+ private static final long serialVersionUID = 8991171442005200006L;
+ private final ObjectMapper objectMapper = new ObjectMapper()
+ .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+
+ public CollectedClientDataDeserializer() {
+ this(null);
+ }
+
+ public CollectedClientDataDeserializer(Class> vc) {
+ super(vc);
+ }
+
+ @Override
+ public CollectedClientData deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) {
+ try {
+ final String originalTextValue = jsonParser.getText();
+ final byte[] decodedClientDataJSON = Base64.getDecoder().decode(originalTextValue);
+ final CollectedClientData collectedClientData = objectMapper.readValue(decodedClientDataJSON, CollectedClientData.class);
+ collectedClientData.setEncoded(new String(decodedClientDataJSON));
+ return collectedClientData;
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AaguidList.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AaguidList.java
new file mode 100644
index 000000000..ed4af7695
--- /dev/null
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AaguidList.java
@@ -0,0 +1,195 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.powerauth.fido2.rest.model.entity;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.UUID;
+
+/**
+ * Class containing map of all known FIDO2 AAGUID authenticator identifiers.
+ *
+ * @author Petr Dvorak, petr@wultra.com
+ */
+public class AaguidList {
+
+ private static final Map vendors = new HashMap<>();
+
+ public AaguidList() {
+
+ // Android
+ vendors.put(UUID.fromString("b93fd961-f2e6-462f-b122-82002247de78"), "Android Authenticator with SafetyNet Attestation");
+
+ // ATKey
+ vendors.put(UUID.fromString("ba76a271-6eb6-4171-874d-b6428dbe3437"), "ATKey.ProS");
+ vendors.put(UUID.fromString("d41f5a69-b817-4144-a13c-9ebd6d9254d6"), "ATKey.Card CTAP2.0");
+ vendors.put(UUID.fromString("e1a96183-5016-4f24-b55b-e3ae23614cc6"), "ATKey.Pro CTAP2.0");
+ vendors.put(UUID.fromString("e416201b-afeb-41ca-a03d-2281c28322aa"), "ATKey.Pro CTAP2.1");
+
+ // Atos
+ vendors.put(UUID.fromString("1c086528-58d5-f211-823c-356786e36140"), "Atos CardOS FIDO2");
+
+ // Crayonic
+ vendors.put(UUID.fromString("be727034-574a-f799-5c76-0929e0430973"), "Crayonic KeyVault K1 (USB-NFC-BLE FIDO2 Authenticator)");
+
+ // Cryptnox
+ vendors.put(UUID.fromString("9c835346-796b-4c27-8898-d6032f515cc5"), "Cryptnox FIDO2");
+
+ // Ensurity
+ vendors.put(UUID.fromString("454e5346-4944-4ffd-6c93-8e9267193e9a"), "Ensurity ThinC");
+
+ // ESS
+ vendors.put(UUID.fromString("5343502d-5343-5343-6172-644649444f32"), "ESS Smart Card Inc. Authenticator");
+
+ // eWBM
+ vendors.put(UUID.fromString("61250591-b2bc-4456-b719-0b17be90bb30"), "eWBM eFPA FIDO2 Authenticator");
+ vendors.put(UUID.fromString("87dbc5a1-4c94-4dc8-8a47-97d800fd1f3c"), "eWBM eFA320 FIDO2 Authenticator");
+ vendors.put(UUID.fromString("95442b2e-f15e-4def-b270-efb106facb4e"), "eWBM eFA310 FIDO2 Authenticator");
+
+ // FEITIAN
+ vendors.put(UUID.fromString("12ded745-4bed-47d4-abaa-e713f51d6393"), "AllinPass FIDO");
+ vendors.put(UUID.fromString("2c0df832-92de-4be1-8412-88a8f074df4a"), "FIDO Java Card");
+ vendors.put(UUID.fromString("310b2830-bd4a-4da5-832e-9a0dfc90abf2"), "MultiPass FIDO");
+ vendors.put(UUID.fromString("3e22415d-7fdf-4ea4-8a0c-dd60c4249b9d"), "Feitian iePass FIDO Authenticator");
+ vendors.put(UUID.fromString("6e22415d-7fdf-4ea4-8a0c-dd60c4249b9d"), "iePass FIDO");
+ vendors.put(UUID.fromString("77010bd7-212a-4fc9-b236-d2ca5e9d4084"), "BioPass FIDO");
+ vendors.put(UUID.fromString("833b721a-ff5f-4d00-bb2e-bdda3ec01e29"), "ePassFIDO K10, A4B, K28");
+ vendors.put(UUID.fromString("8c97a730-3f7b-41a6-87d6-1e9b62bda6f0"), "FIDO Fingerprint Card");
+ vendors.put(UUID.fromString("b6ede29c-3772-412c-8a78-539c1f4c62d2"), "BioPass FIDO Plus");
+ vendors.put(UUID.fromString("ee041bce-25e5-4cdb-8f86-897fd6418464"), "ePassFIDO K39, NFC, NFC Plus");
+
+ // GoTrust
+ vendors.put(UUID.fromString("3b1adb99-0dfe-46fd-90b8-7f7614a4de2a"), "GoTrust Idem Key FIDO2 Authenticator");
+ vendors.put(UUID.fromString("9f0d8150-baa5-4c00-9299-ad62c8bb4e87"), "GoTrust Idem Card FIDO2 Authenticator");
+
+ // HID Global
+ vendors.put(UUID.fromString("54d9fee8-e621-4291-8b18-7157b99c5bec"), "HID Crescendo Enabled");
+ vendors.put(UUID.fromString("692db549-7ae5-44d5-a1e5-dd20a493b723"), "HID Crescendo Key");
+ vendors.put(UUID.fromString("aeb6569c-f8fb-4950-ac60-24ca2bbe2e52"), "HID Crescendo C2300");
+
+ // Hideez
+ vendors.put(UUID.fromString("3e078ffd-4c54-4586-8baa-a77da113aec5"), "Hideez Key 3 FIDO2");
+ vendors.put(UUID.fromString("4e768f2c-5fab-48b3-b300-220eb487752b"), "Hideez Key 4 FIDO2 SDK");
+
+ // Hyper
+ vendors.put(UUID.fromString("9f77e279-a6e2-4d58-b700-31e5943c6a98"), "Hyper FIDO Pro");
+ vendors.put(UUID.fromString("d821a7d4-e97c-4cb6-bd82-4237731fd4be"), "Hyper FIDO Bio Security Key");
+
+ // MKGroup
+ vendors.put(UUID.fromString("f4c63eff-d26c-4248-801c-3736c7eaa93a"), "FIDO KeyPass S3");
+
+ // KEY-ID
+ vendors.put(UUID.fromString("d91c5288-0ef0-49b7-b8ae-21ca0aa6b3f3"), "KEY-ID FIDO2 Authenticator");
+
+ // NEOWAVE
+ vendors.put(UUID.fromString("3789da91-f943-46bc-95c3-50ea2012f03a"), "NEOWAVE Winkeo FIDO2");
+ vendors.put(UUID.fromString("c5703116-972b-4851-a3e7-ae1259843399"), "NEOWAVE Badgeo FIDO2");
+
+ // NXP Semiconductors
+ vendors.put(UUID.fromString("07a9f89c-6407-4594-9d56-621d5f1e358b"), "NXP Semiconductors FIDO2 Conformance Testing CTAP2 Authenticator");
+
+ // OCTATCO
+ vendors.put(UUID.fromString("a1f52be5-dfab-4364-b51c-2bd496b14a56"), "OCTATCO EzFinger2 FIDO2 AUTHENTICATOR");
+ vendors.put(UUID.fromString("bc2fe499-0d8e-4ffe-96f3-94a82840cf8c"), "OCTATCO EzQuant FIDO2 AUTHENTICATOR");
+
+ // OneSpan
+ vendors.put(UUID.fromString("30b5035e-d297-4fc1-b00b-addc96ba6a97"), "OneSpan FIDO Touch");
+
+ // Precision InnaIT
+ vendors.put(UUID.fromString("88bbd2f0-342a-42e7-9729-dd158be5407a"), "Precision InnaIT Key FIDO 2 Level 2 certified");
+
+ // SmartDisplayer
+ vendors.put(UUID.fromString("516d3969-5a57-5651-5958-4e7a49434167"), "SmartDisplayer BobeePass (NFC-BLE FIDO2 Authenticator)");
+
+ // Solo
+ vendors.put(UUID.fromString("8876631b-d4a0-427f-5773-0ec71c9e0279"), "Solo Secp256R1 FIDO2 CTAP2 Authenticator");
+ vendors.put(UUID.fromString("8976631b-d4a0-427f-5773-0ec71c9e0279"), "Solo Tap Secp256R1 FIDO2 CTAP2 Authenticator");
+
+ // Somu
+ vendors.put(UUID.fromString("9876631b-d4a0-427f-5773-0ec71c9e0279"), "Somu Secp256R1 FIDO2 CTAP2 Authenticator");
+
+ // Swissbit
+ vendors.put(UUID.fromString("931327dd-c89b-406c-a81e-ed7058ef36c6"), "Swissbit iShield FIDO2");
+
+ // Thales
+ vendors.put(UUID.fromString("b50d5e0a-7f81-4959-9b12-f45407407503"), "Thales IDPrime MD 3940 FIDO");
+ vendors.put(UUID.fromString("efb96b10-a9ee-4b6c-a4a9-d32125ccd4a4"), "Thales eToken FIDO");
+
+ // TrustKey
+ vendors.put(UUID.fromString("95442b2e-f15e-4def-b270-efb106facb4e"), "TrustKey G310(H)");
+ vendors.put(UUID.fromString("87dbc5a1-4c94-4dc8-8a47-97d800fd1f3c"), "TrustKey G320(H)");
+ vendors.put(UUID.fromString("da776f39-f6c8-4a89-b252-1d86137a46ba"), "TrustKey T110");
+ vendors.put(UUID.fromString("e3512a8a-62ae-11ea-bc55-0242ac130003"), "TrustKey T120");
+
+ // TOKEN2
+ vendors.put(UUID.fromString("ab32f0c6-2239-afbb-c470-d2ef4e254db7"), "TOKEN2 FIDO2 Security Key");
+
+ // uTrust
+ vendors.put(UUID.fromString("73402251-f2a8-4f03-873e-3cb6db604b03"), "uTrust FIDO2 Security Key");
+
+ // Vancosys
+ vendors.put(UUID.fromString("39a5647e-1853-446c-a1f6-a79bae9f5bc7"), "Vancosys Android Authenticator");
+ vendors.put(UUID.fromString("820d89ed-d65a-409e-85cb-f73f0578f82a"), "Vancosys iOS Authenticator");
+
+ // VinCSS
+ vendors.put(UUID.fromString("5fdb81b8-53f0-4967-a881-f5ec26fe4d18"), "VinCSS FIDO2 Authenticator");
+
+ // VivoKey
+ vendors.put(UUID.fromString("d7a423ad-3e19-4492-9200-78137dccc136"), "VivoKey Apex FIDO2");
+
+ // Windows Hello
+ vendors.put(UUID.fromString("08987058-cadc-4b81-b6e1-30de50dcbe96"), "Windows Hello Hardware Authenticator");
+ vendors.put(UUID.fromString("6028b017-b1d4-4c02-b4b3-afcdafc96bb2"), "Windows Hello Software Authenticator");
+ vendors.put(UUID.fromString("9ddd1817-af5a-4672-a2b9-3e3dd95000a9"), "Windows Hello VBS Hardware Authenticator");
+
+ // WiSECURE
+ vendors.put(UUID.fromString("504d7149-4e4c-3841-4555-55445a677357"), "WiSECURE AuthTron USB FIDO2 Authenticator");
+
+ // Yubico
+ vendors.put(UUID.fromString("0bb43545-fd2c-4185-87dd-feb0b2916ace"), "Security Key NFC by Yubico - Enterprise Edition");
+ vendors.put(UUID.fromString("149a2021-8ef6-4133-96b8-81f8d5b7f1f5"), "Security Key by Yubico with NFC");
+ vendors.put(UUID.fromString("2fc0579f-8113-47ea-b116-bb5a8db9202a"), "YubiKey 5 Series with NFC");
+ vendors.put(UUID.fromString("6d44ba9b-f6ec-2e49-b930-0c8fe920cb73"), "Security Key by Yubico with NFC");
+ vendors.put(UUID.fromString("73bb0cd4-e502-49b8-9c6f-b59445bf720b"), "YubiKey 5 FIPS Series");
+ vendors.put(UUID.fromString("85203421-48f9-4355-9bc8-8a53846e5083"), "YubiKey 5Ci FIPS");
+ vendors.put(UUID.fromString("a4e9fc6d-4cbe-4758-b8ba-37598bb5bbaa"), "Security Key by Yubico with NFC");
+ vendors.put(UUID.fromString("b92c3f9a-c014-4056-887f-140a2501163b"), "Security Key by Yubico");
+ vendors.put(UUID.fromString("c1f9a0bc-1dd2-404a-b27f-8e29047a43fd"), "YubiKey 5 FIPS Series with NFC");
+ vendors.put(UUID.fromString("c5ef55ff-ad9a-4b9f-b580-adebafe026d0"), "YubiKey 5Ci");
+ vendors.put(UUID.fromString("cb69481e-8ff7-4039-93ec-0a2729a154a8"), "YubiKey 5 Series");
+ vendors.put(UUID.fromString("d8522d9f-575b-4866-88a9-ba99fa02f35b"), "YubiKey Bio Series");
+ vendors.put(UUID.fromString("ee882879-721c-4913-9775-3dfcce97072a"), "YubiKey 5 Series");
+ vendors.put(UUID.fromString("f8a011f3-8c0a-4d15-8006-17111f9edc7d"), "Security Key by Yubico");
+ vendors.put(UUID.fromString("fa2b99dc-9e39-4257-8f92-4a30d23c4118"), "YubiKey 5 Series with NFC");
+ vendors.put(UUID.fromString("34f5766d-1536-4a24-9033-0e294e510fb0"), "YubiKey 5 Series CTAP2.1 Preview Expired");
+ vendors.put(UUID.fromString("83c47309-aabb-4108-8470-8be838b573cb"), "YubiKey Bio Series (Enterprise Profile)");
+
+ // Other authenticators
+ vendors.put(UUID.fromString("ad784498-1902-3f54-b99a-10bb7dbd9588"), "Apple MacBook Pro 14-inch, 2021");
+ vendors.put(UUID.nameUUIDFromBytes(new byte[16]), "Apple Passkeys");
+
+ }
+
+ public String vendorName(byte[] aaguid) {
+ final String vendor = vendors.get(UUID.nameUUIDFromBytes(aaguid));
+ return Objects.requireNonNullElse(vendor, "Unknown FIDO2 Authenticator");
+ }
+
+}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AssertionChallenge.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AssertionChallenge.java
new file mode 100644
index 000000000..6bcc5286f
--- /dev/null
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AssertionChallenge.java
@@ -0,0 +1,35 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.powerauth.fido2.rest.model.entity;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * @author Petr Dvorak, petr@wultra.com
+ */
+@Data
+public class AssertionChallenge {
+
+ private List applicationIds;
+ private String challenge;
+ private String userId;
+
+}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AttestationObject.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AttestationObject.java
new file mode 100644
index 000000000..203d32cd2
--- /dev/null
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AttestationObject.java
@@ -0,0 +1,41 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.powerauth.fido2.rest.model.entity;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.wultra.powerauth.fido2.rest.model.converter.serialization.AttestationStatementDeserializer;
+import com.wultra.powerauth.fido2.rest.model.converter.serialization.AuthenticatorDataDeserializer;
+import lombok.Data;
+
+/**
+ * @author Petr Dvorak, petr@wultra.com
+ */
+@Data
+public class AttestationObject {
+
+ private String encoded;
+
+ private String fmt;
+
+ @JsonDeserialize(using = AuthenticatorDataDeserializer.class)
+ private AuthenticatorData authData;
+ @JsonDeserialize(using = AttestationStatementDeserializer.class)
+ private AttestationStatement attStmt;
+
+}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AttestationStatement.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AttestationStatement.java
new file mode 100644
index 000000000..8c59404fe
--- /dev/null
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AttestationStatement.java
@@ -0,0 +1,31 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.powerauth.fido2.rest.model.entity;
+
+import com.wultra.powerauth.fido2.rest.model.enumeration.SignatureAlgorithm;
+import lombok.Data;
+
+/**
+ * @author Petr Dvorak, petr@wultra.com
+ */
+@Data
+public class AttestationStatement {
+ private SignatureAlgorithm algorithm;
+ private byte[] signature;
+}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AttestedCredentialData.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AttestedCredentialData.java
new file mode 100644
index 000000000..f08265ec2
--- /dev/null
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AttestedCredentialData.java
@@ -0,0 +1,33 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.powerauth.fido2.rest.model.entity;
+
+import lombok.Data;
+
+/**
+ * @author Petr Dvorak, petr@wultra.com
+ */
+@Data
+public class AttestedCredentialData {
+
+ private byte[] aaguid;
+ private byte[] credentialId;
+ private PublicKeyObject publicKeyObject;
+
+}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AuthenticatorAssertionResponse.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AuthenticatorAssertionResponse.java
new file mode 100644
index 000000000..54d962491
--- /dev/null
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AuthenticatorAssertionResponse.java
@@ -0,0 +1,48 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.powerauth.fido2.rest.model.entity;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.wultra.powerauth.fido2.rest.model.converter.serialization.AuthenticatorDataDeserializer;
+import com.wultra.powerauth.fido2.rest.model.converter.serialization.Base64ToByteArrayDeserializer;
+import com.wultra.powerauth.fido2.rest.model.converter.serialization.Base64ToStringDeserializer;
+import com.wultra.powerauth.fido2.rest.model.converter.serialization.CollectedClientDataDeserializer;
+import jakarta.validation.constraints.NotEmpty;
+import lombok.Data;
+
+/**
+ * @author Petr Dvorak, petr@wultra.com
+ */
+@Data
+public class AuthenticatorAssertionResponse {
+
+ @JsonDeserialize(using = CollectedClientDataDeserializer.class)
+ private CollectedClientData clientDataJSON;
+
+ @JsonDeserialize(using = AuthenticatorDataDeserializer.class)
+ private AuthenticatorData authenticatorData;
+
+ @NotEmpty
+ @JsonDeserialize(using = Base64ToByteArrayDeserializer.class)
+ private byte[] signature;
+
+ @JsonDeserialize(using = Base64ToStringDeserializer.class)
+ private String userHandle;
+
+}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AuthenticatorAttestationResponse.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AuthenticatorAttestationResponse.java
new file mode 100644
index 000000000..94a4538ce
--- /dev/null
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AuthenticatorAttestationResponse.java
@@ -0,0 +1,41 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.powerauth.fido2.rest.model.entity;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.wultra.powerauth.fido2.rest.model.converter.serialization.AttestationObjectDeserializer;
+import com.wultra.powerauth.fido2.rest.model.converter.serialization.Base64ToByteArrayDeserializer;
+import com.wultra.powerauth.fido2.rest.model.converter.serialization.CollectedClientDataDeserializer;
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * @author Petr Dvorak, petr@wultra.com
+ */
+@Data
+public class AuthenticatorAttestationResponse {
+
+ @JsonDeserialize(using = CollectedClientDataDeserializer.class)
+ private CollectedClientData clientDataJSON;
+ @JsonDeserialize(using = AttestationObjectDeserializer.class)
+ private AttestationObject attestationObject;
+ private List transports;
+
+}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AuthenticatorData.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AuthenticatorData.java
new file mode 100644
index 000000000..dc6b1c587
--- /dev/null
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AuthenticatorData.java
@@ -0,0 +1,40 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.powerauth.fido2.rest.model.entity;
+
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.PositiveOrZero;
+import lombok.Data;
+
+/**
+ * @author Petr Dvorak, petr@wultra.com
+ */
+@Data
+public class AuthenticatorData {
+
+ @NotEmpty
+ private byte[] encoded;
+ @NotEmpty
+ private byte[] rpIdHash;
+ private Flags flags = new Flags();
+ @PositiveOrZero
+ private int signCount;
+ private AttestedCredentialData attestedCredentialData = new AttestedCredentialData();
+
+}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AuthenticatorDetail.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AuthenticatorDetail.java
new file mode 100644
index 000000000..e5b42e7d1
--- /dev/null
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AuthenticatorDetail.java
@@ -0,0 +1,52 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.powerauth.fido2.rest.model.entity;
+
+import com.wultra.security.powerauth.client.model.enumeration.ActivationStatus;
+import lombok.Data;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Information about a registered authenticator.
+ *
+ * @author Petr Dvorak, petr@wultra.com
+ */
+@Data
+public class AuthenticatorDetail {
+
+ private String userId;
+ private String activationId;
+ private String applicationId;
+ private String activationName;
+ private String externalId;
+ private ActivationStatus activationStatus;
+ private String extras;
+ private String platform;
+ private String deviceInfo;
+ private String blockedReason;
+ private long failedAttempts;
+ private long maxFailedAttempts;
+ private List applicationRoles = new ArrayList<>();
+ private List activationFlags = new ArrayList<>();
+ private byte[] publicKeyBytes;
+
+}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/CollectedClientData.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/CollectedClientData.java
new file mode 100644
index 000000000..5f021963e
--- /dev/null
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/CollectedClientData.java
@@ -0,0 +1,43 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.powerauth.fido2.rest.model.entity;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.wultra.powerauth.fido2.rest.model.converter.serialization.Base64ToStringDeserializer;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotEmpty;
+import lombok.Data;
+
+/**
+ * @author Petr Dvorak, petr@wultra.com
+ */
+@Data
+public class CollectedClientData {
+ @NotEmpty
+ private String encoded;
+ @NotBlank
+ private String type;
+ @NotEmpty
+ @JsonDeserialize(using = Base64ToStringDeserializer.class)
+ private String challenge;
+ @NotBlank
+ private String origin;
+ private String topOrigin;
+ private boolean crossOrigin;
+}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/EllipticCurvePoint.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/EllipticCurvePoint.java
new file mode 100644
index 000000000..798bd5e03
--- /dev/null
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/EllipticCurvePoint.java
@@ -0,0 +1,30 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.powerauth.fido2.rest.model.entity;
+
+import lombok.Data;
+
+/**
+ * @author Petr Dvorak, petr@wultra.com
+ */
+@Data
+public class EllipticCurvePoint {
+ private byte[] x;
+ private byte[] y;
+}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/Flags.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/Flags.java
new file mode 100644
index 000000000..d7ea3d5c5
--- /dev/null
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/Flags.java
@@ -0,0 +1,38 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.powerauth.fido2.rest.model.entity;
+
+import lombok.Data;
+
+/**
+ * @author Petr Dvorak, petr@wultra.com
+ */
+@Data
+public class Flags {
+
+ private boolean userPresent;
+ private boolean reservedBit2;
+ private boolean userVerified;
+ private boolean backupEligible;
+ private boolean backupState;
+ private boolean reservedBit6;
+ private boolean attestedCredentialsIncluded;
+ private boolean extensionDataInlcuded;
+
+}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/PublicKeyObject.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/PublicKeyObject.java
new file mode 100644
index 000000000..58ac15f71
--- /dev/null
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/PublicKeyObject.java
@@ -0,0 +1,35 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.powerauth.fido2.rest.model.entity;
+
+import com.wultra.powerauth.fido2.rest.model.enumeration.CurveType;
+import com.wultra.powerauth.fido2.rest.model.enumeration.SignatureAlgorithm;
+import lombok.Data;
+
+/**
+ * @author Petr Dvorak, petr@wultra.com
+ */
+@Data
+public class PublicKeyObject {
+
+ private SignatureAlgorithm algorithm;
+ private CurveType curveType;
+ private EllipticCurvePoint point = new EllipticCurvePoint();
+
+}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/RegistrationChallenge.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/RegistrationChallenge.java
new file mode 100644
index 000000000..dc97ee192
--- /dev/null
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/RegistrationChallenge.java
@@ -0,0 +1,34 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.powerauth.fido2.rest.model.entity;
+
+import lombok.Data;
+
+/**
+ * Model class representing registration challenge.
+ *
+ * @author Petr Dvorak, petr@wultra.com
+ */
+@Data
+public class RegistrationChallenge {
+ private String activationId;
+ private String applicationId;
+ private String challenge;
+ private String userId;
+}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/enumeration/CurveType.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/enumeration/CurveType.java
new file mode 100644
index 000000000..21598d731
--- /dev/null
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/enumeration/CurveType.java
@@ -0,0 +1,27 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.powerauth.fido2.rest.model.enumeration;
+
+/**
+ * @author Petr Dvorak, petr@wultra.com
+ */
+public enum CurveType {
+ P256,
+ UNKNOWN
+}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/enumeration/Fmt.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/enumeration/Fmt.java
new file mode 100644
index 000000000..10a9f66fa
--- /dev/null
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/enumeration/Fmt.java
@@ -0,0 +1,45 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.powerauth.fido2.rest.model.enumeration;
+
+import java.util.List;
+
+/**
+ * @author Petr Dvorak, petr@wultra.com
+ */
+public enum Fmt {
+
+ FMT_PACKED("packed"),
+ FMT_NONE("none");
+
+ private final String value;
+
+ public static final List allowedFmt = List.of(
+ FMT_PACKED.value, FMT_NONE.value
+ // not supported: "tpm", "android-key", "android-safetynet", "apple", "fido-u2f"
+ );
+
+ Fmt(String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+ return value;
+ }
+}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/enumeration/SignatureAlgorithm.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/enumeration/SignatureAlgorithm.java
new file mode 100644
index 000000000..712069058
--- /dev/null
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/enumeration/SignatureAlgorithm.java
@@ -0,0 +1,27 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.powerauth.fido2.rest.model.enumeration;
+
+/**
+ * @author Petr Dvorak, petr@wultra.com
+ */
+public enum SignatureAlgorithm {
+ ES256,
+ UNKNOWN
+}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/AssertionChallengeRequest.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/AssertionChallengeRequest.java
new file mode 100644
index 000000000..ddc5a19fc
--- /dev/null
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/AssertionChallengeRequest.java
@@ -0,0 +1,43 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.powerauth.fido2.rest.model.request;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotEmpty;
+import lombok.Data;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Request for obtaining assertion challenge.
+ *
+ * @author Petr Dvorak, petr@wultra.com
+ */
+@Data
+public class AssertionChallengeRequest {
+ @NotEmpty
+ private List<@NotBlank String> applicationIds;
+ private String externalId;
+ @NotBlank
+ private String operationType;
+ private Map parameters = new HashMap<>();
+
+}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/AssertionRequest.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/AssertionRequest.java
new file mode 100644
index 000000000..a67618d2b
--- /dev/null
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/AssertionRequest.java
@@ -0,0 +1,50 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.powerauth.fido2.rest.model.request;
+
+import com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorAssertionResponse;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Petr Dvorak, petr@wultra.com
+ */
+@Data
+public class AssertionRequest {
+
+ @NotBlank
+ private String id;
+ @NotBlank
+ private String type;
+ @NotBlank
+ private String authenticatorAttachment;
+ private AuthenticatorAssertionResponse response = new AuthenticatorAssertionResponse();
+ @NotBlank
+ private String applicationId;
+ @NotBlank
+ private String relyingPartyId;
+ private List allowedOrigins = new ArrayList<>();
+ private List allowedTopOrigins = new ArrayList<>();
+ private boolean requiresUserVerification;
+ private String expectedChallenge;
+
+}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/RegisteredAuthenticatorsRequest.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/RegisteredAuthenticatorsRequest.java
new file mode 100644
index 000000000..3a742b537
--- /dev/null
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/RegisteredAuthenticatorsRequest.java
@@ -0,0 +1,34 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.powerauth.fido2.rest.model.request;
+
+import lombok.Data;
+
+/**
+ * Request for obtaining list of registered authenticators for given user.
+ *
+ * @author Petr Dvorak, petr@wultra.com
+ */
+@Data
+public class RegisteredAuthenticatorsRequest {
+
+ private String applicationId;
+ private String userId;
+
+}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/RegistrationChallengeRequest.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/RegistrationChallengeRequest.java
new file mode 100644
index 000000000..d86c16126
--- /dev/null
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/RegistrationChallengeRequest.java
@@ -0,0 +1,36 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.powerauth.fido2.rest.model.request;
+
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+/**
+ * Request object for registration challenge.
+ *
+ * @author Petr Dvorak, petr@wultra.com
+ */
+@Data
+public class RegistrationChallengeRequest {
+
+ @NotBlank
+ private String userId;
+ @NotBlank
+ private String applicationId;
+}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/RegistrationRequest.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/RegistrationRequest.java
new file mode 100644
index 000000000..c29fbd17d
--- /dev/null
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/RegistrationRequest.java
@@ -0,0 +1,56 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.powerauth.fido2.rest.model.request;
+
+import com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorAttestationResponse;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Petr Dvorak, petr@wultra.com
+ */
+@Data
+public class RegistrationRequest {
+
+ // Relying party parameters
+ @NotBlank
+ private String applicationId;
+ @NotBlank
+ private String activationName;
+ private String expectedChallenge;
+
+ // Authenticator parameters
+ @NotBlank
+ private String id;
+ @NotBlank
+ private String type;
+ @NotBlank
+ private String authenticatorAttachment;
+ private AuthenticatorAttestationResponse response = new AuthenticatorAttestationResponse();
+ @NotBlank
+ private String relyingPartyId;
+ private List allowedOrigins = new ArrayList<>();
+ private List allowedTopOrigins = new ArrayList<>();
+ private boolean requiresUserVerification;
+
+
+}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/response/AssertionChallengeResponse.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/response/AssertionChallengeResponse.java
new file mode 100644
index 000000000..275e441cb
--- /dev/null
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/response/AssertionChallengeResponse.java
@@ -0,0 +1,33 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.powerauth.fido2.rest.model.response;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * @author Petr Dvorak, petr@wultra.com
+ */
+@Data
+public class AssertionChallengeResponse {
+ private List applicationIds;
+ private String challenge;
+ private String userId;
+}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/response/AssertionVerificationResponse.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/response/AssertionVerificationResponse.java
new file mode 100644
index 000000000..f80bdd017
--- /dev/null
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/response/AssertionVerificationResponse.java
@@ -0,0 +1,45 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.powerauth.fido2.rest.model.response;
+
+import com.wultra.security.powerauth.client.model.enumeration.ActivationStatus;
+import lombok.Data;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Result of the assertion verification.
+ *
+ * @author Petr Dvorak, petr@wultra.com
+ */
+@Data
+public class AssertionVerificationResponse {
+ private boolean assertionValid;
+ private String userId;
+ private String activationId;
+ private String applicationId;
+ private ActivationStatus activationStatus;
+ private String blockedReason;
+ private long remainingAttempts;
+ private List applicationRoles = new ArrayList<>();
+ private List activationFlags = new ArrayList<>();
+
+}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/response/RegisteredAuthenticatorsResponse.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/response/RegisteredAuthenticatorsResponse.java
new file mode 100644
index 000000000..5cd7b4da9
--- /dev/null
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/response/RegisteredAuthenticatorsResponse.java
@@ -0,0 +1,35 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.powerauth.fido2.rest.model.response;
+
+import com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorDetail;
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Petr Dvorak, petr@wultra.com
+ */
+@Data
+public class RegisteredAuthenticatorsResponse {
+
+ private List authenticators = new ArrayList<>();
+
+}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/response/RegistrationChallengeResponse.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/response/RegistrationChallengeResponse.java
new file mode 100644
index 000000000..48f24cbc7
--- /dev/null
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/response/RegistrationChallengeResponse.java
@@ -0,0 +1,33 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.powerauth.fido2.rest.model.response;
+
+import lombok.Data;
+
+/**
+ * @author Petr Dvorak, petr@wultra.com
+ */
+@Data
+public class RegistrationChallengeResponse {
+ private String activationId;
+ private String applicationId;
+ private String challenge;
+ private String userId;
+
+}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/response/RegistrationResponse.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/response/RegistrationResponse.java
new file mode 100644
index 000000000..2df4e62e2
--- /dev/null
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/response/RegistrationResponse.java
@@ -0,0 +1,50 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.powerauth.fido2.rest.model.response;
+
+import com.wultra.security.powerauth.client.model.enumeration.ActivationStatus;
+import lombok.Data;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Petr Dvorak, petr@wultra.com
+ */
+@Data
+public class RegistrationResponse {
+
+ private String userId;
+ private String activationId;
+ private String applicationId;
+ private String externalId;
+ private String activationName;
+ private ActivationStatus activationStatus;
+ private String extras;
+ private String platform;
+ private String deviceInfo;
+ private String blockedReason;
+ private long failedAttempts;
+ private long maxFailedAttempts;
+ private List applicationRoles = new ArrayList<>();
+ private List activationFlags = new ArrayList<>();
+ private byte[] publicKeyBytes;
+
+}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/validator/AssertionRequestValidator.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/validator/AssertionRequestValidator.java
new file mode 100644
index 000000000..f9b1addfb
--- /dev/null
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/validator/AssertionRequestValidator.java
@@ -0,0 +1,91 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.powerauth.fido2.rest.model.validator;
+
+import com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorData;
+import com.wultra.powerauth.fido2.rest.model.entity.CollectedClientData;
+import com.wultra.powerauth.fido2.rest.model.request.AssertionRequest;
+import io.getlime.security.powerauth.crypto.lib.util.Hash;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Validator for the assertion request class.
+ *
+ * @author Petr Dvorak, petr@wultra.com
+ */
+@Component
+@Slf4j
+public class AssertionRequestValidator {
+
+ public String validate(AssertionRequest request) {
+
+ if (request == null || request.getResponse() == null
+ || request.getResponse().getClientDataJSON() == null
+ || request.getResponse().getAuthenticatorData() == null) {
+ return "Invalid request, you need to include response.clientDataJSON and response.attestationObject.";
+ }
+
+ final CollectedClientData clientDataJSON = request.getResponse().getClientDataJSON();
+
+ if (!"webauthn.get".equals(clientDataJSON.getType())) {
+ return "Request does not contain webauthn.create type.";
+ }
+
+ final String expectedChallenge = request.getExpectedChallenge();
+ if (expectedChallenge != null && !expectedChallenge.equals(clientDataJSON.getChallenge())) {
+ return "Request does not contain the correct challenge.";
+ }
+
+ final String origin = clientDataJSON.getOrigin();
+ final List allowedOrigins = request.getAllowedOrigins();
+ if (origin == null || !allowedOrigins.contains(origin)) {
+ return "Request does not contain the correct origin.";
+ }
+
+ final List allowedTopOrigins = request.getAllowedTopOrigins();
+ if (clientDataJSON.getTopOrigin() != null && !allowedTopOrigins.contains(clientDataJSON.getTopOrigin())) {
+ return "Request contains the top origin which is not allowed.";
+ }
+
+ final AuthenticatorData authenticatorData = request.getResponse().getAuthenticatorData();
+
+ final byte[] rpIdHash = authenticatorData.getRpIdHash();
+ final String relyingPartyId = request.getRelyingPartyId();
+ final byte[] expectedRpIdHash = Hash.sha256(relyingPartyId);
+ if (!Arrays.equals(rpIdHash, expectedRpIdHash)) {
+ return "The origin does not match relying party ID.";
+ }
+
+ if (!authenticatorData.getFlags().isUserPresent()) {
+ return "User is not present during the authentication.";
+ }
+
+ final boolean requiresUserVerification = request.isRequiresUserVerification();
+ if (requiresUserVerification && !authenticatorData.getFlags().isUserVerified()) {
+ return "User is not present during the authentication, but user verification is required.";
+ }
+
+ return null;
+ }
+
+}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/validator/RegistrationRequestValidator.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/validator/RegistrationRequestValidator.java
new file mode 100644
index 000000000..4f8d902e1
--- /dev/null
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/validator/RegistrationRequestValidator.java
@@ -0,0 +1,125 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.powerauth.fido2.rest.model.validator;
+
+import com.wultra.powerauth.fido2.rest.model.entity.*;
+import com.wultra.powerauth.fido2.rest.model.enumeration.Fmt;
+import com.wultra.powerauth.fido2.rest.model.enumeration.SignatureAlgorithm;
+import com.wultra.powerauth.fido2.rest.model.request.RegistrationRequest;
+import io.getlime.security.powerauth.crypto.lib.util.Hash;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Validator for registration request.
+ *
+ * @author Petr Dvorak, petr@wultra.com
+ */
+@Component
+@Slf4j
+public class RegistrationRequestValidator {
+
+ public String validate(RegistrationRequest request) {
+
+ if (request == null || request.getResponse() == null
+ || request.getResponse().getClientDataJSON() == null
+ || request.getResponse().getAttestationObject() == null) {
+ return "Invalid request, you need to include response.clientDataJSON and response.attestationObject.";
+ }
+
+ final CollectedClientData clientDataJSON = request.getResponse().getClientDataJSON();
+
+ if (!"webauthn.create".equals(clientDataJSON.getType())) {
+ return "Request does not contain webauthn.create type.";
+ }
+
+ final String expectedChallenge = request.getExpectedChallenge();
+ if (expectedChallenge != null && !expectedChallenge.equals(clientDataJSON.getChallenge())) {
+ return "Request does not contain the correct challenge.";
+ }
+
+ final String origin = clientDataJSON.getOrigin();
+ final List allowedOrigins = request.getAllowedOrigins();
+ if (origin == null || !allowedOrigins.contains(origin)) {
+ return "Request does not contain the correct origin.";
+ }
+
+ final List allowedTopOrigins = request.getAllowedTopOrigins();
+ if (clientDataJSON.getTopOrigin() != null && !allowedTopOrigins.contains(clientDataJSON.getTopOrigin())) {
+ return "Request contains the top origin which is not allowed.";
+ }
+
+ final AttestationObject attestationObject = request.getResponse().getAttestationObject();
+ final AuthenticatorData authData = attestationObject.getAuthData();
+ if (authData == null) {
+ return "Missing authentication data.";
+ }
+
+ final byte[] rpIdHash = authData.getRpIdHash();
+ final String relyingPartyId = request.getRelyingPartyId();
+ final byte[] expectedRpIdHash = Hash.sha256(relyingPartyId);
+ if (!Arrays.equals(rpIdHash, expectedRpIdHash)) {
+ return "The origin does not match relying party ID.";
+ }
+
+ final Flags flags = authData.getFlags();
+
+ if (!flags.isUserPresent()) {
+ return "User is not present during the authentication.";
+ }
+
+ final boolean requiresUserVerification = request.isRequiresUserVerification();
+ if (requiresUserVerification && !flags.isUserVerified()) {
+ return "User is not present during the authentication, but user verification is required.";
+ }
+
+ final String fmt = attestationObject.getFmt();
+ if (!Fmt.allowedFmt.contains(fmt)) {
+ return "Invalid attestation format identifier.";
+ }
+
+ final AttestedCredentialData attestedCredentialData = authData.getAttestedCredentialData();
+ if (attestedCredentialData == null) {
+ return "Missing attestation data.";
+ }
+
+ final PublicKeyObject publicKeyObject = attestedCredentialData.getPublicKeyObject();
+ if (publicKeyObject == null) {
+ return "Missing public key inside attestation data";
+ }
+
+ final SignatureAlgorithm algorithm = publicKeyObject.getAlgorithm();
+ if (SignatureAlgorithm.ES256 != algorithm) {
+ return "The provided algorithm is not supported by the server.";
+ }
+
+ if (fmt.equals(Fmt.FMT_PACKED.getValue())) {
+ final AttestationStatement attStmt = attestationObject.getAttStmt();
+ if (!attStmt.getAlgorithm().equals(algorithm)) {
+ return "Attestation algorithm does not match algorithm used for the public key.";
+ }
+ }
+
+ return null;
+ }
+
+}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/AssertionService.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/AssertionService.java
new file mode 100644
index 000000000..90524cf49
--- /dev/null
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/AssertionService.java
@@ -0,0 +1,103 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.powerauth.fido2.service;
+
+import com.wultra.powerauth.fido2.errorhandling.Fido2AuthenticationFailedException;
+import com.wultra.powerauth.fido2.rest.model.converter.AssertionChallengeConverter;
+import com.wultra.powerauth.fido2.rest.model.converter.AssertionConverter;
+import com.wultra.powerauth.fido2.rest.model.entity.AssertionChallenge;
+import com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorAssertionResponse;
+import com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorDetail;
+import com.wultra.powerauth.fido2.rest.model.request.AssertionChallengeRequest;
+import com.wultra.powerauth.fido2.rest.model.request.AssertionRequest;
+import com.wultra.powerauth.fido2.rest.model.response.AssertionChallengeResponse;
+import com.wultra.powerauth.fido2.rest.model.response.AssertionVerificationResponse;
+import com.wultra.security.powerauth.client.model.enumeration.ActivationStatus;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * Service related to handling assertions.
+ *
+ * @author Petr Dvorak, petr@wultra.com
+ */
+@Service
+@Slf4j
+public class AssertionService {
+
+ private final CryptographyService cryptographyService;
+ private final ChallengeProvider challengeProvider;
+ private final AuthenticatorProvider authenticatorProvider;
+ private final AssertionConverter assertionConverter;
+ private final AssertionChallengeConverter assertionChallengeConverter;
+
+ @Autowired
+ public AssertionService(CryptographyService cryptographyService, ChallengeProvider challengeProvider, AuthenticatorProvider authenticatorProvider, AssertionConverter assertionConverter, AssertionChallengeConverter assertionChallengeConverter) {
+ this.cryptographyService = cryptographyService;
+ this.challengeProvider = challengeProvider;
+ this.authenticatorProvider = authenticatorProvider;
+ this.assertionConverter = assertionConverter;
+ this.assertionChallengeConverter = assertionChallengeConverter;
+ }
+
+ /**
+ * Request assertion challenge value.
+ *
+ * @param request Request with assertion challenge parameters.
+ * @return Assertion challenge information.
+ */
+ public AssertionChallengeResponse requestAssertionChallenge(AssertionChallengeRequest request) throws Exception {
+ final AssertionChallenge assertionChallenge = challengeProvider.provideChallengeForAuthentication(
+ null, request.getApplicationIds(), request.getOperationType(), request.getParameters(), request.getExternalId()
+ );
+ if (assertionChallenge == null) {
+ throw new Fido2AuthenticationFailedException("Unable to obtain challenge with provided parameters.");
+ }
+ return assertionChallengeConverter.fromChallenge(assertionChallenge);
+ }
+
+ /**
+ * Authenticate using the provided request.
+ *
+ * @param request Request with assertion.
+ * @throws Fido2AuthenticationFailedException In case authentication fails.
+ */
+ public AssertionVerificationResponse authenticate(AssertionRequest request) throws Fido2AuthenticationFailedException {
+ try {
+ final AuthenticatorAssertionResponse response = request.getResponse();
+ final String applicationId = request.getApplicationId();
+ final String authenticatorId = request.getId();
+ final AuthenticatorDetail authenticatorDetail = authenticatorProvider.findByCredentialId(applicationId, authenticatorId);
+ if (authenticatorDetail.getActivationStatus() == ActivationStatus.ACTIVE) {
+ final boolean signatureCorrect = cryptographyService.verifySignatureForAssertion(applicationId, authenticatorId, response.getClientDataJSON(), response.getAuthenticatorData(), response.getSignature(), authenticatorDetail);
+ if (signatureCorrect) {
+ return assertionConverter.fromAuthenticatorDetail(authenticatorDetail, signatureCorrect);
+ } else {
+ throw new Fido2AuthenticationFailedException("Authentication failed due to incorrect signature.");
+ }
+ } else {
+ throw new Fido2AuthenticationFailedException("Authentication failed due to incorrect authenticator state.");
+ }
+ } catch (Exception e) {
+ throw new Fido2AuthenticationFailedException("Authentication failed.", e);
+ }
+ }
+
+}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/AuthenticatorProvider.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/AuthenticatorProvider.java
new file mode 100644
index 000000000..9ae64d0fb
--- /dev/null
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/AuthenticatorProvider.java
@@ -0,0 +1,38 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.powerauth.fido2.service;
+
+import com.wultra.powerauth.fido2.errorhandling.Fido2AuthenticationFailedException;
+import com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorDetail;
+
+import java.util.List;
+
+/**
+ * Interface for handling authenticator handling logic.
+ *
+ * @author Petr Dvorak, petr@wultra.com
+ */
+public interface AuthenticatorProvider {
+
+ AuthenticatorDetail storeAuthenticator(String applicationId, String challenge, AuthenticatorDetail authenticatorDetail) throws Fido2AuthenticationFailedException;
+
+ List findByUserId(String userId, String applicationId) throws Fido2AuthenticationFailedException;
+ AuthenticatorDetail findByCredentialId(String credentialId, String applicationId) throws Fido2AuthenticationFailedException;
+
+}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/ChallengeProvider.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/ChallengeProvider.java
new file mode 100644
index 000000000..4801901a9
--- /dev/null
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/ChallengeProvider.java
@@ -0,0 +1,90 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.powerauth.fido2.service;
+
+import com.wultra.powerauth.fido2.errorhandling.Fido2AuthenticationFailedException;
+import com.wultra.powerauth.fido2.rest.model.entity.AssertionChallenge;
+import com.wultra.powerauth.fido2.rest.model.entity.RegistrationChallenge;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Interface for challenge providers.
+ *
+ * @author Petr Dvorak, petr@wultra.com
+ */
+public interface ChallengeProvider {
+
+ /**
+ * Obtain challenge information based on challenge value for registration.
+ *
+ * @param applicationId Application key.
+ * @param challenge Challenge value.
+ * @return Challenge Information.
+ * @throws Exception In case any issue occur during processing.
+ */
+ RegistrationChallenge provideChallengeForRegistrationChallengeValue(String applicationId, String challenge) throws Exception;
+
+ /**
+ * Obtain challenge for registration.
+ *
+ * @param userId User ID.
+ * @param applicationId Application ID.
+ * @return Registration challenge.
+ * @throws Exception In case any issue occur during processing.
+ */
+ RegistrationChallenge provideChallengeForRegistration(String userId, String applicationId) throws Exception;
+
+ /**
+ * Obtain challenge for authentication.
+ *
+ * @param userId User ID.
+ * @param applicationIds List of application ID.
+ * @param operationType Type of the operation this challenge is for.
+ * @param parameters Operation parameters.
+ * @return Assertion challenge.
+ * @throws Exception In case any issue occur during processing.
+ */
+ default AssertionChallenge provideChallengeForAuthentication(String userId, List applicationIds, String operationType, Map parameters) throws Exception {
+ return provideChallengeForAuthentication(userId, applicationIds, operationType, parameters, null);
+ };
+
+ /**
+ * Obtain challenge for authentication.
+ *
+ * @param userId User ID.
+ * @param applicationIds List of application ID.
+ * @param operationType Type of the operation this challenge is for.
+ * @param parameters Operation parameters.
+ * @param externalAuthenticationId External ID of operation, i.e., transaction in transaction system.
+ * @return Assertion challenge.
+ * @throws Exception In case any issue occur during processing.
+ */
+ AssertionChallenge provideChallengeForAuthentication(String userId, List applicationIds, String operationType, Map parameters, String externalAuthenticationId) throws Exception;
+
+ /**
+ * Revoke challenge based on the challenge value.
+ *
+ * @param applicationId Application ID.
+ * @param challengeValue Challenge value.
+ * @throws Exception In case any issue occur during processing.
+ */
+ void revokeChallengeForRegistrationChallengeValue(String applicationId, String challengeValue) throws Exception;
+}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/CryptographyService.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/CryptographyService.java
new file mode 100644
index 000000000..f5ed5e0f0
--- /dev/null
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/CryptographyService.java
@@ -0,0 +1,37 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.powerauth.fido2.service;
+
+import com.wultra.powerauth.fido2.rest.model.entity.AttestedCredentialData;
+import com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorData;
+import com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorDetail;
+import com.wultra.powerauth.fido2.rest.model.entity.CollectedClientData;
+
+/**
+ * Interface representing FIDO2 verification service.
+ *
+ * @author Petr Dvorak, petr@wultra.com
+ */
+public interface CryptographyService {
+ boolean verifySignatureForRegistration(String applicationId, CollectedClientData clientDataJSON, AuthenticatorData authData, byte[] signature, AttestedCredentialData attestedCredentialData) throws Exception;
+
+ boolean verifySignatureForAssertion(String applicationId, String authenticatorId, CollectedClientData clientDataJSON, AuthenticatorData authData, byte[] signature, AuthenticatorDetail authenticatorDetail) throws Exception;
+
+ byte[] publicKeyToBytes(AttestedCredentialData attestedCredentialData) throws Exception;
+}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/RegistrationService.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/RegistrationService.java
new file mode 100644
index 000000000..0cca0b6b4
--- /dev/null
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/RegistrationService.java
@@ -0,0 +1,109 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.powerauth.fido2.service;
+
+import com.wultra.powerauth.fido2.errorhandling.Fido2AuthenticationFailedException;
+import com.wultra.powerauth.fido2.rest.model.converter.RegistrationConverter;
+import com.wultra.powerauth.fido2.rest.model.converter.RegistrationChallengeConverter;
+import com.wultra.powerauth.fido2.rest.model.entity.*;
+import com.wultra.powerauth.fido2.rest.model.request.RegistrationRequest;
+import com.wultra.powerauth.fido2.rest.model.response.RegisteredAuthenticatorsResponse;
+import com.wultra.powerauth.fido2.rest.model.response.RegistrationChallengeResponse;
+import com.wultra.powerauth.fido2.rest.model.response.RegistrationResponse;
+import com.wultra.powerauth.fido2.rest.model.validator.RegistrationRequestValidator;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * Service related to handling registrations.
+ *
+ * @author Petr Dvorak, petr@wultra.com
+ */
+@Service
+@Slf4j
+public class RegistrationService {
+
+ private final AuthenticatorProvider authenticatorProvider;
+ private final ChallengeProvider challengeProvider;
+ private final RegistrationChallengeConverter registrationChallengeConverter;
+ private final RegistrationConverter registrationConverter;
+ private final RegistrationRequestValidator registrationRequestValidator;
+ private final CryptographyService cryptographyService;
+
+ @Autowired
+ public RegistrationService(AuthenticatorProvider authenticatorProvider, ChallengeProvider challengeProvider, RegistrationChallengeConverter registrationChallengeConverter, RegistrationConverter registrationConverter, RegistrationRequestValidator registrationRequestValidator, CryptographyService cryptographyService) {
+ this.authenticatorProvider = authenticatorProvider;
+ this.challengeProvider = challengeProvider;
+ this.registrationChallengeConverter = registrationChallengeConverter;
+ this.registrationConverter = registrationConverter;
+ this.registrationRequestValidator = registrationRequestValidator;
+ this.cryptographyService = cryptographyService;
+ }
+
+ public RegisteredAuthenticatorsResponse registrationsForUser(String userId, String applicationId) throws Fido2AuthenticationFailedException {
+ final RegisteredAuthenticatorsResponse responseObject = new RegisteredAuthenticatorsResponse();
+ responseObject.getAuthenticators().addAll(authenticatorProvider.findByUserId(userId, applicationId));
+ return responseObject;
+ }
+
+ public RegistrationChallengeResponse requestRegistrationChallenge(String userId, String applicationId) throws Exception {
+ final RegistrationChallenge challenge = challengeProvider.provideChallengeForRegistration(userId, applicationId);
+ return registrationChallengeConverter.fromChallenge(challenge);
+ }
+
+ public RegistrationResponse register(RegistrationRequest requestObject) throws Exception {
+ final String applicationId = requestObject.getApplicationId();
+
+ final String error = registrationRequestValidator.validate(requestObject);
+ if (error != null) {
+ throw new Fido2AuthenticationFailedException(error);
+ }
+
+ final AuthenticatorAttestationResponse response = requestObject.getResponse();
+
+ final CollectedClientData clientDataJSON = response.getClientDataJSON();
+ final String challengeValue = clientDataJSON.getChallenge();
+
+ final AttestationObject attestationObject = response.getAttestationObject();
+ final AttestationStatement attStmt = attestationObject.getAttStmt();
+ final byte[] signature = attStmt.getSignature();
+
+ final AuthenticatorData authData = attestationObject.getAuthData();
+ final AttestedCredentialData attestedCredentialData = authData.getAttestedCredentialData();
+
+ final String fmt = attestationObject.getFmt();
+ if ("packed".equals(fmt)) {
+ final boolean verifySignature = cryptographyService.verifySignatureForRegistration(applicationId, clientDataJSON, authData, signature, attestedCredentialData);
+ if (!verifySignature) {
+ // Immediately revoke the challenge
+ challengeProvider.revokeChallengeForRegistrationChallengeValue(applicationId, challengeValue);
+ throw new Fido2AuthenticationFailedException("Registration failed");
+ }
+ } else {
+ logger.info("No signature verification on registration");
+ }
+
+ final RegistrationChallenge challenge = challengeProvider.provideChallengeForRegistrationChallengeValue(applicationId, challengeValue);
+ final AuthenticatorDetail authenticatorDetail = registrationConverter.convert(challenge, requestObject, attestedCredentialData.getAaguid(), cryptographyService.publicKeyToBytes(attestedCredentialData));
+ final AuthenticatorDetail authenticatorDetailResponse = authenticatorProvider.storeAuthenticator(requestObject.getApplicationId(), challenge.getChallenge(), authenticatorDetail);
+ return registrationConverter.convertRegistrationResponse(authenticatorDetailResponse);
+ }
+
+}
diff --git a/powerauth-java-server/pom.xml b/powerauth-java-server/pom.xml
index 4592639db..1d69441f9 100644
--- a/powerauth-java-server/pom.xml
+++ b/powerauth-java-server/pom.xml
@@ -113,6 +113,11 @@
io.getlime.core
audit-base
+
+ io.getlime.security
+ powerauth-fido2
+ 1.5.0-SNAPSHOT
+
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/configuration/OpenApiConfiguration.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/configuration/OpenApiConfiguration.java
index 66a1498ce..9cabeee20 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/configuration/OpenApiConfiguration.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/configuration/OpenApiConfiguration.java
@@ -51,7 +51,10 @@ public class OpenApiConfiguration {
@Bean
public GroupedOpenApi powerAuthApiGroup() {
- final String[] packages = {"io.getlime.security.powerauth.app.server.controller.api"};
+ final String[] packages = {
+ "io.getlime.security.powerauth.app.server.controller.api",
+ "com.wultra.powerauth.fido2.rest.controller"
+ };
return GroupedOpenApi.builder()
.group("powerauth")
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/configuration/PowerAuthAuditConfiguration.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/configuration/PowerAuthAuditConfiguration.java
index 9da7fce4d..e4bf773c9 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/configuration/PowerAuthAuditConfiguration.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/configuration/PowerAuthAuditConfiguration.java
@@ -30,7 +30,7 @@
* @author Roman Strobl, roman.strobl@wultra.com
*/
@Configuration
-@ComponentScan(basePackages = {"com.wultra.core.audit.base"})
+@ComponentScan(basePackages = {"com.wultra.core.audit.base", "com.wultra.powerauth.fido2"})
public class PowerAuthAuditConfiguration {
private final AuditFactory auditFactory;
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/controller/RESTControllerAdvice.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/controller/RESTControllerAdvice.java
index 1c76eabee..14dd61ebe 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/controller/RESTControllerAdvice.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/controller/RESTControllerAdvice.java
@@ -17,6 +17,7 @@
*/
package io.getlime.security.powerauth.app.server.controller;
+import com.wultra.powerauth.fido2.errorhandling.Fido2AuthenticationFailedException;
import com.wultra.security.powerauth.client.model.error.PowerAuthError;
import com.wultra.security.powerauth.client.model.error.PowerAuthErrorRecovery;
import io.getlime.core.rest.model.base.response.ObjectResponse;
@@ -59,6 +60,23 @@ public class RESTControllerAdvice {
return new ObjectResponse<>("ERROR", error);
}
+ /**
+ * Resolver for FIDO2 related errors.
+ * @param ex Exception for HTTP message not readable.
+ * @return Error for HTTP request.
+ */
+ @ResponseStatus(HttpStatus.BAD_REQUEST)
+ @ExceptionHandler(value = Fido2AuthenticationFailedException.class)
+ public @ResponseBody ObjectResponse handleFido2AuthenticationFailedException(Fido2AuthenticationFailedException ex) {
+ logger.error("Error occurred while processing the request: {}", ex.getMessage());
+ logger.debug("Exception details:", ex);
+ final PowerAuthError error = new PowerAuthError();
+ error.setCode("ERROR_FIDO2");
+ error.setMessage(ex.getMessage());
+ error.setLocalizedMessage(ex.getLocalizedMessage());
+ return new ObjectResponse<>("ERROR", error);
+ }
+
/**
* Resolver for Activation Recovery Exception.
* @param ex Activation Recovery Exception.
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/ActivationRecordEntity.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/ActivationRecordEntity.java
index 1081956ff..9a586325a 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/ActivationRecordEntity.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/ActivationRecordEntity.java
@@ -60,6 +60,9 @@ public class ActivationRecordEntity implements Serializable {
@Column(name = "activation_otp")
private String activationOtp;
+ @Column(name = "external_id")
+ private String externalId;
+
@Column(name = "user_id", nullable = false, updatable = false)
private String userId;
@@ -69,6 +72,9 @@ public class ActivationRecordEntity implements Serializable {
@Column(name = "extras")
private String extras;
+ @Column(name = "protocol")
+ private String protocol;
+
@Column(name = "platform")
private String platform;
@@ -146,90 +152,6 @@ public class ActivationRecordEntity implements Serializable {
public ActivationRecordEntity() {
}
- /**
- * Constructor with all parameters.
- *
- * @param activationId Activation ID.
- * @param activationCode Activation code.
- * @param activationOtpValidation Activation OTP validation mode.
- * @param activationOtp Activation OTP value.
- * @param userId User Id.
- * @param activationName Activation name.
- * @param extras Extra parameters.
- * @param platform User device platform.
- * @param deviceInfo User device information.
- * @param serverPrivateKeyBase64 Server private key encoded as Base64.
- * @param serverPublicKeyBase64 Server public key encoded as Base64.
- * @param devicePublicKeyBase64 Device public key encoded as Base64.
- * @param counter Counter.
- * @param failedAttempts Current failed attempt count.
- * @param maxFailedAttempts Maximum allowed failed attempt count.
- * @param timestampCreated Created timestamp.
- * @param timestampActivationExpire Activation completion expiration timestamp.
- * @param timestampLastUsed Last signature timestamp.
- * @param activationStatus Activation status.
- * @param blockedReason Reason why activation is blocked.
- * @param serverPrivateKeyEncryption Mode of server private key encryption (0 = NO_ENCRYPTION, 1 = AES_HMAC).
- * @param masterKeyPair Associated master keypair.
- * @param application Associated application.
- */
- public ActivationRecordEntity(String activationId,
- String activationCode,
- ActivationOtpValidation activationOtpValidation,
- String activationOtp,
- String userId,
- String activationName,
- String extras,
- String platform,
- String deviceInfo,
- List flags,
- String serverPrivateKeyBase64,
- String serverPublicKeyBase64,
- String devicePublicKeyBase64,
- Long counter,
- String ctrDataBase64,
- Long failedAttempts,
- Long maxFailedAttempts,
- Date timestampCreated,
- Date timestampActivationExpire,
- Date timestampLastUsed,
- Date timestampLastChange,
- ActivationStatus activationStatus,
- String blockedReason,
- EncryptionMode serverPrivateKeyEncryption,
- Integer version,
- MasterKeyPairEntity masterKeyPair,
- ApplicationEntity application) {
- super();
- this.activationId = activationId;
- this.activationCode = activationCode;
- this.activationOtpValidation = activationOtpValidation;
- this.activationOtp = activationOtp;
- this.userId = userId;
- this.activationName = activationName;
- this.extras = extras;
- this.deviceInfo = deviceInfo;
- this.platform = platform;
- this.flags.addAll(flags);
- this.serverPrivateKeyBase64 = serverPrivateKeyBase64;
- this.serverPublicKeyBase64 = serverPublicKeyBase64;
- this.devicePublicKeyBase64 = devicePublicKeyBase64;
- this.counter = counter;
- this.ctrDataBase64 = ctrDataBase64;
- this.failedAttempts = failedAttempts;
- this.maxFailedAttempts = maxFailedAttempts;
- this.timestampCreated = timestampCreated;
- this.timestampActivationExpire = timestampActivationExpire;
- this.timestampLastUsed = timestampLastUsed;
- this.timestampLastChange = timestampLastChange;
- this.activationStatus = activationStatus;
- this.blockedReason = blockedReason;
- this.serverPrivateKeyEncryption = serverPrivateKeyEncryption;
- this.version = version;
- this.masterKeyPair = masterKeyPair;
- this.application = application;
- }
-
/**
* Get activation ID.
*
@@ -302,6 +224,24 @@ public void setActivationOtp(String activationOtp) {
this.activationOtp = activationOtp;
}
+ /**
+ * Get external ID.
+ *
+ * @return External ID.
+ */
+ public String getExternalId() {
+ return externalId;
+ }
+
+ /**
+ * Set external ID.
+ *
+ * @param externalId External ID.
+ */
+ public void setExternalId(String externalId) {
+ this.externalId = externalId;
+ }
+
/**
* Get user ID
*
@@ -356,6 +296,24 @@ public void setExtras(String extras) {
this.extras = extras;
}
+ /**
+ * Get protocol.
+ *
+ * @return Protocol.
+ */
+ public String getProtocol() {
+ return protocol;
+ }
+
+ /**
+ * Set protocol.
+ *
+ * @param protocol protocol.
+ */
+ public void setProtocol(String protocol) {
+ this.protocol = protocol;
+ }
+
/**
* Get user device platform.
* @return User device platform.
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/OperationEntity.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/OperationEntity.java
index 080f31795..44393ba0c 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/OperationEntity.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/OperationEntity.java
@@ -44,7 +44,7 @@ public class OperationEntity implements Serializable {
@Column(name = "id", updatable = false, length = 37)
private String id;
- @Column(name = "user_id", nullable = false)
+ @Column(name = "user_id")
private String userId;
@ManyToMany
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/repository/ActivationRepository.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/repository/ActivationRepository.java
index 3f2963544..8f4d92b10 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/repository/ActivationRepository.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/repository/ActivationRepository.java
@@ -180,4 +180,15 @@ default Long getActivationCountByActivationCode(String applicationId, String act
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("SELECT a FROM ActivationRecordEntity a WHERE a.activationStatus IN :states AND a.timestampActivationExpire >= :startingTimestamp AND a.timestampActivationExpire < :currentTimestamp")
Stream findAbandonedActivations(Collection states, Date startingTimestamp, Date currentTimestamp);
+
+ /**
+ * Find all activations for given user ID
+ *
+ * @param applicationId Application ID.
+ * @param externalId External authenticatorId
+ * @return List of activations for given user
+ */
+ @Query("SELECT a FROM ActivationRecordEntity a WHERE a.application.id = :applicationId AND a.externalId = :externalId")
+ List findByExternalId(String applicationId, String externalId);
+
}
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/PowerAuthService.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/PowerAuthService.java
index ca46a5eca..e7d2d23e5 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/PowerAuthService.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/PowerAuthService.java
@@ -27,6 +27,7 @@
import io.getlime.security.powerauth.app.server.configuration.PowerAuthServiceConfiguration;
import io.getlime.security.powerauth.app.server.converter.ActivationStatusConverter;
import io.getlime.security.powerauth.app.server.database.model.enumeration.ActivationStatus;
+import com.wultra.security.powerauth.client.model.enumeration.Protocols;
import io.getlime.security.powerauth.app.server.service.behavior.ServiceBehaviorCatalogue;
import io.getlime.security.powerauth.app.server.service.behavior.tasks.RecoveryServiceBehavior;
import io.getlime.security.powerauth.app.server.service.exceptions.GenericServiceException;
@@ -257,6 +258,7 @@ public InitActivationResponse initActivation(InitActivationRequest request) thro
}
// The maxFailedCount and activationExpireTimestamp values can be null, in this case default values are used
try {
+ final Protocols protocol = request.getProtocol();
final String userId = request.getUserId();
final String applicationId = request.getApplicationId();
final Long maxFailedCount = request.getMaxFailureCount();
@@ -265,6 +267,7 @@ public InitActivationResponse initActivation(InitActivationRequest request) thro
final String activationOtp = request.getActivationOtp();
logger.info("InitActivationRequest received, user ID: {}, application ID: {}", userId, applicationId);
final InitActivationResponse response = behavior.getActivationServiceBehavior().initActivation(
+ protocol,
applicationId,
userId,
maxFailedCount,
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/ActivationServiceBehavior.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/ActivationServiceBehavior.java
index 6500a4762..27449d177 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/ActivationServiceBehavior.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/ActivationServiceBehavior.java
@@ -20,6 +20,7 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wultra.security.powerauth.client.model.entity.Activation;
+import com.wultra.security.powerauth.client.model.enumeration.Protocols;
import com.wultra.security.powerauth.client.model.request.RecoveryCodeActivationRequest;
import com.wultra.security.powerauth.client.model.response.*;
import io.getlime.security.powerauth.app.server.configuration.PowerAuthServiceConfiguration;
@@ -256,8 +257,10 @@ public GetActivationListForUserResponse getActivationList(String applicationId,
activationServiceItem.setActivationId(activation.getActivationId());
activationServiceItem.setActivationStatus(activationStatusConverter.convert(activation.getActivationStatus()));
activationServiceItem.setBlockedReason(activation.getBlockedReason());
+ activationServiceItem.setExternalId(activation.getExternalId());
activationServiceItem.setActivationName(activation.getActivationName());
activationServiceItem.setExtras(activation.getExtras());
+ activationServiceItem.setProtocol(activation.getProtocol());
activationServiceItem.setPlatform(activation.getPlatform());
activationServiceItem.setDeviceInfo(activation.getDeviceInfo());
activationServiceItem.getActivationFlags().addAll(activation.getFlags());
@@ -268,6 +271,9 @@ public GetActivationListForUserResponse getActivationList(String applicationId,
activationServiceItem.setApplicationId(activation.getApplication().getId());
// Unknown version is converted to 0 in service
activationServiceItem.setVersion(activation.getVersion() == null ? 0L : activation.getVersion());
+ activationServiceItem.setFailedAttempts(activation.getFailedAttempts());
+ activationServiceItem.setMaxFailedAttempts(activation.getMaxFailedAttempts());
+ activationServiceItem.setDevicePublicKeyBase64(activation.getDevicePublicKeyBase64());
response.getActivations().add(activationServiceItem);
}
}
@@ -320,8 +326,10 @@ public LookupActivationsResponse lookupActivations(List userIds, List userIds, List flags,
KeyConvertor keyConversionUtilities) throws GenericServiceException {
try {
@@ -743,12 +754,14 @@ public InitActivationResponse initActivation(String applicationId, String userId
activation.setActivationCode(activationCode);
activation.setActivationOtpValidation(activationOtpValidationConverter.convertTo(activationOtpValidation));
activation.setActivationOtp(activationOtpHash);
+ activation.setExternalId(null);
activation.setActivationName(null);
activation.setActivationStatus(ActivationStatus.CREATED);
activation.setCounter(0L);
activation.setCtrDataBase64(null);
activation.setDevicePublicKeyBase64(null);
activation.setExtras(null);
+ activation.setProtocol(protocols.toString());
activation.setPlatform(null);
activation.setDeviceInfo(null);
activation.setFailedAttempts(0L);
@@ -914,6 +927,7 @@ public PrepareActivationResponse prepareActivation(String activationCode, String
// The device public key is converted back to bytes and base64 encoded so that the key is saved in normalized form
activation.setDevicePublicKeyBase64(Base64.getEncoder().encodeToString(keyConversion.convertPublicKeyToBytes(devicePublicKey)));
activation.setActivationName(request.getActivationName());
+ activation.setExternalId(request.getExternalId());
activation.setExtras(request.getExtras());
if (request.getPlatform() != null) {
activation.setPlatform(request.getPlatform().toLowerCase());
@@ -1042,7 +1056,7 @@ public CreateActivationResponse createActivation(
final com.wultra.security.powerauth.client.model.enumeration.ActivationOtpValidation activationOtpValidation = activationOtp != null ? com.wultra.security.powerauth.client.model.enumeration.ActivationOtpValidation.ON_COMMIT : com.wultra.security.powerauth.client.model.enumeration.ActivationOtpValidation.NONE;
// Create an activation record and obtain the activation database record
- final InitActivationResponse initResponse = this.initActivation(applicationId, userId, maxFailureCount, activationExpireTimestamp, activationOtpValidation, activationOtp, null, keyConversion);
+ final InitActivationResponse initResponse = this.initActivation(Protocols.POWERAUTH, applicationId, userId, maxFailureCount, activationExpireTimestamp, activationOtpValidation, activationOtp, null, keyConversion);
final String activationId = initResponse.getActivationId();
final ActivationRecordEntity activation = activationRepository.findActivationWithLock(activationId);
@@ -1108,6 +1122,7 @@ public CreateActivationResponse createActivation(
// The device public key is converted back to bytes and base64 encoded so that the key is saved in normalized form
activation.setDevicePublicKeyBase64(Base64.getEncoder().encodeToString(keyConversion.convertPublicKeyToBytes(devicePublicKey)));
activation.setActivationName(request.getActivationName());
+ activation.setExternalId(request.getExternalId());
activation.setExtras(request.getExtras());
if (request.getPlatform() != null) {
activation.setPlatform(request.getPlatform().toLowerCase());
@@ -1713,6 +1728,7 @@ public RecoveryCodeActivationResponse createActivationUsingRecoveryCode(Recovery
// Initialize version 3 activation entity.
// Parameter maxFailureCount can be customized, activationExpireTime is null because activation is committed immediately.
final InitActivationResponse initResponse = initActivation(
+ Protocols.POWERAUTH,
applicationId,
recoveryCodeEntity.getUserId(),
maxFailureCount,
@@ -1748,6 +1764,7 @@ public RecoveryCodeActivationResponse createActivationUsingRecoveryCode(Recovery
// The device public key is converted back to bytes and base64 encoded so that the key is saved in normalized form
activation.setDevicePublicKeyBase64(Base64.getEncoder().encodeToString(keyConversion.convertPublicKeyToBytes(devicePublicKey)));
activation.setActivationName(layer2Request.getActivationName());
+ activation.setExternalId(layer2Request.getExternalId());
activation.setExtras(layer2Request.getExtras());
if (layer2Request.getPlatform() != null) {
activation.setPlatform(layer2Request.getPlatform().toLowerCase());
@@ -1969,6 +1986,45 @@ private void revokeRecoveryCodes(String activationId) {
}
}
+ public List findByExternalId(String applicationId, String externalId) {
+ final Date timestamp = new Date();
+ final List activationsList = repositoryCatalogue.getActivationRepository().findByExternalId(applicationId, externalId);
+
+ final List result = new ArrayList<>();
+
+ if (activationsList != null) {
+ for (ActivationRecordEntity activation : activationsList) {
+
+ deactivatePendingActivation(timestamp, activation, false);
+
+ // Map between database object and service objects
+ final Activation activationServiceItem = new Activation();
+ activationServiceItem.setActivationId(activation.getActivationId());
+ activationServiceItem.setActivationStatus(activationStatusConverter.convert(activation.getActivationStatus()));
+ activationServiceItem.setBlockedReason(activation.getBlockedReason());
+ activationServiceItem.setExternalId(activation.getExternalId());
+ activationServiceItem.setActivationName(activation.getActivationName());
+ activationServiceItem.setExtras(activation.getExtras());
+ activationServiceItem.setProtocol(activation.getProtocol());
+ activationServiceItem.setPlatform(activation.getPlatform());
+ activationServiceItem.setDeviceInfo(activation.getDeviceInfo());
+ activationServiceItem.getActivationFlags().addAll(activation.getFlags());
+ activationServiceItem.setTimestampCreated(activation.getTimestampCreated());
+ activationServiceItem.setTimestampLastUsed(activation.getTimestampLastUsed());
+ activationServiceItem.setTimestampLastChange(activation.getTimestampLastChange());
+ activationServiceItem.setUserId(activation.getUserId());
+ activationServiceItem.setApplicationId(activation.getApplication().getId());
+ // Unknown version is converted to 0 in service
+ activationServiceItem.setVersion(activation.getVersion() == null ? 0L : activation.getVersion());
+ activationServiceItem.setFailedAttempts(activation.getFailedAttempts());
+ activationServiceItem.setMaxFailedAttempts(activation.getMaxFailedAttempts());
+ activationServiceItem.setDevicePublicKeyBase64(activation.getDevicePublicKeyBase64());
+ result.add(activationServiceItem);
+ }
+ }
+ return result;
+ }
+
// Scheduled tasks
@Scheduled(fixedRateString = "${powerauth.service.scheduled.job.activationsCleanup:5000}")
@@ -1988,5 +2044,4 @@ public void expireActivations() {
});
}
}
-
}
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/OperationServiceBehavior.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/OperationServiceBehavior.java
index 87d22d5a0..7e596067f 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/OperationServiceBehavior.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/OperationServiceBehavior.java
@@ -237,13 +237,15 @@ public OperationUserActionResponse attemptApproveOperation(OperationApproveReque
// Check the operation properties match the request
final PowerAuthSignatureTypes factorEnum = PowerAuthSignatureTypes.getEnumFromString(signatureType.toString());
- if (operationEntity.getUserId().equals(userId) // correct user approved the operation
+ final String expectedUserId = operationEntity.getUserId();
+ if ((expectedUserId == null || expectedUserId.equals(userId)) // correct user approved the operation, or no prior user was set
&& operationEntity.getApplications().contains(application.get()) // operation is approved by the expected application
&& isDataEqual(operationEntity, data) // operation data matched the expected value
&& factorsAcceptable(operationEntity, factorEnum) // auth factors are acceptable
&& operationEntity.getMaxFailureCount() > operationEntity.getFailureCount()) { // operation has sufficient attempts left (redundant check)
// Approve the operation
+ operationEntity.setUserId(userId);
operationEntity.setStatus(OperationStatusDo.APPROVED);
operationEntity.setTimestampFinalized(currentTimestamp);
operationEntity.setAdditionalData(mapMerge(operationEntity.getAdditionalData(), additionalData));
@@ -274,6 +276,7 @@ && factorsAcceptable(operationEntity, factorEnum) // auth factors are acceptable
final Long maxFailureCount = operationEntity.getMaxFailureCount();
if (failureCount < maxFailureCount) {
+ operationEntity.setUserId(userId);
operationEntity.setFailureCount(failureCount);
operationEntity.setAdditionalData(mapMerge(operationEntity.getAdditionalData(), additionalData));
@@ -299,6 +302,7 @@ && factorsAcceptable(operationEntity, factorEnum) // auth factors are acceptable
response.setOperation(operationDetailResponse);
return response;
} else {
+ operationEntity.setUserId(userId);
operationEntity.setStatus(OperationStatusDo.FAILED);
operationEntity.setTimestampFinalized(currentTimestamp);
operationEntity.setFailureCount(maxFailureCount); // just in case, set the failure count to max value
@@ -360,10 +364,12 @@ public OperationUserActionResponse rejectOperation(OperationRejectRequest reques
throw localizationProvider.buildExceptionForCode(ServiceError.OPERATION_REJECT_FAILURE);
}
- if (operationEntity.getUserId().equals(userId) // correct user rejects the operation
+ final String expectedUserId = operationEntity.getUserId();
+ if ((expectedUserId == null || expectedUserId.equals(userId)) // correct user approved the operation, or no prior user was set
&& operationEntity.getApplications().contains(application.get())) { // operation is rejected by the expected application
// Reject the operation
+ operationEntity.setUserId(userId);
operationEntity.setStatus(OperationStatusDo.REJECTED);
operationEntity.setTimestampFinalized(currentTimestamp);
operationEntity.setAdditionalData(mapMerge(operationEntity.getAdditionalData(), additionalData));
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAuthenticatorProvider.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAuthenticatorProvider.java
new file mode 100644
index 000000000..4504ff4af
--- /dev/null
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAuthenticatorProvider.java
@@ -0,0 +1,344 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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.server.service.fido2;
+
+import com.wultra.powerauth.fido2.errorhandling.Fido2AuthenticationFailedException;
+import com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorDetail;
+import com.wultra.powerauth.fido2.service.AuthenticatorProvider;
+import com.wultra.security.powerauth.client.model.entity.Activation;
+import com.wultra.security.powerauth.client.model.enumeration.ActivationStatus;
+import com.wultra.security.powerauth.client.model.response.GetActivationListForUserResponse;
+import io.getlime.security.powerauth.app.server.converter.ActivationStatusConverter;
+import io.getlime.security.powerauth.app.server.database.RepositoryCatalogue;
+import io.getlime.security.powerauth.app.server.database.model.entity.ActivationRecordEntity;
+import io.getlime.security.powerauth.app.server.database.model.entity.ApplicationEntity;
+import com.wultra.security.powerauth.client.model.enumeration.Protocols;
+import io.getlime.security.powerauth.app.server.database.repository.ActivationRepository;
+import io.getlime.security.powerauth.app.server.database.repository.ApplicationRepository;
+import io.getlime.security.powerauth.app.server.service.behavior.ServiceBehaviorCatalogue;
+import io.getlime.security.powerauth.app.server.service.exceptions.GenericServiceException;
+import io.getlime.security.powerauth.app.server.service.i18n.LocalizationProvider;
+import io.getlime.security.powerauth.app.server.service.model.ServiceError;
+import io.getlime.security.powerauth.crypto.lib.generator.HashBasedCounter;
+import io.getlime.security.powerauth.crypto.lib.model.exception.CryptoProviderException;
+import io.getlime.security.powerauth.crypto.lib.model.exception.GenericCryptoException;
+import io.getlime.security.powerauth.crypto.lib.util.KeyConvertor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.security.PublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.util.*;
+
+/**
+ * Authenticator provider based on PowerAuth activations.
+ *
+ * @author Petr Dvorak, petr@wultra.com
+ */
+@Service
+@Slf4j
+public class PowerAuthAuthenticatorProvider implements AuthenticatorProvider {
+ private final ApplicationRepository applicationRepository;
+
+ private final RepositoryCatalogue repositoryCatalogue;
+ private final ServiceBehaviorCatalogue serviceBehaviorCatalogue;
+
+ private LocalizationProvider localizationProvider;
+
+ private final KeyConvertor keyConvertor = new KeyConvertor();
+ private final ActivationStatusConverter activationStatusConverter = new ActivationStatusConverter();
+
+ @Autowired
+ public PowerAuthAuthenticatorProvider(RepositoryCatalogue repositoryCatalogue, ServiceBehaviorCatalogue serviceBehaviorCatalogue,
+ ApplicationRepository applicationRepository) {
+ this.repositoryCatalogue = repositoryCatalogue;
+ this.serviceBehaviorCatalogue = serviceBehaviorCatalogue;
+ this.applicationRepository = applicationRepository;
+ }
+
+ @Autowired
+ public void setLocalizationProvider(LocalizationProvider localizationProvider) {
+ this.localizationProvider = localizationProvider;
+ }
+
+ @Override
+ @Transactional
+ public List findByUserId(String userId, String applicationId) throws Fido2AuthenticationFailedException {
+
+ // Find application
+ final Optional application = applicationRepository.findById(applicationId);
+ if (application.isEmpty()) {
+ logger.warn("Application with given ID is not present: {}", applicationId);
+ throw new Fido2AuthenticationFailedException("Application with given ID is not present: " + applicationId);
+ }
+
+ final GetActivationListForUserResponse activationList = serviceBehaviorCatalogue.getActivationServiceBehavior().getActivationList(applicationId, userId);
+
+ final List authenticatorDetailList = new ArrayList<>();
+ for (Activation activation : activationList.getActivations()) {
+ if ((activation.getActivationStatus() == ActivationStatus.CREATED) || (activation.getActivationStatus() == ActivationStatus.PENDING_COMMIT)) { // unfinished activations
+ continue;
+ }
+ if (activation.getActivationStatus() == ActivationStatus.REMOVED && activation.getDevicePublicKeyBase64() == null) { // never finished removed activations
+ continue;
+ }
+ if (!Protocols.FIDO2.toString().equals(activation.getProtocol())) {
+ continue;
+ }
+ final AuthenticatorDetail authenticatorDetail = convert(activation, application.get());
+ authenticatorDetailList.add(authenticatorDetail);
+ }
+ return authenticatorDetailList;
+ }
+
+ @Override
+ @Transactional
+ public AuthenticatorDetail findByCredentialId(String applicationId, String credentialId) throws Fido2AuthenticationFailedException {
+
+ // Find application
+ final Optional application = applicationRepository.findById(applicationId);
+ if (application.isEmpty()) {
+ logger.warn("Application with given ID is not present: {}", applicationId);
+ throw new Fido2AuthenticationFailedException("Application with given ID is not present: " + applicationId);
+ }
+
+ final List activationRecordEntities = serviceBehaviorCatalogue.getActivationServiceBehavior().findByExternalId(applicationId, credentialId);
+ if (activationRecordEntities == null || activationRecordEntities.size() != 1) {
+ throw new Fido2AuthenticationFailedException("Two authenticators with the same ID exist - ambiguous result.");
+ }
+ final Activation activation = activationRecordEntities.get(0);
+
+ return convert(activation, application.get());
+ }
+
+ @Override
+ @Transactional
+ public AuthenticatorDetail storeAuthenticator(String applicationId, String activationCode, AuthenticatorDetail authenticatorDetail) throws Fido2AuthenticationFailedException {
+
+ try {
+ // Get current timestamp
+ final Date timestamp = new Date();
+
+ // Get required repositories
+ final ActivationRepository activationRepository = repositoryCatalogue.getActivationRepository();
+
+ // Find application
+ final Optional application = applicationRepository.findById(applicationId);
+ if (application.isEmpty()) {
+ logger.warn("Application with given ID is not present: {}", applicationId);
+ throw new Fido2AuthenticationFailedException("Application with given ID is not present: " + applicationId);
+ }
+ final ApplicationEntity applicationEntity = application.get();
+
+ // Fetch the current activation by activation code
+ final Set states = Set.of(io.getlime.security.powerauth.app.server.database.model.enumeration.ActivationStatus.CREATED);
+ // Search for activation without lock to avoid potential deadlocks
+ ActivationRecordEntity activation = activationRepository.findCreatedActivationWithoutLock(applicationId, activationCode, states, timestamp);
+
+ // Make sure to deactivate the activation if it is expired
+ if (activation == null) {
+ logger.warn("Activation with activation code: {} could not be obtained. It either does not exist or it already expired.", activationCode);
+ // Rollback is not required, error occurs before writing to database
+ throw localizationProvider.buildExceptionForCode(ServiceError.ACTIVATION_NOT_FOUND);
+ }
+
+ // Make sure this is the FIDO2 authenticator
+ if (!Protocols.FIDO2.toString().equals(activation.getProtocol())) {
+ logger.warn("Invalid authenticator protocol, expected 'fido2', obtained: {}", activation.getProtocol());
+ // Rollback is not required, error occurs before writing to database
+ throw localizationProvider.buildExceptionForCode(ServiceError.ACTIVATION_NOT_FOUND);
+ }
+
+ // Search for activation again to acquire PESSIMISTIC_WRITE lock for activation row
+ activation = activationRepository.findActivationWithLock(activation.getActivationId());
+ deactivatePendingActivation(timestamp, activation);
+
+ // Validate that the activation is in correct state for the prepare step
+ validateCreatedActivation(activation, applicationEntity);
+
+ // Extract the device public key from request
+ final byte[] devicePublicKeyBytes = authenticatorDetail.getPublicKeyBytes();
+ PublicKey devicePublicKey = null;
+ try {
+ devicePublicKey = keyConvertor.convertBytesToPublicKey(devicePublicKeyBytes);
+ } catch (InvalidKeySpecException ex) {
+ handleInvalidPublicKey(activation);
+ }
+
+ // Initialize hash based counter
+ final HashBasedCounter counter = new HashBasedCounter();
+ final byte[] ctrData = counter.init();
+ final String ctrDataBase64 = Base64.getEncoder().encodeToString(ctrData);
+
+ // Update the activation record
+ activation.setActivationStatus(io.getlime.security.powerauth.app.server.database.model.enumeration.ActivationStatus.ACTIVE);
+ // The device public key is converted back to bytes and base64 encoded so that the key is saved in normalized form
+ activation.setDevicePublicKeyBase64(Base64.getEncoder().encodeToString(keyConvertor.convertPublicKeyToBytes(devicePublicKey)));
+ activation.setActivationName(authenticatorDetail.getActivationName());
+ activation.setExternalId(authenticatorDetail.getExternalId());
+ activation.setExtras(authenticatorDetail.getExtras());
+ if (authenticatorDetail.getPlatform() != null) {
+ activation.setPlatform(authenticatorDetail.getPlatform().toLowerCase());
+ } else {
+ activation.setPlatform("unknown");
+ }
+ activation.setDeviceInfo(authenticatorDetail.getDeviceInfo());
+ // PowerAuth protocol version 3.0 uses 0x3 as version in activation status
+ activation.setVersion(3);
+ // Set initial counter data
+ activation.setCtrDataBase64(ctrDataBase64);
+
+ // Persist activation report and notify listeners
+ serviceBehaviorCatalogue.getActivationHistoryServiceBehavior().saveActivationAndLogChange(activation);
+ serviceBehaviorCatalogue.getCallbackUrlBehavior().notifyCallbackListenersOnActivationChange(activation);
+
+ final Activation activationResponse = new Activation();
+ activationResponse.setActivationId(activation.getActivationId());
+ activationResponse.setActivationStatus(activationStatusConverter.convert(activation.getActivationStatus()));
+ activationResponse.setBlockedReason(activation.getBlockedReason());
+ activationResponse.setExternalId(activation.getExternalId());
+ activationResponse.setActivationName(activation.getActivationName());
+ activationResponse.setExtras(activation.getExtras());
+ activationResponse.setPlatform(activation.getPlatform());
+ activationResponse.setDeviceInfo(activation.getDeviceInfo());
+ activationResponse.getActivationFlags().addAll(activation.getFlags());
+ activationResponse.setTimestampCreated(activation.getTimestampCreated());
+ activationResponse.setTimestampLastUsed(activation.getTimestampLastUsed());
+ activationResponse.setTimestampLastChange(activation.getTimestampLastChange());
+ activationResponse.setUserId(activation.getUserId());
+ activationResponse.setApplicationId(activation.getApplication().getId());
+ // Unknown version is converted to 0 in service
+ activationResponse.setVersion(activation.getVersion() == null ? 0L : activation.getVersion());
+ activationResponse.setFailedAttempts(activation.getFailedAttempts());
+ activationResponse.setMaxFailedAttempts(activation.getMaxFailedAttempts());
+ activationResponse.setDevicePublicKeyBase64(activation.getDevicePublicKeyBase64());
+
+ // Generate authenticator detail
+ return convert(activationResponse, applicationEntity);
+ } catch (GenericCryptoException ex) {
+ logger.error(ex.getMessage(), ex);
+ // Rollback is not required, cryptography errors can only occur before writing to database
+ throw new Fido2AuthenticationFailedException("Generic cryptography error");
+ } catch (CryptoProviderException ex) {
+ logger.error(ex.getMessage(), ex);
+ // Rollback is not required, cryptography errors can only occur before writing to database
+ throw new Fido2AuthenticationFailedException("Invalid cryptography provider");
+ } catch (GenericServiceException e) {
+ throw new Fido2AuthenticationFailedException("Generic service exception");
+ }
+
+ }
+
+ private AuthenticatorDetail convert(Activation activation, ApplicationEntity application) {
+ final AuthenticatorDetail authenticatorDetail = new AuthenticatorDetail();
+
+ authenticatorDetail.setApplicationId(activation.getApplicationId());
+ authenticatorDetail.setUserId(activation.getUserId());
+ authenticatorDetail.setActivationId(activation.getActivationId());
+ authenticatorDetail.setActivationStatus(activation.getActivationStatus());
+ authenticatorDetail.setActivationName(activation.getActivationName());
+ authenticatorDetail.setExternalId(activation.getExternalId());
+ authenticatorDetail.setExtras(activation.getExtras());
+ authenticatorDetail.setActivationFlags(activation.getActivationFlags());
+ authenticatorDetail.setDeviceInfo(activation.getDeviceInfo());
+ authenticatorDetail.setPlatform(activation.getPlatform());
+ authenticatorDetail.setFailedAttempts(activation.getFailedAttempts());
+ authenticatorDetail.setMaxFailedAttempts(activation.getMaxFailedAttempts());
+ authenticatorDetail.setBlockedReason(activation.getBlockedReason());
+ if (activation.getDevicePublicKeyBase64() != null) {
+ authenticatorDetail.setPublicKeyBytes(Base64.getDecoder().decode(activation.getDevicePublicKeyBase64()));
+ }
+
+ authenticatorDetail.setApplicationRoles(application.getRoles());
+
+ return authenticatorDetail;
+ }
+
+
+ /**
+ * Validate activation in prepare or create activation step: it should be in CREATED state, it should be linked to correct
+ * application and the activation code should have valid length.
+ *
+ * @param activation Activation used in prepare activation step.
+ * @param application Application used in prepare activation step.
+ * @throws GenericServiceException In case activation state is invalid.
+ */
+ private void validateCreatedActivation(ActivationRecordEntity activation, ApplicationEntity application) throws GenericServiceException {
+ // If there is no such activation or application does not match the activation application, fail validation
+ if (activation == null
+ || !io.getlime.security.powerauth.app.server.database.model.enumeration.ActivationStatus.CREATED.equals(activation.getActivationStatus())
+ || !Objects.equals(activation.getApplication().getRid(), application.getRid())) {
+ logger.info("Activation state is invalid, activation ID: {}", activation != null ? activation.getActivationId() : "unknown");
+ // Regular exception is used during prepareActivation
+ throw localizationProvider.buildExceptionForCode(ServiceError.ACTIVATION_EXPIRED);
+ }
+
+ // Make sure activation code has 23 characters
+ if (activation.getActivationCode().length() != 23) {
+ logger.warn("Activation code is invalid, activation ID: {}", activation.getActivationId());
+ // Regular exception is used during prepareActivation
+ throw localizationProvider.buildExceptionForCode(ServiceError.ACTIVATION_EXPIRED);
+ }
+ }
+
+ /**
+ * Deactivate the activation in CREATED or PENDING_COMMIT if it's activation expiration timestamp
+ * is below the given timestamp.
+ *
+ * @param timestamp Timestamp to check activations against.
+ * @param activation Activation to check.
+ */
+ private void deactivatePendingActivation(Date timestamp, ActivationRecordEntity activation) {
+ if ((activation.getActivationStatus().equals(io.getlime.security.powerauth.app.server.database.model.enumeration.ActivationStatus.CREATED) || activation.getActivationStatus().equals(io.getlime.security.powerauth.app.server.database.model.enumeration.ActivationStatus.PENDING_COMMIT))
+ && (timestamp.getTime() > activation.getTimestampActivationExpire().getTime())) {
+ logger.info("Deactivating pending activation, activation ID: {}", activation.getActivationId());
+ removeActivationInternal(activation);
+ }
+ }
+
+ /**
+ * Internal logic for processing activation removal.
+ *
+ * @param activation Activation entity.
+ */
+ private void removeActivationInternal(final ActivationRecordEntity activation) {
+ activation.setActivationStatus(io.getlime.security.powerauth.app.server.database.model.enumeration.ActivationStatus.REMOVED);
+ // Recovery codes are revoked in case revocation is requested, or always when the activation is in CREATED or PENDING_COMMIT state
+ serviceBehaviorCatalogue.getActivationHistoryServiceBehavior().saveActivationAndLogChange(activation, null);
+ serviceBehaviorCatalogue.getCallbackUrlBehavior().notifyCallbackListenersOnActivationChange(activation);
+ }
+
+ /**
+ * Handle case when public key is invalid. Remove provided activation (mark as REMOVED),
+ * notify callback listeners, and throw an exception.
+ *
+ * @param activation Activation to be removed.
+ * @throws GenericServiceException Error caused by invalid public key.
+ */
+ private void handleInvalidPublicKey(ActivationRecordEntity activation) throws GenericServiceException {
+ activation.setActivationStatus(io.getlime.security.powerauth.app.server.database.model.enumeration.ActivationStatus.REMOVED);
+ serviceBehaviorCatalogue.getActivationHistoryServiceBehavior().saveActivationAndLogChange(activation);
+ serviceBehaviorCatalogue.getCallbackUrlBehavior().notifyCallbackListenersOnActivationChange(activation);
+ logger.warn("Invalid public key, activation ID: {}", activation.getActivationId());
+ // Exception must not be rollbacking, otherwise data written to database in this method would be lost
+ throw localizationProvider.buildExceptionForCode(ServiceError.ACTIVATION_NOT_FOUND);
+ }
+}
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthChallengeProvider.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthChallengeProvider.java
new file mode 100644
index 000000000..dabb05842
--- /dev/null
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthChallengeProvider.java
@@ -0,0 +1,162 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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.server.service.fido2;
+
+import com.wultra.powerauth.fido2.errorhandling.Fido2AuthenticationFailedException;
+import com.wultra.powerauth.fido2.rest.model.entity.AssertionChallenge;
+import com.wultra.powerauth.fido2.rest.model.entity.RegistrationChallenge;
+import com.wultra.powerauth.fido2.service.ChallengeProvider;
+import com.wultra.security.powerauth.client.model.enumeration.ActivationOtpValidation;
+import com.wultra.security.powerauth.client.model.request.OperationCreateRequest;
+import com.wultra.security.powerauth.client.model.response.InitActivationResponse;
+import com.wultra.security.powerauth.client.model.response.OperationDetailResponse;
+import io.getlime.security.powerauth.app.server.database.RepositoryCatalogue;
+import io.getlime.security.powerauth.app.server.database.model.entity.ActivationRecordEntity;
+import io.getlime.security.powerauth.app.server.database.model.entity.ApplicationEntity;
+import io.getlime.security.powerauth.app.server.database.model.enumeration.ActivationStatus;
+import com.wultra.security.powerauth.client.model.enumeration.Protocols;
+import io.getlime.security.powerauth.app.server.service.behavior.ServiceBehaviorCatalogue;
+import io.getlime.security.powerauth.app.server.service.exceptions.GenericServiceException;
+import io.getlime.security.powerauth.crypto.lib.util.KeyConvertor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * @author Petr Dvorak, petr@wultra.com
+ */
+@Service
+@Slf4j
+public class PowerAuthChallengeProvider implements ChallengeProvider {
+
+ private final RepositoryCatalogue repositoryCatalogue;
+ private final ServiceBehaviorCatalogue serviceBehaviorCatalogue;
+
+ private final KeyConvertor keyConvertor = new KeyConvertor();
+
+ @Autowired
+ public PowerAuthChallengeProvider(RepositoryCatalogue repositoryCatalogue, ServiceBehaviorCatalogue serviceBehaviorCatalogue) {
+ this.repositoryCatalogue = repositoryCatalogue;
+ this.serviceBehaviorCatalogue = serviceBehaviorCatalogue;
+ }
+
+ @Override
+ @Transactional
+ public RegistrationChallenge provideChallengeForRegistrationChallengeValue(String applicationId, String challengeValue) throws Fido2AuthenticationFailedException {
+
+ // Find application
+ final Optional application = repositoryCatalogue.getApplicationRepository().findById(applicationId);
+ if (application.isEmpty()) {
+ logger.warn("Application with given ID is not present: {}", applicationId);
+ throw new Fido2AuthenticationFailedException("Application with given ID is not present: " + applicationId);
+ }
+
+ final Date currentTimestamp = new Date();
+
+ // Obtain just the activation code part, just in case there was a value with signature
+ final String[] split = challengeValue.split("&", 1);
+ final String activationCode = split[0];
+
+ // Only allow created activations to be finished
+ final ActivationRecordEntity activationRecordEntity = repositoryCatalogue.getActivationRepository()
+ .findCreatedActivationWithoutLock(applicationId, activationCode, List.of(ActivationStatus.CREATED), currentTimestamp);
+
+ if (activationRecordEntity == null) {
+ throw new Fido2AuthenticationFailedException("Activation failed");
+ }
+
+ final String activationId = activationRecordEntity.getActivationId();
+ final String userId = activationRecordEntity.getUserId();
+
+ final RegistrationChallenge assertionChallenge = new RegistrationChallenge();
+ assertionChallenge.setChallenge(challengeValue);
+ assertionChallenge.setApplicationId(applicationId);
+ assertionChallenge.setActivationId(activationId);
+ assertionChallenge.setUserId(userId);
+ return assertionChallenge;
+ }
+
+ @Override
+ @Transactional
+ public RegistrationChallenge provideChallengeForRegistration(String userId, String applicationId) throws GenericServiceException {
+ final InitActivationResponse initActivationResponse = serviceBehaviorCatalogue.getActivationServiceBehavior()
+ .initActivation(Protocols.FIDO2, applicationId, userId, null, null, ActivationOtpValidation.NONE, null, null, keyConvertor);
+ final RegistrationChallenge registrationChallenge = new RegistrationChallenge();
+ registrationChallenge.setUserId(initActivationResponse.getUserId());
+ registrationChallenge.setApplicationId(initActivationResponse.getApplicationId());
+ registrationChallenge.setActivationId(initActivationResponse.getActivationId());
+ registrationChallenge.setChallenge(initActivationResponse.getActivationCode());
+ return registrationChallenge;
+ }
+
+ @Override
+ @Transactional
+ public AssertionChallenge provideChallengeForAuthentication(String userId, List applicationIds, String operationType, Map parameters, String externalAuthenticationId) throws GenericServiceException {
+ final OperationCreateRequest operationCreateRequest = new OperationCreateRequest();
+ operationCreateRequest.setApplications(applicationIds);
+ operationCreateRequest.setTemplateName(operationType);
+ operationCreateRequest.getParameters().putAll(parameters);
+
+ final OperationDetailResponse operationDetailResponse = serviceBehaviorCatalogue.getOperationBehavior().createOperation(operationCreateRequest);
+ final AssertionChallenge assertionChallenge = new AssertionChallenge();
+ assertionChallenge.setUserId(operationDetailResponse.getUserId());
+ assertionChallenge.setApplicationIds(operationDetailResponse.getApplications());
+ assertionChallenge.setChallenge(operationDetailResponse.getId() + "&" + operationDetailResponse.getData());
+ return assertionChallenge;
+ }
+
+ @Override
+ @Transactional
+ public void revokeChallengeForRegistrationChallengeValue(String applicationId, String challengeValue) throws Fido2AuthenticationFailedException {
+ final Date currentTimestamp = new Date();
+
+ // Find application
+ final Optional application = repositoryCatalogue.getApplicationRepository().findById(applicationId);
+ if (application.isEmpty()) {
+ logger.warn("Application with given ID is not present: {}", applicationId);
+ throw new Fido2AuthenticationFailedException("Application with given ID is not present: " + applicationId);
+ }
+
+ // Obtain just the activation code part, just in case there was a value with signature
+ final String[] split = challengeValue.split("&", 1);
+ final String activationCode = split[0];
+
+ final List statuses = List.of(ActivationStatus.CREATED, ActivationStatus.PENDING_COMMIT, ActivationStatus.ACTIVE, ActivationStatus.BLOCKED);
+ final ActivationRecordEntity activationRecordEntity = repositoryCatalogue.getActivationRepository()
+ .findCreatedActivationWithoutLock(applicationId, activationCode, statuses, currentTimestamp);
+
+ if (activationRecordEntity == null) {
+ throw new Fido2AuthenticationFailedException("With given value not found.");
+ }
+
+ final String activationId = activationRecordEntity.getActivationId();
+ try {
+ serviceBehaviorCatalogue.getActivationServiceBehavior().removeActivation(activationId, null, true);
+ } catch (GenericServiceException e) {
+ throw new Fido2AuthenticationFailedException("Activation could not have been removed.", e);
+ }
+
+ }
+}
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthCryptographyService.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthCryptographyService.java
new file mode 100644
index 000000000..a1c21e340
--- /dev/null
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthCryptographyService.java
@@ -0,0 +1,78 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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.server.service.fido2;
+
+import com.wultra.powerauth.fido2.rest.model.entity.*;
+import com.wultra.powerauth.fido2.service.CryptographyService;
+import io.getlime.security.powerauth.crypto.lib.model.exception.CryptoProviderException;
+import io.getlime.security.powerauth.crypto.lib.model.exception.GenericCryptoException;
+import io.getlime.security.powerauth.crypto.lib.util.Hash;
+import io.getlime.security.powerauth.crypto.lib.util.KeyConvertor;
+import io.getlime.security.powerauth.crypto.lib.util.SignatureUtils;
+import org.springframework.stereotype.Service;
+
+import java.security.InvalidKeyException;
+import java.security.PublicKey;
+import java.security.spec.InvalidKeySpecException;
+
+/**
+ * Service providing FIDO2 cryptographic functionality.
+ *
+ * @author Petr Dvorak, petr@wultra.com
+ */
+@Service
+public class PowerAuthCryptographyService implements CryptographyService {
+
+ private final KeyConvertor keyConvertor = new KeyConvertor();
+
+ public boolean verifySignatureForAssertion(String applicationId, String authenticatorId, CollectedClientData clientDataJSON, AuthenticatorData authData, byte[] signature, AuthenticatorDetail authenticatorDetail) throws GenericCryptoException, InvalidKeySpecException, CryptoProviderException, InvalidKeyException {
+ final byte[] publicKeyBytes = authenticatorDetail.getPublicKeyBytes();
+ final PublicKey publicKey = keyConvertor.convertBytesToPublicKey(publicKeyBytes);
+ return verifySignature(clientDataJSON, authData, signature, publicKey);
+ }
+
+ public boolean verifySignatureForRegistration(String applicationId, CollectedClientData clientDataJSON, AuthenticatorData authData, byte[] signature, AttestedCredentialData attestedCredentialData) throws GenericCryptoException, InvalidKeySpecException, CryptoProviderException, InvalidKeyException {
+ final EllipticCurvePoint point = attestedCredentialData.getPublicKeyObject().getPoint();
+ final PublicKey publicKey = keyConvertor.convertPointBytesToPublicKey(point.getX(), point.getY());
+ return verifySignature(clientDataJSON, authData, signature, publicKey);
+ }
+
+ public byte[] publicKeyToBytes(AttestedCredentialData attestedCredentialData) throws GenericCryptoException, InvalidKeySpecException, CryptoProviderException {
+ final EllipticCurvePoint point = attestedCredentialData.getPublicKeyObject().getPoint();
+ final PublicKey publicKey = keyConvertor.convertPointBytesToPublicKey(point.getX(), point.getY());
+ return keyConvertor.convertPublicKeyToBytes(publicKey);
+ }
+
+ // private methods
+
+
+ private boolean verifySignature(CollectedClientData clientDataJSON, AuthenticatorData authData, byte[] signature, PublicKey publicKey) throws GenericCryptoException, CryptoProviderException, InvalidKeyException {
+ final byte[] clientDataJSONEncodedHash = concat(authData.getEncoded(), Hash.sha256(clientDataJSON.getEncoded()));
+ final SignatureUtils signatureUtils = new SignatureUtils();
+ return signatureUtils.validateECDSASignature(clientDataJSONEncodedHash, signature, publicKey);
+ }
+
+ private byte[] concat(byte[] a, byte[] b) {
+ final byte[] combined = new byte[a.length + b.length];
+ System.arraycopy(a, 0, combined, 0, a.length);
+ System.arraycopy(b, 0, combined, a.length, b.length);
+ return combined;
+ }
+
+}
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/request/ActivationLayer2Request.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/request/ActivationLayer2Request.java
index 5288aee36..8c831f941 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/request/ActivationLayer2Request.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/request/ActivationLayer2Request.java
@@ -28,6 +28,7 @@ public class ActivationLayer2Request {
private String devicePublicKey;
private String activationOtp;
private String activationName;
+ private String externalId;
private String extras;
private String platform;
private String deviceInfo;
@@ -38,18 +39,6 @@ public class ActivationLayer2Request {
public ActivationLayer2Request() {
}
- /**
- * Parameterized constructor.
- * @param devicePublicKey Device public key.
- * @param activationName Activation name.
- * @param extras Activation extras.
- */
- public ActivationLayer2Request(String devicePublicKey, String activationName, String extras) {
- this.devicePublicKey = devicePublicKey;
- this.activationName = activationName;
- this.extras = extras;
- }
-
/**
* Get Base64 encoded device public key.
* @return Device public key.
@@ -98,6 +87,23 @@ public void setActivationName(String activationName) {
this.activationName = activationName;
}
+ /**
+ * Get external ID.
+ *
+ * @return External ID.
+ */
+ public String getExternalId() {
+ return externalId;
+ }
+
+ /**
+ * Set external ID.
+ * @param externalId External ID.
+ */
+ public void setExternalId(String externalId) {
+ this.externalId = externalId;
+ }
+
/**
* Get activation extras.
* @return Activation extras.
diff --git a/powerauth-java-server/src/main/resources/xsd/PowerAuth-2.0.xsd b/powerauth-java-server/src/main/resources/xsd/PowerAuth-2.0.xsd
deleted file mode 100644
index 91d3409f9..000000000
--- a/powerauth-java-server/src/main/resources/xsd/PowerAuth-2.0.xsd
+++ /dev/null
@@ -1,270 +0,0 @@
-
-
-
-
-
-
-
-
-
- Request for client-server key exchange procedure.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Response for client-server key exchange procedure.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Request for creating a new activation directly.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Response for creating a new activation directly.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Request for the vault unlock procedure.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Response for the vault unlock procedure.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Request for obtaining a derived end-to-end encryption key for non-personalized
- encryption.
-
-
-
-
-
-
-
-
-
-
-
-
-
- Response for obtaining a derived end-to-end encryption key for non-personalized
- encryption.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Request for obtaining a derived personalized end-to-end encryption key.
-
-
-
-
-
-
-
-
-
-
-
- Response for obtaining a derived personalized end-to-end encryption key.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Request for obtaining a new token for simple authentication.
-
-
-
-
-
-
-
-
-
-
-
-
- Response for obtaining a new token for simple authentication.
-
-
-
-
-
-
-
-
-
-
-
-
-
- Enum representing the possible activation states (CREATED, PENDING_COMMIT, ACTIVE, BLOCKED,
- REMOVED).
-
-
-
-
-
-
-
-
-
-
-
-
-
- Enum representing the possible signatures types by the authentication factors used.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/powerauth-java-server/src/main/resources/xsd/PowerAuth-3.0.xsd b/powerauth-java-server/src/main/resources/xsd/PowerAuth-3.0.xsd
deleted file mode 100644
index 7a702d668..000000000
--- a/powerauth-java-server/src/main/resources/xsd/PowerAuth-3.0.xsd
+++ /dev/null
@@ -1,1910 +0,0 @@
-
-
-
-
-
-
-
-
-
- Request for getting the system status information.
-
-
-
-
-
-
-
-
- Response for getting the system status information.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Request for getting the list of all possible server error codes.
-
-
-
-
-
-
-
-
-
-
- Response for getting the list of all possible server error codes.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Request for getting the application list.
-
-
-
-
-
-
-
-
- Response for getting the application list.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Request for getting the application detail, including the list of versions.
-
-
-
-
-
-
-
-
-
-
-
- Response for getting the application detail, including the list of versions.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Request for getting the application detail based on the application version key.
-
-
-
-
-
-
-
-
-
-
-
- Response for getting the application detail based on the application version key.
-
-
-
-
-
-
-
-
-
-
-
-
-
- Request for creating a new activation.
-
-
-
-
-
-
-
-
-
-
- Response for creating a new activation.
-
-
-
-
-
-
-
-
-
-
-
-
-
- Request for creating a new activation version.
-
-
-
-
-
-
-
-
-
-
-
- Response for creating a new activation version.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Request for marking a given application version as unsupported.
-
-
-
-
-
-
-
-
-
-
-
- Response for marking a given application version as unsupported.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Request for marking a given application version as supported.
-
-
-
-
-
-
-
-
-
-
-
- Response for marking a given application version as supported.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Request for initiating the activation process by generating the activation code.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Response for initiating the activation process.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Request for client-server key exchange procedure.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Response for client-server key exchange procedure.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Request for creating a new activation directly.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Response for creating a new activation directly.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Request for update activation OTP before activation commit.
-
-
-
-
-
-
-
-
-
-
-
-
- Response for update activation OTP before activation commit.
-
-
-
-
-
-
-
-
-
-
-
-
-
- Request for committing the activation with given ID.
-
-
-
-
-
-
-
-
-
-
-
-
- Response for committing the activation with given ID.
-
-
-
-
-
-
-
-
-
-
-
-
-
- Request for getting the activation details / status information.
-
-
-
-
-
-
-
-
-
-
-
- Response for getting the activation details / status information.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Request for removing the activation with given ID.
-
-
-
-
-
-
-
-
-
-
-
-
- Response for removing the activation with given ID.
-
-
-
-
-
-
-
-
-
-
-
-
-
- Request for getting the list of activations for given user.
-
-
-
-
-
-
-
-
-
-
-
- Response for getting the list of activations for given user.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Request for querying activations using various parameters.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Response for querying activations using various parameters.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Request for updating status for activations.
-
-
-
-
-
-
-
-
-
-
-
- Response for updating status for activations.
-
-
-
-
-
-
-
-
-
-
-
-
- Request for the signature verification process.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Response for the signature verification process.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Request for generating personalized signature payload for the offline signature.
-
-
-
-
-
-
-
-
-
-
-
-
- Response for generating personalized signature payload for the offline signature.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Request for generating non-personalized signature payload for the offline signature.
-
-
-
-
-
-
-
-
-
-
-
-
- Response for generating non-personalized signature payload for the offline signature.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Request for the offline signature verification process.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Response for the signature verification process.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Request for blocking the activation with given ID.
-
-
-
-
-
-
-
-
-
-
-
-
- Response for blocking the activation with given ID.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Request for unblocking the activation with given ID.
-
-
-
-
-
-
-
-
-
-
-
- Response for unblocking the activation with given ID.
-
-
-
-
-
-
-
-
-
-
-
-
-
- Request for the vault unlock procedure.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Response for the vault unlock procedure.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Request for the asymmetric signature (ECDSA) validation procedure.
-
-
-
-
-
-
-
-
-
-
-
-
- Response for the asymmetric signature (ECDSA) validation procedure.
-
-
-
-
-
-
-
-
-
-
-
-
- Request for getting the signature audit log.
-
-
-
-
-
-
-
-
-
-
-
-
-
- Response for getting the signature audit log.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Request for getting the activation status change log.
-
-
-
-
-
-
-
-
-
-
-
-
- Response for getting the activation status change log.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Request for creating a new integration record.
-
-
-
-
-
-
-
-
-
-
- Response for creating a new integration record.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Request for getting the list of integrations.
-
-
-
-
-
-
-
-
- Response for getting the list of integrations.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Request for removing an integration record.
-
-
-
-
-
-
-
-
-
-
- Response for removing an integration record.
-
-
-
-
-
-
-
-
-
-
-
-
-
- Request for creating a new callback URL record associated with given application.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Response for creating a new callback URL.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Request for updating a callback URL record associated with given application.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Response for updating a callback URL.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Request for getting the list of callback URLs for given application.
-
-
-
-
-
-
-
-
-
-
- Response for getting the list of callback URLs.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Request for removing a callback URL record.
-
-
-
-
-
-
-
-
-
-
- Response for removing an callback URL record.
-
-
-
-
-
-
-
-
-
-
-
-
-
- Request for obtaining a new token for simple authentication.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Response for obtaining a new token for simple authentication.
-
-
-
-
-
-
-
-
-
-
-
- Request for validating the request authenticated with a token..
-
-
-
-
-
-
-
-
-
-
-
-
-
- Response for validating the request authenticated with a token.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Request for removing the token with given ID.
-
-
-
-
-
-
-
-
-
-
-
- Response for removing the token with given ID.
-
-
-
-
-
-
-
-
-
-
-
-
- Request for obtaining ECIES decryptor.
-
-
-
-
-
-
-
-
-
-
-
-
-
- Response containing setup for ECIES decryptor.
-
-
-
-
-
-
-
-
-
-
-
-
-
- Request to start upgrade.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Response to start upgrade.
-
-
-
-
-
-
-
-
-
-
-
- Upgrade commit request
-
-
-
-
-
-
-
-
-
-
-
- Upgrade commit response.
-
-
-
-
-
-
-
-
-
-
-
-
- Create recovery code request.
-
-
-
-
-
-
-
-
-
-
-
-
- Create recovery code response.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Confirm recovery code request.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Confirm recovery code response.
-
-
-
-
-
-
-
-
-
-
-
-
-
- Search for recovery codes request.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Search for recovery codes response.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Revoke recovery codes request.
-
-
-
-
-
-
-
-
-
-
- Revoke recovery code response.
-
-
-
-
-
-
-
-
-
-
- Request for creating a new activation using recovery code.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Response for creating a new activation using recovery code.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Get application recovery configuration request.
-
-
-
-
-
-
-
-
-
-
- Get application recovery configuration response.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Update application recovery configuration request.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Update application recovery configuration response.
-
-
-
-
-
-
-
-
-
-
- List activation flags request.
-
-
-
-
-
-
-
-
-
-
- List activation flags response.
-
-
-
-
-
-
-
-
-
-
-
- Add activation flags request.
-
-
-
-
-
-
-
-
-
-
-
- Add activation flags response.
-
-
-
-
-
-
-
-
-
-
-
- Update activation flags request.
-
-
-
-
-
-
-
-
-
-
-
- Update activation flags response.
-
-
-
-
-
-
-
-
-
-
-
- Remove activation flags request.
-
-
-
-
-
-
-
-
-
-
-
- Remove activation flags response.
-
-
-
-
-
-
-
-
-
-
-
- List application roles request.
-
-
-
-
-
-
-
-
-
-
- List application roles response.
-
-
-
-
-
-
-
-
-
-
-
- Add application roles request.
-
-
-
-
-
-
-
-
-
-
-
- Add application roles response.
-
-
-
-
-
-
-
-
-
-
-
- Update application roles request.
-
-
-
-
-
-
-
-
-
-
-
- Update application roles response.
-
-
-
-
-
-
-
-
-
-
-
- Remove application roles request.
-
-
-
-
-
-
-
-
-
-
-
- Remove application roles response.
-
-
-
-
-
-
-
-
-
-
-
-
-
- Enum representing the possible activation states (CREATED, PENDING_COMMIT, ACTIVE, BLOCKED,
- REMOVED).
-
-
-
-
-
-
-
-
-
-
-
-
-
- Enum representing the possible modes of OTP validation (NONE, ON_KEY_EXCHANGE, ON_COMMIT).
-
-
-
-
-
-
-
-
-
-
- Enum representing the possible recovery code states (CREATED, ACTIVE, BLOCKED, REVOKED).
-
-
-
-
-
-
-
-
-
-
-
- Enum representing the possible recovery PUK states (VALID, USED, INVALID).
-
-
-
-
-
-
-
-
-
-
- Enum representing the possible signatures types by the authentication factors used.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
From 189bed9d13980d616aa05b4bdb9824bbf116adff Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Petr=20Dvo=C5=99=C3=A1k?=
Date: Wed, 12 Jul 2023 13:09:43 +0200
Subject: [PATCH 002/146] Map assertion challenge to operations
---
...-id.xml => 20230430-add-columns-fido2.xml} | 10 +-
.../1.5.x/db.changelog-version.xml | 2 +-
pom.xml | 2 +-
powerauth-client-model/pom.xml | 4 +
.../client/model/enumeration/Protocols.java | 6 +
.../GetActivationListForUserRequest.java | 6 +-
powerauth-fido2/pom.xml | 4 +-
.../controller/RegistrationController.java | 3 -
.../AssertionChallengeConverter.java | 2 +
.../converter/RegistrationConverter.java | 26 +-
.../rest/model/entity/AssertionChallenge.java | 2 +
.../rest/model/entity/AttestationObject.java | 2 +
.../rest/model/entity/AuthenticatorData.java | 2 +
.../model/entity/AuthenticatorParameters.java | 48 ++++
.../model/entity/CollectedClientData.java | 2 +
.../model/request/RegistrationRequest.java | 14 +-
.../response/AssertionChallengeResponse.java | 2 +
.../RegistrationRequestValidator.java | 32 ++-
.../fido2/service/AssertionService.java | 12 +-
.../AssertionVerificationProvider.java | 79 ++++++
.../fido2/service/AuthenticatorProvider.java | 2 -
.../fido2/service/ChallengeProvider.java | 28 --
.../fido2/service/RegistrationService.java | 2 +-
.../app/server/service/PowerAuthService.java | 3 +-
.../tasks/ActivationServiceBehavior.java | 10 +-
.../fido2/PowerAuthAssertionProvider.java | 252 ++++++++++++++++++
.../fido2/PowerAuthAuthenticatorProvider.java | 6 +-
.../fido2/PowerAuthChallengeProvider.java | 24 +-
28 files changed, 489 insertions(+), 98 deletions(-)
rename docs/db/changelog/changesets/powerauth-java-server/1.5.x/{20230430-add-columns-external-id.xml => 20230430-add-columns-fido2.xml} (82%)
create mode 100644 powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AuthenticatorParameters.java
create mode 100644 powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/AssertionVerificationProvider.java
create mode 100644 powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAssertionProvider.java
diff --git a/docs/db/changelog/changesets/powerauth-java-server/1.5.x/20230430-add-columns-external-id.xml b/docs/db/changelog/changesets/powerauth-java-server/1.5.x/20230430-add-columns-fido2.xml
similarity index 82%
rename from docs/db/changelog/changesets/powerauth-java-server/1.5.x/20230430-add-columns-external-id.xml
rename to docs/db/changelog/changesets/powerauth-java-server/1.5.x/20230430-add-columns-fido2.xml
index ba56c0200..85e5b3a14 100644
--- a/docs/db/changelog/changesets/powerauth-java-server/1.5.x/20230430-add-columns-external-id.xml
+++ b/docs/db/changelog/changesets/powerauth-java-server/1.5.x/20230430-add-columns-fido2.xml
@@ -21,7 +21,7 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.9.xsd">
-
+
@@ -33,7 +33,7 @@
-
+
@@ -45,8 +45,12 @@
-
+
+
+
+
+
diff --git a/docs/db/changelog/changesets/powerauth-java-server/1.5.x/db.changelog-version.xml b/docs/db/changelog/changesets/powerauth-java-server/1.5.x/db.changelog-version.xml
index 637e48247..690633650 100644
--- a/docs/db/changelog/changesets/powerauth-java-server/1.5.x/db.changelog-version.xml
+++ b/docs/db/changelog/changesets/powerauth-java-server/1.5.x/db.changelog-version.xml
@@ -4,6 +4,6 @@
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.9.xsd">
-
+
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 7c5a7a3eb..04d0a6862 100644
--- a/pom.xml
+++ b/pom.xml
@@ -88,7 +88,7 @@
1.5.0-SNAPSHOT
1.7.0-SNAPSHOT
- 1.73
+ 1.75
3.0.12
diff --git a/powerauth-client-model/pom.xml b/powerauth-client-model/pom.xml
index 22592cf39..8343b07d2 100644
--- a/powerauth-client-model/pom.xml
+++ b/powerauth-client-model/pom.xml
@@ -40,6 +40,10 @@
org.springframework
spring-core
+
+ com.fasterxml.jackson.core
+ jackson-annotations
+
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/enumeration/Protocols.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/enumeration/Protocols.java
index 4a1f73612..9e43497e2 100644
--- a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/enumeration/Protocols.java
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/enumeration/Protocols.java
@@ -18,11 +18,17 @@
package com.wultra.security.powerauth.client.model.enumeration;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
/**
+ * Enumeration representing supported protocols for authenticators.
+ *
* @author Petr Dvorak, petr@wultra.com
*/
public enum Protocols {
+ @JsonProperty("powerauth")
POWERAUTH("powerauth"),
+ @JsonProperty("fido2")
FIDO2("fido2");
private final String protocol;
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/GetActivationListForUserRequest.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/GetActivationListForUserRequest.java
index 07ad2f4dc..42949a003 100644
--- a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/GetActivationListForUserRequest.java
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/GetActivationListForUserRequest.java
@@ -18,10 +18,13 @@
package com.wultra.security.powerauth.client.model.request;
+import com.wultra.security.powerauth.client.model.enumeration.Protocols;
import lombok.Data;
+import java.util.Set;
+
/**
- * Model class representing request for activation lisf for a given user.
+ * Model class representing request for activation list for a given user.
*
* @author Petr Dvorak, petr@wultra.com
*/
@@ -30,5 +33,6 @@ public class GetActivationListForUserRequest {
private String userId;
private String applicationId;
+ private Set protocols = Set.of(Protocols.FIDO2, Protocols.POWERAUTH);
}
diff --git a/powerauth-fido2/pom.xml b/powerauth-fido2/pom.xml
index 793a80870..0b78d3307 100644
--- a/powerauth-fido2/pom.xml
+++ b/powerauth-fido2/pom.xml
@@ -48,7 +48,7 @@
org.springdoc
springdoc-openapi-starter-webmvc-ui
- 2.0.4
+ 2.1.0
org.springframework.boot
@@ -59,7 +59,7 @@
com.fasterxml.jackson.dataformat
jackson-dataformat-cbor
- 2.13.4
+ 2.14.2
org.postgresql
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/RegistrationController.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/RegistrationController.java
index 9d0cf6041..01def9fb8 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/RegistrationController.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/RegistrationController.java
@@ -18,15 +18,12 @@
package com.wultra.powerauth.fido2.rest.controller;
-import com.wultra.powerauth.fido2.rest.model.entity.AaguidList;
-import com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorDetail;
import com.wultra.powerauth.fido2.rest.model.request.RegisteredAuthenticatorsRequest;
import com.wultra.powerauth.fido2.rest.model.request.RegistrationChallengeRequest;
import com.wultra.powerauth.fido2.rest.model.request.RegistrationRequest;
import com.wultra.powerauth.fido2.rest.model.response.RegisteredAuthenticatorsResponse;
import com.wultra.powerauth.fido2.rest.model.response.RegistrationChallengeResponse;
import com.wultra.powerauth.fido2.rest.model.response.RegistrationResponse;
-import com.wultra.powerauth.fido2.service.AuthenticatorProvider;
import com.wultra.powerauth.fido2.service.RegistrationService;
import io.getlime.core.rest.model.base.request.ObjectRequest;
import io.getlime.core.rest.model.base.response.ObjectResponse;
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/AssertionChallengeConverter.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/AssertionChallengeConverter.java
index 5c1451483..112a083cf 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/AssertionChallengeConverter.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/AssertionChallengeConverter.java
@@ -46,6 +46,8 @@ public AssertionChallengeResponse fromChallenge(AssertionChallenge source) {
destination.setUserId(source.getUserId());
destination.setApplicationIds(source.getApplicationIds());
destination.setChallenge(source.getChallenge());
+ destination.setFailedAttempts(source.getFailedAttempts());
+ destination.setMaxFailedAttempts(source.getMaxFailedAttempts());
return destination;
}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/RegistrationConverter.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/RegistrationConverter.java
index 002078848..13a504632 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/RegistrationConverter.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/RegistrationConverter.java
@@ -22,6 +22,7 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wultra.powerauth.fido2.rest.model.entity.AaguidList;
import com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorDetail;
+import com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorParameters;
import com.wultra.powerauth.fido2.rest.model.entity.RegistrationChallenge;
import com.wultra.powerauth.fido2.rest.model.request.RegistrationRequest;
import com.wultra.powerauth.fido2.rest.model.response.RegistrationResponse;
@@ -30,6 +31,8 @@
import org.springframework.stereotype.Component;
import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
/**
* Converter class for registration related objects.
@@ -41,6 +44,7 @@
public class RegistrationConverter {
private final AaguidList aaguidRegistry = new AaguidList();
+ private final ObjectMapper objectMapper = new ObjectMapper();
public AuthenticatorDetail convert(RegistrationChallenge challenge, RegistrationRequest requestObject, byte[] aaguid, byte[] publicKey) {
try {
@@ -49,10 +53,10 @@ public AuthenticatorDetail convert(RegistrationChallenge challenge, Registration
authenticatorDetail.setActivationId(challenge.getActivationId());
authenticatorDetail.setApplicationId(challenge.getApplicationId());
- authenticatorDetail.setExternalId(requestObject.getId());
- authenticatorDetail.setExtras(new ObjectMapper().writeValueAsString(requestObject.getResponse().getTransports()));
+ authenticatorDetail.setExternalId(requestObject.getAuthenticatorParameters().getId());
+ authenticatorDetail.setExtras(convertExtras(requestObject));
authenticatorDetail.setActivationName(requestObject.getActivationName());
- authenticatorDetail.setPlatform(requestObject.getAuthenticatorAttachment());
+ authenticatorDetail.setPlatform(requestObject.getAuthenticatorParameters().getAuthenticatorAttachment());
authenticatorDetail.setDeviceInfo(aaguidRegistry.vendorName(aaguid));
authenticatorDetail.setActivationStatus(ActivationStatus.ACTIVE);
authenticatorDetail.setActivationFlags(new ArrayList<>());
@@ -66,6 +70,22 @@ public AuthenticatorDetail convert(RegistrationChallenge challenge, Registration
}
}
+ private String convertExtras(RegistrationRequest requestObject) throws JsonProcessingException {
+ final AuthenticatorParameters authenticatorParameters = requestObject.getAuthenticatorParameters();
+ final Map params = new HashMap<>();
+ params.put("relyingPartyId", authenticatorParameters.getRelyingPartyId());
+ params.put("allowedOrigins", authenticatorParameters.getAllowedOrigins());
+ params.put("allowedTopOrigins", authenticatorParameters.getAllowedTopOrigins());
+ params.put("transports", authenticatorParameters.getResponse().getTransports());
+ params.put("authenticatorAttachment", authenticatorParameters.getAuthenticatorAttachment());
+ params.put("attestationStatement", authenticatorParameters.getResponse().getAttestationObject());
+ params.put("origin", authenticatorParameters.getResponse().getClientDataJSON().getOrigin());
+ params.put("topOrigin", authenticatorParameters.getResponse().getClientDataJSON().getTopOrigin());
+ params.put("isCrossOrigin", authenticatorParameters.getResponse().getClientDataJSON().isCrossOrigin());
+ params.put("aaguid", authenticatorParameters.getResponse().getAttestationObject().getAuthData().getAttestedCredentialData().getAaguid());
+ return objectMapper.writeValueAsString(params);
+ }
+
public RegistrationResponse convertRegistrationResponse(AuthenticatorDetail source) {
final RegistrationResponse result = new RegistrationResponse();
result.setUserId(source.getUserId());
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AssertionChallenge.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AssertionChallenge.java
index 6bcc5286f..7d54eed23 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AssertionChallenge.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AssertionChallenge.java
@@ -31,5 +31,7 @@ public class AssertionChallenge {
private List applicationIds;
private String challenge;
private String userId;
+ private Long failedAttempts;
+ private Long maxFailedAttempts;
}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AttestationObject.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AttestationObject.java
index 203d32cd2..dafa7c33c 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AttestationObject.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AttestationObject.java
@@ -18,6 +18,7 @@
package com.wultra.powerauth.fido2.rest.model.entity;
+import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.wultra.powerauth.fido2.rest.model.converter.serialization.AttestationStatementDeserializer;
import com.wultra.powerauth.fido2.rest.model.converter.serialization.AuthenticatorDataDeserializer;
@@ -29,6 +30,7 @@
@Data
public class AttestationObject {
+ @JsonIgnore
private String encoded;
private String fmt;
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AuthenticatorData.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AuthenticatorData.java
index dc6b1c587..7d976f852 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AuthenticatorData.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AuthenticatorData.java
@@ -18,6 +18,7 @@
package com.wultra.powerauth.fido2.rest.model.entity;
+import com.fasterxml.jackson.annotation.JsonIgnore;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.PositiveOrZero;
import lombok.Data;
@@ -29,6 +30,7 @@
public class AuthenticatorData {
@NotEmpty
+ @JsonIgnore
private byte[] encoded;
@NotEmpty
private byte[] rpIdHash;
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AuthenticatorParameters.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AuthenticatorParameters.java
new file mode 100644
index 000000000..debe022fc
--- /dev/null
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AuthenticatorParameters.java
@@ -0,0 +1,48 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.powerauth.fido2.rest.model.entity;
+
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Data class representing the parameters obtained from the authenticator registration.
+ *
+ * @author Petr Dvorak, petr@wultra.com
+ */
+@Data
+public class AuthenticatorParameters {
+
+ @NotBlank
+ private String id;
+ @NotBlank
+ private String type;
+ @NotBlank
+ private String authenticatorAttachment;
+ private AuthenticatorAttestationResponse response = new AuthenticatorAttestationResponse();
+ @NotBlank
+ private String relyingPartyId;
+ private List allowedOrigins = new ArrayList<>();
+ private List allowedTopOrigins = new ArrayList<>();
+ private boolean requiresUserVerification;
+
+}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/CollectedClientData.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/CollectedClientData.java
index 5f021963e..5985b996b 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/CollectedClientData.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/CollectedClientData.java
@@ -18,6 +18,7 @@
package com.wultra.powerauth.fido2.rest.model.entity;
+import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.wultra.powerauth.fido2.rest.model.converter.serialization.Base64ToStringDeserializer;
import jakarta.validation.constraints.NotBlank;
@@ -30,6 +31,7 @@
@Data
public class CollectedClientData {
@NotEmpty
+ @JsonIgnore
private String encoded;
@NotBlank
private String type;
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/RegistrationRequest.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/RegistrationRequest.java
index c29fbd17d..cd63a4038 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/RegistrationRequest.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/RegistrationRequest.java
@@ -19,6 +19,7 @@
package com.wultra.powerauth.fido2.rest.model.request;
import com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorAttestationResponse;
+import com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorParameters;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
@@ -39,18 +40,7 @@ public class RegistrationRequest {
private String expectedChallenge;
// Authenticator parameters
- @NotBlank
- private String id;
- @NotBlank
- private String type;
- @NotBlank
- private String authenticatorAttachment;
- private AuthenticatorAttestationResponse response = new AuthenticatorAttestationResponse();
- @NotBlank
- private String relyingPartyId;
- private List allowedOrigins = new ArrayList<>();
- private List allowedTopOrigins = new ArrayList<>();
- private boolean requiresUserVerification;
+ private AuthenticatorParameters authenticatorParameters;
}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/response/AssertionChallengeResponse.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/response/AssertionChallengeResponse.java
index 275e441cb..e9c08f3dd 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/response/AssertionChallengeResponse.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/response/AssertionChallengeResponse.java
@@ -30,4 +30,6 @@ public class AssertionChallengeResponse {
private List applicationIds;
private String challenge;
private String userId;
+ private Long failedAttempts;
+ private Long maxFailedAttempts;
}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/validator/RegistrationRequestValidator.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/validator/RegistrationRequestValidator.java
index 4f8d902e1..ebc3b1bb9 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/validator/RegistrationRequestValidator.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/validator/RegistrationRequestValidator.java
@@ -40,13 +40,25 @@ public class RegistrationRequestValidator {
public String validate(RegistrationRequest request) {
- if (request == null || request.getResponse() == null
- || request.getResponse().getClientDataJSON() == null
- || request.getResponse().getAttestationObject() == null) {
- return "Invalid request, you need to include response.clientDataJSON and response.attestationObject.";
+ if (request == null) {
+ return "Null request provided.";
}
- final CollectedClientData clientDataJSON = request.getResponse().getClientDataJSON();
+ final AuthenticatorParameters authenticatorParameters = request.getAuthenticatorParameters();
+
+ if (authenticatorParameters == null) {
+ return "Null authenticator parameters provided.";
+ }
+
+ final AuthenticatorAttestationResponse response = authenticatorParameters.getResponse();
+
+ if (response == null
+ || response.getClientDataJSON() == null
+ || response.getAttestationObject() == null) {
+ return "Invalid request authenticator parameters, you need to include response.clientDataJSON and response.attestationObject.";
+ }
+
+ final CollectedClientData clientDataJSON = response.getClientDataJSON();
if (!"webauthn.create".equals(clientDataJSON.getType())) {
return "Request does not contain webauthn.create type.";
@@ -58,24 +70,24 @@ public String validate(RegistrationRequest request) {
}
final String origin = clientDataJSON.getOrigin();
- final List allowedOrigins = request.getAllowedOrigins();
+ final List allowedOrigins = authenticatorParameters.getAllowedOrigins();
if (origin == null || !allowedOrigins.contains(origin)) {
return "Request does not contain the correct origin.";
}
- final List allowedTopOrigins = request.getAllowedTopOrigins();
+ final List allowedTopOrigins = authenticatorParameters.getAllowedTopOrigins();
if (clientDataJSON.getTopOrigin() != null && !allowedTopOrigins.contains(clientDataJSON.getTopOrigin())) {
return "Request contains the top origin which is not allowed.";
}
- final AttestationObject attestationObject = request.getResponse().getAttestationObject();
+ final AttestationObject attestationObject = response.getAttestationObject();
final AuthenticatorData authData = attestationObject.getAuthData();
if (authData == null) {
return "Missing authentication data.";
}
final byte[] rpIdHash = authData.getRpIdHash();
- final String relyingPartyId = request.getRelyingPartyId();
+ final String relyingPartyId = authenticatorParameters.getRelyingPartyId();
final byte[] expectedRpIdHash = Hash.sha256(relyingPartyId);
if (!Arrays.equals(rpIdHash, expectedRpIdHash)) {
return "The origin does not match relying party ID.";
@@ -87,7 +99,7 @@ public String validate(RegistrationRequest request) {
return "User is not present during the authentication.";
}
- final boolean requiresUserVerification = request.isRequiresUserVerification();
+ final boolean requiresUserVerification = authenticatorParameters.isRequiresUserVerification();
if (requiresUserVerification && !flags.isUserVerified()) {
return "User is not present during the authentication, but user verification is required.";
}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/AssertionService.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/AssertionService.java
index 90524cf49..0ad65cdff 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/AssertionService.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/AssertionService.java
@@ -45,14 +45,16 @@ public class AssertionService {
private final CryptographyService cryptographyService;
private final ChallengeProvider challengeProvider;
private final AuthenticatorProvider authenticatorProvider;
+ private final AssertionVerificationProvider assertionVerificationProvider;
private final AssertionConverter assertionConverter;
private final AssertionChallengeConverter assertionChallengeConverter;
@Autowired
- public AssertionService(CryptographyService cryptographyService, ChallengeProvider challengeProvider, AuthenticatorProvider authenticatorProvider, AssertionConverter assertionConverter, AssertionChallengeConverter assertionChallengeConverter) {
+ public AssertionService(CryptographyService cryptographyService, ChallengeProvider challengeProvider, AuthenticatorProvider authenticatorProvider, AssertionVerificationProvider assertionVerificationProvider, AssertionConverter assertionConverter, AssertionChallengeConverter assertionChallengeConverter) {
this.cryptographyService = cryptographyService;
this.challengeProvider = challengeProvider;
this.authenticatorProvider = authenticatorProvider;
+ this.assertionVerificationProvider = assertionVerificationProvider;
this.assertionConverter = assertionConverter;
this.assertionChallengeConverter = assertionChallengeConverter;
}
@@ -64,8 +66,8 @@ public AssertionService(CryptographyService cryptographyService, ChallengeProvid
* @return Assertion challenge information.
*/
public AssertionChallengeResponse requestAssertionChallenge(AssertionChallengeRequest request) throws Exception {
- final AssertionChallenge assertionChallenge = challengeProvider.provideChallengeForAuthentication(
- null, request.getApplicationIds(), request.getOperationType(), request.getParameters(), request.getExternalId()
+ final AssertionChallenge assertionChallenge = assertionVerificationProvider.provideChallengeForAssertion(
+ request.getApplicationIds(), request.getOperationType(), request.getParameters(), request.getExternalId()
);
if (assertionChallenge == null) {
throw new Fido2AuthenticationFailedException("Unable to obtain challenge with provided parameters.");
@@ -84,15 +86,19 @@ public AssertionVerificationResponse authenticate(AssertionRequest request) thro
final AuthenticatorAssertionResponse response = request.getResponse();
final String applicationId = request.getApplicationId();
final String authenticatorId = request.getId();
+ final String challenge = request.getResponse().getClientDataJSON().getChallenge();
final AuthenticatorDetail authenticatorDetail = authenticatorProvider.findByCredentialId(applicationId, authenticatorId);
if (authenticatorDetail.getActivationStatus() == ActivationStatus.ACTIVE) {
final boolean signatureCorrect = cryptographyService.verifySignatureForAssertion(applicationId, authenticatorId, response.getClientDataJSON(), response.getAuthenticatorData(), response.getSignature(), authenticatorDetail);
if (signatureCorrect) {
+ assertionVerificationProvider.approveAssertion(challenge, authenticatorDetail);
return assertionConverter.fromAuthenticatorDetail(authenticatorDetail, signatureCorrect);
} else {
+ assertionVerificationProvider.failAssertion(challenge, authenticatorDetail);
throw new Fido2AuthenticationFailedException("Authentication failed due to incorrect signature.");
}
} else {
+ assertionVerificationProvider.failAssertion(challenge, authenticatorDetail);
throw new Fido2AuthenticationFailedException("Authentication failed due to incorrect authenticator state.");
}
} catch (Exception e) {
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/AssertionVerificationProvider.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/AssertionVerificationProvider.java
new file mode 100644
index 000000000..385d7a7d1
--- /dev/null
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/AssertionVerificationProvider.java
@@ -0,0 +1,79 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.powerauth.fido2.service;
+
+import com.wultra.powerauth.fido2.errorhandling.Fido2AuthenticationFailedException;
+import com.wultra.powerauth.fido2.rest.model.entity.AssertionChallenge;
+import com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorDetail;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Interface with methods responsible for assertion verification.
+ *
+ * @author Petr Dvorak, petr@wultra.com
+ */
+public interface AssertionVerificationProvider {
+
+ /**
+ * Obtain challenge for authentication.
+ *
+ * @param applicationIds List of application ID.
+ * @param operationType Type of the operation this challenge is for.
+ * @param parameters Operation parameters.
+ * @return Assertion challenge.
+ * @throws Exception In case any issue occur during processing.
+ */
+ default AssertionChallenge provideChallengeForAssertion(List applicationIds, String operationType, Map parameters) throws Exception {
+ return provideChallengeForAssertion(applicationIds, operationType, parameters, null);
+ };
+
+ /**
+ * Obtain challenge for authentication.
+ *
+ * @param applicationIds List of application ID.
+ * @param operationType Type of the operation this challenge is for.
+ * @param parameters Operation parameters.
+ * @param externalAuthenticationId External ID of operation, i.e., transaction in transaction system.
+ * @return Assertion challenge.
+ * @throws Exception In case any issue occur during processing.
+ */
+ AssertionChallenge provideChallengeForAssertion(List applicationIds, String operationType, Map parameters, String externalAuthenticationId) throws Exception;
+
+ /**
+ * Approve assertion.
+ *
+ * @param challengeValue Challenge value.
+ * @param authenticatorDetail Authenticator information.
+ * @return Assertion challenge.
+ * @throws Fido2AuthenticationFailedException In case assertion approval fails.
+ */
+ AssertionChallenge approveAssertion(String challengeValue, AuthenticatorDetail authenticatorDetail) throws Fido2AuthenticationFailedException;
+
+ /**
+ * Fail assertion approval.
+ *
+ * @param challenge Challenge for assertion.
+ * @param authenticatorDetail Authenticator detail.
+ * @return Info about the assertion.
+ * @throws Fido2AuthenticationFailedException In case assertion approval fails.
+ */
+ AssertionChallenge failAssertion(String challenge, AuthenticatorDetail authenticatorDetail) throws Fido2AuthenticationFailedException;
+}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/AuthenticatorProvider.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/AuthenticatorProvider.java
index 9ae64d0fb..1b7cfcb20 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/AuthenticatorProvider.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/AuthenticatorProvider.java
@@ -29,9 +29,7 @@
* @author Petr Dvorak, petr@wultra.com
*/
public interface AuthenticatorProvider {
-
AuthenticatorDetail storeAuthenticator(String applicationId, String challenge, AuthenticatorDetail authenticatorDetail) throws Fido2AuthenticationFailedException;
-
List findByUserId(String userId, String applicationId) throws Fido2AuthenticationFailedException;
AuthenticatorDetail findByCredentialId(String credentialId, String applicationId) throws Fido2AuthenticationFailedException;
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/ChallengeProvider.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/ChallengeProvider.java
index 4801901a9..088893ec9 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/ChallengeProvider.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/ChallengeProvider.java
@@ -18,7 +18,6 @@
package com.wultra.powerauth.fido2.service;
-import com.wultra.powerauth.fido2.errorhandling.Fido2AuthenticationFailedException;
import com.wultra.powerauth.fido2.rest.model.entity.AssertionChallenge;
import com.wultra.powerauth.fido2.rest.model.entity.RegistrationChallenge;
@@ -52,33 +51,6 @@ public interface ChallengeProvider {
*/
RegistrationChallenge provideChallengeForRegistration(String userId, String applicationId) throws Exception;
- /**
- * Obtain challenge for authentication.
- *
- * @param userId User ID.
- * @param applicationIds List of application ID.
- * @param operationType Type of the operation this challenge is for.
- * @param parameters Operation parameters.
- * @return Assertion challenge.
- * @throws Exception In case any issue occur during processing.
- */
- default AssertionChallenge provideChallengeForAuthentication(String userId, List applicationIds, String operationType, Map parameters) throws Exception {
- return provideChallengeForAuthentication(userId, applicationIds, operationType, parameters, null);
- };
-
- /**
- * Obtain challenge for authentication.
- *
- * @param userId User ID.
- * @param applicationIds List of application ID.
- * @param operationType Type of the operation this challenge is for.
- * @param parameters Operation parameters.
- * @param externalAuthenticationId External ID of operation, i.e., transaction in transaction system.
- * @return Assertion challenge.
- * @throws Exception In case any issue occur during processing.
- */
- AssertionChallenge provideChallengeForAuthentication(String userId, List applicationIds, String operationType, Map parameters, String externalAuthenticationId) throws Exception;
-
/**
* Revoke challenge based on the challenge value.
*
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/RegistrationService.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/RegistrationService.java
index 0cca0b6b4..09c433f6d 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/RegistrationService.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/RegistrationService.java
@@ -76,7 +76,7 @@ public RegistrationResponse register(RegistrationRequest requestObject) throws E
throw new Fido2AuthenticationFailedException(error);
}
- final AuthenticatorAttestationResponse response = requestObject.getResponse();
+ final AuthenticatorAttestationResponse response = requestObject.getAuthenticatorParameters().getResponse();
final CollectedClientData clientDataJSON = response.getClientDataJSON();
final String challengeValue = clientDataJSON.getChallenge();
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/PowerAuthService.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/PowerAuthService.java
index e7d2d23e5..894074388 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/PowerAuthService.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/PowerAuthService.java
@@ -143,8 +143,9 @@ public GetActivationListForUserResponse getActivationListForUser(GetActivationLi
try {
final String userId = request.getUserId();
final String applicationId = request.getApplicationId();
+ final Set protocols = request.getProtocols();
logger.info("GetActivationListForUserRequest received, user ID: {}, application ID: {}", userId, applicationId);
- final GetActivationListForUserResponse response = behavior.getActivationServiceBehavior().getActivationList(applicationId, userId);
+ final GetActivationListForUserResponse response = behavior.getActivationServiceBehavior().getActivationList(applicationId, userId, protocols);
logger.info("GetActivationListForUserRequest succeeded");
return response;
} catch (RuntimeException | Error ex) {
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/ActivationServiceBehavior.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/ActivationServiceBehavior.java
index 27449d177..45179506e 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/ActivationServiceBehavior.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/ActivationServiceBehavior.java
@@ -230,7 +230,7 @@ private void validateCreatedActivation(ActivationRecordEntity activation, Applic
* @param userId User ID
* @return Response with list of matching activations
*/
- public GetActivationListForUserResponse getActivationList(String applicationId, String userId) {
+ public GetActivationListForUserResponse getActivationList(String applicationId, String userId, Set protocols) {
// Generate timestamp in advance
final Date timestamp = new Date();
@@ -252,6 +252,10 @@ public GetActivationListForUserResponse getActivationList(String applicationId,
deactivatePendingActivation(timestamp, activation, false);
+ if (!protocols.contains(Protocols.valueOf(activation.getProtocol().toUpperCase()))) { // skip authenticators that were not required
+ continue;
+ }
+
// Map between database object and service objects
final Activation activationServiceItem = new Activation();
activationServiceItem.setActivationId(activation.getActivationId());
@@ -630,7 +634,7 @@ public GetActivationStatusResponse getActivationStatus(String activationId, Stri
* @return Response with activation initialization data
* @throws GenericServiceException If invalid values are provided.
*/
- public InitActivationResponse initActivation(Protocols protocols, String applicationId, String userId, Long maxFailureCount, Date activationExpireTimestamp,
+ public InitActivationResponse initActivation(Protocols protocol, String applicationId, String userId, Long maxFailureCount, Date activationExpireTimestamp,
com.wultra.security.powerauth.client.model.enumeration.ActivationOtpValidation activationOtpValidation, String activationOtp, List flags,
KeyConvertor keyConversionUtilities) throws GenericServiceException {
try {
@@ -761,7 +765,7 @@ public InitActivationResponse initActivation(Protocols protocols, String applica
activation.setCtrDataBase64(null);
activation.setDevicePublicKeyBase64(null);
activation.setExtras(null);
- activation.setProtocol(protocols.toString());
+ activation.setProtocol(protocol.toString());
activation.setPlatform(null);
activation.setDeviceInfo(null);
activation.setFailedAttempts(0L);
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAssertionProvider.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAssertionProvider.java
new file mode 100644
index 000000000..d8fc7afc4
--- /dev/null
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAssertionProvider.java
@@ -0,0 +1,252 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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.server.service.fido2;
+
+import com.wultra.powerauth.fido2.errorhandling.Fido2AuthenticationFailedException;
+import com.wultra.powerauth.fido2.rest.model.entity.AssertionChallenge;
+import com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorDetail;
+import com.wultra.powerauth.fido2.service.AssertionVerificationProvider;
+import com.wultra.security.powerauth.client.model.entity.KeyValue;
+import com.wultra.security.powerauth.client.model.enumeration.OperationStatus;
+import com.wultra.security.powerauth.client.model.enumeration.SignatureType;
+import com.wultra.security.powerauth.client.model.enumeration.UserActionResult;
+import com.wultra.security.powerauth.client.model.request.OperationApproveRequest;
+import com.wultra.security.powerauth.client.model.request.OperationCreateRequest;
+import com.wultra.security.powerauth.client.model.request.OperationFailApprovalRequest;
+import com.wultra.security.powerauth.client.model.response.OperationDetailResponse;
+import com.wultra.security.powerauth.client.model.response.OperationUserActionResponse;
+import io.getlime.security.powerauth.app.server.database.RepositoryCatalogue;
+import io.getlime.security.powerauth.app.server.database.model.AdditionalInformation;
+import io.getlime.security.powerauth.app.server.database.model.entity.ActivationRecordEntity;
+import io.getlime.security.powerauth.app.server.database.model.enumeration.ActivationStatus;
+import io.getlime.security.powerauth.app.server.database.repository.ActivationRepository;
+import io.getlime.security.powerauth.app.server.service.behavior.ServiceBehaviorCatalogue;
+import io.getlime.security.powerauth.app.server.service.behavior.tasks.AuditingServiceBehavior;
+import io.getlime.security.powerauth.app.server.service.exceptions.GenericServiceException;
+import io.getlime.security.powerauth.app.server.service.model.signature.SignatureData;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Service responsible for assertion verification.
+ *
+ * @author Petr Dvorak, petr@wultra.com
+ */
+@Service
+public class PowerAuthAssertionProvider implements AssertionVerificationProvider {
+
+ private final ServiceBehaviorCatalogue serviceBehaviorCatalogue;
+ private final RepositoryCatalogue repositoryCatalogue;
+
+ @Autowired
+ public PowerAuthAssertionProvider(ServiceBehaviorCatalogue serviceBehaviorCatalogue, RepositoryCatalogue repositoryCatalogue) {
+ this.serviceBehaviorCatalogue = serviceBehaviorCatalogue;
+ this.repositoryCatalogue = repositoryCatalogue;
+ }
+
+ @Override
+ @Transactional
+ public AssertionChallenge provideChallengeForAssertion(List applicationIds, String operationType, Map parameters, String externalAuthenticationId) throws GenericServiceException {
+ final OperationCreateRequest operationCreateRequest = new OperationCreateRequest();
+ operationCreateRequest.setApplications(applicationIds);
+ operationCreateRequest.setTemplateName(operationType);
+ operationCreateRequest.getParameters().putAll(parameters);
+
+ final OperationDetailResponse operationDetailResponse = serviceBehaviorCatalogue.getOperationBehavior().createOperation(operationCreateRequest);
+ final AssertionChallenge assertionChallenge = new AssertionChallenge();
+ assertionChallenge.setUserId(operationDetailResponse.getUserId());
+ assertionChallenge.setApplicationIds(operationDetailResponse.getApplications());
+ assertionChallenge.setChallenge(operationDetailResponse.getId() + "&" + operationDetailResponse.getData());
+ assertionChallenge.setFailedAttempts(operationDetailResponse.getFailureCount());
+ assertionChallenge.setMaxFailedAttempts(operationDetailResponse.getMaxFailureCount());
+ return assertionChallenge;
+ }
+
+ @Override
+ @Transactional
+ public AssertionChallenge approveAssertion(String challengeValue, AuthenticatorDetail authenticatorDetail) throws Fido2AuthenticationFailedException {
+ try {
+
+ final String[] split = challengeValue.split("&", 2);
+ final String operationId = split[0];
+ final String operationData = split[1];
+
+ final OperationApproveRequest operationApproveRequest = new OperationApproveRequest();
+ operationApproveRequest.setOperationId(operationId);
+ operationApproveRequest.setData(operationData);
+ operationApproveRequest.setApplicationId(authenticatorDetail.getApplicationId());
+ operationApproveRequest.setUserId(authenticatorDetail.getUserId());
+ operationApproveRequest.setSignatureType(SignatureType.POSSESSION_KNOWLEDGE); //TODO: Use correct type
+ //operationApproveRequest.getAdditionalData(); // TODO: Use context data from request
+ final OperationUserActionResponse approveOperation = serviceBehaviorCatalogue.getOperationBehavior().attemptApproveOperation(operationApproveRequest);
+ final UserActionResult result = approveOperation.getResult();
+ final OperationDetailResponse operation = approveOperation.getOperation();
+ if (result == UserActionResult.APPROVED) {
+ final AssertionChallenge assertionChallenge = new AssertionChallenge();
+ assertionChallenge.setChallenge(challengeValue);
+ assertionChallenge.setUserId(operation.getUserId());
+ assertionChallenge.setApplicationIds(operation.getApplications());
+ assertionChallenge.setFailedAttempts(operation.getFailureCount());
+ assertionChallenge.setMaxFailedAttempts(operation.getMaxFailureCount());
+ return assertionChallenge;
+ } else {
+ handleStatus(operation.getStatus());
+ throw new Fido2AuthenticationFailedException("Operation approval failed");
+ }
+ } catch (GenericServiceException ex) {
+ throw new Fido2AuthenticationFailedException(ex.getMessage(), ex);
+ }
+ }
+
+ @Override
+ @Transactional
+ public AssertionChallenge failAssertion(String challengeValue, AuthenticatorDetail authenticatorDetail) throws Fido2AuthenticationFailedException {
+ try {
+ final Date currentTimestamp = new Date();
+
+ final String[] split = challengeValue.split("&", 1);
+ final String operationId = split[0];
+
+ final OperationFailApprovalRequest operationFailApprovalRequest = new OperationFailApprovalRequest();
+ operationFailApprovalRequest.setOperationId(operationId);
+ //operationApproveRequest.getAdditionalData(); // TODO: Use context data from request
+
+ final ActivationRecordEntity activationWithLock = repositoryCatalogue.getActivationRepository().findActivationWithLock(authenticatorDetail.getActivationId());
+
+ handleInvalidSignatureImpl(activationWithLock, new SignatureData(), currentTimestamp);
+
+ final OperationUserActionResponse approveOperation = serviceBehaviorCatalogue.getOperationBehavior().failApprovalOperation(operationFailApprovalRequest);
+ final OperationDetailResponse operation = approveOperation.getOperation();
+ handleStatus(operation.getStatus());
+ final AssertionChallenge assertionChallenge = new AssertionChallenge();
+ assertionChallenge.setChallenge(challengeValue);
+ assertionChallenge.setUserId(operation.getUserId());
+ assertionChallenge.setApplicationIds(operation.getApplications());
+ assertionChallenge.setFailedAttempts(operation.getFailureCount());
+ assertionChallenge.setMaxFailedAttempts(operation.getMaxFailureCount());
+ return assertionChallenge;
+ } catch (GenericServiceException ex) {
+ throw new Fido2AuthenticationFailedException(ex.getMessage(), ex);
+ }
+ }
+
+ /**
+ * Implementation of handle invalid signature.
+ * @param activation Activation used for signature verification.
+ * @param signatureData Data related to the signature.
+ * @param currentTimestamp Signature verification timestamp.
+ */
+ private void handleInvalidSignatureImpl(ActivationRecordEntity activation, SignatureData signatureData, Date currentTimestamp) {
+ // Get ActivationRepository
+ final ActivationRepository activationRepository = repositoryCatalogue.getActivationRepository();
+
+ final AuditingServiceBehavior.ActivationRecordDto activationDto = createActivationDtoFrom(activation);
+
+ // By default do not notify listeners
+ boolean notifyCallbackListeners = false;
+
+ // Update the last used date
+ activation.setTimestampLastUsed(currentTimestamp);
+
+ long remainingAttempts = (activation.getMaxFailedAttempts() - activation.getFailedAttempts());
+ if (remainingAttempts <= 0) {
+ activation.setActivationStatus(ActivationStatus.BLOCKED);
+ activation.setBlockedReason(AdditionalInformation.Reason.BLOCKED_REASON_MAX_FAILED_ATTEMPTS);
+ // Save the activation and log change
+ serviceBehaviorCatalogue.getActivationHistoryServiceBehavior().saveActivationAndLogChange(activation);
+ final KeyValue entry = new KeyValue();
+ entry.setKey(AdditionalInformation.Key.BLOCKED_REASON);
+ entry.setValue(AdditionalInformation.Reason.BLOCKED_REASON_MAX_FAILED_ATTEMPTS);
+ signatureData.getAdditionalInfo().add(entry);
+ // notify callback listeners
+ notifyCallbackListeners = true;
+ } else {
+ // Save the activation
+ activationRepository.save(activation);
+ }
+
+ // Create the audit log record.
+ serviceBehaviorCatalogue.getAuditingServiceBehavior().logSignatureAuditRecord(activationDto, signatureData, SignatureType.POSSESSION_KNOWLEDGE,false, null, "signature_does_not_match", currentTimestamp);
+
+ // Notify callback listeners, if needed
+ if (notifyCallbackListeners) {
+ serviceBehaviorCatalogue.getCallbackUrlBehavior().notifyCallbackListenersOnActivationChange(activation);
+ }
+ }
+
+ /**
+ * Implementation of handle inactive activation during signature verification.
+ * @param activation Activation used for signature verification.
+ * @param signatureData Data related to the signature.
+ * @param signatureType Used signature type.
+ * @param currentTimestamp Signature verification timestamp.
+ */
+ private void handleInactiveActivationSignatureImpl(ActivationRecordEntity activation, SignatureData signatureData, SignatureType signatureType, Date currentTimestamp) {
+ // Get ActivationRepository
+ final ActivationRepository activationRepository = repositoryCatalogue.getActivationRepository();
+
+ // Update the last used date
+ activation.setTimestampLastUsed(currentTimestamp);
+
+ // Save the activation
+ activationRepository.save(activation);
+
+ // Create the audit log record
+ final AuditingServiceBehavior.ActivationRecordDto activationDto = createActivationDtoFrom(activation);
+ serviceBehaviorCatalogue.getAuditingServiceBehavior().logSignatureAuditRecord(activationDto, signatureData, signatureType, false, activation.getVersion(), "activation_invalid_state", currentTimestamp);
+ }
+
+ /**
+ * Handle operation status.
+ *
+ *
+ * - PENDING - noop
+ * - CANCELLED, APPROVED, REJECTED, or EXPIRED - throws exception with appropriate code and message.
+ *
+ *
+ * @param status Operation status.
+ * @throws Fido2AuthenticationFailedException In case operation is in status that does not allow processing, the method throws appropriate exception.
+ */
+ private void handleStatus(OperationStatus status) throws Fido2AuthenticationFailedException {
+ switch (status) {
+ case PENDING -> { /* The operation is still pending, no-op. */ }
+ case CANCELED -> throw new Fido2AuthenticationFailedException("OPERATION_ALREADY_CANCELED - Operation was already canceled");
+ case APPROVED, REJECTED -> throw new Fido2AuthenticationFailedException("OPERATION_ALREADY_FINISHED - Operation was already completed");
+ case FAILED -> throw new Fido2AuthenticationFailedException("OPERATION_ALREADY_FAILED - Operation already failed");
+ default -> throw new Fido2AuthenticationFailedException("OPERATION_EXPIRED - Operation already expired");
+ }
+ }
+
+ private static AuditingServiceBehavior.ActivationRecordDto createActivationDtoFrom(ActivationRecordEntity activation) {
+ return AuditingServiceBehavior.ActivationRecordDto.builder()
+ .activationId(activation.getActivationId())
+ .applicationId(activation.getApplication().getId())
+ .counter(activation.getCounter())
+ .ctrDataBase64(activation.getCtrDataBase64())
+ .userId(activation.getUserId())
+ .activationStatus(activation.getActivationStatus())
+ .build();
+ }
+
+}
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAuthenticatorProvider.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAuthenticatorProvider.java
index 4504ff4af..ea6d694a1 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAuthenticatorProvider.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAuthenticatorProvider.java
@@ -90,7 +90,7 @@ public List findByUserId(String userId, String applicationI
throw new Fido2AuthenticationFailedException("Application with given ID is not present: " + applicationId);
}
- final GetActivationListForUserResponse activationList = serviceBehaviorCatalogue.getActivationServiceBehavior().getActivationList(applicationId, userId);
+ final GetActivationListForUserResponse activationList = serviceBehaviorCatalogue.getActivationServiceBehavior().getActivationList(applicationId, userId, Set.of(Protocols.FIDO2));
final List authenticatorDetailList = new ArrayList<>();
for (Activation activation : activationList.getActivations()) {
@@ -100,7 +100,7 @@ public List findByUserId(String userId, String applicationI
if (activation.getActivationStatus() == ActivationStatus.REMOVED && activation.getDevicePublicKeyBase64() == null) { // never finished removed activations
continue;
}
- if (!Protocols.FIDO2.toString().equals(activation.getProtocol())) {
+ if (!Protocols.FIDO2.toString().equalsIgnoreCase(activation.getProtocol())) { // Check the protocol, just in case
continue;
}
final AuthenticatorDetail authenticatorDetail = convert(activation, application.get());
@@ -153,7 +153,7 @@ public AuthenticatorDetail storeAuthenticator(String applicationId, String activ
// Search for activation without lock to avoid potential deadlocks
ActivationRecordEntity activation = activationRepository.findCreatedActivationWithoutLock(applicationId, activationCode, states, timestamp);
- // Make sure to deactivate the activation if it is expired
+ // Check if the activation exists
if (activation == null) {
logger.warn("Activation with activation code: {} could not be obtained. It either does not exist or it already expired.", activationCode);
// Rollback is not required, error occurs before writing to database
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthChallengeProvider.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthChallengeProvider.java
index dabb05842..fce69ead9 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthChallengeProvider.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthChallengeProvider.java
@@ -19,18 +19,15 @@
package io.getlime.security.powerauth.app.server.service.fido2;
import com.wultra.powerauth.fido2.errorhandling.Fido2AuthenticationFailedException;
-import com.wultra.powerauth.fido2.rest.model.entity.AssertionChallenge;
import com.wultra.powerauth.fido2.rest.model.entity.RegistrationChallenge;
import com.wultra.powerauth.fido2.service.ChallengeProvider;
import com.wultra.security.powerauth.client.model.enumeration.ActivationOtpValidation;
-import com.wultra.security.powerauth.client.model.request.OperationCreateRequest;
+import com.wultra.security.powerauth.client.model.enumeration.Protocols;
import com.wultra.security.powerauth.client.model.response.InitActivationResponse;
-import com.wultra.security.powerauth.client.model.response.OperationDetailResponse;
import io.getlime.security.powerauth.app.server.database.RepositoryCatalogue;
import io.getlime.security.powerauth.app.server.database.model.entity.ActivationRecordEntity;
import io.getlime.security.powerauth.app.server.database.model.entity.ApplicationEntity;
import io.getlime.security.powerauth.app.server.database.model.enumeration.ActivationStatus;
-import com.wultra.security.powerauth.client.model.enumeration.Protocols;
import io.getlime.security.powerauth.app.server.service.behavior.ServiceBehaviorCatalogue;
import io.getlime.security.powerauth.app.server.service.exceptions.GenericServiceException;
import io.getlime.security.powerauth.crypto.lib.util.KeyConvertor;
@@ -41,10 +38,11 @@
import java.util.Date;
import java.util.List;
-import java.util.Map;
import java.util.Optional;
/**
+ * Challenge provider based on the PowerAuth core implementations.
+ *
* @author Petr Dvorak, petr@wultra.com
*/
@Service
@@ -111,22 +109,6 @@ public RegistrationChallenge provideChallengeForRegistration(String userId, Stri
return registrationChallenge;
}
- @Override
- @Transactional
- public AssertionChallenge provideChallengeForAuthentication(String userId, List applicationIds, String operationType, Map parameters, String externalAuthenticationId) throws GenericServiceException {
- final OperationCreateRequest operationCreateRequest = new OperationCreateRequest();
- operationCreateRequest.setApplications(applicationIds);
- operationCreateRequest.setTemplateName(operationType);
- operationCreateRequest.getParameters().putAll(parameters);
-
- final OperationDetailResponse operationDetailResponse = serviceBehaviorCatalogue.getOperationBehavior().createOperation(operationCreateRequest);
- final AssertionChallenge assertionChallenge = new AssertionChallenge();
- assertionChallenge.setUserId(operationDetailResponse.getUserId());
- assertionChallenge.setApplicationIds(operationDetailResponse.getApplications());
- assertionChallenge.setChallenge(operationDetailResponse.getId() + "&" + operationDetailResponse.getData());
- return assertionChallenge;
- }
-
@Override
@Transactional
public void revokeChallengeForRegistrationChallengeValue(String applicationId, String challengeValue) throws Fido2AuthenticationFailedException {
From 532abfaaa85d9e21b5696f004922a02ce5992868 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Petr=20Dvo=C5=99=C3=A1k?=
Date: Sat, 9 Sep 2023 11:18:03 +0200
Subject: [PATCH 003/146] Allow claiming operation by given user ID by
accessing the details
---
.../model/request/OperationDetailRequest.java | 5 ++++
.../tasks/OperationServiceBehavior.java | 30 +++++++++++++++++--
2 files changed, 33 insertions(+), 2 deletions(-)
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/OperationDetailRequest.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/OperationDetailRequest.java
index 674948ccb..8499ab0d4 100644
--- a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/OperationDetailRequest.java
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/OperationDetailRequest.java
@@ -29,4 +29,9 @@
public class OperationDetailRequest {
private String operationId;
+
+ /**
+ * Optional user identifier of the user who is requesting the operation.
+ */
+ private String userId;
}
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/OperationServiceBehavior.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/OperationServiceBehavior.java
index 8f59238c9..832e86fcf 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/OperationServiceBehavior.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/OperationServiceBehavior.java
@@ -385,7 +385,7 @@ public OperationUserActionResponse rejectOperation(OperationRejectRequest reques
}
final String expectedUserId = operationEntity.getUserId();
- if ((expectedUserId == null || expectedUserId.equals(userId)) // correct user approved the operation, or no prior user was set
+ if ((expectedUserId == null || expectedUserId.equals(userId)) // correct user rejected the operation, or no prior user was set
&& operationEntity.getApplications().contains(application.get())) { // operation is rejected by the expected application
// Reject the operation
@@ -567,7 +567,12 @@ public OperationDetailResponse getOperation(OperationDetailRequest request) thro
throw localizationProvider.buildExceptionForCode(ServiceError.OPERATION_NOT_FOUND);
}
- final OperationEntity operationEntity = expireOperation(operationOptional.get(), currentTimestamp);
+ final String userId = request.getUserId();
+
+ final OperationEntity operationEntity = expireOperation(
+ claimOperation(operationOptional.get(), userId, currentTimestamp),
+ currentTimestamp
+ );
final OperationDetailResponse operationDetailResponse = convertFromEntity(operationEntity);
generateAndSetOtpToOperationDetail(operationEntity, operationDetailResponse);
return operationDetailResponse;
@@ -687,6 +692,27 @@ private OperationDetailResponse convertFromEntity(OperationEntity source) {
return destination;
}
+ private OperationEntity claimOperation(OperationEntity source, String userId, Date currentTimestamp) throws GenericServiceException {
+ // If a user accessing the operation is specified in the query, either claim the operation to that user,
+ // or check if the user is already granted to be able to access the operation.
+ if (userId != null) {
+ if (OperationStatusDo.PENDING.equals(source.getStatus())
+ && source.getTimestampExpires().before(currentTimestamp)) {
+ final String operationId = source.getId();
+ final String expectedUserId = source.getUserId();
+ if (expectedUserId == null) {
+ logger.info("Operation {} will be assigned to the user {}.", operationId, userId);
+ source.setUserId(userId);
+ return operationRepository.save(source);
+ } else if (!expectedUserId.equals(userId)) {
+ logger.warn("Operation with ID: {}, was accessed by user: {}, while previously assigned to user: {}.", operationId, userId, expectedUserId);
+ throw localizationProvider.buildExceptionForCode(ServiceError.OPERATION_NOT_FOUND);
+ }
+ }
+ }
+ return source;
+ }
+
private OperationEntity expireOperation(OperationEntity source, Date currentTimestamp) {
// Operation is still pending and timestamp is after the expiration.
if (OperationStatusDo.PENDING.equals(source.getStatus())
From dfa3f3ef3d96dae37ec6bf8df33ab4b829c60e61 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Petr=20Dvo=C5=99=C3=A1k?=
Date: Fri, 27 Oct 2023 16:49:20 +0200
Subject: [PATCH 004/146] Cleanup the code
---
powerauth-fido2/pom.xml | 6 ++--
.../rest/controller/AssertionController.java | 2 --
.../controller/RegistrationController.java | 3 --
.../converter/RegistrationConverter.java | 6 ++--
.../model/entity/AuthenticatorDetail.java | 4 +--
.../model/response/RegistrationResponse.java | 3 +-
.../fido2/service/AssertionService.java | 19 ++++++-----
.../fido2/service/RegistrationService.java | 15 ++++----
.../AssertionProvider.java} | 4 +--
.../{ => provider}/AuthenticatorProvider.java | 2 +-
.../{ => provider}/CryptographyService.java | 2 +-
.../RegistrationProvider.java} | 32 ++++++++---------
powerauth-java-server/pom.xml | 2 +-
.../fido2/PowerAuthAssertionProvider.java | 4 +--
.../fido2/PowerAuthAuthenticatorProvider.java | 17 +++++++---
.../fido2/PowerAuthCryptographyService.java | 2 +-
...ava => PowerAuthRegistrationProvider.java} | 34 +++++++++----------
.../tasks/ActivationServiceBehaviorTest.java | 3 +-
18 files changed, 82 insertions(+), 78 deletions(-)
rename powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/{AssertionVerificationProvider.java => provider/AssertionProvider.java} (97%)
rename powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/{ => provider}/AuthenticatorProvider.java (96%)
rename powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/{ => provider}/CryptographyService.java (97%)
rename powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/{ChallengeProvider.java => provider/RegistrationProvider.java} (81%)
rename powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/{PowerAuthChallengeProvider.java => PowerAuthRegistrationProvider.java} (94%)
diff --git a/powerauth-fido2/pom.xml b/powerauth-fido2/pom.xml
index 0b78d3307..26ed63ec5 100644
--- a/powerauth-fido2/pom.xml
+++ b/powerauth-fido2/pom.xml
@@ -27,7 +27,7 @@
io.getlime.security
powerauth-server-parent
- 1.5.0-SNAPSHOT
+ 1.6.0-SNAPSHOT
@@ -75,7 +75,7 @@
io.getlime.security
powerauth-java-crypto
- 1.5.0-SNAPSHOT
+ 1.5.1
io.getlime.core
@@ -85,7 +85,7 @@
io.getlime.security
powerauth-client-model
- 1.5.0-SNAPSHOT
+ 1.6.0-SNAPSHOT
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/AssertionController.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/AssertionController.java
index 510fc4612..0d9650988 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/AssertionController.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/AssertionController.java
@@ -55,7 +55,6 @@ public AssertionController(AssertionRequestValidator assertionRequestValidator,
}
@PostMapping("challenge")
- @CrossOrigin(origins = "http://localhost:8081")
public ObjectResponse requestAssertionChallenge(@Valid @RequestBody ObjectRequest request) throws Exception {
final AssertionChallengeRequest requestObject = request.getRequestObject();
final AssertionChallengeResponse assertionChallengeResponse = assertionService.requestAssertionChallenge(requestObject);
@@ -63,7 +62,6 @@ public ObjectResponse requestAssertionChallenge(@Val
}
@PostMapping
- @CrossOrigin(origins = "http://localhost:8081")
public ObjectResponse authenticate(@Valid @RequestBody ObjectRequest request) throws Fido2AuthenticationFailedException {
final AssertionRequest requestObject = request.getRequestObject();
final String error = assertionRequestValidator.validate(requestObject);
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/RegistrationController.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/RegistrationController.java
index 01def9fb8..40450dadb 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/RegistrationController.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/RegistrationController.java
@@ -54,7 +54,6 @@ public RegistrationController(RegistrationService registrationService) {
}
@PostMapping("list")
- @CrossOrigin(origins = "http://localhost:8081")
public ObjectResponse registeredAuthenticators(@Valid @RequestBody ObjectRequest request) throws Exception {
final RegisteredAuthenticatorsRequest requestObject = request.getRequestObject();
final RegisteredAuthenticatorsResponse responseObject = registrationService.registrationsForUser(requestObject.getUserId(), requestObject.getApplicationId());
@@ -62,7 +61,6 @@ public ObjectResponse registeredAuthenticators
}
@PostMapping("challenge")
- @CrossOrigin(origins = "http://localhost:8081")
public ObjectResponse requestRegistrationChallenge(@Valid @RequestBody ObjectRequest request) throws Exception {
final RegistrationChallengeRequest requestObject = request.getRequestObject();
final RegistrationChallengeResponse responseObject = registrationService.requestRegistrationChallenge(requestObject.getUserId(), requestObject.getApplicationId());
@@ -70,7 +68,6 @@ public ObjectResponse requestRegistrationChalleng
}
@PostMapping
- @CrossOrigin(origins = "http://localhost:8081")
public ObjectResponse register(@Valid @RequestBody ObjectRequest request) throws Exception {
final RegistrationRequest requestObject = request.getRequestObject();
final RegistrationResponse responseObject = registrationService.register(requestObject);
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/RegistrationConverter.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/RegistrationConverter.java
index 13a504632..338f461fc 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/RegistrationConverter.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/RegistrationConverter.java
@@ -19,7 +19,6 @@
package com.wultra.powerauth.fido2.rest.model.converter;
import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
import com.wultra.powerauth.fido2.rest.model.entity.AaguidList;
import com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorDetail;
import com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorParameters;
@@ -44,7 +43,6 @@
public class RegistrationConverter {
private final AaguidList aaguidRegistry = new AaguidList();
- private final ObjectMapper objectMapper = new ObjectMapper();
public AuthenticatorDetail convert(RegistrationChallenge challenge, RegistrationRequest requestObject, byte[] aaguid, byte[] publicKey) {
try {
@@ -70,7 +68,7 @@ public AuthenticatorDetail convert(RegistrationChallenge challenge, Registration
}
}
- private String convertExtras(RegistrationRequest requestObject) throws JsonProcessingException {
+ private Map convertExtras(RegistrationRequest requestObject) throws JsonProcessingException {
final AuthenticatorParameters authenticatorParameters = requestObject.getAuthenticatorParameters();
final Map params = new HashMap<>();
params.put("relyingPartyId", authenticatorParameters.getRelyingPartyId());
@@ -83,7 +81,7 @@ private String convertExtras(RegistrationRequest requestObject) throws JsonProce
params.put("topOrigin", authenticatorParameters.getResponse().getClientDataJSON().getTopOrigin());
params.put("isCrossOrigin", authenticatorParameters.getResponse().getClientDataJSON().isCrossOrigin());
params.put("aaguid", authenticatorParameters.getResponse().getAttestationObject().getAuthData().getAttestedCredentialData().getAaguid());
- return objectMapper.writeValueAsString(params);
+ return params;
}
public RegistrationResponse convertRegistrationResponse(AuthenticatorDetail source) {
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AuthenticatorDetail.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AuthenticatorDetail.java
index e5b42e7d1..975efe071 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AuthenticatorDetail.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AuthenticatorDetail.java
@@ -21,9 +21,9 @@
import com.wultra.security.powerauth.client.model.enumeration.ActivationStatus;
import lombok.Data;
-import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
/**
* Information about a registered authenticator.
@@ -39,7 +39,7 @@ public class AuthenticatorDetail {
private String activationName;
private String externalId;
private ActivationStatus activationStatus;
- private String extras;
+ private Map extras;
private String platform;
private String deviceInfo;
private String blockedReason;
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/response/RegistrationResponse.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/response/RegistrationResponse.java
index 2df4e62e2..694379991 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/response/RegistrationResponse.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/response/RegistrationResponse.java
@@ -24,6 +24,7 @@
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
/**
* @author Petr Dvorak, petr@wultra.com
@@ -37,7 +38,7 @@ public class RegistrationResponse {
private String externalId;
private String activationName;
private ActivationStatus activationStatus;
- private String extras;
+ private Map extras;
private String platform;
private String deviceInfo;
private String blockedReason;
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/AssertionService.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/AssertionService.java
index 0ad65cdff..eab7b88c1 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/AssertionService.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/AssertionService.java
@@ -28,6 +28,9 @@
import com.wultra.powerauth.fido2.rest.model.request.AssertionRequest;
import com.wultra.powerauth.fido2.rest.model.response.AssertionChallengeResponse;
import com.wultra.powerauth.fido2.rest.model.response.AssertionVerificationResponse;
+import com.wultra.powerauth.fido2.service.provider.AssertionProvider;
+import com.wultra.powerauth.fido2.service.provider.AuthenticatorProvider;
+import com.wultra.powerauth.fido2.service.provider.CryptographyService;
import com.wultra.security.powerauth.client.model.enumeration.ActivationStatus;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
@@ -43,18 +46,16 @@
public class AssertionService {
private final CryptographyService cryptographyService;
- private final ChallengeProvider challengeProvider;
private final AuthenticatorProvider authenticatorProvider;
- private final AssertionVerificationProvider assertionVerificationProvider;
+ private final AssertionProvider assertionProvider;
private final AssertionConverter assertionConverter;
private final AssertionChallengeConverter assertionChallengeConverter;
@Autowired
- public AssertionService(CryptographyService cryptographyService, ChallengeProvider challengeProvider, AuthenticatorProvider authenticatorProvider, AssertionVerificationProvider assertionVerificationProvider, AssertionConverter assertionConverter, AssertionChallengeConverter assertionChallengeConverter) {
+ public AssertionService(CryptographyService cryptographyService, AuthenticatorProvider authenticatorProvider, AssertionProvider assertionProvider, AssertionConverter assertionConverter, AssertionChallengeConverter assertionChallengeConverter) {
this.cryptographyService = cryptographyService;
- this.challengeProvider = challengeProvider;
this.authenticatorProvider = authenticatorProvider;
- this.assertionVerificationProvider = assertionVerificationProvider;
+ this.assertionProvider = assertionProvider;
this.assertionConverter = assertionConverter;
this.assertionChallengeConverter = assertionChallengeConverter;
}
@@ -66,7 +67,7 @@ public AssertionService(CryptographyService cryptographyService, ChallengeProvid
* @return Assertion challenge information.
*/
public AssertionChallengeResponse requestAssertionChallenge(AssertionChallengeRequest request) throws Exception {
- final AssertionChallenge assertionChallenge = assertionVerificationProvider.provideChallengeForAssertion(
+ final AssertionChallenge assertionChallenge = assertionProvider.provideChallengeForAssertion(
request.getApplicationIds(), request.getOperationType(), request.getParameters(), request.getExternalId()
);
if (assertionChallenge == null) {
@@ -91,14 +92,14 @@ public AssertionVerificationResponse authenticate(AssertionRequest request) thro
if (authenticatorDetail.getActivationStatus() == ActivationStatus.ACTIVE) {
final boolean signatureCorrect = cryptographyService.verifySignatureForAssertion(applicationId, authenticatorId, response.getClientDataJSON(), response.getAuthenticatorData(), response.getSignature(), authenticatorDetail);
if (signatureCorrect) {
- assertionVerificationProvider.approveAssertion(challenge, authenticatorDetail);
+ assertionProvider.approveAssertion(challenge, authenticatorDetail);
return assertionConverter.fromAuthenticatorDetail(authenticatorDetail, signatureCorrect);
} else {
- assertionVerificationProvider.failAssertion(challenge, authenticatorDetail);
+ assertionProvider.failAssertion(challenge, authenticatorDetail);
throw new Fido2AuthenticationFailedException("Authentication failed due to incorrect signature.");
}
} else {
- assertionVerificationProvider.failAssertion(challenge, authenticatorDetail);
+ assertionProvider.failAssertion(challenge, authenticatorDetail);
throw new Fido2AuthenticationFailedException("Authentication failed due to incorrect authenticator state.");
}
} catch (Exception e) {
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/RegistrationService.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/RegistrationService.java
index 09c433f6d..ea8422e88 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/RegistrationService.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/RegistrationService.java
@@ -27,6 +27,9 @@
import com.wultra.powerauth.fido2.rest.model.response.RegistrationChallengeResponse;
import com.wultra.powerauth.fido2.rest.model.response.RegistrationResponse;
import com.wultra.powerauth.fido2.rest.model.validator.RegistrationRequestValidator;
+import com.wultra.powerauth.fido2.service.provider.AuthenticatorProvider;
+import com.wultra.powerauth.fido2.service.provider.CryptographyService;
+import com.wultra.powerauth.fido2.service.provider.RegistrationProvider;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -41,16 +44,16 @@
public class RegistrationService {
private final AuthenticatorProvider authenticatorProvider;
- private final ChallengeProvider challengeProvider;
+ private final RegistrationProvider registrationProvider;
private final RegistrationChallengeConverter registrationChallengeConverter;
private final RegistrationConverter registrationConverter;
private final RegistrationRequestValidator registrationRequestValidator;
private final CryptographyService cryptographyService;
@Autowired
- public RegistrationService(AuthenticatorProvider authenticatorProvider, ChallengeProvider challengeProvider, RegistrationChallengeConverter registrationChallengeConverter, RegistrationConverter registrationConverter, RegistrationRequestValidator registrationRequestValidator, CryptographyService cryptographyService) {
+ public RegistrationService(AuthenticatorProvider authenticatorProvider, RegistrationProvider registrationProvider, RegistrationChallengeConverter registrationChallengeConverter, RegistrationConverter registrationConverter, RegistrationRequestValidator registrationRequestValidator, CryptographyService cryptographyService) {
this.authenticatorProvider = authenticatorProvider;
- this.challengeProvider = challengeProvider;
+ this.registrationProvider = registrationProvider;
this.registrationChallengeConverter = registrationChallengeConverter;
this.registrationConverter = registrationConverter;
this.registrationRequestValidator = registrationRequestValidator;
@@ -64,7 +67,7 @@ public RegisteredAuthenticatorsResponse registrationsForUser(String userId, Stri
}
public RegistrationChallengeResponse requestRegistrationChallenge(String userId, String applicationId) throws Exception {
- final RegistrationChallenge challenge = challengeProvider.provideChallengeForRegistration(userId, applicationId);
+ final RegistrationChallenge challenge = registrationProvider.provideChallengeForRegistration(userId, applicationId);
return registrationChallengeConverter.fromChallenge(challenge);
}
@@ -93,14 +96,14 @@ public RegistrationResponse register(RegistrationRequest requestObject) throws E
final boolean verifySignature = cryptographyService.verifySignatureForRegistration(applicationId, clientDataJSON, authData, signature, attestedCredentialData);
if (!verifySignature) {
// Immediately revoke the challenge
- challengeProvider.revokeChallengeForRegistrationChallengeValue(applicationId, challengeValue);
+ registrationProvider.revokeChallengeForRegistrationChallengeValue(applicationId, challengeValue);
throw new Fido2AuthenticationFailedException("Registration failed");
}
} else {
logger.info("No signature verification on registration");
}
- final RegistrationChallenge challenge = challengeProvider.provideChallengeForRegistrationChallengeValue(applicationId, challengeValue);
+ final RegistrationChallenge challenge = registrationProvider.provideChallengeForRegistrationChallengeValue(applicationId, challengeValue);
final AuthenticatorDetail authenticatorDetail = registrationConverter.convert(challenge, requestObject, attestedCredentialData.getAaguid(), cryptographyService.publicKeyToBytes(attestedCredentialData));
final AuthenticatorDetail authenticatorDetailResponse = authenticatorProvider.storeAuthenticator(requestObject.getApplicationId(), challenge.getChallenge(), authenticatorDetail);
return registrationConverter.convertRegistrationResponse(authenticatorDetailResponse);
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/AssertionVerificationProvider.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/provider/AssertionProvider.java
similarity index 97%
rename from powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/AssertionVerificationProvider.java
rename to powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/provider/AssertionProvider.java
index 385d7a7d1..e2bae3032 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/AssertionVerificationProvider.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/provider/AssertionProvider.java
@@ -16,7 +16,7 @@
* along with this program. If not, see .
*/
-package com.wultra.powerauth.fido2.service;
+package com.wultra.powerauth.fido2.service.provider;
import com.wultra.powerauth.fido2.errorhandling.Fido2AuthenticationFailedException;
import com.wultra.powerauth.fido2.rest.model.entity.AssertionChallenge;
@@ -30,7 +30,7 @@
*
* @author Petr Dvorak, petr@wultra.com
*/
-public interface AssertionVerificationProvider {
+public interface AssertionProvider {
/**
* Obtain challenge for authentication.
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/AuthenticatorProvider.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/provider/AuthenticatorProvider.java
similarity index 96%
rename from powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/AuthenticatorProvider.java
rename to powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/provider/AuthenticatorProvider.java
index 1b7cfcb20..7587b3dce 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/AuthenticatorProvider.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/provider/AuthenticatorProvider.java
@@ -16,7 +16,7 @@
* along with this program. If not, see .
*/
-package com.wultra.powerauth.fido2.service;
+package com.wultra.powerauth.fido2.service.provider;
import com.wultra.powerauth.fido2.errorhandling.Fido2AuthenticationFailedException;
import com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorDetail;
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/CryptographyService.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/provider/CryptographyService.java
similarity index 97%
rename from powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/CryptographyService.java
rename to powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/provider/CryptographyService.java
index f5ed5e0f0..aeeadbcc2 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/CryptographyService.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/provider/CryptographyService.java
@@ -16,7 +16,7 @@
* along with this program. If not, see .
*/
-package com.wultra.powerauth.fido2.service;
+package com.wultra.powerauth.fido2.service.provider;
import com.wultra.powerauth.fido2.rest.model.entity.AttestedCredentialData;
import com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorData;
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/ChallengeProvider.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/provider/RegistrationProvider.java
similarity index 81%
rename from powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/ChallengeProvider.java
rename to powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/provider/RegistrationProvider.java
index 088893ec9..18b3981cd 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/ChallengeProvider.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/provider/RegistrationProvider.java
@@ -16,43 +16,39 @@
* along with this program. If not, see .
*/
-package com.wultra.powerauth.fido2.service;
+package com.wultra.powerauth.fido2.service.provider;
-import com.wultra.powerauth.fido2.rest.model.entity.AssertionChallenge;
import com.wultra.powerauth.fido2.rest.model.entity.RegistrationChallenge;
-import java.util.List;
-import java.util.Map;
-
/**
- * Interface for challenge providers.
+ * Interface for registration use-cases.
*
* @author Petr Dvorak, petr@wultra.com
*/
-public interface ChallengeProvider {
+public interface RegistrationProvider {
/**
- * Obtain challenge information based on challenge value for registration.
+ * Obtain a new challenge for registration.
*
- * @param applicationId Application key.
- * @param challenge Challenge value.
- * @return Challenge Information.
+ * @param userId User ID.
+ * @param applicationId Application ID.
+ * @return Registration challenge.
* @throws Exception In case any issue occur during processing.
*/
- RegistrationChallenge provideChallengeForRegistrationChallengeValue(String applicationId, String challenge) throws Exception;
+ RegistrationChallenge provideChallengeForRegistration(String userId, String applicationId) throws Exception;
/**
- * Obtain challenge for registration.
+ * Obtain an existing challenge information based on challenge value for registration.
*
- * @param userId User ID.
- * @param applicationId Application ID.
- * @return Registration challenge.
+ * @param applicationId Application key.
+ * @param challenge Challenge value.
+ * @return Challenge Information.
* @throws Exception In case any issue occur during processing.
*/
- RegistrationChallenge provideChallengeForRegistration(String userId, String applicationId) throws Exception;
+ RegistrationChallenge provideChallengeForRegistrationChallengeValue(String applicationId, String challenge) throws Exception;
/**
- * Revoke challenge based on the challenge value.
+ * Revoke existing challenge based on the challenge value.
*
* @param applicationId Application ID.
* @param challengeValue Challenge value.
diff --git a/powerauth-java-server/pom.xml b/powerauth-java-server/pom.xml
index 4a5210ee0..b035b0971 100644
--- a/powerauth-java-server/pom.xml
+++ b/powerauth-java-server/pom.xml
@@ -116,7 +116,7 @@
io.getlime.security
powerauth-fido2
- 1.5.0-SNAPSHOT
+ 1.6.0-SNAPSHOT
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAssertionProvider.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAssertionProvider.java
index d8fc7afc4..4ed907795 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAssertionProvider.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAssertionProvider.java
@@ -21,7 +21,7 @@
import com.wultra.powerauth.fido2.errorhandling.Fido2AuthenticationFailedException;
import com.wultra.powerauth.fido2.rest.model.entity.AssertionChallenge;
import com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorDetail;
-import com.wultra.powerauth.fido2.service.AssertionVerificationProvider;
+import com.wultra.powerauth.fido2.service.provider.AssertionProvider;
import com.wultra.security.powerauth.client.model.entity.KeyValue;
import com.wultra.security.powerauth.client.model.enumeration.OperationStatus;
import com.wultra.security.powerauth.client.model.enumeration.SignatureType;
@@ -54,7 +54,7 @@
* @author Petr Dvorak, petr@wultra.com
*/
@Service
-public class PowerAuthAssertionProvider implements AssertionVerificationProvider {
+public class PowerAuthAssertionProvider implements AssertionProvider {
private final ServiceBehaviorCatalogue serviceBehaviorCatalogue;
private final RepositoryCatalogue repositoryCatalogue;
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAuthenticatorProvider.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAuthenticatorProvider.java
index b1d320c98..5be941632 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAuthenticatorProvider.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAuthenticatorProvider.java
@@ -18,9 +18,12 @@
package io.getlime.security.powerauth.app.server.service.fido2;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
import com.wultra.powerauth.fido2.errorhandling.Fido2AuthenticationFailedException;
import com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorDetail;
-import com.wultra.powerauth.fido2.service.AuthenticatorProvider;
+import com.wultra.powerauth.fido2.service.provider.AuthenticatorProvider;
import com.wultra.security.powerauth.client.model.entity.Activation;
import com.wultra.security.powerauth.client.model.enumeration.ActivationStatus;
import com.wultra.security.powerauth.client.model.response.GetActivationListForUserResponse;
@@ -42,7 +45,6 @@
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
-import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -66,6 +68,7 @@ public class PowerAuthAuthenticatorProvider implements AuthenticatorProvider {
private LocalizationProvider localizationProvider;
private final KeyConvertor keyConvertor = new KeyConvertor();
+ private final ObjectMapper objectMapper = new ObjectMapper();
private final ActivationStatusConverter activationStatusConverter = new ActivationStatusConverter();
@Autowired
@@ -201,7 +204,7 @@ public AuthenticatorDetail storeAuthenticator(String applicationId, String activ
activation.setDevicePublicKeyBase64(Base64.getEncoder().encodeToString(keyConvertor.convertPublicKeyToBytes(devicePublicKey)));
activation.setActivationName(authenticatorDetail.getActivationName());
activation.setExternalId(authenticatorDetail.getExternalId());
- activation.setExtras(authenticatorDetail.getExtras());
+ activation.setExtras(objectMapper.writeValueAsString(authenticatorDetail.getExtras()));
if (authenticatorDetail.getPlatform() != null) {
activation.setPlatform(authenticatorDetail.getPlatform().toLowerCase());
} else {
@@ -248,6 +251,8 @@ public AuthenticatorDetail storeAuthenticator(String applicationId, String activ
logger.error(ex.getMessage(), ex);
// Rollback is not required, cryptography errors can only occur before writing to database
throw new Fido2AuthenticationFailedException("Invalid cryptography provider");
+ } catch (JsonProcessingException e) {
+ throw new Fido2AuthenticationFailedException("Unable to serialize extras");
} catch (GenericServiceException e) {
throw new Fido2AuthenticationFailedException("Generic service exception");
}
@@ -263,7 +268,11 @@ private AuthenticatorDetail convert(Activation activation, ApplicationEntity app
authenticatorDetail.setActivationStatus(activation.getActivationStatus());
authenticatorDetail.setActivationName(activation.getActivationName());
authenticatorDetail.setExternalId(activation.getExternalId());
- authenticatorDetail.setExtras(activation.getExtras());
+ try {
+ authenticatorDetail.setExtras(objectMapper.readValue(activation.getExtras(), new TypeReference>() {}));
+ } catch (JsonProcessingException e) {
+ //
+ }
authenticatorDetail.setActivationFlags(activation.getActivationFlags());
authenticatorDetail.setDeviceInfo(activation.getDeviceInfo());
authenticatorDetail.setPlatform(activation.getPlatform());
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthCryptographyService.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthCryptographyService.java
index a1c21e340..01ac9fe28 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthCryptographyService.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthCryptographyService.java
@@ -19,7 +19,7 @@
package io.getlime.security.powerauth.app.server.service.fido2;
import com.wultra.powerauth.fido2.rest.model.entity.*;
-import com.wultra.powerauth.fido2.service.CryptographyService;
+import com.wultra.powerauth.fido2.service.provider.CryptographyService;
import io.getlime.security.powerauth.crypto.lib.model.exception.CryptoProviderException;
import io.getlime.security.powerauth.crypto.lib.model.exception.GenericCryptoException;
import io.getlime.security.powerauth.crypto.lib.util.Hash;
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthChallengeProvider.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthRegistrationProvider.java
similarity index 94%
rename from powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthChallengeProvider.java
rename to powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthRegistrationProvider.java
index fce69ead9..da1be956d 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthChallengeProvider.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthRegistrationProvider.java
@@ -20,7 +20,7 @@
import com.wultra.powerauth.fido2.errorhandling.Fido2AuthenticationFailedException;
import com.wultra.powerauth.fido2.rest.model.entity.RegistrationChallenge;
-import com.wultra.powerauth.fido2.service.ChallengeProvider;
+import com.wultra.powerauth.fido2.service.provider.RegistrationProvider;
import com.wultra.security.powerauth.client.model.enumeration.ActivationOtpValidation;
import com.wultra.security.powerauth.client.model.enumeration.Protocols;
import com.wultra.security.powerauth.client.model.response.InitActivationResponse;
@@ -47,7 +47,7 @@
*/
@Service
@Slf4j
-public class PowerAuthChallengeProvider implements ChallengeProvider {
+public class PowerAuthRegistrationProvider implements RegistrationProvider {
private final RepositoryCatalogue repositoryCatalogue;
private final ServiceBehaviorCatalogue serviceBehaviorCatalogue;
@@ -55,11 +55,24 @@ public class PowerAuthChallengeProvider implements ChallengeProvider {
private final KeyConvertor keyConvertor = new KeyConvertor();
@Autowired
- public PowerAuthChallengeProvider(RepositoryCatalogue repositoryCatalogue, ServiceBehaviorCatalogue serviceBehaviorCatalogue) {
+ public PowerAuthRegistrationProvider(RepositoryCatalogue repositoryCatalogue, ServiceBehaviorCatalogue serviceBehaviorCatalogue) {
this.repositoryCatalogue = repositoryCatalogue;
this.serviceBehaviorCatalogue = serviceBehaviorCatalogue;
}
+ @Override
+ @Transactional
+ public RegistrationChallenge provideChallengeForRegistration(String userId, String applicationId) throws GenericServiceException {
+ final InitActivationResponse initActivationResponse = serviceBehaviorCatalogue.getActivationServiceBehavior()
+ .initActivation(Protocols.FIDO2, applicationId, userId, null, null, ActivationOtpValidation.NONE, null, null, keyConvertor);
+ final RegistrationChallenge registrationChallenge = new RegistrationChallenge();
+ registrationChallenge.setUserId(initActivationResponse.getUserId());
+ registrationChallenge.setApplicationId(initActivationResponse.getApplicationId());
+ registrationChallenge.setActivationId(initActivationResponse.getActivationId());
+ registrationChallenge.setChallenge(initActivationResponse.getActivationCode());
+ return registrationChallenge;
+ }
+
@Override
@Transactional
public RegistrationChallenge provideChallengeForRegistrationChallengeValue(String applicationId, String challengeValue) throws Fido2AuthenticationFailedException {
@@ -96,19 +109,6 @@ public RegistrationChallenge provideChallengeForRegistrationChallengeValue(Strin
return assertionChallenge;
}
- @Override
- @Transactional
- public RegistrationChallenge provideChallengeForRegistration(String userId, String applicationId) throws GenericServiceException {
- final InitActivationResponse initActivationResponse = serviceBehaviorCatalogue.getActivationServiceBehavior()
- .initActivation(Protocols.FIDO2, applicationId, userId, null, null, ActivationOtpValidation.NONE, null, null, keyConvertor);
- final RegistrationChallenge registrationChallenge = new RegistrationChallenge();
- registrationChallenge.setUserId(initActivationResponse.getUserId());
- registrationChallenge.setApplicationId(initActivationResponse.getApplicationId());
- registrationChallenge.setActivationId(initActivationResponse.getActivationId());
- registrationChallenge.setChallenge(initActivationResponse.getActivationCode());
- return registrationChallenge;
- }
-
@Override
@Transactional
public void revokeChallengeForRegistrationChallengeValue(String applicationId, String challengeValue) throws Fido2AuthenticationFailedException {
@@ -130,7 +130,7 @@ public void revokeChallengeForRegistrationChallengeValue(String applicationId, S
.findCreatedActivationWithoutLock(applicationId, activationCode, statuses, currentTimestamp);
if (activationRecordEntity == null) {
- throw new Fido2AuthenticationFailedException("With given value not found.");
+ throw new Fido2AuthenticationFailedException("Registration with given value not found.");
}
final String activationId = activationRecordEntity.getActivationId();
diff --git a/powerauth-java-server/src/test/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/ActivationServiceBehaviorTest.java b/powerauth-java-server/src/test/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/ActivationServiceBehaviorTest.java
index 8242670bd..39eb6df72 100644
--- a/powerauth-java-server/src/test/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/ActivationServiceBehaviorTest.java
+++ b/powerauth-java-server/src/test/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/ActivationServiceBehaviorTest.java
@@ -19,6 +19,7 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wultra.security.powerauth.client.model.enumeration.ActivationStatus;
+import com.wultra.security.powerauth.client.model.enumeration.Protocols;
import com.wultra.security.powerauth.client.model.enumeration.RecoveryCodeStatus;
import com.wultra.security.powerauth.client.model.request.*;
import com.wultra.security.powerauth.client.model.response.*;
@@ -344,7 +345,7 @@ private RecoveryCodeActivationRequest buildRecoveryCodeActivationRequest(String
}
private InitActivationResponse initActivation(String applicationId) throws Exception {
- return tested.initActivation(applicationId, userId, null, null, null, null, null, keyConvertor);
+ return tested.initActivation(Protocols.POWERAUTH, applicationId, userId, null, null, null, null, null, keyConvertor);
}
private GetApplicationDetailResponse createApplication() throws Exception {
From f7223449d2a0bf92157ad7a88b04a981c56f73e9 Mon Sep 17 00:00:00 2001
From: "roman.strobl@wultra.com"
Date: Fri, 3 Nov 2023 12:31:49 +0100
Subject: [PATCH 005/146] Use correct separator for activation code and its
signature
---
.../app/server/service/fido2/PowerAuthRegistrationProvider.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthRegistrationProvider.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthRegistrationProvider.java
index da1be956d..8d9387026 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthRegistrationProvider.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthRegistrationProvider.java
@@ -87,7 +87,7 @@ public RegistrationChallenge provideChallengeForRegistrationChallengeValue(Strin
final Date currentTimestamp = new Date();
// Obtain just the activation code part, just in case there was a value with signature
- final String[] split = challengeValue.split("&", 1);
+ final String[] split = challengeValue.split("#", 1);
final String activationCode = split[0];
// Only allow created activations to be finished
From 1d3c260c746cb302f51b0b75415d51cac25ec1e4 Mon Sep 17 00:00:00 2001
From: "roman.strobl@wultra.com"
Date: Fri, 3 Nov 2023 12:32:33 +0100
Subject: [PATCH 006/146] Fix typo
---
.../com/wultra/powerauth/fido2/rest/model/entity/Flags.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/Flags.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/Flags.java
index d7ea3d5c5..3f93a6e16 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/Flags.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/Flags.java
@@ -33,6 +33,6 @@ public class Flags {
private boolean backupState;
private boolean reservedBit6;
private boolean attestedCredentialsIncluded;
- private boolean extensionDataInlcuded;
+ private boolean extensionDataIncluded;
}
From a3058e640c0545725771ad79c4b3ff0bf5649140 Mon Sep 17 00:00:00 2001
From: "roman.strobl@wultra.com"
Date: Tue, 7 Nov 2023 15:25:25 +0100
Subject: [PATCH 007/146] Fix typo
---
.../converter/serialization/AuthenticatorDataDeserializer.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AuthenticatorDataDeserializer.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AuthenticatorDataDeserializer.java
index 5a8ecb00c..11ebfaeb3 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AuthenticatorDataDeserializer.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AuthenticatorDataDeserializer.java
@@ -80,7 +80,7 @@ public AuthenticatorData deserialize(JsonParser jsonParser, DeserializationConte
flags.setBackupState(isFlagOn(flagByte, 4));
flags.setReservedBit6(isFlagOn(flagByte, 5));
flags.setAttestedCredentialsIncluded(isFlagOn(flagByte,6));
- flags.setExtensionDataInlcuded(isFlagOn(flagByte,7));
+ flags.setExtensionDataIncluded(isFlagOn(flagByte,7));
// Get Signature Counter
final byte[] signCountBytes = new byte[4];
From 56e997335f654c6637aadca269972a5a708b2487 Mon Sep 17 00:00:00 2001
From: "roman.strobl@wultra.com"
Date: Tue, 7 Nov 2023 16:39:45 +0100
Subject: [PATCH 008/146] Fix incorrect activation code parsing
---
.../app/server/service/fido2/PowerAuthRegistrationProvider.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthRegistrationProvider.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthRegistrationProvider.java
index 8d9387026..f9be5ee22 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthRegistrationProvider.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthRegistrationProvider.java
@@ -122,7 +122,7 @@ public void revokeChallengeForRegistrationChallengeValue(String applicationId, S
}
// Obtain just the activation code part, just in case there was a value with signature
- final String[] split = challengeValue.split("&", 1);
+ final String[] split = challengeValue.split("#", 1);
final String activationCode = split[0];
final List statuses = List.of(ActivationStatus.CREATED, ActivationStatus.PENDING_COMMIT, ActivationStatus.ACTIVE, ActivationStatus.BLOCKED);
From ba9b41f8b2502e6cc21318f9c6f886f70568f29f Mon Sep 17 00:00:00 2001
From: "roman.strobl@wultra.com"
Date: Wed, 8 Nov 2023 18:13:43 +0100
Subject: [PATCH 009/146] Fix error message for checking webauthn method
---
.../fido2/rest/model/validator/AssertionRequestValidator.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/validator/AssertionRequestValidator.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/validator/AssertionRequestValidator.java
index f9b1addfb..5334ba035 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/validator/AssertionRequestValidator.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/validator/AssertionRequestValidator.java
@@ -48,7 +48,7 @@ public String validate(AssertionRequest request) {
final CollectedClientData clientDataJSON = request.getResponse().getClientDataJSON();
if (!"webauthn.get".equals(clientDataJSON.getType())) {
- return "Request does not contain webauthn.create type.";
+ return "Request does not contain webauthn.get type.";
}
final String expectedChallenge = request.getExpectedChallenge();
From 87d76298bc8aa2f959e11475c852c8e280a8d97c Mon Sep 17 00:00:00 2001
From: "roman.strobl@wultra.com"
Date: Wed, 8 Nov 2023 18:23:50 +0100
Subject: [PATCH 010/146] Fix typo
---
.../service/behavior/tasks/ActivationServiceBehavior.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/ActivationServiceBehavior.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/ActivationServiceBehavior.java
index ed97d6806..9249ef512 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/ActivationServiceBehavior.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/ActivationServiceBehavior.java
@@ -237,7 +237,7 @@ private void validateCreatedActivation(ActivationRecordEntity activation, Applic
* @param applicationId The Application ID for which to retrieve activations. If this is null, activations for all
* applications associated with the provided user ID are retrieved.
* @param userId The User ID for which to retrieve activations. This is required and cannot be null.
- * @param protocols Set of protocols to be returned..
+ * @param protocols Set of protocols to be returned.
* @param pageable An object that defines the pagination properties, including the page number and the size of each page.
* It is used to retrieve the activations in a paginated format.
* @return A {@link GetActivationListForUserResponse} object that includes the list of matching activations. Each
From 8c3d111d3265938d1ed2ad8dff138f5b4e16d70b Mon Sep 17 00:00:00 2001
From: "roman.strobl@wultra.com"
Date: Wed, 8 Nov 2023 18:27:09 +0100
Subject: [PATCH 011/146] Avoid using equalsIgnoreCase for security-related
codea
---
.../server/service/fido2/PowerAuthAuthenticatorProvider.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAuthenticatorProvider.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAuthenticatorProvider.java
index 5be941632..0ec177b86 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAuthenticatorProvider.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAuthenticatorProvider.java
@@ -107,7 +107,7 @@ public List findByUserId(String userId, String applicationI
if (activation.getActivationStatus() == ActivationStatus.REMOVED && activation.getDevicePublicKeyBase64() == null) { // never finished removed activations
continue;
}
- if (!Protocols.FIDO2.toString().equalsIgnoreCase(activation.getProtocol())) { // Check the protocol, just in case
+ if (!Protocols.FIDO2.toString().equals(activation.getProtocol())) { // Check the protocol, just in case
continue;
}
final AuthenticatorDetail authenticatorDetail = convert(activation, application.get());
From 50618df5997b686012ff204624fafa3b6b912274 Mon Sep 17 00:00:00 2001
From: "roman.strobl@wultra.com"
Date: Wed, 8 Nov 2023 18:27:43 +0100
Subject: [PATCH 012/146] Fix typo
---
.../powerauth-java-server/1.5.x/20230430-add-columns-fido2.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/db/changelog/changesets/powerauth-java-server/1.5.x/20230430-add-columns-fido2.xml b/docs/db/changelog/changesets/powerauth-java-server/1.5.x/20230430-add-columns-fido2.xml
index 85e5b3a14..a4203c2d3 100644
--- a/docs/db/changelog/changesets/powerauth-java-server/1.5.x/20230430-add-columns-fido2.xml
+++ b/docs/db/changelog/changesets/powerauth-java-server/1.5.x/20230430-add-columns-fido2.xml
@@ -46,7 +46,7 @@
-
+
From bdf114d7358b6470bdd36d6f879ca3b48fce10eb Mon Sep 17 00:00:00 2001
From: "roman.strobl@wultra.com"
Date: Wed, 8 Nov 2023 19:45:06 +0100
Subject: [PATCH 013/146] Check flag position within byte
---
.../serialization/AuthenticatorDataDeserializer.java | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AuthenticatorDataDeserializer.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AuthenticatorDataDeserializer.java
index 11ebfaeb3..f7eb70b3a 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AuthenticatorDataDeserializer.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AuthenticatorDataDeserializer.java
@@ -138,7 +138,10 @@ public AuthenticatorData deserialize(JsonParser jsonParser, DeserializationConte
return result;
}
- private boolean isFlagOn(byte flags, int position) {
+ private boolean isFlagOn(byte flags, int position) throws IOException {
+ if (position < 0 || position > 7) {
+ throw new IOException("Invalid position for flag: " + position);
+ }
return ((flags >> position) & 1) == 1;
}
From 20692828cbc38f71a2f62ddb232c6f7cc6a5d2e3 Mon Sep 17 00:00:00 2001
From: "roman.strobl@wultra.com"
Date: Wed, 8 Nov 2023 19:47:36 +0100
Subject: [PATCH 014/146] Avoid initialization of point with default zero-based
byte arrays used for x and y coordinates
---
.../serialization/AuthenticatorDataDeserializer.java | 7 +++++--
.../powerauth/fido2/rest/model/entity/PublicKeyObject.java | 2 +-
2 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AuthenticatorDataDeserializer.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AuthenticatorDataDeserializer.java
index f7eb70b3a..f6588489e 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AuthenticatorDataDeserializer.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AuthenticatorDataDeserializer.java
@@ -25,6 +25,7 @@
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.dataformat.cbor.databind.CBORMapper;
import com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorData;
+import com.wultra.powerauth.fido2.rest.model.entity.EllipticCurvePoint;
import com.wultra.powerauth.fido2.rest.model.entity.Flags;
import com.wultra.powerauth.fido2.rest.model.entity.PublicKeyObject;
import com.wultra.powerauth.fido2.rest.model.enumeration.CurveType;
@@ -129,8 +130,10 @@ public AuthenticatorData deserialize(JsonParser jsonParser, DeserializationConte
final byte[] xBytes = (byte[]) credentialPublicKeyMap.get("-2");
final byte[] yBytes = (byte[]) credentialPublicKeyMap.get("-3");
- publicKeyObject.getPoint().setX(xBytes);
- publicKeyObject.getPoint().setY(yBytes);
+ final EllipticCurvePoint point = new EllipticCurvePoint();
+ point.setX(xBytes);
+ point.setY(yBytes);
+ publicKeyObject.setPoint(point);
result.getAttestedCredentialData().setPublicKeyObject(publicKeyObject);
}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/PublicKeyObject.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/PublicKeyObject.java
index 58ac15f71..60e0f20d6 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/PublicKeyObject.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/PublicKeyObject.java
@@ -30,6 +30,6 @@ public class PublicKeyObject {
private SignatureAlgorithm algorithm;
private CurveType curveType;
- private EllipticCurvePoint point = new EllipticCurvePoint();
+ private EllipticCurvePoint point;
}
From d4a16c2c4ebbfa020edbb7fc95692769de781276 Mon Sep 17 00:00:00 2001
From: "roman.strobl@wultra.com"
Date: Wed, 8 Nov 2023 20:54:53 +0100
Subject: [PATCH 015/146] Remove unused dependency
---
powerauth-fido2/pom.xml | 5 -----
1 file changed, 5 deletions(-)
diff --git a/powerauth-fido2/pom.xml b/powerauth-fido2/pom.xml
index 26ed63ec5..a6297d7d9 100644
--- a/powerauth-fido2/pom.xml
+++ b/powerauth-fido2/pom.xml
@@ -50,11 +50,6 @@
springdoc-openapi-starter-webmvc-ui
2.1.0
-
- org.springframework.boot
- spring-boot-starter-tomcat
- provided
-
com.fasterxml.jackson.dataformat
From 535320680634c7cce5000e703dea48545f0310bc Mon Sep 17 00:00:00 2001
From: "roman.strobl@wultra.com"
Date: Wed, 8 Nov 2023 20:59:35 +0100
Subject: [PATCH 016/146] Fix imports
---
.../powerauth/fido2/rest/controller/AssertionController.java | 5 ++++-
.../fido2/rest/controller/RegistrationController.java | 5 ++++-
.../serialization/CollectedClientDataDeserializer.java | 2 --
.../rest/model/entity/AuthenticatorAttestationResponse.java | 1 -
.../fido2/rest/model/request/RegistrationRequest.java | 4 ----
.../rest/model/response/AssertionVerificationResponse.java | 1 -
.../fido2/rest/model/response/RegistrationResponse.java | 1 -
.../wultra/powerauth/fido2/service/RegistrationService.java | 2 +-
8 files changed, 9 insertions(+), 12 deletions(-)
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/AssertionController.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/AssertionController.java
index 0d9650988..6b8b5f573 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/AssertionController.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/AssertionController.java
@@ -31,7 +31,10 @@
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
/**
* Controller responsible for FIDO2 assertion handling.
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/RegistrationController.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/RegistrationController.java
index 40450dadb..69c4817e6 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/RegistrationController.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/RegistrationController.java
@@ -32,7 +32,10 @@
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
/**
* Controller responsible for FIDO2 authenticator registration handling.
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/CollectedClientDataDeserializer.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/CollectedClientDataDeserializer.java
index 5b53b39c1..c2eda992f 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/CollectedClientDataDeserializer.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/CollectedClientDataDeserializer.java
@@ -18,7 +18,6 @@
package com.wultra.powerauth.fido2.rest.model.converter.serialization;
-import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.DeserializationFeature;
@@ -26,7 +25,6 @@
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.wultra.powerauth.fido2.rest.model.entity.CollectedClientData;
import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.IOException;
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AuthenticatorAttestationResponse.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AuthenticatorAttestationResponse.java
index 94a4538ce..f78eb68be 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AuthenticatorAttestationResponse.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AuthenticatorAttestationResponse.java
@@ -20,7 +20,6 @@
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.wultra.powerauth.fido2.rest.model.converter.serialization.AttestationObjectDeserializer;
-import com.wultra.powerauth.fido2.rest.model.converter.serialization.Base64ToByteArrayDeserializer;
import com.wultra.powerauth.fido2.rest.model.converter.serialization.CollectedClientDataDeserializer;
import lombok.Data;
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/RegistrationRequest.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/RegistrationRequest.java
index cd63a4038..ae64d98fc 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/RegistrationRequest.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/RegistrationRequest.java
@@ -18,14 +18,10 @@
package com.wultra.powerauth.fido2.rest.model.request;
-import com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorAttestationResponse;
import com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorParameters;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
-import java.util.ArrayList;
-import java.util.List;
-
/**
* @author Petr Dvorak, petr@wultra.com
*/
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/response/AssertionVerificationResponse.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/response/AssertionVerificationResponse.java
index f80bdd017..80c101085 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/response/AssertionVerificationResponse.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/response/AssertionVerificationResponse.java
@@ -21,7 +21,6 @@
import com.wultra.security.powerauth.client.model.enumeration.ActivationStatus;
import lombok.Data;
-import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/response/RegistrationResponse.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/response/RegistrationResponse.java
index 694379991..c0015dcf5 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/response/RegistrationResponse.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/response/RegistrationResponse.java
@@ -21,7 +21,6 @@
import com.wultra.security.powerauth.client.model.enumeration.ActivationStatus;
import lombok.Data;
-import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/RegistrationService.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/RegistrationService.java
index ea8422e88..877f17a57 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/RegistrationService.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/RegistrationService.java
@@ -19,8 +19,8 @@
package com.wultra.powerauth.fido2.service;
import com.wultra.powerauth.fido2.errorhandling.Fido2AuthenticationFailedException;
-import com.wultra.powerauth.fido2.rest.model.converter.RegistrationConverter;
import com.wultra.powerauth.fido2.rest.model.converter.RegistrationChallengeConverter;
+import com.wultra.powerauth.fido2.rest.model.converter.RegistrationConverter;
import com.wultra.powerauth.fido2.rest.model.entity.*;
import com.wultra.powerauth.fido2.rest.model.request.RegistrationRequest;
import com.wultra.powerauth.fido2.rest.model.response.RegisteredAuthenticatorsResponse;
From 147da015c2689bb7e0235c20104655f0f6025804 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Petr=20Dvo=C5=99=C3=A1k?=
Date: Tue, 14 Nov 2023 12:02:10 +0100
Subject: [PATCH 017/146] Fix incorrect split character
---
.../server/service/fido2/PowerAuthRegistrationProvider.java | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthRegistrationProvider.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthRegistrationProvider.java
index da1be956d..f9be5ee22 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthRegistrationProvider.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthRegistrationProvider.java
@@ -87,7 +87,7 @@ public RegistrationChallenge provideChallengeForRegistrationChallengeValue(Strin
final Date currentTimestamp = new Date();
// Obtain just the activation code part, just in case there was a value with signature
- final String[] split = challengeValue.split("&", 1);
+ final String[] split = challengeValue.split("#", 1);
final String activationCode = split[0];
// Only allow created activations to be finished
@@ -122,7 +122,7 @@ public void revokeChallengeForRegistrationChallengeValue(String applicationId, S
}
// Obtain just the activation code part, just in case there was a value with signature
- final String[] split = challengeValue.split("&", 1);
+ final String[] split = challengeValue.split("#", 1);
final String activationCode = split[0];
final List statuses = List.of(ActivationStatus.CREATED, ActivationStatus.PENDING_COMMIT, ActivationStatus.ACTIVE, ActivationStatus.BLOCKED);
From 287baf77e5231685ad1ae16c94c7bcfbc51a46ae Mon Sep 17 00:00:00 2001
From: Roman Strobl
Date: Tue, 14 Nov 2023 22:47:16 +0100
Subject: [PATCH 018/146] Fix #1129: Add a new REST client for FIDO2 services
---
.../client/PowerAuthFido2Client.java | 198 ++++++++++++++
.../model/entity/fido2/AttestationObject.java | 38 +++
.../entity/fido2/AttestationStatement.java | 33 +++
.../entity/fido2/AttestedCredentialData.java | 33 +++
.../fido2/AuthenticatorAssertionResponse.java | 39 +++
.../AuthenticatorAttestationResponse.java | 35 +++
.../model/entity/fido2/AuthenticatorData.java | 42 +++
.../entity/fido2/AuthenticatorDetail.java | 52 ++++
.../entity/fido2/AuthenticatorParameters.java | 48 ++++
.../entity/fido2/CollectedClientData.java | 43 +++
.../entity/fido2/EllipticCurvePoint.java | 32 +++
.../client/model/entity/fido2/Flags.java | 38 +++
.../model/entity/fido2/PublicKeyObject.java | 35 +++
.../model/enumeration/fido2/CurveType.java | 29 ++
.../enumeration/fido2/SignatureAlgorithm.java | 29 ++
.../fido2/AssertionChallengeRequest.java | 44 +++
.../fido2/AssertionVerificationRequest.java | 52 ++++
.../RegisteredAuthenticatorsRequest.java | 37 +++
.../fido2/RegistrationChallengeRequest.java | 36 +++
.../request/fido2/RegistrationRequest.java | 42 +++
.../fido2/AssertionChallengeResponse.java | 41 +++
.../fido2/AssertionVerificationResponse.java | 45 +++
.../RegisteredAuthenticatorsResponse.java | 35 +++
.../fido2/RegistrationChallengeResponse.java | 34 +++
.../response/fido2/RegistrationResponse.java | 50 ++++
.../rest/controller/AssertionController.java | 8 +-
.../controller/RegistrationController.java | 2 +-
...java => AssertionVerificationRequest.java} | 2 +-
.../RegisteredAuthenticatorsRequest.java | 5 +-
.../validator/AssertionRequestValidator.java | 4 +-
.../fido2/service/AssertionService.java | 4 +-
.../rest/client/PowerAuthFido2RestClient.java | 258 ++++++++++++++++++
...PowerAuthFido2RestClientConfiguration.java | 241 ++++++++++++++++
33 files changed, 1653 insertions(+), 11 deletions(-)
create mode 100644 powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/PowerAuthFido2Client.java
create mode 100644 powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AttestationObject.java
create mode 100644 powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AttestationStatement.java
create mode 100644 powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AttestedCredentialData.java
create mode 100644 powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AuthenticatorAssertionResponse.java
create mode 100644 powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AuthenticatorAttestationResponse.java
create mode 100644 powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AuthenticatorData.java
create mode 100644 powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AuthenticatorDetail.java
create mode 100644 powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AuthenticatorParameters.java
create mode 100644 powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/CollectedClientData.java
create mode 100644 powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/EllipticCurvePoint.java
create mode 100644 powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/Flags.java
create mode 100644 powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/PublicKeyObject.java
create mode 100644 powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/enumeration/fido2/CurveType.java
create mode 100644 powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/enumeration/fido2/SignatureAlgorithm.java
create mode 100644 powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/fido2/AssertionChallengeRequest.java
create mode 100644 powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/fido2/AssertionVerificationRequest.java
create mode 100644 powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/fido2/RegisteredAuthenticatorsRequest.java
create mode 100644 powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/fido2/RegistrationChallengeRequest.java
create mode 100644 powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/fido2/RegistrationRequest.java
create mode 100644 powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/response/fido2/AssertionChallengeResponse.java
create mode 100644 powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/response/fido2/AssertionVerificationResponse.java
create mode 100644 powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/response/fido2/RegisteredAuthenticatorsResponse.java
create mode 100644 powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/response/fido2/RegistrationChallengeResponse.java
create mode 100644 powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/response/fido2/RegistrationResponse.java
rename powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/{AssertionRequest.java => AssertionVerificationRequest.java} (97%)
create mode 100644 powerauth-rest-client-spring/src/main/java/com/wultra/security/powerauth/rest/client/PowerAuthFido2RestClient.java
create mode 100644 powerauth-rest-client-spring/src/main/java/com/wultra/security/powerauth/rest/client/PowerAuthFido2RestClientConfiguration.java
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/PowerAuthFido2Client.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/PowerAuthFido2Client.java
new file mode 100644
index 000000000..22b60bbcf
--- /dev/null
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/PowerAuthFido2Client.java
@@ -0,0 +1,198 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.security.powerauth.client;
+
+import com.wultra.security.powerauth.client.model.entity.fido2.AuthenticatorAssertionResponse;
+import com.wultra.security.powerauth.client.model.entity.fido2.AuthenticatorParameters;
+import com.wultra.security.powerauth.client.model.error.PowerAuthClientException;
+import com.wultra.security.powerauth.client.model.request.fido2.*;
+import com.wultra.security.powerauth.client.model.response.fido2.*;
+import org.springframework.util.MultiValueMap;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * PowerAuth FIDO2 client interface.
+ *
+ * @author Roman Strobl, roman.strobl@wultra.com
+ */
+public interface PowerAuthFido2Client {
+
+ /**
+ * Get list of registered authenticators for a user.
+ *
+ * @param request Registered authenticator list request.
+ * @return Registered authenticator list response.
+ * @throws Exception In case REST API call fails.
+ */
+ RegisteredAuthenticatorsResponse getRegisteredAuthenticatorList(RegisteredAuthenticatorsRequest request) throws Exception;
+
+ /**
+ * Get list of registered authenticators for a user.
+ *
+ * @param request Registered authenticator list request.
+ * @param queryParams HTTP query parameters.
+ * @param httpHeaders HTTP headers.
+ * @return Registered authenticator list response.
+ * @throws Exception In case REST API call fails.
+ */
+ RegisteredAuthenticatorsResponse getRegisteredAuthenticatorList(RegisteredAuthenticatorsRequest request, MultiValueMap queryParams, MultiValueMap httpHeaders) throws Exception;
+
+ /**
+ * Get list of registered authenticators for a user.
+ *
+ * @param userId User identifier.
+ * @param applicationId Application identifier.
+ * @return Registered authenticator list response.
+ * @throws Exception In case REST API call fails.
+ */
+ RegisteredAuthenticatorsResponse getRegisteredAuthenticatorList(String userId, String applicationId) throws Exception;
+
+ /**
+ * Request a registration challenge.
+ *
+ * @param request Registration challenge request.
+ * @return Registration challenge response.
+ * @throws Exception In case REST API call fails.
+ */
+ RegistrationChallengeResponse requestRegistrationChallenge(RegistrationChallengeRequest request) throws Exception;
+
+ /**
+ * Request a registration challenge.
+ *
+ * @param request Registration challenge request.
+ * @param queryParams HTTP query parameters.
+ * @param httpHeaders HTTP headers.
+ * @return Registration challenge response.
+ * @throws Exception In case REST API call fails.
+ */
+ RegistrationChallengeResponse requestRegistrationChallenge(RegistrationChallengeRequest request, MultiValueMap queryParams, MultiValueMap httpHeaders) throws Exception;
+
+ /**
+ * Request a registration challenge.
+ *
+ * @param userId User identifier.
+ * @param applicationId Application identifier.
+ * @return Registration challenge response.
+ * @throws Exception In case REST API call fails.
+ */
+ RegistrationChallengeResponse requestRegistrationChallenge(String userId, String applicationId) throws Exception;
+
+ /**
+ * @param request Registration request.
+ * @return Registration response.
+ * @throws Exception In case REST API call fails.
+ */
+ RegistrationResponse register(RegistrationRequest request) throws Exception;
+
+ /**
+ * @param request Registration request.
+ * @param queryParams HTTP query parameters.
+ * @param httpHeaders HTTP headers.
+ * @return Registration response.
+ * @throws Exception In case REST API call fails.
+ */
+ RegistrationResponse register(RegistrationRequest request, MultiValueMap queryParams, MultiValueMap httpHeaders) throws Exception;
+
+ /**
+ * @param applicationId Application identifier.
+ * @param activationName Activation name.
+ * @param expectedChallenge Expected challenge.
+ * @param authenticatorParameters Authenticator parameters.
+ *
+ * @return Registration response.
+ * @throws Exception In case REST API call fails.
+ */
+ RegistrationResponse register(String applicationId, String activationName, String expectedChallenge, AuthenticatorParameters authenticatorParameters) throws Exception;
+
+ /**
+ * Call the assertion challenge endpoint of FIDO2 service.
+ *
+ * @param request Assertion challenge request.
+ * @return Assertion challenge response.
+ * @throws PowerAuthClientException In case REST API call fails.
+ */
+ AssertionChallengeResponse requestAssertionChallenge(AssertionChallengeRequest request) throws PowerAuthClientException;
+
+ /**
+ * Call the assertion challenge endpoint of FIDO2 service.
+ *
+ * @param request Assertion challenge request.
+ * @param queryParams HTTP query parameters.
+ * @param httpHeaders HTTP headers.
+ * @return Assertion challenge response.
+ * @throws PowerAuthClientException In case REST API call fails.
+ */
+ AssertionChallengeResponse requestAssertionChallenge(AssertionChallengeRequest request, MultiValueMap queryParams, MultiValueMap httpHeaders) throws PowerAuthClientException;
+
+ /**
+ * Call the assertion challenge endpoint of FIDO2 service.
+ *
+ * @param applicationIds Application identifiers.
+ * @param externalId External identifier.
+ * @param operationType Operation type.
+ * @param parameters Parameters.
+ * @return Assertion challenge response.
+ * @throws PowerAuthClientException In case REST API call fails.
+ */
+ AssertionChallengeResponse requestAssertionChallenge(List applicationIds, String externalId, String operationType, Map parameters) throws PowerAuthClientException;
+
+ /**
+ * Call the authentication endpoint of FIDO2 service.
+ *
+ * @param request Assertion verification request.
+ * @return Assertion verification response.
+ * @throws PowerAuthClientException In case REST API call fails.
+ */
+ AssertionVerificationResponse authenticate(AssertionVerificationRequest request) throws PowerAuthClientException;
+
+ /**
+ * Call the authentication endpoint of FIDO2 service.
+ *
+ * @param request Assertion verification request.
+ * @param queryParams HTTP query parameters.
+ * @param httpHeaders HTTP headers.
+ * @return Assertion verification response.
+ * @throws PowerAuthClientException In case REST API call fails.
+ */
+ AssertionVerificationResponse authenticate(AssertionVerificationRequest request, MultiValueMap queryParams, MultiValueMap httpHeaders) throws PowerAuthClientException;
+
+ /**
+ * Call the authentication endpoint of FIDO2 service.
+ *
+ * @param id Credential identifier.
+ * @param type Credential type.
+ * @param authenticatorAttachment Authenticator attachment.
+ * @param response Authenticator assertion response.
+ * @param applicationId Application identifier.
+ * @param relyingPartyId Relaying party identifier.
+ * @param allowedOrigins List of allowed origins.
+ * @param allowedTopOrigins List of allowed top origins.
+ * @param requiresUserVerification Whether user verification is required during authentication.
+ * @param expectedChallenge Expected challenge.
+ * @return Assertion verification response.
+ * @throws PowerAuthClientException In case REST API call fails.
+ */
+ AssertionVerificationResponse authenticate(String id, String type, String authenticatorAttachment, AuthenticatorAssertionResponse response,
+ String applicationId, String relyingPartyId, List allowedOrigins, List allowedTopOrigins,
+ boolean requiresUserVerification, String expectedChallenge) throws PowerAuthClientException;
+
+
+}
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AttestationObject.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AttestationObject.java
new file mode 100644
index 000000000..227cf24a8
--- /dev/null
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AttestationObject.java
@@ -0,0 +1,38 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.security.powerauth.client.model.entity.fido2;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import lombok.Data;
+
+/**
+ * @author Roman Strobl, roman.strobl@wultra.com
+ */
+@Data
+public class AttestationObject {
+
+ @JsonIgnore
+ private String encoded;
+
+ private String fmt;
+
+ private AuthenticatorData authData;
+ private AttestationStatement attStmt;
+
+}
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AttestationStatement.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AttestationStatement.java
new file mode 100644
index 000000000..bb85e8cb0
--- /dev/null
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AttestationStatement.java
@@ -0,0 +1,33 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.security.powerauth.client.model.entity.fido2;
+
+import com.wultra.security.powerauth.client.model.enumeration.fido2.SignatureAlgorithm;
+import lombok.Data;
+
+/**
+ * @author Roman Strobl, roman.strobl@wultra.com
+ */
+@Data
+public class AttestationStatement {
+
+ private SignatureAlgorithm algorithm;
+ private byte[] signature;
+
+}
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AttestedCredentialData.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AttestedCredentialData.java
new file mode 100644
index 000000000..f3caf2345
--- /dev/null
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AttestedCredentialData.java
@@ -0,0 +1,33 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.security.powerauth.client.model.entity.fido2;
+
+import lombok.Data;
+
+/**
+ * @author Roman Strobl, roman.strobl@wultra.com
+ */
+@Data
+public class AttestedCredentialData {
+
+ private byte[] aaguid;
+ private byte[] credentialId;
+ private PublicKeyObject publicKeyObject;
+
+}
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AuthenticatorAssertionResponse.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AuthenticatorAssertionResponse.java
new file mode 100644
index 000000000..621cd47ff
--- /dev/null
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AuthenticatorAssertionResponse.java
@@ -0,0 +1,39 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.security.powerauth.client.model.entity.fido2;
+
+import jakarta.validation.constraints.NotEmpty;
+import lombok.Data;
+
+/**
+ * @author Roman Strobl, roman.strobl@wultra.com
+ */
+@Data
+public class AuthenticatorAssertionResponse {
+
+ private CollectedClientData clientDataJSON;
+
+ private AuthenticatorData authenticatorData;
+
+ @NotEmpty
+ private byte[] signature;
+
+ private String userHandle;
+
+}
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AuthenticatorAttestationResponse.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AuthenticatorAttestationResponse.java
new file mode 100644
index 000000000..fa9f9872c
--- /dev/null
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AuthenticatorAttestationResponse.java
@@ -0,0 +1,35 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.security.powerauth.client.model.entity.fido2;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * @author Roman Strobl, roman.strobl@wultra.com
+ */
+@Data
+public class AuthenticatorAttestationResponse {
+
+ private CollectedClientData clientDataJSON;
+ private AttestationObject attestationObject;
+ private List transports;
+
+}
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AuthenticatorData.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AuthenticatorData.java
new file mode 100644
index 000000000..b68b8887a
--- /dev/null
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AuthenticatorData.java
@@ -0,0 +1,42 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.security.powerauth.client.model.entity.fido2;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.PositiveOrZero;
+import lombok.Data;
+
+/**
+ * @author Roman Strobl, roman.strobl@wultra.com
+ */
+@Data
+public class AuthenticatorData {
+
+ @NotEmpty
+ @JsonIgnore
+ private byte[] encoded;
+ @NotEmpty
+ private byte[] rpIdHash;
+ private Flags flags = new Flags();
+ @PositiveOrZero
+ private int signCount;
+ private AttestedCredentialData attestedCredentialData = new AttestedCredentialData();
+
+}
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AuthenticatorDetail.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AuthenticatorDetail.java
new file mode 100644
index 000000000..f5b52c76d
--- /dev/null
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AuthenticatorDetail.java
@@ -0,0 +1,52 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.security.powerauth.client.model.entity.fido2;
+
+import com.wultra.security.powerauth.client.model.enumeration.ActivationStatus;
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Information about a registered authenticator.
+ *
+ * @author Roman Strobl, roman.strobl@wultra.com
+ */
+@Data
+public class AuthenticatorDetail {
+
+ private String userId;
+ private String activationId;
+ private String applicationId;
+ private String activationName;
+ private String externalId;
+ private ActivationStatus activationStatus;
+ private Map extras;
+ private String platform;
+ private String deviceInfo;
+ private String blockedReason;
+ private long failedAttempts;
+ private long maxFailedAttempts;
+ private List applicationRoles = new ArrayList<>();
+ private List activationFlags = new ArrayList<>();
+ private byte[] publicKeyBytes;
+
+}
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AuthenticatorParameters.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AuthenticatorParameters.java
new file mode 100644
index 000000000..4ca5d1d1e
--- /dev/null
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AuthenticatorParameters.java
@@ -0,0 +1,48 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.security.powerauth.client.model.entity.fido2;
+
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Data class representing the parameters obtained from the authenticator registration.
+ *
+ * @author Roman Strobl, roman.strobl@wulra.com
+ */
+@Data
+public class AuthenticatorParameters {
+
+ @NotBlank
+ private String id;
+ @NotBlank
+ private String type;
+ @NotBlank
+ private String authenticatorAttachment;
+ private AuthenticatorAttestationResponse response = new AuthenticatorAttestationResponse();
+ @NotBlank
+ private String relyingPartyId;
+ private List allowedOrigins = new ArrayList<>();
+ private List allowedTopOrigins = new ArrayList<>();
+ private boolean requiresUserVerification;
+
+}
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/CollectedClientData.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/CollectedClientData.java
new file mode 100644
index 000000000..7a89bfa0b
--- /dev/null
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/CollectedClientData.java
@@ -0,0 +1,43 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.security.powerauth.client.model.entity.fido2;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotEmpty;
+import lombok.Data;
+
+/**
+ * @author Roman Strobl, roman.strobl@wultra.com
+ */
+@Data
+public class CollectedClientData {
+
+ @NotEmpty
+ @JsonIgnore
+ private String encoded;
+ @NotBlank
+ private String type;
+ @NotEmpty
+ private String challenge;
+ @NotBlank
+ private String origin;
+ private String topOrigin;
+ private boolean crossOrigin;
+}
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/EllipticCurvePoint.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/EllipticCurvePoint.java
new file mode 100644
index 000000000..8a402c8d4
--- /dev/null
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/EllipticCurvePoint.java
@@ -0,0 +1,32 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.security.powerauth.client.model.entity.fido2;
+
+import lombok.Data;
+
+/**
+ * @author Roman Strobl, roman.strobl@wultra.com
+ */
+@Data
+public class EllipticCurvePoint {
+
+ private byte[] x;
+ private byte[] y;
+
+}
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/Flags.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/Flags.java
new file mode 100644
index 000000000..9e57170d2
--- /dev/null
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/Flags.java
@@ -0,0 +1,38 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.security.powerauth.client.model.entity.fido2;
+
+import lombok.Data;
+
+/**
+ * @author Roman Strobl, roman.strobl@wultra.com
+ */
+@Data
+public class Flags {
+
+ private boolean userPresent;
+ private boolean reservedBit2;
+ private boolean userVerified;
+ private boolean backupEligible;
+ private boolean backupState;
+ private boolean reservedBit6;
+ private boolean attestedCredentialsIncluded;
+ private boolean extensionDataIncluded;
+
+}
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/PublicKeyObject.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/PublicKeyObject.java
new file mode 100644
index 000000000..08d453d15
--- /dev/null
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/PublicKeyObject.java
@@ -0,0 +1,35 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.security.powerauth.client.model.entity.fido2;
+
+import com.wultra.security.powerauth.client.model.enumeration.fido2.CurveType;
+import com.wultra.security.powerauth.client.model.enumeration.fido2.SignatureAlgorithm;
+import lombok.Data;
+
+/**
+ * @author Roman Strobl, roman.strobl@wultra.com
+ */
+@Data
+public class PublicKeyObject {
+
+ private SignatureAlgorithm algorithm;
+ private CurveType curveType;
+ private EllipticCurvePoint point;
+
+}
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/enumeration/fido2/CurveType.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/enumeration/fido2/CurveType.java
new file mode 100644
index 000000000..9418febb4
--- /dev/null
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/enumeration/fido2/CurveType.java
@@ -0,0 +1,29 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.security.powerauth.client.model.enumeration.fido2;
+
+/**
+ * @author Roman Strobl, roman.strobl@wultra.com
+ */
+public enum CurveType {
+
+ P256,
+ UNKNOWN
+
+}
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/enumeration/fido2/SignatureAlgorithm.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/enumeration/fido2/SignatureAlgorithm.java
new file mode 100644
index 000000000..f405e0af0
--- /dev/null
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/enumeration/fido2/SignatureAlgorithm.java
@@ -0,0 +1,29 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.security.powerauth.client.model.enumeration.fido2;
+
+/**
+ * @author Roman Strobl, roman.strobl@wultra.com
+ */
+public enum SignatureAlgorithm {
+
+ ES256,
+ UNKNOWN
+
+}
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/fido2/AssertionChallengeRequest.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/fido2/AssertionChallengeRequest.java
new file mode 100644
index 000000000..78cf11325
--- /dev/null
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/fido2/AssertionChallengeRequest.java
@@ -0,0 +1,44 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.security.powerauth.client.model.request.fido2;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotEmpty;
+import lombok.Data;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Request for obtaining assertion challenge.
+ *
+ * @author Roman Strobl, roman.strobl@wultra.com
+ */
+@Data
+public class AssertionChallengeRequest {
+
+ @NotEmpty
+ private List<@NotBlank String> applicationIds;
+ private String externalId;
+ @NotBlank
+ private String operationType;
+ private Map parameters = new HashMap<>();
+
+}
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/fido2/AssertionVerificationRequest.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/fido2/AssertionVerificationRequest.java
new file mode 100644
index 000000000..6357face8
--- /dev/null
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/fido2/AssertionVerificationRequest.java
@@ -0,0 +1,52 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.security.powerauth.client.model.request.fido2;
+
+import com.wultra.security.powerauth.client.model.entity.fido2.AuthenticatorAssertionResponse;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Request for validating an assertion.
+ *
+ * @author Roman Strobl, roman.strobl@wultra.com
+ */
+@Data
+public class AssertionVerificationRequest {
+
+ @NotBlank
+ private String id;
+ @NotBlank
+ private String type;
+ @NotBlank
+ private String authenticatorAttachment;
+ private AuthenticatorAssertionResponse response = new AuthenticatorAssertionResponse();
+ @NotBlank
+ private String applicationId;
+ @NotBlank
+ private String relyingPartyId;
+ private List allowedOrigins = new ArrayList<>();
+ private List allowedTopOrigins = new ArrayList<>();
+ private boolean requiresUserVerification;
+ private String expectedChallenge;
+
+}
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/fido2/RegisteredAuthenticatorsRequest.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/fido2/RegisteredAuthenticatorsRequest.java
new file mode 100644
index 000000000..3a6a52d55
--- /dev/null
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/fido2/RegisteredAuthenticatorsRequest.java
@@ -0,0 +1,37 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.security.powerauth.client.model.request.fido2;
+
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+/**
+ * Request for obtaining list of registered authenticators for given user.
+ *
+ * @author Roman Strobl, roman.strobl@wultra.com
+ */
+@Data
+public class RegisteredAuthenticatorsRequest {
+
+ @NotBlank
+ private String userId;
+ @NotBlank
+ private String applicationId;
+
+}
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/fido2/RegistrationChallengeRequest.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/fido2/RegistrationChallengeRequest.java
new file mode 100644
index 000000000..b23bb8b29
--- /dev/null
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/fido2/RegistrationChallengeRequest.java
@@ -0,0 +1,36 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.security.powerauth.client.model.request.fido2;
+
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+/**
+ * Request object for registration challenge.
+ *
+ * @author Roman Strobl, roman.strobl@wultra.com
+ */
+@Data
+public class RegistrationChallengeRequest {
+
+ @NotBlank
+ private String userId;
+ @NotBlank
+ private String applicationId;
+}
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/fido2/RegistrationRequest.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/fido2/RegistrationRequest.java
new file mode 100644
index 000000000..f4e52d4de
--- /dev/null
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/fido2/RegistrationRequest.java
@@ -0,0 +1,42 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.security.powerauth.client.model.request.fido2;
+
+import com.wultra.security.powerauth.client.model.entity.fido2.AuthenticatorParameters;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+/**
+ * @author Roman Strobl, roman.strobl@wultra.com
+ */
+@Data
+public class RegistrationRequest {
+
+ // Relying party parameters
+ @NotBlank
+ private String applicationId;
+ @NotBlank
+ private String activationName;
+ private String expectedChallenge;
+
+ // Authenticator parameters
+ private AuthenticatorParameters authenticatorParameters;
+
+
+}
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/response/fido2/AssertionChallengeResponse.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/response/fido2/AssertionChallengeResponse.java
new file mode 100644
index 000000000..30442cd95
--- /dev/null
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/response/fido2/AssertionChallengeResponse.java
@@ -0,0 +1,41 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2021 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 com.wultra.security.powerauth.client.model.response.fido2;
+
+import lombok.Data;
+import lombok.ToString;
+
+import java.util.List;
+
+/**
+ * Response for obtaining assertion challenge.
+ *
+ * @author Roman Strobl, roman.strobl@wultra.com
+ */
+@Data
+public class AssertionChallengeResponse {
+
+ private List applicationIds;
+ @ToString.Exclude
+ private String challenge;
+ private String userId;
+ private Long failedAttempts;
+ private Long maxFailedAttempts;
+
+}
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/response/fido2/AssertionVerificationResponse.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/response/fido2/AssertionVerificationResponse.java
new file mode 100644
index 000000000..d005ee2c5
--- /dev/null
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/response/fido2/AssertionVerificationResponse.java
@@ -0,0 +1,45 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.security.powerauth.client.model.response.fido2;
+
+import com.wultra.security.powerauth.client.model.enumeration.ActivationStatus;
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Response for the assertion verification.
+ *
+ * @author Roman Strobl, roman.strobl@wultra.com
+ */
+@Data
+public class AssertionVerificationResponse {
+
+ private boolean assertionValid;
+ private String userId;
+ private String activationId;
+ private String applicationId;
+ private ActivationStatus activationStatus;
+ private String blockedReason;
+ private long remainingAttempts;
+ private List applicationRoles = new ArrayList<>();
+ private List activationFlags = new ArrayList<>();
+
+}
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/response/fido2/RegisteredAuthenticatorsResponse.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/response/fido2/RegisteredAuthenticatorsResponse.java
new file mode 100644
index 000000000..5e869c23d
--- /dev/null
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/response/fido2/RegisteredAuthenticatorsResponse.java
@@ -0,0 +1,35 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.security.powerauth.client.model.response.fido2;
+
+import com.wultra.security.powerauth.client.model.entity.fido2.AuthenticatorDetail;
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Roman Strobl, roman.strobl@wultra.com
+ */
+@Data
+public class RegisteredAuthenticatorsResponse {
+
+ private List authenticators = new ArrayList<>();
+
+}
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/response/fido2/RegistrationChallengeResponse.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/response/fido2/RegistrationChallengeResponse.java
new file mode 100644
index 000000000..e8b80d07f
--- /dev/null
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/response/fido2/RegistrationChallengeResponse.java
@@ -0,0 +1,34 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.security.powerauth.client.model.response.fido2;
+
+import lombok.Data;
+
+/**
+ * @author Roman Strobl, roman.strobl@wultra.com
+ */
+@Data
+public class RegistrationChallengeResponse {
+
+ private String activationId;
+ private String applicationId;
+ private String challenge;
+ private String userId;
+
+}
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/response/fido2/RegistrationResponse.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/response/fido2/RegistrationResponse.java
new file mode 100644
index 000000000..d09e0040c
--- /dev/null
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/response/fido2/RegistrationResponse.java
@@ -0,0 +1,50 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.security.powerauth.client.model.response.fido2;
+
+import com.wultra.security.powerauth.client.model.enumeration.ActivationStatus;
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Roman Strobl, roman.strobl@wultra.com
+ */
+@Data
+public class RegistrationResponse {
+
+ private String userId;
+ private String activationId;
+ private String applicationId;
+ private String externalId;
+ private String activationName;
+ private ActivationStatus activationStatus;
+ private Map extras;
+ private String platform;
+ private String deviceInfo;
+ private String blockedReason;
+ private long failedAttempts;
+ private long maxFailedAttempts;
+ private List applicationRoles = new ArrayList<>();
+ private List activationFlags = new ArrayList<>();
+ private byte[] publicKeyBytes;
+
+}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/AssertionController.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/AssertionController.java
index 6b8b5f573..b2eff2c08 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/AssertionController.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/AssertionController.java
@@ -19,7 +19,7 @@
import com.wultra.powerauth.fido2.errorhandling.Fido2AuthenticationFailedException;
import com.wultra.powerauth.fido2.rest.model.request.AssertionChallengeRequest;
-import com.wultra.powerauth.fido2.rest.model.request.AssertionRequest;
+import com.wultra.powerauth.fido2.rest.model.request.AssertionVerificationRequest;
import com.wultra.powerauth.fido2.rest.model.response.AssertionChallengeResponse;
import com.wultra.powerauth.fido2.rest.model.response.AssertionVerificationResponse;
import com.wultra.powerauth.fido2.rest.model.validator.AssertionRequestValidator;
@@ -43,7 +43,7 @@
*/
@Validated
@RestController
-@RequestMapping("assertions")
+@RequestMapping("fido2/assertions")
@Slf4j
@Tag(name = "FIDO2 Assertions Controller")
public class AssertionController {
@@ -65,8 +65,8 @@ public ObjectResponse requestAssertionChallenge(@Val
}
@PostMapping
- public ObjectResponse authenticate(@Valid @RequestBody ObjectRequest request) throws Fido2AuthenticationFailedException {
- final AssertionRequest requestObject = request.getRequestObject();
+ public ObjectResponse authenticate(@Valid @RequestBody ObjectRequest request) throws Fido2AuthenticationFailedException {
+ final AssertionVerificationRequest requestObject = request.getRequestObject();
final String error = assertionRequestValidator.validate(requestObject);
if (error != null) {
throw new Fido2AuthenticationFailedException(error);
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/RegistrationController.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/RegistrationController.java
index 69c4817e6..a219a0870 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/RegistrationController.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/RegistrationController.java
@@ -44,7 +44,7 @@
*/
@Validated
@RestController
-@RequestMapping("registrations")
+@RequestMapping("fido2/registrations")
@Slf4j
@Tag(name = "FIDO2 Registration Controller")
public class RegistrationController {
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/AssertionRequest.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/AssertionVerificationRequest.java
similarity index 97%
rename from powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/AssertionRequest.java
rename to powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/AssertionVerificationRequest.java
index a67618d2b..bea3f8e2c 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/AssertionRequest.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/AssertionVerificationRequest.java
@@ -29,7 +29,7 @@
* @author Petr Dvorak, petr@wultra.com
*/
@Data
-public class AssertionRequest {
+public class AssertionVerificationRequest {
@NotBlank
private String id;
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/RegisteredAuthenticatorsRequest.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/RegisteredAuthenticatorsRequest.java
index 3a742b537..6a2f83a3d 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/RegisteredAuthenticatorsRequest.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/RegisteredAuthenticatorsRequest.java
@@ -18,6 +18,7 @@
package com.wultra.powerauth.fido2.rest.model.request;
+import jakarta.validation.constraints.NotBlank;
import lombok.Data;
/**
@@ -28,7 +29,9 @@
@Data
public class RegisteredAuthenticatorsRequest {
- private String applicationId;
+ @NotBlank
private String userId;
+ @NotBlank
+ private String applicationId;
}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/validator/AssertionRequestValidator.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/validator/AssertionRequestValidator.java
index 5334ba035..b235b4ab7 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/validator/AssertionRequestValidator.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/validator/AssertionRequestValidator.java
@@ -20,7 +20,7 @@
import com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorData;
import com.wultra.powerauth.fido2.rest.model.entity.CollectedClientData;
-import com.wultra.powerauth.fido2.rest.model.request.AssertionRequest;
+import com.wultra.powerauth.fido2.rest.model.request.AssertionVerificationRequest;
import io.getlime.security.powerauth.crypto.lib.util.Hash;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@@ -37,7 +37,7 @@
@Slf4j
public class AssertionRequestValidator {
- public String validate(AssertionRequest request) {
+ public String validate(AssertionVerificationRequest request) {
if (request == null || request.getResponse() == null
|| request.getResponse().getClientDataJSON() == null
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/AssertionService.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/AssertionService.java
index eab7b88c1..05a766464 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/AssertionService.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/AssertionService.java
@@ -25,7 +25,7 @@
import com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorAssertionResponse;
import com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorDetail;
import com.wultra.powerauth.fido2.rest.model.request.AssertionChallengeRequest;
-import com.wultra.powerauth.fido2.rest.model.request.AssertionRequest;
+import com.wultra.powerauth.fido2.rest.model.request.AssertionVerificationRequest;
import com.wultra.powerauth.fido2.rest.model.response.AssertionChallengeResponse;
import com.wultra.powerauth.fido2.rest.model.response.AssertionVerificationResponse;
import com.wultra.powerauth.fido2.service.provider.AssertionProvider;
@@ -82,7 +82,7 @@ public AssertionChallengeResponse requestAssertionChallenge(AssertionChallengeRe
* @param request Request with assertion.
* @throws Fido2AuthenticationFailedException In case authentication fails.
*/
- public AssertionVerificationResponse authenticate(AssertionRequest request) throws Fido2AuthenticationFailedException {
+ public AssertionVerificationResponse authenticate(AssertionVerificationRequest request) throws Fido2AuthenticationFailedException {
try {
final AuthenticatorAssertionResponse response = request.getResponse();
final String applicationId = request.getApplicationId();
diff --git a/powerauth-rest-client-spring/src/main/java/com/wultra/security/powerauth/rest/client/PowerAuthFido2RestClient.java b/powerauth-rest-client-spring/src/main/java/com/wultra/security/powerauth/rest/client/PowerAuthFido2RestClient.java
new file mode 100644
index 000000000..553f2441d
--- /dev/null
+++ b/powerauth-rest-client-spring/src/main/java/com/wultra/security/powerauth/rest/client/PowerAuthFido2RestClient.java
@@ -0,0 +1,258 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.security.powerauth.rest.client;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.wultra.core.rest.client.base.DefaultRestClient;
+import com.wultra.core.rest.client.base.RestClient;
+import com.wultra.core.rest.client.base.RestClientException;
+import com.wultra.security.powerauth.client.PowerAuthFido2Client;
+import com.wultra.security.powerauth.client.model.entity.fido2.AuthenticatorAssertionResponse;
+import com.wultra.security.powerauth.client.model.entity.fido2.AuthenticatorParameters;
+import com.wultra.security.powerauth.client.model.error.PowerAuthClientException;
+import com.wultra.security.powerauth.client.model.error.PowerAuthError;
+import com.wultra.security.powerauth.client.model.request.fido2.*;
+import com.wultra.security.powerauth.client.model.response.InitActivationResponse;
+import com.wultra.security.powerauth.client.model.response.fido2.*;
+import io.getlime.core.rest.model.base.request.ObjectRequest;
+import io.getlime.core.rest.model.base.response.ObjectResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpStatus;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Class implementing a PowerAuth REST client.
+ *
+ * @author Roman Strobl, roman.strobl@wultra.com
+ *
+ */
+public class PowerAuthFido2RestClient implements PowerAuthFido2Client {
+
+ private static final Logger logger = LoggerFactory.getLogger(PowerAuthFido2RestClient.class);
+
+ private static final String PA_REST_FIDO2_PREFIX = "/fido2";
+ private static final MultiValueMap EMPTY_MULTI_MAP = new LinkedMultiValueMap<>();
+
+ private final RestClient restClient;
+ private final ObjectMapper objectMapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+
+ /**
+ * PowerAuth REST client constructor.
+ *
+ * @param baseUrl BASE URL of REST endpoints.
+ */
+ public PowerAuthFido2RestClient(String baseUrl) throws PowerAuthClientException {
+ this(baseUrl, new PowerAuthRestClientConfiguration());
+ }
+
+ /**
+ * PowerAuth REST client constructor.
+ *
+ * @param baseUrl Base URL of REST endpoints.
+ */
+ public PowerAuthFido2RestClient(String baseUrl, PowerAuthRestClientConfiguration config) throws PowerAuthClientException {
+ final DefaultRestClient.Builder builder = DefaultRestClient.builder().baseUrl(baseUrl)
+ .acceptInvalidCertificate(config.getAcceptInvalidSslCertificate())
+ .connectionTimeout(config.getConnectTimeout())
+ .maxInMemorySize(config.getMaxMemorySize());
+ if (config.isProxyEnabled()) {
+ final DefaultRestClient.ProxyBuilder proxyBuilder = builder.proxy().host(config.getProxyHost()).port(config.getProxyPort());
+ if (config.getProxyUsername() != null) {
+ proxyBuilder.username(config.getProxyUsername()).password(config.getProxyPassword());
+ }
+ proxyBuilder.build();
+ }
+ if (config.getPowerAuthClientToken() != null) {
+ builder.httpBasicAuth().username(config.getPowerAuthClientToken()).password(config.getPowerAuthClientSecret()).build();
+ }
+ if (config.getDefaultHttpHeaders() != null) {
+ builder.defaultHttpHeaders(config.getDefaultHttpHeaders());
+ }
+ if (config.getFilter() != null) {
+ builder.filter(config.getFilter());
+ }
+ try {
+ restClient = builder.build();
+ } catch (RestClientException ex) {
+ throw new PowerAuthClientException("REST client initialization failed, error: " + ex.getMessage(), ex);
+ }
+ }
+
+ /**
+ * Call the PowerAuth FIDO2 API.
+ *
+ * @param path Path of the endpoint.
+ * @param request Request object.
+ * @param queryParams HTTP query parameters.
+ * @param httpHeaders HTTP headers.
+ * @param responseType Response type.
+ * @return Response.
+ */
+ private T callFido2RestApi(String path, Object request, MultiValueMap queryParams, MultiValueMap httpHeaders, Class responseType) throws PowerAuthClientException {
+ final ObjectRequest> objectRequest = new ObjectRequest<>(request);
+ try {
+ final ObjectResponse objectResponse = restClient.postObject(PA_REST_FIDO2_PREFIX + path, objectRequest, queryParams, httpHeaders, responseType);
+ return objectResponse.getResponseObject();
+ } catch (RestClientException ex) {
+ if (ex.getStatusCode() == null) {
+ // Logging for network errors when port is closed
+ logger.warn("PowerAuth FIDO2 service is not accessible, error: {}", ex.getMessage());
+ logger.debug(ex.getMessage(), ex);
+ } else if (ex.getStatusCode() == HttpStatus.NOT_FOUND) {
+ // Logging for 404 errors
+ logger.warn("PowerAuth FIDO2 service is not available, error: {}", ex.getMessage());
+ logger.debug(ex.getMessage(), ex);
+ } else if (ex.getStatusCode() == HttpStatus.BAD_REQUEST) {
+ // Error handling for PowerAuth errors
+ handleBadRequestError(ex);
+ }
+ // Error handling for generic HTTP errors
+ throw new PowerAuthClientException(ex.getMessage(), ex);
+ }
+ }
+
+ /**
+ * Handle the HTTP response with BAD_REQUEST status code.
+ * @param ex Exception which captured the error.
+ * @throws PowerAuthClientException PowerAuth client exception.
+ */
+ private void handleBadRequestError(RestClientException ex) throws PowerAuthClientException {
+ // Try to parse exception into PowerAuthError model class
+ try {
+ final TypeReference> typeReference = new TypeReference<>(){};
+ final ObjectResponse error = objectMapper.readValue(ex.getResponse(), typeReference);
+ if (error == null || error.getResponseObject() == null) {
+ throw new PowerAuthClientException("Invalid response object");
+ }
+ throw new PowerAuthClientException(error.getResponseObject().getMessage(), ex, error.getResponseObject());
+ } catch (IOException ex2) {
+ // Parsing failed, return a regular error
+ throw new PowerAuthClientException(ex.getMessage(), ex);
+ }
+ }
+
+ @Override
+ public RegisteredAuthenticatorsResponse getRegisteredAuthenticatorList(RegisteredAuthenticatorsRequest request) throws Exception {
+ return callFido2RestApi("/registrations/list", request, EMPTY_MULTI_MAP, EMPTY_MULTI_MAP, RegisteredAuthenticatorsResponse.class);
+ }
+
+ @Override
+ public RegisteredAuthenticatorsResponse getRegisteredAuthenticatorList(RegisteredAuthenticatorsRequest request, MultiValueMap queryParams, MultiValueMap httpHeaders) throws Exception {
+ return callFido2RestApi("/registrations/list", request, queryParams, httpHeaders, RegisteredAuthenticatorsResponse.class);
+ }
+
+ @Override
+ public RegisteredAuthenticatorsResponse getRegisteredAuthenticatorList(String userId, String applicationId) throws Exception {
+ final RegisteredAuthenticatorsRequest request = new RegisteredAuthenticatorsRequest();
+ request.setUserId(userId);
+ request.setApplicationId(applicationId);
+ return callFido2RestApi("/registrations/list", request, EMPTY_MULTI_MAP, EMPTY_MULTI_MAP, RegisteredAuthenticatorsResponse.class);
+ }
+
+ @Override
+ public RegistrationChallengeResponse requestRegistrationChallenge(RegistrationChallengeRequest request) throws Exception {
+ return callFido2RestApi("/registrations/challenge", request, EMPTY_MULTI_MAP, EMPTY_MULTI_MAP, RegistrationChallengeResponse.class);
+ }
+
+ @Override
+ public RegistrationChallengeResponse requestRegistrationChallenge(RegistrationChallengeRequest request, MultiValueMap queryParams, MultiValueMap httpHeaders) throws Exception {
+ return callFido2RestApi("/registrations/challenge", request, queryParams, httpHeaders, RegistrationChallengeResponse.class);
+ }
+
+ @Override
+ public RegistrationChallengeResponse requestRegistrationChallenge(String userId, String applicationId) throws Exception {
+ final RegistrationChallengeRequest request = new RegistrationChallengeRequest();
+ request.setUserId(userId);
+ request.setApplicationId(applicationId);
+ return callFido2RestApi("/registrations/challenge", request, EMPTY_MULTI_MAP, EMPTY_MULTI_MAP, RegistrationChallengeResponse.class);
+ }
+
+ @Override
+ public RegistrationResponse register(RegistrationRequest request) throws Exception {
+ return callFido2RestApi("/registrations", request, EMPTY_MULTI_MAP, EMPTY_MULTI_MAP, RegistrationResponse.class);
+ }
+
+ @Override
+ public RegistrationResponse register(RegistrationRequest request, MultiValueMap queryParams, MultiValueMap httpHeaders) throws Exception {
+ return callFido2RestApi("/registrations", request, queryParams, httpHeaders, RegistrationResponse.class);
+ }
+
+ @Override
+ public RegistrationResponse register(String applicationId, String activationName, String expectedChallenge, AuthenticatorParameters authenticatorParameters) throws Exception {
+ RegistrationRequest request = new RegistrationRequest();
+ request.setApplicationId(applicationId);
+ request.setActivationName(activationName);
+ request.setExpectedChallenge(expectedChallenge);
+ request.setAuthenticatorParameters(authenticatorParameters);
+ return callFido2RestApi("/registrations", request, EMPTY_MULTI_MAP, EMPTY_MULTI_MAP, RegistrationResponse.class);
+ }
+
+ @Override
+ public AssertionChallengeResponse requestAssertionChallenge(AssertionChallengeRequest request) throws PowerAuthClientException {
+ return callFido2RestApi("/assertions/challenge", request, EMPTY_MULTI_MAP, EMPTY_MULTI_MAP, AssertionChallengeResponse.class);
+ }
+
+ @Override
+ public AssertionChallengeResponse requestAssertionChallenge(AssertionChallengeRequest request, MultiValueMap queryParams, MultiValueMap httpHeaders) throws PowerAuthClientException {
+ return callFido2RestApi("/assertions/challenge", request, queryParams, httpHeaders, AssertionChallengeResponse.class);
+ }
+
+ @Override
+ public AssertionChallengeResponse requestAssertionChallenge(List applicationIds, String externalId, String operationType, Map parameters) throws PowerAuthClientException {
+ final AssertionChallengeRequest request = new AssertionChallengeRequest();
+ return callFido2RestApi("/assertions/challenge", request, EMPTY_MULTI_MAP, EMPTY_MULTI_MAP, AssertionChallengeResponse.class);
+ }
+
+ @Override
+ public AssertionVerificationResponse authenticate(AssertionVerificationRequest request) throws PowerAuthClientException {
+ return callFido2RestApi("/assertions", request, EMPTY_MULTI_MAP, EMPTY_MULTI_MAP, AssertionVerificationResponse.class);
+ }
+
+ @Override
+ public AssertionVerificationResponse authenticate(AssertionVerificationRequest request, MultiValueMap queryParams, MultiValueMap httpHeaders) throws PowerAuthClientException {
+ return callFido2RestApi("/assertions", request, queryParams, httpHeaders, AssertionVerificationResponse.class);
+ }
+
+ @Override
+ public AssertionVerificationResponse authenticate(String id, String type, String authenticatorAttachment, AuthenticatorAssertionResponse response,
+ String applicationId, String relyingPartyId, List allowedOrigins, List allowedTopOrigins,
+ boolean requiresUserVerification, String expectedChallenge) throws PowerAuthClientException {
+ final AssertionVerificationRequest request = new AssertionVerificationRequest();
+ request.setId(id);
+ request.setType(type);
+ request.setAuthenticatorAttachment(authenticatorAttachment);
+ request.setResponse(response);
+ request.setApplicationId(applicationId);
+ request.setRelyingPartyId(relyingPartyId);
+ request.setAllowedOrigins(allowedOrigins);
+ request.setAllowedTopOrigins(allowedTopOrigins);
+ request.setRequiresUserVerification(requiresUserVerification);
+ request.setExpectedChallenge(expectedChallenge);
+ return callFido2RestApi("/assertions", request, EMPTY_MULTI_MAP, EMPTY_MULTI_MAP, AssertionVerificationResponse.class);
+ }
+
+}
diff --git a/powerauth-rest-client-spring/src/main/java/com/wultra/security/powerauth/rest/client/PowerAuthFido2RestClientConfiguration.java b/powerauth-rest-client-spring/src/main/java/com/wultra/security/powerauth/rest/client/PowerAuthFido2RestClientConfiguration.java
new file mode 100644
index 000000000..668bbf4b3
--- /dev/null
+++ b/powerauth-rest-client-spring/src/main/java/com/wultra/security/powerauth/rest/client/PowerAuthFido2RestClientConfiguration.java
@@ -0,0 +1,241 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.security.powerauth.rest.client;
+
+import org.springframework.http.HttpHeaders;
+import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
+
+import java.time.Duration;
+
+/**
+ * Configuration of PowerAuth FIDO2 REST client.
+ *
+ * @author Roman Strobl, roman.strobl@wultra.com
+ *
+ */
+public class PowerAuthFido2RestClientConfiguration {
+
+ // Use 1 MB as default maximum memory size
+ private int maxMemorySize = 1024 * 1024;
+ // Use 5 seconds as default connect timeout
+ private Duration connectTimeout = Duration.ofMillis(5000);
+ private boolean proxyEnabled = false;
+ private String proxyHost;
+ private int proxyPort;
+ private String proxyUsername;
+ private String proxyPassword;
+ private String powerAuthClientToken;
+ private String powerAuthClientSecret;
+ private boolean acceptInvalidSslCertificate;
+ private HttpHeaders defaultHttpHeaders;
+ private ExchangeFilterFunction filter;
+
+ /**
+ * Get maximum memory size for HTTP requests in bytes.
+ * @return Maximum memory size for HTTP requests in bytes.
+ */
+ public int getMaxMemorySize() {
+ return maxMemorySize;
+ }
+
+ /**
+ * Set maximum memory size for HTTP requests in bytes.
+ * @param maxMemorySize Maximum memory size for HTTP requests in bytes.
+ */
+ public void setMaxMemorySize(int maxMemorySize) {
+ this.maxMemorySize = maxMemorySize;
+ }
+
+ /**
+ * Get connection timeout as a Duration.
+ * @return Connection timeout as a Duration.
+ */
+ public Duration getConnectTimeout() {
+ return connectTimeout;
+ }
+
+ /**
+ * Set connection timeout as a Duration.
+ * @param connectTimeout Connection timeout as a Duration.
+ */
+ public void setConnectTimeout(Duration connectTimeout) {
+ this.connectTimeout = connectTimeout;
+ }
+
+ /**
+ * Get whether HTTP proxy is enabled.
+ * @return Whether HTTP proxy is enabled.
+ */
+ public boolean isProxyEnabled() {
+ return proxyEnabled;
+ }
+
+ /**
+ * Set whether HTTP proxy is enabled.
+ * @param proxyEnabled Whether HTTP proxy is enabled.
+ */
+ public void setProxyEnabled(boolean proxyEnabled) {
+ this.proxyEnabled = proxyEnabled;
+ }
+
+ /**
+ * Get proxy host.
+ * @return Proxy host.
+ */
+ public String getProxyHost() {
+ return proxyHost;
+ }
+
+ /**
+ * Set proxy host.
+ * @param proxyHost Proxy host.
+ */
+ public void setProxyHost(String proxyHost) {
+ this.proxyHost = proxyHost;
+ }
+
+ /**
+ * Get proxy port.
+ * @return Proxy port.
+ */
+ public int getProxyPort() {
+ return proxyPort;
+ }
+
+ /**
+ * Set proxy port.
+ * @param proxyPort Proxy port.
+ */
+ public void setProxyPort(int proxyPort) {
+ this.proxyPort = proxyPort;
+ }
+
+ /**
+ * Get proxy username.
+ * @return Proxy username.
+ */
+ public String getProxyUsername() {
+ return proxyUsername;
+ }
+
+ /**
+ * Set proxy username.
+ * @param proxyUsername Proxy username.s
+ */
+ public void setProxyUsername(String proxyUsername) {
+ this.proxyUsername = proxyUsername;
+ }
+
+ /**
+ * Get proxy password.
+ * @return Proxy password.
+ */
+ public String getProxyPassword() {
+ return proxyPassword;
+ }
+
+ /**
+ * Set proxy password.
+ * @param proxyPassword Proxy password.
+ */
+ public void setProxyPassword(String proxyPassword) {
+ this.proxyPassword = proxyPassword;
+ }
+
+ /**
+ * Get HTTP basic authentication username.
+ * @return HTTP basic authentication username.
+ */
+ public String getPowerAuthClientToken() {
+ return powerAuthClientToken;
+ }
+
+ /**
+ * Set HTTP basic authentication username.
+ * @param powerAuthClientToken HTTP basic authentication username.
+ */
+ public void setPowerAuthClientToken(String powerAuthClientToken) {
+ this.powerAuthClientToken = powerAuthClientToken;
+ }
+
+ /**
+ * Get HTTP basic authentication password.
+ * @return HTTP basic authentication password.
+ */
+ public String getPowerAuthClientSecret() {
+ return powerAuthClientSecret;
+ }
+
+ /**
+ * Set HTTP basic authentication password.
+ * @param powerAuthClientSecret HTTP basic authentication password.
+ */
+ public void setPowerAuthClientSecret(String powerAuthClientSecret) {
+ this.powerAuthClientSecret = powerAuthClientSecret;
+ }
+
+ /**
+ * Get whether SSL certificate errors are ignored.
+ * @return Whether SSL certificate errors are ignored.
+ */
+ public boolean getAcceptInvalidSslCertificate() {
+ return acceptInvalidSslCertificate;
+ }
+
+ /**
+ * Set whether SSL certificate errors are ignored.
+ * @param acceptInvalidSslCertificate Whether SSL certificate errors are ignored.
+ */
+ public void setAcceptInvalidSslCertificate(boolean acceptInvalidSslCertificate) {
+ this.acceptInvalidSslCertificate = acceptInvalidSslCertificate;
+ }
+
+ /**
+ * Get default HTTP headers.
+ * @return Default HTTP headers.
+ */
+ public HttpHeaders getDefaultHttpHeaders() {
+ return defaultHttpHeaders;
+ }
+
+ /**
+ * Set default HTTP headers.
+ * @param defaultHttpHeaders Default HTTP headers.
+ */
+ public void setDefaultHttpHeaders(HttpHeaders defaultHttpHeaders) {
+ this.defaultHttpHeaders = defaultHttpHeaders;
+ }
+
+ /**
+ * Get exchange filter function.
+ * @return Exchange filter function.
+ */
+ public ExchangeFilterFunction getFilter() {
+ return filter;
+ }
+
+ /**
+ * Set exchange filter function.
+ * @param filter Exchange filter function.
+ */
+ public void setFilter(ExchangeFilterFunction filter) {
+ this.filter = filter;
+ }
+
+}
From 971c6611978c9eaa8ae7ba4e4ea26e816dbed0ca Mon Sep 17 00:00:00 2001
From: Roman Strobl
Date: Wed, 15 Nov 2023 14:24:25 +0100
Subject: [PATCH 019/146] Fix issues found in pull request review
---
.../client/PowerAuthFido2Client.java | 36 +++++++++----------
.../fido2/RegistrationChallengeResponse.java | 2 ++
.../rest/client/PowerAuthFido2RestClient.java | 18 +++++-----
3 files changed, 29 insertions(+), 27 deletions(-)
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/PowerAuthFido2Client.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/PowerAuthFido2Client.java
index 22b60bbcf..8406c2f6f 100644
--- a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/PowerAuthFido2Client.java
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/PowerAuthFido2Client.java
@@ -40,9 +40,9 @@ public interface PowerAuthFido2Client {
*
* @param request Registered authenticator list request.
* @return Registered authenticator list response.
- * @throws Exception In case REST API call fails.
+ * @throws PowerAuthClientException In case REST API call fails.
*/
- RegisteredAuthenticatorsResponse getRegisteredAuthenticatorList(RegisteredAuthenticatorsRequest request) throws Exception;
+ RegisteredAuthenticatorsResponse getRegisteredAuthenticatorList(RegisteredAuthenticatorsRequest request) throws PowerAuthClientException;
/**
* Get list of registered authenticators for a user.
@@ -51,9 +51,9 @@ public interface PowerAuthFido2Client {
* @param queryParams HTTP query parameters.
* @param httpHeaders HTTP headers.
* @return Registered authenticator list response.
- * @throws Exception In case REST API call fails.
+ * @throws PowerAuthClientException In case REST API call fails.
*/
- RegisteredAuthenticatorsResponse getRegisteredAuthenticatorList(RegisteredAuthenticatorsRequest request, MultiValueMap queryParams, MultiValueMap httpHeaders) throws Exception;
+ RegisteredAuthenticatorsResponse getRegisteredAuthenticatorList(RegisteredAuthenticatorsRequest request, MultiValueMap queryParams, MultiValueMap httpHeaders) throws PowerAuthClientException;
/**
* Get list of registered authenticators for a user.
@@ -61,18 +61,18 @@ public interface PowerAuthFido2Client {
* @param userId User identifier.
* @param applicationId Application identifier.
* @return Registered authenticator list response.
- * @throws Exception In case REST API call fails.
+ * @throws PowerAuthClientException In case REST API call fails.
*/
- RegisteredAuthenticatorsResponse getRegisteredAuthenticatorList(String userId, String applicationId) throws Exception;
+ RegisteredAuthenticatorsResponse getRegisteredAuthenticatorList(String userId, String applicationId) throws PowerAuthClientException;
/**
* Request a registration challenge.
*
* @param request Registration challenge request.
* @return Registration challenge response.
- * @throws Exception In case REST API call fails.
+ * @throws PowerAuthClientException In case REST API call fails.
*/
- RegistrationChallengeResponse requestRegistrationChallenge(RegistrationChallengeRequest request) throws Exception;
+ RegistrationChallengeResponse requestRegistrationChallenge(RegistrationChallengeRequest request) throws PowerAuthClientException;
/**
* Request a registration challenge.
@@ -81,9 +81,9 @@ public interface PowerAuthFido2Client {
* @param queryParams HTTP query parameters.
* @param httpHeaders HTTP headers.
* @return Registration challenge response.
- * @throws Exception In case REST API call fails.
+ * @throws PowerAuthClientException In case REST API call fails.
*/
- RegistrationChallengeResponse requestRegistrationChallenge(RegistrationChallengeRequest request, MultiValueMap queryParams, MultiValueMap httpHeaders) throws Exception;
+ RegistrationChallengeResponse requestRegistrationChallenge(RegistrationChallengeRequest request, MultiValueMap queryParams, MultiValueMap httpHeaders) throws PowerAuthClientException;
/**
* Request a registration challenge.
@@ -91,25 +91,25 @@ public interface PowerAuthFido2Client {
* @param userId User identifier.
* @param applicationId Application identifier.
* @return Registration challenge response.
- * @throws Exception In case REST API call fails.
+ * @throws PowerAuthClientException In case REST API call fails.
*/
- RegistrationChallengeResponse requestRegistrationChallenge(String userId, String applicationId) throws Exception;
+ RegistrationChallengeResponse requestRegistrationChallenge(String userId, String applicationId) throws PowerAuthClientException;
/**
* @param request Registration request.
* @return Registration response.
- * @throws Exception In case REST API call fails.
+ * @throws PowerAuthClientException In case REST API call fails.
*/
- RegistrationResponse register(RegistrationRequest request) throws Exception;
+ RegistrationResponse register(RegistrationRequest request) throws PowerAuthClientException;
/**
* @param request Registration request.
* @param queryParams HTTP query parameters.
* @param httpHeaders HTTP headers.
* @return Registration response.
- * @throws Exception In case REST API call fails.
+ * @throws PowerAuthClientException In case REST API call fails.
*/
- RegistrationResponse register(RegistrationRequest request, MultiValueMap queryParams, MultiValueMap httpHeaders) throws Exception;
+ RegistrationResponse register(RegistrationRequest request, MultiValueMap queryParams, MultiValueMap httpHeaders) throws PowerAuthClientException;
/**
* @param applicationId Application identifier.
@@ -118,9 +118,9 @@ public interface PowerAuthFido2Client {
* @param authenticatorParameters Authenticator parameters.
*
* @return Registration response.
- * @throws Exception In case REST API call fails.
+ * @throws PowerAuthClientException In case REST API call fails.
*/
- RegistrationResponse register(String applicationId, String activationName, String expectedChallenge, AuthenticatorParameters authenticatorParameters) throws Exception;
+ RegistrationResponse register(String applicationId, String activationName, String expectedChallenge, AuthenticatorParameters authenticatorParameters) throws PowerAuthClientException;
/**
* Call the assertion challenge endpoint of FIDO2 service.
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/response/fido2/RegistrationChallengeResponse.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/response/fido2/RegistrationChallengeResponse.java
index e8b80d07f..d8dcfa55f 100644
--- a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/response/fido2/RegistrationChallengeResponse.java
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/response/fido2/RegistrationChallengeResponse.java
@@ -19,6 +19,7 @@
package com.wultra.security.powerauth.client.model.response.fido2;
import lombok.Data;
+import lombok.ToString;
/**
* @author Roman Strobl, roman.strobl@wultra.com
@@ -28,6 +29,7 @@ public class RegistrationChallengeResponse {
private String activationId;
private String applicationId;
+ @ToString.Exclude
private String challenge;
private String userId;
diff --git a/powerauth-rest-client-spring/src/main/java/com/wultra/security/powerauth/rest/client/PowerAuthFido2RestClient.java b/powerauth-rest-client-spring/src/main/java/com/wultra/security/powerauth/rest/client/PowerAuthFido2RestClient.java
index 553f2441d..3fda9e846 100644
--- a/powerauth-rest-client-spring/src/main/java/com/wultra/security/powerauth/rest/client/PowerAuthFido2RestClient.java
+++ b/powerauth-rest-client-spring/src/main/java/com/wultra/security/powerauth/rest/client/PowerAuthFido2RestClient.java
@@ -156,17 +156,17 @@ private void handleBadRequestError(RestClientException ex) throws PowerAuthClien
}
@Override
- public RegisteredAuthenticatorsResponse getRegisteredAuthenticatorList(RegisteredAuthenticatorsRequest request) throws Exception {
+ public RegisteredAuthenticatorsResponse getRegisteredAuthenticatorList(RegisteredAuthenticatorsRequest request) throws PowerAuthClientException {
return callFido2RestApi("/registrations/list", request, EMPTY_MULTI_MAP, EMPTY_MULTI_MAP, RegisteredAuthenticatorsResponse.class);
}
@Override
- public RegisteredAuthenticatorsResponse getRegisteredAuthenticatorList(RegisteredAuthenticatorsRequest request, MultiValueMap queryParams, MultiValueMap httpHeaders) throws Exception {
+ public RegisteredAuthenticatorsResponse getRegisteredAuthenticatorList(RegisteredAuthenticatorsRequest request, MultiValueMap queryParams, MultiValueMap httpHeaders) throws PowerAuthClientException {
return callFido2RestApi("/registrations/list", request, queryParams, httpHeaders, RegisteredAuthenticatorsResponse.class);
}
@Override
- public RegisteredAuthenticatorsResponse getRegisteredAuthenticatorList(String userId, String applicationId) throws Exception {
+ public RegisteredAuthenticatorsResponse getRegisteredAuthenticatorList(String userId, String applicationId) throws PowerAuthClientException {
final RegisteredAuthenticatorsRequest request = new RegisteredAuthenticatorsRequest();
request.setUserId(userId);
request.setApplicationId(applicationId);
@@ -174,17 +174,17 @@ public RegisteredAuthenticatorsResponse getRegisteredAuthenticatorList(String us
}
@Override
- public RegistrationChallengeResponse requestRegistrationChallenge(RegistrationChallengeRequest request) throws Exception {
+ public RegistrationChallengeResponse requestRegistrationChallenge(RegistrationChallengeRequest request) throws PowerAuthClientException {
return callFido2RestApi("/registrations/challenge", request, EMPTY_MULTI_MAP, EMPTY_MULTI_MAP, RegistrationChallengeResponse.class);
}
@Override
- public RegistrationChallengeResponse requestRegistrationChallenge(RegistrationChallengeRequest request, MultiValueMap queryParams, MultiValueMap httpHeaders) throws Exception {
+ public RegistrationChallengeResponse requestRegistrationChallenge(RegistrationChallengeRequest request, MultiValueMap queryParams, MultiValueMap httpHeaders) throws PowerAuthClientException {
return callFido2RestApi("/registrations/challenge", request, queryParams, httpHeaders, RegistrationChallengeResponse.class);
}
@Override
- public RegistrationChallengeResponse requestRegistrationChallenge(String userId, String applicationId) throws Exception {
+ public RegistrationChallengeResponse requestRegistrationChallenge(String userId, String applicationId) throws PowerAuthClientException {
final RegistrationChallengeRequest request = new RegistrationChallengeRequest();
request.setUserId(userId);
request.setApplicationId(applicationId);
@@ -192,17 +192,17 @@ public RegistrationChallengeResponse requestRegistrationChallenge(String userId,
}
@Override
- public RegistrationResponse register(RegistrationRequest request) throws Exception {
+ public RegistrationResponse register(RegistrationRequest request) throws PowerAuthClientException {
return callFido2RestApi("/registrations", request, EMPTY_MULTI_MAP, EMPTY_MULTI_MAP, RegistrationResponse.class);
}
@Override
- public RegistrationResponse register(RegistrationRequest request, MultiValueMap queryParams, MultiValueMap httpHeaders) throws Exception {
+ public RegistrationResponse register(RegistrationRequest request, MultiValueMap queryParams, MultiValueMap httpHeaders) throws PowerAuthClientException {
return callFido2RestApi("/registrations", request, queryParams, httpHeaders, RegistrationResponse.class);
}
@Override
- public RegistrationResponse register(String applicationId, String activationName, String expectedChallenge, AuthenticatorParameters authenticatorParameters) throws Exception {
+ public RegistrationResponse register(String applicationId, String activationName, String expectedChallenge, AuthenticatorParameters authenticatorParameters) throws PowerAuthClientException {
RegistrationRequest request = new RegistrationRequest();
request.setApplicationId(applicationId);
request.setActivationName(activationName);
From 9dcec5444f6a32d671cf51c155dd78d48e6f2905 Mon Sep 17 00:00:00 2001
From: Roman Strobl
Date: Wed, 15 Nov 2023 16:38:21 +0100
Subject: [PATCH 020/146] Exclude sensitive parameters from toString, add
JavaDoc
---
.../security/powerauth/client/PowerAuthFido2Client.java | 6 ++++++
.../client/model/entity/fido2/AttestationStatement.java | 2 ++
.../model/entity/fido2/AuthenticatorAssertionResponse.java | 2 ++
.../client/model/entity/fido2/EllipticCurvePoint.java | 3 +++
4 files changed, 13 insertions(+)
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/PowerAuthFido2Client.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/PowerAuthFido2Client.java
index 8406c2f6f..cd182ee64 100644
--- a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/PowerAuthFido2Client.java
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/PowerAuthFido2Client.java
@@ -96,6 +96,8 @@ public interface PowerAuthFido2Client {
RegistrationChallengeResponse requestRegistrationChallenge(String userId, String applicationId) throws PowerAuthClientException;
/**
+ * Register a FIDO2 authenticator.
+ *
* @param request Registration request.
* @return Registration response.
* @throws PowerAuthClientException In case REST API call fails.
@@ -103,6 +105,8 @@ public interface PowerAuthFido2Client {
RegistrationResponse register(RegistrationRequest request) throws PowerAuthClientException;
/**
+ * Register a FIDO2 authenticator.
+ *
* @param request Registration request.
* @param queryParams HTTP query parameters.
* @param httpHeaders HTTP headers.
@@ -112,6 +116,8 @@ public interface PowerAuthFido2Client {
RegistrationResponse register(RegistrationRequest request, MultiValueMap queryParams, MultiValueMap httpHeaders) throws PowerAuthClientException;
/**
+ * Register a FIDO2 authenticator.
+ *
* @param applicationId Application identifier.
* @param activationName Activation name.
* @param expectedChallenge Expected challenge.
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AttestationStatement.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AttestationStatement.java
index bb85e8cb0..15f93698d 100644
--- a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AttestationStatement.java
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AttestationStatement.java
@@ -20,6 +20,7 @@
import com.wultra.security.powerauth.client.model.enumeration.fido2.SignatureAlgorithm;
import lombok.Data;
+import lombok.ToString;
/**
* @author Roman Strobl, roman.strobl@wultra.com
@@ -28,6 +29,7 @@
public class AttestationStatement {
private SignatureAlgorithm algorithm;
+ @ToString.Exclude
private byte[] signature;
}
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AuthenticatorAssertionResponse.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AuthenticatorAssertionResponse.java
index 621cd47ff..304bbf4de 100644
--- a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AuthenticatorAssertionResponse.java
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AuthenticatorAssertionResponse.java
@@ -20,6 +20,7 @@
import jakarta.validation.constraints.NotEmpty;
import lombok.Data;
+import lombok.ToString;
/**
* @author Roman Strobl, roman.strobl@wultra.com
@@ -32,6 +33,7 @@ public class AuthenticatorAssertionResponse {
private AuthenticatorData authenticatorData;
@NotEmpty
+ @ToString.Exclude
private byte[] signature;
private String userHandle;
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/EllipticCurvePoint.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/EllipticCurvePoint.java
index 8a402c8d4..cd6453293 100644
--- a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/EllipticCurvePoint.java
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/EllipticCurvePoint.java
@@ -19,6 +19,7 @@
package com.wultra.security.powerauth.client.model.entity.fido2;
import lombok.Data;
+import lombok.ToString;
/**
* @author Roman Strobl, roman.strobl@wultra.com
@@ -26,7 +27,9 @@
@Data
public class EllipticCurvePoint {
+ @ToString.Exclude
private byte[] x;
+ @ToString.Exclude
private byte[] y;
}
From b0536139ab9647fa694e438cfadf3434a7a82850 Mon Sep 17 00:00:00 2001
From: Roman Strobl
Date: Sun, 19 Nov 2023 18:31:25 +0100
Subject: [PATCH 021/146] Fix #1142: FIDO2: Add handling for compressed /
uncompressed EC keys
---
.../serialization/AuthenticatorDataDeserializer.java | 7 +++++++
.../fido2/rest/model/entity/PublicKeyObject.java | 2 ++
.../powerauth/fido2/rest/model/enumeration/ECKeyType.java | 8 ++++----
3 files changed, 13 insertions(+), 4 deletions(-)
rename powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/enumeration/fido2/CurveType.java => powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/enumeration/ECKeyType.java (87%)
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AuthenticatorDataDeserializer.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AuthenticatorDataDeserializer.java
index f6588489e..4d07b2e8a 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AuthenticatorDataDeserializer.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AuthenticatorDataDeserializer.java
@@ -29,6 +29,7 @@
import com.wultra.powerauth.fido2.rest.model.entity.Flags;
import com.wultra.powerauth.fido2.rest.model.entity.PublicKeyObject;
import com.wultra.powerauth.fido2.rest.model.enumeration.CurveType;
+import com.wultra.powerauth.fido2.rest.model.enumeration.ECKeyType;
import com.wultra.powerauth.fido2.rest.model.enumeration.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@@ -127,6 +128,12 @@ public AuthenticatorData deserialize(JsonParser jsonParser, DeserializationConte
} else {
throw new RuntimeException("Unsupported curve type: " + curveType);
}
+ final Integer keyType = (Integer) credentialPublicKeyMap.get("1");
+ if (keyType != null && 2 == keyType) {
+ publicKeyObject.setKeyType(ECKeyType.UNCOMPRESSED);
+ } else {
+ throw new RuntimeException("Unsupported key type: " + keyType);
+ }
final byte[] xBytes = (byte[]) credentialPublicKeyMap.get("-2");
final byte[] yBytes = (byte[]) credentialPublicKeyMap.get("-3");
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/PublicKeyObject.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/PublicKeyObject.java
index 60e0f20d6..c34a075e2 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/PublicKeyObject.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/PublicKeyObject.java
@@ -19,6 +19,7 @@
package com.wultra.powerauth.fido2.rest.model.entity;
import com.wultra.powerauth.fido2.rest.model.enumeration.CurveType;
+import com.wultra.powerauth.fido2.rest.model.enumeration.ECKeyType;
import com.wultra.powerauth.fido2.rest.model.enumeration.SignatureAlgorithm;
import lombok.Data;
@@ -30,6 +31,7 @@ public class PublicKeyObject {
private SignatureAlgorithm algorithm;
private CurveType curveType;
+ private ECKeyType keyType;
private EllipticCurvePoint point;
}
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/enumeration/fido2/CurveType.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/enumeration/ECKeyType.java
similarity index 87%
rename from powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/enumeration/fido2/CurveType.java
rename to powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/enumeration/ECKeyType.java
index 9418febb4..a4f8851ea 100644
--- a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/enumeration/fido2/CurveType.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/enumeration/ECKeyType.java
@@ -16,14 +16,14 @@
* along with this program. If not, see .
*/
-package com.wultra.security.powerauth.client.model.enumeration.fido2;
+package com.wultra.powerauth.fido2.rest.model.enumeration;
/**
* @author Roman Strobl, roman.strobl@wultra.com
*/
-public enum CurveType {
+public enum ECKeyType {
- P256,
- UNKNOWN
+ COMPRESSED,
+ UNCOMPRESSED
}
From 81d66340cf9fbba67154db1a4043af27da3e67cd Mon Sep 17 00:00:00 2001
From: Roman Strobl
Date: Sun, 19 Nov 2023 18:32:41 +0100
Subject: [PATCH 022/146] Simplify request model classes
---
.../model/entity/fido2/AttestationObject.java | 38 ----------------
.../entity/fido2/AttestationStatement.java | 35 ---------------
.../entity/fido2/AttestedCredentialData.java | 33 --------------
.../fido2/AuthenticatorAssertionResponse.java | 4 +-
.../AuthenticatorAttestationResponse.java | 4 +-
.../model/entity/fido2/AuthenticatorData.java | 42 ------------------
.../entity/fido2/CollectedClientData.java | 43 -------------------
.../entity/fido2/EllipticCurvePoint.java | 35 ---------------
.../client/model/entity/fido2/Flags.java | 38 ----------------
.../model/entity/fido2/PublicKeyObject.java | 35 ---------------
.../enumeration/fido2/SignatureAlgorithm.java | 29 -------------
11 files changed, 4 insertions(+), 332 deletions(-)
delete mode 100644 powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AttestationObject.java
delete mode 100644 powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AttestationStatement.java
delete mode 100644 powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AttestedCredentialData.java
delete mode 100644 powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AuthenticatorData.java
delete mode 100644 powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/CollectedClientData.java
delete mode 100644 powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/EllipticCurvePoint.java
delete mode 100644 powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/Flags.java
delete mode 100644 powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/PublicKeyObject.java
delete mode 100644 powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/enumeration/fido2/SignatureAlgorithm.java
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AttestationObject.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AttestationObject.java
deleted file mode 100644
index 227cf24a8..000000000
--- a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AttestationObject.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * PowerAuth Server and related software components
- * Copyright (C) 2023 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 com.wultra.security.powerauth.client.model.entity.fido2;
-
-import com.fasterxml.jackson.annotation.JsonIgnore;
-import lombok.Data;
-
-/**
- * @author Roman Strobl, roman.strobl@wultra.com
- */
-@Data
-public class AttestationObject {
-
- @JsonIgnore
- private String encoded;
-
- private String fmt;
-
- private AuthenticatorData authData;
- private AttestationStatement attStmt;
-
-}
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AttestationStatement.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AttestationStatement.java
deleted file mode 100644
index 15f93698d..000000000
--- a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AttestationStatement.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * PowerAuth Server and related software components
- * Copyright (C) 2023 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 com.wultra.security.powerauth.client.model.entity.fido2;
-
-import com.wultra.security.powerauth.client.model.enumeration.fido2.SignatureAlgorithm;
-import lombok.Data;
-import lombok.ToString;
-
-/**
- * @author Roman Strobl, roman.strobl@wultra.com
- */
-@Data
-public class AttestationStatement {
-
- private SignatureAlgorithm algorithm;
- @ToString.Exclude
- private byte[] signature;
-
-}
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AttestedCredentialData.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AttestedCredentialData.java
deleted file mode 100644
index f3caf2345..000000000
--- a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AttestedCredentialData.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * PowerAuth Server and related software components
- * Copyright (C) 2023 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 com.wultra.security.powerauth.client.model.entity.fido2;
-
-import lombok.Data;
-
-/**
- * @author Roman Strobl, roman.strobl@wultra.com
- */
-@Data
-public class AttestedCredentialData {
-
- private byte[] aaguid;
- private byte[] credentialId;
- private PublicKeyObject publicKeyObject;
-
-}
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AuthenticatorAssertionResponse.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AuthenticatorAssertionResponse.java
index 304bbf4de..7e7116c5d 100644
--- a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AuthenticatorAssertionResponse.java
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AuthenticatorAssertionResponse.java
@@ -28,9 +28,9 @@
@Data
public class AuthenticatorAssertionResponse {
- private CollectedClientData clientDataJSON;
+ private String clientDataJSON;
- private AuthenticatorData authenticatorData;
+ private String authenticatorData;
@NotEmpty
@ToString.Exclude
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AuthenticatorAttestationResponse.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AuthenticatorAttestationResponse.java
index fa9f9872c..cca330fc7 100644
--- a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AuthenticatorAttestationResponse.java
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AuthenticatorAttestationResponse.java
@@ -28,8 +28,8 @@
@Data
public class AuthenticatorAttestationResponse {
- private CollectedClientData clientDataJSON;
- private AttestationObject attestationObject;
+ private String clientDataJSON;
+ private String attestationObject;
private List transports;
}
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AuthenticatorData.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AuthenticatorData.java
deleted file mode 100644
index b68b8887a..000000000
--- a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AuthenticatorData.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * PowerAuth Server and related software components
- * Copyright (C) 2023 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 com.wultra.security.powerauth.client.model.entity.fido2;
-
-import com.fasterxml.jackson.annotation.JsonIgnore;
-import jakarta.validation.constraints.NotEmpty;
-import jakarta.validation.constraints.PositiveOrZero;
-import lombok.Data;
-
-/**
- * @author Roman Strobl, roman.strobl@wultra.com
- */
-@Data
-public class AuthenticatorData {
-
- @NotEmpty
- @JsonIgnore
- private byte[] encoded;
- @NotEmpty
- private byte[] rpIdHash;
- private Flags flags = new Flags();
- @PositiveOrZero
- private int signCount;
- private AttestedCredentialData attestedCredentialData = new AttestedCredentialData();
-
-}
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/CollectedClientData.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/CollectedClientData.java
deleted file mode 100644
index 7a89bfa0b..000000000
--- a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/CollectedClientData.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * PowerAuth Server and related software components
- * Copyright (C) 2023 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 com.wultra.security.powerauth.client.model.entity.fido2;
-
-import com.fasterxml.jackson.annotation.JsonIgnore;
-import jakarta.validation.constraints.NotBlank;
-import jakarta.validation.constraints.NotEmpty;
-import lombok.Data;
-
-/**
- * @author Roman Strobl, roman.strobl@wultra.com
- */
-@Data
-public class CollectedClientData {
-
- @NotEmpty
- @JsonIgnore
- private String encoded;
- @NotBlank
- private String type;
- @NotEmpty
- private String challenge;
- @NotBlank
- private String origin;
- private String topOrigin;
- private boolean crossOrigin;
-}
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/EllipticCurvePoint.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/EllipticCurvePoint.java
deleted file mode 100644
index cd6453293..000000000
--- a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/EllipticCurvePoint.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * PowerAuth Server and related software components
- * Copyright (C) 2023 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 com.wultra.security.powerauth.client.model.entity.fido2;
-
-import lombok.Data;
-import lombok.ToString;
-
-/**
- * @author Roman Strobl, roman.strobl@wultra.com
- */
-@Data
-public class EllipticCurvePoint {
-
- @ToString.Exclude
- private byte[] x;
- @ToString.Exclude
- private byte[] y;
-
-}
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/Flags.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/Flags.java
deleted file mode 100644
index 9e57170d2..000000000
--- a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/Flags.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * PowerAuth Server and related software components
- * Copyright (C) 2023 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 com.wultra.security.powerauth.client.model.entity.fido2;
-
-import lombok.Data;
-
-/**
- * @author Roman Strobl, roman.strobl@wultra.com
- */
-@Data
-public class Flags {
-
- private boolean userPresent;
- private boolean reservedBit2;
- private boolean userVerified;
- private boolean backupEligible;
- private boolean backupState;
- private boolean reservedBit6;
- private boolean attestedCredentialsIncluded;
- private boolean extensionDataIncluded;
-
-}
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/PublicKeyObject.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/PublicKeyObject.java
deleted file mode 100644
index 08d453d15..000000000
--- a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/PublicKeyObject.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * PowerAuth Server and related software components
- * Copyright (C) 2023 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 com.wultra.security.powerauth.client.model.entity.fido2;
-
-import com.wultra.security.powerauth.client.model.enumeration.fido2.CurveType;
-import com.wultra.security.powerauth.client.model.enumeration.fido2.SignatureAlgorithm;
-import lombok.Data;
-
-/**
- * @author Roman Strobl, roman.strobl@wultra.com
- */
-@Data
-public class PublicKeyObject {
-
- private SignatureAlgorithm algorithm;
- private CurveType curveType;
- private EllipticCurvePoint point;
-
-}
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/enumeration/fido2/SignatureAlgorithm.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/enumeration/fido2/SignatureAlgorithm.java
deleted file mode 100644
index f405e0af0..000000000
--- a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/enumeration/fido2/SignatureAlgorithm.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * PowerAuth Server and related software components
- * Copyright (C) 2023 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 com.wultra.security.powerauth.client.model.enumeration.fido2;
-
-/**
- * @author Roman Strobl, roman.strobl@wultra.com
- */
-public enum SignatureAlgorithm {
-
- ES256,
- UNKNOWN
-
-}
From e7f172a6ce3a3716bafbd9446f347ad17ecfa6cb Mon Sep 17 00:00:00 2001
From: Roman Strobl
Date: Sun, 19 Nov 2023 18:34:58 +0100
Subject: [PATCH 023/146] Add validation annotations
---
.../model/entity/fido2/AuthenticatorAssertionResponse.java | 3 +++
.../model/entity/fido2/AuthenticatorAttestationResponse.java | 3 +++
2 files changed, 6 insertions(+)
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AuthenticatorAssertionResponse.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AuthenticatorAssertionResponse.java
index 7e7116c5d..5d0e7e8cf 100644
--- a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AuthenticatorAssertionResponse.java
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AuthenticatorAssertionResponse.java
@@ -18,6 +18,7 @@
package com.wultra.security.powerauth.client.model.entity.fido2;
+import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import lombok.Data;
import lombok.ToString;
@@ -28,8 +29,10 @@
@Data
public class AuthenticatorAssertionResponse {
+ @NotBlank
private String clientDataJSON;
+ @NotBlank
private String authenticatorData;
@NotEmpty
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AuthenticatorAttestationResponse.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AuthenticatorAttestationResponse.java
index cca330fc7..b2dd754e7 100644
--- a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AuthenticatorAttestationResponse.java
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/fido2/AuthenticatorAttestationResponse.java
@@ -18,6 +18,7 @@
package com.wultra.security.powerauth.client.model.entity.fido2;
+import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import java.util.List;
@@ -28,7 +29,9 @@
@Data
public class AuthenticatorAttestationResponse {
+ @NotBlank
private String clientDataJSON;
+ @NotBlank
private String attestationObject;
private List transports;
From 4eef368621b426cef2fbd3759e0bd6ecb1d5375b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Roman=20=C5=A0trobl?=
Date: Mon, 27 Nov 2023 07:17:27 +0100
Subject: [PATCH 024/146] Fix #1152: FIDO2: Invalid timestamp check for
operation claim (#1153)
* Fix #1152: FIDO2: Invalid timestamp check for operation claim
* Update code comments
---
.../service/behavior/tasks/OperationServiceBehavior.java | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/OperationServiceBehavior.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/OperationServiceBehavior.java
index 832e86fcf..32121e37e 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/OperationServiceBehavior.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/OperationServiceBehavior.java
@@ -696,8 +696,9 @@ private OperationEntity claimOperation(OperationEntity source, String userId, Da
// If a user accessing the operation is specified in the query, either claim the operation to that user,
// or check if the user is already granted to be able to access the operation.
if (userId != null) {
+ // Only pending, non-expired operations should be claimable.
if (OperationStatusDo.PENDING.equals(source.getStatus())
- && source.getTimestampExpires().before(currentTimestamp)) {
+ && source.getTimestampExpires().after(currentTimestamp)) {
final String operationId = source.getId();
final String expectedUserId = source.getUserId();
if (expectedUserId == null) {
@@ -714,7 +715,7 @@ private OperationEntity claimOperation(OperationEntity source, String userId, Da
}
private OperationEntity expireOperation(OperationEntity source, Date currentTimestamp) {
- // Operation is still pending and timestamp is after the expiration.
+ // Pending operations expiring before current timestamp should be marked as expired.
if (OperationStatusDo.PENDING.equals(source.getStatus())
&& source.getTimestampExpires().before(currentTimestamp)) {
logger.info("Operation {} expired.", source.getId());
From 18cf674ee7cc2064c2851e9bdea69bb260ca3c6b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Roman=20=C5=A0trobl?=
Date: Mon, 27 Nov 2023 07:19:36 +0100
Subject: [PATCH 025/146] Fix #1140: Implement tests for FIDO2 REST API (#1149)
---
pom.xml | 3 +
.../converter/RegistrationConverter.java | 5 +-
.../AuthenticatorDataDeserializer.java | 6 +-
.../{EllipticCurvePoint.java => ECPoint.java} | 2 +-
.../rest/model/entity/PublicKeyObject.java | 2 +-
.../RegistrationRequestValidator.java | 27 +
.../fido2/service/RegistrationService.java | 2 +-
.../service/provider/CryptographyService.java | 7 +-
powerauth-java-server/pom.xml | 6 +
.../fido2/PowerAuthAssertionProvider.java | 3 +
.../fido2/PowerAuthCryptographyService.java | 10 +-
.../SelfAttestedPackedAuthenticator.java | 54 ++
.../fido2/Fido2AuthenticatorTest.java | 463 ++++++++++++++++++
13 files changed, 570 insertions(+), 20 deletions(-)
rename powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/{EllipticCurvePoint.java => ECPoint.java} (96%)
create mode 100644 powerauth-java-server/src/test/java/com/webauthn4j/test/authenticator/webauthn/SelfAttestedPackedAuthenticator.java
create mode 100644 powerauth-java-server/src/test/java/com/wultra/powerauth/fido2/Fido2AuthenticatorTest.java
diff --git a/pom.xml b/pom.xml
index c4eab21a1..c9fbc4e72 100644
--- a/pom.xml
+++ b/pom.xml
@@ -102,6 +102,9 @@
1.11.0
7.4
+
+
+ 0.21.8.RELEASE
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/RegistrationConverter.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/RegistrationConverter.java
index 338f461fc..2915d8ac3 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/RegistrationConverter.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/RegistrationConverter.java
@@ -72,11 +72,8 @@ private Map convertExtras(RegistrationRequest requestObject) thr
final AuthenticatorParameters authenticatorParameters = requestObject.getAuthenticatorParameters();
final Map params = new HashMap<>();
params.put("relyingPartyId", authenticatorParameters.getRelyingPartyId());
- params.put("allowedOrigins", authenticatorParameters.getAllowedOrigins());
- params.put("allowedTopOrigins", authenticatorParameters.getAllowedTopOrigins());
- params.put("transports", authenticatorParameters.getResponse().getTransports());
params.put("authenticatorAttachment", authenticatorParameters.getAuthenticatorAttachment());
- params.put("attestationStatement", authenticatorParameters.getResponse().getAttestationObject());
+ params.put("credentialId", authenticatorParameters.getResponse().getAttestationObject().getAuthData().getAttestedCredentialData().getCredentialId());
params.put("origin", authenticatorParameters.getResponse().getClientDataJSON().getOrigin());
params.put("topOrigin", authenticatorParameters.getResponse().getClientDataJSON().getTopOrigin());
params.put("isCrossOrigin", authenticatorParameters.getResponse().getClientDataJSON().isCrossOrigin());
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AuthenticatorDataDeserializer.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AuthenticatorDataDeserializer.java
index 4d07b2e8a..f942c7398 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AuthenticatorDataDeserializer.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AuthenticatorDataDeserializer.java
@@ -25,7 +25,7 @@
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.dataformat.cbor.databind.CBORMapper;
import com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorData;
-import com.wultra.powerauth.fido2.rest.model.entity.EllipticCurvePoint;
+import com.wultra.powerauth.fido2.rest.model.entity.ECPoint;
import com.wultra.powerauth.fido2.rest.model.entity.Flags;
import com.wultra.powerauth.fido2.rest.model.entity.PublicKeyObject;
import com.wultra.powerauth.fido2.rest.model.enumeration.CurveType;
@@ -59,7 +59,7 @@ private AuthenticatorDataDeserializer(Class> vc) {
}
@Override
- public AuthenticatorData deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException {
+ public AuthenticatorData deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
final AuthenticatorData result = new AuthenticatorData();
// Serialize Auth Data
@@ -137,7 +137,7 @@ public AuthenticatorData deserialize(JsonParser jsonParser, DeserializationConte
final byte[] xBytes = (byte[]) credentialPublicKeyMap.get("-2");
final byte[] yBytes = (byte[]) credentialPublicKeyMap.get("-3");
- final EllipticCurvePoint point = new EllipticCurvePoint();
+ final ECPoint point = new ECPoint();
point.setX(xBytes);
point.setY(yBytes);
publicKeyObject.setPoint(point);
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/EllipticCurvePoint.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/ECPoint.java
similarity index 96%
rename from powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/EllipticCurvePoint.java
rename to powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/ECPoint.java
index 798bd5e03..fdae77c50 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/EllipticCurvePoint.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/ECPoint.java
@@ -24,7 +24,7 @@
* @author Petr Dvorak, petr@wultra.com
*/
@Data
-public class EllipticCurvePoint {
+public class ECPoint {
private byte[] x;
private byte[] y;
}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/PublicKeyObject.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/PublicKeyObject.java
index c34a075e2..47dbbf5da 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/PublicKeyObject.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/PublicKeyObject.java
@@ -32,6 +32,6 @@ public class PublicKeyObject {
private SignatureAlgorithm algorithm;
private CurveType curveType;
private ECKeyType keyType;
- private EllipticCurvePoint point;
+ private ECPoint point;
}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/validator/RegistrationRequestValidator.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/validator/RegistrationRequestValidator.java
index ebc3b1bb9..21800b9c6 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/validator/RegistrationRequestValidator.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/validator/RegistrationRequestValidator.java
@@ -19,6 +19,8 @@
package com.wultra.powerauth.fido2.rest.model.validator;
import com.wultra.powerauth.fido2.rest.model.entity.*;
+import com.wultra.powerauth.fido2.rest.model.enumeration.CurveType;
+import com.wultra.powerauth.fido2.rest.model.enumeration.ECKeyType;
import com.wultra.powerauth.fido2.rest.model.enumeration.Fmt;
import com.wultra.powerauth.fido2.rest.model.enumeration.SignatureAlgorithm;
import com.wultra.powerauth.fido2.rest.model.request.RegistrationRequest;
@@ -114,6 +116,16 @@ public String validate(RegistrationRequest request) {
return "Missing attestation data.";
}
+ final byte[] credentialId = attestedCredentialData.getCredentialId();
+ if (credentialId == null) {
+ return "Missing credential identifier.";
+ }
+
+ final byte[] aaguid = attestedCredentialData.getAaguid();
+ if (aaguid == null) {
+ return "Missing aaguid.";
+ }
+
final PublicKeyObject publicKeyObject = attestedCredentialData.getPublicKeyObject();
if (publicKeyObject == null) {
return "Missing public key inside attestation data";
@@ -124,6 +136,21 @@ public String validate(RegistrationRequest request) {
return "The provided algorithm is not supported by the server.";
}
+ final CurveType curveType = publicKeyObject.getCurveType();
+ if (CurveType.P256 != curveType) {
+ return "The provided curve type is not supported by the server.";
+ }
+
+ final ECKeyType keyType = publicKeyObject.getKeyType();
+ if (ECKeyType.UNCOMPRESSED != keyType) {
+ return "The provided key type is not supported by the server.";
+ }
+
+ final ECPoint point = publicKeyObject.getPoint();
+ if (point == null) {
+ return "Missing EC point in public key object.";
+ }
+
if (fmt.equals(Fmt.FMT_PACKED.getValue())) {
final AttestationStatement attStmt = attestationObject.getAttStmt();
if (!attStmt.getAlgorithm().equals(algorithm)) {
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/RegistrationService.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/RegistrationService.java
index 877f17a57..75c6e5f74 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/RegistrationService.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/RegistrationService.java
@@ -104,7 +104,7 @@ public RegistrationResponse register(RegistrationRequest requestObject) throws E
}
final RegistrationChallenge challenge = registrationProvider.provideChallengeForRegistrationChallengeValue(applicationId, challengeValue);
- final AuthenticatorDetail authenticatorDetail = registrationConverter.convert(challenge, requestObject, attestedCredentialData.getAaguid(), cryptographyService.publicKeyToBytes(attestedCredentialData));
+ final AuthenticatorDetail authenticatorDetail = registrationConverter.convert(challenge, requestObject, attestedCredentialData.getAaguid(), cryptographyService.publicKeyToBytes(attestedCredentialData.getPublicKeyObject()));
final AuthenticatorDetail authenticatorDetailResponse = authenticatorProvider.storeAuthenticator(requestObject.getApplicationId(), challenge.getChallenge(), authenticatorDetail);
return registrationConverter.convertRegistrationResponse(authenticatorDetailResponse);
}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/provider/CryptographyService.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/provider/CryptographyService.java
index aeeadbcc2..e3f157ff7 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/provider/CryptographyService.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/provider/CryptographyService.java
@@ -18,10 +18,7 @@
package com.wultra.powerauth.fido2.service.provider;
-import com.wultra.powerauth.fido2.rest.model.entity.AttestedCredentialData;
-import com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorData;
-import com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorDetail;
-import com.wultra.powerauth.fido2.rest.model.entity.CollectedClientData;
+import com.wultra.powerauth.fido2.rest.model.entity.*;
/**
* Interface representing FIDO2 verification service.
@@ -33,5 +30,5 @@ public interface CryptographyService {
boolean verifySignatureForAssertion(String applicationId, String authenticatorId, CollectedClientData clientDataJSON, AuthenticatorData authData, byte[] signature, AuthenticatorDetail authenticatorDetail) throws Exception;
- byte[] publicKeyToBytes(AttestedCredentialData attestedCredentialData) throws Exception;
+ byte[] publicKeyToBytes(PublicKeyObject publicKey) throws Exception;
}
diff --git a/powerauth-java-server/pom.xml b/powerauth-java-server/pom.xml
index 880a6ea7d..f6bc5cf34 100644
--- a/powerauth-java-server/pom.xml
+++ b/powerauth-java-server/pom.xml
@@ -171,6 +171,12 @@
h2
test
+
+ com.webauthn4j
+ webauthn4j-test
+ ${webauthn4j.version}
+ test
+
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAssertionProvider.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAssertionProvider.java
index 4ed907795..ea176b42e 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAssertionProvider.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAssertionProvider.java
@@ -89,6 +89,9 @@ public AssertionChallenge approveAssertion(String challengeValue, AuthenticatorD
try {
final String[] split = challengeValue.split("&", 2);
+ if (split.length != 2) {
+ throw new Fido2AuthenticationFailedException("Invalid challenge");
+ }
final String operationId = split[0];
final String operationData = split[1];
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthCryptographyService.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthCryptographyService.java
index 01ac9fe28..a2aecfce4 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthCryptographyService.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthCryptographyService.java
@@ -48,15 +48,15 @@ public boolean verifySignatureForAssertion(String applicationId, String authenti
}
public boolean verifySignatureForRegistration(String applicationId, CollectedClientData clientDataJSON, AuthenticatorData authData, byte[] signature, AttestedCredentialData attestedCredentialData) throws GenericCryptoException, InvalidKeySpecException, CryptoProviderException, InvalidKeyException {
- final EllipticCurvePoint point = attestedCredentialData.getPublicKeyObject().getPoint();
+ final ECPoint point = attestedCredentialData.getPublicKeyObject().getPoint();
final PublicKey publicKey = keyConvertor.convertPointBytesToPublicKey(point.getX(), point.getY());
return verifySignature(clientDataJSON, authData, signature, publicKey);
}
- public byte[] publicKeyToBytes(AttestedCredentialData attestedCredentialData) throws GenericCryptoException, InvalidKeySpecException, CryptoProviderException {
- final EllipticCurvePoint point = attestedCredentialData.getPublicKeyObject().getPoint();
- final PublicKey publicKey = keyConvertor.convertPointBytesToPublicKey(point.getX(), point.getY());
- return keyConvertor.convertPublicKeyToBytes(publicKey);
+ public byte[] publicKeyToBytes(PublicKeyObject publicKey) throws GenericCryptoException, InvalidKeySpecException, CryptoProviderException {
+ final ECPoint point = publicKey.getPoint();
+ final PublicKey publicKeyConverted = keyConvertor.convertPointBytesToPublicKey(point.getX(), point.getY());
+ return keyConvertor.convertPublicKeyToBytes(publicKeyConverted);
}
// private methods
diff --git a/powerauth-java-server/src/test/java/com/webauthn4j/test/authenticator/webauthn/SelfAttestedPackedAuthenticator.java b/powerauth-java-server/src/test/java/com/webauthn4j/test/authenticator/webauthn/SelfAttestedPackedAuthenticator.java
new file mode 100644
index 000000000..9be31b978
--- /dev/null
+++ b/powerauth-java-server/src/test/java/com/webauthn4j/test/authenticator/webauthn/SelfAttestedPackedAuthenticator.java
@@ -0,0 +1,54 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.webauthn4j.test.authenticator.webauthn;
+
+import com.webauthn4j.data.attestation.statement.AttestationStatement;
+import com.webauthn4j.data.attestation.statement.COSEAlgorithmIdentifier;
+import com.webauthn4j.data.attestation.statement.PackedAttestationStatement;
+import com.webauthn4j.test.TestDataUtil;
+import com.webauthn4j.test.client.RegistrationEmulationOption;
+
+import java.security.cert.X509Certificate;
+
+/**
+ * Self-attested packed authenticator.
+ *
+ * @author Roman Strobl, roman.strobl@wultra.com
+ */
+public class SelfAttestedPackedAuthenticator extends WebAuthnModelAuthenticator {
+
+ @Override
+ public AttestationStatement createAttestationStatement(AttestationStatementRequest attestationStatementRequest, RegistrationEmulationOption registrationEmulationOption) {
+ final byte[] signature;
+ if (registrationEmulationOption.isSignatureOverrideEnabled()) {
+ signature = registrationEmulationOption.getSignature();
+ } else {
+ signature = TestDataUtil.calculateSignature(attestationStatementRequest.getCredentialKeyPair().getPrivate(), attestationStatementRequest.getSignedData());
+ }
+
+ return new PackedAttestationStatement(COSEAlgorithmIdentifier.ES256, signature, null);
+ }
+
+ @Override
+ X509Certificate createAttestationCertificate(AttestationStatementRequest attestationStatementRequest, AttestationOption attestationOption) {
+ return null;
+ }
+
+}
\ No newline at end of file
diff --git a/powerauth-java-server/src/test/java/com/wultra/powerauth/fido2/Fido2AuthenticatorTest.java b/powerauth-java-server/src/test/java/com/wultra/powerauth/fido2/Fido2AuthenticatorTest.java
new file mode 100644
index 000000000..815f279b1
--- /dev/null
+++ b/powerauth-java-server/src/test/java/com/wultra/powerauth/fido2/Fido2AuthenticatorTest.java
@@ -0,0 +1,463 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.powerauth.fido2;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.dataformat.cbor.databind.CBORMapper;
+import com.google.common.io.BaseEncoding;
+import com.webauthn4j.data.AuthenticatorAssertionResponse;
+import com.webauthn4j.data.AuthenticatorAttestationResponse;
+import com.webauthn4j.data.*;
+import com.webauthn4j.data.attestation.statement.COSEAlgorithmIdentifier;
+import com.webauthn4j.data.client.Origin;
+import com.webauthn4j.data.client.challenge.Challenge;
+import com.webauthn4j.data.client.challenge.DefaultChallenge;
+import com.webauthn4j.data.extension.client.AuthenticationExtensionClientOutput;
+import com.webauthn4j.data.extension.client.RegistrationExtensionClientOutput;
+import com.webauthn4j.test.EmulatorUtil;
+import com.webauthn4j.test.authenticator.webauthn.SelfAttestedPackedAuthenticator;
+import com.webauthn4j.test.authenticator.webauthn.WebAuthnAuthenticatorAdaptor;
+import com.webauthn4j.test.client.ClientPlatform;
+import com.wultra.powerauth.fido2.errorhandling.Fido2AuthenticationFailedException;
+import com.wultra.powerauth.fido2.rest.model.entity.*;
+import com.wultra.powerauth.fido2.rest.model.request.AssertionChallengeRequest;
+import com.wultra.powerauth.fido2.rest.model.request.AssertionVerificationRequest;
+import com.wultra.powerauth.fido2.rest.model.request.RegistrationRequest;
+import com.wultra.powerauth.fido2.rest.model.response.AssertionChallengeResponse;
+import com.wultra.powerauth.fido2.rest.model.response.AssertionVerificationResponse;
+import com.wultra.powerauth.fido2.rest.model.response.RegistrationChallengeResponse;
+import com.wultra.powerauth.fido2.rest.model.response.RegistrationResponse;
+import com.wultra.powerauth.fido2.service.AssertionService;
+import com.wultra.powerauth.fido2.service.RegistrationService;
+import com.wultra.security.powerauth.client.model.enumeration.ActivationStatus;
+import com.wultra.security.powerauth.client.model.enumeration.SignatureType;
+import com.wultra.security.powerauth.client.model.request.CreateApplicationRequest;
+import com.wultra.security.powerauth.client.model.request.GetActivationStatusRequest;
+import com.wultra.security.powerauth.client.model.request.OperationTemplateCreateRequest;
+import com.wultra.security.powerauth.client.model.response.OperationTemplateDetailResponse;
+import io.getlime.security.powerauth.app.server.Application;
+import io.getlime.security.powerauth.app.server.service.PowerAuthService;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Objects;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Test of self-attested packed authenticator against PowerAuth server.
+ *
+ * @author Roman Strobl, roman.strobl@wultra.com
+ */
+@SpringBootTest(classes = Application.class)
+class Fido2AuthenticatorTest {
+
+ private final CBORMapper CBOR_MAPPER = new CBORMapper();
+ private final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
+
+ private final static String RP_ID = "powerauth.com";
+ private final static Origin ORIGIN = new Origin("http://localhost");
+ private final static String USER_ID = "test_" + UUID.randomUUID();
+ private final static String APPLICATION_ID = "fido2_test_" + UUID.randomUUID();
+ private final static String ACTIVATION_NAME = "fido2_test_activation";
+ private final static long REQUEST_TIMEOUT = 100L;
+
+ private final ClientPlatform CLIENT_PLATFORM_SELF_ATTESTED = new ClientPlatform(ORIGIN, new WebAuthnAuthenticatorAdaptor(new SelfAttestedPackedAuthenticator()));
+ private final ClientPlatform CLIENT_PLATFORM_BASIC_ATTESTATION = new ClientPlatform(ORIGIN, new WebAuthnAuthenticatorAdaptor(EmulatorUtil.PACKED_AUTHENTICATOR));
+
+ private final PowerAuthService powerAuthService;
+ private final RegistrationService registrationService;
+ private final AssertionService assertionService;
+
+
+ @Autowired
+ public Fido2AuthenticatorTest(PowerAuthService powerAuthService, RegistrationService registrationService, AssertionService assertionService) throws Exception {
+ this.powerAuthService = powerAuthService;
+ this.registrationService = registrationService;
+ this.assertionService = assertionService;
+ createApplication();
+ createOperationTemplate();
+ }
+
+ @Test
+ void packedAuthenticatorSuccessTest() throws Exception {
+ registerCredential();
+ authenticate();
+ }
+
+ @Test
+ public void packedAuthenticatorInvalidRegistrationChallengeTest() throws Exception {
+ // Use invalid challenge
+ final Challenge challenge = new DefaultChallenge(BaseEncoding.base64().encode(UUID.randomUUID().toString().getBytes(StandardCharsets.UTF_8)));
+ final AuthenticatorSelectionCriteria authenticatorCriteria = new AuthenticatorSelectionCriteria(
+ AuthenticatorAttachment.PLATFORM, true, UserVerificationRequirement.REQUIRED);
+ final PublicKeyCredentialParameters pkParam = new PublicKeyCredentialParameters(PublicKeyCredentialType.PUBLIC_KEY, COSEAlgorithmIdentifier.ES256);
+ final PublicKeyCredentialUserEntity user = new PublicKeyCredentialUserEntity(USER_ID.getBytes(StandardCharsets.UTF_8), USER_ID, USER_ID);
+ final PublicKeyCredentialCreationOptions credentialCreationOptions = new PublicKeyCredentialCreationOptions(new PublicKeyCredentialRpEntity(RP_ID, RP_ID),
+ user, challenge, Collections.singletonList(pkParam), REQUEST_TIMEOUT, Collections.emptyList(),
+ authenticatorCriteria, AttestationConveyancePreference.DIRECT, null
+ );
+
+ // Prepare registration request
+ com.wultra.powerauth.fido2.rest.model.request.RegistrationRequest registrationRequest = prepareRegistrationRequest(credentialCreationOptions, challenge, CLIENT_PLATFORM_SELF_ATTESTED);
+
+ // Register credential
+ assertThrows(Fido2AuthenticationFailedException.class, () -> registrationService.register(registrationRequest));
+ }
+
+ @Test
+ public void packedAuthenticatorInvalidAttestationTest() throws Exception {
+ // Obtain challenge from PowerAuth server
+ final RegistrationChallengeResponse challengeResponse = registrationService.requestRegistrationChallenge(USER_ID, APPLICATION_ID);
+ assertEquals(APPLICATION_ID, challengeResponse.getApplicationId());
+ assertEquals(USER_ID, challengeResponse.getUserId());
+ assertNotNull(challengeResponse.getChallenge());
+ assertNotNull(challengeResponse.getActivationId());
+
+ // Use obtained activation code as a challenge, prepare credential options
+ final Challenge challenge = new DefaultChallenge(challengeResponse.getChallenge().getBytes(StandardCharsets.UTF_8));
+ final AuthenticatorSelectionCriteria authenticatorCriteria = new AuthenticatorSelectionCriteria(
+ AuthenticatorAttachment.PLATFORM, true, UserVerificationRequirement.REQUIRED);
+ final PublicKeyCredentialParameters pkParam = new PublicKeyCredentialParameters(PublicKeyCredentialType.PUBLIC_KEY, COSEAlgorithmIdentifier.ES256);
+ final PublicKeyCredentialUserEntity user = new PublicKeyCredentialUserEntity(USER_ID.getBytes(StandardCharsets.UTF_8), USER_ID, USER_ID);
+ final PublicKeyCredentialCreationOptions credentialCreationOptions = new PublicKeyCredentialCreationOptions(new PublicKeyCredentialRpEntity(RP_ID, RP_ID),
+ user, challenge, Collections.singletonList(pkParam), REQUEST_TIMEOUT, Collections.emptyList(),
+ authenticatorCriteria, AttestationConveyancePreference.DIRECT, null
+ );
+
+ // Prepare registration request
+ com.wultra.powerauth.fido2.rest.model.request.RegistrationRequest registrationRequest = prepareRegistrationRequest(credentialCreationOptions, challenge, CLIENT_PLATFORM_BASIC_ATTESTATION);
+
+ // Register credential
+ assertThrows(Fido2AuthenticationFailedException.class, () -> registrationService.register(registrationRequest));
+ }
+
+ @Test
+ public void packedAuthenticatorNoAttestationTest() throws Exception {
+ // Obtain challenge from PowerAuth server
+ final RegistrationChallengeResponse challengeResponse = registrationService.requestRegistrationChallenge(USER_ID, APPLICATION_ID);
+ assertEquals(APPLICATION_ID, challengeResponse.getApplicationId());
+ assertEquals(USER_ID, challengeResponse.getUserId());
+ assertNotNull(challengeResponse.getChallenge());
+ assertNotNull(challengeResponse.getActivationId());
+
+ // Use obtained activation code as a challenge, prepare credential options
+ final Challenge challenge = new DefaultChallenge(challengeResponse.getChallenge().getBytes(StandardCharsets.UTF_8));
+ final AuthenticatorSelectionCriteria authenticatorCriteria = new AuthenticatorSelectionCriteria(
+ AuthenticatorAttachment.PLATFORM, true, UserVerificationRequirement.REQUIRED);
+ final PublicKeyCredentialParameters pkParam = new PublicKeyCredentialParameters(PublicKeyCredentialType.PUBLIC_KEY, COSEAlgorithmIdentifier.ES256);
+ final PublicKeyCredentialUserEntity user = new PublicKeyCredentialUserEntity(USER_ID.getBytes(StandardCharsets.UTF_8), USER_ID, USER_ID);
+ final PublicKeyCredentialCreationOptions credentialCreationOptions = new PublicKeyCredentialCreationOptions(new PublicKeyCredentialRpEntity(RP_ID, RP_ID),
+ user, challenge, Collections.singletonList(pkParam), REQUEST_TIMEOUT, Collections.emptyList(),
+ authenticatorCriteria, AttestationConveyancePreference.NONE, null
+ );
+
+ // Prepare registration request
+ com.wultra.powerauth.fido2.rest.model.request.RegistrationRequest registrationRequest = prepareRegistrationRequest(credentialCreationOptions, challenge, CLIENT_PLATFORM_BASIC_ATTESTATION);
+
+ // Register credential
+ final RegistrationResponse registrationResponse = registrationService.register(registrationRequest);
+ assertEquals(challengeResponse.getActivationId(), registrationResponse.getActivationId());
+ }
+
+ @Test
+ public void packedAuthenticatorInvalidAssertionChallengeTest() throws Exception {
+ registerCredential();
+
+ final Challenge challenge = new DefaultChallenge(BaseEncoding.base64().encode(UUID.randomUUID().toString().getBytes(StandardCharsets.UTF_8)));
+ final PublicKeyCredentialRequestOptions getOptions = new PublicKeyCredentialRequestOptions(challenge, REQUEST_TIMEOUT,
+ RP_ID, null, UserVerificationRequirement.REQUIRED, null);
+ final PublicKeyCredential credential = CLIENT_PLATFORM_SELF_ATTESTED.get(getOptions);
+ final AssertionVerificationRequest authRequest = new AssertionVerificationRequest();
+ authRequest.setId(credential.getId());
+ authRequest.setType(credential.getType());
+ authRequest.setAuthenticatorAttachment(AuthenticatorAttachment.PLATFORM.getValue());
+ authRequest.setApplicationId(APPLICATION_ID);
+ authRequest.setRelyingPartyId(RP_ID);
+ authRequest.setAllowedOrigins(Collections.singletonList(ORIGIN.toString()));
+ authRequest.setRequiresUserVerification(true);
+ authRequest.setExpectedChallenge(BaseEncoding.base64().encode(challenge.getValue()));
+
+ // Convert clientDataJSON and authenticatorData into object and supply encoded values for signature verification
+ final byte[] clientDataJSON = Objects.requireNonNull(credential.getAuthenticatorResponse()).getClientDataJSON();
+ final CollectedClientData clientData = OBJECT_MAPPER.readValue(clientDataJSON, CollectedClientData.class);
+ clientData.setEncoded(new String(clientDataJSON));
+ final byte[] authenticatorData = Objects.requireNonNull(credential.getAuthenticatorResponse()).getAuthenticatorData();
+ final AuthenticatorData authData = deserializeAuthenticationData(authenticatorData);
+ final byte[] userHandle = Objects.requireNonNull(credential.getAuthenticatorResponse()).getUserHandle();
+ final byte[] signature = Objects.requireNonNull(credential.getAuthenticatorResponse()).getSignature();
+
+ final com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorAssertionResponse assertionResponse = new com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorAssertionResponse();
+ assertionResponse.setClientDataJSON(clientData);
+ assertionResponse.setAuthenticatorData(authData);
+ assertionResponse.setUserHandle(new String(userHandle, StandardCharsets.UTF_8));
+ assertionResponse.setSignature(signature);
+ authRequest.setResponse(assertionResponse);
+
+ // Authenticate
+ assertThrows(Fido2AuthenticationFailedException.class, () -> assertionService.authenticate(authRequest));
+ }
+
+ @Test
+ public void packedAuthenticatorInvalidSignatureTest() throws Exception {
+ registerCredential();
+
+ // Obtain authentication challenge from PowerAuth server
+ final AssertionChallengeRequest challengeRequest = new AssertionChallengeRequest();
+ challengeRequest.setApplicationIds(Collections.singletonList(APPLICATION_ID));
+ challengeRequest.setOperationType("login");
+ challengeRequest.setExternalId(UUID.randomUUID().toString());
+ final AssertionChallengeResponse challengeResponse = assertionService.requestAssertionChallenge(challengeRequest);
+ assertEquals(APPLICATION_ID, challengeResponse.getApplicationIds().get(0));
+ assertNull(challengeResponse.getUserId());
+ assertNotNull(challengeResponse.getChallenge());
+ assertEquals(0, challengeResponse.getFailedAttempts());
+ assertEquals(5, challengeResponse.getMaxFailedAttempts());
+
+ // Prepare authentication request
+ final Challenge challenge = new DefaultChallenge(challengeResponse.getChallenge().getBytes(StandardCharsets.UTF_8));
+ final PublicKeyCredentialRequestOptions getOptions = new PublicKeyCredentialRequestOptions(challenge, REQUEST_TIMEOUT,
+ RP_ID, null, UserVerificationRequirement.REQUIRED, null);
+ final PublicKeyCredential credential = CLIENT_PLATFORM_SELF_ATTESTED.get(getOptions);
+ final AssertionVerificationRequest authRequest = new AssertionVerificationRequest();
+ authRequest.setId(credential.getId());
+ authRequest.setType(credential.getType());
+ authRequest.setAuthenticatorAttachment(AuthenticatorAttachment.PLATFORM.getValue());
+ authRequest.setApplicationId(APPLICATION_ID);
+ authRequest.setRelyingPartyId(RP_ID);
+ authRequest.setAllowedOrigins(Collections.singletonList(ORIGIN.toString()));
+ authRequest.setRequiresUserVerification(true);
+ authRequest.setExpectedChallenge(new String(challenge.getValue(), StandardCharsets.UTF_8));
+
+ // Convert clientDataJSON and authenticatorData into object and supply encoded values for signature verification
+ final byte[] clientDataJSON = Objects.requireNonNull(credential.getAuthenticatorResponse()).getClientDataJSON();
+ final CollectedClientData clientData = OBJECT_MAPPER.readValue(clientDataJSON, CollectedClientData.class);
+ clientData.setEncoded(new String(clientDataJSON));
+ final byte[] authenticatorData = Objects.requireNonNull(credential.getAuthenticatorResponse()).getAuthenticatorData();
+ final AuthenticatorData authData = deserializeAuthenticationData(authenticatorData);
+ final byte[] userHandle = Objects.requireNonNull(credential.getAuthenticatorResponse()).getUserHandle();
+
+ final com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorAssertionResponse assertionResponse = new com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorAssertionResponse();
+ assertionResponse.setClientDataJSON(clientData);
+ assertionResponse.setAuthenticatorData(authData);
+ assertionResponse.setUserHandle(new String(userHandle, StandardCharsets.UTF_8));
+ assertionResponse.setSignature(new byte[32]);
+ authRequest.setResponse(assertionResponse);
+
+ // Authenticate
+ assertThrows(Fido2AuthenticationFailedException.class, () -> assertionService.authenticate(authRequest));
+ }
+
+ private void createApplication() throws Exception {
+ // Search if application for FIDO2 tests exists
+ final boolean applicationFound = powerAuthService.getApplicationList().getApplications().stream()
+ .map(com.wultra.security.powerauth.client.model.entity.Application::getApplicationId)
+ .anyMatch(APPLICATION_ID::equals);
+ if (applicationFound) {
+ return;
+ }
+ // Create application for FIDO2 tests
+ CreateApplicationRequest request = new CreateApplicationRequest();
+ request.setApplicationId(APPLICATION_ID);
+ powerAuthService.createApplication(request);
+ }
+
+ private void registerCredential() throws Exception {
+ // Obtain challenge from PowerAuth server
+ final RegistrationChallengeResponse challengeResponse = registrationService.requestRegistrationChallenge(USER_ID, APPLICATION_ID);
+ assertEquals(APPLICATION_ID, challengeResponse.getApplicationId());
+ assertEquals(USER_ID, challengeResponse.getUserId());
+ assertNotNull(challengeResponse.getChallenge());
+ assertNotNull(challengeResponse.getActivationId());
+
+ // Check that activation is in CREATED state
+ final GetActivationStatusRequest activationStatusRequest = new GetActivationStatusRequest();
+ activationStatusRequest.setActivationId(challengeResponse.getActivationId());
+ assertEquals(ActivationStatus.CREATED, powerAuthService.getActivationStatus(activationStatusRequest).getActivationStatus());
+
+ // Use obtained activation code as a challenge, prepare credential options
+ final Challenge challenge = new DefaultChallenge(challengeResponse.getChallenge().getBytes(StandardCharsets.UTF_8));
+ final AuthenticatorSelectionCriteria authenticatorCriteria = new AuthenticatorSelectionCriteria(
+ AuthenticatorAttachment.PLATFORM, true, UserVerificationRequirement.REQUIRED);
+ final PublicKeyCredentialParameters pkParam = new PublicKeyCredentialParameters(PublicKeyCredentialType.PUBLIC_KEY, COSEAlgorithmIdentifier.ES256);
+ final PublicKeyCredentialUserEntity user = new PublicKeyCredentialUserEntity(USER_ID.getBytes(StandardCharsets.UTF_8), USER_ID, USER_ID);
+ final PublicKeyCredentialCreationOptions credentialCreationOptions = new PublicKeyCredentialCreationOptions(new PublicKeyCredentialRpEntity(RP_ID, RP_ID),
+ user, challenge, Collections.singletonList(pkParam), REQUEST_TIMEOUT, Collections.emptyList(),
+ authenticatorCriteria, AttestationConveyancePreference.DIRECT, null
+ );
+
+ // Prepare registration request
+ com.wultra.powerauth.fido2.rest.model.request.RegistrationRequest registrationRequest = prepareRegistrationRequest(credentialCreationOptions, challenge, CLIENT_PLATFORM_SELF_ATTESTED);
+
+ // Register credential
+ final RegistrationResponse registrationResponse = registrationService.register(registrationRequest);
+ assertEquals(APPLICATION_ID, registrationResponse.getApplicationId());
+
+ // Check that activation is in ACTIVE state
+ final GetActivationStatusRequest activationStatusRequest2 = new GetActivationStatusRequest();
+ activationStatusRequest2.setActivationId(challengeResponse.getActivationId());
+ assertEquals(ActivationStatus.ACTIVE, powerAuthService.getActivationStatus(activationStatusRequest2).getActivationStatus());
+ }
+
+ private RegistrationRequest prepareRegistrationRequest(PublicKeyCredentialCreationOptions credentialCreationOptions, Challenge challenge, ClientPlatform clientPlatform) throws Exception {
+ // Create credential on authenticator emulator
+ final PublicKeyCredential credential = clientPlatform.create(credentialCreationOptions);
+ com.wultra.powerauth.fido2.rest.model.request.RegistrationRequest registrationRequest = new RegistrationRequest();
+ registrationRequest.setApplicationId(APPLICATION_ID);
+ registrationRequest.setActivationName(ACTIVATION_NAME);
+ registrationRequest.setExpectedChallenge(new String(challenge.getValue(), StandardCharsets.UTF_8));
+ final AuthenticatorParameters authenticationParameters = new AuthenticatorParameters();
+ authenticationParameters.setId(credential.getId());
+ authenticationParameters.setRelyingPartyId(RP_ID);
+ authenticationParameters.setAllowedOrigins(Collections.singletonList(ORIGIN.toString()));
+ authenticationParameters.setType(credential.getType());
+ authenticationParameters.setRequiresUserVerification(true);
+
+ // Convert clientDataJSON and attestationObject into object and supply encoded values for signature verification
+ final byte[] clientDataJSON = Objects.requireNonNull(credential.getAuthenticatorResponse()).getClientDataJSON();
+ final CollectedClientData clientData = OBJECT_MAPPER.readValue(clientDataJSON, CollectedClientData.class);
+ clientData.setEncoded(new String(clientDataJSON));
+ final byte[] attestationObject = Objects.requireNonNull(credential.getAuthenticatorResponse()).getAttestationObject();
+ final AttestationObject attObj = CBOR_MAPPER.readValue(attestationObject, AttestationObject.class);
+ attObj.setEncoded(new String(attestationObject));
+
+ final com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorAttestationResponse attestationResponse = new com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorAttestationResponse();
+ attestationResponse.setClientDataJSON(clientData);
+ attestationResponse.setAttestationObject(attObj);
+ final AuthenticatorTransport[] transports = credential.getAuthenticatorResponse().getTransports().toArray(new AuthenticatorTransport[0]);
+ attestationResponse.setTransports(Arrays.stream(transports).map(AuthenticatorTransport::toString).collect(Collectors.toList()));
+ authenticationParameters.setResponse(attestationResponse);
+ registrationRequest.setAuthenticatorParameters(authenticationParameters);
+ return registrationRequest;
+ }
+
+ private void createOperationTemplate() throws Exception {
+ final boolean templateFound = powerAuthService.getAllTemplates().stream()
+ .map(OperationTemplateDetailResponse::getTemplateName)
+ .anyMatch("login"::equals);
+ if (templateFound) {
+ return;
+ }
+ final OperationTemplateCreateRequest templateCreateRequest = new OperationTemplateCreateRequest();
+ templateCreateRequest.setTemplateName("login");
+ templateCreateRequest.setOperationType("login");
+ templateCreateRequest.setDataTemplate("A2");
+ templateCreateRequest.setMaxFailureCount(5L);
+ templateCreateRequest.setExpiration(300L);
+ templateCreateRequest.getSignatureType().add(SignatureType.POSSESSION_KNOWLEDGE);
+ powerAuthService.createOperationTemplate(templateCreateRequest);
+ }
+
+ private void authenticate() throws Exception {
+ // Obtain authentication challenge from PowerAuth server
+ final AssertionChallengeRequest challengeRequest = new AssertionChallengeRequest();
+ challengeRequest.setApplicationIds(Collections.singletonList(APPLICATION_ID));
+ challengeRequest.setOperationType("login");
+ challengeRequest.setExternalId(UUID.randomUUID().toString());
+ final AssertionChallengeResponse challengeResponse = assertionService.requestAssertionChallenge(challengeRequest);
+ assertEquals(APPLICATION_ID, challengeResponse.getApplicationIds().get(0));
+ assertNull(challengeResponse.getUserId());
+ assertNotNull(challengeResponse.getChallenge());
+ assertEquals(0, challengeResponse.getFailedAttempts());
+ assertEquals(5, challengeResponse.getMaxFailedAttempts());
+
+ // Prepare authentication request
+ final Challenge challenge = new DefaultChallenge(challengeResponse.getChallenge().getBytes(StandardCharsets.UTF_8));
+ final PublicKeyCredentialRequestOptions getOptions = new PublicKeyCredentialRequestOptions(challenge, REQUEST_TIMEOUT,
+ RP_ID, null, UserVerificationRequirement.REQUIRED, null);
+ final PublicKeyCredential credential = CLIENT_PLATFORM_SELF_ATTESTED.get(getOptions);
+ final AssertionVerificationRequest authRequest = new AssertionVerificationRequest();
+ authRequest.setId(credential.getId());
+ authRequest.setType(credential.getType());
+ authRequest.setAuthenticatorAttachment(AuthenticatorAttachment.PLATFORM.getValue());
+ authRequest.setApplicationId(APPLICATION_ID);
+ authRequest.setRelyingPartyId(RP_ID);
+ authRequest.setAllowedOrigins(Collections.singletonList(ORIGIN.toString()));
+ authRequest.setRequiresUserVerification(true);
+ authRequest.setExpectedChallenge(new String(challenge.getValue(), StandardCharsets.UTF_8));
+
+ // Convert clientDataJSON and authenticatorData into object and supply encoded values for signature verification
+ final byte[] clientDataJSON = Objects.requireNonNull(credential.getAuthenticatorResponse()).getClientDataJSON();
+ final CollectedClientData clientData = OBJECT_MAPPER.readValue(clientDataJSON, CollectedClientData.class);
+ clientData.setEncoded(new String(clientDataJSON));
+ final byte[] authenticatorData = Objects.requireNonNull(credential.getAuthenticatorResponse()).getAuthenticatorData();
+ final AuthenticatorData authData = deserializeAuthenticationData(authenticatorData);
+ final byte[] userHandle = Objects.requireNonNull(credential.getAuthenticatorResponse()).getUserHandle();
+ final byte[] signature = Objects.requireNonNull(credential.getAuthenticatorResponse()).getSignature();
+
+ final com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorAssertionResponse assertionResponse = new com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorAssertionResponse();
+ assertionResponse.setClientDataJSON(clientData);
+ assertionResponse.setAuthenticatorData(authData);
+ assertionResponse.setUserHandle(new String(userHandle, StandardCharsets.UTF_8));
+ assertionResponse.setSignature(signature);
+ authRequest.setResponse(assertionResponse);
+
+ // Authenticate
+ final AssertionVerificationResponse authResponse = assertionService.authenticate(authRequest);
+ assertEquals(APPLICATION_ID, authResponse.getApplicationId());
+
+ }
+
+ private AuthenticatorData deserializeAuthenticationData(byte[] authData) throws IOException {
+ final AuthenticatorData result = new AuthenticatorData();
+ result.setEncoded(authData);
+
+ // Get RP ID Hash
+ final byte[] rpIdHash = new byte[32];
+ System.arraycopy(authData, 0, rpIdHash,0, 32);
+ result.setRpIdHash(rpIdHash);
+
+ // Get Flags
+ final byte flagByte = authData[32];
+ final Flags flags = result.getFlags();
+
+ flags.setUserPresent(isFlagOn(flagByte, 0));
+ flags.setReservedBit2(isFlagOn(flagByte, 1));
+ flags.setUserVerified(isFlagOn(flagByte, 2));
+ flags.setBackupEligible(isFlagOn(flagByte, 3));
+ flags.setBackupState(isFlagOn(flagByte, 4));
+ flags.setReservedBit6(isFlagOn(flagByte, 5));
+ flags.setAttestedCredentialsIncluded(isFlagOn(flagByte,6));
+ flags.setExtensionDataIncluded(isFlagOn(flagByte,7));
+
+ // Get Signature Counter
+ final byte[] signCountBytes = new byte[4];
+ System.arraycopy(authData, 33, signCountBytes, 0, 4);
+ final int signCount = ByteBuffer.wrap(signCountBytes).getInt(); // big-endian by default
+ result.setSignCount(signCount);
+
+ return result;
+ }
+
+ private boolean isFlagOn(byte flags, int position) throws IOException {
+ if (position < 0 || position > 7) {
+ throw new IOException("Invalid position for flag: " + position);
+ }
+ return ((flags >> position) & 1) == 1;
+ }
+}
From cc2b874badba96780bfb9e60c1bf3fa240e2323a Mon Sep 17 00:00:00 2001
From: Lubos Racansky
Date: Fri, 5 Jan 2024 11:17:57 +0100
Subject: [PATCH 026/146] Fix #1236: Update Wultra dependencies to SNAPSHOT
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index ae5057e2e..0d92063ac 100644
--- a/pom.xml
+++ b/pom.xml
@@ -84,8 +84,8 @@
4.0.1
- 1.6.0
- 1.8.0
+ 1.7.0-SNAPSHOT
+ 1.9.0-SNAPSHOT
1.77
From 880911d3aa031bf4bf3b77e76cf64547f07810d8 Mon Sep 17 00:00:00 2001
From: Lubos Racansky
Date: Fri, 5 Jan 2024 15:40:13 +0100
Subject: [PATCH 027/146] Fix #1195: Set develop version to 1.7.0-SNAPSHOT
---
pom.xml | 2 +-
powerauth-admin/pom.xml | 2 +-
powerauth-client-model/pom.xml | 2 +-
powerauth-java-server/pom.xml | 2 +-
powerauth-rest-client-spring/pom.xml | 2 +-
5 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/pom.xml b/pom.xml
index 06d173797..c75406dac 100644
--- a/pom.xml
+++ b/pom.xml
@@ -26,7 +26,7 @@
io.getlime.security
powerauth-server-parent
- 1.6.0
+ 1.7.0-SNAPSHOT
pom
diff --git a/powerauth-admin/pom.xml b/powerauth-admin/pom.xml
index b2e9f6b9a..8bed0cfd2 100644
--- a/powerauth-admin/pom.xml
+++ b/powerauth-admin/pom.xml
@@ -11,7 +11,7 @@
io.getlime.security
powerauth-server-parent
- 1.6.0
+ 1.7.0-SNAPSHOT
diff --git a/powerauth-client-model/pom.xml b/powerauth-client-model/pom.xml
index b88455c54..e8d72bdbf 100644
--- a/powerauth-client-model/pom.xml
+++ b/powerauth-client-model/pom.xml
@@ -28,7 +28,7 @@
io.getlime.security
powerauth-server-parent
- 1.6.0
+ 1.7.0-SNAPSHOT
diff --git a/powerauth-java-server/pom.xml b/powerauth-java-server/pom.xml
index 911aec6be..0958be03f 100644
--- a/powerauth-java-server/pom.xml
+++ b/powerauth-java-server/pom.xml
@@ -29,7 +29,7 @@
io.getlime.security
powerauth-server-parent
- 1.6.0
+ 1.7.0-SNAPSHOT
diff --git a/powerauth-rest-client-spring/pom.xml b/powerauth-rest-client-spring/pom.xml
index db5c5c99c..31302c4f4 100644
--- a/powerauth-rest-client-spring/pom.xml
+++ b/powerauth-rest-client-spring/pom.xml
@@ -28,7 +28,7 @@
io.getlime.security
powerauth-server-parent
- 1.6.0
+ 1.7.0-SNAPSHOT
From ea636210aca69b45eac8a261480a7854ee6feffe Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 2 Jan 2024 11:34:24 +0000
Subject: [PATCH 028/146] Bump
org.springframework.boot:spring-boot-starter-parent
Bumps [org.springframework.boot:spring-boot-starter-parent](https://github.com/spring-projects/spring-boot) from 3.1.6 to 3.2.1.
- [Release notes](https://github.com/spring-projects/spring-boot/releases)
- [Commits](https://github.com/spring-projects/spring-boot/compare/v3.1.6...v3.2.1)
---
updated-dependencies:
- dependency-name: org.springframework.boot:spring-boot-starter-parent
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot]
---
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index 633512d68..198435db1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -32,7 +32,7 @@
org.springframework.boot
spring-boot-starter-parent
- 3.1.6
+ 3.2.1
From 123c5c6f7788e27b9847453539418240f452cba2 Mon Sep 17 00:00:00 2001
From: Lubos Racansky
Date: Tue, 2 Jan 2024 12:41:17 +0100
Subject: [PATCH 029/146] Revert "Fix #1175: Update logback"
This reverts commit d1e3f94e801af59a6be209db52892fee3bc20273.
---
pom.xml | 2 --
1 file changed, 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 198435db1..2ae0f2707 100644
--- a/pom.xml
+++ b/pom.xml
@@ -102,8 +102,6 @@
1.11.0
7.4
3.15.5
-
- 1.4.14
From c0640eb6729867110ebdd172cb8d97af1b1d74f4 Mon Sep 17 00:00:00 2001
From: Lubos Racansky
Date: Fri, 5 Jan 2024 16:18:28 +0100
Subject: [PATCH 030/146] Bump spring-cloud-vault version to 4.1.0
---
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index 2ae0f2707..b2b2ef2b8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -81,7 +81,7 @@
- 4.0.1
+ 4.1.0
1.7.0-SNAPSHOT
From 311ff8850ca8006d0a6d50c3d5fbfa2b40f1c498 Mon Sep 17 00:00:00 2001
From: Jan Dusil <134381434+jandusil@users.noreply.github.com>
Date: Mon, 8 Jan 2024 13:29:22 +0100
Subject: [PATCH 031/146] Clean up catching Error class (#1233)
---
.../app/server/service/PowerAuthService.java | 138 +++++++++---------
1 file changed, 69 insertions(+), 69 deletions(-)
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/PowerAuthService.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/PowerAuthService.java
index f6f22bfa4..462cd7472 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/PowerAuthService.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/PowerAuthService.java
@@ -167,7 +167,7 @@ public GetActivationListForUserResponse getActivationListForUser(GetActivationLi
final GetActivationListForUserResponse response = behavior.getActivationServiceBehavior().getActivationList(applicationId, userId, pageable, activationStatuses);
logger.info("GetActivationListForUserRequest succeeded");
return response;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -207,7 +207,7 @@ public LookupActivationsResponse lookupActivations(LookupActivationsRequest requ
final LookupActivationsResponse response = behavior.getActivationServiceBehavior().lookupActivations(userIds, applicationIds, timestampLastUsedBefore, timestampLastUsedAfter, activationStatus, activationFlags);
logger.info("LookupActivationsRequest succeeded");
return response;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -233,7 +233,7 @@ public UpdateStatusForActivationsResponse updateStatusForActivations(UpdateStatu
final UpdateStatusForActivationsResponse response = behavior.getActivationServiceBehavior().updateStatusForActivation(activationIds, activationStatus);
logger.info("UpdateStatusForActivationsRequest succeeded");
return response;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -259,7 +259,7 @@ public GetActivationStatusResponse getActivationStatus(GetActivationStatusReques
} catch (GenericServiceException ex) {
// already logged
throw ex;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -299,7 +299,7 @@ public InitActivationResponse initActivation(InitActivationRequest request) thro
} catch (GenericServiceException ex) {
// already logged
throw ex;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -330,7 +330,7 @@ public PrepareActivationResponse prepareActivation(PrepareActivationRequest requ
} catch (GenericServiceException ex) {
// already logged
throw ex;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -374,7 +374,7 @@ public CreateActivationResponse createActivation(CreateActivationRequest request
} catch (GenericServiceException ex) {
// already logged
throw ex;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -415,7 +415,7 @@ public VerifySignatureResponse verifySignature(VerifySignatureRequest request) t
} catch (GenericServiceException ex) {
// already logged
throw ex;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -440,7 +440,7 @@ public CreatePersonalizedOfflineSignaturePayloadResponse createPersonalizedOffli
} catch (GenericServiceException ex) {
// already logged
throw ex;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -482,7 +482,7 @@ public CreateNonPersonalizedOfflineSignaturePayloadResponse createNonPersonalize
} catch (GenericServiceException ex) {
// already logged
throw ex;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -518,7 +518,7 @@ public VerifyOfflineSignatureResponse verifyOfflineSignature(VerifyOfflineSignat
} catch (GenericServiceException ex) {
// already logged
throw ex;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -571,7 +571,7 @@ public UpdateActivationOtpResponse updateActivationOtp(UpdateActivationOtpReques
} catch (GenericServiceException ex) {
// already logged
throw ex;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -598,7 +598,7 @@ public CommitActivationResponse commitActivation(CommitActivationRequest request
} catch (GenericServiceException ex) {
// already logged
throw ex;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -625,7 +625,7 @@ public RemoveActivationResponse removeActivation(RemoveActivationRequest request
} catch (GenericServiceException ex) {
// already logged
throw ex;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -652,7 +652,7 @@ public BlockActivationResponse blockActivation(BlockActivationRequest request) t
} catch (GenericServiceException ex) {
// already logged
throw ex;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -678,7 +678,7 @@ public UnblockActivationResponse unblockActivation(UnblockActivationRequest requ
} catch (GenericServiceException ex) {
// already logged
throw ex;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -731,7 +731,7 @@ public VaultUnlockResponse vaultUnlock(VaultUnlockRequest request) throws Generi
} catch (GenericServiceException ex) {
// already logged
throw ex;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -760,7 +760,7 @@ public VerifyECDSASignatureResponse verifyECDSASignature(VerifyECDSASignatureReq
} catch (GenericServiceException ex) {
// already logged
throw ex;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -787,7 +787,7 @@ public SignatureAuditResponse getSignatureAuditLog(SignatureAuditRequest request
final SignatureAuditResponse response = behavior.getAuditingServiceBehavior().getSignatureAuditLog(userId, applicationId, startingDate, endingDate);
logger.info("SignatureAuditRequest succeeded");
return response;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -812,7 +812,7 @@ public ActivationHistoryResponse getActivationHistory(ActivationHistoryRequest r
final ActivationHistoryResponse response = behavior.getActivationHistoryServiceBehavior().getActivationHistory(activationId, startingDate, endingDate);
logger.info("ActivationHistoryRequest succeeded");
return response;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -828,7 +828,7 @@ public GetApplicationListResponse getApplicationList() throws GenericServiceExce
final GetApplicationListResponse response = behavior.getApplicationServiceBehavior().getApplicationList();
logger.info("GetApplicationListRequest succeeded");
return response;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -853,7 +853,7 @@ public GetApplicationDetailResponse getApplicationDetail(GetApplicationDetailReq
} catch (GenericServiceException ex) {
// already logged
throw ex;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -877,7 +877,7 @@ public LookupApplicationByAppKeyResponse lookupApplicationByAppKey(LookupApplica
} catch (GenericServiceException ex) {
// already logged
throw ex;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -901,7 +901,7 @@ public CreateApplicationResponse createApplication(CreateApplicationRequest requ
} catch (GenericServiceException ex) {
// already logged
throw ex;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -930,7 +930,7 @@ public CreateApplicationVersionResponse createApplicationVersion(CreateApplicati
} catch (GenericServiceException ex) {
// already logged
throw ex;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -951,7 +951,7 @@ public UnsupportApplicationVersionResponse unsupportApplicationVersion(Unsupport
} catch (GenericServiceException ex) {
// already logged
throw ex;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -972,7 +972,7 @@ public SupportApplicationVersionResponse supportApplicationVersion(SupportApplic
} catch (GenericServiceException ex) {
// already logged
throw ex;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -993,7 +993,7 @@ public CreateIntegrationResponse createIntegration(CreateIntegrationRequest requ
final CreateIntegrationResponse response = behavior.getIntegrationBehavior().createIntegration(request);
logger.info("CreateIntegrationRequest succeeded");
return response;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -1009,7 +1009,7 @@ public GetIntegrationListResponse getIntegrationList() throws GenericServiceExce
final GetIntegrationListResponse response = behavior.getIntegrationBehavior().getIntegrationList();
logger.info("GetIntegrationListRequest succeeded");
return response;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -1025,7 +1025,7 @@ public RemoveIntegrationResponse removeIntegration(RemoveIntegrationRequest requ
final RemoveIntegrationResponse response = behavior.getIntegrationBehavior().removeIntegration(request);
logger.info("RemoveIntegrationRequest succeeded");
return response;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -1049,7 +1049,7 @@ public CreateCallbackUrlResponse createCallbackUrl(CreateCallbackUrlRequest requ
} catch (GenericServiceException ex) {
// already logged
throw ex;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -1072,7 +1072,7 @@ public UpdateCallbackUrlResponse updateCallbackUrl(UpdateCallbackUrlRequest requ
} catch (GenericServiceException ex) {
// already logged
throw ex;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -1088,7 +1088,7 @@ public GetCallbackUrlListResponse getCallbackUrlList(GetCallbackUrlListRequest r
final GetCallbackUrlListResponse response = behavior.getCallbackUrlBehavior().getCallbackUrlList(request);
logger.info("GetCallbackUrlListRequest succeeded");
return response;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -1104,7 +1104,7 @@ public RemoveCallbackUrlResponse removeCallbackUrl(RemoveCallbackUrlRequest requ
final RemoveCallbackUrlResponse response = behavior.getCallbackUrlBehavior().removeCallbackUrl(request);
logger.info("RemoveCallbackUrlRequest succeeded");
return response;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -1128,7 +1128,7 @@ public CreateTokenResponse createToken(CreateTokenRequest request) throws Generi
} catch (GenericServiceException ex) {
// already logged
throw ex;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -1164,7 +1164,7 @@ public ValidateTokenResponse validateToken(ValidateTokenRequest request) throws
} catch (GenericServiceException ex) {
// already logged
throw ex;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -1185,7 +1185,7 @@ public RemoveTokenResponse removeToken(RemoveTokenRequest request) throws Generi
final RemoveTokenResponse response = behavior.getTokenBehavior().removeToken(request);
logger.info("RemoveTokenRequest succeeded");
return response;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -1210,7 +1210,7 @@ public GetEciesDecryptorResponse getEciesDecryptor(GetEciesDecryptorRequest requ
} catch (GenericServiceException ex) {
// already logged
throw ex;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -1234,7 +1234,7 @@ public StartUpgradeResponse startUpgrade(StartUpgradeRequest request) throws Gen
} catch (GenericServiceException ex) {
// already logged
throw ex;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -1258,7 +1258,7 @@ public CommitUpgradeResponse commitUpgrade(CommitUpgradeRequest request) throws
} catch (GenericServiceException ex) {
// already logged
throw ex;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -1282,7 +1282,7 @@ public CreateRecoveryCodeResponse createRecoveryCode(CreateRecoveryCodeRequest r
} catch (GenericServiceException ex) {
// already logged
throw ex;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -1306,7 +1306,7 @@ public ConfirmRecoveryCodeResponse confirmRecoveryCode(ConfirmRecoveryCodeReques
} catch (GenericServiceException ex) {
// already logged
throw ex;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -1330,7 +1330,7 @@ public LookupRecoveryCodesResponse lookupRecoveryCodes(LookupRecoveryCodesReques
} catch (GenericServiceException ex) {
// already logged
throw ex;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -1354,7 +1354,7 @@ public RevokeRecoveryCodesResponse revokeRecoveryCodes(RevokeRecoveryCodesReques
} catch (GenericServiceException ex) {
// already logged
throw ex;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -1378,7 +1378,7 @@ public RecoveryCodeActivationResponse createActivationUsingRecoveryCode(Recovery
} catch (GenericServiceException ex) {
// already logged
throw ex;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -1402,7 +1402,7 @@ public GetRecoveryConfigResponse getRecoveryConfig(GetRecoveryConfigRequest requ
} catch (GenericServiceException ex) {
// already logged
throw ex;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -1426,7 +1426,7 @@ public UpdateRecoveryConfigResponse updateRecoveryConfig(UpdateRecoveryConfigReq
} catch (GenericServiceException ex) {
// already logged
throw ex;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -1451,7 +1451,7 @@ public ListActivationFlagsResponse listActivationFlags(ListActivationFlagsReques
} catch (GenericServiceException ex) {
// already logged
throw ex;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -1482,7 +1482,7 @@ public AddActivationFlagsResponse addActivationFlags(AddActivationFlagsRequest r
} catch (GenericServiceException ex) {
// already logged
throw ex;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -1513,7 +1513,7 @@ public UpdateActivationFlagsResponse updateActivationFlags(UpdateActivationFlags
} catch (GenericServiceException ex) {
// already logged
throw ex;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -1544,7 +1544,7 @@ public RemoveActivationFlagsResponse removeActivationFlags(RemoveActivationFlags
} catch (GenericServiceException ex) {
// already logged
throw ex;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -1569,7 +1569,7 @@ public ListApplicationRolesResponse listApplicationRoles(ListApplicationRolesReq
} catch (GenericServiceException ex) {
// already logged
throw ex;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -1600,7 +1600,7 @@ public AddApplicationRolesResponse addApplicationRoles(AddApplicationRolesReques
} catch (GenericServiceException ex) {
// already logged
throw ex;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -1631,7 +1631,7 @@ public UpdateApplicationRolesResponse updateApplicationRoles(UpdateApplicationRo
} catch (GenericServiceException ex) {
// already logged
throw ex;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -1662,7 +1662,7 @@ public RemoveApplicationRolesResponse removeApplicationRoles(RemoveApplicationRo
} catch (GenericServiceException ex) {
// already logged
throw ex;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -1685,7 +1685,7 @@ public OperationDetailResponse createOperation(OperationCreateRequest request) t
} catch (GenericServiceException ex) {
// already logged
throw ex;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -1708,7 +1708,7 @@ public OperationDetailResponse operationDetail(OperationDetailRequest request) t
} catch (GenericServiceException ex) {
// already logged
throw ex;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -1728,7 +1728,7 @@ public OperationListResponse findPendingOperationsForUser(OperationListForUserRe
final OperationListResponse response = behavior.getOperationBehavior().findPendingOperationsForUser(convert(request));
logger.info("OperationListForUserRequest succeeded");
return response;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -1748,7 +1748,7 @@ public OperationListResponse findAllOperationsForUser(OperationListForUserReques
final OperationListResponse response = behavior.getOperationBehavior().findAllOperationsForUser(convert(request));
logger.info("OperationListForUserRequest succeeded");
return response;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -1768,7 +1768,7 @@ public OperationListResponse findAllOperationsByExternalId(OperationExtIdRequest
final OperationListResponse response = behavior.getOperationBehavior().findOperationsByExternalId(convert(request));
logger.info("findAllOperationsByExternalId succeeded");
return response;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -1791,7 +1791,7 @@ public OperationDetailResponse cancelOperation(OperationCancelRequest request) t
} catch (GenericServiceException ex) {
// already logged
throw ex;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -1821,7 +1821,7 @@ public OperationUserActionResponse approveOperation(OperationApproveRequest requ
} catch (GenericServiceException ex) {
// already logged
throw ex;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back, operation ID: {}", operationId, ex);
throw ex;
} catch (Exception ex) {
@@ -1848,7 +1848,7 @@ public OperationUserActionResponse rejectOperation(OperationRejectRequest reques
} catch (GenericServiceException ex) {
// already logged
throw ex;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -1871,7 +1871,7 @@ public OperationUserActionResponse failApprovalOperation(OperationFailApprovalRe
} catch (GenericServiceException ex) {
// already logged
throw ex;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -1887,7 +1887,7 @@ public OperationTemplateListResponse getAllTemplates() throws Exception {
final OperationTemplateListResponse response = behavior.getOperationTemplateBehavior().getAllTemplates();
logger.info("OperationTemplateListResponse succeeded");
return response;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -1910,7 +1910,7 @@ public OperationTemplateDetailResponse getTemplateDetail(OperationTemplateDetail
} catch (GenericServiceException ex) {
// already logged
throw ex;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -1933,7 +1933,7 @@ public OperationTemplateDetailResponse createOperationTemplate(OperationTemplate
} catch (GenericServiceException ex) {
// already logged
throw ex;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -1956,7 +1956,7 @@ public OperationTemplateDetailResponse updateOperationTemplate(OperationTemplate
} catch (GenericServiceException ex) {
// already logged
throw ex;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
@@ -1978,7 +1978,7 @@ public void removeOperationTemplate(OperationTemplateDeleteRequest request) thro
} catch (GenericServiceException ex) {
// already logged
throw ex;
- } catch (RuntimeException | Error ex) {
+ } catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
} catch (Exception ex) {
From 559e2a1d1a7887d6326f4eba0bf4403423a04f82 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 15 Jan 2024 01:42:06 +0000
Subject: [PATCH 032/146] Bump nl.jqno.equalsverifier:equalsverifier from
3.15.5 to 3.15.6
Bumps [nl.jqno.equalsverifier:equalsverifier](https://github.com/jqno/equalsverifier) from 3.15.5 to 3.15.6.
- [Release notes](https://github.com/jqno/equalsverifier/releases)
- [Changelog](https://github.com/jqno/equalsverifier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/jqno/equalsverifier/compare/equalsverifier-3.15.5...equalsverifier-3.15.6)
---
updated-dependencies:
- dependency-name: nl.jqno.equalsverifier:equalsverifier
dependency-type: direct:development
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
---
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index b2b2ef2b8..1c03a552e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -101,7 +101,7 @@
1.11.0
7.4
- 3.15.5
+ 3.15.6
From 2db1e0bdc583ecb4c9f04c7bb05ccb9042107299 Mon Sep 17 00:00:00 2001
From: Lubos Racansky
Date: Mon, 15 Jan 2024 11:41:08 +0100
Subject: [PATCH 033/146] Fix #1245: Warning: Using generated security password
- Exclude UserDetailsServiceAutoConfiguration
---
powerauth-admin/src/main/resources/application.properties | 2 ++
1 file changed, 2 insertions(+)
diff --git a/powerauth-admin/src/main/resources/application.properties b/powerauth-admin/src/main/resources/application.properties
index 1e2768fe9..78997389f 100644
--- a/powerauth-admin/src/main/resources/application.properties
+++ b/powerauth-admin/src/main/resources/application.properties
@@ -55,3 +55,5 @@ management.health.ldap.enabled=false
#management.endpoints.web.exposure.include=health, prometheus
#management.endpoint.prometheus.enabled=true
#management.prometheus.metrics.export.enabled=true
+
+spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration
From f6829969e53ea7e27ab74253fd9830ad8e88b053 Mon Sep 17 00:00:00 2001
From: Roman Strobl
Date: Mon, 15 Jan 2024 19:05:31 +0800
Subject: [PATCH 034/146] Move FIDO2 DB migration to 1.7.x
---
.../powerauth-java-server/1.5.x/db.changelog-version.xml | 1 -
.../20240115-add-columns-fido2.xml} | 0
.../powerauth-java-server/1.7.x/db.changelog-version.xml | 8 ++++++++
.../powerauth-java-server/db.changelog-module.xml | 1 +
4 files changed, 9 insertions(+), 1 deletion(-)
rename docs/db/changelog/changesets/powerauth-java-server/{1.5.x/20230430-add-columns-fido2.xml => 1.7.x/20240115-add-columns-fido2.xml} (100%)
create mode 100644 docs/db/changelog/changesets/powerauth-java-server/1.7.x/db.changelog-version.xml
diff --git a/docs/db/changelog/changesets/powerauth-java-server/1.5.x/db.changelog-version.xml b/docs/db/changelog/changesets/powerauth-java-server/1.5.x/db.changelog-version.xml
index 9576d19dc..ce290d088 100644
--- a/docs/db/changelog/changesets/powerauth-java-server/1.5.x/db.changelog-version.xml
+++ b/docs/db/changelog/changesets/powerauth-java-server/1.5.x/db.changelog-version.xml
@@ -6,7 +6,6 @@
-
diff --git a/docs/db/changelog/changesets/powerauth-java-server/1.5.x/20230430-add-columns-fido2.xml b/docs/db/changelog/changesets/powerauth-java-server/1.7.x/20240115-add-columns-fido2.xml
similarity index 100%
rename from docs/db/changelog/changesets/powerauth-java-server/1.5.x/20230430-add-columns-fido2.xml
rename to docs/db/changelog/changesets/powerauth-java-server/1.7.x/20240115-add-columns-fido2.xml
diff --git a/docs/db/changelog/changesets/powerauth-java-server/1.7.x/db.changelog-version.xml b/docs/db/changelog/changesets/powerauth-java-server/1.7.x/db.changelog-version.xml
new file mode 100644
index 000000000..3adca5752
--- /dev/null
+++ b/docs/db/changelog/changesets/powerauth-java-server/1.7.x/db.changelog-version.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
diff --git a/docs/db/changelog/changesets/powerauth-java-server/db.changelog-module.xml b/docs/db/changelog/changesets/powerauth-java-server/db.changelog-module.xml
index cae01c7d2..08b2c353c 100644
--- a/docs/db/changelog/changesets/powerauth-java-server/db.changelog-module.xml
+++ b/docs/db/changelog/changesets/powerauth-java-server/db.changelog-module.xml
@@ -15,5 +15,6 @@
+
\ No newline at end of file
From 32453c843fdb47cc70ab3bd79dafe33bcc6b7f66 Mon Sep 17 00:00:00 2001
From: Roman Strobl
Date: Mon, 15 Jan 2024 20:38:30 +0800
Subject: [PATCH 035/146] Remove duplicate user_id column migration
---
.../1.7.x/20240115-add-columns-fido2.xml | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/docs/db/changelog/changesets/powerauth-java-server/1.7.x/20240115-add-columns-fido2.xml b/docs/db/changelog/changesets/powerauth-java-server/1.7.x/20240115-add-columns-fido2.xml
index a4203c2d3..a5fc9c5df 100644
--- a/docs/db/changelog/changesets/powerauth-java-server/1.7.x/20240115-add-columns-fido2.xml
+++ b/docs/db/changelog/changesets/powerauth-java-server/1.7.x/20240115-add-columns-fido2.xml
@@ -45,11 +45,7 @@
-
-
-
-
-
+
From 723a01a96ca839b836a75cb175864ef8b37fc4ea Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Roman=20=C5=A0trobl?=
Date: Mon, 15 Jan 2024 20:56:54 +0800
Subject: [PATCH 036/146] Update version in path
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Luboš Račanský
---
.../powerauth-java-server/1.7.x/20240115-add-columns-fido2.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/db/changelog/changesets/powerauth-java-server/1.7.x/20240115-add-columns-fido2.xml b/docs/db/changelog/changesets/powerauth-java-server/1.7.x/20240115-add-columns-fido2.xml
index a5fc9c5df..965a52739 100644
--- a/docs/db/changelog/changesets/powerauth-java-server/1.7.x/20240115-add-columns-fido2.xml
+++ b/docs/db/changelog/changesets/powerauth-java-server/1.7.x/20240115-add-columns-fido2.xml
@@ -45,7 +45,7 @@
-
+
From 763d6566267d73717986ebce624d6c98a00a9979 Mon Sep 17 00:00:00 2001
From: Roman Strobl
Date: Mon, 15 Jan 2024 20:58:34 +0800
Subject: [PATCH 037/146] Update liquibase migration script
---
.../1.7.x/20240115-add-columns-fido2.xml | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/docs/db/changelog/changesets/powerauth-java-server/1.7.x/20240115-add-columns-fido2.xml b/docs/db/changelog/changesets/powerauth-java-server/1.7.x/20240115-add-columns-fido2.xml
index 965a52739..125b77a3e 100644
--- a/docs/db/changelog/changesets/powerauth-java-server/1.7.x/20240115-add-columns-fido2.xml
+++ b/docs/db/changelog/changesets/powerauth-java-server/1.7.x/20240115-add-columns-fido2.xml
@@ -21,7 +21,7 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.9.xsd">
-
+
@@ -33,7 +33,7 @@
-
+
@@ -45,7 +45,7 @@
-
+
From e1ae565060b17e8b0857d0618c742c58d75a3959 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Roman=20=C5=A0trobl?=
Date: Tue, 16 Jan 2024 08:07:58 +0800
Subject: [PATCH 038/146] Fix #1247: FIDO2: Database migration instructions
(#1249)
---
docs/Migration-Instructions.md | 2 ++
docs/PowerAuth-Server-1.7.0.md | 21 +++++++++++++++++++
docs/sql/mssql/migration_1.6.0_1.7.0.sql | 13 ++++++++++++
docs/sql/oracle/migration_1.6.0_1.7.0.sql | 10 +++++++++
docs/sql/postgresql/migration_1.6.0_1.7.0.sql | 11 ++++++++++
5 files changed, 57 insertions(+)
create mode 100644 docs/PowerAuth-Server-1.7.0.md
create mode 100644 docs/sql/mssql/migration_1.6.0_1.7.0.sql
create mode 100644 docs/sql/oracle/migration_1.6.0_1.7.0.sql
create mode 100644 docs/sql/postgresql/migration_1.6.0_1.7.0.sql
diff --git a/docs/Migration-Instructions.md b/docs/Migration-Instructions.md
index f3db65634..2043315cd 100644
--- a/docs/Migration-Instructions.md
+++ b/docs/Migration-Instructions.md
@@ -6,6 +6,8 @@ This page contains PowerAuth Server migration instructions.
When updating across multiple versions, you need to perform all migration steps additively.
+- [PowerAuth Server 1.7.0](./PowerAuth-Server-1.7.0.md)
+- [PowerAuth Server 1.6.0](./PowerAuth-Server-1.6.0.md)
- [PowerAuth Server 1.5.0](./PowerAuth-Server-1.5.0.md)
- [PowerAuth Server 1.4.0](./PowerAuth-Server-1.4.0.md)
- [PowerAuth Server 1.3.0](./PowerAuth-Server-1.3.0.md)
diff --git a/docs/PowerAuth-Server-1.7.0.md b/docs/PowerAuth-Server-1.7.0.md
new file mode 100644
index 000000000..ff4879b28
--- /dev/null
+++ b/docs/PowerAuth-Server-1.7.0.md
@@ -0,0 +1,21 @@
+# Migration from 1.6.x to 1.7.0
+
+This guide contains instructions for migration from PowerAuth Server version `1.6.x` to version `1.7.0`.
+
+## Database Changes
+
+For convenience you can use liquibase for your database migration.
+
+For manual changes use SQL scripts:
+
+- [PostgreSQL script](./sql/postgresql/migration_1.6.0_1.7.0.sql)
+- [Oracle script](./sql/oracle/migration_1.6.0_1.7.0.sql)
+- [MSSQL script](./sql/mssql/migration_1.6.0_1.7.0.sql)
+
+### Updated DB Schema for FIDO2 Support
+
+Following columns have been added to table `pa_activation` for FIDO2 support:
+- `external_id` - external identifier of the activation
+- `protocol` - protocol enumeration: `powerauth` or `fido2`
+
+The data type for column `extras` in table `pa_activation` was changed to `TEXT` / `CLOB` to support larger data.
diff --git a/docs/sql/mssql/migration_1.6.0_1.7.0.sql b/docs/sql/mssql/migration_1.6.0_1.7.0.sql
new file mode 100644
index 000000000..25a5b4719
--- /dev/null
+++ b/docs/sql/mssql/migration_1.6.0_1.7.0.sql
@@ -0,0 +1,13 @@
+-- Changeset powerauth-java-server/1.7.x/20240115-add-columns-fido2::1::Roman Strobl
+-- Add external_id column
+ALTER TABLE pa_activation ADD external_id varchar(255);
+GO
+
+-- Changeset powerauth-java-server/1.7.x/20240115-add-columns-fido2::2::Roman Strobl
+-- Add protocol column
+ALTER TABLE pa_activation ADD protocol varchar(32) CONSTRAINT DF_pa_activation_protocol DEFAULT 'powerauth';
+GO
+
+-- Changeset powerauth-java-server/1.7.x/20240115-add-columns-fido2::3::Roman Strobl
+ALTER TABLE pa_activation ALTER COLUMN extras varchar (max);
+GO
diff --git a/docs/sql/oracle/migration_1.6.0_1.7.0.sql b/docs/sql/oracle/migration_1.6.0_1.7.0.sql
new file mode 100644
index 000000000..7cf04bcd1
--- /dev/null
+++ b/docs/sql/oracle/migration_1.6.0_1.7.0.sql
@@ -0,0 +1,10 @@
+-- Changeset powerauth-java-server/1.7.x/20240115-add-columns-fido2::1::Roman Strobl
+-- Add external_id column
+ALTER TABLE pa_activation ADD external_id VARCHAR2(255);
+
+-- Changeset powerauth-java-server/1.7.x/20240115-add-columns-fido2::2::Roman Strobl
+-- Add protocol column
+ALTER TABLE pa_activation ADD protocol VARCHAR2(32) DEFAULT 'powerauth';
+
+-- Changeset powerauth-java-server/1.7.x/20240115-add-columns-fido2::3::Roman Strobl
+ALTER TABLE pa_activation MODIFY extras CLOB;
diff --git a/docs/sql/postgresql/migration_1.6.0_1.7.0.sql b/docs/sql/postgresql/migration_1.6.0_1.7.0.sql
new file mode 100644
index 000000000..4d022a85f
--- /dev/null
+++ b/docs/sql/postgresql/migration_1.6.0_1.7.0.sql
@@ -0,0 +1,11 @@
+-- Changeset powerauth-java-server/1.7.x/20240115-add-columns-fido2::1::Roman Strobl
+-- Add external_id column
+ALTER TABLE pa_activation ADD external_id VARCHAR(255);
+
+-- Changeset powerauth-java-server/1.7.x/20240115-add-columns-fido2::2::Roman Strobl
+-- Add protocol column
+ALTER TABLE pa_activation ADD protocol VARCHAR(32) DEFAULT 'powerauth';
+
+-- Changeset powerauth-java-server/1.7.x/20240115-add-columns-fido2::3::Roman Strobl
+ALTER TABLE pa_activation ALTER COLUMN extras TYPE TEXT USING (extras::TEXT);
+
From a08f88bb31828af9781dd34e4e361644bf6cf594 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Pe=C5=A1ek?=
Date: Tue, 16 Jan 2024 12:08:38 +0100
Subject: [PATCH 039/146] Fix #1241: Rethrow original GenericServiceException
(#1242)
---
.../powerauth/app/server/service/PowerAuthService.java | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/PowerAuthService.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/PowerAuthService.java
index 462cd7472..aeb1a30a0 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/PowerAuthService.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/PowerAuthService.java
@@ -1728,6 +1728,8 @@ public OperationListResponse findPendingOperationsForUser(OperationListForUserRe
final OperationListResponse response = behavior.getOperationBehavior().findPendingOperationsForUser(convert(request));
logger.info("OperationListForUserRequest succeeded");
return response;
+ } catch (GenericServiceException ex) {
+ throw ex;
} catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
@@ -1748,6 +1750,8 @@ public OperationListResponse findAllOperationsForUser(OperationListForUserReques
final OperationListResponse response = behavior.getOperationBehavior().findAllOperationsForUser(convert(request));
logger.info("OperationListForUserRequest succeeded");
return response;
+ } catch (GenericServiceException ex) {
+ throw ex;
} catch (RuntimeException ex) {
logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
throw ex;
From 1dd93c7d06e6b3346efc0b85e3e15ea0eca9db70 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Roman=20=C5=A0trobl?=
Date: Wed, 17 Jan 2024 14:31:26 +0800
Subject: [PATCH 040/146] Fix #1251: FIDO2: Update OpenAPI documentation
(#1252)
---
.../rest/controller/AssertionController.java | 23 ++++++++++++-
.../controller/RegistrationController.java | 32 ++++++++++++++++++-
2 files changed, 53 insertions(+), 2 deletions(-)
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/AssertionController.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/AssertionController.java
index b2eff2c08..bfc67ad0b 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/AssertionController.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/AssertionController.java
@@ -26,6 +26,9 @@
import com.wultra.powerauth.fido2.service.AssertionService;
import io.getlime.core.rest.model.base.request.ObjectRequest;
import io.getlime.core.rest.model.base.response.ObjectResponse;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
@@ -45,7 +48,7 @@
@RestController
@RequestMapping("fido2/assertions")
@Slf4j
-@Tag(name = "FIDO2 Assertions Controller")
+@Tag(name = "FIDO2 Assertions Controller", description = "API for FIDO2 assertions")
public class AssertionController {
private final AssertionRequestValidator assertionRequestValidator;
@@ -57,6 +60,15 @@ public AssertionController(AssertionRequestValidator assertionRequestValidator,
this.assertionService = assertionService;
}
+ @Operation(
+ summary = "Generate an assertion challenge",
+ description = "Generate a FIDO2 assertion challenge for an operation."
+ )
+ @ApiResponses(value = {
+ @ApiResponse(responseCode = "200", description = "Assertion challenge was generated"),
+ @ApiResponse(responseCode = "400", description = "Invalid request"),
+ @ApiResponse(responseCode = "500", description = "Unexpected server error")
+ })
@PostMapping("challenge")
public ObjectResponse requestAssertionChallenge(@Valid @RequestBody ObjectRequest request) throws Exception {
final AssertionChallengeRequest requestObject = request.getRequestObject();
@@ -64,6 +76,15 @@ public ObjectResponse requestAssertionChallenge(@Val
return new ObjectResponse<>(assertionChallengeResponse);
}
+ @Operation(
+ summary = "Verify an assertion",
+ description = "Verify a FIDO2 assertion for an operation based on an assertion verification request generated and signed by the authenticator."
+ )
+ @ApiResponses(value = {
+ @ApiResponse(responseCode = "200", description = "Assertion verification succeeded"),
+ @ApiResponse(responseCode = "400", description = "Invalid request or assertion verification failed"),
+ @ApiResponse(responseCode = "500", description = "Unexpected server error")
+ })
@PostMapping
public ObjectResponse authenticate(@Valid @RequestBody ObjectRequest request) throws Fido2AuthenticationFailedException {
final AssertionVerificationRequest requestObject = request.getRequestObject();
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/RegistrationController.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/RegistrationController.java
index a219a0870..0f562dd58 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/RegistrationController.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/RegistrationController.java
@@ -27,6 +27,9 @@
import com.wultra.powerauth.fido2.service.RegistrationService;
import io.getlime.core.rest.model.base.request.ObjectRequest;
import io.getlime.core.rest.model.base.response.ObjectResponse;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
@@ -46,7 +49,7 @@
@RestController
@RequestMapping("fido2/registrations")
@Slf4j
-@Tag(name = "FIDO2 Registration Controller")
+@Tag(name = "FIDO2 Registration Controller", description = "API for FIDO2 authenticator registrations")
public class RegistrationController {
private final RegistrationService registrationService;
@@ -56,6 +59,15 @@ public RegistrationController(RegistrationService registrationService) {
this.registrationService = registrationService;
}
+ @Operation(
+ summary = "List registered authenticators",
+ description = "Obtain a list of registered FIDO2 authenticators for specified user."
+ )
+ @ApiResponses(value = {
+ @ApiResponse(responseCode = "200", description = "List of registered authenticators received"),
+ @ApiResponse(responseCode = "400", description = "Invalid request"),
+ @ApiResponse(responseCode = "500", description = "Unexpected server error")
+ })
@PostMapping("list")
public ObjectResponse registeredAuthenticators(@Valid @RequestBody ObjectRequest request) throws Exception {
final RegisteredAuthenticatorsRequest requestObject = request.getRequestObject();
@@ -63,6 +75,15 @@ public ObjectResponse registeredAuthenticators
return new ObjectResponse<>(responseObject);
}
+ @Operation(
+ summary = "Generate a registration challenge",
+ description = "Generate a FIDO2 registration challenge for specified user."
+ )
+ @ApiResponses(value = {
+ @ApiResponse(responseCode = "200", description = "Registration challenge was generated"),
+ @ApiResponse(responseCode = "400", description = "Invalid request"),
+ @ApiResponse(responseCode = "500", description = "Unexpected server error")
+ })
@PostMapping("challenge")
public ObjectResponse requestRegistrationChallenge(@Valid @RequestBody ObjectRequest request) throws Exception {
final RegistrationChallengeRequest requestObject = request.getRequestObject();
@@ -70,6 +91,15 @@ public ObjectResponse requestRegistrationChalleng
return new ObjectResponse<>(responseObject);
}
+ @Operation(
+ summary = "Register an authenticator",
+ description = "Register a FIDO2 authenticator based on a registration request generated and signed by the authenticator."
+ )
+ @ApiResponses(value = {
+ @ApiResponse(responseCode = "200", description = "Registration succeeded"),
+ @ApiResponse(responseCode = "400", description = "Invalid request or request signature verification failed"),
+ @ApiResponse(responseCode = "500", description = "Unexpected server error")
+ })
@PostMapping
public ObjectResponse register(@Valid @RequestBody ObjectRequest request) throws Exception {
final RegistrationRequest requestObject = request.getRequestObject();
From 4acec34f2fa54404f5dfb8a4db0845c5380f47b4 Mon Sep 17 00:00:00 2001
From: Lubos Racansky
Date: Wed, 17 Jan 2024 08:24:50 +0100
Subject: [PATCH 041/146] Fix #1257: Database initialization using Liquibase
fails
---
.../1.4.x/20230322-init-db.xml | 17 -----------------
1 file changed, 17 deletions(-)
diff --git a/docs/db/changelog/changesets/powerauth-java-server/1.4.x/20230322-init-db.xml b/docs/db/changelog/changesets/powerauth-java-server/1.4.x/20230322-init-db.xml
index f6edbd3e6..43579957a 100644
--- a/docs/db/changelog/changesets/powerauth-java-server/1.4.x/20230322-init-db.xml
+++ b/docs/db/changelog/changesets/powerauth-java-server/1.4.x/20230322-init-db.xml
@@ -557,23 +557,6 @@
-
-
-
-
-
-
- Create a new table pa_operation_application
-
-
-
-
-
-
-
-
-
-
From 92d7bfa076ac36c1dae96b3354888a7e3f1e1f10 Mon Sep 17 00:00:00 2001
From: Lubos Racansky
Date: Wed, 17 Jan 2024 10:05:36 +0100
Subject: [PATCH 042/146] Fix #1256: Invalid DDL scripts for release 1.6.0
---
docs/Database-Structure.md | 6 +-
docs/sql/mssql/create_schema.sql | 379 +++++++++++++++
docs/sql/oracle/create_schema.sql | 659 ++++++++++----------------
docs/sql/oracle/delete_schema.sql | 34 --
docs/sql/postgresql/create_schema.sql | 616 ++++++++++--------------
docs/sql/postgresql/delete_schema.sql | 34 --
6 files changed, 879 insertions(+), 849 deletions(-)
create mode 100644 docs/sql/mssql/create_schema.sql
delete mode 100755 docs/sql/oracle/delete_schema.sql
delete mode 100644 docs/sql/postgresql/delete_schema.sql
diff --git a/docs/Database-Structure.md b/docs/Database-Structure.md
index 4329d1cd6..31d5cbb2e 100644
--- a/docs/Database-Structure.md
+++ b/docs/Database-Structure.md
@@ -6,11 +6,7 @@ You can download DDL scripts for supported databases:
- [Oracle - Create Database Schema](./sql/oracle/create_schema.sql)
- [PostgreSQL - Create Database Schema](./sql/postgresql/create_schema.sql)
-
-The drop scripts are available for supported databases:
-
-- [Oracle - Drop Tables and Sequences](./sql/oracle/delete_schema.sql)
-- [PostgreSQL - Drop Tables and Sequences](./sql/postgresql/delete_schema.sql)
+- [MS SQL - Create Database Schema](./sql/mssql/create_schema.sql)
See the overall database schema:
diff --git a/docs/sql/mssql/create_schema.sql b/docs/sql/mssql/create_schema.sql
new file mode 100644
index 000000000..f22ead3e9
--- /dev/null
+++ b/docs/sql/mssql/create_schema.sql
@@ -0,0 +1,379 @@
+-- Changeset powerauth-java-server/1.4.x/20230322-audit.xml::1::Lubos Racansky
+-- Create a new table audit_log
+CREATE TABLE audit_log (audit_log_id varchar(36) NOT NULL, application_name varchar(256) NOT NULL, audit_level varchar(32) NOT NULL, audit_type varchar(256), timestamp_created datetime2 CONSTRAINT DF_audit_log_timestamp_created DEFAULT GETDATE(), message varchar (max) NOT NULL, exception_message varchar (max), stack_trace varchar (max), param varchar (max), calling_class varchar(256) NOT NULL, thread_name varchar(256) NOT NULL, version varchar(256), build_time datetime2, CONSTRAINT PK_AUDIT_LOG PRIMARY KEY (audit_log_id));
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-audit.xml::2::Lubos Racansky
+-- Create a new table audit_log
+CREATE TABLE audit_param (audit_log_id varchar(36), timestamp_created datetime2 CONSTRAINT DF_audit_param_timestamp_created DEFAULT GETDATE(), param_key varchar(256), param_value varchar(4000));
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-audit.xml::3::Lubos Racansky
+-- Create a new index on audit_log(timestamp_created)
+CREATE NONCLUSTERED INDEX audit_log_timestamp ON audit_log(timestamp_created);
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-audit.xml::4::Lubos Racansky
+-- Create a new index on audit_log(application_name)
+CREATE NONCLUSTERED INDEX audit_log_application ON audit_log(application_name);
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-audit.xml::5::Lubos Racansky
+-- Create a new index on audit_log(audit_level)
+CREATE NONCLUSTERED INDEX audit_log_level ON audit_log(audit_level);
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-audit.xml::6::Lubos Racansky
+-- Create a new index on audit_log(audit_type)
+CREATE NONCLUSTERED INDEX audit_log_type ON audit_log(audit_type);
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-audit.xml::7::Lubos Racansky
+-- Create a new index on audit_param(audit_log_id)
+CREATE NONCLUSTERED INDEX audit_param_log ON audit_param(audit_log_id);
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-audit.xml::8::Lubos Racansky
+-- Create a new index on audit_param(timestamp_created)
+CREATE NONCLUSTERED INDEX audit_param_timestamp ON audit_param(timestamp_created);
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-audit.xml::9::Lubos Racansky
+-- Create a new index on audit_log(param_key)
+CREATE NONCLUSTERED INDEX audit_param_key ON audit_param(param_key);
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-audit.xml::10::Lubos Racansky
+-- Create a new index on audit_log(param_value)
+CREATE NONCLUSTERED INDEX audit_param_value ON audit_param(param_value);
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::1::Lubos Racansky
+-- Create a new sequence pa_application_seq
+CREATE SEQUENCE pa_application_seq START WITH 1 INCREMENT BY 1;
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::2::Lubos Racansky
+-- Create a new sequence pa_application_version_seq
+CREATE SEQUENCE pa_application_version_seq START WITH 1 INCREMENT BY 1;
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::3::Lubos Racansky
+-- Create a new sequence pa_master_keypair_seq
+CREATE SEQUENCE pa_master_keypair_seq START WITH 1 INCREMENT BY 1;
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::4::Lubos Racansky
+-- Create a new sequence pa_signature_audit_seq
+CREATE SEQUENCE pa_signature_audit_seq START WITH 1 INCREMENT BY 1;
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::5::Lubos Racansky
+-- Create a new sequence pa_activation_history_seq
+CREATE SEQUENCE pa_activation_history_seq START WITH 1 INCREMENT BY 1;
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::6::Lubos Racansky
+-- Create a new sequence pa_recovery_code_seq
+CREATE SEQUENCE pa_recovery_code_seq START WITH 1 INCREMENT BY 1;
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::7::Lubos Racansky
+-- Create a new sequence pa_recovery_puk_seq
+CREATE SEQUENCE pa_recovery_puk_seq START WITH 1 INCREMENT BY 1;
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::8::Lubos Racansky
+-- Create a new sequence pa_recovery_config_seq
+CREATE SEQUENCE pa_recovery_config_seq START WITH 1 INCREMENT BY 1;
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::9::Lubos Racansky
+-- Create a new sequence pa_operation_template_seq
+CREATE SEQUENCE pa_operation_template_seq START WITH 1 INCREMENT BY 1;
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::10::Lubos Racansky
+-- Create a new table pa_application
+CREATE TABLE pa_application (id int NOT NULL, name varchar(255) NOT NULL, roles varchar(255), CONSTRAINT PK_PA_APPLICATION PRIMARY KEY (id));
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::11::Lubos Racansky
+-- Create a new table pa_master_keypair
+CREATE TABLE pa_master_keypair (id int NOT NULL, application_id int NOT NULL, master_key_private_base64 varchar(255) NOT NULL, master_key_public_base64 varchar(255) NOT NULL, name varchar(255), timestamp_created datetime2(6) NOT NULL, CONSTRAINT PK_PA_MASTER_KEYPAIR PRIMARY KEY (id), CONSTRAINT keypair_application_fk FOREIGN KEY (application_id) REFERENCES pa_application(id));
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::12::Lubos Racansky
+-- Create a new table pa_activation
+CREATE TABLE pa_activation (activation_id varchar(37) NOT NULL, application_id int NOT NULL, user_id varchar(255) NOT NULL, activation_name varchar(255), activation_code varchar(255), activation_status int NOT NULL, activation_otp varchar(255), activation_otp_validation int CONSTRAINT DF_pa_activation_activation_otp_validation DEFAULT 0 NOT NULL, blocked_reason varchar(255), counter int NOT NULL, ctr_data varchar(255), device_public_key_base64 varchar(255), extras varchar(255), platform varchar(255), device_info varchar(255), flags varchar(255), failed_attempts int NOT NULL, max_failed_attempts int CONSTRAINT DF_pa_activation_max_failed_attempts DEFAULT 5 NOT NULL, server_private_key_base64 varchar(255) NOT NULL, server_private_key_encryption int CONSTRAINT DF_pa_activation_server_private_key_encryption DEFAULT 0 NOT NULL, server_public_key_base64 varchar(255) NOT NULL, timestamp_activation_expire datetime2(6) NOT NULL, timestamp_created datetime2(6) NOT NULL, timestamp_last_used datetime2(6) NOT NULL, timestamp_last_change datetime2(6), master_keypair_id int, version int CONSTRAINT DF_pa_activation_version DEFAULT 2, CONSTRAINT PK_PA_ACTIVATION PRIMARY KEY (activation_id), CONSTRAINT activation_keypair_fk FOREIGN KEY (master_keypair_id) REFERENCES pa_master_keypair(id), CONSTRAINT activation_application_fk FOREIGN KEY (application_id) REFERENCES pa_application(id));
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::13::Lubos Racansky
+-- Create a new table pa_application_version
+CREATE TABLE pa_application_version (id int NOT NULL, application_id int NOT NULL, application_key varchar(255), application_secret varchar(255), name varchar(255), supported bit, CONSTRAINT PK_PA_APPLICATION_VERSION PRIMARY KEY (id), CONSTRAINT version_application_fk FOREIGN KEY (application_id) REFERENCES pa_application(id));
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::14::Lubos Racansky
+-- Create a new table pa_signature_audit
+CREATE TABLE pa_signature_audit (id bigint NOT NULL, activation_id varchar(37) NOT NULL, activation_counter int NOT NULL, activation_ctr_data varchar(255), activation_status int, additional_info varchar(255), data_base64 varchar (max), note varchar(255), signature_type varchar(255) NOT NULL, signature varchar(255) NOT NULL, timestamp_created datetime2(6) NOT NULL, valid bit, version int CONSTRAINT DF_pa_signature_audit_version DEFAULT 2, signature_version varchar(255), CONSTRAINT PK_PA_SIGNATURE_AUDIT PRIMARY KEY (id), CONSTRAINT audit_activation_fk FOREIGN KEY (activation_id) REFERENCES pa_activation(activation_id));
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::15::Lubos Racansky
+-- Create a new table pa_integration
+CREATE TABLE pa_integration (id varchar(37) NOT NULL, name varchar(255), client_token varchar(37) NOT NULL, client_secret varchar(37) NOT NULL, CONSTRAINT PK_PA_INTEGRATION PRIMARY KEY (id));
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::16::Lubos Racansky
+-- Create a new table pa_application_callback
+CREATE TABLE pa_application_callback (id varchar(37) NOT NULL, application_id int NOT NULL, name varchar(255), callback_url varchar(1024), type varchar(64) CONSTRAINT DF_pa_application_callback_type DEFAULT 'ACTIVATION_STATUS_CHANGE' NOT NULL, attributes varchar(1024), authentication varchar (max), CONSTRAINT PK_PA_APPLICATION_CALLBACK PRIMARY KEY (id), CONSTRAINT callback_application_fk FOREIGN KEY (application_id) REFERENCES pa_application(id));
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::17::Lubos Racansky
+-- Create a new table pa_token
+CREATE TABLE pa_token (token_id varchar(37) NOT NULL, token_secret varchar(255) NOT NULL, activation_id varchar(37) NOT NULL, signature_type varchar(255) NOT NULL, timestamp_created datetime2(6) NOT NULL, CONSTRAINT PK_PA_TOKEN PRIMARY KEY (token_id), CONSTRAINT activation_token_fk FOREIGN KEY (activation_id) REFERENCES pa_activation(activation_id));
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::18::Lubos Racansky
+-- Create a new table pa_activation_history
+CREATE TABLE pa_activation_history (id bigint NOT NULL, activation_id varchar(37) NOT NULL, activation_status int, event_reason varchar(255), external_user_id varchar(255), timestamp_created datetime2(6) NOT NULL, activation_version int, CONSTRAINT PK_PA_ACTIVATION_HISTORY PRIMARY KEY (id), CONSTRAINT history_activation_fk FOREIGN KEY (activation_id) REFERENCES pa_activation(activation_id));
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::19::Lubos Racansky
+-- Create a new table pa_recovery_code
+CREATE TABLE pa_recovery_code (id bigint NOT NULL, recovery_code varchar(23) NOT NULL, application_id int NOT NULL, user_id varchar(255) NOT NULL, activation_id varchar(37), status int NOT NULL, failed_attempts int CONSTRAINT DF_pa_recovery_code_failed_attempts DEFAULT 0 NOT NULL, max_failed_attempts int CONSTRAINT DF_pa_recovery_code_max_failed_attempts DEFAULT 10 NOT NULL, timestamp_created datetime2(6) NOT NULL, timestamp_last_used datetime2(6), timestamp_last_change datetime2(6), CONSTRAINT PK_PA_RECOVERY_CODE PRIMARY KEY (id), CONSTRAINT recovery_code_application_fk FOREIGN KEY (application_id) REFERENCES pa_application(id), CONSTRAINT recovery_code_activation_fk FOREIGN KEY (activation_id) REFERENCES pa_activation(activation_id));
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::20::Lubos Racansky
+-- Create a new table pa_recovery_puk
+CREATE TABLE pa_recovery_puk (id bigint NOT NULL, recovery_code_id bigint NOT NULL, puk varchar(255), puk_encryption int CONSTRAINT DF_pa_recovery_puk_puk_encryption DEFAULT 0 NOT NULL, puk_index bigint NOT NULL, status int NOT NULL, timestamp_last_change datetime2(6), CONSTRAINT PK_PA_RECOVERY_PUK PRIMARY KEY (id), CONSTRAINT recovery_puk_code_fk FOREIGN KEY (recovery_code_id) REFERENCES pa_recovery_code(id));
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::21::Lubos Racansky
+-- Create a new table pa_recovery_config
+CREATE TABLE pa_recovery_config (id int NOT NULL, application_id int NOT NULL, activation_recovery_enabled bit CONSTRAINT DF_pa_recovery_config_activation_recovery_enabled DEFAULT 0 NOT NULL, recovery_postcard_enabled bit CONSTRAINT DF_pa_recovery_config_recovery_postcard_enabled DEFAULT 0 NOT NULL, allow_multiple_recovery_codes bit CONSTRAINT DF_pa_recovery_config_allow_multiple_recovery_codes DEFAULT 0 NOT NULL, postcard_private_key_base64 varchar(255), postcard_public_key_base64 varchar(255), remote_public_key_base64 varchar(255), postcard_priv_key_encryption int CONSTRAINT DF_pa_recovery_config_postcard_priv_key_encryption DEFAULT 0 NOT NULL, CONSTRAINT PK_PA_RECOVERY_CONFIG PRIMARY KEY (id), CONSTRAINT recovery_config_app_fk FOREIGN KEY (application_id) REFERENCES pa_application(id));
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::22::Lubos Racansky
+-- Create a new table pa_operation
+CREATE TABLE pa_operation (id varchar(37) NOT NULL, user_id varchar(255) NOT NULL, external_id varchar(255), activation_flag varchar(255), operation_type varchar(255) NOT NULL, template_name varchar(255), data varchar (max) NOT NULL, parameters varchar (max), additional_data varchar (max), status int NOT NULL, signature_type varchar(255) NOT NULL, failure_count bigint CONSTRAINT DF_pa_operation_failure_count DEFAULT 0 NOT NULL, max_failure_count bigint NOT NULL, timestamp_created datetime2 NOT NULL, timestamp_expires datetime2 NOT NULL, timestamp_finalized datetime2, risk_flags varchar(255), CONSTRAINT PK_PA_OPERATION PRIMARY KEY (id));
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::23::Lubos Racansky
+-- Create a new table pa_operation_template
+CREATE TABLE pa_operation_template (id bigint NOT NULL, template_name varchar(255) NOT NULL, operation_type varchar(255) NOT NULL, data_template varchar(255) NOT NULL, signature_type varchar(255) NOT NULL, max_failure_count bigint NOT NULL, expiration bigint NOT NULL, risk_flags varchar(255), CONSTRAINT PK_PA_OPERATION_TEMPLATE PRIMARY KEY (id));
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::24::Lubos Racansky
+-- Create a new table pa_operation_application
+CREATE TABLE pa_operation_application (application_id bigint NOT NULL, operation_id varchar(37) NOT NULL, CONSTRAINT PK_PA_OPERATION_APPLICATION PRIMARY KEY (application_id, operation_id));
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::26::Lubos Racansky
+-- Create a new index on pa_activation(application_id)
+CREATE NONCLUSTERED INDEX pa_activation_application ON pa_activation(application_id);
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::27::Lubos Racansky
+-- Create a new index on pa_activation(master_keypair_id)
+CREATE NONCLUSTERED INDEX pa_activation_keypair ON pa_activation(master_keypair_id);
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::28::Lubos Racansky
+-- Create a new index on pa_activation(activation_code)
+CREATE NONCLUSTERED INDEX pa_activation_code ON pa_activation(activation_code);
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::29::Lubos Racansky
+-- Create a new index on pa_activation(user_id)
+CREATE NONCLUSTERED INDEX pa_activation_user_id ON pa_activation(user_id);
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::30::Lubos Racansky
+-- Create a new index on pa_activation(activation_status, timestamp_activation_expire)
+CREATE NONCLUSTERED INDEX pa_activation_expiration ON pa_activation(activation_status, timestamp_activation_expire);
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::31::Lubos Racansky
+-- Create a new index on pa_activation_history(activation_id)
+CREATE NONCLUSTERED INDEX pa_activation_history_act ON pa_activation_history(activation_id);
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::32::Lubos Racansky
+-- Create a new index on pa_activation_history(timestamp_created)
+CREATE NONCLUSTERED INDEX pa_activation_history_created ON pa_activation_history(timestamp_created);
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::33::Lubos Racansky
+-- Create a new index on pa_application_version(application_id)
+CREATE NONCLUSTERED INDEX pa_application_version_app ON pa_application_version(application_id);
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::34::Lubos Racansky
+-- Create a new index on pa_master_keypair(application_id)
+CREATE NONCLUSTERED INDEX pa_master_keypair_application ON pa_master_keypair(application_id);
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::35::Lubos Racansky
+-- Create a new unique index on pa_application_version(application_key)
+CREATE UNIQUE NONCLUSTERED INDEX pa_app_version_app_key ON pa_application_version(application_key);
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::36::Lubos Racansky
+-- Create a new index on pa_application_callback(application_id)
+CREATE NONCLUSTERED INDEX pa_app_callback_app ON pa_application_callback(application_id);
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::37::Lubos Racansky
+-- Create a new unique index on pa_integration(client_token)
+CREATE UNIQUE NONCLUSTERED INDEX pa_integration_token ON pa_integration(client_token);
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::38::Lubos Racansky
+-- Create a new index on pa_signature_audit(activation_id)
+CREATE NONCLUSTERED INDEX pa_signature_audit_activation ON pa_signature_audit(activation_id);
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::39::Lubos Racansky
+-- Create a new index on pa_signature_audit(timestamp_created)
+CREATE NONCLUSTERED INDEX pa_signature_audit_created ON pa_signature_audit(timestamp_created);
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::40::Lubos Racansky
+-- Create a new index on pa_token(activation_id)
+CREATE NONCLUSTERED INDEX pa_token_activation ON pa_token(activation_id);
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::41::Lubos Racansky
+-- Create a new index on pa_recovery_code(recovery_code)
+CREATE NONCLUSTERED INDEX pa_recovery_code_code ON pa_recovery_code(recovery_code);
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::42::Lubos Racansky
+-- Create a new index on pa_recovery_code(application_id)
+CREATE NONCLUSTERED INDEX pa_recovery_code_app ON pa_recovery_code(application_id);
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::43::Lubos Racansky
+-- Create a new index on pa_recovery_code(user_id)
+CREATE NONCLUSTERED INDEX pa_recovery_code_user ON pa_recovery_code(user_id);
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::44::Lubos Racansky
+-- Create a new index on pa_recovery_code(activation_id)
+CREATE NONCLUSTERED INDEX pa_recovery_code_act ON pa_recovery_code(activation_id);
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::45::Lubos Racansky
+-- Create a new unique index on pa_recovery_puk(recovery_code_id, puk_index)
+CREATE UNIQUE NONCLUSTERED INDEX pa_recovery_code_puk ON pa_recovery_puk(recovery_code_id, puk_index);
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::46::Lubos Racansky
+-- Create a new index on pa_recovery_puk(recovery_code_id)
+CREATE NONCLUSTERED INDEX pa_recovery_puk_code ON pa_recovery_puk(recovery_code_id);
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::47::Lubos Racansky
+-- Create a new unique index on pa_recovery_config(application_id)
+CREATE UNIQUE NONCLUSTERED INDEX pa_recovery_config_app ON pa_recovery_config(application_id);
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::48::Lubos Racansky
+-- Create a new unique index on pa_application(name)
+CREATE UNIQUE NONCLUSTERED INDEX pa_application_name ON pa_application(name);
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::49::Lubos Racansky
+-- Create a new index on pa_operation(user_id)
+CREATE NONCLUSTERED INDEX pa_operation_user ON pa_operation(user_id);
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::50::Lubos Racansky
+-- Create a new index on pa_operation(timestamp_created)
+CREATE NONCLUSTERED INDEX pa_operation_ts_created_idx ON pa_operation(timestamp_created);
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::51::Lubos Racansky
+-- Create a new index on pa_operation(timestamp_expires)
+CREATE NONCLUSTERED INDEX pa_operation_ts_expires_idx ON pa_operation(timestamp_expires);
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::52::Lubos Racansky
+-- Create a new index on pa_operation(timestamp_expires, status)
+CREATE NONCLUSTERED INDEX pa_operation_status_exp ON pa_operation(timestamp_expires, status);
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::53::Lubos Racansky
+-- Create a new index on pa_operation_template(template_name)
+CREATE NONCLUSTERED INDEX pa_operation_template_name_idx ON pa_operation_template(template_name);
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230322-shedlock.xml::1::Lubos Racansky
+-- Create a new table shedlock
+CREATE TABLE shedlock (name varchar(64) NOT NULL, lock_until datetime2 NOT NULL, locked_at datetime2 NOT NULL, locked_by varchar(255) NOT NULL, CONSTRAINT PK_SHEDLOCK PRIMARY KEY (name));
+GO
+
+-- Changeset powerauth-java-server/1.4.x/20230323-add-tag-1.4.0.xml::1::Lubos Racansky
+-- Changeset powerauth-java-server/1.5.x/20230324-add-column-signature-data-body::1::Roman Strobl
+-- Add signature_data_body column of type Clob
+ALTER TABLE pa_signature_audit ADD signature_data_body varchar(MAX);
+GO
+
+-- Changeset powerauth-java-server/1.5.x/20230323-add-column-signature-metadata::1::Lubos Racansky
+-- Add signature_metadata column of type Clob
+ALTER TABLE pa_signature_audit ADD signature_metadata varchar(MAX);
+GO
+
+-- Changeset powerauth-java-server/1.5.x/20230426-add-column-totp-seed::1::Lubos Racansky
+-- Add totp_seed column
+ALTER TABLE pa_operation ADD totp_seed varchar(24);
+GO
+
+-- Changeset powerauth-java-server/1.5.x/20230426-add-column-totp-seed::2::Lubos Racansky
+-- Add proximity_check_enabled column
+ALTER TABLE pa_operation_template ADD proximity_check_enabled bit CONSTRAINT DF_pa_operation_template_proximity_check_enabled DEFAULT 0 NOT NULL;
+GO
+
+-- Changeset powerauth-java-server/1.5.x/20230723-add-table-unique-value.xml::1::Roman Strobl
+-- Create a new table pa_unique_value
+CREATE TABLE pa_unique_value (unique_value varchar(255) NOT NULL, type int NOT NULL, timestamp_expires datetime2 NOT NULL, CONSTRAINT PK_PA_UNIQUE_VALUE PRIMARY KEY (unique_value));
+GO
+
+-- Changeset powerauth-java-server/1.5.x/20230723-add-table-unique-value.xml::2::Roman Strobl
+-- Create a new index on pa_unique_value(timestamp_expires)
+CREATE NONCLUSTERED INDEX pa_unique_value_expiration ON pa_unique_value(timestamp_expires);
+GO
+
+-- Changeset powerauth-java-server/1.5.x/20230822-add-tag-1.5.0.xml::1::Lubos Racansky
+-- Changeset powerauth-java-server/1.6.x/20231018-add-constraint-operation-template-name.xml::1::Jan Pesek
+-- Add unique constraint to pa_operation_template.template_name
+ALTER TABLE pa_operation_template ADD CONSTRAINT pa_operation_template_template_name_uk UNIQUE (template_name);
+GO
+
+-- Changeset powerauth-java-server/1.6.x/20231103-add-activation-name-history.xml::1::Lubos Racansky
+-- Add activation_name column to pa_activation_history
+ALTER TABLE pa_activation_history ADD activation_name varchar(255);
+GO
+
+-- Changeset powerauth-java-server/1.6.x/20231106-add-foreign-keys.xml::1::Jan Pesek
+ALTER TABLE pa_operation_application ADD CONSTRAINT pa_operation_application_application_id_fk FOREIGN KEY (application_id) REFERENCES pa_application (id);
+GO
+
+-- Changeset powerauth-java-server/1.6.x/20231106-add-foreign-keys.xml::2::Jan Pesek
+ALTER TABLE pa_operation_application ADD CONSTRAINT pa_operation_application_operation_id_fk FOREIGN KEY (operation_id) REFERENCES pa_operation (id);
+GO
+
+-- Changeset powerauth-java-server/1.6.x/20231112-add-activation-id.xml::1::Jan Dusil
+-- Add activation_id column to pa_operation with foreign key constraint
+ALTER TABLE pa_operation ADD activation_id varchar(37);
+GO
+
+ALTER TABLE pa_operation ADD CONSTRAINT pa_operation_activation_id_fk FOREIGN KEY (activation_id) REFERENCES pa_activation (activation_id);
+GO
+
+-- Changeset powerauth-java-server/1.6.x/20231123-operation-user-nullable.xml::1::Roman Strobl
+-- Make user_id column in table pa_operation nullable
+ALTER TABLE pa_operation ALTER COLUMN user_id varchar(255) NULL;
+GO
+
+-- Changeset powerauth-java-server/1.6.x/20231212-add-tag-1.6.0.xml::1::Lubos Racansky
diff --git a/docs/sql/oracle/create_schema.sql b/docs/sql/oracle/create_schema.sql
index 20330e0f3..45e7cc4df 100755
--- a/docs/sql/oracle/create_schema.sql
+++ b/docs/sql/oracle/create_schema.sql
@@ -1,454 +1,303 @@
---
--- Create sequences.
---
-CREATE SEQUENCE "PA_APPLICATION_SEQ" MINVALUE 1 MAXVALUE 9999999999999999999999999999 INCREMENT BY 1 START WITH 1 CACHE 20 NOORDER NOCYCLE;
-CREATE SEQUENCE "PA_APPLICATION_VERSION_SEQ" MINVALUE 1 MAXVALUE 9999999999999999999999999999 INCREMENT BY 1 START WITH 1 CACHE 20 NOORDER NOCYCLE;
-CREATE SEQUENCE "PA_MASTER_KEYPAIR_SEQ" MINVALUE 1 MAXVALUE 9999999999999999999999999999 INCREMENT BY 1 START WITH 1 CACHE 20 NOORDER NOCYCLE;
-CREATE SEQUENCE "PA_SIGNATURE_AUDIT_SEQ" MINVALUE 1 MAXVALUE 9999999999999999999999999999 INCREMENT BY 1 START WITH 1 CACHE 20 NOORDER NOCYCLE;
-CREATE SEQUENCE "PA_ACTIVATION_HISTORY_SEQ" MINVALUE 1 MAXVALUE 9999999999999999999999999999 INCREMENT BY 1 START WITH 1 CACHE 20 NOORDER NOCYCLE;
-CREATE SEQUENCE "PA_RECOVERY_CODE_SEQ" MINVALUE 1 MAXVALUE 9999999999999999999999999999 INCREMENT BY 1 START WITH 1 CACHE 20 NOORDER NOCYCLE;
-CREATE SEQUENCE "PA_RECOVERY_PUK_SEQ" MINVALUE 1 MAXVALUE 9999999999999999999999999999 INCREMENT BY 1 START WITH 1 CACHE 20 NOORDER NOCYCLE;
-CREATE SEQUENCE "PA_RECOVERY_CONFIG_SEQ" MINVALUE 1 MAXVALUE 9999999999999999999999999999 INCREMENT BY 1 START WITH 1 CACHE 20 NOORDER NOCYCLE;
-CREATE SEQUENCE "PA_OPERATION_TEMPLATE_SEQ" MINVALUE 1 MAXVALUE 9999999999999999999999999999 INCREMENT BY 1 START WITH 1 CACHE 20 NOORDER NOCYCLE;
-
---
--- DDL for Table PA_ACTIVATION
---
-CREATE TABLE "PA_ACTIVATION"
-(
- "ACTIVATION_ID" VARCHAR2(37 CHAR) NOT NULL PRIMARY KEY,
- "APPLICATION_ID" NUMBER(19,0) NOT NULL,
- "USER_ID" VARCHAR2(255 CHAR) NOT NULL,
- "ACTIVATION_NAME" VARCHAR2(255 CHAR),
- "ACTIVATION_CODE" VARCHAR2(255 CHAR),
- "ACTIVATION_STATUS" NUMBER(10,0) NOT NULL,
- "ACTIVATION_OTP" VARCHAR2(255 CHAR),
- "ACTIVATION_OTP_VALIDATION" NUMBER(2,0) DEFAULT 0 NOT NULL,
- "BLOCKED_REASON" VARCHAR2(255 CHAR),
- "COUNTER" NUMBER(19,0) NOT NULL,
- "CTR_DATA" VARCHAR2(255 CHAR),
- "DEVICE_PUBLIC_KEY_BASE64" VARCHAR2(255 CHAR),
- "EXTRAS" VARCHAR2(255 CHAR),
- "PLATFORM" VARCHAR2(255 CHAR),
- "DEVICE_INFO" VARCHAR2(255 CHAR),
- "FLAGS" VARCHAR2(255 CHAR),
- "FAILED_ATTEMPTS" NUMBER(19,0) NOT NULL,
- "MAX_FAILED_ATTEMPTS" NUMBER(19,0) DEFAULT 5 NOT NULL,
- "SERVER_PRIVATE_KEY_BASE64" VARCHAR2(255 CHAR) NOT NULL,
- "SERVER_PRIVATE_KEY_ENCRYPTION" NUMBER(10,0) DEFAULT 0 NOT NULL,
- "SERVER_PUBLIC_KEY_BASE64" VARCHAR2(255 CHAR) NOT NULL,
- "TIMESTAMP_ACTIVATION_EXPIRE" TIMESTAMP (6) NOT NULL,
- "TIMESTAMP_CREATED" TIMESTAMP (6) NOT NULL,
- "TIMESTAMP_LAST_USED" TIMESTAMP (6) NOT NULL,
- "TIMESTAMP_LAST_CHANGE" TIMESTAMP (6),
- "MASTER_KEYPAIR_ID" NUMBER(19,0),
- "VERSION" NUMBER(2,0) DEFAULT 2
-);
-
---
--- DDL for Table PA_APPLICATION
---
-CREATE TABLE "PA_APPLICATION"
-(
- "ID" NUMBER(19,0) NOT NULL PRIMARY KEY,
- "NAME" VARCHAR2(255 CHAR) NOT NULL,
- "ROLES" VARCHAR2(255 CHAR)
-);
-
-
---
--- DDL for Table PA_APPLICATION_VERSION
---
-CREATE TABLE "PA_APPLICATION_VERSION"
-(
- "ID" NUMBER(19,0) NOT NULL PRIMARY KEY,
- "APPLICATION_ID" NUMBER(19,0) NOT NULL,
- "APPLICATION_KEY" VARCHAR2(255 CHAR),
- "APPLICATION_SECRET" VARCHAR2(255 CHAR),
- "NAME" VARCHAR2(255 CHAR),
- "SUPPORTED" NUMBER(1,0)
-);
-
---
--- DDL for Table PA_MASTER_KEYPAIR
---
-CREATE TABLE "PA_MASTER_KEYPAIR"
-(
- "ID" NUMBER(19,0) NOT NULL PRIMARY KEY,
- "APPLICATION_ID" NUMBER(19,0) NOT NULL,
- "MASTER_KEY_PRIVATE_BASE64" VARCHAR2(255 CHAR) NOT NULL,
- "MASTER_KEY_PUBLIC_BASE64" VARCHAR2(255 CHAR) NOT NULL,
- "NAME" VARCHAR2(255 CHAR),
- "TIMESTAMP_CREATED" TIMESTAMP (6) NOT NULL
-);
-
---
--- DDL for Table PA_SIGNATURE_AUDIT
---
-CREATE TABLE "PA_SIGNATURE_AUDIT"
-(
- "ID" NUMBER(19,0) NOT NULL PRIMARY KEY,
- "ACTIVATION_ID" VARCHAR2(37 CHAR) NOT NULL,
- "ACTIVATION_COUNTER" NUMBER(19,0) NOT NULL,
- "ACTIVATION_CTR_DATA" VARCHAR2(255 CHAR),
- "ACTIVATION_STATUS" NUMBER(10,0),
- "ADDITIONAL_INFO" VARCHAR2(255 CHAR),
- "DATA_BASE64" CLOB,
- "NOTE" VARCHAR2(255 CHAR),
- "SIGNATURE_TYPE" VARCHAR2(255 CHAR) NOT NULL,
- "SIGNATURE" VARCHAR2(255 CHAR) NOT NULL,
- "SIGNATURE_METADATA" CLOB,
- "SIGNATURE_DATA_BODY" CLOB,
- "TIMESTAMP_CREATED" TIMESTAMP (6) NOT NULL,
- "VALID" NUMBER(1,0) DEFAULT 0 NOT NULL,
- "VERSION" NUMBER(2,0) DEFAULT 2,
- "SIGNATURE_VERSION" VARCHAR2(255 CHAR)
-);
-
---
--- DDL for Table PA_INTEGRATION
---
-CREATE TABLE "PA_INTEGRATION"
-(
- "ID" VARCHAR2(37 CHAR) NOT NULL PRIMARY KEY,
- "NAME" VARCHAR2(255 CHAR),
- "CLIENT_TOKEN" VARCHAR2(37 CHAR) NOT NULL,
- "CLIENT_SECRET" VARCHAR2(37 CHAR) NOT NULL
-);
-
---
--- DDL for Table PA_APPLICATION_CALLBACK
---
-CREATE TABLE "PA_APPLICATION_CALLBACK"
-(
- "ID" VARCHAR2(37 CHAR) NOT NULL PRIMARY KEY,
- "APPLICATION_ID" NUMBER(19,0) NOT NULL,
- "NAME" VARCHAR2(255 CHAR),
- "CALLBACK_URL" VARCHAR2(1024 CHAR),
- "TYPE" VARCHAR2(64 CHAR) DEFAULT 'ACTIVATION_STATUS_CHANGE' NOT NULL,
- "ATTRIBUTES" VARCHAR2(1024 CHAR),
- "AUTHENTICATION" CLOB
-);
-
---
--- DDL for Table PA_TOKEN
---
-
-CREATE TABLE "PA_TOKEN"
-(
- "TOKEN_ID" VARCHAR2(37 CHAR) NOT NULL PRIMARY KEY,
- "TOKEN_SECRET" VARCHAR2(255 CHAR) NOT NULL,
- "ACTIVATION_ID" VARCHAR2(255 CHAR) NOT NULL,
- "SIGNATURE_TYPE" VARCHAR2(255 CHAR) NOT NULL,
- "TIMESTAMP_CREATED" TIMESTAMP (6) NOT NULL
-);
-
---
--- DDL for Table PA_ACTIVATION_HISTORY
---
-CREATE TABLE "PA_ACTIVATION_HISTORY"
-(
- "ID" NUMBER(19,0) NOT NULL PRIMARY KEY,
- "ACTIVATION_ID" VARCHAR2(37 CHAR) NOT NULL,
- "ACTIVATION_STATUS" NUMBER(10,0),
- "EVENT_REASON" VARCHAR2(255 CHAR),
- "EXTERNAL_USER_ID" VARCHAR2(255 CHAR),
- "TIMESTAMP_CREATED" TIMESTAMP (6) NOT NULL,
- "ACTIVATION_VERSION" NUMBER(2,0)
-);
-
---
--- DDL for Table PA_RECOVERY_CODE
---
-
-CREATE TABLE "PA_RECOVERY_CODE" (
- "ID" NUMBER(19,0) NOT NULL PRIMARY KEY,
- "RECOVERY_CODE" VARCHAR2(23 CHAR) NOT NULL,
- "APPLICATION_ID" NUMBER(19,0) NOT NULL,
- "USER_ID" VARCHAR2(255 CHAR) NOT NULL,
- "ACTIVATION_ID" VARCHAR2(37 CHAR),
- "STATUS" NUMBER(10,0) NOT NULL,
- "FAILED_ATTEMPTS" NUMBER(19,0) DEFAULT 0 NOT NULL,
- "MAX_FAILED_ATTEMPTS" NUMBER(19,0) DEFAULT 10 NOT NULL,
- "TIMESTAMP_CREATED" TIMESTAMP (6) NOT NULL,
- "TIMESTAMP_LAST_USED" TIMESTAMP (6),
- "TIMESTAMP_LAST_CHANGE" TIMESTAMP (6)
-);
-
---
--- DDL for Table PA_RECOVERY_PUK
---
-
-CREATE TABLE "PA_RECOVERY_PUK" (
- "ID" NUMBER(19,0) NOT NULL PRIMARY KEY,
- "RECOVERY_CODE_ID" NUMBER(19,0) NOT NULL,
- "PUK" VARCHAR2(255 CHAR),
- "PUK_ENCRYPTION" NUMBER(10,0) DEFAULT 0 NOT NULL,
- "PUK_INDEX" NUMBER(19,0) NOT NULL,
- "STATUS" NUMBER(10,0) NOT NULL,
- "TIMESTAMP_LAST_CHANGE" TIMESTAMP (6)
-);
-
---
--- DDL for Table PA_RECOVERY_CONFIG
---
-
-CREATE TABLE "PA_RECOVERY_CONFIG" (
- "ID" NUMBER(19,0) NOT NULL PRIMARY KEY,
- "APPLICATION_ID" NUMBER(19,0) NOT NULL,
- "ACTIVATION_RECOVERY_ENABLED" NUMBER(1,0) DEFAULT 0 NOT NULL,
- "RECOVERY_POSTCARD_ENABLED" NUMBER(1,0) DEFAULT 0 NOT NULL,
- "ALLOW_MULTIPLE_RECOVERY_CODES" NUMBER(1,0) DEFAULT 0 NOT NULL,
- "POSTCARD_PRIVATE_KEY_BASE64" VARCHAR2(255 CHAR),
- "POSTCARD_PUBLIC_KEY_BASE64" VARCHAR2(255 CHAR),
- "REMOTE_PUBLIC_KEY_BASE64" VARCHAR2(255 CHAR),
- "POSTCARD_PRIV_KEY_ENCRYPTION" NUMBER(10,0) DEFAULT 0 NOT NULL
-);
-
---
--- DDL for Table PA_OPERATION
---
-CREATE TABLE "PA_OPERATION" (
- "ID" VARCHAR2(37 CHAR) NOT NULL PRIMARY KEY,
- "USER_ID" VARCHAR2(255 CHAR) NOT NULL,
- "EXTERNAL_ID" VARCHAR2(255 CHAR),
- "ACTIVATION_FLAG" VARCHAR2(255),
- "OPERATION_TYPE" VARCHAR2(255 CHAR) NOT NULL,
- "TEMPLATE_NAME" VARCHAR2(255),
- "DATA" CLOB NOT NULL,
- "PARAMETERS" CLOB,
- "ADDITIONAL_DATA" CLOB,
- "STATUS" NUMBER(10,0) NOT NULL,
- "SIGNATURE_TYPE" VARCHAR(255 CHAR) NOT NULL,
- "FAILURE_COUNT" NUMBER(19,0) DEFAULT 0 NOT NULL,
- "MAX_FAILURE_COUNT" NUMBER(19,0) NOT NULL,
- "TIMESTAMP_CREATED" TIMESTAMP(6) NOT NULL,
- "TIMESTAMP_EXPIRES" TIMESTAMP(6) NOT NULL,
- "TIMESTAMP_FINALIZED" TIMESTAMP(6),
- "RISK_FLAGS" VARCHAR2(255 CHAR),
- "TOTP_SEED" VARCHAR2(24 CHAR)
-);
-
---
--- DDL for Table PA_OPERATION_TEMPLATE
---
-CREATE TABLE "PA_OPERATION_TEMPLATE" (
- "ID" NUMBER(19, 0) NOT NULL PRIMARY KEY,
- "TEMPLATE_NAME" VARCHAR2(255 CHAR) NOT NULL,
- "OPERATION_TYPE" VARCHAR2(255 CHAR) NOT NULL,
- "DATA_TEMPLATE" VARCHAR2(255 CHAR) NOT NULL,
- "SIGNATURE_TYPE" VARCHAR2(255 CHAR) NOT NULL,
- "MAX_FAILURE_COUNT" NUMBER(19, 0) NOT NULL,
- "EXPIRATION" NUMBER(19, 0) NOT NULL,
- "RISK_FLAGS" VARCHAR2(255 CHAR),
- "PROXIMITY_CHECK_ENABLED" NUMBER(1, 0) DEFAULT 0 NOT NULL
-);
-
---
--- DDL for Table PA_OPERATION_APPLICATION
---
-CREATE TABLE pa_operation_application (
- "APPLICATION_ID" NUMBER(19,0) NOT NULL,
- "OPERATION_ID" VARCHAR2(37 CHAR) NOT NULL,
- CONSTRAINT pa_operation_application_pk PRIMARY KEY ("APPLICATION_ID", "OPERATION_ID")
-);
-
---
--- DDL for Table PA_UNIQUE_VALUE
---
-CREATE TABLE PA_UNIQUE_VALUE (
- unique_value VARCHAR2(255 CHAR) NOT NULL PRIMARY KEY,
- type NUMBER(10,0) NOT NULL,
- timestamp_expires TIMESTAMP NOT NULL
-);
+-- Changeset powerauth-java-server/1.4.x/20230322-audit.xml::1::Lubos Racansky
+-- Create a new table audit_log
+CREATE TABLE audit_log (audit_log_id VARCHAR2(36) NOT NULL, application_name VARCHAR2(256) NOT NULL, audit_level VARCHAR2(32) NOT NULL, audit_type VARCHAR2(256), timestamp_created TIMESTAMP DEFAULT sysdate, message CLOB NOT NULL, exception_message CLOB, stack_trace CLOB, param CLOB, calling_class VARCHAR2(256) NOT NULL, thread_name VARCHAR2(256) NOT NULL, version VARCHAR2(256), build_time TIMESTAMP, CONSTRAINT PK_AUDIT_LOG PRIMARY KEY (audit_log_id));
-CREATE INDEX pa_unique_value_expiration ON pa_unique_value(timestamp_expires);
+-- Changeset powerauth-java-server/1.4.x/20230322-audit.xml::2::Lubos Racansky
+-- Create a new table audit_log
+CREATE TABLE audit_param (audit_log_id VARCHAR2(36), timestamp_created TIMESTAMP DEFAULT sysdate, param_key VARCHAR2(256), param_value VARCHAR2(4000));
+
+-- Changeset powerauth-java-server/1.4.x/20230322-audit.xml::3::Lubos Racansky
+-- Create a new index on audit_log(timestamp_created)
+CREATE INDEX audit_log_timestamp ON audit_log(timestamp_created);
+
+-- Changeset powerauth-java-server/1.4.x/20230322-audit.xml::4::Lubos Racansky
+-- Create a new index on audit_log(application_name)
+CREATE INDEX audit_log_application ON audit_log(application_name);
+
+-- Changeset powerauth-java-server/1.4.x/20230322-audit.xml::5::Lubos Racansky
+-- Create a new index on audit_log(audit_level)
+CREATE INDEX audit_log_level ON audit_log(audit_level);
+
+-- Changeset powerauth-java-server/1.4.x/20230322-audit.xml::6::Lubos Racansky
+-- Create a new index on audit_log(audit_type)
+CREATE INDEX audit_log_type ON audit_log(audit_type);
+
+-- Changeset powerauth-java-server/1.4.x/20230322-audit.xml::7::Lubos Racansky
+-- Create a new index on audit_param(audit_log_id)
+CREATE INDEX audit_param_log ON audit_param(audit_log_id);
+
+-- Changeset powerauth-java-server/1.4.x/20230322-audit.xml::8::Lubos Racansky
+-- Create a new index on audit_param(timestamp_created)
+CREATE INDEX audit_param_timestamp ON audit_param(timestamp_created);
+
+-- Changeset powerauth-java-server/1.4.x/20230322-audit.xml::9::Lubos Racansky
+-- Create a new index on audit_log(param_key)
+CREATE INDEX audit_param_key ON audit_param(param_key);
+
+-- Changeset powerauth-java-server/1.4.x/20230322-audit.xml::10::Lubos Racansky
+-- Create a new index on audit_log(param_value)
+CREATE INDEX audit_param_value ON audit_param(param_value);
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::1::Lubos Racansky
+-- Create a new sequence pa_application_seq
+CREATE SEQUENCE pa_application_seq START WITH 1 INCREMENT BY 1 CACHE 20;
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::2::Lubos Racansky
+-- Create a new sequence pa_application_version_seq
+CREATE SEQUENCE pa_application_version_seq START WITH 1 INCREMENT BY 1 CACHE 20;
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::3::Lubos Racansky
+-- Create a new sequence pa_master_keypair_seq
+CREATE SEQUENCE pa_master_keypair_seq START WITH 1 INCREMENT BY 1 CACHE 20;
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::4::Lubos Racansky
+-- Create a new sequence pa_signature_audit_seq
+CREATE SEQUENCE pa_signature_audit_seq START WITH 1 INCREMENT BY 1 CACHE 20;
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::5::Lubos Racansky
+-- Create a new sequence pa_activation_history_seq
+CREATE SEQUENCE pa_activation_history_seq START WITH 1 INCREMENT BY 1 CACHE 20;
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::6::Lubos Racansky
+-- Create a new sequence pa_recovery_code_seq
+CREATE SEQUENCE pa_recovery_code_seq START WITH 1 INCREMENT BY 1 CACHE 20;
---
--- DDL for Table SHEDLOCK
---
-BEGIN EXECUTE IMMEDIATE 'CREATE TABLE shedlock (
- name VARCHAR(64) NOT NULL PRIMARY KEY,
- lock_until TIMESTAMP(3) NOT NULL,
- locked_at TIMESTAMP(3) NOT NULL,
- locked_by VARCHAR(255) NOT NULL
-)';
-EXCEPTION WHEN OTHERS THEN IF SQLCODE != -955 THEN RAISE; END IF; END;
-/
-
---
--- Create audit log table.
---
-BEGIN EXECUTE IMMEDIATE 'CREATE TABLE audit_log (
- audit_log_id VARCHAR2(36 CHAR) PRIMARY KEY,
- application_name VARCHAR2(256 CHAR) NOT NULL,
- audit_level VARCHAR2(32 CHAR) NOT NULL,
- audit_type VARCHAR2(256 CHAR),
- timestamp_created TIMESTAMP,
- message CLOB NOT NULL,
- exception_message CLOB,
- stack_trace CLOB,
- param CLOB,
- calling_class VARCHAR2(256 CHAR) NOT NULL,
- thread_name VARCHAR2(256 CHAR) NOT NULL,
- version VARCHAR2(256 CHAR),
- build_time TIMESTAMP
-)';
-EXCEPTION WHEN OTHERS THEN IF SQLCODE != -955 THEN RAISE; END IF; END;
-/
-
---
--- Create audit parameters table.
---
-BEGIN EXECUTE IMMEDIATE 'CREATE TABLE audit_param (
- audit_log_id VARCHAR2(36 CHAR),
- timestamp_created TIMESTAMP,
- param_key VARCHAR2(256 CHAR),
- param_value VARCHAR2(4000 CHAR)
-)';
-EXCEPTION WHEN OTHERS THEN IF SQLCODE != -955 THEN RAISE; END IF; END;
-/
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::7::Lubos Racansky
+-- Create a new sequence pa_recovery_puk_seq
+CREATE SEQUENCE pa_recovery_puk_seq START WITH 1 INCREMENT BY 1 CACHE 20;
---
--- Ref Constraints for Table PA_ACTIVATION
---
-ALTER TABLE "PA_ACTIVATION" ADD CONSTRAINT "ACTIVATION_KEYPAIR_FK" FOREIGN KEY ("MASTER_KEYPAIR_ID") REFERENCES "PA_MASTER_KEYPAIR" ("ID") ENABLE;
-ALTER TABLE "PA_ACTIVATION" ADD CONSTRAINT "ACTIVATION_APPLICATION_FK" FOREIGN KEY ("APPLICATION_ID") REFERENCES "PA_APPLICATION" ("ID") ENABLE;
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::8::Lubos Racansky
+-- Create a new sequence pa_recovery_config_seq
+CREATE SEQUENCE pa_recovery_config_seq START WITH 1 INCREMENT BY 1 CACHE 20;
---
--- Ref Constraints for Table PA_APPLICATION_VERSION
---
-ALTER TABLE "PA_APPLICATION_VERSION" ADD CONSTRAINT "VERSION_APPLICATION_FK" FOREIGN KEY ("APPLICATION_ID") REFERENCES "PA_APPLICATION" ("ID") ENABLE;
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::9::Lubos Racansky
+-- Create a new sequence pa_operation_template_seq
+CREATE SEQUENCE pa_operation_template_seq START WITH 1 INCREMENT BY 1 CACHE 20;
---
--- Ref Constraints for Table PA_MASTER_KEYPAIR
---
-ALTER TABLE "PA_MASTER_KEYPAIR" ADD CONSTRAINT "KEYPAIR_APPLICATION_FK" FOREIGN KEY ("APPLICATION_ID") REFERENCES "PA_APPLICATION" ("ID") ENABLE;
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::10::Lubos Racansky
+-- Create a new table pa_application
+CREATE TABLE pa_application (id INTEGER NOT NULL, name VARCHAR2(255) NOT NULL, roles VARCHAR2(255), CONSTRAINT PK_PA_APPLICATION PRIMARY KEY (id));
---
--- Ref Constraints for Table PA_SIGNATURE_AUDIT
---
-ALTER TABLE "PA_SIGNATURE_AUDIT" ADD CONSTRAINT "AUDIT_ACTIVATION_FK" FOREIGN KEY ("ACTIVATION_ID") REFERENCES "PA_ACTIVATION" ("ACTIVATION_ID") ENABLE;
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::11::Lubos Racansky
+-- Create a new table pa_master_keypair
+CREATE TABLE pa_master_keypair (id INTEGER NOT NULL, application_id INTEGER NOT NULL, master_key_private_base64 VARCHAR2(255) NOT NULL, master_key_public_base64 VARCHAR2(255) NOT NULL, name VARCHAR2(255), timestamp_created TIMESTAMP(6) NOT NULL, CONSTRAINT PK_PA_MASTER_KEYPAIR PRIMARY KEY (id), CONSTRAINT keypair_application_fk FOREIGN KEY (application_id) REFERENCES pa_application(id));
---
--- Ref Constraints for Table PA_APPLICATION_CALLBACK
---
-ALTER TABLE "PA_APPLICATION_CALLBACK" ADD CONSTRAINT "CALLBACK_APPLICATION_FK" FOREIGN KEY ("APPLICATION_ID") REFERENCES "PA_APPLICATION" ("ID") ENABLE;
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::12::Lubos Racansky
+-- Create a new table pa_activation
+CREATE TABLE pa_activation (activation_id VARCHAR2(37) NOT NULL, application_id INTEGER NOT NULL, user_id VARCHAR2(255) NOT NULL, activation_name VARCHAR2(255), activation_code VARCHAR2(255), activation_status INTEGER NOT NULL, activation_otp VARCHAR2(255), activation_otp_validation INTEGER DEFAULT 0 NOT NULL, blocked_reason VARCHAR2(255), counter INTEGER NOT NULL, ctr_data VARCHAR2(255), device_public_key_base64 VARCHAR2(255), extras VARCHAR2(255), platform VARCHAR2(255), device_info VARCHAR2(255), flags VARCHAR2(255), failed_attempts INTEGER NOT NULL, max_failed_attempts INTEGER DEFAULT 5 NOT NULL, server_private_key_base64 VARCHAR2(255) NOT NULL, server_private_key_encryption INTEGER DEFAULT 0 NOT NULL, server_public_key_base64 VARCHAR2(255) NOT NULL, timestamp_activation_expire TIMESTAMP(6) NOT NULL, timestamp_created TIMESTAMP(6) NOT NULL, timestamp_last_used TIMESTAMP(6) NOT NULL, timestamp_last_change TIMESTAMP(6), master_keypair_id INTEGER, version INTEGER DEFAULT 2, CONSTRAINT PK_PA_ACTIVATION PRIMARY KEY (activation_id), CONSTRAINT activation_keypair_fk FOREIGN KEY (master_keypair_id) REFERENCES pa_master_keypair(id), CONSTRAINT activation_application_fk FOREIGN KEY (application_id) REFERENCES pa_application(id));
---
--- Ref Constraints for Table PA_TOKEN
---
-ALTER TABLE "PA_TOKEN" ADD CONSTRAINT "ACTIVATION_TOKEN_FK" FOREIGN KEY ("ACTIVATION_ID") REFERENCES "PA_ACTIVATION" ("ACTIVATION_ID") ENABLE;
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::13::Lubos Racansky
+-- Create a new table pa_application_version
+CREATE TABLE pa_application_version (id INTEGER NOT NULL, application_id INTEGER NOT NULL, application_key VARCHAR2(255), application_secret VARCHAR2(255), name VARCHAR2(255), supported BOOLEAN, CONSTRAINT PK_PA_APPLICATION_VERSION PRIMARY KEY (id), CONSTRAINT version_application_fk FOREIGN KEY (application_id) REFERENCES pa_application(id));
---
--- Ref Constraints for Table PA_ACTIVATION_HISTORY
---
-ALTER TABLE "PA_ACTIVATION_HISTORY" ADD CONSTRAINT "HISTORY_ACTIVATION_FK" FOREIGN KEY ("ACTIVATION_ID") REFERENCES "PA_ACTIVATION" ("ACTIVATION_ID") ENABLE;
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::14::Lubos Racansky
+-- Create a new table pa_signature_audit
+CREATE TABLE pa_signature_audit (id NUMBER(38, 0) NOT NULL, activation_id VARCHAR2(37) NOT NULL, activation_counter INTEGER NOT NULL, activation_ctr_data VARCHAR2(255), activation_status INTEGER, additional_info VARCHAR2(255), data_base64 CLOB, note VARCHAR2(255), signature_type VARCHAR2(255) NOT NULL, signature VARCHAR2(255) NOT NULL, timestamp_created TIMESTAMP(6) NOT NULL, valid BOOLEAN, version INTEGER DEFAULT 2, signature_version VARCHAR2(255), CONSTRAINT PK_PA_SIGNATURE_AUDIT PRIMARY KEY (id), CONSTRAINT audit_activation_fk FOREIGN KEY (activation_id) REFERENCES pa_activation(activation_id));
---
--- Ref Constraints for Table PA_RECOVERY_CODE
---
-ALTER TABLE "PA_RECOVERY_CODE" ADD CONSTRAINT "RECOVERY_CODE_APPLICATION_FK" FOREIGN KEY ("APPLICATION_ID") REFERENCES "PA_APPLICATION" ("ID") ENABLE;
-ALTER TABLE "PA_RECOVERY_CODE" ADD CONSTRAINT "RECOVERY_CODE_ACTIVATION_FK" FOREIGN KEY ("ACTIVATION_ID") REFERENCES "PA_ACTIVATION" ("ACTIVATION_ID") ENABLE;
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::15::Lubos Racansky
+-- Create a new table pa_integration
+CREATE TABLE pa_integration (id VARCHAR2(37) NOT NULL, name VARCHAR2(255), client_token VARCHAR2(37) NOT NULL, client_secret VARCHAR2(37) NOT NULL, CONSTRAINT PK_PA_INTEGRATION PRIMARY KEY (id));
---
--- Ref Constraints for Table PA_RECOVERY_PUK
---
-ALTER TABLE "PA_RECOVERY_PUK" ADD CONSTRAINT "RECOVERY_PUK_CODE_FK" FOREIGN KEY ("RECOVERY_CODE_ID") REFERENCES "PA_RECOVERY_CODE" ("ID") ENABLE;
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::16::Lubos Racansky
+-- Create a new table pa_application_callback
+CREATE TABLE pa_application_callback (id VARCHAR2(37) NOT NULL, application_id INTEGER NOT NULL, name VARCHAR2(255), callback_url VARCHAR2(1024), type VARCHAR2(64) DEFAULT 'ACTIVATION_STATUS_CHANGE' NOT NULL, attributes VARCHAR2(1024), authentication CLOB, CONSTRAINT PK_PA_APPLICATION_CALLBACK PRIMARY KEY (id), CONSTRAINT callback_application_fk FOREIGN KEY (application_id) REFERENCES pa_application(id));
---
--- Ref Constraints for Table PA_RECOVERY_CONFIG
---
-ALTER TABLE "PA_RECOVERY_CONFIG" ADD CONSTRAINT "RECOVERY_CONFIG_APP_FK" FOREIGN KEY ("APPLICATION_ID") REFERENCES "PA_APPLICATION" ("ID") ENABLE;
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::17::Lubos Racansky
+-- Create a new table pa_token
+CREATE TABLE pa_token (token_id VARCHAR2(37) NOT NULL, token_secret VARCHAR2(255) NOT NULL, activation_id VARCHAR2(37) NOT NULL, signature_type VARCHAR2(255) NOT NULL, timestamp_created TIMESTAMP(6) NOT NULL, CONSTRAINT PK_PA_TOKEN PRIMARY KEY (token_id), CONSTRAINT activation_token_fk FOREIGN KEY (activation_id) REFERENCES pa_activation(activation_id));
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::18::Lubos Racansky
+-- Create a new table pa_activation_history
+CREATE TABLE pa_activation_history (id NUMBER(38, 0) NOT NULL, activation_id VARCHAR2(37) NOT NULL, activation_status INTEGER, event_reason VARCHAR2(255), external_user_id VARCHAR2(255), timestamp_created TIMESTAMP(6) NOT NULL, activation_version INTEGER, CONSTRAINT PK_PA_ACTIVATION_HISTORY PRIMARY KEY (id), CONSTRAINT history_activation_fk FOREIGN KEY (activation_id) REFERENCES pa_activation(activation_id));
----
---- Indexes for better performance. Oracle does not create indexes on foreign key automatically.
----
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::19::Lubos Racansky
+-- Create a new table pa_recovery_code
+CREATE TABLE pa_recovery_code (id NUMBER(38, 0) NOT NULL, recovery_code VARCHAR2(23) NOT NULL, application_id INTEGER NOT NULL, user_id VARCHAR2(255) NOT NULL, activation_id VARCHAR2(37), status INTEGER NOT NULL, failed_attempts INTEGER DEFAULT 0 NOT NULL, max_failed_attempts INTEGER DEFAULT 10 NOT NULL, timestamp_created TIMESTAMP(6) NOT NULL, timestamp_last_used TIMESTAMP(6), timestamp_last_change TIMESTAMP(6), CONSTRAINT PK_PA_RECOVERY_CODE PRIMARY KEY (id), CONSTRAINT recovery_code_activation_fk FOREIGN KEY (activation_id) REFERENCES pa_activation(activation_id), CONSTRAINT recovery_code_application_fk FOREIGN KEY (application_id) REFERENCES pa_application(id));
-CREATE INDEX PA_ACTIVATION_APPLICATION ON PA_ACTIVATION(APPLICATION_ID);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::20::Lubos Racansky
+-- Create a new table pa_recovery_puk
+CREATE TABLE pa_recovery_puk (id NUMBER(38, 0) NOT NULL, recovery_code_id NUMBER(38, 0) NOT NULL, puk VARCHAR2(255), puk_encryption INTEGER DEFAULT 0 NOT NULL, puk_index NUMBER(38, 0) NOT NULL, status INTEGER NOT NULL, timestamp_last_change TIMESTAMP(6), CONSTRAINT PK_PA_RECOVERY_PUK PRIMARY KEY (id), CONSTRAINT recovery_puk_code_fk FOREIGN KEY (recovery_code_id) REFERENCES pa_recovery_code(id));
-CREATE INDEX PA_ACTIVATION_KEYPAIR ON PA_ACTIVATION(MASTER_KEYPAIR_ID);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::21::Lubos Racansky
+-- Create a new table pa_recovery_config
+CREATE TABLE pa_recovery_config (id INTEGER NOT NULL, application_id INTEGER NOT NULL, activation_recovery_enabled BOOLEAN DEFAULT 0 NOT NULL, recovery_postcard_enabled BOOLEAN DEFAULT 0 NOT NULL, allow_multiple_recovery_codes BOOLEAN DEFAULT 0 NOT NULL, postcard_private_key_base64 VARCHAR2(255), postcard_public_key_base64 VARCHAR2(255), remote_public_key_base64 VARCHAR2(255), postcard_priv_key_encryption INTEGER DEFAULT 0 NOT NULL, CONSTRAINT PK_PA_RECOVERY_CONFIG PRIMARY KEY (id), CONSTRAINT recovery_config_app_fk FOREIGN KEY (application_id) REFERENCES pa_application(id));
-CREATE INDEX PA_ACTIVATION_CODE ON PA_ACTIVATION(ACTIVATION_CODE);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::22::Lubos Racansky
+-- Create a new table pa_operation
+CREATE TABLE pa_operation (id VARCHAR2(37) NOT NULL, user_id VARCHAR2(255) NOT NULL, external_id VARCHAR2(255), activation_flag VARCHAR2(255), operation_type VARCHAR2(255) NOT NULL, template_name VARCHAR2(255), data CLOB NOT NULL, parameters CLOB, additional_data CLOB, status INTEGER NOT NULL, signature_type VARCHAR2(255) NOT NULL, failure_count NUMBER(38, 0) DEFAULT 0 NOT NULL, max_failure_count NUMBER(38, 0) NOT NULL, timestamp_created TIMESTAMP NOT NULL, timestamp_expires TIMESTAMP NOT NULL, timestamp_finalized TIMESTAMP, risk_flags VARCHAR2(255), CONSTRAINT PK_PA_OPERATION PRIMARY KEY (id));
-CREATE INDEX PA_ACTIVATION_USER_ID ON PA_ACTIVATION(USER_ID);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::23::Lubos Racansky
+-- Create a new table pa_operation_template
+CREATE TABLE pa_operation_template (id NUMBER(38, 0) NOT NULL, template_name VARCHAR2(255) NOT NULL, operation_type VARCHAR2(255) NOT NULL, data_template VARCHAR2(255) NOT NULL, signature_type VARCHAR2(255) NOT NULL, max_failure_count NUMBER(38, 0) NOT NULL, expiration NUMBER(38, 0) NOT NULL, risk_flags VARCHAR2(255), CONSTRAINT PK_PA_OPERATION_TEMPLATE PRIMARY KEY (id));
-CREATE INDEX PA_ACTIVATION_EXPIRATION ON PA_ACTIVATION (ACTIVATION_STATUS, TIMESTAMP_ACTIVATION_EXPIRE);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::24::Lubos Racansky
+-- Create a new table pa_operation_application
+CREATE TABLE pa_operation_application (application_id NUMBER(38, 0) NOT NULL, operation_id VARCHAR2(37) NOT NULL, CONSTRAINT PK_PA_OPERATION_APPLICATION PRIMARY KEY (application_id, operation_id));
-CREATE INDEX PA_ACTIVATION_HISTORY_ACT ON PA_ACTIVATION_HISTORY(ACTIVATION_ID);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::26::Lubos Racansky
+-- Create a new index on pa_activation(application_id)
+CREATE INDEX pa_activation_application ON pa_activation(application_id);
-CREATE INDEX PA_ACTIVATION_HISTORY_CREATED ON PA_ACTIVATION_HISTORY(TIMESTAMP_CREATED);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::27::Lubos Racansky
+-- Create a new index on pa_activation(master_keypair_id)
+CREATE INDEX pa_activation_keypair ON pa_activation(master_keypair_id);
-CREATE INDEX PA_APPLICATION_VERSION_APP ON PA_APPLICATION_VERSION(APPLICATION_ID);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::28::Lubos Racansky
+-- Create a new index on pa_activation(activation_code)
+CREATE INDEX pa_activation_code ON pa_activation(activation_code);
-CREATE INDEX PA_MASTER_KEYPAIR_APPLICATION ON PA_MASTER_KEYPAIR(APPLICATION_ID);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::29::Lubos Racansky
+-- Create a new index on pa_activation(user_id)
+CREATE INDEX pa_activation_user_id ON pa_activation(user_id);
-CREATE UNIQUE INDEX PA_APP_VERSION_APP_KEY ON PA_APPLICATION_VERSION(APPLICATION_KEY);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::30::Lubos Racansky
+-- Create a new index on pa_activation(activation_status, timestamp_activation_expire)
+CREATE INDEX pa_activation_expiration ON pa_activation(activation_status, timestamp_activation_expire);
-CREATE INDEX PA_APP_CALLBACK_APP ON PA_APPLICATION_CALLBACK(APPLICATION_ID);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::31::Lubos Racansky
+-- Create a new index on pa_activation_history(activation_id)
+CREATE INDEX pa_activation_history_act ON pa_activation_history(activation_id);
-CREATE UNIQUE INDEX PA_INTEGRATION_TOKEN ON PA_INTEGRATION(CLIENT_TOKEN);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::32::Lubos Racansky
+-- Create a new index on pa_activation_history(timestamp_created)
+CREATE INDEX pa_activation_history_created ON pa_activation_history(timestamp_created);
-CREATE INDEX PA_SIGNATURE_AUDIT_ACTIVATION ON PA_SIGNATURE_AUDIT(ACTIVATION_ID);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::33::Lubos Racansky
+-- Create a new index on pa_application_version(application_id)
+CREATE INDEX pa_application_version_app ON pa_application_version(application_id);
-CREATE INDEX PA_SIGNATURE_AUDIT_CREATED ON PA_SIGNATURE_AUDIT(TIMESTAMP_CREATED);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::34::Lubos Racansky
+-- Create a new index on pa_master_keypair(application_id)
+CREATE INDEX pa_master_keypair_application ON pa_master_keypair(application_id);
-CREATE INDEX PA_TOKEN_ACTIVATION ON PA_TOKEN(ACTIVATION_ID);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::35::Lubos Racansky
+-- Create a new unique index on pa_application_version(application_key)
+CREATE UNIQUE INDEX pa_app_version_app_key ON pa_application_version(application_key);
-CREATE INDEX PA_RECOVERY_CODE ON PA_RECOVERY_CODE(RECOVERY_CODE);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::36::Lubos Racansky
+-- Create a new index on pa_application_callback(application_id)
+CREATE INDEX pa_app_callback_app ON pa_application_callback(application_id);
-CREATE INDEX PA_RECOVERY_CODE_APP ON PA_RECOVERY_CODE(APPLICATION_ID);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::37::Lubos Racansky
+-- Create a new unique index on pa_integration(client_token)
+CREATE UNIQUE INDEX pa_integration_token ON pa_integration(client_token);
-CREATE INDEX PA_RECOVERY_CODE_USER ON PA_RECOVERY_CODE(USER_ID);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::38::Lubos Racansky
+-- Create a new index on pa_signature_audit(activation_id)
+CREATE INDEX pa_signature_audit_activation ON pa_signature_audit(activation_id);
-CREATE INDEX PA_RECOVERY_CODE_ACT ON PA_RECOVERY_CODE(ACTIVATION_ID);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::39::Lubos Racansky
+-- Create a new index on pa_signature_audit(timestamp_created)
+CREATE INDEX pa_signature_audit_created ON pa_signature_audit(timestamp_created);
-CREATE UNIQUE INDEX PA_RECOVERY_CODE_PUK ON PA_RECOVERY_PUK(RECOVERY_CODE_ID, PUK_INDEX);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::40::Lubos Racansky
+-- Create a new index on pa_token(activation_id)
+CREATE INDEX pa_token_activation ON pa_token(activation_id);
-CREATE INDEX PA_RECOVERY_PUK_CODE ON PA_RECOVERY_PUK(RECOVERY_CODE_ID);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::41::Lubos Racansky
+-- Create a new index on pa_recovery_code(recovery_code)
+CREATE INDEX pa_recovery_code_code ON pa_recovery_code(recovery_code);
-CREATE UNIQUE INDEX PA_RECOVERY_CONFIG_APP ON PA_RECOVERY_CONFIG(APPLICATION_ID);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::42::Lubos Racansky
+-- Create a new index on pa_recovery_code(application_id)
+CREATE INDEX pa_recovery_code_app ON pa_recovery_code(application_id);
-CREATE UNIQUE INDEX PA_APPLICATION_NAME ON PA_APPLICATION(NAME);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::43::Lubos Racansky
+-- Create a new index on pa_recovery_code(user_id)
+CREATE INDEX pa_recovery_code_user ON pa_recovery_code(user_id);
-CREATE INDEX PA_OPERATION_USER ON PA_OPERATION(USER_ID);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::44::Lubos Racansky
+-- Create a new index on pa_recovery_code(activation_id)
+CREATE INDEX pa_recovery_code_act ON pa_recovery_code(activation_id);
-CREATE INDEX PA_OPERATION_TS_CREATED_IDX ON PA_OPERATION(TIMESTAMP_CREATED);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::45::Lubos Racansky
+-- Create a new unique index on pa_recovery_puk(recovery_code_id, puk_index)
+CREATE UNIQUE INDEX pa_recovery_code_puk ON pa_recovery_puk(recovery_code_id, puk_index);
-CREATE INDEX PA_OPERATION_TS_EXPIRES_IDX ON PA_OPERATION(TIMESTAMP_EXPIRES);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::46::Lubos Racansky
+-- Create a new index on pa_recovery_puk(recovery_code_id)
+CREATE INDEX pa_recovery_puk_code ON pa_recovery_puk(recovery_code_id);
-CREATE INDEX PA_OPERATION_STATUS_EXP ON PA_OPERATION(TIMESTAMP_EXPIRES, STATUS);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::47::Lubos Racansky
+-- Create a new unique index on pa_recovery_config(application_id)
+CREATE UNIQUE INDEX pa_recovery_config_app ON pa_recovery_config(application_id);
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::48::Lubos Racansky
+-- Create a new unique index on pa_application(name)
+CREATE UNIQUE INDEX pa_application_name ON pa_application(name);
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::49::Lubos Racansky
+-- Create a new index on pa_operation(user_id)
+CREATE INDEX pa_operation_user ON pa_operation(user_id);
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::50::Lubos Racansky
+-- Create a new index on pa_operation(timestamp_created)
+CREATE INDEX pa_operation_ts_created_idx ON pa_operation(timestamp_created);
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::51::Lubos Racansky
+-- Create a new index on pa_operation(timestamp_expires)
+CREATE INDEX pa_operation_ts_expires_idx ON pa_operation(timestamp_expires);
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::52::Lubos Racansky
+-- Create a new index on pa_operation(timestamp_expires, status)
+CREATE INDEX pa_operation_status_exp ON pa_operation(timestamp_expires, status);
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::53::Lubos Racansky
+-- Create a new index on pa_operation_template(template_name)
+CREATE INDEX pa_operation_template_name_idx ON pa_operation_template(template_name);
+
+-- Changeset powerauth-java-server/1.4.x/20230322-shedlock.xml::1::Lubos Racansky
+-- Create a new table shedlock
+CREATE TABLE shedlock (name VARCHAR2(64) NOT NULL, lock_until TIMESTAMP NOT NULL, locked_at TIMESTAMP NOT NULL, locked_by VARCHAR2(255) NOT NULL, CONSTRAINT PK_SHEDLOCK PRIMARY KEY (name));
+
+-- Changeset powerauth-java-server/1.4.x/20230323-add-tag-1.4.0.xml::1::Lubos Racansky
+-- Changeset powerauth-java-server/1.5.x/20230324-add-column-signature-data-body::1::Roman Strobl
+-- Add signature_data_body column of type Clob
+ALTER TABLE pa_signature_audit ADD signature_data_body CLOB;
+
+-- Changeset powerauth-java-server/1.5.x/20230323-add-column-signature-metadata::1::Lubos Racansky
+-- Add signature_metadata column of type Clob
+ALTER TABLE pa_signature_audit ADD signature_metadata CLOB;
+
+-- Changeset powerauth-java-server/1.5.x/20230426-add-column-totp-seed::1::Lubos Racansky
+-- Add totp_seed column
+ALTER TABLE pa_operation ADD totp_seed VARCHAR2(24);
+
+-- Changeset powerauth-java-server/1.5.x/20230426-add-column-totp-seed::2::Lubos Racansky
+-- Add proximity_check_enabled column
+ALTER TABLE pa_operation_template ADD proximity_check_enabled BOOLEAN DEFAULT 0 NOT NULL;
+
+-- Changeset powerauth-java-server/1.5.x/20230723-add-table-unique-value.xml::1::Roman Strobl
+-- Create a new table pa_unique_value
+CREATE TABLE pa_unique_value (unique_value VARCHAR2(255) NOT NULL, type INTEGER NOT NULL, timestamp_expires TIMESTAMP NOT NULL, CONSTRAINT PK_PA_UNIQUE_VALUE PRIMARY KEY (unique_value));
+
+-- Changeset powerauth-java-server/1.5.x/20230723-add-table-unique-value.xml::2::Roman Strobl
+-- Create a new index on pa_unique_value(timestamp_expires)
+CREATE INDEX pa_unique_value_expiration ON pa_unique_value(timestamp_expires);
-CREATE INDEX PA_OPERATION_TEMPLATE_NAME_IDX ON PA_OPERATION_TEMPLATE(TEMPLATE_NAME);
+-- Changeset powerauth-java-server/1.5.x/20230822-add-tag-1.5.0.xml::1::Lubos Racansky
+-- Changeset powerauth-java-server/1.6.x/20231018-add-constraint-operation-template-name.xml::1::Jan Pesek
+-- Add unique constraint to pa_operation_template.template_name
+ALTER TABLE pa_operation_template ADD CONSTRAINT pa_operation_template_template_name_uk UNIQUE (template_name);
---
--- Auditing indexes.
---
-BEGIN EXECUTE IMMEDIATE 'CREATE INDEX audit_log_timestamp ON audit_log (timestamp_created)';
-EXCEPTION WHEN OTHERS THEN IF SQLCODE != -955 THEN RAISE; END IF; END;
-/
+-- Changeset powerauth-java-server/1.6.x/20231103-add-activation-name-history.xml::1::Lubos Racansky
+-- Add activation_name column to pa_activation_history
+ALTER TABLE pa_activation_history ADD activation_name VARCHAR2(255);
-BEGIN EXECUTE IMMEDIATE 'CREATE INDEX audit_log_application ON audit_log (application_name)';
-EXCEPTION WHEN OTHERS THEN IF SQLCODE != -955 THEN RAISE; END IF; END;
-/
+-- Changeset powerauth-java-server/1.6.x/20231106-add-foreign-keys.xml::1::Jan Pesek
+ALTER TABLE pa_operation_application ADD CONSTRAINT pa_operation_application_application_id_fk FOREIGN KEY (application_id) REFERENCES pa_application (id);
-BEGIN EXECUTE IMMEDIATE 'CREATE INDEX audit_log_level ON audit_log (audit_level)';
-EXCEPTION WHEN OTHERS THEN IF SQLCODE != -955 THEN RAISE; END IF; END;
-/
+-- Changeset powerauth-java-server/1.6.x/20231106-add-foreign-keys.xml::2::Jan Pesek
+ALTER TABLE pa_operation_application ADD CONSTRAINT pa_operation_application_operation_id_fk FOREIGN KEY (operation_id) REFERENCES pa_operation (id);
-BEGIN EXECUTE IMMEDIATE 'CREATE INDEX audit_log_type ON audit_log (audit_type)';
-EXCEPTION WHEN OTHERS THEN IF SQLCODE != -955 THEN RAISE; END IF; END;
-/
+-- Changeset powerauth-java-server/1.6.x/20231112-add-activation-id.xml::1::Jan Dusil
+-- Add activation_id column to pa_operation with foreign key constraint
+ALTER TABLE pa_operation ADD activation_id VARCHAR2(37);
-BEGIN EXECUTE IMMEDIATE 'CREATE INDEX audit_param_log ON audit_param (audit_log_id)';
-EXCEPTION WHEN OTHERS THEN IF SQLCODE != -955 THEN RAISE; END IF; END;
-/
+ALTER TABLE pa_operation ADD CONSTRAINT pa_operation_activation_id_fk FOREIGN KEY (activation_id) REFERENCES pa_activation (activation_id);
-BEGIN EXECUTE IMMEDIATE 'CREATE INDEX audit_param_timestamp ON audit_param (timestamp_created)';
-EXCEPTION WHEN OTHERS THEN IF SQLCODE != -955 THEN RAISE; END IF; END;
-/
+-- Changeset powerauth-java-server/1.6.x/20231123-operation-user-nullable.xml::1::Roman Strobl
+-- Make user_id column in table pa_operation nullable
+ALTER TABLE pa_operation MODIFY user_id NULL;
-BEGIN EXECUTE IMMEDIATE 'CREATE INDEX audit_param_key ON audit_param (param_key)';
-EXCEPTION WHEN OTHERS THEN IF SQLCODE != -955 THEN RAISE; END IF; END;
-/
+-- Changeset powerauth-java-server/1.6.x/20231212-add-tag-1.6.0.xml::1::Lubos Racansky
diff --git a/docs/sql/oracle/delete_schema.sql b/docs/sql/oracle/delete_schema.sql
deleted file mode 100755
index ea5bec2e7..000000000
--- a/docs/sql/oracle/delete_schema.sql
+++ /dev/null
@@ -1,34 +0,0 @@
---
--- Drop all tables.
---
-DROP TABLE "PA_ACTIVATION" CASCADE CONSTRAINTS;
-DROP TABLE "PA_ACTIVATION_HISTORY" CASCADE CONSTRAINTS;
-DROP TABLE "PA_APPLICATION" CASCADE CONSTRAINTS;
-DROP TABLE "PA_APPLICATION_VERSION" CASCADE CONSTRAINTS;
-DROP TABLE "PA_TOKEN" CASCADE CONSTRAINTS;
-DROP TABLE "PA_MASTER_KEYPAIR" CASCADE CONSTRAINTS;
-DROP TABLE "PA_SIGNATURE_AUDIT" CASCADE CONSTRAINTS;
-DROP TABLE "PA_INTEGRATION" CASCADE CONSTRAINTS;
-DROP TABLE "PA_APPLICATION_CALLBACK" CASCADE CONSTRAINTS;
-DROP TABLE "PA_RECOVERY_CODE" CASCADE CONSTRAINTS;
-DROP TABLE "PA_RECOVERY_PUK" CASCADE CONSTRAINTS;
-DROP TABLE "PA_RECOVERY_CONFIG" CASCADE CONSTRAINTS;
-DROP TABLE "PA_OPERATION" CASCADE CONSTRAINTS;
-DROP TABLE "PA_OPERATION_TEMPLATE" CASCADE CONSTRAINTS;
-DROP TABLE "PA_UNIQUE_VALUE" CASCADE CONSTRAINTS;
-
--- Optionally drop the shedlock table
--- DROP TABLE "shedlock" CASCADE CONSTRAINTS;
-
---
--- Drop all sequences.
---
-DROP SEQUENCE "PA_APPLICATION_SEQ";
-DROP SEQUENCE "PA_APPLICATION_VERSION_SEQ";
-DROP SEQUENCE "PA_MASTER_KEYPAIR_SEQ";
-DROP SEQUENCE "PA_SIGNATURE_AUDIT_SEQ";
-DROP SEQUENCE "PA_ACTIVATION_HISTORY_SEQ";
-DROP SEQUENCE "PA_RECOVERY_CODE_SEQ";
-DROP SEQUENCE "PA_RECOVERY_PUK_SEQ";
-DROP SEQUENCE "PA_RECOVERY_CONFIG_SEQ";
-DROP SEQUENCE "PA_OPERATION_TEMPLATE_SEQ";
diff --git a/docs/sql/postgresql/create_schema.sql b/docs/sql/postgresql/create_schema.sql
index 485aa57b0..7ab67c672 100644
--- a/docs/sql/postgresql/create_schema.sql
+++ b/docs/sql/postgresql/create_schema.sql
@@ -1,429 +1,303 @@
---
--- Create sequences. Maximum value for PostgreSQL is 9223372036854775807.
---- See: https://www.postgresql.org/docs/9.6/sql-createsequence.html
---
-CREATE SEQUENCE pa_application_seq MINVALUE 1 MAXVALUE 9223372036854775807 INCREMENT BY 1 START WITH 1 CACHE 20;
-CREATE SEQUENCE pa_application_version_seq MINVALUE 1 MAXVALUE 9223372036854775807 INCREMENT BY 1 START WITH 1 CACHE 20;
-CREATE SEQUENCE pa_master_keypair_seq MINVALUE 1 MAXVALUE 9223372036854775807 INCREMENT BY 1 START WITH 1 CACHE 20;
-CREATE SEQUENCE pa_signature_audit_seq MINVALUE 1 MAXVALUE 9223372036854775807 INCREMENT BY 1 START WITH 1 CACHE 20;
-CREATE SEQUENCE pa_activation_history_seq MINVALUE 1 MAXVALUE 9223372036854775807 INCREMENT BY 1 START WITH 1 CACHE 20;
-CREATE SEQUENCE pa_recovery_code_seq MINVALUE 1 MAXVALUE 9223372036854775807 INCREMENT BY 1 START WITH 1 CACHE 20;
-CREATE SEQUENCE pa_recovery_puk_seq MINVALUE 1 MAXVALUE 9223372036854775807 INCREMENT BY 1 START WITH 1 CACHE 20;
-CREATE SEQUENCE pa_recovery_config_seq MINVALUE 1 MAXVALUE 9223372036854775807 INCREMENT BY 1 START WITH 1 CACHE 20;
-CREATE SEQUENCE pa_operation_template_seq MINVALUE 1 MAXVALUE 9223372036854775807 INCREMENT BY 1 START WITH 1 CACHE 20;
-
---
--- DDL for Table PA_ACTIVATION
---
-CREATE TABLE pa_activation
-(
- activation_id VARCHAR(37) NOT NULL PRIMARY KEY,
- application_id INTEGER NOT NULL,
- user_id VARCHAR(255) NOT NULL,
- activation_name VARCHAR(255),
- activation_code VARCHAR(255),
- activation_status INTEGER NOT NULL,
- activation_otp VARCHAR(255),
- activation_otp_validation INTEGER DEFAULT 0 NOT NULL,
- blocked_reason VARCHAR(255),
- counter INTEGER NOT NULL,
- ctr_data VARCHAR(255),
- device_public_key_base64 VARCHAR(255),
- extras VARCHAR(255),
- platform VARCHAR(255),
- device_info VARCHAR(255),
- flags VARCHAR(255),
- failed_attempts INTEGER NOT NULL,
- max_failed_attempts INTEGER DEFAULT 5 NOT NULL,
- server_private_key_base64 VARCHAR(255) NOT NULL,
- server_private_key_encryption INTEGER DEFAULT 0 NOT NULL,
- server_public_key_base64 VARCHAR(255) NOT NULL,
- timestamp_activation_expire TIMESTAMP (6) NOT NULL,
- timestamp_created TIMESTAMP (6) NOT NULL,
- timestamp_last_used TIMESTAMP (6) NOT NULL,
- timestamp_last_change TIMESTAMP (6),
- master_keypair_id INTEGER,
- version INTEGER DEFAULT 2
-);
-
---
--- DDL for Table PA_APPLICATION
---
-CREATE TABLE pa_application
-(
- id INTEGER NOT NULL PRIMARY KEY,
- name VARCHAR(255) NOT NULL,
- roles VARCHAR(255)
-);
-
-
---
--- DDL for Table PA_APPLICATION_VERSION
---
-CREATE TABLE pa_application_version
-(
- id INTEGER NOT NULL PRIMARY KEY,
- application_id INTEGER NOT NULL,
- application_key VARCHAR(255),
- application_secret VARCHAR(255),
- name VARCHAR(255),
- supported BOOLEAN
-);
-
---
--- DDL for Table PA_MASTER_KEYPAIR
---
-CREATE TABLE pa_master_keypair
-(
- id INTEGER NOT NULL PRIMARY KEY,
- application_id INTEGER NOT NULL,
- master_key_private_base64 VARCHAR(255) NOT NULL,
- master_key_public_base64 VARCHAR(255) NOT NULL,
- name VARCHAR(255),
- timestamp_created TIMESTAMP (6) NOT NULL
-);
-
---
--- DDL for Table PA_SIGNATURE_AUDIT
---
-CREATE TABLE pa_signature_audit
-(
- id BIGINT NOT NULL PRIMARY KEY,
- activation_id VARCHAR(37) NOT NULL,
- activation_counter INTEGER NOT NULL,
- activation_ctr_data VARCHAR(255),
- activation_status INTEGER,
- additional_info VARCHAR(255),
- data_base64 TEXT,
- note VARCHAR(255),
- signature_type VARCHAR(255) NOT NULL,
- signature VARCHAR(255) NOT NULL,
- signature_metadata TEXT,
- signature_data_body TEXT,
- timestamp_created TIMESTAMP (6) NOT NULL,
- valid BOOLEAN,
- version INTEGER DEFAULT 2,
- signature_version VARCHAR(255)
-);
-
---
--- DDL for Table PA_INTEGRATION
---
-CREATE TABLE pa_integration
-(
- id VARCHAR(37) NOT NULL PRIMARY KEY,
- name VARCHAR(255),
- client_token VARCHAR(37) NOT NULL,
- client_secret VARCHAR(37) NOT NULL
-);
-
---
--- DDL for Table PA_APPLICATION_CALLBACK
---
-CREATE TABLE pa_application_callback
-(
- id VARCHAR(37) NOT NULL PRIMARY KEY,
- application_id INTEGER NOT NULL,
- name VARCHAR(255),
- callback_url VARCHAR(1024),
- type VARCHAR(64) DEFAULT 'ACTIVATION_STATUS_CHANGE' NOT NULL,
- attributes VARCHAR(1024),
- authentication TEXT
-);
-
---
--- DDL for Table PA_TOKEN
---
-
-CREATE TABLE pa_token
-(
- token_id VARCHAR(37) NOT NULL PRIMARY KEY,
- token_secret VARCHAR(255) NOT NULL,
- activation_id VARCHAR(37) NOT NULL,
- signature_type VARCHAR(255) NOT NULL,
- timestamp_created TIMESTAMP (6) NOT NULL
-);
-
---
--- DDL for Table PA_ACTIVATION_HISTORY
---
-CREATE TABLE pa_activation_history
-(
- id BIGINT NOT NULL PRIMARY KEY,
- activation_id VARCHAR(37) NOT NULL,
- activation_status INTEGER,
- event_reason VARCHAR(255),
- external_user_id VARCHAR(255),
- timestamp_created TIMESTAMP (6) NOT NULL,
- activation_version INTEGER
-);
-
---
--- DDL for Table PA_RECOVERY_CODE
---
-
-CREATE TABLE pa_recovery_code (
- id BIGINT NOT NULL PRIMARY KEY,
- recovery_code VARCHAR(23) NOT NULL,
- application_id INTEGER NOT NULL,
- user_id VARCHAR(255) NOT NULL,
- activation_id VARCHAR(37),
- status INTEGER NOT NULL,
- failed_attempts INTEGER DEFAULT 0 NOT NULL,
- max_failed_attempts INTEGER DEFAULT 10 NOT NULL,
- timestamp_created TIMESTAMP (6) NOT NULL,
- timestamp_last_used TIMESTAMP (6),
- timestamp_last_change TIMESTAMP (6)
-);
-
---
--- DDL for Table PA_RECOVERY_PUK
---
-
-CREATE TABLE pa_recovery_puk (
- id BIGINT NOT NULL PRIMARY KEY,
- recovery_code_id BIGINT NOT NULL,
- puk VARCHAR(255),
- puk_encryption INTEGER DEFAULT 0 NOT NULL,
- puk_index BIGINT NOT NULL,
- status INTEGER NOT NULL,
- timestamp_last_change TIMESTAMP (6)
-);
-
---
--- DDL for Table PA_RECOVERY_CONFIG
---
-
-CREATE TABLE pa_recovery_config (
- id INTEGER NOT NULL PRIMARY KEY,
- application_id INTEGER NOT NULL,
- activation_recovery_enabled BOOLEAN NOT NULL DEFAULT FALSE,
- recovery_postcard_enabled BOOLEAN NOT NULL DEFAULT FALSE,
- allow_multiple_recovery_codes BOOLEAN NOT NULL DEFAULT FALSE,
- postcard_private_key_base64 VARCHAR(255),
- postcard_public_key_base64 VARCHAR(255),
- remote_public_key_base64 VARCHAR(255),
- postcard_priv_key_encryption INTEGER DEFAULT 0 NOT NULL
-);
-
---
--- DDL for Table PA_OPERATION
---
-CREATE TABLE pa_operation (
- id VARCHAR(37) NOT NULL PRIMARY KEY,
- user_id VARCHAR(255) NOT NULL,
- external_id VARCHAR(255),
- activation_flag VARCHAR(255),
- operation_type VARCHAR(255) NOT NULL,
- template_name VARCHAR(255),
- data TEXT NOT NULL,
- parameters TEXT,
- additional_data TEXT,
- status INTEGER NOT NULL,
- signature_type VARCHAR(255) NOT NULL,
- failure_count BIGINT DEFAULT 0 NOT NULL,
- max_failure_count BIGINT NOT NULL,
- timestamp_created TIMESTAMP NOT NULL,
- timestamp_expires TIMESTAMP NOT NULL,
- timestamp_finalized TIMESTAMP,
- risk_flags VARCHAR(255),
- totp_seed VARCHAR(24)
-);
-
---
--- DDL for Table PA_OPERATION_TEMPLATE
---
-CREATE TABLE pa_operation_template (
- id BIGINT NOT NULL PRIMARY KEY,
- template_name VARCHAR(255) NOT NULL,
- operation_type VARCHAR(255) NOT NULL,
- data_template VARCHAR(255) NOT NULL,
- signature_type VARCHAR(255) NOT NULL,
- max_failure_count BIGINT NOT NULL,
- expiration BIGINT NOT NULL,
- risk_flags VARCHAR(255),
- proximity_check_enabled BOOLEAN NOT NULL DEFAULT FALSE
-);
-
---
--- DDL for Table PA_OPERATION_APPLICATION
---
-CREATE TABLE pa_operation_application (
- application_id BIGINT NOT NULL,
- operation_id VARCHAR(37) NOT NULL,
- CONSTRAINT pa_operation_application_pk PRIMARY KEY (application_id, operation_id)
-);
-
---
--- DDL for Table PA_UNIQUE_VALUE
---
-CREATE TABLE pa_unique_value (
- unique_value VARCHAR(255) NOT NULL PRIMARY KEY,
- type INTEGER NOT NULL,
- timestamp_expires TIMESTAMP NOT NULL
-);
-
---
--- DDL for Table SHEDLOCK
---
-CREATE TABLE IF NOT EXISTS shedlock (
- name VARCHAR(64) NOT NULL PRIMARY KEY,
- lock_until TIMESTAMP NOT NULL,
- locked_at TIMESTAMP NOT NULL,
- locked_by VARCHAR(255) NOT NULL
-);
-
---
--- Create audit log table.
---
-CREATE TABLE IF NOT EXISTS audit_log (
- audit_log_id VARCHAR(36) PRIMARY KEY,
- application_name VARCHAR(256) NOT NULL,
- audit_level VARCHAR(32) NOT NULL,
- audit_type VARCHAR(256),
- timestamp_created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
- message TEXT NOT NULL,
- exception_message TEXT,
- stack_trace TEXT,
- param TEXT,
- calling_class VARCHAR(256) NOT NULL,
- thread_name VARCHAR(256) NOT NULL,
- version VARCHAR(256),
- build_time TIMESTAMP
-);
-
---
--- Create audit parameters table.
---
-CREATE TABLE IF NOT EXISTS audit_param (
- audit_log_id VARCHAR(36),
- timestamp_created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
- param_key VARCHAR(256),
- param_value VARCHAR(4000)
-);
-
---
--- Ref Constraints for Table PA_ACTIVATION
---
-ALTER TABLE pa_activation ADD CONSTRAINT activation_keypair_fk FOREIGN KEY (master_keypair_id) REFERENCES pa_master_keypair (id);
-ALTER TABLE pa_activation ADD CONSTRAINT activation_application_fk FOREIGN KEY (application_id) REFERENCES pa_application (id);
-
---
--- Ref Constraints for Table PA_APPLICATION_VERSION
---
-ALTER TABLE pa_application_version ADD CONSTRAINT version_application_fk FOREIGN KEY (application_id) REFERENCES pa_application (id);
-
---
--- Ref Constraints for Table PA_MASTER_KEYPAIR
---
-ALTER TABLE pa_master_keypair ADD CONSTRAINT keypair_application_fk FOREIGN KEY (application_id) REFERENCES pa_application (id);
-
---
--- Ref Constraints for Table PA_SIGNATURE_AUDIT
---
-ALTER TABLE pa_signature_audit ADD CONSTRAINT audit_activation_fk FOREIGN KEY (activation_id) REFERENCES pa_activation (activation_id);
-
---
--- Ref Constraints for Table PA_APPLICATION_CALLBACK
---
-ALTER TABLE pa_application_callback ADD CONSTRAINT callback_application_fk FOREIGN KEY (application_id) REFERENCES pa_application (id);
-
---
--- Ref Constraints for Table PA_TOKEN
---
-ALTER TABLE pa_token ADD CONSTRAINT activation_token_fk FOREIGN KEY (activation_id) REFERENCES pa_activation (activation_id);
-
---
--- Ref Constraints for Table PA_ACTIVATION_HISTORY
---
-ALTER TABLE pa_activation_history ADD CONSTRAINT history_activation_fk FOREIGN KEY (activation_id) REFERENCES pa_activation (activation_id);
-
---
--- Ref Constraints for Table PA_RECOVERY_CODE
---
-ALTER TABLE pa_recovery_code ADD CONSTRAINT recovery_code_application_fk FOREIGN KEY (application_id) REFERENCES pa_application (id);
-ALTER TABLE pa_recovery_code ADD CONSTRAINT recovery_code_activation_fk FOREIGN KEY (activation_id) REFERENCES pa_activation (activation_id);
-
---
--- Ref Constraints for Table PA_RECOVERY_PUK
---
-ALTER TABLE pa_recovery_puk ADD CONSTRAINT recovery_puk_code_fk FOREIGN KEY (recovery_code_id) REFERENCES pa_recovery_code (id);
-
---
--- Ref Constraints for Table PA_RECOVERY_CONFIG
---
-ALTER TABLE pa_recovery_config ADD CONSTRAINT recovery_config_app_fk FOREIGN KEY (application_id) REFERENCES pa_application (id);
-
-
----
---- Indexes for better performance. PostgreSQL does not CREATE INDEXes ON foreign key automatically.
----
+-- Changeset powerauth-java-server/1.4.x/20230322-audit.xml::1::Lubos Racansky
+-- Create a new table audit_log
+CREATE TABLE audit_log (audit_log_id VARCHAR(36) NOT NULL, application_name VARCHAR(256) NOT NULL, audit_level VARCHAR(32) NOT NULL, audit_type VARCHAR(256), timestamp_created TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW(), message TEXT NOT NULL, exception_message TEXT, stack_trace TEXT, param TEXT, calling_class VARCHAR(256) NOT NULL, thread_name VARCHAR(256) NOT NULL, version VARCHAR(256), build_time TIMESTAMP WITHOUT TIME ZONE, CONSTRAINT audit_log_pkey PRIMARY KEY (audit_log_id));
+-- Changeset powerauth-java-server/1.4.x/20230322-audit.xml::2::Lubos Racansky
+-- Create a new table audit_log
+CREATE TABLE audit_param (audit_log_id VARCHAR(36), timestamp_created TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW(), param_key VARCHAR(256), param_value VARCHAR(4000));
+
+-- Changeset powerauth-java-server/1.4.x/20230322-audit.xml::3::Lubos Racansky
+-- Create a new index on audit_log(timestamp_created)
+CREATE INDEX audit_log_timestamp ON audit_log(timestamp_created);
+
+-- Changeset powerauth-java-server/1.4.x/20230322-audit.xml::4::Lubos Racansky
+-- Create a new index on audit_log(application_name)
+CREATE INDEX audit_log_application ON audit_log(application_name);
+
+-- Changeset powerauth-java-server/1.4.x/20230322-audit.xml::5::Lubos Racansky
+-- Create a new index on audit_log(audit_level)
+CREATE INDEX audit_log_level ON audit_log(audit_level);
+
+-- Changeset powerauth-java-server/1.4.x/20230322-audit.xml::6::Lubos Racansky
+-- Create a new index on audit_log(audit_type)
+CREATE INDEX audit_log_type ON audit_log(audit_type);
+
+-- Changeset powerauth-java-server/1.4.x/20230322-audit.xml::7::Lubos Racansky
+-- Create a new index on audit_param(audit_log_id)
+CREATE INDEX audit_param_log ON audit_param(audit_log_id);
+
+-- Changeset powerauth-java-server/1.4.x/20230322-audit.xml::8::Lubos Racansky
+-- Create a new index on audit_param(timestamp_created)
+CREATE INDEX audit_param_timestamp ON audit_param(timestamp_created);
+
+-- Changeset powerauth-java-server/1.4.x/20230322-audit.xml::9::Lubos Racansky
+-- Create a new index on audit_log(param_key)
+CREATE INDEX audit_param_key ON audit_param(param_key);
+
+-- Changeset powerauth-java-server/1.4.x/20230322-audit.xml::10::Lubos Racansky
+-- Create a new index on audit_log(param_value)
+CREATE INDEX audit_param_value ON audit_param(param_value);
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::1::Lubos Racansky
+-- Create a new sequence pa_application_seq
+CREATE SEQUENCE IF NOT EXISTS pa_application_seq START WITH 1 INCREMENT BY 1 CACHE 20;
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::2::Lubos Racansky
+-- Create a new sequence pa_application_version_seq
+CREATE SEQUENCE IF NOT EXISTS pa_application_version_seq START WITH 1 INCREMENT BY 1 CACHE 20;
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::3::Lubos Racansky
+-- Create a new sequence pa_master_keypair_seq
+CREATE SEQUENCE IF NOT EXISTS pa_master_keypair_seq START WITH 1 INCREMENT BY 1 CACHE 20;
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::4::Lubos Racansky
+-- Create a new sequence pa_signature_audit_seq
+CREATE SEQUENCE IF NOT EXISTS pa_signature_audit_seq START WITH 1 INCREMENT BY 1 CACHE 20;
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::5::Lubos Racansky
+-- Create a new sequence pa_activation_history_seq
+CREATE SEQUENCE IF NOT EXISTS pa_activation_history_seq START WITH 1 INCREMENT BY 1 CACHE 20;
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::6::Lubos Racansky
+-- Create a new sequence pa_recovery_code_seq
+CREATE SEQUENCE IF NOT EXISTS pa_recovery_code_seq START WITH 1 INCREMENT BY 1 CACHE 20;
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::7::Lubos Racansky
+-- Create a new sequence pa_recovery_puk_seq
+CREATE SEQUENCE IF NOT EXISTS pa_recovery_puk_seq START WITH 1 INCREMENT BY 1 CACHE 20;
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::8::Lubos Racansky
+-- Create a new sequence pa_recovery_config_seq
+CREATE SEQUENCE IF NOT EXISTS pa_recovery_config_seq START WITH 1 INCREMENT BY 1 CACHE 20;
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::9::Lubos Racansky
+-- Create a new sequence pa_operation_template_seq
+CREATE SEQUENCE IF NOT EXISTS pa_operation_template_seq START WITH 1 INCREMENT BY 1 CACHE 20;
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::10::Lubos Racansky
+-- Create a new table pa_application
+CREATE TABLE pa_application (id INTEGER NOT NULL, name VARCHAR(255) NOT NULL, roles VARCHAR(255), CONSTRAINT pa_application_pkey PRIMARY KEY (id));
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::11::Lubos Racansky
+-- Create a new table pa_master_keypair
+CREATE TABLE pa_master_keypair (id INTEGER NOT NULL, application_id INTEGER NOT NULL, master_key_private_base64 VARCHAR(255) NOT NULL, master_key_public_base64 VARCHAR(255) NOT NULL, name VARCHAR(255), timestamp_created TIMESTAMP(6) WITHOUT TIME ZONE NOT NULL, CONSTRAINT pa_master_keypair_pkey PRIMARY KEY (id), CONSTRAINT keypair_application_fk FOREIGN KEY (application_id) REFERENCES pa_application(id));
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::12::Lubos Racansky
+-- Create a new table pa_activation
+CREATE TABLE pa_activation (activation_id VARCHAR(37) NOT NULL, application_id INTEGER NOT NULL, user_id VARCHAR(255) NOT NULL, activation_name VARCHAR(255), activation_code VARCHAR(255), activation_status INTEGER NOT NULL, activation_otp VARCHAR(255), activation_otp_validation INTEGER DEFAULT 0 NOT NULL, blocked_reason VARCHAR(255), counter INTEGER NOT NULL, ctr_data VARCHAR(255), device_public_key_base64 VARCHAR(255), extras VARCHAR(255), platform VARCHAR(255), device_info VARCHAR(255), flags VARCHAR(255), failed_attempts INTEGER NOT NULL, max_failed_attempts INTEGER DEFAULT 5 NOT NULL, server_private_key_base64 VARCHAR(255) NOT NULL, server_private_key_encryption INTEGER DEFAULT 0 NOT NULL, server_public_key_base64 VARCHAR(255) NOT NULL, timestamp_activation_expire TIMESTAMP(6) WITHOUT TIME ZONE NOT NULL, timestamp_created TIMESTAMP(6) WITHOUT TIME ZONE NOT NULL, timestamp_last_used TIMESTAMP(6) WITHOUT TIME ZONE NOT NULL, timestamp_last_change TIMESTAMP(6) WITHOUT TIME ZONE, master_keypair_id INTEGER, version INTEGER DEFAULT 2, CONSTRAINT pa_activation_pkey PRIMARY KEY (activation_id), CONSTRAINT activation_keypair_fk FOREIGN KEY (master_keypair_id) REFERENCES pa_master_keypair(id), CONSTRAINT activation_application_fk FOREIGN KEY (application_id) REFERENCES pa_application(id));
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::13::Lubos Racansky
+-- Create a new table pa_application_version
+CREATE TABLE pa_application_version (id INTEGER NOT NULL, application_id INTEGER NOT NULL, application_key VARCHAR(255), application_secret VARCHAR(255), name VARCHAR(255), supported BOOLEAN, CONSTRAINT pa_application_version_pkey PRIMARY KEY (id), CONSTRAINT version_application_fk FOREIGN KEY (application_id) REFERENCES pa_application(id));
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::14::Lubos Racansky
+-- Create a new table pa_signature_audit
+CREATE TABLE pa_signature_audit (id BIGINT NOT NULL, activation_id VARCHAR(37) NOT NULL, activation_counter INTEGER NOT NULL, activation_ctr_data VARCHAR(255), activation_status INTEGER, additional_info VARCHAR(255), data_base64 TEXT, note VARCHAR(255), signature_type VARCHAR(255) NOT NULL, signature VARCHAR(255) NOT NULL, timestamp_created TIMESTAMP(6) WITHOUT TIME ZONE NOT NULL, valid BOOLEAN, version INTEGER DEFAULT 2, signature_version VARCHAR(255), CONSTRAINT pa_signature_audit_pkey PRIMARY KEY (id), CONSTRAINT audit_activation_fk FOREIGN KEY (activation_id) REFERENCES pa_activation(activation_id));
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::15::Lubos Racansky
+-- Create a new table pa_integration
+CREATE TABLE pa_integration (id VARCHAR(37) NOT NULL, name VARCHAR(255), client_token VARCHAR(37) NOT NULL, client_secret VARCHAR(37) NOT NULL, CONSTRAINT pa_integration_pkey PRIMARY KEY (id));
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::16::Lubos Racansky
+-- Create a new table pa_application_callback
+CREATE TABLE pa_application_callback (id VARCHAR(37) NOT NULL, application_id INTEGER NOT NULL, name VARCHAR(255), callback_url VARCHAR(1024), type VARCHAR(64) DEFAULT 'ACTIVATION_STATUS_CHANGE' NOT NULL, attributes VARCHAR(1024), authentication TEXT, CONSTRAINT pa_application_callback_pkey PRIMARY KEY (id), CONSTRAINT callback_application_fk FOREIGN KEY (application_id) REFERENCES pa_application(id));
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::17::Lubos Racansky
+-- Create a new table pa_token
+CREATE TABLE pa_token (token_id VARCHAR(37) NOT NULL, token_secret VARCHAR(255) NOT NULL, activation_id VARCHAR(37) NOT NULL, signature_type VARCHAR(255) NOT NULL, timestamp_created TIMESTAMP(6) WITHOUT TIME ZONE NOT NULL, CONSTRAINT pa_token_pkey PRIMARY KEY (token_id), CONSTRAINT activation_token_fk FOREIGN KEY (activation_id) REFERENCES pa_activation(activation_id));
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::18::Lubos Racansky
+-- Create a new table pa_activation_history
+CREATE TABLE pa_activation_history (id BIGINT NOT NULL, activation_id VARCHAR(37) NOT NULL, activation_status INTEGER, event_reason VARCHAR(255), external_user_id VARCHAR(255), timestamp_created TIMESTAMP(6) WITHOUT TIME ZONE NOT NULL, activation_version INTEGER, CONSTRAINT pa_activation_history_pkey PRIMARY KEY (id), CONSTRAINT history_activation_fk FOREIGN KEY (activation_id) REFERENCES pa_activation(activation_id));
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::19::Lubos Racansky
+-- Create a new table pa_recovery_code
+CREATE TABLE pa_recovery_code (id BIGINT NOT NULL, recovery_code VARCHAR(23) NOT NULL, application_id INTEGER NOT NULL, user_id VARCHAR(255) NOT NULL, activation_id VARCHAR(37), status INTEGER NOT NULL, failed_attempts INTEGER DEFAULT 0 NOT NULL, max_failed_attempts INTEGER DEFAULT 10 NOT NULL, timestamp_created TIMESTAMP(6) WITHOUT TIME ZONE NOT NULL, timestamp_last_used TIMESTAMP(6) WITHOUT TIME ZONE, timestamp_last_change TIMESTAMP(6) WITHOUT TIME ZONE, CONSTRAINT pa_recovery_code_pkey PRIMARY KEY (id), CONSTRAINT recovery_code_activation_fk FOREIGN KEY (activation_id) REFERENCES pa_activation(activation_id), CONSTRAINT recovery_code_application_fk FOREIGN KEY (application_id) REFERENCES pa_application(id));
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::20::Lubos Racansky
+-- Create a new table pa_recovery_puk
+CREATE TABLE pa_recovery_puk (id BIGINT NOT NULL, recovery_code_id BIGINT NOT NULL, puk VARCHAR(255), puk_encryption INTEGER DEFAULT 0 NOT NULL, puk_index BIGINT NOT NULL, status INTEGER NOT NULL, timestamp_last_change TIMESTAMP(6) WITHOUT TIME ZONE, CONSTRAINT pa_recovery_puk_pkey PRIMARY KEY (id), CONSTRAINT recovery_puk_code_fk FOREIGN KEY (recovery_code_id) REFERENCES pa_recovery_code(id));
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::21::Lubos Racansky
+-- Create a new table pa_recovery_config
+CREATE TABLE pa_recovery_config (id INTEGER NOT NULL, application_id INTEGER NOT NULL, activation_recovery_enabled BOOLEAN DEFAULT FALSE NOT NULL, recovery_postcard_enabled BOOLEAN DEFAULT FALSE NOT NULL, allow_multiple_recovery_codes BOOLEAN DEFAULT FALSE NOT NULL, postcard_private_key_base64 VARCHAR(255), postcard_public_key_base64 VARCHAR(255), remote_public_key_base64 VARCHAR(255), postcard_priv_key_encryption INTEGER DEFAULT 0 NOT NULL, CONSTRAINT pa_recovery_config_pkey PRIMARY KEY (id), CONSTRAINT recovery_config_app_fk FOREIGN KEY (application_id) REFERENCES pa_application(id));
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::22::Lubos Racansky
+-- Create a new table pa_operation
+CREATE TABLE pa_operation (id VARCHAR(37) NOT NULL, user_id VARCHAR(255) NOT NULL, external_id VARCHAR(255), activation_flag VARCHAR(255), operation_type VARCHAR(255) NOT NULL, template_name VARCHAR(255), data TEXT NOT NULL, parameters TEXT, additional_data TEXT, status INTEGER NOT NULL, signature_type VARCHAR(255) NOT NULL, failure_count BIGINT DEFAULT 0 NOT NULL, max_failure_count BIGINT NOT NULL, timestamp_created TIMESTAMP WITHOUT TIME ZONE NOT NULL, timestamp_expires TIMESTAMP WITHOUT TIME ZONE NOT NULL, timestamp_finalized TIMESTAMP WITHOUT TIME ZONE, risk_flags VARCHAR(255), CONSTRAINT pa_operation_pkey PRIMARY KEY (id));
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::23::Lubos Racansky
+-- Create a new table pa_operation_template
+CREATE TABLE pa_operation_template (id BIGINT NOT NULL, template_name VARCHAR(255) NOT NULL, operation_type VARCHAR(255) NOT NULL, data_template VARCHAR(255) NOT NULL, signature_type VARCHAR(255) NOT NULL, max_failure_count BIGINT NOT NULL, expiration BIGINT NOT NULL, risk_flags VARCHAR(255), CONSTRAINT pa_operation_template_pkey PRIMARY KEY (id));
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::24::Lubos Racansky
+-- Create a new table pa_operation_application
+CREATE TABLE pa_operation_application (application_id BIGINT NOT NULL, operation_id VARCHAR(37) NOT NULL, CONSTRAINT pa_operation_application_pkey PRIMARY KEY (application_id, operation_id));
+
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::26::Lubos Racansky
+-- Create a new index on pa_activation(application_id)
CREATE INDEX pa_activation_application ON pa_activation(application_id);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::27::Lubos Racansky
+-- Create a new index on pa_activation(master_keypair_id)
CREATE INDEX pa_activation_keypair ON pa_activation(master_keypair_id);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::28::Lubos Racansky
+-- Create a new index on pa_activation(activation_code)
CREATE INDEX pa_activation_code ON pa_activation(activation_code);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::29::Lubos Racansky
+-- Create a new index on pa_activation(user_id)
CREATE INDEX pa_activation_user_id ON pa_activation(user_id);
-CREATE INDEX pa_activation_expiration on pa_activation (activation_status, timestamp_activation_expire);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::30::Lubos Racansky
+-- Create a new index on pa_activation(activation_status, timestamp_activation_expire)
+CREATE INDEX pa_activation_expiration ON pa_activation(activation_status, timestamp_activation_expire);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::31::Lubos Racansky
+-- Create a new index on pa_activation_history(activation_id)
CREATE INDEX pa_activation_history_act ON pa_activation_history(activation_id);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::32::Lubos Racansky
+-- Create a new index on pa_activation_history(timestamp_created)
CREATE INDEX pa_activation_history_created ON pa_activation_history(timestamp_created);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::33::Lubos Racansky
+-- Create a new index on pa_application_version(application_id)
CREATE INDEX pa_application_version_app ON pa_application_version(application_id);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::34::Lubos Racansky
+-- Create a new index on pa_master_keypair(application_id)
CREATE INDEX pa_master_keypair_application ON pa_master_keypair(application_id);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::35::Lubos Racansky
+-- Create a new unique index on pa_application_version(application_key)
CREATE UNIQUE INDEX pa_app_version_app_key ON pa_application_version(application_key);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::36::Lubos Racansky
+-- Create a new index on pa_application_callback(application_id)
CREATE INDEX pa_app_callback_app ON pa_application_callback(application_id);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::37::Lubos Racansky
+-- Create a new unique index on pa_integration(client_token)
CREATE UNIQUE INDEX pa_integration_token ON pa_integration(client_token);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::38::Lubos Racansky
+-- Create a new index on pa_signature_audit(activation_id)
CREATE INDEX pa_signature_audit_activation ON pa_signature_audit(activation_id);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::39::Lubos Racansky
+-- Create a new index on pa_signature_audit(timestamp_created)
CREATE INDEX pa_signature_audit_created ON pa_signature_audit(timestamp_created);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::40::Lubos Racansky
+-- Create a new index on pa_token(activation_id)
CREATE INDEX pa_token_activation ON pa_token(activation_id);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::41::Lubos Racansky
+-- Create a new index on pa_recovery_code(recovery_code)
CREATE INDEX pa_recovery_code_code ON pa_recovery_code(recovery_code);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::42::Lubos Racansky
+-- Create a new index on pa_recovery_code(application_id)
CREATE INDEX pa_recovery_code_app ON pa_recovery_code(application_id);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::43::Lubos Racansky
+-- Create a new index on pa_recovery_code(user_id)
CREATE INDEX pa_recovery_code_user ON pa_recovery_code(user_id);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::44::Lubos Racansky
+-- Create a new index on pa_recovery_code(activation_id)
CREATE INDEX pa_recovery_code_act ON pa_recovery_code(activation_id);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::45::Lubos Racansky
+-- Create a new unique index on pa_recovery_puk(recovery_code_id, puk_index)
CREATE UNIQUE INDEX pa_recovery_code_puk ON pa_recovery_puk(recovery_code_id, puk_index);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::46::Lubos Racansky
+-- Create a new index on pa_recovery_puk(recovery_code_id)
CREATE INDEX pa_recovery_puk_code ON pa_recovery_puk(recovery_code_id);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::47::Lubos Racansky
+-- Create a new unique index on pa_recovery_config(application_id)
CREATE UNIQUE INDEX pa_recovery_config_app ON pa_recovery_config(application_id);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::48::Lubos Racansky
+-- Create a new unique index on pa_application(name)
CREATE UNIQUE INDEX pa_application_name ON pa_application(name);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::49::Lubos Racansky
+-- Create a new index on pa_operation(user_id)
CREATE INDEX pa_operation_user ON pa_operation(user_id);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::50::Lubos Racansky
+-- Create a new index on pa_operation(timestamp_created)
CREATE INDEX pa_operation_ts_created_idx ON pa_operation(timestamp_created);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::51::Lubos Racansky
+-- Create a new index on pa_operation(timestamp_expires)
CREATE INDEX pa_operation_ts_expires_idx ON pa_operation(timestamp_expires);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::52::Lubos Racansky
+-- Create a new index on pa_operation(timestamp_expires, status)
CREATE INDEX pa_operation_status_exp ON pa_operation(timestamp_expires, status);
+-- Changeset powerauth-java-server/1.4.x/20230322-init-db.xml::53::Lubos Racansky
+-- Create a new index on pa_operation_template(template_name)
CREATE INDEX pa_operation_template_name_idx ON pa_operation_template(template_name);
+-- Changeset powerauth-java-server/1.4.x/20230322-shedlock.xml::1::Lubos Racansky
+-- Create a new table shedlock
+CREATE TABLE shedlock (name VARCHAR(64) NOT NULL, lock_until TIMESTAMP WITHOUT TIME ZONE NOT NULL, locked_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, locked_by VARCHAR(255) NOT NULL, CONSTRAINT shedlock_pkey PRIMARY KEY (name));
+
+-- Changeset powerauth-java-server/1.4.x/20230323-add-tag-1.4.0.xml::1::Lubos Racansky
+-- Changeset powerauth-java-server/1.5.x/20230324-add-column-signature-data-body::1::Roman Strobl
+-- Add signature_data_body column of type Clob
+ALTER TABLE pa_signature_audit ADD signature_data_body TEXT;
+
+-- Changeset powerauth-java-server/1.5.x/20230323-add-column-signature-metadata::1::Lubos Racansky
+-- Add signature_metadata column of type Clob
+ALTER TABLE pa_signature_audit ADD signature_metadata TEXT;
+
+-- Changeset powerauth-java-server/1.5.x/20230426-add-column-totp-seed::1::Lubos Racansky
+-- Add totp_seed column
+ALTER TABLE pa_operation ADD totp_seed VARCHAR(24);
+
+-- Changeset powerauth-java-server/1.5.x/20230426-add-column-totp-seed::2::Lubos Racansky
+-- Add proximity_check_enabled column
+ALTER TABLE pa_operation_template ADD proximity_check_enabled BOOLEAN DEFAULT FALSE NOT NULL;
+
+-- Changeset powerauth-java-server/1.5.x/20230723-add-table-unique-value.xml::1::Roman Strobl
+-- Create a new table pa_unique_value
+CREATE TABLE pa_unique_value (unique_value VARCHAR(255) NOT NULL, type INTEGER NOT NULL, timestamp_expires TIMESTAMP WITHOUT TIME ZONE NOT NULL, CONSTRAINT pa_unique_value_pkey PRIMARY KEY (unique_value));
+
+-- Changeset powerauth-java-server/1.5.x/20230723-add-table-unique-value.xml::2::Roman Strobl
+-- Create a new index on pa_unique_value(timestamp_expires)
CREATE INDEX pa_unique_value_expiration ON pa_unique_value(timestamp_expires);
---
--- Auditing indexes.
---
-CREATE INDEX IF NOT EXISTS audit_log_timestamp ON audit_log (timestamp_created);
-CREATE INDEX IF NOT EXISTS audit_log_application ON audit_log (application_name);
-CREATE INDEX IF NOT EXISTS audit_log_level ON audit_log (audit_level);
-CREATE INDEX IF NOT EXISTS audit_log_type ON audit_log (audit_type);
-CREATE INDEX IF NOT EXISTS audit_param_log ON audit_param (audit_log_id);
-CREATE INDEX IF NOT EXISTS audit_param_timestamp ON audit_param (timestamp_created);
-CREATE INDEX IF NOT EXISTS audit_param_key ON audit_param (param_key);
+-- Changeset powerauth-java-server/1.5.x/20230822-add-tag-1.5.0.xml::1::Lubos Racansky
+-- Changeset powerauth-java-server/1.6.x/20231018-add-constraint-operation-template-name.xml::1::Jan Pesek
+-- Add unique constraint to pa_operation_template.template_name
+ALTER TABLE pa_operation_template ADD CONSTRAINT pa_operation_template_template_name_uk UNIQUE (template_name);
+
+-- Changeset powerauth-java-server/1.6.x/20231103-add-activation-name-history.xml::1::Lubos Racansky
+-- Add activation_name column to pa_activation_history
+ALTER TABLE pa_activation_history ADD activation_name VARCHAR(255);
+
+-- Changeset powerauth-java-server/1.6.x/20231106-add-foreign-keys.xml::1::Jan Pesek
+ALTER TABLE pa_operation_application ADD CONSTRAINT pa_operation_application_application_id_fk FOREIGN KEY (application_id) REFERENCES pa_application (id);
+
+-- Changeset powerauth-java-server/1.6.x/20231106-add-foreign-keys.xml::2::Jan Pesek
+ALTER TABLE pa_operation_application ADD CONSTRAINT pa_operation_application_operation_id_fk FOREIGN KEY (operation_id) REFERENCES pa_operation (id);
+
+-- Changeset powerauth-java-server/1.6.x/20231112-add-activation-id.xml::1::Jan Dusil
+-- Add activation_id column to pa_operation with foreign key constraint
+ALTER TABLE pa_operation ADD activation_id VARCHAR(37);
+
+ALTER TABLE pa_operation ADD CONSTRAINT pa_operation_activation_id_fk FOREIGN KEY (activation_id) REFERENCES pa_activation (activation_id);
+
+-- Changeset powerauth-java-server/1.6.x/20231123-operation-user-nullable.xml::1::Roman Strobl
+-- Make user_id column in table pa_operation nullable
+ALTER TABLE pa_operation ALTER COLUMN user_id DROP NOT NULL;
+
+-- Changeset powerauth-java-server/1.6.x/20231212-add-tag-1.6.0.xml::1::Lubos Racansky
diff --git a/docs/sql/postgresql/delete_schema.sql b/docs/sql/postgresql/delete_schema.sql
deleted file mode 100644
index 2b63f1272..000000000
--- a/docs/sql/postgresql/delete_schema.sql
+++ /dev/null
@@ -1,34 +0,0 @@
---
--- Drop all tables.
---
-DROP TABLE IF EXISTS "pa_activation" CASCADE;
-DROP TABLE IF EXISTS "pa_activation_history" CASCADE;
-DROP TABLE IF EXISTS "pa_application" CASCADE;
-DROP TABLE IF EXISTS "pa_application_version" CASCADE;
-DROP TABLE IF EXISTS "pa_token" CASCADE;
-DROP TABLE IF EXISTS "pa_master_keypair" CASCADE;
-DROP TABLE IF EXISTS "pa_signature_audit" CASCADE;
-DROP TABLE IF EXISTS "pa_integration" CASCADE;
-DROP TABLE IF EXISTS "pa_application_callback" CASCADE;
-DROP TABLE IF EXISTS "pa_recovery_config" CASCADE;
-DROP TABLE IF EXISTS "pa_recovery_puk" CASCADE;
-DROP TABLE IF EXISTS "pa_recovery_code" CASCADE;
-DROP TABLE IF EXISTS "pa_operation" CASCADE;
-DROP TABLE IF EXISTS "pa_operation_template" CASCADE;
-DROP TABLE IF EXISTS "pa_unique_value" CASCADE;
-
--- Optionally drop the shedlock table
--- DROP TABLE IF EXISTS "shedlock" CASCADE;
-
---
--- Drop all sequences.
---
-DROP SEQUENCE IF EXISTS "pa_application_seq";
-DROP SEQUENCE IF EXISTS "pa_application_version_seq";
-DROP SEQUENCE IF EXISTS "pa_master_keypair_seq";
-DROP SEQUENCE IF EXISTS "pa_signature_audit_seq";
-DROP SEQUENCE IF EXISTS "pa_activation_history_seq";
-DROP SEQUENCE IF EXISTS "pa_recovery_code_seq";
-DROP SEQUENCE IF EXISTS "pa_recovery_puk_seq";
-DROP SEQUENCE IF EXISTS "pa_recovery_config_seq";
-DROP SEQUENCE IF EXISTS "pa_operation_template_seq";
From 299a6cdfd4a171168eaa9622d276549a55d24ca2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Roman=20=C5=A0trobl?=
Date: Thu, 18 Jan 2024 14:40:42 +0800
Subject: [PATCH 043/146] Fix #1253: FIDO2: Improve error handling (#1254)
---
.../model/converter/RegistrationConverter.java | 8 +++++---
.../AttestationObjectDeserializer.java | 5 +++--
.../CollectedClientDataDeserializer.java | 3 ++-
.../fido2/service/AssertionService.java | 6 +++++-
.../fido2/service/RegistrationService.java | 7 +++++--
.../service/provider/AuthenticatorProvider.java | 3 ++-
.../fido2/PowerAuthAuthenticatorProvider.java | 17 ++++++++++-------
7 files changed, 32 insertions(+), 17 deletions(-)
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/RegistrationConverter.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/RegistrationConverter.java
index 2915d8ac3..e313bbfd1 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/RegistrationConverter.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/RegistrationConverter.java
@@ -32,6 +32,7 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
+import java.util.Optional;
/**
* Converter class for registration related objects.
@@ -44,7 +45,7 @@ public class RegistrationConverter {
private final AaguidList aaguidRegistry = new AaguidList();
- public AuthenticatorDetail convert(RegistrationChallenge challenge, RegistrationRequest requestObject, byte[] aaguid, byte[] publicKey) {
+ public Optional convert(RegistrationChallenge challenge, RegistrationRequest requestObject, byte[] aaguid, byte[] publicKey) {
try {
final AuthenticatorDetail authenticatorDetail = new AuthenticatorDetail();
authenticatorDetail.setUserId(challenge.getUserId());
@@ -62,9 +63,10 @@ public AuthenticatorDetail convert(RegistrationChallenge challenge, Registration
authenticatorDetail.setPublicKeyBytes(publicKey);
authenticatorDetail.setFailedAttempts(0L);
authenticatorDetail.setMaxFailedAttempts(5L);
- return authenticatorDetail;
+ return Optional.of(authenticatorDetail);
} catch (JsonProcessingException e) {
- throw new RuntimeException(e);
+ logger.warn(e.getMessage(), e);
+ return Optional.empty();
}
}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AttestationObjectDeserializer.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AttestationObjectDeserializer.java
index 0ce540c74..536c07ce5 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AttestationObjectDeserializer.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AttestationObjectDeserializer.java
@@ -52,7 +52,7 @@ public AttestationObjectDeserializer(Class> vc) {
}
@Override
- public AttestationObject deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException {
+ public AttestationObject deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
try {
final String originalTextValue = jsonParser.getText();
final byte[] decodedAttestationObject = Base64.getDecoder().decode(originalTextValue);
@@ -60,7 +60,8 @@ public AttestationObject deserialize(JsonParser jsonParser, DeserializationConte
attestationObject.setEncoded(originalTextValue);
return attestationObject;
} catch (IOException e) {
- throw new RuntimeException(e);
+ logger.warn(e.getMessage(), e);
+ return null;
}
}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/CollectedClientDataDeserializer.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/CollectedClientDataDeserializer.java
index c2eda992f..c6ddc20d7 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/CollectedClientDataDeserializer.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/CollectedClientDataDeserializer.java
@@ -60,7 +60,8 @@ public CollectedClientData deserialize(JsonParser jsonParser, DeserializationCon
collectedClientData.setEncoded(new String(decodedClientDataJSON));
return collectedClientData;
} catch (IOException e) {
- throw new RuntimeException(e);
+ logger.warn(e.getMessage(), e);
+ return null;
}
}
}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/AssertionService.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/AssertionService.java
index 05a766464..5940f0e91 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/AssertionService.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/AssertionService.java
@@ -36,6 +36,8 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
+import java.util.Optional;
+
/**
* Service related to handling assertions.
*
@@ -88,7 +90,9 @@ public AssertionVerificationResponse authenticate(AssertionVerificationRequest r
final String applicationId = request.getApplicationId();
final String authenticatorId = request.getId();
final String challenge = request.getResponse().getClientDataJSON().getChallenge();
- final AuthenticatorDetail authenticatorDetail = authenticatorProvider.findByCredentialId(applicationId, authenticatorId);
+ final Optional authenticatorOptional = authenticatorProvider.findByCredentialId(applicationId, authenticatorId);
+ authenticatorOptional.orElseThrow(() -> new Fido2AuthenticationFailedException("Invalid request"));
+ final AuthenticatorDetail authenticatorDetail = authenticatorOptional.get();
if (authenticatorDetail.getActivationStatus() == ActivationStatus.ACTIVE) {
final boolean signatureCorrect = cryptographyService.verifySignatureForAssertion(applicationId, authenticatorId, response.getClientDataJSON(), response.getAuthenticatorData(), response.getSignature(), authenticatorDetail);
if (signatureCorrect) {
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/RegistrationService.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/RegistrationService.java
index 75c6e5f74..f34da19a8 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/RegistrationService.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/RegistrationService.java
@@ -34,6 +34,8 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
+import java.util.Optional;
+
/**
* Service related to handling registrations.
*
@@ -104,8 +106,9 @@ public RegistrationResponse register(RegistrationRequest requestObject) throws E
}
final RegistrationChallenge challenge = registrationProvider.provideChallengeForRegistrationChallengeValue(applicationId, challengeValue);
- final AuthenticatorDetail authenticatorDetail = registrationConverter.convert(challenge, requestObject, attestedCredentialData.getAaguid(), cryptographyService.publicKeyToBytes(attestedCredentialData.getPublicKeyObject()));
- final AuthenticatorDetail authenticatorDetailResponse = authenticatorProvider.storeAuthenticator(requestObject.getApplicationId(), challenge.getChallenge(), authenticatorDetail);
+ final Optional authenticatorOptional = registrationConverter.convert(challenge, requestObject, attestedCredentialData.getAaguid(), cryptographyService.publicKeyToBytes(attestedCredentialData.getPublicKeyObject()));
+ authenticatorOptional.orElseThrow(() -> new Fido2AuthenticationFailedException("Invalid request"));
+ final AuthenticatorDetail authenticatorDetailResponse = authenticatorProvider.storeAuthenticator(requestObject.getApplicationId(), challenge.getChallenge(), authenticatorOptional.get());
return registrationConverter.convertRegistrationResponse(authenticatorDetailResponse);
}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/provider/AuthenticatorProvider.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/provider/AuthenticatorProvider.java
index 7587b3dce..b8336e338 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/provider/AuthenticatorProvider.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/provider/AuthenticatorProvider.java
@@ -22,6 +22,7 @@
import com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorDetail;
import java.util.List;
+import java.util.Optional;
/**
* Interface for handling authenticator handling logic.
@@ -31,6 +32,6 @@
public interface AuthenticatorProvider {
AuthenticatorDetail storeAuthenticator(String applicationId, String challenge, AuthenticatorDetail authenticatorDetail) throws Fido2AuthenticationFailedException;
List findByUserId(String userId, String applicationId) throws Fido2AuthenticationFailedException;
- AuthenticatorDetail findByCredentialId(String credentialId, String applicationId) throws Fido2AuthenticationFailedException;
+ Optional findByCredentialId(String credentialId, String applicationId) throws Fido2AuthenticationFailedException;
}
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAuthenticatorProvider.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAuthenticatorProvider.java
index 3b93f060a..c24f3fdca 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAuthenticatorProvider.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAuthenticatorProvider.java
@@ -104,8 +104,8 @@ public List findByUserId(String userId, String applicationI
if (!Protocols.FIDO2.toString().equals(activation.getProtocol())) { // Check the protocol, just in case
continue;
}
- final AuthenticatorDetail authenticatorDetail = convert(activation, application.get());
- authenticatorDetailList.add(authenticatorDetail);
+ final Optional authenticatorOptional = convert(activation, application.get());
+ authenticatorOptional.ifPresent(authenticatorDetailList::add);
}
pageIndex++;
activationList = serviceBehaviorCatalogue.getActivationServiceBehavior().getActivationList(applicationId, userId, Set.of(Protocols.FIDO2), PageRequest.of(pageIndex, 1000), Set.of(ActivationStatus.ACTIVE, ActivationStatus.BLOCKED));
@@ -115,7 +115,7 @@ public List findByUserId(String userId, String applicationI
@Override
@Transactional
- public AuthenticatorDetail findByCredentialId(String applicationId, String credentialId) throws Fido2AuthenticationFailedException {
+ public Optional findByCredentialId(String applicationId, String credentialId) throws Fido2AuthenticationFailedException {
// Find application
final Optional application = applicationRepository.findById(applicationId);
@@ -236,7 +236,9 @@ public AuthenticatorDetail storeAuthenticator(String applicationId, String activ
activationResponse.setDevicePublicKeyBase64(activation.getDevicePublicKeyBase64());
// Generate authenticator detail
- return convert(activationResponse, applicationEntity);
+ final Optional authenticatorOptional = convert(activationResponse, applicationEntity);
+ authenticatorOptional.orElseThrow(() -> new Fido2AuthenticationFailedException("Authenticator object deserialization failed"));
+ return authenticatorOptional.get();
} catch (GenericCryptoException ex) {
logger.error(ex.getMessage(), ex);
// Rollback is not required, cryptography errors can only occur before writing to database
@@ -253,7 +255,7 @@ public AuthenticatorDetail storeAuthenticator(String applicationId, String activ
}
- private AuthenticatorDetail convert(Activation activation, ApplicationEntity application) {
+ private Optional convert(Activation activation, ApplicationEntity application) {
final AuthenticatorDetail authenticatorDetail = new AuthenticatorDetail();
authenticatorDetail.setApplicationId(activation.getApplicationId());
@@ -265,7 +267,8 @@ private AuthenticatorDetail convert(Activation activation, ApplicationEntity app
try {
authenticatorDetail.setExtras(objectMapper.readValue(activation.getExtras(), new TypeReference>() {}));
} catch (JsonProcessingException e) {
- //
+ logger.warn(e.getMessage(), e);
+ return Optional.empty();
}
authenticatorDetail.setActivationFlags(activation.getActivationFlags());
authenticatorDetail.setDeviceInfo(activation.getDeviceInfo());
@@ -279,7 +282,7 @@ private AuthenticatorDetail convert(Activation activation, ApplicationEntity app
authenticatorDetail.setApplicationRoles(application.getRoles());
- return authenticatorDetail;
+ return Optional.of(authenticatorDetail);
}
From b88ec40ba3beba5ca8aa48e80cf4982b04c018a7 Mon Sep 17 00:00:00 2001
From: Roman Strobl
Date: Thu, 18 Jan 2024 14:50:52 +0800
Subject: [PATCH 044/146] Add a test profile for FIDO2 tests
---
.../java/com/wultra/powerauth/fido2/Fido2AuthenticatorTest.java | 2 ++
1 file changed, 2 insertions(+)
diff --git a/powerauth-java-server/src/test/java/com/wultra/powerauth/fido2/Fido2AuthenticatorTest.java b/powerauth-java-server/src/test/java/com/wultra/powerauth/fido2/Fido2AuthenticatorTest.java
index de2a13d02..ee478485a 100644
--- a/powerauth-java-server/src/test/java/com/wultra/powerauth/fido2/Fido2AuthenticatorTest.java
+++ b/powerauth-java-server/src/test/java/com/wultra/powerauth/fido2/Fido2AuthenticatorTest.java
@@ -56,6 +56,7 @@
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.ActiveProfiles;
import java.io.IOException;
import java.nio.ByteBuffer;
@@ -71,6 +72,7 @@
* @author Roman Strobl, roman.strobl@wultra.com
*/
@SpringBootTest(classes = Application.class)
+@ActiveProfiles("test")
class Fido2AuthenticatorTest {
private final CBORMapper CBOR_MAPPER = new CBORMapper();
From b685dea13d60576df8775d5dba72e1aabd3c9932 Mon Sep 17 00:00:00 2001
From: Roman Strobl
Date: Thu, 18 Jan 2024 14:58:52 +0800
Subject: [PATCH 045/146] Fix formatting
---
powerauth-fido2/pom.xml | 144 ++++++++++++++++++++--------------------
1 file changed, 72 insertions(+), 72 deletions(-)
diff --git a/powerauth-fido2/pom.xml b/powerauth-fido2/pom.xml
index 2b1a392b3..b6e079c1f 100644
--- a/powerauth-fido2/pom.xml
+++ b/powerauth-fido2/pom.xml
@@ -17,84 +17,84 @@
~ along with this program. If not, see .
-->
- 4.0.0
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+ 4.0.0
- powerauth-fido2
- powerauth-fido2
- powerauth-fido2
+ powerauth-fido2
+ powerauth-fido2
+ powerauth-fido2
-
- io.getlime.security
- powerauth-server-parent
- 1.7.0-SNAPSHOT
-
+
+ io.getlime.security
+ powerauth-server-parent
+ 1.7.0-SNAPSHOT
+
-
-
- org.springframework.boot
- spring-boot-starter-web
-
-
- org.springframework.boot
- spring-boot-starter-validation
-
-
- org.apache.tomcat.embed
- tomcat-embed-el
-
-
-
-
- org.springdoc
- springdoc-openapi-starter-webmvc-ui
- 2.1.0
-
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
+
+ org.apache.tomcat.embed
+ tomcat-embed-el
+
+
+
+
+ org.springdoc
+ springdoc-openapi-starter-webmvc-ui
+ 2.1.0
+
-
- com.fasterxml.jackson.dataformat
- jackson-dataformat-cbor
- 2.14.2
-
-
- org.postgresql
- postgresql
- runtime
-
-
- org.projectlombok
- lombok
- true
-
+
+ com.fasterxml.jackson.dataformat
+ jackson-dataformat-cbor
+ 2.14.2
+
+
+ org.postgresql
+ postgresql
+ runtime
+
+
+ org.projectlombok
+ lombok
+ true
+
-
- io.getlime.security
- powerauth-java-crypto
- 1.5.1
-
-
- io.getlime.core
- rest-client-base
- 1.7.0-SNAPSHOT
-
-
- io.getlime.security
- powerauth-client-model
- 1.7.0-SNAPSHOT
-
+
+ io.getlime.security
+ powerauth-java-crypto
+ 1.5.1
+
+
+ io.getlime.core
+ rest-client-base
+ 1.7.0-SNAPSHOT
+
+
+ io.getlime.security
+ powerauth-client-model
+ 1.7.0-SNAPSHOT
+
-
- org.bouncycastle
- bcprov-jdk18on
- ${bcprov-jdk18on.version}
- provided
-
+
+ org.bouncycastle
+ bcprov-jdk18on
+ ${bcprov-jdk18on.version}
+ provided
+
-
- org.springframework.boot
- spring-boot-starter-test
- test
-
-
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
From b56c5ed0507ba4b5bcb7a1be86be109379f2fbba Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Roman=20=C5=A0trobl?=
Date: Thu, 18 Jan 2024 15:02:55 +0800
Subject: [PATCH 046/146] Remove empty line
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Luboš Račanský
---
.../powerauth/fido2/rest/model/request/RegistrationRequest.java | 2 --
1 file changed, 2 deletions(-)
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/RegistrationRequest.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/RegistrationRequest.java
index ae64d98fc..591bb6fe1 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/RegistrationRequest.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/RegistrationRequest.java
@@ -37,6 +37,4 @@ public class RegistrationRequest {
// Authenticator parameters
private AuthenticatorParameters authenticatorParameters;
-
-
}
From e893b808c70d6401b714a95a645372923828f05a Mon Sep 17 00:00:00 2001
From: Roman Strobl
Date: Thu, 18 Jan 2024 15:05:43 +0800
Subject: [PATCH 047/146] Remove explicit versions
---
powerauth-fido2/pom.xml | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/powerauth-fido2/pom.xml b/powerauth-fido2/pom.xml
index b6e079c1f..37f273cf2 100644
--- a/powerauth-fido2/pom.xml
+++ b/powerauth-fido2/pom.xml
@@ -70,17 +70,15 @@
io.getlime.security
powerauth-java-crypto
- 1.5.1
+ ${powerauth-java-crypto.version}
io.getlime.core
rest-client-base
- 1.7.0-SNAPSHOT
io.getlime.security
powerauth-client-model
- 1.7.0-SNAPSHOT
From 7cd066e7eac135767ac3f3c5484163881f136532 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Roman=20=C5=A0trobl?=
Date: Thu, 18 Jan 2024 15:07:55 +0800
Subject: [PATCH 048/146] Switch to project version
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Luboš Račanský
---
powerauth-java-server/pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/powerauth-java-server/pom.xml b/powerauth-java-server/pom.xml
index d79ac193e..52831c215 100644
--- a/powerauth-java-server/pom.xml
+++ b/powerauth-java-server/pom.xml
@@ -108,7 +108,7 @@
io.getlime.security
powerauth-fido2
- 1.7.0-SNAPSHOT
+ ${project.version}
From 847ef9f9cd4249e649ad145dce9153ddc2e41103 Mon Sep 17 00:00:00 2001
From: Roman Strobl
Date: Thu, 18 Jan 2024 15:08:34 +0800
Subject: [PATCH 049/146] Remove lombok dependency
---
powerauth-fido2/pom.xml | 5 -----
1 file changed, 5 deletions(-)
diff --git a/powerauth-fido2/pom.xml b/powerauth-fido2/pom.xml
index 37f273cf2..e9c976d58 100644
--- a/powerauth-fido2/pom.xml
+++ b/powerauth-fido2/pom.xml
@@ -61,11 +61,6 @@
postgresql
runtime
-
- org.projectlombok
- lombok
- true
-
io.getlime.security
From 550e9bd9ed98c2a7fc48c4bb67c74f5848f964ea Mon Sep 17 00:00:00 2001
From: Roman Strobl
Date: Thu, 18 Jan 2024 15:27:46 +0800
Subject: [PATCH 050/146] Use smaller OpenAPI dependency
---
powerauth-fido2/pom.xml | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/powerauth-fido2/pom.xml b/powerauth-fido2/pom.xml
index e9c976d58..f285598af 100644
--- a/powerauth-fido2/pom.xml
+++ b/powerauth-fido2/pom.xml
@@ -46,9 +46,8 @@
- org.springdoc
- springdoc-openapi-starter-webmvc-ui
- 2.1.0
+ io.swagger.core.v3
+ swagger-annotations-jakarta
From 23c0bf27b482356d82a09460a0aaa756d6a3a4c6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Roman=20=C5=A0trobl?=
Date: Thu, 18 Jan 2024 15:30:13 +0800
Subject: [PATCH 051/146] Remove explicit version
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Luboš Račanský
---
powerauth-fido2/pom.xml | 1 -
1 file changed, 1 deletion(-)
diff --git a/powerauth-fido2/pom.xml b/powerauth-fido2/pom.xml
index f285598af..ab79ca44e 100644
--- a/powerauth-fido2/pom.xml
+++ b/powerauth-fido2/pom.xml
@@ -53,7 +53,6 @@
com.fasterxml.jackson.dataformat
jackson-dataformat-cbor
- 2.14.2
org.postgresql
From 82f19a3860e554533735b4ad07193b4289d3a5f0 Mon Sep 17 00:00:00 2001
From: Lubos Racansky
Date: Thu, 18 Jan 2024 08:46:52 +0100
Subject: [PATCH 052/146] Fix #1263: Clean-up FIDO2 pom.xml
---
pom.xml | 12 ++++++++++++
powerauth-fido2/pom.xml | 14 --------------
powerauth-java-server/pom.xml | 3 ---
3 files changed, 12 insertions(+), 17 deletions(-)
diff --git a/pom.xml b/pom.xml
index 944574a7e..50f7f2efc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -123,6 +123,12 @@
${project.version}
+
+ io.getlime.security
+ powerauth-java-crypto
+ ${powerauth-java-crypto.version}
+
+
io.getlime.core
@@ -132,6 +138,12 @@
import
+
+ org.bouncycastle
+ bcprov-jdk18on
+ ${bcprov-jdk18on.version}
+
+
io.swagger.core.v3
swagger-annotations-jakarta
diff --git a/powerauth-fido2/pom.xml b/powerauth-fido2/pom.xml
index ab79ca44e..139030213 100644
--- a/powerauth-fido2/pom.xml
+++ b/powerauth-fido2/pom.xml
@@ -38,12 +38,6 @@
org.springframework.boot
spring-boot-starter-validation
-
-
- org.apache.tomcat.embed
- tomcat-embed-el
-
-
io.swagger.core.v3
@@ -54,16 +48,10 @@
com.fasterxml.jackson.dataformat
jackson-dataformat-cbor
-
- org.postgresql
- postgresql
- runtime
-
io.getlime.security
powerauth-java-crypto
- ${powerauth-java-crypto.version}
io.getlime.core
@@ -77,8 +65,6 @@
org.bouncycastle
bcprov-jdk18on
- ${bcprov-jdk18on.version}
- provided
diff --git a/powerauth-java-server/pom.xml b/powerauth-java-server/pom.xml
index 52831c215..3d83b9d00 100644
--- a/powerauth-java-server/pom.xml
+++ b/powerauth-java-server/pom.xml
@@ -87,7 +87,6 @@
io.getlime.security
powerauth-java-crypto
- ${powerauth-java-crypto.version}
io.getlime.core
@@ -111,7 +110,6 @@
${project.version}
-
org.springdoc
@@ -128,7 +126,6 @@
org.bouncycastle
bcprov-jdk18on
- ${bcprov-jdk18on.version}
From 420737c764a492f500904a8581481b7bd70f392f Mon Sep 17 00:00:00 2001
From: Jan Dusil <134381434+jandusil@users.noreply.github.com>
Date: Fri, 19 Jan 2024 09:53:10 +0100
Subject: [PATCH 053/146] Fix #1099: Move tests from powerauth-tests to this
repo (#1235)
* Fix #1099: Move tests from powerauth-tests to this repo
- Move operation tests
- Move application roles test
- Move callback controller tests
- Move activation tests
- Move more auth tests
- Add javadoc
- Add helper methods
---
powerauth-java-server/pom.xml | 15 +
.../api/PowerAuthControllerTest.java | 1384 ++++++++++++++++-
.../api/PowerAuthControllerTestConfig.java | 82 +
.../tasks/OperationServiceBehaviorTest.java | 57 +
.../api/PowerAuthControllerTest.sql | 8 -
5 files changed, 1453 insertions(+), 93 deletions(-)
create mode 100644 powerauth-java-server/src/test/java/io/getlime/security/powerauth/app/server/controller/api/PowerAuthControllerTestConfig.java
delete mode 100644 powerauth-java-server/src/test/resources/io/getlime/security/powerauth/app/server/controller/api/PowerAuthControllerTest.sql
diff --git a/powerauth-java-server/pom.xml b/powerauth-java-server/pom.xml
index 3d83b9d00..c8913711d 100644
--- a/powerauth-java-server/pom.xml
+++ b/powerauth-java-server/pom.xml
@@ -156,6 +156,14 @@
micrometer-registry-prometheus
+
+
+ io.netty
+ netty-resolver-dns-native-macos
+ runtime
+ osx-aarch_64
+
+
org.springframework.boot
@@ -178,6 +186,13 @@
${webauthn4j.version}
test
+
+
+ io.getlime.security
+ powerauth-rest-client-spring
+ test
+
+
nl.jqno.equalsverifier
equalsverifier
diff --git a/powerauth-java-server/src/test/java/io/getlime/security/powerauth/app/server/controller/api/PowerAuthControllerTest.java b/powerauth-java-server/src/test/java/io/getlime/security/powerauth/app/server/controller/api/PowerAuthControllerTest.java
index 341f30036..a8de53129 100644
--- a/powerauth-java-server/src/test/java/io/getlime/security/powerauth/app/server/controller/api/PowerAuthControllerTest.java
+++ b/powerauth-java-server/src/test/java/io/getlime/security/powerauth/app/server/controller/api/PowerAuthControllerTest.java
@@ -1,6 +1,6 @@
/*
* PowerAuth Server and related software components
- * Copyright (C) 2023 Wultra s.r.o.
+ * 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
@@ -17,112 +17,1326 @@
*/
package io.getlime.security.powerauth.app.server.controller.api;
-import com.wultra.core.audit.base.database.DatabaseAudit;
-import io.getlime.security.powerauth.app.server.database.model.entity.ActivationHistoryEntity;
-import io.getlime.security.powerauth.app.server.database.model.entity.ActivationRecordEntity;
-import jakarta.persistence.EntityManager;
-import jakarta.persistence.Tuple;
-import org.junit.jupiter.api.Test;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.wultra.security.powerauth.client.PowerAuthClient;
+import com.wultra.security.powerauth.client.model.entity.CallbackUrl;
+import com.wultra.security.powerauth.client.model.enumeration.*;
+import com.wultra.security.powerauth.client.model.error.PowerAuthClientException;
+import com.wultra.security.powerauth.client.model.request.*;
+import com.wultra.security.powerauth.client.model.response.*;
+import io.getlime.security.powerauth.app.server.service.model.request.ActivationLayer2Request;
+import io.getlime.security.powerauth.crypto.lib.encryptor.ClientEncryptor;
+import io.getlime.security.powerauth.crypto.lib.encryptor.EncryptorFactory;
+import io.getlime.security.powerauth.crypto.lib.encryptor.ServerEncryptor;
+import io.getlime.security.powerauth.crypto.lib.encryptor.model.EncryptedRequest;
+import io.getlime.security.powerauth.crypto.lib.encryptor.model.EncryptorId;
+import io.getlime.security.powerauth.crypto.lib.encryptor.model.EncryptorParameters;
+import io.getlime.security.powerauth.crypto.lib.encryptor.model.v3.ClientEncryptorSecrets;
+import io.getlime.security.powerauth.crypto.lib.encryptor.model.v3.ServerEncryptorSecrets;
+import io.getlime.security.powerauth.crypto.lib.generator.KeyGenerator;
+import io.getlime.security.powerauth.crypto.lib.util.KeyConvertor;
+import org.junit.jupiter.api.*;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.http.MediaType;
import org.springframework.test.context.ActiveProfiles;
-import org.springframework.test.context.jdbc.Sql;
-import org.springframework.test.web.servlet.MockMvc;
import org.springframework.transaction.annotation.Transactional;
-import java.util.List;
+import java.io.ByteArrayOutputStream;
+import java.nio.charset.StandardCharsets;
+import java.security.KeyPair;
+import java.security.PublicKey;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.util.*;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.containsString;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+import static org.hamcrest.Matchers.greaterThan;
+import static org.hamcrest.Matchers.hasSize;
+import static org.junit.jupiter.api.Assertions.*;
/**
* Test for {@link PowerAuthController}.
*
* @author Lubos Racansky, lubos.racansky@wultra.com
*/
-@SpringBootTest
-@AutoConfigureMockMvc
-@Sql
-@Transactional
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
@ActiveProfiles("test")
+@Transactional
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class PowerAuthControllerTest {
@Autowired
- private MockMvc mockMvc;
+ private PowerAuthClient powerAuthClient;
@Autowired
- private EntityManager entityManager;
+ private PowerAuthControllerTestConfig config;
+ private final KeyConvertor keyConvertor = new KeyConvertor();
+ private final EncryptorFactory encryptorFactory = new EncryptorFactory();
+ private final KeyGenerator keyGenerator = new KeyGenerator();
+ private final ObjectMapper objectMapper = new ObjectMapper();
- @Autowired
- private DatabaseAudit databaseAudit;
+ @BeforeAll
+ void initializeData() throws Exception {
+ createApplication();
+ createLoginOperationTemplate();
+ }
+
+ /**
+ * Tests the process of removing an activation.
+ *
+ * This test carries out the following operations:
+ *
+ * - Creates a request to remove an existing activation, using the activation ID from the configuration.
+ * - Sends the removal request and asserts that the response confirms the activation's removal.
+ * - Asserts that the activation ID in the removal response matches the one from the configuration.
+ * - Fetches the current status of the activation to verify that it has been set to 'REMOVED'.
+ *
+ *
+ *
+ * @throws Exception if any error occurs during the test execution or if the assertions fail.
+ */
+ @Test
+ void testRemoveActivation() throws Exception {
+ initActivation();
+ final RemoveActivationRequest removeActivationRequest = new RemoveActivationRequest();
+ removeActivationRequest.setActivationId(config.getActivationId());
+ removeActivationRequest.setExternalUserId(null);
+
+ final RemoveActivationResponse removeActivationResponse = powerAuthClient.removeActivation(removeActivationRequest);
+ assertTrue(removeActivationResponse.isRemoved());
+ assertEquals(config.getActivationId(), removeActivationResponse.getActivationId());
+
+ final GetActivationStatusRequest activationStatusRequest = new GetActivationStatusRequest();
+ activationStatusRequest.setActivationId(config.getActivationId());
+
+ final GetActivationStatusResponse statusResponse = powerAuthClient.getActivationStatus(activationStatusRequest);
+ assertEquals(ActivationStatus.REMOVED, statusResponse.getActivationStatus());
+ }
+
+ /**
+ * Tests the pagination functionality in retrieving an activation list for a specific user.
+ *
+ * The test executes the following steps:
+ *
+ * - Prepares a base request for fetching user activations, using user and application IDs from the configuration.
+ * - Creates 10 new activations for the user to ensure multiple pages of data.
+ * - Fetches the first page of activations, specifying page number and size, and validates the number of activations returned.
+ * - Fetches the second page of activations with a different page number but same page size, again validating the number of activations returned.
+ * - Asserts that the activations on the two pages are not identical, verifying the functionality of pagination.
+ * - Removes the created activations to maintain test isolation and ensure clean up after test execution.
+ *
+ *
+ *
+ * @throws Exception if any error occurs during the test execution, the initiation of activations, the removal of activations, or if the assertions fail.
+ */
+ @Test
+ void testActivationListForUserPagination() throws Exception {
+ final GetActivationListForUserRequest baseRequest = new GetActivationListForUserRequest();
+ baseRequest.setUserId(PowerAuthControllerTestConfig.USER_ID);
+ baseRequest.setApplicationId(config.getApplicationId());
+ final List activationIds = new ArrayList<>();
+
+ for (int i = 0; i < 10; i++) {
+ final InitActivationRequest initActivationRequest = new InitActivationRequest();
+ initActivationRequest.setApplicationId(config.getApplicationId());
+ initActivationRequest.setUserId(PowerAuthControllerTestConfig.USER_ID);
+ final InitActivationResponse initResponse = powerAuthClient.initActivation(initActivationRequest);
+ activationIds.add(initResponse.getActivationId());
+ }
+
+ final GetActivationListForUserRequest requestPage1 = new GetActivationListForUserRequest();
+ requestPage1.setUserId(baseRequest.getUserId());
+ requestPage1.setApplicationId(baseRequest.getApplicationId());
+ requestPage1.setPageNumber(0);
+ requestPage1.setPageSize(5);
+
+ final GetActivationListForUserResponse activationListForUserResponse1 = powerAuthClient
+ .getActivationListForUser(requestPage1);
+ assertEquals(5, activationListForUserResponse1.getActivations().size());
+
+ final GetActivationListForUserRequest requestPage2 = new GetActivationListForUserRequest();
+ requestPage2.setUserId(baseRequest.getUserId());
+ requestPage2.setApplicationId(baseRequest.getApplicationId());
+ requestPage2.setPageNumber(1);
+ requestPage2.setPageSize(5);
+
+ final GetActivationListForUserResponse activationListForUserResponse2 = powerAuthClient
+ .getActivationListForUser(requestPage2);
+ assertEquals(5, activationListForUserResponse2.getActivations().size());
+ assertThat(activationListForUserResponse2.getActivations(), hasSize(5));
+
+ assertNotEquals(activationListForUserResponse2.getActivations(), activationListForUserResponse1.getActivations());
+
+ for (final String id : activationIds) {
+ final RemoveActivationResponse removeActivationResponse = powerAuthClient.removeActivation(id, PowerAuthControllerTestConfig.USER_ID);
+ assertTrue(removeActivationResponse.isRemoved());
+ }
+ }
+
+ /**
+ * Tests the activation lookup process based on specific criteria.
+ *
+ * This test executes the following actions:
+ *
+ * - Constructs a request to look up activations, specifying criteria such as user IDs,
+ * application IDs, activation status, and a timestamp. The timestamp is set to 10 seconds
+ * before the current time to include recent activations.
+ * - Sends the lookup request and verifies the response to ensure it contains the expected
+ * number of activations meeting the specified criteria.
+ * - Asserts that the number of activations in the response matches the expected count,
+ * confirming the correct functionality of the lookup process.
+ *
+ *
+ * @throws Exception if any errors occur during the execution of the test or if the assertions fail.
+ */
+ @Test
+ void testLookupActivations() throws Exception {
+ initActivation();
+ final LookupActivationsRequest lookupActivationsRequest = new LookupActivationsRequest();
+ /* We are looking for an activation created during initialization of the test suite. */
+ final Date timestampCreated = Date.from(LocalDateTime.now().minusSeconds(1).atZone(ZoneId.systemDefault()).toInstant());
+ lookupActivationsRequest.setUserIds(List.of(PowerAuthControllerTestConfig.USER_ID));
+ lookupActivationsRequest.setApplicationIds(List.of(config.getApplicationId()));
+ lookupActivationsRequest.setActivationStatus(ActivationStatus.CREATED);
+ lookupActivationsRequest.setTimestampLastUsedAfter(timestampCreated);
+
+ final LookupActivationsResponse lookupActivationsResponse = powerAuthClient.lookupActivations(lookupActivationsRequest);
+ assertThat(lookupActivationsResponse.getActivations(), hasSize(1));
+ removeActivation();
+ }
+ /**
+ * Tests the process of updating the status of specific activations.
+ *
+ * This test performs the following actions:
+ *
+ * - Creates a request to update the status of specified activations to 'BLOCKED', using the activation ID from the configuration.
+ * - Sends the update request and verifies that the response confirms the successful update of the activation statuses.
+ * - Retrieves the current status of the activation to validate that it has been updated to 'BLOCKED' as expected.
+ *
+ *
+ * @throws Exception if any error occurs during the test execution or if the assertions fail.
+ */
+ @Test
+ void testUpdateActivationStatus() throws Exception {
+ initActivation();
+ final UpdateStatusForActivationsRequest updateStatusForActivationsRequest = new UpdateStatusForActivationsRequest();
+ updateStatusForActivationsRequest.setActivationIds(List.of(config.getActivationId()));
+ updateStatusForActivationsRequest.setActivationStatus(ActivationStatus.BLOCKED);
+
+ final UpdateStatusForActivationsResponse updateStatusForActivationsResponse =
+ powerAuthClient.updateStatusForActivations(updateStatusForActivationsRequest);
+ assertTrue(updateStatusForActivationsResponse.isUpdated());
+
+ final GetActivationStatusRequest activationStatusRequest = new GetActivationStatusRequest();
+ activationStatusRequest.setActivationId(config.getActivationId());
+ final GetActivationStatusResponse statusResponse = powerAuthClient.getActivationStatus(activationStatusRequest);
+ assertEquals(ActivationStatus.BLOCKED, statusResponse.getActivationStatus());
+ removeActivation();
+ }
+
+ /**
+ * Tests the process of retrieving activation history for a specific activation ID.
+ *
+ * This test performs the following steps:
+ *
+ * - Defines a time range, starting 10 seconds before the current time and ending 10 seconds after.
+ * - Constructs an activation history request with the specified activation ID and the defined time range.
+ * - Sends the request to retrieve the activation history.
+ * - Asserts that the response contains exactly one history item.
+ * - Verifies that the activation ID of the history item matches the activation ID used in the request.
+ *
+ *
+ *
+ * @throws Exception if any error occurs during the test execution or if the assertions fail.
+ */
+ @Test
+ void testActivationHistory() throws Exception {
+ initActivation();
+ final Date before = Date.from(LocalDateTime.now().minusSeconds(1).atZone(ZoneId.systemDefault()).toInstant());
+ final Date after = Date.from(LocalDateTime.now().plusSeconds(1).atZone(ZoneId.systemDefault()).toInstant());
+ final ActivationHistoryRequest activationHistoryRequest = new ActivationHistoryRequest();
+ activationHistoryRequest.setActivationId(config.getActivationId());
+ activationHistoryRequest.setTimestampFrom(before);
+ activationHistoryRequest.setTimestampTo(after);
+
+ final ActivationHistoryResponse activationHistoryResponse = powerAuthClient.getActivationHistory(activationHistoryRequest);
+ assertThat(activationHistoryResponse.getItems(), hasSize(1));
+ assertEquals(config.getActivationId(), activationHistoryResponse.getItems().get(0).getActivationId());
+ removeActivation();
+ }
+
+ /**
+ * Tests the process of blocking and unblocking an activation.
+ *
+ * This test follows these steps:
+ *
+ * - Prepares and sends a request to block an activation, specifying the activation ID and a blocking reason.
+ * - Verifies that the response indicates the activation was successfully blocked.
+ * - Asserts the consistency of the activation ID and blocking reason in the block response.
+ * - Fetches the current status of the activation to confirm that it has been changed to 'BLOCKED'.
+ * - Prepares and sends a request to unblock the same activation.
+ * - Verifies that the unblock response indicates the activation was successfully unblocked.
+ * - Fetches the activation status again to confirm that it has reverted to 'ACTIVE'.
+ *
+ *
+ *
+ * @throws Exception if any error occurs during the test execution or if the assertions fail.
+ */
+ @Test
+ void testBlockAndUnblockActivation() throws Exception {
+ initActivation();
+ final UpdateStatusForActivationsRequest updateStatusForActivationsRequest = new UpdateStatusForActivationsRequest();
+ updateStatusForActivationsRequest.setActivationIds(List.of(config.getActivationId()));
+ updateStatusForActivationsRequest.setActivationStatus(ActivationStatus.ACTIVE);
+
+ final UpdateStatusForActivationsResponse updateStatusForActivationsResponse =
+ powerAuthClient.updateStatusForActivations(updateStatusForActivationsRequest);
+ assertTrue(updateStatusForActivationsResponse.isUpdated());
+
+ final BlockActivationRequest blockActivationRequest = new BlockActivationRequest();
+ final String blockingReason = "Test-blocking";
+ blockActivationRequest.setActivationId(config.getActivationId());
+ blockActivationRequest.setReason(blockingReason);
+
+ final BlockActivationResponse blockActivationResponse = powerAuthClient.blockActivation((blockActivationRequest));
+ assertEquals(config.getActivationId(), blockActivationResponse.getActivationId());
+ assertEquals(blockingReason, blockActivationResponse.getBlockedReason());
+ assertEquals(ActivationStatus.BLOCKED, blockActivationResponse.getActivationStatus());
+
+ final GetActivationStatusRequest activationStatusRequest = new GetActivationStatusRequest();
+ activationStatusRequest.setActivationId(config.getActivationId());
+ final GetActivationStatusResponse statusResponse = powerAuthClient.getActivationStatus(activationStatusRequest);
+ assertEquals(ActivationStatus.BLOCKED, statusResponse.getActivationStatus());
+
+ final UnblockActivationRequest unblockActivationRequest = new UnblockActivationRequest();
+ unblockActivationRequest.setActivationId(config.getActivationId());
+ final UnblockActivationResponse unblockActivationResponse = powerAuthClient.unblockActivation(unblockActivationRequest);
+ assertEquals(config.getActivationId(), unblockActivationResponse.getActivationId());
+ assertEquals(ActivationStatus.ACTIVE, unblockActivationResponse.getActivationStatus());
+
+ final GetActivationStatusResponse statusResponse2 = powerAuthClient.getActivationStatus(activationStatusRequest);
+ assertEquals(ActivationStatus.ACTIVE, statusResponse2.getActivationStatus());
+ removeActivation();
+ }
+
+ /**
+ * Tests the process of retrieving activation history for a specific activation ID.
+ *
+ * This test performs the following steps:
+ *
+ * - Defines a time range, starting 10 seconds before the current time and ending 10 seconds after.
+ * - Constructs an activation history request with the specified activation ID and the defined time range.
+ * - Sends the request to retrieve the activation history.
+ * - Asserts that the response contains exactly one history item.
+ * - Verifies that the activation ID of the history item matches the activation ID used in the request.
+ *
+ *
+ *
+ * @throws Exception if any error occurs during the test execution or if the assertions fail.
+ */
@Test
void testUpdateActivation() throws Exception {
- mockMvc.perform(post("/rest/v3/activation/name/update")
- .content("""
- {
- "requestObject": {
- "activationId": "e43a5dec-afea-4a10-a80b-b2183399f16b",
- "activationName": "my iPhone",
- "externalUserId": "joe-1"
- }
- }
- """)
- .contentType(MediaType.APPLICATION_JSON)
- .accept(MediaType.APPLICATION_JSON))
- .andExpect(status().isOk())
- .andExpect(jsonPath("$.status").value("OK"))
- .andExpect(jsonPath("$.responseObject.activationName").value("my iPhone"));
-
- final ActivationRecordEntity activation = entityManager.find(ActivationRecordEntity.class, "e43a5dec-afea-4a10-a80b-b2183399f16b");
- assertEquals("my iPhone", activation.getActivationName());
-
- final List historyEntries = entityManager.createQuery("select h from ActivationHistoryEntity h where h.activation = :activation", ActivationHistoryEntity.class)
- .setParameter("activation", activation)
- .getResultList();
- assertEquals(1, historyEntries.size());
-
- final ActivationHistoryEntity historyEntry = historyEntries.iterator().next();
- assertEquals("my iPhone", historyEntry.getActivationName());
- assertEquals("ACTIVATION_NAME_UPDATED", historyEntry.getEventReason());
- assertEquals("joe-1", historyEntry.getExternalUserId());
-
- databaseAudit.flush();
- final String expectedAuditMessage = "Updated activation with ID: e43a5dec-afea-4a10-a80b-b2183399f16b";
- @SuppressWarnings("unchecked")
- final List auditEntries = entityManager.createNativeQuery("select * from audit_log where message = :message", Tuple.class)
- .setParameter("message", expectedAuditMessage)
- .getResultList();
- assertEquals(1, auditEntries.size());
-
- final String param = auditEntries.get(0).get("param").toString();
- assertThat(param, containsString("\"activationId\":\"e43a5dec-afea-4a10-a80b-b2183399f16b\""));
- assertThat(param, containsString("\"activationName\":\"my iPhone\""));
- assertThat(param, containsString("\"reason\":\"ACTIVATION_NAME_UPDATED\""));
- }
-
- @Test
- void testUpdateActivation_badRequest() throws Exception {
- final String expectedErrorMessage = "requestObject.activationId - must not be blank, requestObject.activationName - must not be blank, requestObject.externalUserId - must not be blank";
-
- mockMvc.perform(post("/rest/v3/activation/name/update")
- .content("""
- {
- "requestObject": {}
- }
- """)
- .contentType(MediaType.APPLICATION_JSON)
- .accept(MediaType.APPLICATION_JSON))
- .andExpect(status().isBadRequest())
- .andExpect(jsonPath("$.status").value("ERROR"))
- .andExpect(jsonPath("$.responseObject.code").value("ERR0024"))
- .andExpect(jsonPath("$.responseObject.message").value(expectedErrorMessage))
- .andExpect(jsonPath("$.responseObject.localizedMessage").value(expectedErrorMessage));
+ initActivation();
+ final UpdateStatusForActivationsRequest updateStatusForActivationsRequest = new UpdateStatusForActivationsRequest();
+ updateStatusForActivationsRequest.setActivationIds(List.of(config.getActivationId()));
+ updateStatusForActivationsRequest.setActivationStatus(ActivationStatus.ACTIVE);
+
+ final UpdateStatusForActivationsResponse updateStatusForActivationsResponse =
+ powerAuthClient.updateStatusForActivations(updateStatusForActivationsRequest);
+ assertTrue(updateStatusForActivationsResponse.isUpdated());
+
+ final UpdateActivationNameRequest updateActivationNameRequest = new UpdateActivationNameRequest();
+ final String updatedName = "Updated_app_name";
+ final String externalUserId = "external_user";
+ updateActivationNameRequest.setActivationId(config.getActivationId());
+ updateActivationNameRequest.setActivationName(updatedName);
+ updateActivationNameRequest.setExternalUserId(externalUserId);
+
+ final UpdateActivationNameResponse updateActivationNameResponse =
+ powerAuthClient.updateActivationName(updateActivationNameRequest);
+ assertEquals(updatedName, updateActivationNameResponse.getActivationName());
+ assertEquals(config.getActivationId(), updateActivationNameResponse.getActivationId());
+ assertEquals(ActivationStatus.ACTIVE, updateActivationNameResponse.getActivationStatus());
+ removeActivation();
+ }
+
+ /**
+ * Tests the handling of a bad request in the update activation functionality.
+ *
+ * This test performs the following actions:
+ *
+ * - Sends an incomplete request to the activation name update endpoint, which is expected to be invalid.
+ * - Catches and asserts the thrown PowerAuthClientException to verify the response handling of a bad request.
+ * - Checks that the exception message and localized message contain the expected error message.
+ * - Verifies that the PowerAuthClientException contains the expected PowerAuth error code, confirming correct error identification.
+ *
+ * The test expects a specific error code and message indicating that mandatory fields in the request object are blank.
+ *
+ */
+ @Test
+ void testUpdateActivation_badRequest() {
+ final String expectedErrorMessage = "requestObject.activationId - must not be blank," +
+ " requestObject.activationName - must not be blank, requestObject.externalUserId - must not be blank";
+ final String expectedErrorCode = "ERR0024";
+ final PowerAuthClientException thrownException = assertThrows(
+ PowerAuthClientException.class,
+ () -> powerAuthClient.updateActivationName(new UpdateActivationNameRequest())
+ );
+ assertEquals(expectedErrorMessage, thrownException.getMessage());
+ assertEquals(expectedErrorMessage, thrownException.getLocalizedMessage());
+ assertTrue(thrownException.getPowerAuthError().isPresent());
+ assertEquals(expectedErrorCode, thrownException.getPowerAuthError().get().getCode());
+ }
+
+ /**
+ * Tests the operation approval process.
+ *
+ * This test validates the functionality of approving an operation. It involves the following steps:
+ *
+ * - Creation of a new operation.
+ * - Construction of an operation approval request, which includes setting various parameters like operation ID, user ID, data, application ID, and signature type.
+ * - Submission of the operation approval request to the PowerAuthClient's operation approval endpoint.
+ * - Verification of the response to ensure that the operation was approved successfully. This includes checking the response status, the operation's data, template name, and operation ID.
+ * - Confirmation that the operation entity in the database reflects the approved status.
+ *
+ * This test ensures that the operation approval workflow functions correctly and that the operation's status is updated as expected in the system.
+ *
+ * @throws Exception if there is an issue with the PowerAuthClient's operation approval process or the initial operation creation.
+ */
+ @Test
+ void testOperationApprove() throws Exception {
+ final OperationDetailResponse operationDetailResponse = createOperation();
+ final String operationId = operationDetailResponse.getId();
+ final OperationApproveRequest operationApproveRequest = new OperationApproveRequest();
+ operationApproveRequest.setOperationId(operationId);
+ operationApproveRequest.setUserId(PowerAuthControllerTestConfig.USER_ID);
+ operationApproveRequest.setData(PowerAuthControllerTestConfig.DATA);
+ operationApproveRequest.setApplicationId(config.getApplicationId());
+ operationApproveRequest.setSignatureType(SignatureType.POSSESSION_KNOWLEDGE);
+
+ final OperationUserActionResponse operationUserActionResponse =
+ powerAuthClient.operationApprove(operationApproveRequest);
+ assertNotNull(operationUserActionResponse.getOperation());
+ assertEquals(OperationStatus.APPROVED, operationUserActionResponse.getOperation().getStatus());
+ assertEquals(PowerAuthControllerTestConfig.DATA, operationUserActionResponse.getOperation().getData());
+ assertEquals(config.getLoginOperationTemplateName(), operationUserActionResponse.getOperation().getTemplateName());
+ assertEquals(operationId, operationUserActionResponse.getOperation().getId());
+ }
+
+ /**
+ * Tests the retrieval of operation details.
+ *
+ * This test checks the functionality of fetching details for a specific operation. It proceeds as follows:
+ *
+ * - Creates a new operation using the provided configuration.
+ * - Constructs a request for operation details, using the operation ID obtained from the previous step.
+ * - Retrieves the operation details by sending the request to the PowerAuth client.
+ * - Asserts that the response contains the correct operation status, data, template name, and ID.
+ *
+ * This test ensures that the operation details are correctly retrieved and match the expected values.
+ *
+ * @throws Exception if an error occurs in operation creation or detail retrieval, or if the assertions fail.
+ */
+ @Test
+ void testGetOperationDetail() throws Exception {
+ final OperationDetailResponse operation = createOperation();
+ final OperationDetailRequest detailRequest = new OperationDetailRequest();
+ final String operationId = operation.getId();
+ detailRequest.setOperationId(operationId);
+ detailRequest.setUserId(PowerAuthControllerTestConfig.USER_ID);
+
+ final OperationDetailResponse detailResponse = powerAuthClient.operationDetail(detailRequest);
+ assertEquals(OperationStatus.PENDING, detailResponse.getStatus());
+ assertEquals(PowerAuthControllerTestConfig.DATA, detailResponse.getData());
+ assertEquals(config.getLoginOperationTemplateName(), detailResponse.getTemplateName());
+ assertEquals(operationId, detailResponse.getId());
+ }
+
+ /**
+ * Tests the creation, reading, and deletion of callback URLs.
+ *
+ * This test covers the complete lifecycle of a callback URL within the system, including:
+ *
+ * - Creating a new callback URL using the provided configuration.
+ * - Retrieving a list of all callback URLs to confirm the successful creation of the new URL.
+ * - Identifying the created callback URL from the list and verifying its properties.
+ * - Deleting the newly created callback URL and verifying its removal by fetching the list again.
+ *
+ * The test ensures that the callback URL is correctly created, listed, and deleted in the system,
+ * and that all associated details are accurately reflected.
+ *
+ * @throws Exception if an error occurs during the creation, retrieval, or deletion of the callback URL,
+ * or if the assertions fail.
+ */
+ @Test
+ void testCreateReadDelete() throws Exception {
+ createCallback();
+ final GetCallbackUrlListResponse callbackUrlListResponse = powerAuthClient.getCallbackUrlList(config.getApplicationId());
+
+ assertNotNull(callbackUrlListResponse.getCallbackUrlList());
+ final CallbackUrl foundCallback = callbackUrlListResponse.getCallbackUrlList().stream()
+ .filter(callback -> PowerAuthControllerTestConfig.CALLBACK_NAME.equals(callback.getName()))
+ .findAny()
+ .orElseThrow(() -> new AssertionError("Callback not found"));
+
+ assertEquals(PowerAuthControllerTestConfig.CALLBACK_URL, foundCallback.getCallbackUrl());
+ assertEquals(config.getApplicationId(), foundCallback.getApplicationId());
+ assertThat(foundCallback.getAttributes(), hasSize(1));
+ assertEquals("activationId", foundCallback.getAttributes().get(0));
+ removeCallback(foundCallback.getId());
+ }
+
+ /**
+ * Tests the update functionality for callback URLs.
+ *
+ * This test verifies the ability to update the properties of a callback URL in the system. The sequential steps include:
+ *
+ * - Creating a new callback URL and obtaining its initial properties.
+ * - Updating the callback URL's properties, such as its name, URL, and attributes, using a mock HTTP POST request.
+ * - Verifying that the updated properties are correctly reflected in the system. This is done by fetching the updated callback URL and comparing its properties with the expected values.
+ *
+ * The test asserts that the callback URL's properties, once updated, match the new values provided, ensuring the system's update mechanism functions as expected.
+ *
+ * @throws Exception if any error occurs during the execution of the test, such as failure in updating the callback URL properties or if the assertions fail.
+ */
+ @Test
+ void testCallbackUpdate() throws Exception {
+ final CreateCallbackUrlResponse callbackUrlResponse = createCallback();
+ final String updatedCallbackName = UUID.randomUUID().toString();
+ final String updatedCallbackUrl = "http://test2.test2";
+ final List callbackAttributes = Arrays.asList("activationId", "userId", "deviceInfo", "platform");
+
+ final UpdateCallbackUrlRequest updateCallbackUrlRequest = new UpdateCallbackUrlRequest();
+ updateCallbackUrlRequest.setCallbackUrl(updatedCallbackUrl);
+ updateCallbackUrlRequest.setAttributes(callbackAttributes);
+ updateCallbackUrlRequest.setName(updatedCallbackName);
+ updateCallbackUrlRequest.setId(callbackUrlResponse.getId());
+ updateCallbackUrlRequest.setApplicationId(config.getApplicationId());
+ updateCallbackUrlRequest.setAuthentication(null);
+
+ final UpdateCallbackUrlResponse updateCallbackUrlResponse = powerAuthClient.updateCallbackUrl(updateCallbackUrlRequest);
+ assertEquals(callbackAttributes, updateCallbackUrlResponse.getAttributes());
+ assertThat(updateCallbackUrlResponse.getAttributes(), hasSize(4));
+ assertEquals(updatedCallbackUrl, updateCallbackUrlResponse.getCallbackUrl());
+ assertEquals(config.getApplicationId(), updateCallbackUrlResponse.getApplicationId());
+ assertEquals(updatedCallbackName, updateCallbackUrlResponse.getName());
+ }
+
+ /**
+ * Tests the CRUD (Create, Read, Update, Delete) operations for application roles.
+ * This process involves adding new roles, verifying their existence, updating them, and finally removing a role.
+ *
+ * The test executes the following sequence:
+ *
+ * - Adds new application roles ('ROLE1', 'ROLE2') using the '/rest/v3/application/roles/create' endpoint.
+ * - Confirms the successful addition of these roles by fetching the application's detail.
+ * - Retrieves the current list of application roles to verify the recently added roles.
+ * - Updates the application roles to a new set of roles ('ROLE5', 'ROLE6') and verifies the update.
+ * - Removes one of the newly added roles ('ROLE5') and checks if the list of roles reflects this change.
+ *
+ *
+ * @throws Exception if any error occurs during the execution of the test.
+ */
+ @Test
+ void testApplicationRolesCrud() throws Exception {
+ final List addedRoles = List.of("ROLE1", "ROLE2");
+ final AddApplicationRolesRequest addApplicationRolesRequest = new AddApplicationRolesRequest();
+ addApplicationRolesRequest.setApplicationId(config.getApplicationId());
+ addApplicationRolesRequest.setApplicationRoles(addedRoles);
+
+ final AddApplicationRolesResponse addApplicationRolesResponse =
+ powerAuthClient.addApplicationRoles(addApplicationRolesRequest);
+ assertEquals(config.getApplicationId(), addApplicationRolesResponse.getApplicationId());
+ assertThat(addApplicationRolesResponse.getApplicationRoles(), hasSize(2));
+ assertTrue(addApplicationRolesResponse.getApplicationRoles().containsAll(addedRoles));
+
+ final GetApplicationDetailRequest applicationDetailRequest = new GetApplicationDetailRequest();
+ applicationDetailRequest.setApplicationId(config.getApplicationId());
+
+ final GetApplicationDetailResponse applicationDetailResponse =
+ powerAuthClient.getApplicationDetail(applicationDetailRequest);
+ assertEquals(config.getApplicationId(), applicationDetailResponse.getApplicationId());
+ assertThat(applicationDetailResponse.getApplicationRoles(), hasSize(2));
+ assertTrue(applicationDetailResponse.getApplicationRoles().containsAll(addedRoles));
+
+ final ListApplicationRolesRequest applicationRolesRequest = new ListApplicationRolesRequest();
+ applicationRolesRequest.setApplicationId(config.getApplicationId());
+
+ final ListApplicationRolesResponse listApplicationRolesResponse =
+ powerAuthClient.listApplicationRoles(applicationRolesRequest);
+ assertThat(listApplicationRolesResponse.getApplicationRoles(), hasSize(2));
+ assertTrue(listApplicationRolesResponse.getApplicationRoles().containsAll(addedRoles));
+
+ final UpdateApplicationRolesRequest updateApplicationRolesRequest = new UpdateApplicationRolesRequest();
+ final List addedRoles2 = List.of("ROLE5", "ROLE6");
+ updateApplicationRolesRequest.setApplicationId(config.getApplicationId());
+ updateApplicationRolesRequest.setApplicationRoles(addedRoles2);
+
+ final UpdateApplicationRolesResponse updateApplicationRolesResponse =
+ powerAuthClient.updateApplicationRoles(updateApplicationRolesRequest);
+ assertEquals(config.getApplicationId(), updateApplicationRolesResponse.getApplicationId());
+ assertThat(updateApplicationRolesResponse.getApplicationRoles(), hasSize(2));
+ assertTrue(updateApplicationRolesResponse.getApplicationRoles().containsAll(addedRoles2));
+
+ final RemoveApplicationRolesRequest removeApplicationRolesRequest = new RemoveApplicationRolesRequest();
+ removeApplicationRolesRequest.setApplicationId(config.getApplicationId());
+ removeApplicationRolesRequest.setApplicationRoles(List.of("ROLE5"));
+
+ final RemoveApplicationRolesResponse removeApplicationRolesResponse =
+ powerAuthClient.removeApplicationRoles(removeApplicationRolesRequest);
+ assertEquals(config.getApplicationId(), removeApplicationRolesResponse.getApplicationId());
+ assertThat(removeApplicationRolesResponse.getApplicationRoles(), hasSize(1));
+ assertTrue(removeApplicationRolesResponse.getApplicationRoles().contains("ROLE6"));
+ }
+
+ /**
+ * Tests the retrieval of the list of applications from the PowerAuth Server.
+ *
+ * This test executes the following actions:
+ *
+ * - Sends a request to the PowerAuth Server to retrieve the list of all registered applications.
+ * - Verifies that the response contains the expected number of applications.
+ * - Checks if the application list includes the application with the ID specified in the test configuration.
+ *
+ *
+ * This test assumes that there is only one application configured in the PowerAuth Server,
+ * which is the application used in the test setup. The application ID is obtained from the test configuration.
+ *
+ * @throws Exception if any error occurs during the execution of the test or if the assertions fail.
+ */
+ @Test
+ void testApplicationList() throws Exception {
+ final GetApplicationListResponse applicationListResponse = powerAuthClient.getApplicationList();
+ assertThat(applicationListResponse.getApplications(), hasSize(1));
+ assertEquals(config.getApplicationId(), applicationListResponse.getApplications().get(0).getApplicationId());
+ }
+
+ /**
+ * Tests the retrieval of application details based on an application key from the PowerAuth Server.
+ *
+ * This test executes the following actions:
+ *
+ * - Constructs a request object with the application key obtained from the test configuration.
+ * - Sends the request to the PowerAuth Server's endpoint responsible for looking up application details by application key.
+ * - Verifies that the response contains the correct application ID associated with the provided application key.
+ *
+ *
+ * The test assumes that an application key is already set up in the test configuration and
+ * corresponds to a valid application registered in the PowerAuth Server.
+ *
+ * @throws Exception if any error occurs during the execution of the test or if the assertions fail.
+ */
+ @Test
+ void testApplicationVersionLookup() throws Exception {
+ final LookupApplicationByAppKeyRequest applicationByAppKeyRequest = new LookupApplicationByAppKeyRequest();
+ applicationByAppKeyRequest.setApplicationKey(config.getApplicationKey());
+
+ final LookupApplicationByAppKeyResponse lookupActivationsResponse =
+ powerAuthClient.lookupApplicationByAppKey(applicationByAppKeyRequest);
+ assertEquals(config.getApplicationId(), lookupActivationsResponse.getApplicationId());
+ }
+
+ /**
+ * Tests the management of support status for application versions in the PowerAuth Server.
+ *
+ * This test executes the following actions:
+ *
+ * - Marks a specific application version as unsupported using the PowerAuth Server API, and verifies the operation's success.
+ * - Subsequently marks the same application version as supported, again using the PowerAuth Server API, and verifies this operation as well.
+ * - Checks that the application version's support status is updated correctly in both cases.
+ *
+ *
+ * The test assumes that the application and version IDs are set up in the test configuration and correspond to a valid application version registered in the PowerAuth Server.
+ *
+ * @throws Exception if any error occurs during the execution of the test or if the assertions fail.
+ */
+ @Test
+ void testApplicationSupport() throws Exception {
+ final UnsupportApplicationVersionRequest unsupportApplicationVersionRequest = new UnsupportApplicationVersionRequest();
+ unsupportApplicationVersionRequest.setApplicationId(config.getApplicationId());
+ unsupportApplicationVersionRequest.setApplicationVersionId(config.getApplicationVersionId());
+
+ final UnsupportApplicationVersionResponse unsupportApplicationVersionResponse =
+ powerAuthClient.unsupportApplicationVersion(unsupportApplicationVersionRequest);
+ assertEquals(config.getApplicationVersionId(), unsupportApplicationVersionResponse.getApplicationVersionId());
+ assertFalse(unsupportApplicationVersionResponse.isSupported());
+
+ final SupportApplicationVersionRequest supportApplicationVersionRequest = new SupportApplicationVersionRequest();
+ supportApplicationVersionRequest.setApplicationId(config.getApplicationId());
+ supportApplicationVersionRequest.setApplicationVersionId(config.getApplicationVersionId());
+
+ final SupportApplicationVersionResponse supportApplicationVersionResponse =
+ powerAuthClient.supportApplicationVersion(supportApplicationVersionRequest);
+ assertEquals(config.getApplicationVersionId(), supportApplicationVersionRequest.getApplicationVersionId());
+ assertTrue(supportApplicationVersionResponse.isSupported());
+ }
+
+ /**
+ * Tests the creation, retrieval, and deletion of application integrations in the PowerAuth Server.
+ *
+ * This test executes the following actions:
+ *
+ * - Creates a new application integration using the PowerAuth Server API and verifies the operation's success.
+ * - Retrieves a list of all current application integrations to confirm the presence of the newly created integration.
+ * - Deletes the newly created integration and then retrieves the list of integrations again to ensure its removal.
+ *
+ *
+ * The test ensures that the PowerAuth Server correctly handles the lifecycle of application integrations, including their creation, listing, and deletion. It checks the presence of necessary attributes in the integration response, such as the integration name, ID, client secret, and client token.
+ *
+ * @throws Exception if any error occurs during the execution of the test or if the assertions fail.
+ */
+ @Test
+ void testApplicationIntegration() throws Exception {
+ final String integrationName = UUID.randomUUID().toString();
+ final CreateIntegrationRequest createIntegrationRequest = new CreateIntegrationRequest();
+ createIntegrationRequest.setName(integrationName);
+
+ final CreateIntegrationResponse createIntegrationResponse = powerAuthClient.createIntegration(createIntegrationRequest);
+ assertEquals(integrationName, createIntegrationResponse.getName());
+ assertNotNull(createIntegrationResponse.getId());
+ assertNotNull(createIntegrationResponse.getClientSecret());
+ assertNotNull(createIntegrationResponse.getClientSecret());
+
+ final GetIntegrationListResponse getIntegrationListResponse = powerAuthClient.getIntegrationList();
+ assertNotNull(getIntegrationListResponse.getItems());
+ assertThat(getIntegrationListResponse.getItems(), hasSize(1));
+ assertEquals(integrationName, getIntegrationListResponse.getItems().get(0).getName());
+ assertEquals(createIntegrationResponse.getId(), getIntegrationListResponse.getItems().get(0).getId());
+ assertEquals(createIntegrationResponse.getClientSecret(), getIntegrationListResponse.getItems().get(0).getClientSecret());
+ assertEquals(createIntegrationResponse.getClientToken(), getIntegrationListResponse.getItems().get(0).getClientToken());
+
+ final RemoveIntegrationRequest removeIntegrationRequest = new RemoveIntegrationRequest();
+ removeIntegrationRequest.setId(createIntegrationResponse.getId());
+
+ final RemoveIntegrationResponse removeIntegrationResponse = powerAuthClient.removeIntegration(removeIntegrationRequest);
+ assertTrue(removeIntegrationResponse.isRemoved());
+ assertEquals(createIntegrationResponse.getId(), removeIntegrationResponse.getId());
+ }
+
+ /**
+ * Tests the complete lifecycle of recovery codes in the PowerAuth Server, including creation, lookup, and revocation.
+ *
+ * The test follows these steps:
+ *
+ * - Creates a set of recovery codes for a specific user and verifies the successful creation and the expected attributes of the response, such as the number of PUks and the user ID.
+ * - Looks up the created recovery codes using the user's ID and activation ID, ensuring the correct status of the recovery codes and PUks.
+ * - Revokes the created recovery codes and confirms their successful revocation.
+ *
+ *
+ * This test ensures that the PowerAuth Server can correctly handle the entire process of managing recovery codes, from creation to revocation, providing the expected responses at each step.
+ *
+ * @throws Exception if any error occurs during the execution of the test or if the assertions fail.
+ */
+ @Test
+ void testRecoveryCodeCreateLookupRevoke() throws Exception {
+ final CreateRecoveryCodeRequest createRecoveryCodeRequest = new CreateRecoveryCodeRequest();
+ createRecoveryCodeRequest.setApplicationId(config.getApplicationId());
+ createRecoveryCodeRequest.setUserId(PowerAuthControllerTestConfig.USER_ID);
+ createRecoveryCodeRequest.setPukCount(2L);
+
+ final CreateRecoveryCodeResponse createRecoveryCodeResponse = powerAuthClient.createRecoveryCode(createRecoveryCodeRequest);
+ assertThat(createRecoveryCodeResponse.getPuks(), hasSize(2));
+ assertEquals(PowerAuthControllerTestConfig.USER_ID, createRecoveryCodeResponse.getUserId());
+
+ final LookupRecoveryCodesRequest lookupRecoveryCodesRequest = new LookupRecoveryCodesRequest();
+ lookupRecoveryCodesRequest.setActivationId(config.getActivationId());
+ lookupRecoveryCodesRequest.setUserId(PowerAuthControllerTestConfig.USER_ID);
+ lookupRecoveryCodesRequest.setRecoveryCodeStatus(RecoveryCodeStatus.CREATED);
+ lookupRecoveryCodesRequest.setRecoveryPukStatus(RecoveryPukStatus.VALID);
+
+ final LookupRecoveryCodesResponse lookupRecoveryCodesResponse = powerAuthClient.lookupRecoveryCodes(lookupRecoveryCodesRequest);
+ assertThat(lookupRecoveryCodesResponse.getRecoveryCodes(), hasSize(greaterThan(0)));
+
+ final RevokeRecoveryCodesRequest revokeRecoveryCodesRequest = new RevokeRecoveryCodesRequest();
+ revokeRecoveryCodesRequest.setRecoveryCodeIds(List.of(createRecoveryCodeResponse.getRecoveryCodeId()));
+
+ final RevokeRecoveryCodesResponse revokeRecoveryCodesResponse = powerAuthClient.revokeRecoveryCodes(revokeRecoveryCodesRequest);
+ assertTrue(revokeRecoveryCodesResponse.isRevoked());
+ }
+
+ /**
+ * Tests the generation of non-personalized offline signature payloads in the PowerAuth Server.
+ *
+ * The test executes the following steps:
+ *
+ * - Sends a request to generate a non-personalized offline signature payload, specifying the application ID and the data to be signed.
+ * - Verifies that the response contains a valid offline data string and a nonce, both essential components for offline signature verification.
+ *
+ *
+ * This test validates the PowerAuth Server's ability to generate the necessary data for offline signature scenarios where personalization of the payload (to a specific user or device) is not required.
+ *
+ * @throws Exception if any unexpected error occurs during the execution of the test or if the response does not contain the expected data.
+ */
+ @Test
+ void testNonPersonalizedOfflineSignaturePayload() throws Exception {
+ final CreateNonPersonalizedOfflineSignaturePayloadRequest nonPersonalizedOfflineSignaturePayloadRequest =
+ new CreateNonPersonalizedOfflineSignaturePayloadRequest();
+ nonPersonalizedOfflineSignaturePayloadRequest.setApplicationId(config.getApplicationId());
+ nonPersonalizedOfflineSignaturePayloadRequest.setData(PowerAuthControllerTestConfig.DATA);
+
+ final CreateNonPersonalizedOfflineSignaturePayloadResponse nonPersonalizedOfflineSignaturePayloadResponse
+ = powerAuthClient.createNonPersonalizedOfflineSignaturePayload(nonPersonalizedOfflineSignaturePayloadRequest);
+ assertNotNull(nonPersonalizedOfflineSignaturePayloadResponse.getOfflineData());
+ assertNotNull(nonPersonalizedOfflineSignaturePayloadResponse.getNonce());
+ }
+
+ /**
+ * Tests the generation of personalized offline signature payloads in the PowerAuth Server.
+ *
+ * This test comprises the following key steps:
+ *
+ * - Initializes an activation to generate a personalized context for the offline signature.
+ * - Sends a request to generate a personalized offline signature payload, specifying the activation ID and the data to be signed.
+ * - Verifies that the response includes a valid offline data string and a nonce, which are crucial for offline signature processes.
+ * - Ensures clean-up by removing the activation created for the test.
+ *
+ *
+ * This test is crucial to ensure the PowerAuth Server correctly handles the generation of offline signature payloads that are personalized to a specific activation, typically representing a user or device.
+ *
+ * @throws Exception if any unexpected error occurs during the execution of the test or if the response fails to contain the expected personalized offline data.
+ */
+ @Test
+ void testPersonalizedOfflineSignaturePayload() throws Exception {
+ initActivation();
+ final CreatePersonalizedOfflineSignaturePayloadRequest personalizedOfflineSignaturePayloadRequest =
+ new CreatePersonalizedOfflineSignaturePayloadRequest();
+ personalizedOfflineSignaturePayloadRequest.setActivationId(config.getActivationId());
+ personalizedOfflineSignaturePayloadRequest.setProximityCheck(null);
+ personalizedOfflineSignaturePayloadRequest.setData(PowerAuthControllerTestConfig.DATA);
+
+ final CreatePersonalizedOfflineSignaturePayloadResponse personalizedOfflineSignaturePayloadResponse
+ = powerAuthClient.createPersonalizedOfflineSignaturePayload(personalizedOfflineSignaturePayloadRequest);
+ assertNotNull(personalizedOfflineSignaturePayloadResponse.getOfflineData());
+ assertNotNull(personalizedOfflineSignaturePayloadResponse.getNonce());
+ removeActivation();
+ }
+
+ /**
+ * Tests the verification of an offline signature.
+ *
+ * This method tests the verification process of an offline signature in the PowerAuth system.
+ * It involves several steps:
+ *
+ * - Initializing activation with configuration settings.
+ * - Generating a public key pair and converting it to byte array.
+ * - Setting up encryption parameters and creating an encrypted activation request.
+ * - Preparing and committing activation using PowerAuth Client.
+ * - Sending a request to verify an offline signature with test data and checking the response.
+ *
+ * The test expects the verification of the offline signature to be invalid (false) for the provided test data.
+ *
+ *
+ * @throws Exception if there is an issue during the setup or execution of the test, such as failure in activation initialization, encryption, or if the PowerAuth Client encounters an error.
+ */
+ @Test
+ void testVerifyOfflineSignature() throws Exception {
+ initActivation();
+
+ final EncryptedRequest encryptedRequest = generateEncryptedRequestActivationLayer(config.getActivationName());
+
+ final PrepareActivationRequest prepareActivationRequest = new PrepareActivationRequest();
+ prepareActivationRequest.setActivationCode(config.getActivationCode());
+ prepareActivationRequest.setApplicationKey(config.getApplicationKey());
+ prepareActivationRequest.setTimestamp(encryptedRequest.getTimestamp());
+ prepareActivationRequest.setProtocolVersion(PowerAuthControllerTestConfig.PROTOCOL_VERSION);
+ prepareActivationRequest.setEncryptedData(encryptedRequest.getEncryptedData());
+ prepareActivationRequest.setMac(encryptedRequest.getMac());
+ prepareActivationRequest.setNonce(encryptedRequest.getNonce());
+ prepareActivationRequest.setEphemeralPublicKey(encryptedRequest.getEphemeralPublicKey());
+
+ final PrepareActivationResponse prepareResponse = powerAuthClient.prepareActivation(prepareActivationRequest);
+ assertEquals(ActivationStatus.PENDING_COMMIT, prepareResponse.getActivationStatus());
+
+ final CommitActivationResponse commitResponse = powerAuthClient.commitActivation(config.getActivationId(), null);
+ assertEquals(config.getActivationId(), commitResponse.getActivationId());
+
+ final VerifyOfflineSignatureRequest verifyOfflineSignatureRequest =
+ new VerifyOfflineSignatureRequest();
+ verifyOfflineSignatureRequest.setActivationId(config.getActivationId());
+ verifyOfflineSignatureRequest.setAllowBiometry(false);
+ verifyOfflineSignatureRequest.setSignature("123456");
+ verifyOfflineSignatureRequest.setData(PowerAuthControllerTestConfig.DATA);
+
+ final VerifyOfflineSignatureResponse verifyOfflineSignatureResponse =
+ powerAuthClient.verifyOfflineSignature(verifyOfflineSignatureRequest);
+ assertFalse(verifyOfflineSignatureResponse.isSignatureValid());
+ assertEquals(config.getActivationId(), verifyOfflineSignatureResponse.getActivationId());
+
+ removeActivation();
+ }
+
+ /**
+ * Tests the creation of an activation in the PowerAuth system.
+ *
+ * This test method performs the following steps to verify the activation creation process:
+ *
+ * - Generates a device key pair and converts the public key to a byte array.
+ * - Creates an activation layer 2 request with the generated public key and activation name.
+ * - Encrypts the activation request using client-side encryption.
+ * - Sends the create activation request to the PowerAuth server with the necessary parameters including the encrypted data and user ID.
+ * - Verifies the creation of the activation by checking the response from the server, ensuring the activation ID, user ID, application ID, and activation status are as expected.
+ * - Retrieves and checks the activation status to ensure it is pending for commit.
+ * - Commits the activation and verifies that the activation process is completed successfully.
+ *
+ *
+ * The test asserts that the activation is created and transitioned through the expected statuses, from pending commit to active.
+ *
+ * @throws Exception if an error occurs during any step of the activation creation and verification process.
+ */
+ @Test
+ void testCreateActivation() throws Exception {
+ final Date expireDate = Date.from(LocalDateTime.now().plusMinutes(5).atZone(ZoneId.systemDefault()).toInstant());
+ final String activationName = "TEST_ACTIVATION";
+ final EncryptedRequest encryptedRequest = generateEncryptedRequestActivationLayer(activationName);
+
+ final CreateActivationRequest createActivationRequest = new CreateActivationRequest();
+ createActivationRequest.setUserId(PowerAuthControllerTestConfig.USER_ID);
+ createActivationRequest.setMaxFailureCount(5L);
+ createActivationRequest.setMac(encryptedRequest.getMac());
+ createActivationRequest.setNonce(encryptedRequest.getNonce());
+ createActivationRequest.setEncryptedData(encryptedRequest.getEncryptedData());
+ createActivationRequest.setEphemeralPublicKey(encryptedRequest.getEphemeralPublicKey());
+ createActivationRequest.setTimestampActivationExpire(expireDate);
+ createActivationRequest.setTimestamp(encryptedRequest.getTimestamp());
+ createActivationRequest.setProtocolVersion(PowerAuthControllerTestConfig.PROTOCOL_VERSION);
+ createActivationRequest.setApplicationKey(config.getApplicationKey());
+
+ final CreateActivationResponse createActivationResponse = powerAuthClient.createActivation(createActivationRequest);
+ assertNotNull(createActivationResponse.getActivationId());
+ assertEquals(PowerAuthControllerTestConfig.USER_ID, createActivationResponse.getUserId());
+ assertEquals(config.getApplicationId(), createActivationResponse.getApplicationId());
+ assertEquals(ActivationStatus.PENDING_COMMIT, createActivationResponse.getActivationStatus());
+
+ final GetActivationStatusResponse statusResponse = powerAuthClient.getActivationStatus(createActivationResponse.getActivationId());
+ assertEquals(ActivationStatus.PENDING_COMMIT, statusResponse.getActivationStatus());
+ assertEquals(createActivationResponse.getActivationId(), statusResponse.getActivationId());
+
+ final CommitActivationResponse commitResponse = powerAuthClient
+ .commitActivation(createActivationResponse.getActivationId(), PowerAuthControllerTestConfig.USER_ID);
+ assertTrue(commitResponse.isActivated());
+ assertEquals(createActivationResponse.getActivationId(), commitResponse.getActivationId());
+ }
+
+ /**
+ * Tests the process of updating an activation OTP (One-Time Password) and committing the activation in PowerAuth system.
+ *
+ * The method performs the following operations:
+ *
+ * - Initializes an activation with a specific configuration using the PowerAuth client.
+ * - Generates an encrypted request for the activation including necessary parameters like activation name, code, application key, and others.
+ * - Sends a 'prepare activation' request and verifies that the activation status is 'PENDING_COMMIT'.
+ * - Updates the activation OTP by sending an 'update activation OTP' request and checks the response to ensure the OTP update is successful.
+ * - Sends a 'commit activation' request with the updated OTP and verifies that the activation is successfully activated.
+ *
+ *
+ * This test ensures that the activation can be updated with a new OTP and then successfully committed using this new OTP.
+ *
+ * @throws Exception if an error occurs during the preparation, OTP update, or activation commitment process.
+ */
+ @Test
+ void testUpdateActivationOtpAndCommit() throws Exception {
+ initActivation();
+ final String activationOtp = "12345678";
+ final EncryptedRequest encryptedRequest = generateEncryptedRequestActivationLayer(config.getActivationName());
+
+ final PrepareActivationRequest prepareActivationRequest = new PrepareActivationRequest();
+ prepareActivationRequest.setActivationCode(config.getActivationCode());
+ prepareActivationRequest.setApplicationKey(config.getApplicationKey());
+ prepareActivationRequest.setTimestamp(encryptedRequest.getTimestamp());
+ prepareActivationRequest.setProtocolVersion(PowerAuthControllerTestConfig.PROTOCOL_VERSION);
+ prepareActivationRequest.setEncryptedData(encryptedRequest.getEncryptedData());
+ prepareActivationRequest.setMac(encryptedRequest.getMac());
+ prepareActivationRequest.setNonce(encryptedRequest.getNonce());
+ prepareActivationRequest.setEphemeralPublicKey(encryptedRequest.getEphemeralPublicKey());
+
+ final PrepareActivationResponse prepareResponse = powerAuthClient.prepareActivation(prepareActivationRequest);
+ assertEquals(ActivationStatus.PENDING_COMMIT, prepareResponse.getActivationStatus());
+
+ final UpdateActivationOtpRequest updateActivationOtpRequest = new UpdateActivationOtpRequest();
+ updateActivationOtpRequest.setActivationId(config.getActivationId());
+ updateActivationOtpRequest.setActivationOtp(activationOtp);
+ updateActivationOtpRequest.setExternalUserId(PowerAuthControllerTestConfig.USER_ID);
+
+ final UpdateActivationOtpResponse otpResponse = powerAuthClient.updateActivationOtp(updateActivationOtpRequest);
+ assertTrue(otpResponse.isUpdated());
+ assertEquals(config.getActivationId(), otpResponse.getActivationId());
+
+ final CommitActivationRequest commitActivationRequest = new CommitActivationRequest();
+ commitActivationRequest.setActivationOtp(activationOtp);
+ commitActivationRequest.setActivationId(config.getActivationId());
+ commitActivationRequest.setExternalUserId(PowerAuthControllerTestConfig.USER_ID);
+
+ final CommitActivationResponse commitResponse = powerAuthClient.commitActivation(commitActivationRequest);
+ assertTrue(commitResponse.isActivated());
+ assertEquals(config.getActivationId(), commitResponse.getActivationId());
+ }
+
+ /**
+ * Tests the retrieval and utilization of the ECIES (Elliptic Curve Integrated Encryption Scheme) decryptor in the PowerAuth system.
+ *
+ * This test performs the following operations:
+ *
+ * - Generates test data and encrypts it using the client-side ECIES encryption process.
+ * - Constructs a request to retrieve the ECIES decryptor from the PowerAuth server, including necessary parameters like protocol version, application key, and encrypted data details.
+ * - Sends the request to the PowerAuth server and retrieves the ECIES decryptor response, including the secret key and shared information.
+ * - Decrypts the previously encrypted data using the server-side ECIES decryptor with the retrieved keys and verifies the correctness of the decryption.
+ *
+ *
+ * The test ensures that the ECIES decryptor can be correctly obtained from the PowerAuth server and used to decrypt data encrypted by the client, validating the integrity and functionality of the ECIES encryption/decryption process.
+ *
+ * @throws Exception if an error occurs during the encryption, decryption, or communication with the PowerAuth server.
+ */
+ @Test
+ void testGetEciesDecryptor() throws Exception {
+ final String requestData = "test_data";
+
+ final ClientEncryptor clientEncryptor = encryptorFactory.getClientEncryptor(
+ EncryptorId.APPLICATION_SCOPE_GENERIC,
+ new EncryptorParameters(PowerAuthControllerTestConfig.PROTOCOL_VERSION, config.getApplicationKey(), null),
+ new ClientEncryptorSecrets(wrapPublicKeyString(), config.getApplicationSecret())
+ );
+ final EncryptedRequest encryptedRequest = clientEncryptor.encryptRequest(requestData.getBytes(StandardCharsets.UTF_8));
+ final GetEciesDecryptorRequest eciesDecryptorRequest = new GetEciesDecryptorRequest();
+ eciesDecryptorRequest.setProtocolVersion(PowerAuthControllerTestConfig.PROTOCOL_VERSION);
+ eciesDecryptorRequest.setActivationId(null);
+ eciesDecryptorRequest.setApplicationKey(config.getApplicationKey());
+ eciesDecryptorRequest.setEphemeralPublicKey(encryptedRequest.getEphemeralPublicKey());
+ eciesDecryptorRequest.setNonce(encryptedRequest.getNonce());
+ eciesDecryptorRequest.setTimestamp(encryptedRequest.getTimestamp());
+ final GetEciesDecryptorResponse decryptorResponse = powerAuthClient.getEciesDecryptor(eciesDecryptorRequest);
+
+ final byte[] secretKey = Base64.getDecoder().decode(decryptorResponse.getSecretKey());
+ final byte[] sharedInfo2Base = Base64.getDecoder().decode(decryptorResponse.getSharedInfo2());
+ final ServerEncryptor serverEncryptor = encryptorFactory.getServerEncryptor(
+ EncryptorId.APPLICATION_SCOPE_GENERIC,
+ new EncryptorParameters(PowerAuthControllerTestConfig.PROTOCOL_VERSION, config.getApplicationKey(), null),
+ new ServerEncryptorSecrets(secretKey, sharedInfo2Base)
+ );
+ final byte[] decryptedData = serverEncryptor.decryptRequest(encryptedRequest);
+ assertArrayEquals(requestData.getBytes(StandardCharsets.UTF_8), decryptedData);
+ }
+
+ /**
+ * Tests the retrieval of system status.
+ *
+ * This test verifies the response from the system status endpoint. It checks for expected values
+ * such as application name, status, and display name. Additionally, it ensures the timestamp
+ * returned by the system status is the current date (ignoring the time part).
+ * This is crucial for verifying the system's operational status and basic metadata.
+ *
+ *
+ * @throws Exception if an error occurs during the retrieval of the system status or if any of the assertions fail.
+ */
+ @Test
+ void testSystemStatus() throws Exception {
+ final GetSystemStatusResponse systemStatusResponse = powerAuthClient.getSystemStatus();
+ assertEquals("OK", systemStatusResponse.getStatus());
+ assertEquals("powerauth-server", systemStatusResponse.getApplicationName());
+ assertEquals("PowerAuth Server", systemStatusResponse.getApplicationDisplayName());
+ assertNotNull(systemStatusResponse.getTimestamp());
+ final LocalDate localDateFromResponse = systemStatusResponse.getTimestamp().toInstant()
+ .atZone(ZoneId.systemDefault()).toLocalDate();
+ assertEquals(LocalDate.now(), localDateFromResponse);
+ }
+
+ /**
+ * Tests the retrieval of a list of error codes.
+ *
+ * This test sends a request to obtain a comprehensive list of error codes available in the system.
+ * It verifies that the response contains a list with an expected minimum number of error entries,
+ * ensuring a broad range of error scenarios is covered. The test specifically requests the error
+ * codes in English language, but it can be adapted for other languages if needed.
+ *
+ *
+ * The assertion for the minimum number of entries (more than 32) is based on the current
+ * implementation and may need to be adjusted if the number of error codes changes in future versions.
+ *
+ * @throws Exception if an error occurs during the retrieval of the error list or if the assertion for the minimum number of error entries fails.
+ */
+ @Test
+ void testErrorList() throws Exception {
+ final GetErrorCodeListRequest getErrorCodeListRequest = new GetErrorCodeListRequest();
+ getErrorCodeListRequest.setLanguage(Locale.ENGLISH.getLanguage());
+
+ final GetErrorCodeListResponse errorCodeListResponse = powerAuthClient.getErrorList(getErrorCodeListRequest);
+ assertThat(errorCodeListResponse.getErrors(), hasSize(greaterThan(32)));
+ }
+
+ /* HELPER INITIALIZATION METHODS */
+
+ /**
+ * Creates a request object for creating an operation.
+ *
+ * This helper method constructs and returns an {@link OperationCreateRequest} with
+ * predefined application ID, template name, user ID, and proximity OTP settings.
+ *
+ * @param proximityOtpEnabled a boolean indicating whether proximity OTP is enabled
+ * @return a configured {@link OperationCreateRequest} instance
+ */
+ private OperationCreateRequest createOperationCreateRequest(final boolean proximityOtpEnabled) {
+ final OperationCreateRequest operationCreateRequest = new OperationCreateRequest();
+ operationCreateRequest.setApplications(List.of(config.getApplicationId()));
+ operationCreateRequest.setTemplateName(config.getLoginOperationTemplateName());
+ operationCreateRequest.setUserId(PowerAuthControllerTestConfig.USER_ID);
+ operationCreateRequest.setProximityCheckEnabled(proximityOtpEnabled);
+ return operationCreateRequest;
+ }
+
+ /**
+ * Creates a request object for creating a callback URL.
+ *
+ * This helper method constructs and returns a {@link CreateCallbackUrlRequest} with
+ * predefined callback URL, name, type, application ID, and other settings.
+ *
+ * @return a configured {@link CreateCallbackUrlRequest} instance
+ */
+ private CreateCallbackUrlRequest createCallbackUrlRequest() {
+ final CreateCallbackUrlRequest callbackUrlRequest = new CreateCallbackUrlRequest();
+ callbackUrlRequest.setCallbackUrl(PowerAuthControllerTestConfig.CALLBACK_URL);
+ callbackUrlRequest.setName(PowerAuthControllerTestConfig.CALLBACK_NAME);
+ callbackUrlRequest.setType(CallbackUrlType.ACTIVATION_STATUS_CHANGE.name());
+ callbackUrlRequest.setApplicationId(config.getApplicationId());
+ callbackUrlRequest.setAttributes(Collections.singletonList("activationId"));
+ callbackUrlRequest.setAuthentication(null);
+ return callbackUrlRequest;
+ }
+
+ /**
+ * Initializes a new activation and verifies its status.
+ *
+ * This method creates an activation for the provided user and application IDs and verifies
+ * the response to ensure the activation is successfully initialized. It sets the activation
+ * ID in the test configuration and asserts that the activation status is 'CREATED'.
+ *
+ * @throws Exception if any error occurs during activation initialization or verification
+ */
+ protected void initActivation() throws Exception {
+ final InitActivationRequest initActivationRequest = new InitActivationRequest();
+ initActivationRequest.setUserId(PowerAuthControllerTestConfig.USER_ID);
+ initActivationRequest.setApplicationId(config.getApplicationId());
+
+ final InitActivationResponse initActivationResponse = powerAuthClient.initActivation(initActivationRequest);
+ assertNotNull(initActivationResponse);
+ assertNotNull(initActivationResponse.getActivationId());
+ assertNotNull(initActivationResponse.getActivationSignature());
+ assertNotNull(initActivationResponse.getApplicationId());
+ assertEquals(PowerAuthControllerTestConfig.USER_ID, initActivationResponse.getUserId());
+ assertEquals(config.getApplicationId(), initActivationResponse.getApplicationId());
+
+ final GetActivationStatusResponse activationStatusResponse =
+ powerAuthClient.getActivationStatus(initActivationResponse.getActivationId());
+
+ assertEquals(ActivationStatus.CREATED, activationStatusResponse.getActivationStatus());
+ config.setActivationId(activationStatusResponse.getActivationId());
+ config.setActivationCode(activationStatusResponse.getActivationCode());
+ config.setActivationName(activationStatusResponse.getActivationName());
}
+
+ /**
+ * Creates an application in the PowerAuth Server if it does not already exist.
+ *
+ * This method checks for the existence of an application and its version. If not present,
+ * it creates them and sets relevant fields in the test configuration. It also ensures the
+ * application version is supported and sets up activation recovery settings.
+ *
+ * @throws Exception if any error occurs during application creation or setup
+ */
+ protected void createApplication() throws Exception {
+ final GetApplicationListResponse applicationsListResponse = powerAuthClient.getApplicationList();
+ final var applicationOptional = applicationsListResponse.getApplications().stream()
+ .filter(app -> app.getApplicationId().equals(config.getApplicationName()))
+ .findFirst();
+
+ applicationOptional.ifPresent(app -> config.setApplicationId(app.getApplicationId()));
+ final boolean applicationExists = applicationOptional.isPresent();
+
+ if (!applicationExists) {
+ final CreateApplicationResponse response = powerAuthClient.createApplication(config.getApplicationName());
+ assertNotEquals("0", response.getApplicationId());
+ assertEquals(config.getApplicationName(), response.getApplicationId());
+ config.setApplicationId(response.getApplicationId());
+ }
+
+ final GetApplicationDetailResponse detail = powerAuthClient.getApplicationDetail(config.getApplicationId());
+ final var versionOptional = detail.getVersions().stream()
+ .filter(appVersion -> appVersion.getApplicationVersionId().equals(config.getApplicationVersion()))
+ .findFirst();
+ versionOptional.ifPresent(
+ appVersion -> {
+ config.setApplicationVersionId(appVersion.getApplicationVersionId());
+ config.setApplicationKey(appVersion.getApplicationKey());
+ config.setApplicationSecret(appVersion.getApplicationSecret());
+ });
+ final boolean versionExists = versionOptional.isPresent();
+
+ config.setMasterPublicKey(detail.getMasterPublicKey());
+ if (!versionExists) {
+ final CreateApplicationVersionResponse versionResponse = powerAuthClient.createApplicationVersion(config.getApplicationId(), config.getApplicationVersion());
+ assertNotEquals("0", versionResponse.getApplicationVersionId());
+ assertEquals(config.getApplicationVersion(), versionResponse.getApplicationVersionId());
+ config.setApplicationVersionId(versionResponse.getApplicationVersionId());
+ config.setApplicationKey(versionResponse.getApplicationKey());
+ config.setApplicationSecret(versionResponse.getApplicationSecret());
+ } else {
+ powerAuthClient.supportApplicationVersion(config.getApplicationId(), config.getApplicationVersionId());
+ }
+ final GetRecoveryConfigResponse recoveryResponse = powerAuthClient.getRecoveryConfig(config.getApplicationId());
+ if (!recoveryResponse.isActivationRecoveryEnabled() || !recoveryResponse.isRecoveryPostcardEnabled() || recoveryResponse.getPostcardPublicKey() == null || recoveryResponse.getRemotePostcardPublicKey() == null) {
+ final UpdateRecoveryConfigRequest request = new UpdateRecoveryConfigRequest();
+ request.setApplicationId(config.getApplicationId());
+ request.setActivationRecoveryEnabled(true);
+ request.setRecoveryPostcardEnabled(true);
+ request.setAllowMultipleRecoveryCodes(false);
+ request.setRemotePostcardPublicKey(PowerAuthControllerTestConfig.PUBLIC_KEY_RECOVERY_POSTCARD_BASE64);
+ powerAuthClient.updateRecoveryConfig(request);
+ }
+ }
+
+ /**
+ * Creates a new callback URL in the PowerAuth Server and verifies its creation.
+ *
+ * This method creates a callback URL with predefined settings and asserts the response
+ * to ensure the callback URL is successfully created. It returns the response containing
+ * the callback URL details.
+ *
+ * @return the response containing the created callback URL details
+ * @throws Exception if any error occurs during callback URL creation
+ */
+ protected CreateCallbackUrlResponse createCallback() throws Exception {
+ final CreateCallbackUrlRequest callbackUrlRequest = createCallbackUrlRequest();
+ final CreateCallbackUrlResponse response = powerAuthClient.createCallbackUrl(callbackUrlRequest);
+ assertEquals(PowerAuthControllerTestConfig.CALLBACK_NAME, response.getName());
+ assertEquals(PowerAuthControllerTestConfig.CALLBACK_URL, response.getCallbackUrl());
+ assertEquals(config.getApplicationId(), response.getApplicationId());
+
+ return response;
+ }
+
+ /**
+ * Removes a specified callback URL from the PowerAuth Server.
+ *
+ * This method deletes a callback URL using its ID and verifies the removal by asserting
+ * the response.
+ *
+ * @param callbackId the ID of the callback URL to be removed
+ * @throws Exception if any error occurs during callback URL removal
+ */
+ protected void removeCallback(final String callbackId) throws Exception {
+ final RemoveCallbackUrlRequest removeCallbackUrlRequest = new RemoveCallbackUrlRequest();
+ removeCallbackUrlRequest.setId(callbackId);
+
+ final RemoveCallbackUrlResponse removeCallbackUrlResponse = powerAuthClient.removeCallbackUrl(removeCallbackUrlRequest);
+ assertEquals(callbackId, removeCallbackUrlResponse.getId());
+ assertTrue(removeCallbackUrlResponse.isRemoved());
+ }
+
+ /**
+ * Removes an activation from the PowerAuth Server.
+ *
+ * This method deletes an activation using its ID and verifies the removal by asserting
+ * the response.
+ *
+ * @throws Exception if any error occurs during activation removal
+ */
+ protected void removeActivation() throws Exception {
+ final RemoveActivationRequest removeActivationRequest = new RemoveActivationRequest();
+ removeActivationRequest.setActivationId(config.getActivationId());
+ final RemoveActivationResponse removeActivationResponse = powerAuthClient.removeActivation(removeActivationRequest);
+ assertTrue(removeActivationResponse.isRemoved());
+ }
+
+ /**
+ * Creates a new operation in the PowerAuth Server.
+ *
+ * This method creates an operation with predefined settings and asserts the response
+ * to ensure the operation is successfully created. It returns the response containing
+ * operation details.
+ *
+ * @return the response containing the created operation details
+ * @throws Exception if any error occurs during operation creation
+ */
+ protected OperationDetailResponse createOperation() throws Exception {
+ final OperationDetailResponse operationDetailResponse = powerAuthClient
+ .createOperation(createOperationCreateRequest(false));
+ assertNotNull(operationDetailResponse.getId());
+ assertEquals(OperationStatus.PENDING, operationDetailResponse.getStatus());
+ assertEquals(config.getLoginOperationTemplateName(), operationDetailResponse.getTemplateName());
+ return operationDetailResponse;
+ }
+
+ /**
+ * Creates a new login operation template in the PowerAuth Server.
+ *
+ * This method creates a login operation template with predefined settings. It sets the
+ * template name and ID in the test configuration and asserts the response to ensure
+ * the template is successfully created.
+ *
+ * @throws Exception if any error occurs during operation template creation
+ */
+ protected void createLoginOperationTemplate() throws Exception {
+ final OperationTemplateCreateRequest request = new OperationTemplateCreateRequest();
+ request.setTemplateName(UUID.randomUUID().toString());
+ request.setOperationType("login");
+ request.getSignatureType().addAll(Arrays.asList(SignatureType.values()));
+ request.setDataTemplate(PowerAuthControllerTestConfig.DATA);
+ request.setExpiration(300L);
+ request.setMaxFailureCount(5L);
+
+ final OperationTemplateDetailResponse operationTemplate = powerAuthClient.createOperationTemplate(request);
+ config.setLoginOperationTemplateName(operationTemplate.getTemplateName());
+ config.setLoginOperationTemplateId(operationTemplate.getId());
+ }
+
+ /**
+ * Converts a string representation of a master public key into its corresponding {@link PublicKey} object.
+ *
+ * This method uses the {@link KeyConvertor} to decode the base64-encoded string representation of the master public key
+ * into a byte array, which is then converted to a {@link PublicKey} object.
+ *
+ * @return The {@link PublicKey} object corresponding to the decoded master public key.
+ * @throws Exception if there is an error during the conversion process.
+ */
+ protected PublicKey wrapPublicKeyString() throws Exception {
+ return keyConvertor.convertBytesToPublicKey(Base64.getDecoder().decode(config.getMasterPublicKey()));
+ }
+
+ /**
+ * Generates an encrypted request for the Activation Layer 2 using ECIES (Elliptic Curve Integrated Encryption Scheme).
+ *
+ * This method performs the following steps:
+ *
+ * - Generates a new key pair and converts the public key to a byte array.
+ * - Creates an {@link ActivationLayer2Request} with the activation name and device public key.
+ * - Initializes a {@link ClientEncryptor} for Activation Layer 2 encryption.
+ * - Serializes the {@link ActivationLayer2Request} into a byte array.
+ * - Encrypts the serialized request data using the client encryptor.
+ *
+ *
+ * The method returns an {@link EncryptedRequest} containing the encrypted request data and additional encryption parameters.
+ *
+ * @param activationName The activation name for the request.
+ * @return The {@link EncryptedRequest} containing the encrypted request data.
+ * @throws Exception if there is an error during the encryption or serialization process.
+ */
+ protected EncryptedRequest generateEncryptedRequestActivationLayer(final String activationName) throws Exception {
+ final KeyPair keyPair = keyGenerator.generateKeyPair();
+ final PublicKey publicKey = keyPair.getPublic();
+ final byte[] publicKeyBytes = keyConvertor.convertPublicKeyToBytes(publicKey);
+ final ActivationLayer2Request requestL2 = new ActivationLayer2Request();
+ requestL2.setActivationName(activationName);
+ requestL2.setDevicePublicKey(Base64.getEncoder().encodeToString(publicKeyBytes));
+
+ final ClientEncryptor clientEncryptor = encryptorFactory.getClientEncryptor(
+ EncryptorId.ACTIVATION_LAYER_2,
+ new EncryptorParameters(PowerAuthControllerTestConfig.PROTOCOL_VERSION, config.getApplicationKey(), null),
+ new ClientEncryptorSecrets(wrapPublicKeyString(), config.getApplicationSecret())
+ );
+
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ objectMapper.writeValue(baos, requestL2);
+ return clientEncryptor.encryptRequest(baos.toByteArray());
+ }
+
}
diff --git a/powerauth-java-server/src/test/java/io/getlime/security/powerauth/app/server/controller/api/PowerAuthControllerTestConfig.java b/powerauth-java-server/src/test/java/io/getlime/security/powerauth/app/server/controller/api/PowerAuthControllerTestConfig.java
new file mode 100644
index 000000000..a6675d81f
--- /dev/null
+++ b/powerauth-java-server/src/test/java/io/getlime/security/powerauth/app/server/controller/api/PowerAuthControllerTestConfig.java
@@ -0,0 +1,82 @@
+/*
+ * PowerAuth Server 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.server.controller.api;
+
+import com.wultra.security.powerauth.client.PowerAuthClient;
+import com.wultra.security.powerauth.rest.client.PowerAuthRestClient;
+import com.wultra.security.powerauth.rest.client.PowerAuthRestClientConfiguration;
+import lombok.Data;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.*;
+
+/**
+ * Configuration class for PowerAuth Controller tests.
+ *
+ * This class provides configuration settings and helper methods
+ * for testing PowerAuth Controller. It includes methods for initializing
+ * test data, creating applications, managing activations, and handling
+ * other necessary setup for conducting tests effectively.
+ *
+ *
+ * @author Jan Dusil, jan.dusil@wultra.com
+ */
+@Configuration
+@Data
+public class PowerAuthControllerTestConfig {
+
+ private static final String POWERAUTH_REST_URL = "http://localhost:8080/rest";
+ protected static final String PUBLIC_KEY_RECOVERY_POSTCARD_BASE64 = "BABXgGoj4Lizl3GN0rjrtileEEwekFkpX1ERS9yyYjyuM1Iqdti3ihtATBxk5XGvjetPO1YC+qXciUYjIsETtbI=";
+ protected static final String USER_ID = "test-user";
+ protected static final String DATA = "A2";
+ protected static final String CALLBACK_NAME = UUID.randomUUID().toString();
+ protected static final String CALLBACK_URL = "http://test.test";
+ protected static final String PROTOCOL_VERSION = "3.2";
+
+ private String applicationId;
+ private String applicationVersionId;
+ private String applicationKey;
+ private String applicationSecret;
+ private String masterPublicKey;
+ private String applicationVersion = "default" + "_" + System.currentTimeMillis();
+ private final String applicationName = "Pa_tests_component";
+ private Long loginOperationTemplateId;
+ private String loginOperationTemplateName;
+ private String activationId;
+ private String activationCode;
+ private String activationName;
+
+ /**
+ * Creates and configures a new {@link PowerAuthClient} bean.
+ *
+ * The method configures and returns a PowerAuthClient instance for interacting with
+ * the PowerAuth Server. It sets up the client with the necessary configurations such as
+ * accepting invalid SSL certificates for testing purposes.
+ *
+ * @return A configured instance of PowerAuthClient
+ * @throws Exception if there is an issue creating the PowerAuthClient instance
+ */
+ @Bean
+ public PowerAuthClient powerAuthClient() throws Exception {
+ final PowerAuthRestClientConfiguration config = new PowerAuthRestClientConfiguration();
+ config.setAcceptInvalidSslCertificate(true);
+ return new PowerAuthRestClient(POWERAUTH_REST_URL);
+ }
+
+}
diff --git a/powerauth-java-server/src/test/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/OperationServiceBehaviorTest.java b/powerauth-java-server/src/test/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/OperationServiceBehaviorTest.java
index 7eac57119..b3dc9a2a7 100644
--- a/powerauth-java-server/src/test/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/OperationServiceBehaviorTest.java
+++ b/powerauth-java-server/src/test/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/OperationServiceBehaviorTest.java
@@ -425,6 +425,7 @@ void testFindPendingOperationsForUserSorting() throws Exception {
final int year = calendar.get(Calendar.YEAR);
assertEquals(2023, year);
}
+
/**
* Tests the scenario when an application does not exist in the database for pending operations.
*/
@@ -472,6 +473,43 @@ void testOperationClaim() throws Exception {
assertEquals(userId, operationService.getOperation(detailRequest).getUserId());
}
+ @Test
+ void testOperationApproveWithValidProximityOtp() throws Exception {
+ final OperationDetailResponse operation = createOperation(true);
+ final String operationId = operation.getId();
+ final OperationDetailRequest detailRequest = new OperationDetailRequest();
+ detailRequest.setOperationId(operationId);
+
+ final OperationDetailResponse detailResponse = operationService.getOperation(detailRequest);
+ final String totp = detailResponse.getProximityOtp();
+ assertNotNull(totp);
+
+ final OperationApproveRequest approveRequest = createOperationApproveRequest(operationId);
+ approveRequest.getAdditionalData().put("proximity_otp", totp);
+
+ final OperationUserActionResponse actionResponse = operationService.attemptApproveOperation(approveRequest);
+
+ assertEquals("APPROVED", actionResponse.getResult().toString());
+ }
+
+ @Test
+ void testOperationApproveWithInvalidProximityOtp() throws Exception {
+ final OperationDetailResponse operation = createOperation(true);
+
+ final OperationDetailRequest detailRequest = new OperationDetailRequest();
+ detailRequest.setOperationId(operation.getId());
+
+ final String totp = operationService.getOperation(detailRequest).getProximityOtp();
+ assertNotNull(totp);
+
+ final OperationApproveRequest approveRequest = createOperationApproveRequest(operation.getId());
+ approveRequest.getAdditionalData().put("proximity_otp", "1111"); // invalid otp on purpose, it is too short
+
+ final OperationUserActionResponse result = operationService.attemptApproveOperation(approveRequest);
+
+ assertEquals("APPROVAL_FAILED", result.getResult().toString());
+ }
+
private void createApplication() throws GenericServiceException {
boolean appExists = applicationService.getApplicationList().getApplications().stream()
.anyMatch(app -> app.getApplicationId().equals(APP_ID));
@@ -504,4 +542,23 @@ private void createOperationTemplateForLogin() throws GenericServiceException {
}
}
+ private OperationDetailResponse createOperation(final boolean proximityOtp) throws Exception {
+ final OperationCreateRequest operationCreateRequest = new OperationCreateRequest();
+ operationCreateRequest.setApplications(List.of("PA_Tests"));
+ operationCreateRequest.setTemplateName("test-template");
+ operationCreateRequest.setUserId("test-user");
+ operationCreateRequest.setProximityCheckEnabled(proximityOtp);
+ return operationService.createOperation(operationCreateRequest);
+ }
+
+ private static OperationApproveRequest createOperationApproveRequest(final String operationId) {
+ final OperationApproveRequest approveRequest = new OperationApproveRequest();
+ approveRequest.setOperationId(operationId);
+ approveRequest.setUserId("test-user");
+ approveRequest.setApplicationId("PA_Tests");
+ approveRequest.setData("A2");
+ approveRequest.setSignatureType(SignatureType.POSSESSION_KNOWLEDGE);
+ return approveRequest;
+ }
+
}
diff --git a/powerauth-java-server/src/test/resources/io/getlime/security/powerauth/app/server/controller/api/PowerAuthControllerTest.sql b/powerauth-java-server/src/test/resources/io/getlime/security/powerauth/app/server/controller/api/PowerAuthControllerTest.sql
deleted file mode 100644
index bf3b2bf4b..000000000
--- a/powerauth-java-server/src/test/resources/io/getlime/security/powerauth/app/server/controller/api/PowerAuthControllerTest.sql
+++ /dev/null
@@ -1,8 +0,0 @@
-INSERT INTO pa_application (id, name, roles) VALUES
- (21, 'PA_Tests', '[ "ROLE3", "ROLE4" ]');
-
-INSERT INTO pa_master_keypair (id, application_id, master_key_private_base64, master_key_public_base64, name, timestamp_created)
-VALUES (21, 21, 'KdcJHQAT/BBF+26uBGNhGC0GQ93ncTx7V6kusNA8AdE=', 'BP8ZZ0LjiwRCQPob3NFwF9pPDLhxCjnPNmENzayEeeGCiDdk0gl3UzUhYk9ntMg18LZdhpvYnprZ8mk/71WlQqo=', 'PA_Tests Default Keypair', '2022-06-07 09:13:27.599000');
-
-INSERT INTO pa_activation (activation_id, application_id, user_id, activation_name, activation_code, activation_status, activation_otp, activation_otp_validation, blocked_reason, counter, ctr_data, device_public_key_base64, extras, platform, device_info, flags, failed_attempts, max_failed_attempts, server_private_key_base64, server_private_key_encryption, server_public_key_base64, timestamp_activation_expire, timestamp_created, timestamp_last_used, timestamp_last_change, master_keypair_id, version) VALUES
- ('e43a5dec-afea-4a10-a80b-b2183399f16b', 21, 'TestUserV3_d8c2e122-b12a-47f1-bca7-e04637bffd14', 'test v3', 'PXSNR-E2B46-7TY3G-TMR2Q', 3, null, 0, null, 0, 'D5XibWWPCv+nOOfcdfnUGQ==', 'BF3Sc/vqg8Zk70Y8rbT45xzAIxblGoWgLqknCHuNj7f6QFBNi2UnLbG7yMqf2eWShhyBJdu9zqx7DG2qzlqhbBE=', null, 'unknown', 'backend-tests', '[ ]', 0, 1, 'PUz/He8+RFoOPS1NG6Gw3TDXIQ/DnS1skNBOQWzXX60=', 0, 'BPHJ4N90NUuLDq92FJUPcaKZOMad1KH2HrwQEN9DB5ST5fiJU4baYF1VlK1JHglnnN1miL3/Qb6IyW3YSMBySYM=', '2023-04-03 14:04:06.015000', '2023-04-03 13:59:06.015000', '2023-04-03 13:59:16.293000', '2023-04-03 13:59:16.343000', 21, 3);
From 70af45c34f2435da2b99734d43fae71d8597cb1f Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 22 Jan 2024 01:57:40 +0000
Subject: [PATCH 054/146] Bump
org.springframework.boot:spring-boot-starter-parent
Bumps [org.springframework.boot:spring-boot-starter-parent](https://github.com/spring-projects/spring-boot) from 3.2.1 to 3.2.2.
- [Release notes](https://github.com/spring-projects/spring-boot/releases)
- [Commits](https://github.com/spring-projects/spring-boot/compare/v3.2.1...v3.2.2)
---
updated-dependencies:
- dependency-name: org.springframework.boot:spring-boot-starter-parent
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
---
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index 50f7f2efc..230234539 100644
--- a/pom.xml
+++ b/pom.xml
@@ -32,7 +32,7 @@
org.springframework.boot
spring-boot-starter-parent
- 3.2.1
+ 3.2.2
From 12e1f299e7a69c062ced696bf4f04758c418fd6a Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 22 Jan 2024 01:58:28 +0000
Subject: [PATCH 055/146] Bump com.webauthn4j:webauthn4j-test
Bumps [com.webauthn4j:webauthn4j-test](https://github.com/webauthn4j/webauthn4j) from 0.21.8.RELEASE to 0.22.0.RELEASE.
- [Release notes](https://github.com/webauthn4j/webauthn4j/releases)
- [Changelog](https://github.com/webauthn4j/webauthn4j/blob/master/github-release-notes-generator.yml)
- [Commits](https://github.com/webauthn4j/webauthn4j/compare/0.21.8.RELEASE...0.22.0.RELEASE)
---
updated-dependencies:
- dependency-name: com.webauthn4j:webauthn4j-test
dependency-type: direct:development
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot]
---
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index 50f7f2efc..b1bc81f83 100644
--- a/pom.xml
+++ b/pom.xml
@@ -105,7 +105,7 @@
3.15.6
- 0.21.8.RELEASE
+ 0.22.0.RELEASE
From b9b29170f04ded76458bcdcfb9eab4465be62ddc Mon Sep 17 00:00:00 2001
From: Roman Strobl
Date: Mon, 22 Jan 2024 16:54:26 +0800
Subject: [PATCH 056/146] Fix #1265: FIDO2: Improve error handling for
converters
---
.../Fido2DeserializationException.java | 41 +++++++++++++++++++
.../AttestationObjectDeserializer.java | 8 ++--
.../CollectedClientDataDeserializer.java | 7 ++--
.../controller/RESTControllerAdvice.java | 20 ++++++++-
4 files changed, 68 insertions(+), 8 deletions(-)
create mode 100644 powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/errorhandling/Fido2DeserializationException.java
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/errorhandling/Fido2DeserializationException.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/errorhandling/Fido2DeserializationException.java
new file mode 100644
index 000000000..6a7b045f1
--- /dev/null
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/errorhandling/Fido2DeserializationException.java
@@ -0,0 +1,41 @@
+/*
+ * PowerAuth Server 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 com.wultra.powerauth.fido2.errorhandling;
+
+import java.io.IOException;
+import java.io.Serial;
+
+/**
+ * Exception related to FIDO2 deserialization issues.
+ *
+ * @author Roman Strobl, roman.strobl@wultra.com
+ */
+public class Fido2DeserializationException extends IOException {
+
+ @Serial
+ private static final long serialVersionUID = 1835532378587759773L;
+
+ public Fido2DeserializationException(String message) {
+ super(message);
+ }
+
+ public Fido2DeserializationException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AttestationObjectDeserializer.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AttestationObjectDeserializer.java
index 536c07ce5..c1d276de0 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AttestationObjectDeserializer.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AttestationObjectDeserializer.java
@@ -18,11 +18,11 @@
package com.wultra.powerauth.fido2.rest.model.converter.serialization;
-import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.dataformat.cbor.databind.CBORMapper;
+import com.wultra.powerauth.fido2.errorhandling.Fido2DeserializationException;
import com.wultra.powerauth.fido2.rest.model.entity.AttestationObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@@ -52,7 +52,7 @@ public AttestationObjectDeserializer(Class> vc) {
}
@Override
- public AttestationObject deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
+ public AttestationObject deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws Fido2DeserializationException {
try {
final String originalTextValue = jsonParser.getText();
final byte[] decodedAttestationObject = Base64.getDecoder().decode(originalTextValue);
@@ -60,8 +60,8 @@ public AttestationObject deserialize(JsonParser jsonParser, DeserializationConte
attestationObject.setEncoded(originalTextValue);
return attestationObject;
} catch (IOException e) {
- logger.warn(e.getMessage(), e);
- return null;
+ logger.debug(e.getMessage(), e);
+ throw new Fido2DeserializationException(e.getMessage(), e);
}
}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/CollectedClientDataDeserializer.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/CollectedClientDataDeserializer.java
index c6ddc20d7..38498c8a4 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/CollectedClientDataDeserializer.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/CollectedClientDataDeserializer.java
@@ -23,6 +23,7 @@
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+import com.wultra.powerauth.fido2.errorhandling.Fido2DeserializationException;
import com.wultra.powerauth.fido2.rest.model.entity.CollectedClientData;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@@ -52,7 +53,7 @@ public CollectedClientDataDeserializer(Class> vc) {
}
@Override
- public CollectedClientData deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) {
+ public CollectedClientData deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws Fido2DeserializationException {
try {
final String originalTextValue = jsonParser.getText();
final byte[] decodedClientDataJSON = Base64.getDecoder().decode(originalTextValue);
@@ -60,8 +61,8 @@ public CollectedClientData deserialize(JsonParser jsonParser, DeserializationCon
collectedClientData.setEncoded(new String(decodedClientDataJSON));
return collectedClientData;
} catch (IOException e) {
- logger.warn(e.getMessage(), e);
- return null;
+ logger.debug(e.getMessage(), e);
+ throw new Fido2DeserializationException(e.getMessage(), e);
}
}
}
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/controller/RESTControllerAdvice.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/controller/RESTControllerAdvice.java
index 1c5ca2af0..a9ec39b73 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/controller/RESTControllerAdvice.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/controller/RESTControllerAdvice.java
@@ -18,6 +18,7 @@
package io.getlime.security.powerauth.app.server.controller;
import com.wultra.powerauth.fido2.errorhandling.Fido2AuthenticationFailedException;
+import com.wultra.powerauth.fido2.errorhandling.Fido2DeserializationException;
import com.wultra.security.powerauth.client.model.error.PowerAuthError;
import com.wultra.security.powerauth.client.model.error.PowerAuthErrorRecovery;
import io.getlime.core.rest.model.base.response.ObjectResponse;
@@ -95,7 +96,24 @@ public class RESTControllerAdvice {
logger.error("Error occurred while processing the request: {}", ex.getMessage());
logger.debug("Exception details:", ex);
final PowerAuthError error = new PowerAuthError();
- error.setCode("ERROR_FIDO2");
+ error.setCode("ERROR_FIDO2_AUTH");
+ error.setMessage(ex.getMessage());
+ error.setLocalizedMessage(ex.getLocalizedMessage());
+ return new ObjectResponse<>("ERROR", error);
+ }
+
+ /**
+ * Resolver for FIDO2 related deserialization errors.
+ * @param ex Exception for HTTP message not readable.
+ * @return Error for HTTP request.
+ */
+ @ResponseStatus(HttpStatus.BAD_REQUEST)
+ @ExceptionHandler(value = Fido2DeserializationException.class)
+ public @ResponseBody ObjectResponse handleFido2DeserializationFailedException(Fido2DeserializationException ex) {
+ logger.error("Error occurred while processing the request: {}", ex.getMessage());
+ logger.debug("Exception details:", ex);
+ final PowerAuthError error = new PowerAuthError();
+ error.setCode("ERROR_FIDO2_REQUEST");
error.setMessage(ex.getMessage());
error.setLocalizedMessage(ex.getLocalizedMessage());
return new ObjectResponse<>("ERROR", error);
From 5ef203292cce07a84258fdfb410c527e69dff86a Mon Sep 17 00:00:00 2001
From: Lubos Racansky
Date: Tue, 23 Jan 2024 09:50:24 +0100
Subject: [PATCH 057/146] Fix #1269: Document offline anti-fraud check
---
docs/Offline-Signatures.md | 9 +++++++++
docs/WebServices-Methods.md | 2 +-
...CreatePersonalizedOfflineSignaturePayloadRequest.java | 6 +++---
.../model/request/VerifyOfflineSignatureRequest.java | 4 ++--
4 files changed, 15 insertions(+), 6 deletions(-)
diff --git a/docs/Offline-Signatures.md b/docs/Offline-Signatures.md
index d2445fb49..bd1992b61 100644
--- a/docs/Offline-Signatures.md
+++ b/docs/Offline-Signatures.md
@@ -17,6 +17,15 @@ For Web Flow the format of request `data` is documented in the [Offline Signatur
The `offlineData` in response already contains all data required to display a QR code. The validity of the QR code should be verified by computing the ECDSA signature of `offlineData` content before the computed signature and comparing it with the `ECDSA_SIGNATURE` in `offlineData`. The `nonce` in response will be required during offline signature verification step.
+
+### Proximity anti-fraud check
+
+If you want to use the proximity anti-fraud feature in offline mode, you have to specify `nonce`, `proximityCheck.seed`, and `proximityCheck.stepLength` in `CreatePersonalizedOfflineSignaturePayloadRequest`.
+In that case, `CreatePersonalizedOfflineSignaturePayloadResponse#offlineData` contains `CreatePersonalizedOfflineSignaturePayloadRequest#data` plus a generated TOTP.
+The structure is following `{DATA})\n{TOTP}\n{NONCE}\n{KEY_SERVER_PRIVATE_INDICATOR}{ECDSA_SIGNATURE}`.
+This value is transparent for you and is handled by Mobile SDK.
+
+
## Generating non-personalized offline signature payload
Non-personalized offline signatures are used when activation ID is not known. A typical use case is offline verification for login operation.
diff --git a/docs/WebServices-Methods.md b/docs/WebServices-Methods.md
index 8deced4ec..c6d64ba8a 100644
--- a/docs/WebServices-Methods.md
+++ b/docs/WebServices-Methods.md
@@ -885,7 +885,7 @@ REST endpoint: `POST /rest/v3/signature/offline/personalized/create`
|-----------|-----------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `String` | `activationId` | An identifier of an activation |
| `String` | `data` | Data for the signature, for normalized value see the [Offline Signatures QR code](https://github.com/wultra/powerauth-webflow/blob/develop/docs/Off-line-Signatures-QR-Code.md) documentation |
-| `String` | `nonce` | Optional nonce, otherwise it will be generated by PowerAuth server. Needed to be set when proximity check is enabled. |
+| `String` | `nonce` | Optional nonce (16 bytes base64 encoded into 24 characters), otherwise it will be generated by PowerAuth server. Needed to be set when proximity check is enabled. |
| `Object` | `proximityCheck` | Optional parameters for proximity TOTP. |
| `String` | `proximityCheck.seed` | Seed for TOTP, base64 encoded. |
| `Integer` | `proximityCheck.stepLength` | Length of the TOTP step in seconds. |
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/CreatePersonalizedOfflineSignaturePayloadRequest.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/CreatePersonalizedOfflineSignaturePayloadRequest.java
index c47c8d97d..aa3061fd8 100644
--- a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/CreatePersonalizedOfflineSignaturePayloadRequest.java
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/CreatePersonalizedOfflineSignaturePayloadRequest.java
@@ -35,14 +35,14 @@ public class CreatePersonalizedOfflineSignaturePayloadRequest {
private String activationId;
private String data;
- @Schema(description = "Optional nonce, otherwise it will be generated by PowerAuth server. Needed to be set when proximity check is enabled.")
+ @Schema(description = "Optional nonce (16 bytes base64 encoded into 24 characters), otherwise it will be generated by PowerAuth server. Needed to be set when proximity check is enabled.", maxLength = 24)
private String nonce;
@Schema(description = "Optional proximity check configuration of TOTP.")
- private ProximityCheck proximityCheck;
+ private CreateProximityCheck proximityCheck;
@Data
- public static class ProximityCheck {
+ public static class CreateProximityCheck {
@NotNull
@ToString.Exclude
@Schema(description = "Seed for TOTP, base64 encoded.")
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/VerifyOfflineSignatureRequest.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/VerifyOfflineSignatureRequest.java
index f77064950..3deacc7f0 100644
--- a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/VerifyOfflineSignatureRequest.java
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/VerifyOfflineSignatureRequest.java
@@ -42,10 +42,10 @@ public class VerifyOfflineSignatureRequest {
private boolean allowBiometry;
@Schema(description = "Optional proximity check configuration of TOTP.")
- private ProximityCheck proximityCheck;
+ private VerifyProximityCheck proximityCheck;
@Data
- public static class ProximityCheck {
+ public static class VerifyProximityCheck {
@NotNull
@ToString.Exclude
@Schema(description = "Seed for TOTP, base64 encoded.")
From 3708c1520ee2f5a44804e67d0c1307fc4ac6ddfe Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Lubo=C5=A1=20Ra=C4=8Dansk=C3=BD?=
Date: Wed, 24 Jan 2024 13:10:58 +0100
Subject: [PATCH 058/146] Fix #1171: Document requirements on OS entropy for
deployment of PowerAuth server (#1271)
* Fix #1171: Document requirements on OS entropy for the deployment of PowerAuth server
---
docs/System-Requirements.md | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/docs/System-Requirements.md b/docs/System-Requirements.md
index d3cbc82a2..58f91a87e 100644
--- a/docs/System-Requirements.md
+++ b/docs/System-Requirements.md
@@ -60,3 +60,22 @@ You need following software versions:
Deployment is described in a separate documentation:
- [Docker Images for PowerAuth](https://github.com/wultra/powerauth-docker)
+
+
+## Entropy
+
+The PowerAuth stack requires significant amount of entropy because of random number generators (RNG) used for cryptography.
+When not enough entropy is available, the whole system may dramatically slow down or even get stuck.
+That may happen especially in virtualized environment.
+
+For Linux Kernel lower than 5.4, the minimal required entropy is 256, ideally more than 1024.
+For Linux Kernel 5.4 and higher: the minimal required entropy is 256 (it does not report more anyway).
+
+Command to get available entropy bits:
+
+```shell
+cat /proc/sys/kernel/random/entropy_avail
+```
+
+We recommend using Linux Kernel 5.4 and newer, where `/dev/random` does not block anymore.
+If you must run an older version, consider another source of entropy such as [haveged](https://github.com/jirka-h/haveged).
From 6f0c926ec5abfea970fd49ab0c727c1f481f4ccc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Roman=20=C5=A0trobl?=
Date: Thu, 25 Jan 2024 16:54:18 +0700
Subject: [PATCH 059/146] Fix #1262: FIDO2: Write JavaDoc for classes and
methods (#1274)
---
.../model/error/PowerAuthClientException.java | 2 +-
.../model/error/PowerAuthErrorRecovery.java | 2 +-
.../Fido2AuthenticationFailedException.java | 9 +
.../Fido2DeserializationException.java | 9 +
.../rest/controller/AssertionController.java | 19 +-
.../controller/RegistrationController.java | 24 ++-
.../model/converter/AssertionConverter.java | 6 +
.../converter/RegistrationConverter.java | 40 ++--
.../AttestationObjectDeserializer.java | 16 ++
.../AttestationStatementDeserializer.java | 42 +++-
.../AuthenticatorDataDeserializer.java | 194 ++++++++++--------
.../Base64ToByteArrayDeserializer.java | 30 ++-
.../Base64ToStringDeserializer.java | 26 ++-
.../CollectedClientDataDeserializer.java | 16 ++
.../rest/model/entity/AssertionChallenge.java | 2 +
.../rest/model/entity/AttestationObject.java | 2 +
.../model/entity/AttestationStatement.java | 2 +
.../model/entity/AttestedCredentialData.java | 2 +
.../AuthenticatorAssertionResponse.java | 2 +
.../AuthenticatorAttestationResponse.java | 2 +
.../rest/model/entity/AuthenticatorData.java | 2 +
.../model/entity/CollectedClientData.java | 2 +
.../fido2/rest/model/entity/ECPoint.java | 2 +
.../fido2/rest/model/entity/Flags.java | 2 +
.../rest/model/entity/PublicKeyObject.java | 2 +
.../rest/model/enumeration/CurveType.java | 2 +
.../rest/model/enumeration/ECKeyType.java | 2 +
.../fido2/rest/model/enumeration/Fmt.java | 2 +
.../model/enumeration/SignatureAlgorithm.java | 2 +
.../request/AssertionVerificationRequest.java | 2 +
.../model/request/RegistrationRequest.java | 2 +
.../response/AssertionChallengeResponse.java | 2 +
.../RegisteredAuthenticatorsResponse.java | 2 +
.../RegistrationChallengeResponse.java | 2 +
.../model/response/RegistrationResponse.java | 2 +
.../validator/AssertionRequestValidator.java | 5 +
.../RegistrationRequestValidator.java | 5 +
.../fido2/service/AssertionService.java | 8 +
.../fido2/service/RegistrationService.java | 35 +++-
.../provider/AuthenticatorProvider.java | 28 +++
.../service/provider/CryptographyService.java | 31 +++
.../converter/SignatureMetadataConverter.java | 2 +-
.../model/entity/ActivationRecordEntity.java | 2 +-
.../model/entity/ApplicationEntity.java | 2 +-
.../model/entity/MasterKeyPairEntity.java | 2 +-
.../model/entity/RecoveryCodeEntity.java | 2 +-
.../model/entity/RecoveryConfigEntity.java | 2 +-
.../model/entity/RecoveryPukEntity.java | 2 +-
.../model/entity/SignatureEntity.java | 2 +-
.../model/enumeration/EncryptionMode.java | 2 +-
.../service/model/ActivationRecovery.java | 2 +-
.../app/server/service/model/TokenInfo.java | 2 +-
.../request/ActivationLayer2Request.java | 2 +-
.../ConfirmRecoveryRequestPayload.java | 2 +-
.../request/VaultUnlockRequestPayload.java | 2 +-
.../response/ActivationLayer2Response.java | 2 +-
.../ConfirmRecoveryResponsePayload.java | 2 +-
.../response/UpgradeResponsePayload.java | 2 +-
.../response/VaultUnlockResponsePayload.java | 2 +-
.../signature/OfflineSignatureRequest.java | 2 +-
.../signature/OnlineSignatureRequest.java | 2 +-
.../model/signature/SignatureData.java | 2 +-
.../model/signature/SignatureResponse.java | 2 +-
63 files changed, 492 insertions(+), 141 deletions(-)
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/error/PowerAuthClientException.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/error/PowerAuthClientException.java
index 8655aa443..d8b479285 100644
--- a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/error/PowerAuthClientException.java
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/error/PowerAuthClientException.java
@@ -33,7 +33,7 @@ public class PowerAuthClientException extends Exception {
private final PowerAuthError powerAuthError;
/**
- * Default constructor.
+ * No-arg constructor.
*/
public PowerAuthClientException() {
this.powerAuthError = null;
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/error/PowerAuthErrorRecovery.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/error/PowerAuthErrorRecovery.java
index 4599f1039..3d9b7aff9 100644
--- a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/error/PowerAuthErrorRecovery.java
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/error/PowerAuthErrorRecovery.java
@@ -27,7 +27,7 @@ public class PowerAuthErrorRecovery extends PowerAuthError {
private int currentRecoveryPukIndex;
/**
- * Default constructor.
+ * No-arg constructor.
*/
public PowerAuthErrorRecovery() {
}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/errorhandling/Fido2AuthenticationFailedException.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/errorhandling/Fido2AuthenticationFailedException.java
index d3a59a633..13fc82e3d 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/errorhandling/Fido2AuthenticationFailedException.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/errorhandling/Fido2AuthenticationFailedException.java
@@ -30,10 +30,19 @@ public class Fido2AuthenticationFailedException extends Exception {
@Serial
private static final long serialVersionUID = -3214199555928548491L;
+ /**
+ * Exception constructor with message.
+ * @param message Exception message.
+ */
public Fido2AuthenticationFailedException(String message) {
super(message);
}
+ /**
+ * Exception constructor with message and cause.
+ * @param message Exception message.
+ * @param cause Exception cause.
+ */
public Fido2AuthenticationFailedException(String message, Throwable cause) {
super(message, cause);
}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/errorhandling/Fido2DeserializationException.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/errorhandling/Fido2DeserializationException.java
index 6a7b045f1..812984dce 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/errorhandling/Fido2DeserializationException.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/errorhandling/Fido2DeserializationException.java
@@ -31,10 +31,19 @@ public class Fido2DeserializationException extends IOException {
@Serial
private static final long serialVersionUID = 1835532378587759773L;
+ /**
+ * Exception constructor with message.
+ * @param message Exception message.
+ */
public Fido2DeserializationException(String message) {
super(message);
}
+ /**
+ * Exception constructor with message and cause.
+ * @param message Exception message.
+ * @param cause Exception cause.
+ */
public Fido2DeserializationException(String message, Throwable cause) {
super(message, cause);
}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/AssertionController.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/AssertionController.java
index bfc67ad0b..1055883ae 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/AssertionController.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/AssertionController.java
@@ -54,12 +54,23 @@ public class AssertionController {
private final AssertionRequestValidator assertionRequestValidator;
private final AssertionService assertionService;
+ /**
+ * Assertion controller constructor.
+ * @param assertionRequestValidator Assertion request validator.
+ * @param assertionService Assertion service.
+ */
@Autowired
public AssertionController(AssertionRequestValidator assertionRequestValidator, AssertionService assertionService) {
this.assertionRequestValidator = assertionRequestValidator;
this.assertionService = assertionService;
}
+ /**
+ * Request generating of an assertion challenge for an operation.
+ * @param request Assertion challenge request.
+ * @return Assertion challenge response.
+ * @throws Exception Thrown in case assertion challenge could not be generated.
+ */
@Operation(
summary = "Generate an assertion challenge",
description = "Generate a FIDO2 assertion challenge for an operation."
@@ -76,13 +87,19 @@ public ObjectResponse requestAssertionChallenge(@Val
return new ObjectResponse<>(assertionChallengeResponse);
}
+ /**
+ * Verify a FIDO2 assertion for an operation.
+ * @param request Verify an assertion request.
+ * @return Verify an assertion response.
+ * @throws Fido2AuthenticationFailedException Thrown in case assertion validation fails.
+ */
@Operation(
summary = "Verify an assertion",
description = "Verify a FIDO2 assertion for an operation based on an assertion verification request generated and signed by the authenticator."
)
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Assertion verification succeeded"),
- @ApiResponse(responseCode = "400", description = "Invalid request or assertion verification failed"),
+ @ApiResponse(responseCode = "400", description = "Invalid request or assertion verification failed"),
@ApiResponse(responseCode = "500", description = "Unexpected server error")
})
@PostMapping
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/RegistrationController.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/RegistrationController.java
index 0f562dd58..02e54b9e4 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/RegistrationController.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/RegistrationController.java
@@ -54,11 +54,21 @@ public class RegistrationController {
private final RegistrationService registrationService;
+ /**
+ * Registration controller constructor.
+ * @param registrationService Registration service.
+ */
@Autowired
public RegistrationController(RegistrationService registrationService) {
this.registrationService = registrationService;
}
+ /**
+ * Obtain a list of registered FIDO2 authenticators.
+ * @param request Registered authenticators list request.
+ * @return Registered authenticators list response.
+ * @throws Exception Thrown in case registered authenticators list could not be obtained.
+ */
@Operation(
summary = "List registered authenticators",
description = "Obtain a list of registered FIDO2 authenticators for specified user."
@@ -71,10 +81,16 @@ public RegistrationController(RegistrationService registrationService) {
@PostMapping("list")
public ObjectResponse registeredAuthenticators(@Valid @RequestBody ObjectRequest request) throws Exception {
final RegisteredAuthenticatorsRequest requestObject = request.getRequestObject();
- final RegisteredAuthenticatorsResponse responseObject = registrationService.registrationsForUser(requestObject.getUserId(), requestObject.getApplicationId());
+ final RegisteredAuthenticatorsResponse responseObject = registrationService.listRegistrationsForUser(requestObject.getUserId(), requestObject.getApplicationId());
return new ObjectResponse<>(responseObject);
}
+ /**
+ * Request a registration challenge.
+ * @param request Registration challenge request.
+ * @return Registration challenge response.
+ * @throws Exception Thrown in case registration challenge could not be generated.
+ */
@Operation(
summary = "Generate a registration challenge",
description = "Generate a FIDO2 registration challenge for specified user."
@@ -91,6 +107,12 @@ public ObjectResponse requestRegistrationChalleng
return new ObjectResponse<>(responseObject);
}
+ /**
+ * Register an authenticator.
+ * @param request Register an authenticator request.
+ * @return Register an authenticator response.
+ * @throws Exception Thrown in case registration fails.
+ */
@Operation(
summary = "Register an authenticator",
description = "Register a FIDO2 authenticator based on a registration request generated and signed by the authenticator."
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/AssertionConverter.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/AssertionConverter.java
index 27d54689c..0edb52027 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/AssertionConverter.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/AssertionConverter.java
@@ -32,6 +32,12 @@
@Slf4j
public class AssertionConverter {
+ /**
+ * Convert authenticator detail to assertion verification response.
+ * @param source Authenticator detail.
+ * @param assertionValid Whether assertion is valid.
+ * @return Converted assertion verification response.
+ */
public AssertionVerificationResponse fromAuthenticatorDetail(AuthenticatorDetail source, boolean assertionValid) {
if (source == null) {
return null;
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/RegistrationConverter.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/RegistrationConverter.java
index e313bbfd1..8407f2484 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/RegistrationConverter.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/RegistrationConverter.java
@@ -45,6 +45,14 @@ public class RegistrationConverter {
private final AaguidList aaguidRegistry = new AaguidList();
+ /**
+ * Convert registration challenge to authenticator detail.
+ * @param challenge Registration challenge.
+ * @param requestObject Registration request.
+ * @param aaguid AAGUID bytes.
+ * @param publicKey Public key bytes.
+ * @return Authenticator detail, if present.
+ */
public Optional convert(RegistrationChallenge challenge, RegistrationRequest requestObject, byte[] aaguid, byte[] publicKey) {
try {
final AuthenticatorDetail authenticatorDetail = new AuthenticatorDetail();
@@ -70,19 +78,11 @@ public Optional convert(RegistrationChallenge challenge, Re
}
}
- private Map convertExtras(RegistrationRequest requestObject) throws JsonProcessingException {
- final AuthenticatorParameters authenticatorParameters = requestObject.getAuthenticatorParameters();
- final Map params = new HashMap<>();
- params.put("relyingPartyId", authenticatorParameters.getRelyingPartyId());
- params.put("authenticatorAttachment", authenticatorParameters.getAuthenticatorAttachment());
- params.put("credentialId", authenticatorParameters.getResponse().getAttestationObject().getAuthData().getAttestedCredentialData().getCredentialId());
- params.put("origin", authenticatorParameters.getResponse().getClientDataJSON().getOrigin());
- params.put("topOrigin", authenticatorParameters.getResponse().getClientDataJSON().getTopOrigin());
- params.put("isCrossOrigin", authenticatorParameters.getResponse().getClientDataJSON().isCrossOrigin());
- params.put("aaguid", authenticatorParameters.getResponse().getAttestationObject().getAuthData().getAttestedCredentialData().getAaguid());
- return params;
- }
-
+ /**
+ * Convert authenticator detail to registration response.
+ * @param source Authenticator detail.
+ * @return Registration response.
+ */
public RegistrationResponse convertRegistrationResponse(AuthenticatorDetail source) {
final RegistrationResponse result = new RegistrationResponse();
result.setUserId(source.getUserId());
@@ -101,4 +101,18 @@ public RegistrationResponse convertRegistrationResponse(AuthenticatorDetail sour
result.setMaxFailedAttempts(source.getMaxFailedAttempts());
return result;
}
+
+ private Map convertExtras(RegistrationRequest requestObject) throws JsonProcessingException {
+ final AuthenticatorParameters authenticatorParameters = requestObject.getAuthenticatorParameters();
+ final Map params = new HashMap<>();
+ params.put("relyingPartyId", authenticatorParameters.getRelyingPartyId());
+ params.put("authenticatorAttachment", authenticatorParameters.getAuthenticatorAttachment());
+ params.put("credentialId", authenticatorParameters.getResponse().getAttestationObject().getAuthData().getAttestedCredentialData().getCredentialId());
+ params.put("origin", authenticatorParameters.getResponse().getClientDataJSON().getOrigin());
+ params.put("topOrigin", authenticatorParameters.getResponse().getClientDataJSON().getTopOrigin());
+ params.put("isCrossOrigin", authenticatorParameters.getResponse().getClientDataJSON().isCrossOrigin());
+ params.put("aaguid", authenticatorParameters.getResponse().getAttestationObject().getAuthData().getAttestedCredentialData().getAaguid());
+ return params;
+ }
+
}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AttestationObjectDeserializer.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AttestationObjectDeserializer.java
index c1d276de0..7a4c326d0 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AttestationObjectDeserializer.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AttestationObjectDeserializer.java
@@ -32,6 +32,8 @@
import java.util.Base64;
/**
+ * JSON deserializer for the attestation object.
+ *
* @author Petr Dvorak, petr@wultra.com
*/
@Component
@@ -43,14 +45,28 @@ public class AttestationObjectDeserializer extends StdDeserializer vc) {
super(vc);
}
+ /**
+ * Deserialize the FIDO2 attestation object from JSON request.
+ * @param jsonParser JSON parser.
+ * @param deserializationContext Deserialization context.
+ * @return Deserialized FIDO2 attestation object.
+ * @throws Fido2DeserializationException Thrown in case JSON deserialization fails.
+ */
@Override
public AttestationObject deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws Fido2DeserializationException {
try {
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AttestationStatementDeserializer.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AttestationStatementDeserializer.java
index 2308f49f7..d2a0b2c4a 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AttestationStatementDeserializer.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AttestationStatementDeserializer.java
@@ -22,6 +22,7 @@
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+import com.wultra.powerauth.fido2.errorhandling.Fido2DeserializationException;
import com.wultra.powerauth.fido2.rest.model.entity.AttestationStatement;
import com.wultra.powerauth.fido2.rest.model.enumeration.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
@@ -32,6 +33,8 @@
import java.util.Map;
/**
+ * JSON deserializer for the attestation statement.
+ *
* @author Petr Dvorak, petr@wultra.com
*/
@Component
@@ -41,25 +44,44 @@ public class AttestationStatementDeserializer extends StdDeserializer vc) {
super(vc);
}
+ /**
+ * Deserialize the FIDO2 attestation object from JSON request.
+ * @param jsonParser JSON parser.
+ * @param deserializationContext Deserialization context.
+ * @return Deserialized FIDO2 attestation statement.
+ * @throws Fido2DeserializationException Thrown in case JSON deserialization fails.
+ */
@Override
- public AttestationStatement deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
- final Map map = jsonParser.readValueAs(new TypeReference<>() {});
- final AttestationStatement result = new AttestationStatement();
- final Integer alg = (Integer) map.get("alg");
- if (alg != null && -7 == alg) {
- result.setAlgorithm(SignatureAlgorithm.ES256);
- } else {
- result.setAlgorithm(SignatureAlgorithm.UNKNOWN);
+ public AttestationStatement deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws Fido2DeserializationException {
+ try {
+ final Map map = jsonParser.readValueAs(new TypeReference<>() {});
+ final AttestationStatement result = new AttestationStatement();
+ final Integer alg = (Integer) map.get("alg");
+ if (alg != null && -7 == alg) {
+ result.setAlgorithm(SignatureAlgorithm.ES256);
+ } else {
+ result.setAlgorithm(SignatureAlgorithm.UNKNOWN);
+ }
+ result.setSignature((byte[]) map.get("sig"));
+ return result;
+ } catch (IOException e) {
+ logger.debug(e.getMessage(), e);
+ throw new Fido2DeserializationException(e.getMessage(), e);
}
- result.setSignature((byte[]) map.get("sig"));
- return result;
}
}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AuthenticatorDataDeserializer.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AuthenticatorDataDeserializer.java
index f942c7398..a14a52455 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AuthenticatorDataDeserializer.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AuthenticatorDataDeserializer.java
@@ -18,12 +18,12 @@
package com.wultra.powerauth.fido2.rest.model.converter.serialization;
-import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.dataformat.cbor.databind.CBORMapper;
+import com.wultra.powerauth.fido2.errorhandling.Fido2DeserializationException;
import com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorData;
import com.wultra.powerauth.fido2.rest.model.entity.ECPoint;
import com.wultra.powerauth.fido2.rest.model.entity.Flags;
@@ -40,6 +40,8 @@
import java.util.Map;
/**
+ * JSON deserializer for FIDO2 authenticator data.
+ *
* @author Petr Dvorak, petr@wultra.com
*/
@Component
@@ -51,101 +53,119 @@ public class AuthenticatorDataDeserializer extends StdDeserializer vc) {
+
+ /**
+ * Deserializer constructor with value class parameter.
+ * @param vc Value class parameter.
+ */
+ public AuthenticatorDataDeserializer(Class> vc) {
super(vc);
}
+ /**
+ * Deserialized the FIDO2 authenticator data.
+ * @param jsonParser JSON parser.
+ * @param deserializationContext Deserialization context.
+ * @return Deserialized FIDO2 authenticator data.
+ * @throws Fido2DeserializationException Thrown in case JSON deserialization fails.
+ */
@Override
- public AuthenticatorData deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
- final AuthenticatorData result = new AuthenticatorData();
-
- // Serialize Auth Data
- final byte[] authData = jsonParser.getBinaryValue();
- result.setEncoded(authData);
-
- // Get RP ID Hash
- final byte[] rpIdHash = new byte[32];
- System.arraycopy(authData, 0, rpIdHash,0, 32);
- result.setRpIdHash(rpIdHash);
-
- // Get Flags
- final byte flagByte = authData[32];
- final Flags flags = result.getFlags();
-
- flags.setUserPresent(isFlagOn(flagByte, 0));
- flags.setReservedBit2(isFlagOn(flagByte, 1));
- flags.setUserVerified(isFlagOn(flagByte, 2));
- flags.setBackupEligible(isFlagOn(flagByte, 3));
- flags.setBackupState(isFlagOn(flagByte, 4));
- flags.setReservedBit6(isFlagOn(flagByte, 5));
- flags.setAttestedCredentialsIncluded(isFlagOn(flagByte,6));
- flags.setExtensionDataIncluded(isFlagOn(flagByte,7));
-
- // Get Signature Counter
- final byte[] signCountBytes = new byte[4];
- System.arraycopy(authData, 33, signCountBytes, 0, 4);
- final int signCount = ByteBuffer.wrap(signCountBytes).getInt(); // big-endian by default
- result.setSignCount(signCount);
-
- if (authData.length > 37) { // get info about the credentials
-
- // Get AAGUID
- final byte[] aaguid = new byte[16];
- System.arraycopy(authData, 37, aaguid, 0, 16);
- result.getAttestedCredentialData().setAaguid(aaguid);
-
- // Get credential ID length
- final byte[] credentialIdLength = new byte[2];
- System.arraycopy(authData, 53, credentialIdLength, 0, 2);
- final ByteBuffer wrapped = ByteBuffer.wrap(credentialIdLength); // big-endian by default
- short credentialIdLengthValue = wrapped.getShort();
-
- // Get credentialId
- final byte[] credentialId = new byte[credentialIdLengthValue];
- System.arraycopy(authData, 55, credentialId, 0, credentialIdLengthValue);
- result.getAttestedCredentialData().setCredentialId(credentialId);
-
- // Get credentialPublicKey
- final int remainingLength = authData.length - (55 + credentialIdLengthValue);
- final byte[] credentialPublicKey = new byte[remainingLength];
- System.arraycopy(authData, 55 + credentialIdLengthValue, credentialPublicKey, 0, remainingLength);
- final Map credentialPublicKeyMap = cborMapper.readValue(credentialPublicKey, new TypeReference<>() {
- });
-
- final PublicKeyObject publicKeyObject = new PublicKeyObject();
- final Integer algorithm = (Integer) credentialPublicKeyMap.get("3");
- if (algorithm != null && -7 == algorithm) {
- publicKeyObject.setAlgorithm(SignatureAlgorithm.ES256);
- } else {
- throw new RuntimeException("Unsupported algorithm: " + algorithm);
- }
- final Integer curveType = (Integer) credentialPublicKeyMap.get("-1");
- if (curveType != null && 1 == curveType) {
- publicKeyObject.setCurveType(CurveType.P256);
- } else {
- throw new RuntimeException("Unsupported curve type: " + curveType);
- }
- final Integer keyType = (Integer) credentialPublicKeyMap.get("1");
- if (keyType != null && 2 == keyType) {
- publicKeyObject.setKeyType(ECKeyType.UNCOMPRESSED);
- } else {
- throw new RuntimeException("Unsupported key type: " + keyType);
+ public AuthenticatorData deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws Fido2DeserializationException {
+ try {
+ final AuthenticatorData result = new AuthenticatorData();
+
+ // Serialize Auth Data
+ final byte[] authData = jsonParser.getBinaryValue();
+ result.setEncoded(authData);
+
+ // Get RP ID Hash
+ final byte[] rpIdHash = new byte[32];
+ System.arraycopy(authData, 0, rpIdHash, 0, 32);
+ result.setRpIdHash(rpIdHash);
+
+ // Get Flags
+ final byte flagByte = authData[32];
+ final Flags flags = result.getFlags();
+
+ flags.setUserPresent(isFlagOn(flagByte, 0));
+ flags.setReservedBit2(isFlagOn(flagByte, 1));
+ flags.setUserVerified(isFlagOn(flagByte, 2));
+ flags.setBackupEligible(isFlagOn(flagByte, 3));
+ flags.setBackupState(isFlagOn(flagByte, 4));
+ flags.setReservedBit6(isFlagOn(flagByte, 5));
+ flags.setAttestedCredentialsIncluded(isFlagOn(flagByte, 6));
+ flags.setExtensionDataIncluded(isFlagOn(flagByte, 7));
+
+ // Get Signature Counter
+ final byte[] signCountBytes = new byte[4];
+ System.arraycopy(authData, 33, signCountBytes, 0, 4);
+ final int signCount = ByteBuffer.wrap(signCountBytes).getInt(); // big-endian by default
+ result.setSignCount(signCount);
+
+ if (authData.length > 37) { // get info about the credentials
+
+ // Get AAGUID
+ final byte[] aaguid = new byte[16];
+ System.arraycopy(authData, 37, aaguid, 0, 16);
+ result.getAttestedCredentialData().setAaguid(aaguid);
+
+ // Get credential ID length
+ final byte[] credentialIdLength = new byte[2];
+ System.arraycopy(authData, 53, credentialIdLength, 0, 2);
+ final ByteBuffer wrapped = ByteBuffer.wrap(credentialIdLength); // big-endian by default
+ short credentialIdLengthValue = wrapped.getShort();
+
+ // Get credentialId
+ final byte[] credentialId = new byte[credentialIdLengthValue];
+ System.arraycopy(authData, 55, credentialId, 0, credentialIdLengthValue);
+ result.getAttestedCredentialData().setCredentialId(credentialId);
+
+ // Get credentialPublicKey
+ final int remainingLength = authData.length - (55 + credentialIdLengthValue);
+ final byte[] credentialPublicKey = new byte[remainingLength];
+ System.arraycopy(authData, 55 + credentialIdLengthValue, credentialPublicKey, 0, remainingLength);
+ final Map credentialPublicKeyMap = cborMapper.readValue(credentialPublicKey, new TypeReference<>() {});
+
+ final PublicKeyObject publicKeyObject = new PublicKeyObject();
+ final Integer algorithm = (Integer) credentialPublicKeyMap.get("3");
+ if (algorithm != null && -7 == algorithm) {
+ publicKeyObject.setAlgorithm(SignatureAlgorithm.ES256);
+ } else {
+ throw new RuntimeException("Unsupported algorithm: " + algorithm);
+ }
+ final Integer curveType = (Integer) credentialPublicKeyMap.get("-1");
+ if (curveType != null && 1 == curveType) {
+ publicKeyObject.setCurveType(CurveType.P256);
+ } else {
+ throw new RuntimeException("Unsupported curve type: " + curveType);
+ }
+ final Integer keyType = (Integer) credentialPublicKeyMap.get("1");
+ if (keyType != null && 2 == keyType) {
+ publicKeyObject.setKeyType(ECKeyType.UNCOMPRESSED);
+ } else {
+ throw new RuntimeException("Unsupported key type: " + keyType);
+ }
+
+ final byte[] xBytes = (byte[]) credentialPublicKeyMap.get("-2");
+ final byte[] yBytes = (byte[]) credentialPublicKeyMap.get("-3");
+ final ECPoint point = new ECPoint();
+ point.setX(xBytes);
+ point.setY(yBytes);
+ publicKeyObject.setPoint(point);
+
+ result.getAttestedCredentialData().setPublicKeyObject(publicKeyObject);
}
-
- final byte[] xBytes = (byte[]) credentialPublicKeyMap.get("-2");
- final byte[] yBytes = (byte[]) credentialPublicKeyMap.get("-3");
- final ECPoint point = new ECPoint();
- point.setX(xBytes);
- point.setY(yBytes);
- publicKeyObject.setPoint(point);
-
- result.getAttestedCredentialData().setPublicKeyObject(publicKeyObject);
+ return result;
+ } catch (IOException e) {
+ logger.debug(e.getMessage(), e);
+ throw new Fido2DeserializationException(e.getMessage(), e);
}
-
- return result;
}
private boolean isFlagOn(byte flags, int position) throws IOException {
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/Base64ToByteArrayDeserializer.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/Base64ToByteArrayDeserializer.java
index fe0801665..25c3026d7 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/Base64ToByteArrayDeserializer.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/Base64ToByteArrayDeserializer.java
@@ -21,29 +21,55 @@
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+import com.wultra.powerauth.fido2.errorhandling.Fido2DeserializationException;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
import java.io.IOException;
import java.io.Serial;
import java.util.Base64;
/**
+ * Deserializer from Base64 to byte array.
+ *
* @author Petr Dvorak, petr@wultra.com
*/
+@Component
+@Slf4j
public class Base64ToByteArrayDeserializer extends StdDeserializer {
@Serial
private static final long serialVersionUID = 4519714786533202920L;
+ /**
+ * No-arg deserializer constructor.
+ */
public Base64ToByteArrayDeserializer() {
this(null);
}
+ /**
+ * Deserializer constructor with value class parameter.
+ * @param vc Value class.
+ */
public Base64ToByteArrayDeserializer(Class> vc) {
super(vc);
}
+ /**
+ * Deserialize data from Base64 to byte array.
+ * @param jsonParser JSON parser.
+ * @param deserializationContext Deserialization context.
+ * @return Deserialized byte array.
+ * @throws Fido2DeserializationException Thrown in case JSON deserialization fails.
+ */
@Override
- public byte[] deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
- return Base64.getDecoder().decode(jsonParser.getText());
+ public byte[] deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws Fido2DeserializationException {
+ try {
+ return Base64.getDecoder().decode(jsonParser.getText());
+ } catch (IOException e) {
+ logger.debug(e.getMessage(), e);
+ throw new Fido2DeserializationException(e.getMessage(), e);
+ }
}
}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/Base64ToStringDeserializer.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/Base64ToStringDeserializer.java
index 31d61449f..2daff0d15 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/Base64ToStringDeserializer.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/Base64ToStringDeserializer.java
@@ -21,6 +21,7 @@
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+import com.wultra.powerauth.fido2.errorhandling.Fido2DeserializationException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@@ -30,6 +31,8 @@
import java.util.Base64;
/**
+ * Deserializer from Base64 to string.
+ *
* @author Petr Dvorak, petr@wultra.com
*/
@Component
@@ -39,16 +42,35 @@ public class Base64ToStringDeserializer extends StdDeserializer {
@Serial
private static final long serialVersionUID = 2540966716709142276L;
+ /**
+ * No-arg deserializer constructor.
+ */
public Base64ToStringDeserializer() {
this(null);
}
+ /**
+ * Deserializer constructor with value class parameter.
+ * @param vc Value class.
+ */
public Base64ToStringDeserializer(Class> vc) {
super(vc);
}
+ /**
+ * Deserialize data from Base64 to string.
+ * @param jsonParser JSON parser.
+ * @param deserializationContext Deserialization context.
+ * @return Deserialized string.
+ * @throws Fido2DeserializationException Thrown in case JSON deserialization fails.
+ */
@Override
- public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
- return new String(Base64.getDecoder().decode(jsonParser.getText()), StandardCharsets.UTF_8);
+ public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws Fido2DeserializationException {
+ try {
+ return new String(Base64.getDecoder().decode(jsonParser.getText()), StandardCharsets.UTF_8);
+ } catch (IOException e) {
+ logger.debug(e.getMessage(), e);
+ throw new Fido2DeserializationException(e.getMessage(), e);
+ }
}
}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/CollectedClientDataDeserializer.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/CollectedClientDataDeserializer.java
index 38498c8a4..ddb5615fa 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/CollectedClientDataDeserializer.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/CollectedClientDataDeserializer.java
@@ -33,6 +33,8 @@
import java.util.Base64;
/**
+ * Deserializer for FIDO2 collected client data.
+ *
* @author Petr Dvorak, petr@wultra.com
*/
@Component
@@ -44,14 +46,28 @@ public class CollectedClientDataDeserializer extends StdDeserializer vc) {
super(vc);
}
+ /**
+ * Deserialized the FIDO2 collected client data.
+ * @param jsonParser JSON parser.
+ * @param deserializationContext Deserialization context.
+ * @return Deserialized FIDO2 collected client data.
+ * @throws Fido2DeserializationException Thrown in case JSON deserialization fails.
+ */
@Override
public CollectedClientData deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws Fido2DeserializationException {
try {
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AssertionChallenge.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AssertionChallenge.java
index 7d54eed23..e5867df3c 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AssertionChallenge.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AssertionChallenge.java
@@ -23,6 +23,8 @@
import java.util.List;
/**
+ * Assertion challenge.
+ *
* @author Petr Dvorak, petr@wultra.com
*/
@Data
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AttestationObject.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AttestationObject.java
index dafa7c33c..5848374d8 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AttestationObject.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AttestationObject.java
@@ -25,6 +25,8 @@
import lombok.Data;
/**
+ * Attestation object.
+ *
* @author Petr Dvorak, petr@wultra.com
*/
@Data
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AttestationStatement.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AttestationStatement.java
index 8c59404fe..299da3af4 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AttestationStatement.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AttestationStatement.java
@@ -22,6 +22,8 @@
import lombok.Data;
/**
+ * Attestation statement.
+ *
* @author Petr Dvorak, petr@wultra.com
*/
@Data
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AttestedCredentialData.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AttestedCredentialData.java
index f08265ec2..2e3a473df 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AttestedCredentialData.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AttestedCredentialData.java
@@ -21,6 +21,8 @@
import lombok.Data;
/**
+ * Attested credential data.
+ *
* @author Petr Dvorak, petr@wultra.com
*/
@Data
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AuthenticatorAssertionResponse.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AuthenticatorAssertionResponse.java
index 54d962491..3ed46bdc6 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AuthenticatorAssertionResponse.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AuthenticatorAssertionResponse.java
@@ -27,6 +27,8 @@
import lombok.Data;
/**
+ * Authenticator assertion response.
+ *
* @author Petr Dvorak, petr@wultra.com
*/
@Data
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AuthenticatorAttestationResponse.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AuthenticatorAttestationResponse.java
index f78eb68be..cd86705e7 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AuthenticatorAttestationResponse.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AuthenticatorAttestationResponse.java
@@ -26,6 +26,8 @@
import java.util.List;
/**
+ * Authenticator attestation response.
+ *
* @author Petr Dvorak, petr@wultra.com
*/
@Data
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AuthenticatorData.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AuthenticatorData.java
index 7d976f852..d280f191d 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AuthenticatorData.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AuthenticatorData.java
@@ -24,6 +24,8 @@
import lombok.Data;
/**
+ * Authenticator data.
+ *
* @author Petr Dvorak, petr@wultra.com
*/
@Data
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/CollectedClientData.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/CollectedClientData.java
index 5985b996b..cba05f8ee 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/CollectedClientData.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/CollectedClientData.java
@@ -26,6 +26,8 @@
import lombok.Data;
/**
+ * Collected client data.
+ *
* @author Petr Dvorak, petr@wultra.com
*/
@Data
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/ECPoint.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/ECPoint.java
index fdae77c50..8d8b5d350 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/ECPoint.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/ECPoint.java
@@ -21,6 +21,8 @@
import lombok.Data;
/**
+ * Elliptic curve point coordinates.
+ *
* @author Petr Dvorak, petr@wultra.com
*/
@Data
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/Flags.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/Flags.java
index 3f93a6e16..9a2ee93d9 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/Flags.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/Flags.java
@@ -21,6 +21,8 @@
import lombok.Data;
/**
+ * FIDO2 flags.
+ *
* @author Petr Dvorak, petr@wultra.com
*/
@Data
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/PublicKeyObject.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/PublicKeyObject.java
index 47dbbf5da..70ec54056 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/PublicKeyObject.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/PublicKeyObject.java
@@ -24,6 +24,8 @@
import lombok.Data;
/**
+ * Class representing a public key object.
+ *
* @author Petr Dvorak, petr@wultra.com
*/
@Data
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/enumeration/CurveType.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/enumeration/CurveType.java
index 21598d731..299449f66 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/enumeration/CurveType.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/enumeration/CurveType.java
@@ -19,6 +19,8 @@
package com.wultra.powerauth.fido2.rest.model.enumeration;
/**
+ * Elliptic curve type enumeration.
+ *
* @author Petr Dvorak, petr@wultra.com
*/
public enum CurveType {
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/enumeration/ECKeyType.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/enumeration/ECKeyType.java
index a4f8851ea..76ba75175 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/enumeration/ECKeyType.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/enumeration/ECKeyType.java
@@ -19,6 +19,8 @@
package com.wultra.powerauth.fido2.rest.model.enumeration;
/**
+ * Elliptic key type enumeration.
+ *
* @author Roman Strobl, roman.strobl@wultra.com
*/
public enum ECKeyType {
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/enumeration/Fmt.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/enumeration/Fmt.java
index 10a9f66fa..245a89568 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/enumeration/Fmt.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/enumeration/Fmt.java
@@ -21,6 +21,8 @@
import java.util.List;
/**
+ * Attestation format enumeration.
+ *
* @author Petr Dvorak, petr@wultra.com
*/
public enum Fmt {
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/enumeration/SignatureAlgorithm.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/enumeration/SignatureAlgorithm.java
index 712069058..9680c2d3b 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/enumeration/SignatureAlgorithm.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/enumeration/SignatureAlgorithm.java
@@ -19,6 +19,8 @@
package com.wultra.powerauth.fido2.rest.model.enumeration;
/**
+ * Signature algorithm enumeration.
+ *
* @author Petr Dvorak, petr@wultra.com
*/
public enum SignatureAlgorithm {
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/AssertionVerificationRequest.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/AssertionVerificationRequest.java
index bea3f8e2c..09c20505b 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/AssertionVerificationRequest.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/AssertionVerificationRequest.java
@@ -26,6 +26,8 @@
import java.util.List;
/**
+ * Assertion verification request.
+ *
* @author Petr Dvorak, petr@wultra.com
*/
@Data
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/RegistrationRequest.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/RegistrationRequest.java
index 591bb6fe1..46e97b508 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/RegistrationRequest.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/RegistrationRequest.java
@@ -23,6 +23,8 @@
import lombok.Data;
/**
+ * Registration request.
+ *
* @author Petr Dvorak, petr@wultra.com
*/
@Data
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/response/AssertionChallengeResponse.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/response/AssertionChallengeResponse.java
index e9c08f3dd..17e123a30 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/response/AssertionChallengeResponse.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/response/AssertionChallengeResponse.java
@@ -23,6 +23,8 @@
import java.util.List;
/**
+ * Assertion challenge response.
+ *
* @author Petr Dvorak, petr@wultra.com
*/
@Data
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/response/RegisteredAuthenticatorsResponse.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/response/RegisteredAuthenticatorsResponse.java
index 5cd7b4da9..d7eb23993 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/response/RegisteredAuthenticatorsResponse.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/response/RegisteredAuthenticatorsResponse.java
@@ -25,6 +25,8 @@
import java.util.List;
/**
+ * List of registered authenticators response.
+ *
* @author Petr Dvorak, petr@wultra.com
*/
@Data
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/response/RegistrationChallengeResponse.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/response/RegistrationChallengeResponse.java
index 48f24cbc7..334027cfe 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/response/RegistrationChallengeResponse.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/response/RegistrationChallengeResponse.java
@@ -21,6 +21,8 @@
import lombok.Data;
/**
+ * Registration challenge response.
+ *
* @author Petr Dvorak, petr@wultra.com
*/
@Data
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/response/RegistrationResponse.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/response/RegistrationResponse.java
index c0015dcf5..f1f2944f9 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/response/RegistrationResponse.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/response/RegistrationResponse.java
@@ -26,6 +26,8 @@
import java.util.Map;
/**
+ * Registration response.
+ *
* @author Petr Dvorak, petr@wultra.com
*/
@Data
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/validator/AssertionRequestValidator.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/validator/AssertionRequestValidator.java
index b235b4ab7..c5d70d2d8 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/validator/AssertionRequestValidator.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/validator/AssertionRequestValidator.java
@@ -37,6 +37,11 @@
@Slf4j
public class AssertionRequestValidator {
+ /**
+ * Validate an assertion verification request.
+ * @param request Assertion verification request.
+ * @return Validation result.
+ */
public String validate(AssertionVerificationRequest request) {
if (request == null || request.getResponse() == null
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/validator/RegistrationRequestValidator.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/validator/RegistrationRequestValidator.java
index 21800b9c6..29e10fc3b 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/validator/RegistrationRequestValidator.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/validator/RegistrationRequestValidator.java
@@ -40,6 +40,11 @@
@Slf4j
public class RegistrationRequestValidator {
+ /**
+ * Validate a registration request.
+ * @param request Registration request.
+ * @return Validation result.
+ */
public String validate(RegistrationRequest request) {
if (request == null) {
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/AssertionService.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/AssertionService.java
index 5940f0e91..0534b44be 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/AssertionService.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/AssertionService.java
@@ -53,6 +53,14 @@ public class AssertionService {
private final AssertionConverter assertionConverter;
private final AssertionChallengeConverter assertionChallengeConverter;
+ /**
+ * Assertion service constructor.
+ * @param cryptographyService Cryptography service.
+ * @param authenticatorProvider Authenticator provider.
+ * @param assertionProvider Assertion provider.
+ * @param assertionConverter Assertion converter.
+ * @param assertionChallengeConverter Assertion challenge converter.
+ */
@Autowired
public AssertionService(CryptographyService cryptographyService, AuthenticatorProvider authenticatorProvider, AssertionProvider assertionProvider, AssertionConverter assertionConverter, AssertionChallengeConverter assertionChallengeConverter) {
this.cryptographyService = cryptographyService;
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/RegistrationService.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/RegistrationService.java
index f34da19a8..03c0d1d85 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/RegistrationService.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/RegistrationService.java
@@ -52,6 +52,16 @@ public class RegistrationService {
private final RegistrationRequestValidator registrationRequestValidator;
private final CryptographyService cryptographyService;
+ /**
+ * Registration service.
+ *
+ * @param authenticatorProvider Authenticator provider.
+ * @param registrationProvider Registration provider.
+ * @param registrationChallengeConverter Registration challenge converter.
+ * @param registrationConverter Registration converter.
+ * @param registrationRequestValidator Registration request validator.
+ * @param cryptographyService Cryptography service.
+ */
@Autowired
public RegistrationService(AuthenticatorProvider authenticatorProvider, RegistrationProvider registrationProvider, RegistrationChallengeConverter registrationChallengeConverter, RegistrationConverter registrationConverter, RegistrationRequestValidator registrationRequestValidator, CryptographyService cryptographyService) {
this.authenticatorProvider = authenticatorProvider;
@@ -62,17 +72,40 @@ public RegistrationService(AuthenticatorProvider authenticatorProvider, Registra
this.cryptographyService = cryptographyService;
}
- public RegisteredAuthenticatorsResponse registrationsForUser(String userId, String applicationId) throws Fido2AuthenticationFailedException {
+ /**
+ * List registrations for a user.
+ *
+ * @param userId User identifier.
+ * @param applicationId Application identifier.
+ * @return Registered authenticator list response.
+ * @throws Fido2AuthenticationFailedException In case list request fails.
+ */
+ public RegisteredAuthenticatorsResponse listRegistrationsForUser(String userId, String applicationId) throws Fido2AuthenticationFailedException {
final RegisteredAuthenticatorsResponse responseObject = new RegisteredAuthenticatorsResponse();
responseObject.getAuthenticators().addAll(authenticatorProvider.findByUserId(userId, applicationId));
return responseObject;
}
+ /**
+ * Request a registration challenge.
+ *
+ * @param userId User identifier.
+ * @param applicationId Application identifier.
+ * @return Registration challenge response.
+ * @throws Exception Thrown in case creating challenge fails.
+ */
public RegistrationChallengeResponse requestRegistrationChallenge(String userId, String applicationId) throws Exception {
final RegistrationChallenge challenge = registrationProvider.provideChallengeForRegistration(userId, applicationId);
return registrationChallengeConverter.fromChallenge(challenge);
}
+ /**
+ * Register an authenticator.
+ *
+ * @param requestObject Registration request.
+ * @return Registration response.
+ * @throws Exception Thrown in case registration fails.
+ */
public RegistrationResponse register(RegistrationRequest requestObject) throws Exception {
final String applicationId = requestObject.getApplicationId();
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/provider/AuthenticatorProvider.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/provider/AuthenticatorProvider.java
index b8336e338..92a318fed 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/provider/AuthenticatorProvider.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/provider/AuthenticatorProvider.java
@@ -30,8 +30,36 @@
* @author Petr Dvorak, petr@wultra.com
*/
public interface AuthenticatorProvider {
+
+ /**
+ * Store an authenticator.
+ *
+ * @param applicationId Application identifier.
+ * @param challenge Registration challenge.
+ * @param authenticatorDetail Authenticator detail.
+ * @return Authenticator detail.
+ * @throws Fido2AuthenticationFailedException Thrown in case storing authenticator fails.
+ */
AuthenticatorDetail storeAuthenticator(String applicationId, String challenge, AuthenticatorDetail authenticatorDetail) throws Fido2AuthenticationFailedException;
+
+ /**
+ * Find an authenticator by a user identifier.
+ *
+ * @param userId User identifier.
+ * @param applicationId Application identifier.
+ * @return Authenticator detail list.
+ * @throws Fido2AuthenticationFailedException Thrown in case lookup fails.
+ */
List findByUserId(String userId, String applicationId) throws Fido2AuthenticationFailedException;
+
+ /**
+ * Find an authenticator by a credential identifier.
+ *
+ * @param credentialId Credential identifier.
+ * @param applicationId Application identifier.
+ * @return Authenticator detail, if found.
+ * @throws Fido2AuthenticationFailedException Thrown in case lookup fails.
+ */
Optional findByCredentialId(String credentialId, String applicationId) throws Fido2AuthenticationFailedException;
}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/provider/CryptographyService.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/provider/CryptographyService.java
index e3f157ff7..f4fc85c23 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/provider/CryptographyService.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/provider/CryptographyService.java
@@ -26,9 +26,40 @@
* @author Petr Dvorak, petr@wultra.com
*/
public interface CryptographyService {
+
+ /**
+ * Verify signature for a registration.
+ *
+ * @param applicationId Application identifier.
+ * @param clientDataJSON Collected client data.
+ * @param authData Authenticator data.
+ * @param signature Signature bytes.
+ * @param attestedCredentialData Attested credential data.
+ * @return Whether signature verification succeeded.
+ * @throws Exception Thrown in case of a cryptography error.
+ */
boolean verifySignatureForRegistration(String applicationId, CollectedClientData clientDataJSON, AuthenticatorData authData, byte[] signature, AttestedCredentialData attestedCredentialData) throws Exception;
+ /**
+ * Verify signature for an assertion.
+ *
+ * @param applicationId Application identifier.
+ * @param authenticatorId Authenticator identifier.
+ * @param clientDataJSON Collected client data.
+ * @param authData Authenticator data.
+ * @param signature Signature bytes.
+ * @param authenticatorDetail Authenticator detail.
+ * @return Whether signature verification succeeded.
+ * @throws Exception Thrown in case of a cryptography error.
+ */
boolean verifySignatureForAssertion(String applicationId, String authenticatorId, CollectedClientData clientDataJSON, AuthenticatorData authData, byte[] signature, AuthenticatorDetail authenticatorDetail) throws Exception;
+ /**
+ * Convert public key object to bytes.
+ *
+ * @param publicKey Public key object.
+ * @return Public key bytes.
+ * @throws Exception Thrown in case of a cryptography error.
+ */
byte[] publicKeyToBytes(PublicKeyObject publicKey) throws Exception;
}
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/converter/SignatureMetadataConverter.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/converter/SignatureMetadataConverter.java
index 817b927ca..18e32c48f 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/converter/SignatureMetadataConverter.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/converter/SignatureMetadataConverter.java
@@ -43,7 +43,7 @@ public class SignatureMetadataConverter implements AttributeConverter activationHistory = new ArrayList<>();
/**
- * Default constructor.
+ * No-arg constructor.
*/
public ActivationRecordEntity() {
}
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/ApplicationEntity.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/ApplicationEntity.java
index 6f30d80a3..193d979f7 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/ApplicationEntity.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/ApplicationEntity.java
@@ -61,7 +61,7 @@ public class ApplicationEntity implements Serializable {
private final List recoveryCodes = new ArrayList<>();
/**
- * Default constructor.
+ * No-arg constructor.
*/
public ApplicationEntity() {
}
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/MasterKeyPairEntity.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/MasterKeyPairEntity.java
index 25e00dd2d..fb0e1559c 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/MasterKeyPairEntity.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/MasterKeyPairEntity.java
@@ -59,7 +59,7 @@ public class MasterKeyPairEntity implements Serializable {
private ApplicationEntity application;
/**
- * Default constructor
+ * No-arg constructor
*/
public MasterKeyPairEntity() {
}
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/RecoveryCodeEntity.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/RecoveryCodeEntity.java
index 1ebe328b0..8e5a2cd40 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/RecoveryCodeEntity.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/RecoveryCodeEntity.java
@@ -83,7 +83,7 @@ public class RecoveryCodeEntity implements Serializable {
private final List recoveryPuks = new ArrayList<>();
/**
- * Default constructor.
+ * No-arg constructor.
*/
public RecoveryCodeEntity() {
}
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/RecoveryConfigEntity.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/RecoveryConfigEntity.java
index 73d787070..7c734d3de 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/RecoveryConfigEntity.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/RecoveryConfigEntity.java
@@ -70,7 +70,7 @@ public class RecoveryConfigEntity implements Serializable {
private ApplicationEntity application;
/**
- * Default constructor
+ * No-arg constructor
*/
public RecoveryConfigEntity() {
}
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/RecoveryPukEntity.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/RecoveryPukEntity.java
index e01223b41..0e739d0cd 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/RecoveryPukEntity.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/RecoveryPukEntity.java
@@ -67,7 +67,7 @@ public class RecoveryPukEntity implements Serializable {
private Date timestampLastChange;
/**
- * Default constructor.
+ * No-arg constructor.
*/
public RecoveryPukEntity() {
}
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/SignatureEntity.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/SignatureEntity.java
index 9f3688ae3..e0798e6e4 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/SignatureEntity.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/SignatureEntity.java
@@ -95,7 +95,7 @@ public class SignatureEntity implements Serializable {
private Date timestampCreated;
/**
- * Default constructor.
+ * No-arg constructor.
*/
public SignatureEntity() {
}
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/enumeration/EncryptionMode.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/enumeration/EncryptionMode.java
index 323ee127c..43b4ded1f 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/enumeration/EncryptionMode.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/enumeration/EncryptionMode.java
@@ -44,7 +44,7 @@ public enum EncryptionMode {
final byte value;
/**
- * Default constructor with byte value of encryption mode.
+ * No-arg constructor with byte value of encryption mode.
* @param value Byte value of encryption mode.
*/
EncryptionMode(final byte value) {
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/ActivationRecovery.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/ActivationRecovery.java
index f3bb5320b..05aca19e3 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/ActivationRecovery.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/ActivationRecovery.java
@@ -30,7 +30,7 @@ public class ActivationRecovery {
private String puk;
/**
- * Default constuctor.
+ * No-arg constructor.
*/
public ActivationRecovery() {
}
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/TokenInfo.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/TokenInfo.java
index a22782fdb..e1a2de5b8 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/TokenInfo.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/TokenInfo.java
@@ -28,7 +28,7 @@ public class TokenInfo {
private String tokenSecret;
/**
- * Default constructor.
+ * No-arg constructor.
*/
public TokenInfo() {
}
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/request/ActivationLayer2Request.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/request/ActivationLayer2Request.java
index 8c831f941..858f5ca79 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/request/ActivationLayer2Request.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/request/ActivationLayer2Request.java
@@ -34,7 +34,7 @@ public class ActivationLayer2Request {
private String deviceInfo;
/**
- * Default constructor.
+ * No-arg constructor.
*/
public ActivationLayer2Request() {
}
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/request/ConfirmRecoveryRequestPayload.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/request/ConfirmRecoveryRequestPayload.java
index a703606d5..4fd70488f 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/request/ConfirmRecoveryRequestPayload.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/request/ConfirmRecoveryRequestPayload.java
@@ -30,7 +30,7 @@ public class ConfirmRecoveryRequestPayload {
private String recoveryCode;
/**
- * Default constructor.
+ * No-arg constructor.
*/
public ConfirmRecoveryRequestPayload() {
}
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/request/VaultUnlockRequestPayload.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/request/VaultUnlockRequestPayload.java
index 323a18d47..a47ddc2ec 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/request/VaultUnlockRequestPayload.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/request/VaultUnlockRequestPayload.java
@@ -28,7 +28,7 @@ public class VaultUnlockRequestPayload {
private String reason;
/**
- * Default constructor.
+ * No-arg constructor.
*/
public VaultUnlockRequestPayload() {
}
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/response/ActivationLayer2Response.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/response/ActivationLayer2Response.java
index 8b6aa8872..bf4dba184 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/response/ActivationLayer2Response.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/response/ActivationLayer2Response.java
@@ -33,7 +33,7 @@ public class ActivationLayer2Response {
private ActivationRecovery activationRecovery;
/**
- * Default constructor.
+ * No-arg constructor.
*/
public ActivationLayer2Response() {
}
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/response/ConfirmRecoveryResponsePayload.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/response/ConfirmRecoveryResponsePayload.java
index a8505b6b5..dc3c76fa7 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/response/ConfirmRecoveryResponsePayload.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/response/ConfirmRecoveryResponsePayload.java
@@ -30,7 +30,7 @@ public class ConfirmRecoveryResponsePayload {
private boolean alreadyConfirmed;
/**
- * Default constructor.
+ * No-arg constructor.
*/
public ConfirmRecoveryResponsePayload() {
}
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/response/UpgradeResponsePayload.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/response/UpgradeResponsePayload.java
index d4268d5e4..1e4fc4d8e 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/response/UpgradeResponsePayload.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/response/UpgradeResponsePayload.java
@@ -28,7 +28,7 @@
public class UpgradeResponsePayload {
/**
- * Default constructor.
+ * No-arg constructor.
*/
public UpgradeResponsePayload() {
}
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/response/VaultUnlockResponsePayload.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/response/VaultUnlockResponsePayload.java
index 385d9e9d2..244c0b28d 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/response/VaultUnlockResponsePayload.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/response/VaultUnlockResponsePayload.java
@@ -28,7 +28,7 @@ public class VaultUnlockResponsePayload {
private String encryptedVaultEncryptionKey;
/**
- * Default constructor.
+ * No-arg constructor.
*/
public VaultUnlockResponsePayload() {
}
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/signature/OfflineSignatureRequest.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/signature/OfflineSignatureRequest.java
index 2d7c22021..946c413de 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/signature/OfflineSignatureRequest.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/signature/OfflineSignatureRequest.java
@@ -32,7 +32,7 @@ public class OfflineSignatureRequest {
private List signatureTypes;
/**
- * Default constructor.
+ * No-arg constructor.
*/
public OfflineSignatureRequest() {
}
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/signature/OnlineSignatureRequest.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/signature/OnlineSignatureRequest.java
index 05a8351f3..ce102740b 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/signature/OnlineSignatureRequest.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/signature/OnlineSignatureRequest.java
@@ -30,7 +30,7 @@ public class OnlineSignatureRequest {
private SignatureType signatureType;
/**
- * Default constructor.
+ * No-arg constructor.
*/
public OnlineSignatureRequest() {
}
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/signature/SignatureData.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/signature/SignatureData.java
index 933096d5e..789edbff4 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/signature/SignatureData.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/signature/SignatureData.java
@@ -38,7 +38,7 @@ public class SignatureData {
private Integer forcedSignatureVersion;
/**
- * Default constructor.
+ * No-arg constructor.
*/
public SignatureData() {
}
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/signature/SignatureResponse.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/signature/SignatureResponse.java
index 711f29b13..47718f499 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/signature/SignatureResponse.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/model/signature/SignatureResponse.java
@@ -33,7 +33,7 @@ public class SignatureResponse {
private SignatureType usedSignatureType;
/**
- * Default constructor.
+ * No-arg constructor.
*/
public SignatureResponse() {
}
From e8ad7607a1dd72397d22a0648d10fb2c544c4f16 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Roman=20=C5=A0trobl?=
Date: Fri, 26 Jan 2024 17:59:40 +0800
Subject: [PATCH 060/146] Fix #1277: FIDO2: Add a protocol check into existing
services (#1278)
---
.../client/model/enumeration/Protocols.java | 9 ++
.../tasks/ActivationContextValidator.java | 108 ++++++++++++++++++
.../tasks/ActivationServiceBehavior.java | 7 +-
.../AsymmetricSignatureServiceBehavior.java | 5 +-
.../tasks/EciesEncryptionBehavior.java | 14 +--
.../OfflineSignatureServiceBehavior.java | 3 +
.../tasks/RecoveryServiceBehavior.java | 13 +--
.../tasks/SignatureSharedServiceBehavior.java | 22 ++--
.../service/behavior/tasks/TokenBehavior.java | 14 +--
.../tasks/UpgradeServiceBehavior.java | 27 +++--
.../tasks/VaultUnlockServiceBehavior.java | 6 +-
11 files changed, 174 insertions(+), 54 deletions(-)
create mode 100644 powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/ActivationContextValidator.java
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/enumeration/Protocols.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/enumeration/Protocols.java
index 9e43497e2..26c76b14c 100644
--- a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/enumeration/Protocols.java
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/enumeration/Protocols.java
@@ -41,4 +41,13 @@ public enum Protocols {
public String toString() {
return protocol;
}
+
+ public static boolean isPowerAuth(String protocol) {
+ return POWERAUTH.protocol.equals(protocol);
+ }
+
+ public static boolean isFido2(String protocol) {
+ return FIDO2.protocol.equals(protocol);
+ }
+
}
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/ActivationContextValidator.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/ActivationContextValidator.java
new file mode 100644
index 000000000..b533362f4
--- /dev/null
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/ActivationContextValidator.java
@@ -0,0 +1,108 @@
+/*
+ * PowerAuth Server 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.server.service.behavior.tasks;
+
+import com.wultra.security.powerauth.client.model.enumeration.Protocols;
+import io.getlime.security.powerauth.app.server.database.model.enumeration.ActivationStatus;
+import io.getlime.security.powerauth.app.server.service.exceptions.GenericServiceException;
+import io.getlime.security.powerauth.app.server.service.i18n.LocalizationProvider;
+import io.getlime.security.powerauth.app.server.service.model.ServiceError;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+/**
+ * Validation of activation context.
+ *
+ * @author Roman Strobl, roman.strobl@wultra.com
+ */
+@Component
+@Slf4j
+public class ActivationContextValidator {
+
+ /**
+ * Validate that protocol is powerauth.
+ * @param protocol Protocol.
+ * @param localizationProvider Localization provider.
+ * @throws GenericServiceException Thrown when protocol is invalid.
+ */
+ public void validatePowerAuthProtocol(final String protocol, final LocalizationProvider localizationProvider) throws GenericServiceException {
+ if (!Protocols.isPowerAuth(protocol)) {
+ logger.warn("Invalid protocol: {}, expected: powerauth", protocol);
+ throw localizationProvider.buildExceptionForCode(ServiceError.INVALID_REQUEST);
+ }
+ }
+
+ /**
+ * Validate that protocol is fido2.
+ * @param protocol Protocol.
+ * @param localizationProvider Localization provider.
+ * @throws GenericServiceException Thrown when protocol is invalid.
+ */
+ public void validateFido2Protocol(final String protocol, final LocalizationProvider localizationProvider) throws GenericServiceException {
+ if (!Protocols.isFido2(protocol)) {
+ logger.warn("Invalid protocol: {}, expected: fido2", protocol);
+ throw localizationProvider.buildExceptionForCode(ServiceError.INVALID_REQUEST);
+ }
+ }
+
+ /**
+ * Validate that activation status is ACTIVE.
+ * @param activationStatus Actual validation status.
+ * @param activationId Activation identifier.
+ * @param localizationProvider Localization provider.
+ * @throws GenericServiceException Thrown when activation status is invalid.
+ */
+ public void validateActiveStatus(final ActivationStatus activationStatus, final String activationId, final LocalizationProvider localizationProvider) throws GenericServiceException {
+ // Check if the activation is in correct state
+ if (!ActivationStatus.ACTIVE.equals(activationStatus)) {
+ logger.info("Activation is not ACTIVE, activation ID: {}", activationId);
+ throw localizationProvider.buildExceptionForCode(ServiceError.ACTIVATION_INCORRECT_STATE);
+ }
+ }
+
+ /**
+ * Validate activation version.
+ * @param actualVersion Actual version.
+ * @param expectedVersion Expected version.
+ * @param activationId Activation identifier.
+ * @param localizationProvider Localization provider.
+ * @throws GenericServiceException Thrown when activation version is invalid.
+ */
+ public void validateVersion(final int actualVersion, final int expectedVersion, final String activationId, final LocalizationProvider localizationProvider) throws GenericServiceException {
+ if (actualVersion != expectedVersion) {
+ logger.info("Activation version is invalid, activation ID: {}, expected: {}, actual: {}", activationId, expectedVersion, actualVersion);
+ throw localizationProvider.buildExceptionForCode(ServiceError.ACTIVATION_INCORRECT_STATE);
+ }
+ }
+
+ /**
+ * Validate activation version.
+ * @param activationVersion Version of activation.
+ * @param localizationProvider Localization provider.
+ * @throws GenericServiceException Thrown when activation version is invalid.
+ */
+ public void validateVersionValid(final int activationVersion, final LocalizationProvider localizationProvider) throws GenericServiceException {
+ if (activationVersion < 2 || activationVersion > 3) {
+ logger.warn("Invalid activation version: {}", activationVersion);
+ throw localizationProvider.buildExceptionForCode(ServiceError.ACTIVATION_INCORRECT_STATE);
+ }
+ }
+
+}
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/ActivationServiceBehavior.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/ActivationServiceBehavior.java
index e4171f0ba..d00d153b6 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/ActivationServiceBehavior.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/ActivationServiceBehavior.java
@@ -111,6 +111,8 @@ public class ActivationServiceBehavior {
private final ReplayVerificationService replayVerificationService;
+ private final ActivationContextValidator activationValidator;
+
// Prepare converters
private final ActivationStatusConverter activationStatusConverter = new ActivationStatusConverter();
private final ActivationOtpValidationConverter activationOtpValidationConverter = new ActivationOtpValidationConverter();
@@ -127,10 +129,11 @@ public class ActivationServiceBehavior {
private static final Logger logger = LoggerFactory.getLogger(ActivationServiceBehavior.class);
@Autowired
- public ActivationServiceBehavior(RepositoryCatalogue repositoryCatalogue, PowerAuthServiceConfiguration powerAuthServiceConfiguration, ReplayVerificationService eciesreplayPersistenceService, ObjectMapper objectMapper) {
+ public ActivationServiceBehavior(RepositoryCatalogue repositoryCatalogue, PowerAuthServiceConfiguration powerAuthServiceConfiguration, ReplayVerificationService eciesreplayPersistenceService, ActivationContextValidator activationValidator, ObjectMapper objectMapper) {
this.repositoryCatalogue = repositoryCatalogue;
this.powerAuthServiceConfiguration = powerAuthServiceConfiguration;
this.replayVerificationService = eciesreplayPersistenceService;
+ this.activationValidator = activationValidator;
this.objectMapper = objectMapper;
}
@@ -2001,6 +2004,8 @@ private ActivationRecovery createRecoveryCodeForActivation(ActivationRecordEntit
throw localizationProvider.buildExceptionForCode(ServiceError.INVALID_REQUEST);
}
+ activationValidator.validatePowerAuthProtocol(activationEntity.getProtocol(), localizationProvider);
+
// Note: the code below expects that application version for given activation has been verified.
// We want to avoid checking application version twice (once during activation and second time in this method).
// It is also expected that the activation is a valid activation which has just been created.
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/AsymmetricSignatureServiceBehavior.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/AsymmetricSignatureServiceBehavior.java
index 361f77ea1..8fc7171d0 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/AsymmetricSignatureServiceBehavior.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/AsymmetricSignatureServiceBehavior.java
@@ -47,6 +47,7 @@ public class AsymmetricSignatureServiceBehavior {
private final ActivationRepository activationRepository;
private final LocalizationProvider localizationProvider;
+ private final ActivationContextValidator activationValidator;
private final SignatureUtils signatureUtils = new SignatureUtils();
@@ -54,9 +55,10 @@ public class AsymmetricSignatureServiceBehavior {
private static final Logger logger = LoggerFactory.getLogger(AsymmetricSignatureServiceBehavior.class);
@Autowired
- public AsymmetricSignatureServiceBehavior(ActivationRepository activationRepository, LocalizationProvider localizationProvider) {
+ public AsymmetricSignatureServiceBehavior(ActivationRepository activationRepository, LocalizationProvider localizationProvider, ActivationContextValidator activationValidator) {
this.activationRepository = activationRepository;
this.localizationProvider = localizationProvider;
+ this.activationValidator = activationValidator;
}
/**
@@ -75,6 +77,7 @@ public boolean verifyECDSASignature(String activationId, String data, String sig
logger.warn("Activation used when verifying ECDSA signature does not exist, activation ID: {}", activationId);
return false;
}
+ activationValidator.validatePowerAuthProtocol(activation.getProtocol(), localizationProvider);
byte[] devicePublicKeyData = Base64.getDecoder().decode(activation.getDevicePublicKeyBase64());
PublicKey devicePublicKey = keyConversionUtilities.convertBytesToPublicKey(devicePublicKeyData);
return signatureUtils.validateECDSASignature(Base64.getDecoder().decode(data), Base64.getDecoder().decode(signature), devicePublicKey);
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/EciesEncryptionBehavior.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/EciesEncryptionBehavior.java
index d3c1c5e5c..e437d0a9a 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/EciesEncryptionBehavior.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/EciesEncryptionBehavior.java
@@ -21,7 +21,6 @@
import com.wultra.security.powerauth.client.model.response.GetEciesDecryptorResponse;
import io.getlime.security.powerauth.app.server.converter.ServerPrivateKeyConverter;
import io.getlime.security.powerauth.app.server.database.RepositoryCatalogue;
-import io.getlime.security.powerauth.app.server.database.model.enumeration.ActivationStatus;
import io.getlime.security.powerauth.app.server.database.model.enumeration.EncryptionMode;
import io.getlime.security.powerauth.app.server.database.model.ServerPrivateKey;
import io.getlime.security.powerauth.app.server.database.model.entity.ActivationRecordEntity;
@@ -75,6 +74,7 @@ public class EciesEncryptionBehavior {
private final LocalizationProvider localizationProvider;
private final ServerPrivateKeyConverter serverPrivateKeyConverter;
private final ReplayVerificationService replayVerificationService;
+ private final ActivationContextValidator activationValidator;
// Helper classes
private final EncryptorFactory encryptorFactory = new EncryptorFactory();
@@ -85,11 +85,12 @@ public class EciesEncryptionBehavior {
private static final Logger logger = LoggerFactory.getLogger(EciesEncryptionBehavior.class);
@Autowired
- public EciesEncryptionBehavior(RepositoryCatalogue repositoryCatalogue, LocalizationProvider localizationProvider, ServerPrivateKeyConverter serverPrivateKeyConverter, ReplayVerificationService replayVerificationService) {
+ public EciesEncryptionBehavior(RepositoryCatalogue repositoryCatalogue, LocalizationProvider localizationProvider, ServerPrivateKeyConverter serverPrivateKeyConverter, ReplayVerificationService replayVerificationService, ActivationContextValidator activationValidator) {
this.repositoryCatalogue = repositoryCatalogue;
this.localizationProvider = localizationProvider;
this.serverPrivateKeyConverter = serverPrivateKeyConverter;
this.replayVerificationService = replayVerificationService;
+ this.activationValidator = activationValidator;
}
/**
@@ -217,6 +218,8 @@ private GetEciesDecryptorResponse getEciesDecryptorParametersForActivation(GetEc
throw localizationProvider.buildExceptionForCode(ServiceError.ACTIVATION_NOT_FOUND);
}
+ activationValidator.validatePowerAuthProtocol(activation.getProtocol(), localizationProvider);
+
if (request.getTimestamp() != null) {
// Check ECIES request for replay attacks and persist unique value from request
replayVerificationService.checkAndPersistUniqueValue(
@@ -228,12 +231,7 @@ private GetEciesDecryptorResponse getEciesDecryptorParametersForActivation(GetEc
request.getProtocolVersion());
}
- // Check if the activation is in correct state
- if (!ActivationStatus.ACTIVE.equals(activation.getActivationStatus())) {
- logger.info("Activation is not ACTIVE, activation ID: {}", request.getActivationId());
- // Rollback is not required, database is not used for writing
- throw localizationProvider.buildExceptionForCode(ServiceError.ACTIVATION_INCORRECT_STATE);
- }
+ activationValidator.validateActiveStatus(activation.getActivationStatus(), activation.getActivationId(), localizationProvider);
// Lookup the application version and check that it is supported
final ApplicationVersionEntity applicationVersion = repositoryCatalogue.getApplicationVersionRepository().findByApplicationKey(request.getApplicationKey());
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/OfflineSignatureServiceBehavior.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/OfflineSignatureServiceBehavior.java
index 0cdcbbfee..0c692b9d0 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/OfflineSignatureServiceBehavior.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/OfflineSignatureServiceBehavior.java
@@ -82,6 +82,7 @@ public class OfflineSignatureServiceBehavior {
private final SignatureSharedServiceBehavior signatureSharedServiceBehavior;
private final LocalizationProvider localizationProvider;
private final PowerAuthServiceConfiguration powerAuthServiceConfiguration;
+ private final ActivationContextValidator activationValidator;
// Prepare converters
private final ActivationStatusConverter activationStatusConverter = new ActivationStatusConverter();
@@ -131,6 +132,8 @@ public CreatePersonalizedOfflineSignaturePayloadResponse createPersonalizedOffli
throw localizationProvider.buildExceptionForCode(ServiceError.ACTIVATION_NOT_FOUND);
}
+ activationValidator.validatePowerAuthProtocol(activation.getProtocol(), localizationProvider);
+
// Proceed and compute the results
try {
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/RecoveryServiceBehavior.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/RecoveryServiceBehavior.java
index 1ce54b68c..14d7dc0f9 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/RecoveryServiceBehavior.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/RecoveryServiceBehavior.java
@@ -88,6 +88,7 @@ public class RecoveryServiceBehavior {
private final ServerPrivateKeyConverter serverPrivateKeyConverter;
private final RecoveryPrivateKeyConverter recoveryPrivateKeyConverter;
private final ReplayVerificationService replayVerificationService;
+ private final ActivationContextValidator activationValidator;
// Business logic implementation classes
private final PowerAuthServerKeyFactory powerAuthServerKeyFactory = new PowerAuthServerKeyFactory();
@@ -106,13 +107,14 @@ public class RecoveryServiceBehavior {
public RecoveryServiceBehavior(LocalizationProvider localizationProvider,
PowerAuthServiceConfiguration powerAuthServiceConfiguration, RepositoryCatalogue repositoryCatalogue,
ServerPrivateKeyConverter serverPrivateKeyConverter, RecoveryPrivateKeyConverter recoveryPrivateKeyConverter,
- ReplayVerificationService replayVerificationService, ObjectMapper objectMapper, RecoveryPukConverter recoveryPukConverter) {
+ ReplayVerificationService replayVerificationService, ActivationContextValidator activationValidator, ObjectMapper objectMapper, RecoveryPukConverter recoveryPukConverter) {
this.localizationProvider = localizationProvider;
this.powerAuthServiceConfiguration = powerAuthServiceConfiguration;
this.repositoryCatalogue = repositoryCatalogue;
this.serverPrivateKeyConverter = serverPrivateKeyConverter;
this.recoveryPrivateKeyConverter = recoveryPrivateKeyConverter;
this.replayVerificationService = replayVerificationService;
+ this.activationValidator = activationValidator;
this.objectMapper = objectMapper;
this.recoveryPukConverter = recoveryPukConverter;
}
@@ -312,12 +314,9 @@ public ConfirmRecoveryCodeResponse confirmRecoveryCode(ConfirmRecoveryCodeReques
throw localizationProvider.buildExceptionForCode(ServiceError.INVALID_REQUEST);
}
- // Check if the activation is in ACTIVE state
- if (!ActivationStatus.ACTIVE.equals(activation.getActivationStatus())) {
- logger.warn("Activation is not ACTIVE, activation ID: {}", activationId);
- // Rollback is not required, error occurs before writing to database
- throw localizationProvider.buildExceptionForCode(ServiceError.ACTIVATION_INCORRECT_STATE);
- }
+ activationValidator.validatePowerAuthProtocol(activation.getProtocol(), localizationProvider);
+
+ activationValidator.validateActiveStatus(activation.getActivationStatus(), activation.getActivationId(), localizationProvider);
// Get the server private key, decrypt it if required
final String serverPrivateKeyFromEntity = activation.getServerPrivateKeyBase64();
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/SignatureSharedServiceBehavior.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/SignatureSharedServiceBehavior.java
index 85a4ed5b4..7b97d631e 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/SignatureSharedServiceBehavior.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/SignatureSharedServiceBehavior.java
@@ -75,6 +75,7 @@ public class SignatureSharedServiceBehavior {
private final CallbackUrlBehavior callbackUrlBehavior;
private final LocalizationProvider localizationProvider;
private final PowerAuthServiceConfiguration powerAuthServiceConfiguration;
+ private final ActivationContextValidator activationValidator;
private ServerPrivateKeyConverter serverPrivateKeyConverter;
@@ -90,15 +91,17 @@ public class SignatureSharedServiceBehavior {
* @param callbackUrlBehavior Callback URL behavior.
* @param localizationProvider Localization provider for error handling.
* @param powerAuthServiceConfiguration PowerAuth service configuration.
+ * @param activationValidator
*/
@Autowired
- public SignatureSharedServiceBehavior(RepositoryCatalogue repositoryCatalogue, ActivationHistoryServiceBehavior activationHistoryServiceBehavior, AuditingServiceBehavior auditingServiceBehavior, CallbackUrlBehavior callbackUrlBehavior, LocalizationProvider localizationProvider, PowerAuthServiceConfiguration powerAuthServiceConfiguration) {
+ public SignatureSharedServiceBehavior(RepositoryCatalogue repositoryCatalogue, ActivationHistoryServiceBehavior activationHistoryServiceBehavior, AuditingServiceBehavior auditingServiceBehavior, CallbackUrlBehavior callbackUrlBehavior, LocalizationProvider localizationProvider, PowerAuthServiceConfiguration powerAuthServiceConfiguration, ActivationContextValidator activationValidator) {
this.repositoryCatalogue = repositoryCatalogue;
this.activationHistoryServiceBehavior = activationHistoryServiceBehavior;
this.auditingServiceBehavior = auditingServiceBehavior;
this.callbackUrlBehavior = callbackUrlBehavior;
this.localizationProvider = localizationProvider;
this.powerAuthServiceConfiguration = powerAuthServiceConfiguration;
+ this.activationValidator = activationValidator;
}
/**
@@ -266,6 +269,8 @@ public void handleInactiveActivationWithMismatchSignature(ActivationRecordEntity
* @throws GenericCryptoException In case of any other cryptography error.
*/
private SignatureResponse verifySignatureImpl(ActivationRecordEntity activation, SignatureData signatureData, List signatureTypes, KeyConvertor keyConversionUtilities) throws InvalidKeyException, InvalidKeySpecException, GenericServiceException, CryptoProviderException, GenericCryptoException {
+ activationValidator.validatePowerAuthProtocol(activation.getProtocol(), localizationProvider);
+
// Get the server private and device public keys
// Decrypt server private key (depending on encryption mode)
@@ -344,19 +349,6 @@ private SignatureResponse verifySignatureImpl(ActivationRecordEntity activation,
return new SignatureResponse(signatureValid, ctrNext, ctrDataNext, signatureVersion, usedSignatureType);
}
- /**
- * Validate activation version.
- * @param activationVersion Version of activation.
- * @throws GenericServiceException Thrown when activation version is invalid.
- */
- private void validateActivationVersion(Integer activationVersion) throws GenericServiceException {
- if (activationVersion == null || activationVersion < 2 || activationVersion > 3) {
- logger.warn("Invalid activation version: {}", activationVersion);
- // Rollback is not required, error occurs before writing to database
- throw localizationProvider.buildExceptionForCode(ServiceError.ACTIVATION_INCORRECT_STATE);
- }
- }
-
/**
* Implementation of handle invalid application version.
* @param activation Activation used for signature verification.
@@ -596,7 +588,7 @@ private boolean notPossessionFactorSignature(SignatureType signatureType) {
*/
private Integer resolveSignatureVersion(ActivationRecordEntity activation, Integer forcedSignatureVersion) throws GenericServiceException {
// Validate activation version
- validateActivationVersion(activation.getVersion());
+ activationValidator.validateVersionValid(activation.getVersion(), localizationProvider);
// Set signature version based on activation version as default
Integer signatureVersion = activation.getVersion();
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/TokenBehavior.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/TokenBehavior.java
index ef6f00945..601b928e1 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/TokenBehavior.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/TokenBehavior.java
@@ -83,6 +83,7 @@ public class TokenBehavior {
private final PowerAuthServiceConfiguration powerAuthServiceConfiguration;
private final ServerPrivateKeyConverter serverPrivateKeyConverter;
private final ReplayVerificationService replayVerificationService;
+ private final ActivationContextValidator activationValidator;
// Business logic implementation classes
private final ServerTokenGenerator tokenGenerator = new ServerTokenGenerator();
@@ -101,12 +102,13 @@ public class TokenBehavior {
private static final Logger logger = LoggerFactory.getLogger(TokenBehavior.class);
@Autowired
- public TokenBehavior(RepositoryCatalogue repositoryCatalogue, LocalizationProvider localizationProvider, PowerAuthServiceConfiguration powerAuthServiceConfiguration, ServerPrivateKeyConverter serverPrivateKeyConverter, ReplayVerificationService replayVerificationService, ObjectMapper objectMapper) {
+ public TokenBehavior(RepositoryCatalogue repositoryCatalogue, LocalizationProvider localizationProvider, PowerAuthServiceConfiguration powerAuthServiceConfiguration, ServerPrivateKeyConverter serverPrivateKeyConverter, ReplayVerificationService replayVerificationService, ActivationContextValidator activationValidator, ObjectMapper objectMapper) {
this.repositoryCatalogue = repositoryCatalogue;
this.localizationProvider = localizationProvider;
this.powerAuthServiceConfiguration = powerAuthServiceConfiguration;
this.serverPrivateKeyConverter = serverPrivateKeyConverter;
this.replayVerificationService = replayVerificationService;
+ this.activationValidator = activationValidator;
this.objectMapper = objectMapper;
}
@@ -168,12 +170,9 @@ private EncryptedResponse createToken(String activationId, String applicationKey
throw localizationProvider.buildExceptionForCode(ServiceError.ACTIVATION_NOT_FOUND);
}
- // Check if the activation is in correct state
- if (!ActivationStatus.ACTIVE.equals(activation.getActivationStatus())) {
- logger.info("Activation is not ACTIVE, activation ID: {}", activationId);
- // Rollback is not required, error occurs before writing to database
- throw localizationProvider.buildExceptionForCode(ServiceError.ACTIVATION_INCORRECT_STATE);
- }
+ activationValidator.validatePowerAuthProtocol(activation.getProtocol(), localizationProvider);
+
+ activationValidator.validateActiveStatus(activation.getActivationStatus(), activation.getActivationId(), localizationProvider);
if (encryptedRequest.getTimestamp() != null) {
// Check ECIES request for replay attacks and persist unique value from request
@@ -300,6 +299,7 @@ public ValidateTokenResponse validateToken(ValidateTokenRequest request) throws
final ActivationRecordEntity activation = token.getActivation();
final byte[] tokenSecret = Base64.getDecoder().decode(token.getTokenSecret());
final boolean isTokenValid;
+ activationValidator.validatePowerAuthProtocol(activation.getProtocol(), localizationProvider);
if (!ActivationStatus.ACTIVE.equals(activation.getActivationStatus())) {
logger.info("Activation is not ACTIVE, activation ID: {}", activation.getActivationId());
isTokenValid = false;
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/UpgradeServiceBehavior.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/UpgradeServiceBehavior.java
index 9418ade56..d88dbb6d8 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/UpgradeServiceBehavior.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/UpgradeServiceBehavior.java
@@ -25,7 +25,6 @@
import com.wultra.security.powerauth.client.model.response.StartUpgradeResponse;
import io.getlime.security.powerauth.app.server.converter.ServerPrivateKeyConverter;
import io.getlime.security.powerauth.app.server.database.RepositoryCatalogue;
-import io.getlime.security.powerauth.app.server.database.model.enumeration.ActivationStatus;
import io.getlime.security.powerauth.app.server.database.model.AdditionalInformation;
import io.getlime.security.powerauth.app.server.database.model.enumeration.EncryptionMode;
import io.getlime.security.powerauth.app.server.database.model.ServerPrivateKey;
@@ -75,6 +74,7 @@ public class UpgradeServiceBehavior {
private final LocalizationProvider localizationProvider;
private final ServerPrivateKeyConverter serverPrivateKeyConverter;
private final ReplayVerificationService replayVerificationService;
+ private final ActivationContextValidator activationValidator;
// Helper classes
private final EncryptorFactory encryptorFactory = new EncryptorFactory();
@@ -92,13 +92,14 @@ public UpgradeServiceBehavior(
final LocalizationProvider localizationProvider,
final ServerPrivateKeyConverter serverPrivateKeyConverter,
final ReplayVerificationService replayVerificationService,
- final ObjectMapper objectMapper,
+ ActivationContextValidator activationValidator, final ObjectMapper objectMapper,
final ActivationHistoryServiceBehavior activationHistoryServiceBehavior) {
this.repositoryCatalogue = repositoryCatalogue;
this.localizationProvider = localizationProvider;
this.serverPrivateKeyConverter = serverPrivateKeyConverter;
this.replayVerificationService = replayVerificationService;
+ this.activationValidator = activationValidator;
this.objectMapper = objectMapper;
this.activationHistoryServiceBehavior = activationHistoryServiceBehavior;
}
@@ -149,12 +150,11 @@ public StartUpgradeResponse startUpgrade(StartUpgradeRequest request) throws Gen
throw localizationProvider.buildExceptionForCode(ServiceError.ACTIVATION_NOT_FOUND);
}
- // Check if the activation is in correct state and version is 2
- if (!ActivationStatus.ACTIVE.equals(activation.getActivationStatus()) || activation.getVersion() != 2) {
- logger.info("Activation state is invalid, activation ID: {}", activationId);
- // Rollback is not required, error occurs before writing to database
- throw localizationProvider.buildExceptionForCode(ServiceError.ACTIVATION_INCORRECT_STATE);
- }
+ activationValidator.validatePowerAuthProtocol(activation.getProtocol(), localizationProvider);
+
+ activationValidator.validateActiveStatus(activation.getActivationStatus(), activation.getActivationId(), localizationProvider);
+
+ activationValidator.validateVersion(activation.getVersion(), 2, activationId, localizationProvider);
// Do not verify ctr_data, upgrade response may not be delivered to client, so the client may retry the upgrade
@@ -279,12 +279,11 @@ public CommitUpgradeResponse commitUpgrade(CommitUpgradeRequest request) throws
throw localizationProvider.buildExceptionForCode(ServiceError.ACTIVATION_NOT_FOUND);
}
- // Check if the activation is in correct state and version is 2
- if (!ActivationStatus.ACTIVE.equals(activation.getActivationStatus()) || activation.getVersion() != 2) {
- logger.info("Activation state is invalid, activation ID: {}", activationId);
- // Rollback is not required, error occurs before writing to database
- throw localizationProvider.buildExceptionForCode(ServiceError.ACTIVATION_INCORRECT_STATE);
- }
+ activationValidator.validatePowerAuthProtocol(activation.getProtocol(), localizationProvider);
+
+ activationValidator.validateActiveStatus(activation.getActivationStatus(), activation.getActivationId(), localizationProvider);
+
+ activationValidator.validateVersion(activation.getVersion(), 2, activationId, localizationProvider);
// Check if the activation hash based counter was generated (upgrade has been started)
if (activation.getCtrDataBase64() == null) {
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/VaultUnlockServiceBehavior.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/VaultUnlockServiceBehavior.java
index 478d6cab6..902ca1d34 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/VaultUnlockServiceBehavior.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/VaultUnlockServiceBehavior.java
@@ -85,6 +85,7 @@ public class VaultUnlockServiceBehavior {
private final ServerPrivateKeyConverter serverPrivateKeyConverter;
private final ServiceBehaviorCatalogue behavior;
private final ReplayVerificationService replayVerificationService;
+ private final ActivationContextValidator activationValidator;
// Helper classes
private final EncryptorFactory encryptorFactory = new EncryptorFactory();
@@ -97,12 +98,13 @@ public class VaultUnlockServiceBehavior {
private static final Logger logger = LoggerFactory.getLogger(VaultUnlockServiceBehavior.class);
@Autowired
- public VaultUnlockServiceBehavior(RepositoryCatalogue repositoryCatalogue, LocalizationProvider localizationProvider, ServerPrivateKeyConverter serverPrivateKeyConverter, ServiceBehaviorCatalogue behavior, ReplayVerificationService replayVerificationService, ObjectMapper objectMapper) {
+ public VaultUnlockServiceBehavior(RepositoryCatalogue repositoryCatalogue, LocalizationProvider localizationProvider, ServerPrivateKeyConverter serverPrivateKeyConverter, ServiceBehaviorCatalogue behavior, ReplayVerificationService replayVerificationService, ActivationContextValidator activationValidator, ObjectMapper objectMapper) {
this.repositoryCatalogue = repositoryCatalogue;
this.localizationProvider = localizationProvider;
this.serverPrivateKeyConverter = serverPrivateKeyConverter;
this.behavior = behavior;
this.replayVerificationService = replayVerificationService;
+ this.activationValidator = activationValidator;
this.objectMapper = objectMapper;
}
@@ -142,6 +144,8 @@ public VaultUnlockResponse unlockVault(String activationId, String applicationKe
return response;
}
+ activationValidator.validatePowerAuthProtocol(activation.getProtocol(), localizationProvider);
+
// Get the server private key, decrypt it if required
final String serverPrivateKeyFromEntity = activation.getServerPrivateKeyBase64();
final EncryptionMode serverPrivateKeyEncryptionMode = activation.getServerPrivateKeyEncryption();
From b488c0b51e71692b473c5fc5a547e7921f4e48e2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Lubo=C5=A1=20Ra=C4=8Dansk=C3=BD?=
Date: Fri, 26 Jan 2024 11:23:28 +0100
Subject: [PATCH 061/146] Fix #1275: Improve logging of verifyOfflineSignature
(#1276)
* Fix #1275: Improve logging of verifyOfflineSignature, validateToken, verifySignature, and verifyECDSASignature
---
.../powerauth/app/server/service/PowerAuthService.java | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/PowerAuthService.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/PowerAuthService.java
index 05cfb1a26..51da89ab0 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/PowerAuthService.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/PowerAuthService.java
@@ -414,7 +414,7 @@ public VerifySignatureResponse verifySignature(VerifySignatureRequest request) t
try {
logger.info("VerifySignatureRequest received, activation ID: {}", request.getActivationId());
final VerifySignatureResponse response = this.verifySignatureImplNonTransaction(request, new ArrayList<>());
- logger.info("VerifySignatureRequest succeeded");
+ logger.info("VerifySignatureRequest succeeded, activation ID: {}, signature valid: {}", request.getActivationId(), response.isSignatureValid());
return response;
} catch (GenericServiceException ex) {
// already logged
@@ -517,7 +517,7 @@ public VerifyOfflineSignatureResponse verifyOfflineSignature(VerifyOfflineSignat
logger.info("VerifyOfflineSignatureRequest received, activation ID: {}", activationId);
final VerifyOfflineSignatureParameter parameter = convert(request, expectedComponentLength, allowedSignatureTypes, keyConvertor);
final VerifyOfflineSignatureResponse response = behavior.getOfflineSignatureServiceBehavior().verifyOfflineSignature(parameter);
- logger.info("VerifyOfflineSignatureRequest succeeded");
+ logger.info("VerifyOfflineSignatureRequest succeeded, activation ID: {}, signature valid: {}", activationId, response.isSignatureValid());
return response;
} catch (GenericServiceException ex) {
// already logged
@@ -759,7 +759,7 @@ public VerifyECDSASignatureResponse verifyECDSASignature(VerifyECDSASignatureReq
final boolean matches = behavior.getAsymmetricSignatureServiceBehavior().verifyECDSASignature(activationId, signedData, signature, keyConvertor);
final VerifyECDSASignatureResponse response = new VerifyECDSASignatureResponse();
response.setSignatureValid(matches);
- logger.info("VerifyECDSASignatureRequest succeeded");
+ logger.info("VerifyECDSASignatureRequest succeeded, activation ID: {}, signature valid: {}", activationId, response.isSignatureValid());
return response;
} catch (GenericServiceException ex) {
// already logged
@@ -1163,7 +1163,7 @@ public ValidateTokenResponse validateToken(ValidateTokenRequest request) throws
try {
logger.info("ValidateTokenRequest received, token ID: {}", request.getTokenId());
final ValidateTokenResponse response = behavior.getTokenBehavior().validateToken(request);
- logger.info("ValidateTokenRequest succeeded");
+ logger.info("ValidateTokenRequest succeeded, token ID: {}, token valid: {}", request.getTokenId(), response.isTokenValid());
return response;
} catch (GenericServiceException ex) {
// already logged
From c3b58ddf809f76c389e6aa6a75c0f064313dd74c Mon Sep 17 00:00:00 2001
From: Roman Strobl
Date: Fri, 26 Jan 2024 20:25:47 +0800
Subject: [PATCH 062/146] Fix #1279: FIDO2: logging
---
.../fido2/rest/controller/AssertionController.java | 4 ++++
.../fido2/rest/controller/RegistrationController.java | 6 ++++++
2 files changed, 10 insertions(+)
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/AssertionController.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/AssertionController.java
index 1055883ae..140cd708b 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/AssertionController.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/AssertionController.java
@@ -83,7 +83,9 @@ public AssertionController(AssertionRequestValidator assertionRequestValidator,
@PostMapping("challenge")
public ObjectResponse requestAssertionChallenge(@Valid @RequestBody ObjectRequest request) throws Exception {
final AssertionChallengeRequest requestObject = request.getRequestObject();
+ logger.info("AssertionChallengeRequest received, application IDs: {}", requestObject.getApplicationIds());
final AssertionChallengeResponse assertionChallengeResponse = assertionService.requestAssertionChallenge(requestObject);
+ logger.info("AssertionChallengeRequest succeeded, application IDs: {}", requestObject.getApplicationIds());
return new ObjectResponse<>(assertionChallengeResponse);
}
@@ -105,11 +107,13 @@ public ObjectResponse requestAssertionChallenge(@Val
@PostMapping
public ObjectResponse authenticate(@Valid @RequestBody ObjectRequest request) throws Fido2AuthenticationFailedException {
final AssertionVerificationRequest requestObject = request.getRequestObject();
+ logger.info("AssertionVerificationRequest received, ID: {}", requestObject.getId());
final String error = assertionRequestValidator.validate(requestObject);
if (error != null) {
throw new Fido2AuthenticationFailedException(error);
}
final AssertionVerificationResponse signatureResponse = assertionService.authenticate(requestObject);
+ logger.info("AssertionVerificationRequest succeeded, ID: {}, valid: {}", requestObject.getId(), signatureResponse.isAssertionValid());
return new ObjectResponse<>(signatureResponse);
}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/RegistrationController.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/RegistrationController.java
index 02e54b9e4..2da5433de 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/RegistrationController.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/RegistrationController.java
@@ -81,7 +81,9 @@ public RegistrationController(RegistrationService registrationService) {
@PostMapping("list")
public ObjectResponse registeredAuthenticators(@Valid @RequestBody ObjectRequest request) throws Exception {
final RegisteredAuthenticatorsRequest requestObject = request.getRequestObject();
+ logger.info("RegisteredAuthenticatorsRequest received, application ID: {}, user ID: {}", requestObject.getApplicationId(), requestObject.getUserId());
final RegisteredAuthenticatorsResponse responseObject = registrationService.listRegistrationsForUser(requestObject.getUserId(), requestObject.getApplicationId());
+ logger.info("RegisteredAuthenticatorsRequest succeeded, application ID: {}, user ID: {}", requestObject.getApplicationId(), requestObject.getUserId());
return new ObjectResponse<>(responseObject);
}
@@ -103,7 +105,9 @@ public ObjectResponse registeredAuthenticators
@PostMapping("challenge")
public ObjectResponse requestRegistrationChallenge(@Valid @RequestBody ObjectRequest request) throws Exception {
final RegistrationChallengeRequest requestObject = request.getRequestObject();
+ logger.info("RegistrationChallengeRequest received, application ID: {}, user ID: {}", requestObject.getApplicationId(), requestObject.getUserId());
final RegistrationChallengeResponse responseObject = registrationService.requestRegistrationChallenge(requestObject.getUserId(), requestObject.getApplicationId());
+ logger.info("RegistrationChallengeRequest succeeded, application ID: {}, user ID: {}", requestObject.getApplicationId(), requestObject.getUserId());
return new ObjectResponse<>(responseObject);
}
@@ -125,7 +129,9 @@ public ObjectResponse requestRegistrationChalleng
@PostMapping
public ObjectResponse register(@Valid @RequestBody ObjectRequest request) throws Exception {
final RegistrationRequest requestObject = request.getRequestObject();
+ logger.info("RegistrationRequest received, application ID: {}, activation name: {}", requestObject.getApplicationId(), requestObject.getActivationName());
final RegistrationResponse responseObject = registrationService.register(requestObject);
+ logger.info("RegistrationRequest succeeded, application ID: {}, activation name: {}", requestObject.getApplicationId(), requestObject.getActivationName());
return new ObjectResponse<>(responseObject);
}
From 92ca4d269f9eda660f218479c38984c3dfad5253 Mon Sep 17 00:00:00 2001
From: Roman Strobl
Date: Sun, 28 Jan 2024 17:01:07 +0800
Subject: [PATCH 063/146] Fix #1281: FIDO2: Add protocol into activation status
endpoint response
---
.../client/model/response/GetActivationStatusResponse.java | 1 +
.../service/behavior/tasks/ActivationServiceBehavior.java | 3 +++
.../app/server/service/behavior/tasks/CallbackUrlBehavior.java | 3 +++
3 files changed, 7 insertions(+)
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/response/GetActivationStatusResponse.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/response/GetActivationStatusResponse.java
index 98684dbb4..f08d2decd 100644
--- a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/response/GetActivationStatusResponse.java
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/response/GetActivationStatusResponse.java
@@ -42,6 +42,7 @@ public class GetActivationStatusResponse {
private String activationName;
private String userId;
private String extras;
+ private String protocol;
private String platform;
private String deviceInfo;
private String applicationId;
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/ActivationServiceBehavior.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/ActivationServiceBehavior.java
index d00d153b6..80ae7bc24 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/ActivationServiceBehavior.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/ActivationServiceBehavior.java
@@ -473,6 +473,7 @@ public GetActivationStatusResponse getActivationStatus(String activationId, Stri
response.setActivationSignature(Base64.getEncoder().encodeToString(activationSignature));
response.setDevicePublicKeyFingerprint(null);
response.setPlatform(activation.getPlatform());
+ response.setProtocol(activation.getProtocol());
response.setDeviceInfo(activation.getDeviceInfo());
response.getActivationFlags().addAll(activation.getFlags());
response.getApplicationRoles().addAll(application.getRoles());
@@ -587,6 +588,7 @@ public GetActivationStatusResponse getActivationStatus(String activationId, Stri
response.setActivationSignature(null);
response.setDevicePublicKeyFingerprint(activationFingerPrint);
response.setPlatform(activation.getPlatform());
+ response.setProtocol(activation.getProtocol());
response.setDeviceInfo(activation.getDeviceInfo());
response.getActivationFlags().addAll(activation.getFlags());
response.getApplicationRoles().addAll(application.getRoles());
@@ -617,6 +619,7 @@ public GetActivationStatusResponse getActivationStatus(String activationId, Stri
response.setApplicationId(null);
response.setExtras(null);
response.setPlatform(null);
+ response.setProtocol(null);
response.setDeviceInfo(null);
response.setTimestampCreated(zeroDate);
response.setTimestampLastUsed(zeroDate);
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/CallbackUrlBehavior.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/CallbackUrlBehavior.java
index 57c35e902..dbdad10d1 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/CallbackUrlBehavior.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/CallbackUrlBehavior.java
@@ -301,6 +301,9 @@ private Map prepareCallbackDataActivation(CallbackUrlEntity call
if (callbackUrlEntity.getAttributes().contains("platform")) {
callbackData.put("platform", activation.getPlatform());
}
+ if (callbackUrlEntity.getAttributes().contains("protocol")) {
+ callbackData.put("protocol", activation.getProtocol());
+ }
if (callbackUrlEntity.getAttributes().contains("activationFlags")) {
callbackData.put("activationFlags", activation.getFlags());
}
From 787aad05f253484c4faf2afdcb4b943c5dd9022b Mon Sep 17 00:00:00 2001
From: Roman Strobl
Date: Sun, 28 Jan 2024 18:03:38 +0800
Subject: [PATCH 064/146] Fix #1283: FIDO2: Add auditing
---
.../fido2/PowerAuthAssertionProvider.java | 48 ++++++++++---------
.../fido2/PowerAuthAuthenticatorProvider.java | 29 ++++++++++-
2 files changed, 53 insertions(+), 24 deletions(-)
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAssertionProvider.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAssertionProvider.java
index ea176b42e..d8d19995d 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAssertionProvider.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAssertionProvider.java
@@ -18,6 +18,8 @@
package io.getlime.security.powerauth.app.server.service.fido2;
+import com.wultra.core.audit.base.model.AuditDetail;
+import com.wultra.core.audit.base.model.AuditLevel;
import com.wultra.powerauth.fido2.errorhandling.Fido2AuthenticationFailedException;
import com.wultra.powerauth.fido2.rest.model.entity.AssertionChallenge;
import com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorDetail;
@@ -56,13 +58,17 @@
@Service
public class PowerAuthAssertionProvider implements AssertionProvider {
+ private static final String AUDIT_TYPE_FIDO2 = "fido2";
+
private final ServiceBehaviorCatalogue serviceBehaviorCatalogue;
private final RepositoryCatalogue repositoryCatalogue;
+ private final AuditingServiceBehavior audit;
@Autowired
- public PowerAuthAssertionProvider(ServiceBehaviorCatalogue serviceBehaviorCatalogue, RepositoryCatalogue repositoryCatalogue) {
+ public PowerAuthAssertionProvider(ServiceBehaviorCatalogue serviceBehaviorCatalogue, RepositoryCatalogue repositoryCatalogue, AuditingServiceBehavior audit) {
this.serviceBehaviorCatalogue = serviceBehaviorCatalogue;
this.repositoryCatalogue = repositoryCatalogue;
+ this.audit = audit;
}
@Override
@@ -105,6 +111,7 @@ public AssertionChallenge approveAssertion(String challengeValue, AuthenticatorD
final OperationUserActionResponse approveOperation = serviceBehaviorCatalogue.getOperationBehavior().attemptApproveOperation(operationApproveRequest);
final UserActionResult result = approveOperation.getResult();
final OperationDetailResponse operation = approveOperation.getOperation();
+ auditAssertionResult(authenticatorDetail, result);
if (result == UserActionResult.APPROVED) {
final AssertionChallenge assertionChallenge = new AssertionChallenge();
assertionChallenge.setChallenge(challengeValue);
@@ -141,6 +148,7 @@ public AssertionChallenge failAssertion(String challengeValue, AuthenticatorDeta
final OperationUserActionResponse approveOperation = serviceBehaviorCatalogue.getOperationBehavior().failApprovalOperation(operationFailApprovalRequest);
final OperationDetailResponse operation = approveOperation.getOperation();
+ auditAssertionResult(authenticatorDetail, approveOperation.getResult());
handleStatus(operation.getStatus());
final AssertionChallenge assertionChallenge = new AssertionChallenge();
assertionChallenge.setChallenge(challengeValue);
@@ -154,6 +162,22 @@ public AssertionChallenge failAssertion(String challengeValue, AuthenticatorDeta
}
}
+ /**
+ * Audit result of an assertion verification result.
+ * @param authenticator Authenticator detail.
+ * @param result Assertion verification result.
+ */
+ private void auditAssertionResult(final AuthenticatorDetail authenticator, final UserActionResult result) {
+ final AuditDetail auditDetail = AuditDetail.builder()
+ .type(AUDIT_TYPE_FIDO2)
+ .param("userId", authenticator.getUserId())
+ .param("applicationId", authenticator.getApplicationId())
+ .param("activationId", authenticator.getActivationId())
+ .param("result", result)
+ .build();
+ audit.log(AuditLevel.INFO, "Assertion result for activation with ID: {}", auditDetail, authenticator.getActivationId());
+ }
+
/**
* Implementation of handle invalid signature.
* @param activation Activation used for signature verification.
@@ -198,28 +222,6 @@ private void handleInvalidSignatureImpl(ActivationRecordEntity activation, Signa
}
}
- /**
- * Implementation of handle inactive activation during signature verification.
- * @param activation Activation used for signature verification.
- * @param signatureData Data related to the signature.
- * @param signatureType Used signature type.
- * @param currentTimestamp Signature verification timestamp.
- */
- private void handleInactiveActivationSignatureImpl(ActivationRecordEntity activation, SignatureData signatureData, SignatureType signatureType, Date currentTimestamp) {
- // Get ActivationRepository
- final ActivationRepository activationRepository = repositoryCatalogue.getActivationRepository();
-
- // Update the last used date
- activation.setTimestampLastUsed(currentTimestamp);
-
- // Save the activation
- activationRepository.save(activation);
-
- // Create the audit log record
- final AuditingServiceBehavior.ActivationRecordDto activationDto = createActivationDtoFrom(activation);
- serviceBehaviorCatalogue.getAuditingServiceBehavior().logSignatureAuditRecord(activationDto, signatureData, signatureType, false, activation.getVersion(), "activation_invalid_state", currentTimestamp);
- }
-
/**
* Handle operation status.
*
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAuthenticatorProvider.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAuthenticatorProvider.java
index c24f3fdca..e5e42b835 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAuthenticatorProvider.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAuthenticatorProvider.java
@@ -21,6 +21,8 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.wultra.core.audit.base.model.AuditDetail;
+import com.wultra.core.audit.base.model.AuditLevel;
import com.wultra.powerauth.fido2.errorhandling.Fido2AuthenticationFailedException;
import com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorDetail;
import com.wultra.powerauth.fido2.service.provider.AuthenticatorProvider;
@@ -35,6 +37,7 @@
import io.getlime.security.powerauth.app.server.database.repository.ActivationRepository;
import io.getlime.security.powerauth.app.server.database.repository.ApplicationRepository;
import io.getlime.security.powerauth.app.server.service.behavior.ServiceBehaviorCatalogue;
+import io.getlime.security.powerauth.app.server.service.behavior.tasks.AuditingServiceBehavior;
import io.getlime.security.powerauth.app.server.service.exceptions.GenericServiceException;
import io.getlime.security.powerauth.app.server.service.i18n.LocalizationProvider;
import io.getlime.security.powerauth.app.server.service.model.ServiceError;
@@ -60,10 +63,14 @@
@Service
@Slf4j
public class PowerAuthAuthenticatorProvider implements AuthenticatorProvider {
+
+ private static final String AUDIT_TYPE_FIDO2 = "fido2";
+
private final ApplicationRepository applicationRepository;
private final RepositoryCatalogue repositoryCatalogue;
private final ServiceBehaviorCatalogue serviceBehaviorCatalogue;
+ private final AuditingServiceBehavior audit;
private LocalizationProvider localizationProvider;
@@ -73,10 +80,11 @@ public class PowerAuthAuthenticatorProvider implements AuthenticatorProvider {
@Autowired
public PowerAuthAuthenticatorProvider(RepositoryCatalogue repositoryCatalogue, ServiceBehaviorCatalogue serviceBehaviorCatalogue,
- ApplicationRepository applicationRepository) {
+ ApplicationRepository applicationRepository, AuditingServiceBehavior audit) {
this.repositoryCatalogue = repositoryCatalogue;
this.serviceBehaviorCatalogue = serviceBehaviorCatalogue;
this.applicationRepository = applicationRepository;
+ this.audit = audit;
}
@Autowired
@@ -235,6 +243,8 @@ public AuthenticatorDetail storeAuthenticator(String applicationId, String activ
activationResponse.setMaxFailedAttempts(activation.getMaxFailedAttempts());
activationResponse.setDevicePublicKeyBase64(activation.getDevicePublicKeyBase64());
+ auditStoredAuthenticator(activationResponse);
+
// Generate authenticator detail
final Optional authenticatorOptional = convert(activationResponse, applicationEntity);
authenticatorOptional.orElseThrow(() -> new Fido2AuthenticationFailedException("Authenticator object deserialization failed"));
@@ -252,7 +262,24 @@ public AuthenticatorDetail storeAuthenticator(String applicationId, String activ
} catch (GenericServiceException e) {
throw new Fido2AuthenticationFailedException("Generic service exception");
}
+ }
+ /**
+ * Audit stored authenticator for an activation.
+ * @param activation Activation record.
+ */
+ private void auditStoredAuthenticator(Activation activation) {
+ final AuditDetail auditDetail = AuditDetail.builder()
+ .type(AUDIT_TYPE_FIDO2)
+ .param("userId", activation.getUserId())
+ .param("applicationId", activation.getApplicationId())
+ .param("activationId", activation.getActivationId())
+ .param("activationName", activation.getActivationName())
+ .param("externalId", activation.getExternalId())
+ .param("platform", activation.getPlatform())
+ .param("deviceInfo", activation.getDeviceInfo())
+ .build();
+ audit.log(AuditLevel.INFO, "Stored authenticator for activation with ID: {}", auditDetail, activation.getActivationId());
}
private Optional convert(Activation activation, ApplicationEntity application) {
From b1769e84d2cb498054d556b1beb97acdc9d302a8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Roman=20=C5=A0trobl?=
Date: Mon, 29 Jan 2024 14:52:14 +0800
Subject: [PATCH 065/146] Fix #1285: FIDO2: Review provider API (#1286)
---
.../fido2/service/RegistrationService.java | 4 ++--
.../fido2/service/provider/AssertionProvider.java | 14 +-------------
.../service/provider/CryptographyService.java | 1 +
.../service/provider/RegistrationProvider.java | 5 +++--
.../fido2/PowerAuthAuthenticatorProvider.java | 4 ++--
.../fido2/PowerAuthRegistrationProvider.java | 6 +++---
6 files changed, 12 insertions(+), 22 deletions(-)
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/RegistrationService.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/RegistrationService.java
index 03c0d1d85..5d8f20c5b 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/RegistrationService.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/RegistrationService.java
@@ -131,14 +131,14 @@ public RegistrationResponse register(RegistrationRequest requestObject) throws E
final boolean verifySignature = cryptographyService.verifySignatureForRegistration(applicationId, clientDataJSON, authData, signature, attestedCredentialData);
if (!verifySignature) {
// Immediately revoke the challenge
- registrationProvider.revokeChallengeForRegistrationChallengeValue(applicationId, challengeValue);
+ registrationProvider.revokeRegistrationByChallengeValue(applicationId, challengeValue);
throw new Fido2AuthenticationFailedException("Registration failed");
}
} else {
logger.info("No signature verification on registration");
}
- final RegistrationChallenge challenge = registrationProvider.provideChallengeForRegistrationChallengeValue(applicationId, challengeValue);
+ final RegistrationChallenge challenge = registrationProvider.findRegistrationChallengeByValue(applicationId, challengeValue);
final Optional authenticatorOptional = registrationConverter.convert(challenge, requestObject, attestedCredentialData.getAaguid(), cryptographyService.publicKeyToBytes(attestedCredentialData.getPublicKeyObject()));
authenticatorOptional.orElseThrow(() -> new Fido2AuthenticationFailedException("Invalid request"));
final AuthenticatorDetail authenticatorDetailResponse = authenticatorProvider.storeAuthenticator(requestObject.getApplicationId(), challenge.getChallenge(), authenticatorOptional.get());
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/provider/AssertionProvider.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/provider/AssertionProvider.java
index e2bae3032..b6491f773 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/provider/AssertionProvider.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/provider/AssertionProvider.java
@@ -32,19 +32,6 @@
*/
public interface AssertionProvider {
- /**
- * Obtain challenge for authentication.
- *
- * @param applicationIds List of application ID.
- * @param operationType Type of the operation this challenge is for.
- * @param parameters Operation parameters.
- * @return Assertion challenge.
- * @throws Exception In case any issue occur during processing.
- */
- default AssertionChallenge provideChallengeForAssertion(List applicationIds, String operationType, Map parameters) throws Exception {
- return provideChallengeForAssertion(applicationIds, operationType, parameters, null);
- };
-
/**
* Obtain challenge for authentication.
*
@@ -76,4 +63,5 @@ default AssertionChallenge provideChallengeForAssertion(List application
* @throws Fido2AuthenticationFailedException In case assertion approval fails.
*/
AssertionChallenge failAssertion(String challenge, AuthenticatorDetail authenticatorDetail) throws Fido2AuthenticationFailedException;
+
}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/provider/CryptographyService.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/provider/CryptographyService.java
index f4fc85c23..99aa411af 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/provider/CryptographyService.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/provider/CryptographyService.java
@@ -62,4 +62,5 @@ public interface CryptographyService {
* @throws Exception Thrown in case of a cryptography error.
*/
byte[] publicKeyToBytes(PublicKeyObject publicKey) throws Exception;
+
}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/provider/RegistrationProvider.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/provider/RegistrationProvider.java
index 18b3981cd..f1918e309 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/provider/RegistrationProvider.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/provider/RegistrationProvider.java
@@ -45,7 +45,7 @@ public interface RegistrationProvider {
* @return Challenge Information.
* @throws Exception In case any issue occur during processing.
*/
- RegistrationChallenge provideChallengeForRegistrationChallengeValue(String applicationId, String challenge) throws Exception;
+ RegistrationChallenge findRegistrationChallengeByValue(String applicationId, String challenge) throws Exception;
/**
* Revoke existing challenge based on the challenge value.
@@ -54,5 +54,6 @@ public interface RegistrationProvider {
* @param challengeValue Challenge value.
* @throws Exception In case any issue occur during processing.
*/
- void revokeChallengeForRegistrationChallengeValue(String applicationId, String challengeValue) throws Exception;
+ void revokeRegistrationByChallengeValue(String applicationId, String challengeValue) throws Exception;
+
}
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAuthenticatorProvider.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAuthenticatorProvider.java
index e5e42b835..b96355997 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAuthenticatorProvider.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAuthenticatorProvider.java
@@ -93,7 +93,7 @@ public void setLocalizationProvider(LocalizationProvider localizationProvider) {
}
@Override
- @Transactional
+ @Transactional(readOnly = true)
public List findByUserId(String userId, String applicationId) throws Fido2AuthenticationFailedException {
// Find application
@@ -122,7 +122,7 @@ public List findByUserId(String userId, String applicationI
}
@Override
- @Transactional
+ @Transactional(readOnly = true)
public Optional findByCredentialId(String applicationId, String credentialId) throws Fido2AuthenticationFailedException {
// Find application
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthRegistrationProvider.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthRegistrationProvider.java
index f9be5ee22..3b16f7c94 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthRegistrationProvider.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthRegistrationProvider.java
@@ -74,8 +74,8 @@ public RegistrationChallenge provideChallengeForRegistration(String userId, Stri
}
@Override
- @Transactional
- public RegistrationChallenge provideChallengeForRegistrationChallengeValue(String applicationId, String challengeValue) throws Fido2AuthenticationFailedException {
+ @Transactional(readOnly = true)
+ public RegistrationChallenge findRegistrationChallengeByValue(String applicationId, String challengeValue) throws Fido2AuthenticationFailedException {
// Find application
final Optional application = repositoryCatalogue.getApplicationRepository().findById(applicationId);
@@ -111,7 +111,7 @@ public RegistrationChallenge provideChallengeForRegistrationChallengeValue(Strin
@Override
@Transactional
- public void revokeChallengeForRegistrationChallengeValue(String applicationId, String challengeValue) throws Fido2AuthenticationFailedException {
+ public void revokeRegistrationByChallengeValue(String applicationId, String challengeValue) throws Fido2AuthenticationFailedException {
final Date currentTimestamp = new Date();
// Find application
From d2f80a0763063544bc352e8ab6268b2be67826d3 Mon Sep 17 00:00:00 2001
From: Lubos Racansky
Date: Tue, 30 Jan 2024 07:54:08 +0100
Subject: [PATCH 066/146] Fix #1287: Broken implementation guide links on
sidebar
---
docs/_Sidebar.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/docs/_Sidebar.md b/docs/_Sidebar.md
index 07944aef0..508f63ee0 100644
--- a/docs/_Sidebar.md
+++ b/docs/_Sidebar.md
@@ -12,8 +12,8 @@
**Implementation Tutorials**
-- [Authentication in Mobile Banking Apps (SCA)](https://developers.wultra.com/products/mobile-security-suite/develop/tutorials/Authentication-in-Mobile-Apps)
-- [Verifying PowerAuth Signatures On The Server](https://developers.wultra.com/products/mobile-security-suite/develop/tutorials/Manual-Signature-Verification)
+- [Mobile-First Authentication in Banking (SCA)](https://developers.wultra.com/tutorials/posts/Mobile-First-Authentication/)
+- [Verifying PowerAuth Signatures On The Server](https://developers.wultra.com/tutorials/posts/Manual-Signature-Verification/)
**Reference Manual**
From 707ea324018a120f412cb5d76f9e0fa44500dce0 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 5 Feb 2024 01:26:49 +0000
Subject: [PATCH 067/146] Bump com.google.zxing:core from 3.5.2 to 3.5.3
Bumps [com.google.zxing:core](https://github.com/zxing/zxing) from 3.5.2 to 3.5.3.
- [Release notes](https://github.com/zxing/zxing/releases)
- [Changelog](https://github.com/zxing/zxing/blob/master/CHANGES)
- [Commits](https://github.com/zxing/zxing/compare/zxing-3.5.2...zxing-3.5.3)
---
updated-dependencies:
- dependency-name: com.google.zxing:core
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
---
powerauth-admin/pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/powerauth-admin/pom.xml b/powerauth-admin/pom.xml
index 8bed0cfd2..bde52308d 100644
--- a/powerauth-admin/pom.xml
+++ b/powerauth-admin/pom.xml
@@ -66,7 +66,7 @@
com.google.zxing
core
- 3.5.2
+ 3.5.3
com.google.zxing
From d7384380a63d99be7c413aca575b706d1588a231 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 5 Feb 2024 01:27:05 +0000
Subject: [PATCH 068/146] Bump com.webauthn4j:webauthn4j-test
Bumps [com.webauthn4j:webauthn4j-test](https://github.com/webauthn4j/webauthn4j) from 0.22.0.RELEASE to 0.22.1.RELEASE.
- [Release notes](https://github.com/webauthn4j/webauthn4j/releases)
- [Changelog](https://github.com/webauthn4j/webauthn4j/blob/master/github-release-notes-generator.yml)
- [Commits](https://github.com/webauthn4j/webauthn4j/compare/0.22.0.RELEASE...0.22.1.RELEASE)
---
updated-dependencies:
- dependency-name: com.webauthn4j:webauthn4j-test
dependency-type: direct:development
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
---
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index 5b1d3ff41..120298935 100644
--- a/pom.xml
+++ b/pom.xml
@@ -105,7 +105,7 @@
3.15.6
- 0.22.0.RELEASE
+ 0.22.1.RELEASE
From 591c4830581acb13193084c1d49782d59cc32857 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 5 Feb 2024 01:27:44 +0000
Subject: [PATCH 069/146] Bump com.google.zxing:javase from 3.5.2 to 3.5.3
Bumps [com.google.zxing:javase](https://github.com/zxing/zxing) from 3.5.2 to 3.5.3.
- [Release notes](https://github.com/zxing/zxing/releases)
- [Changelog](https://github.com/zxing/zxing/blob/master/CHANGES)
- [Commits](https://github.com/zxing/zxing/compare/zxing-3.5.2...zxing-3.5.3)
---
updated-dependencies:
- dependency-name: com.google.zxing:javase
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
---
powerauth-admin/pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/powerauth-admin/pom.xml b/powerauth-admin/pom.xml
index 8bed0cfd2..c71ba8ad6 100644
--- a/powerauth-admin/pom.xml
+++ b/powerauth-admin/pom.xml
@@ -71,7 +71,7 @@
com.google.zxing
javase
- 3.5.2
+ 3.5.3
From 579f211673556629a24fbb2496600648a35f058c Mon Sep 17 00:00:00 2001
From: Lubos Racansky
Date: Mon, 5 Feb 2024 07:19:58 +0100
Subject: [PATCH 070/146] Fix #1294: Extract zxing version to the maven
property
---
pom.xml | 10 ++++++++++
powerauth-admin/pom.xml | 2 --
2 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 120298935..fdd733e44 100644
--- a/pom.xml
+++ b/pom.xml
@@ -103,6 +103,7 @@
1.11.0
7.4
3.15.6
+ 3.5.3
0.22.1.RELEASE
@@ -144,6 +145,15 @@
${bcprov-jdk18on.version}
+
+
+ com.google.zxing
+ zxing-parent
+ ${zxing.version}
+ pom
+ import
+
+
io.swagger.core.v3
swagger-annotations-jakarta
diff --git a/powerauth-admin/pom.xml b/powerauth-admin/pom.xml
index ab72fbf9f..f698bff13 100644
--- a/powerauth-admin/pom.xml
+++ b/powerauth-admin/pom.xml
@@ -66,12 +66,10 @@
com.google.zxing
core
- 3.5.3
com.google.zxing
javase
- 3.5.3
From a2b0e0148d74d78f46165ec33ee2d29eb27d4ef6 Mon Sep 17 00:00:00 2001
From: Roman Strobl
Date: Mon, 5 Feb 2024 15:53:32 +0800
Subject: [PATCH 071/146] Fix #1290: FIDO2: Show activation protocol in
PowerAuth Admin
---
.../controller/ActivationController.java | 2 +
.../webapp/WEB-INF/jsp/activationDetail.jsp | 66 ++++++++++++-------
.../response/GetActivationStatusResponse.java | 1 +
.../controller/RegistrationController.java | 6 +-
.../tasks/ActivationServiceBehavior.java | 3 +
5 files changed, 53 insertions(+), 25 deletions(-)
diff --git a/powerauth-admin/src/main/java/io/getlime/security/app/admin/controller/ActivationController.java b/powerauth-admin/src/main/java/io/getlime/security/app/admin/controller/ActivationController.java
index 71eb9f94e..e96bd6942 100644
--- a/powerauth-admin/src/main/java/io/getlime/security/app/admin/controller/ActivationController.java
+++ b/powerauth-admin/src/main/java/io/getlime/security/app/admin/controller/ActivationController.java
@@ -159,6 +159,8 @@ public String activationDetail(
model.put("version", activation.getVersion());
model.put("platform", activation.getPlatform());
model.put("deviceInfo", activation.getDeviceInfo());
+ model.put("protocol", activation.getProtocol());
+ model.put("externalId", activation.getExternalId());
model.put("activationFlags", activation.getActivationFlags());
if (activation.getActivationStatus() == ActivationStatus.PENDING_COMMIT && activation.getActivationOtpValidation() == ActivationOtpValidation.ON_COMMIT) {
model.put("showOtpInput", true);
diff --git a/powerauth-admin/src/main/webapp/WEB-INF/jsp/activationDetail.jsp b/powerauth-admin/src/main/webapp/WEB-INF/jsp/activationDetail.jsp
index af61900f4..0fa6d0b9a 100644
--- a/powerauth-admin/src/main/webapp/WEB-INF/jsp/activationDetail.jsp
+++ b/powerauth-admin/src/main/webapp/WEB-INF/jsp/activationDetail.jsp
@@ -151,35 +151,57 @@
- Platform
-
-
- iOS
-
-
- Android
-
-
- Hardware Token
-
-
- Unknown
-
-
- ${platform}
-
-
+
+ Platform
+
+
+ iOS
+
+
+ Android
+
+
+ Hardware Token
+
+
+ Unknown
+
+
+ ${platform}
+
+
+
|
- User Device Information
-
-
-
+
+ User Device Information
+
+
+
+
|
+
+
+
+ Protocol
+
+
+
+ |
+
+
+
+ External ID
+
+
+
+ |
+
+
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/response/GetActivationStatusResponse.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/response/GetActivationStatusResponse.java
index f08d2decd..6f11a4988 100644
--- a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/response/GetActivationStatusResponse.java
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/response/GetActivationStatusResponse.java
@@ -43,6 +43,7 @@ public class GetActivationStatusResponse {
private String userId;
private String extras;
private String protocol;
+ private String externalId;
private String platform;
private String deviceInfo;
private String applicationId;
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/RegistrationController.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/RegistrationController.java
index 2da5433de..117848c6e 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/RegistrationController.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/controller/RegistrationController.java
@@ -49,7 +49,7 @@
@RestController
@RequestMapping("fido2/registrations")
@Slf4j
-@Tag(name = "FIDO2 Registration Controller", description = "API for FIDO2 authenticator registrations")
+@Tag(name = "FIDO2 Registrations Controller", description = "API for FIDO2 authenticator registrations")
public class RegistrationController {
private final RegistrationService registrationService;
@@ -81,9 +81,9 @@ public RegistrationController(RegistrationService registrationService) {
@PostMapping("list")
public ObjectResponse registeredAuthenticators(@Valid @RequestBody ObjectRequest request) throws Exception {
final RegisteredAuthenticatorsRequest requestObject = request.getRequestObject();
- logger.info("RegisteredAuthenticatorsRequest received, application ID: {}, user ID: {}", requestObject.getApplicationId(), requestObject.getUserId());
+ logger.info("RegisteredAuthenticatorsRequest received, user ID: {}, application ID: {}", requestObject.getUserId(), requestObject.getApplicationId());
final RegisteredAuthenticatorsResponse responseObject = registrationService.listRegistrationsForUser(requestObject.getUserId(), requestObject.getApplicationId());
- logger.info("RegisteredAuthenticatorsRequest succeeded, application ID: {}, user ID: {}", requestObject.getApplicationId(), requestObject.getUserId());
+ logger.info("RegisteredAuthenticatorsRequest succeeded, user ID: {}, application ID: {}", requestObject.getUserId(), requestObject.getApplicationId());
return new ObjectResponse<>(responseObject);
}
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/ActivationServiceBehavior.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/ActivationServiceBehavior.java
index 80ae7bc24..9b1791912 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/ActivationServiceBehavior.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/ActivationServiceBehavior.java
@@ -474,6 +474,7 @@ public GetActivationStatusResponse getActivationStatus(String activationId, Stri
response.setDevicePublicKeyFingerprint(null);
response.setPlatform(activation.getPlatform());
response.setProtocol(activation.getProtocol());
+ response.setExternalId(activation.getExternalId());
response.setDeviceInfo(activation.getDeviceInfo());
response.getActivationFlags().addAll(activation.getFlags());
response.getApplicationRoles().addAll(application.getRoles());
@@ -589,6 +590,7 @@ public GetActivationStatusResponse getActivationStatus(String activationId, Stri
response.setDevicePublicKeyFingerprint(activationFingerPrint);
response.setPlatform(activation.getPlatform());
response.setProtocol(activation.getProtocol());
+ response.setExternalId(activation.getExternalId());
response.setDeviceInfo(activation.getDeviceInfo());
response.getActivationFlags().addAll(activation.getFlags());
response.getApplicationRoles().addAll(application.getRoles());
@@ -620,6 +622,7 @@ public GetActivationStatusResponse getActivationStatus(String activationId, Stri
response.setExtras(null);
response.setPlatform(null);
response.setProtocol(null);
+ response.setExternalId(null);
response.setDeviceInfo(null);
response.setTimestampCreated(zeroDate);
response.setTimestampLastUsed(zeroDate);
From c4e0ec2aeb3efae8cdb5c96e43c66a20b6f22548 Mon Sep 17 00:00:00 2001
From: Jan Dusil <134381434+jandusil@users.noreply.github.com>
Date: Tue, 6 Feb 2024 11:23:31 +0100
Subject: [PATCH 072/146] Fix #1272: Add TraceID/SpanID to Monitoring for
Enhanced Observability (#1273)
* Fix #1272: Add Traceable headers
---
docs/Configuration-Properties.md | 3 +++
powerauth-admin/pom.xml | 10 ++++++++++
.../src/main/resources/application.properties | 1 +
powerauth-java-server/pom.xml | 10 ++++++++++
.../src/main/resources/application.properties | 3 ++-
5 files changed, 26 insertions(+), 1 deletion(-)
diff --git a/docs/Configuration-Properties.md b/docs/Configuration-Properties.md
index 0f4cfbcb9..8386f166e 100644
--- a/docs/Configuration-Properties.md
+++ b/docs/Configuration-Properties.md
@@ -71,6 +71,9 @@ The PowerAuth Server uses the following public configuration properties:
## Monitoring and Observability
+| Property | Default | Note |
+|-------------------------------------------|---------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `management.tracing.sampling.probability` | `1.0` | Specifies the proportion of requests that are sampled for tracing. A value of 1.0 means that 100% of requests are sampled, while a value of 0 effectively disables tracing. |
The WAR file includes the `micrometer-registry-prometheus` dependency.
Discuss its configuration with the [Spring Boot documentation](https://docs.spring.io/spring-boot/docs/3.1.x/reference/html/actuator.html#actuator.metrics).
diff --git a/powerauth-admin/pom.xml b/powerauth-admin/pom.xml
index f698bff13..3f144a415 100644
--- a/powerauth-admin/pom.xml
+++ b/powerauth-admin/pom.xml
@@ -99,6 +99,16 @@
micrometer-registry-prometheus
+
+ io.projectreactor
+ reactor-core-micrometer
+
+
+
+ io.micrometer
+ micrometer-tracing-bridge-otel
+
+
org.springframework.boot
diff --git a/powerauth-admin/src/main/resources/application.properties b/powerauth-admin/src/main/resources/application.properties
index 78997389f..b999e5431 100644
--- a/powerauth-admin/src/main/resources/application.properties
+++ b/powerauth-admin/src/main/resources/application.properties
@@ -51,6 +51,7 @@ spring.jpa.open-in-view=false
management.health.ldap.enabled=false
# Monitoring
+management.tracing.sampling.probability=1.0
#management.endpoint.metrics.enabled=true
#management.endpoints.web.exposure.include=health, prometheus
#management.endpoint.prometheus.enabled=true
diff --git a/powerauth-java-server/pom.xml b/powerauth-java-server/pom.xml
index c8913711d..077458476 100644
--- a/powerauth-java-server/pom.xml
+++ b/powerauth-java-server/pom.xml
@@ -156,6 +156,16 @@
micrometer-registry-prometheus
+
+ io.projectreactor
+ reactor-core-micrometer
+
+
+
+ io.micrometer
+ micrometer-tracing-bridge-otel
+
+
io.netty
diff --git a/powerauth-java-server/src/main/resources/application.properties b/powerauth-java-server/src/main/resources/application.properties
index 2851cf497..af3f20178 100644
--- a/powerauth-java-server/src/main/resources/application.properties
+++ b/powerauth-java-server/src/main/resources/application.properties
@@ -125,7 +125,8 @@ powerauth.service.correlation-header.value.validation-regexp=[a-zA-Z0-9\\-]{8,10
powerauth.service.proximity-check.otp.length=8
# Monitoring
+management.tracing.sampling.probability=1.0
#management.endpoint.metrics.enabled=true
#management.endpoints.web.exposure.include=health, prometheus
#management.endpoint.prometheus.enabled=true
-#management.prometheus.metrics.export.enabled=true
+#management.prometheus.metrics.export.enabled=true
\ No newline at end of file
From 10ecf627cfc37423ae59c17de04214b6220d1600 Mon Sep 17 00:00:00 2001
From: Jan Dusil <134381434+jandusil@users.noreply.github.com>
Date: Tue, 6 Feb 2024 12:28:44 +0100
Subject: [PATCH 073/146] Fix #1297: Remove spring.datasource.driverClassName
from app props (#1298)
---
docs/Configuration-Properties.md | 1 -
docs/Deploying-PowerAuth-Server.md | 6 ------
docs/Deploying-Wildfly.md | 1 -
.../src/main/resources/application.properties | 2 --
.../src/test/resources/application-test.properties | 1 -
5 files changed, 11 deletions(-)
diff --git a/docs/Configuration-Properties.md b/docs/Configuration-Properties.md
index 8386f166e..5211072db 100644
--- a/docs/Configuration-Properties.md
+++ b/docs/Configuration-Properties.md
@@ -9,7 +9,6 @@ The PowerAuth Server uses the following public configuration properties:
| `spring.datasource.url` | `jdbc:postgresql://localhost:5432/powerauth` | Database JDBC URL |
| `spring.datasource.username` | `powerauth` | Database JDBC username |
| `spring.datasource.password` | `_empty_` | Database JDBC password |
-| `spring.datasource.driver-class-name` | `org.postgresql.Driver` | Datasource JDBC class name |
| `spring.jpa.hibernate.ddl-auto` | `none` | Configuration of automatic database schema creation |
| `spring.jpa.properties.hibernate.connection.characterEncoding` | `utf8` | Character encoding |
| `spring.jpa.properties.hibernate.connection.useUnicode` | `true` | Character encoding - Unicode support |
diff --git a/docs/Deploying-PowerAuth-Server.md b/docs/Deploying-PowerAuth-Server.md
index 454c52c60..f1c389f14 100644
--- a/docs/Deploying-PowerAuth-Server.md
+++ b/docs/Deploying-PowerAuth-Server.md
@@ -42,7 +42,6 @@ The default database connectivity parameters in `powerauth-java-server.war` are
spring.datasource.url=jdbc:postgresql://localhost:5432/powerauth
spring.datasource.username=powerauth
spring.datasource.password=
-spring.datasource.driver-class-name=org.postgresql.Driver
spring.jpa.hibernate.ddl-auto=none
spring.jpa.properties.hibernate.connection.characterEncoding=utf8
spring.jpa.properties.hibernate.connection.useUnicode=true
@@ -57,7 +56,6 @@ For Oracle database use following connectivity parameters (example):
spring.datasource.url=jdbc:oracle:thin:@//[HOST]:[PORT]/[SERVICENAME]
spring.datasource.username=powerauth
spring.datasource.password=*********
-spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.jpa.hibernate.ddl-auto=none
```
@@ -68,7 +66,6 @@ For PostgreSQL use following connectivity parameters (example):
spring.datasource.url=jdbc:postgresql://[HOST]:[PORT]/[DATABASE]
spring.datasource.username=powerauth
spring.datasource.password=*********
-spring.datasource.driver-class-name=org.postgresql.Driver
spring.jpa.hibernate.ddl-auto=none
```
@@ -136,7 +133,6 @@ You can specify the individual properties directly in the server configuration.
-
```
@@ -157,7 +153,6 @@ To match the previous example, the contents of `/path/to/come/custom.properties`
spring.datasource.url=jdbc:postgresql://localhost:5432/powerauth
spring.datasource.username=powerauth
spring.datasource.password=
-spring.datasource.driver-class-name=org.postgresql.Driver
```
## Generating Your First Application
@@ -234,7 +229,6 @@ Some application servers, such as **WildFly** by JBoss, are very restrictive in
spring.datasource.url=
spring.datasource.username=
spring.datasource.password=
-spring.datasource.driver-class-name=
spring.jpa.database-platform=
spring.jpa.hibernate.ddl-auto=none
spring.datasource.jndi-name=java:/jdbc/powerauth
diff --git a/docs/Deploying-Wildfly.md b/docs/Deploying-Wildfly.md
index 7bc4751b3..0dd363c03 100644
--- a/docs/Deploying-Wildfly.md
+++ b/docs/Deploying-Wildfly.md
@@ -96,7 +96,6 @@ The `application-ext.properties` file is used to override default configuration
spring.datasource.url=jdbc:oracle:thin:@//[host]:[port]/[servicename]
spring.datasource.username=powerauth
spring.datasource.password=powerauth
-spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
# Application Service Configuration
powerauth.service.applicationEnvironment=TEST
diff --git a/powerauth-java-server/src/main/resources/application.properties b/powerauth-java-server/src/main/resources/application.properties
index af3f20178..7a6157e5c 100644
--- a/powerauth-java-server/src/main/resources/application.properties
+++ b/powerauth-java-server/src/main/resources/application.properties
@@ -24,7 +24,6 @@ spring.datasource.url=jdbc:postgresql://localhost:5432/powerauth
spring.datasource.username=powerauth
spring.datasource.password=
spring.datasource.hikari.auto-commit=false
-spring.datasource.driver-class-name=org.postgresql.Driver
spring.jpa.properties.hibernate.connection.characterEncoding=utf8
spring.jpa.properties.hibernate.connection.useUnicode=true
@@ -32,7 +31,6 @@ spring.jpa.properties.hibernate.connection.useUnicode=true
#spring.datasource.url=jdbc:oracle:thin:@//127.0.0.1:1521/powerauth
#spring.datasource.username=powerauth
#spring.datasource.password=
-#spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
# Hibernate Configuration
spring.jpa.hibernate.ddl-auto=none
diff --git a/powerauth-java-server/src/test/resources/application-test.properties b/powerauth-java-server/src/test/resources/application-test.properties
index 66fc7cd8f..e1d502878 100644
--- a/powerauth-java-server/src/test/resources/application-test.properties
+++ b/powerauth-java-server/src/test/resources/application-test.properties
@@ -20,7 +20,6 @@
spring.datasource.url=jdbc:h2:mem:powerauth;DB_CLOSE_ON_EXIT=FALSE
spring.datasource.username=sa
spring.datasource.password=
-spring.datasource.driver-class-name=org.h2.Driver
# Hibernate Configuration
spring.jpa.hibernate.ddl-auto=create-drop
From 50ade67be60f0e75acf7c1abae0d541d34514f71 Mon Sep 17 00:00:00 2001
From: Lubos Racansky
Date: Thu, 8 Feb 2024 13:36:19 +0100
Subject: [PATCH 074/146] Fix #1300: Handle explicit null JSON value in final
fields
---
.../client/model/request/OperationApproveRequest.java | 4 ++++
.../client/model/request/OperationCancelRequest.java | 4 ++++
.../client/model/request/OperationCreateRequest.java | 3 +++
.../client/model/request/OperationFailApprovalRequest.java | 4 ++++
.../client/model/request/OperationRejectRequest.java | 4 ++++
.../client/model/request/OperationTemplateCreateRequest.java | 3 +++
.../client/model/request/OperationTemplateUpdateRequest.java | 4 ++++
7 files changed, 26 insertions(+)
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/OperationApproveRequest.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/OperationApproveRequest.java
index 88ff11131..a27a82c45 100644
--- a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/OperationApproveRequest.java
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/OperationApproveRequest.java
@@ -18,6 +18,8 @@
package com.wultra.security.powerauth.client.model.request;
+import com.fasterxml.jackson.annotation.JsonSetter;
+import com.fasterxml.jackson.annotation.Nulls;
import com.wultra.security.powerauth.client.model.enumeration.SignatureType;
import lombok.Data;
@@ -37,6 +39,8 @@ public class OperationApproveRequest {
private String applicationId;
private String data;
private SignatureType signatureType;
+
+ @JsonSetter(nulls = Nulls.SKIP)
private final Map additionalData = new LinkedHashMap<>();
}
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/OperationCancelRequest.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/OperationCancelRequest.java
index 5a41ff0b5..9c9237ba0 100644
--- a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/OperationCancelRequest.java
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/OperationCancelRequest.java
@@ -18,6 +18,8 @@
package com.wultra.security.powerauth.client.model.request;
+import com.fasterxml.jackson.annotation.JsonSetter;
+import com.fasterxml.jackson.annotation.Nulls;
import lombok.Data;
import java.util.LinkedHashMap;
@@ -32,6 +34,8 @@
public class OperationCancelRequest {
private String operationId;
+
+ @JsonSetter(nulls = Nulls.SKIP)
private final Map additionalData = new LinkedHashMap<>();
}
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/OperationCreateRequest.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/OperationCreateRequest.java
index db54f6b8a..d6b5f1849 100644
--- a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/OperationCreateRequest.java
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/OperationCreateRequest.java
@@ -18,6 +18,8 @@
package com.wultra.security.powerauth.client.model.request;
+import com.fasterxml.jackson.annotation.JsonSetter;
+import com.fasterxml.jackson.annotation.Nulls;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@@ -50,6 +52,7 @@ public class OperationCreateRequest {
private String externalId;
@Schema(description = "Parameters of the operation, will be filled to the operation data", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
+ @JsonSetter(nulls = Nulls.SKIP)
private final Map parameters = new LinkedHashMap<>();
@Schema(description = "Whether proximity check should be used, overrides configuration from operation template", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/OperationFailApprovalRequest.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/OperationFailApprovalRequest.java
index 384ec2bcd..4f2ebbf8e 100644
--- a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/OperationFailApprovalRequest.java
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/OperationFailApprovalRequest.java
@@ -18,6 +18,8 @@
package com.wultra.security.powerauth.client.model.request;
+import com.fasterxml.jackson.annotation.JsonSetter;
+import com.fasterxml.jackson.annotation.Nulls;
import lombok.Data;
import java.util.LinkedHashMap;
@@ -32,6 +34,8 @@
public class OperationFailApprovalRequest {
private String operationId;
+
+ @JsonSetter(nulls = Nulls.SKIP)
private final Map additionalData = new LinkedHashMap<>();
}
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/OperationRejectRequest.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/OperationRejectRequest.java
index 935f9799e..ddc211ed5 100644
--- a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/OperationRejectRequest.java
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/OperationRejectRequest.java
@@ -18,6 +18,8 @@
package com.wultra.security.powerauth.client.model.request;
+import com.fasterxml.jackson.annotation.JsonSetter;
+import com.fasterxml.jackson.annotation.Nulls;
import lombok.Data;
import java.util.LinkedHashMap;
@@ -34,6 +36,8 @@ public class OperationRejectRequest {
private String operationId;
private String userId;
private String applicationId;
+
+ @JsonSetter(nulls = Nulls.SKIP)
private final Map additionalData = new LinkedHashMap<>();
}
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/OperationTemplateCreateRequest.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/OperationTemplateCreateRequest.java
index 4ae62528f..01eaf9ae2 100644
--- a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/OperationTemplateCreateRequest.java
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/OperationTemplateCreateRequest.java
@@ -18,6 +18,8 @@
package com.wultra.security.powerauth.client.model.request;
+import com.fasterxml.jackson.annotation.JsonSetter;
+import com.fasterxml.jackson.annotation.Nulls;
import com.wultra.security.powerauth.client.model.enumeration.SignatureType;
import lombok.Data;
@@ -50,6 +52,7 @@ public class OperationTemplateCreateRequest {
/**
* Allowed signature types.
*/
+ @JsonSetter(nulls = Nulls.SKIP)
private final List signatureType = new ArrayList<>();
/**
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/OperationTemplateUpdateRequest.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/OperationTemplateUpdateRequest.java
index e157ed93e..bb09e5b5b 100644
--- a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/OperationTemplateUpdateRequest.java
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/OperationTemplateUpdateRequest.java
@@ -18,6 +18,8 @@
package com.wultra.security.powerauth.client.model.request;
+import com.fasterxml.jackson.annotation.JsonSetter;
+import com.fasterxml.jackson.annotation.Nulls;
import com.wultra.security.powerauth.client.model.enumeration.SignatureType;
import lombok.Data;
@@ -35,6 +37,8 @@ public class OperationTemplateUpdateRequest {
private Long id;
private String operationType;
private String dataTemplate;
+
+ @JsonSetter(nulls = Nulls.SKIP)
private final List signatureType = new ArrayList<>();
private Long maxFailureCount;
private Long expiration;
From bdba08cf0a2fdc64804eaf816816a2f4b4de9ff9 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 19 Feb 2024 01:34:50 +0000
Subject: [PATCH 075/146] Bump net.javacrumbs.shedlock:shedlock-bom from 5.10.2
to 5.11.0
Bumps [net.javacrumbs.shedlock:shedlock-bom](https://github.com/lukas-krecan/ShedLock) from 5.10.2 to 5.11.0.
- [Commits](https://github.com/lukas-krecan/ShedLock/compare/shedlock-parent-5.10.2...shedlock-parent-5.11.0)
---
updated-dependencies:
- dependency-name: net.javacrumbs.shedlock:shedlock-bom
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot]
---
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index fdd733e44..eb8843bc3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -97,7 +97,7 @@
2.2.20
- 5.10.2
+ 5.11.0
1.11.0
From 845400af8ba500b4a4e8fcd54fdd98df9bbcafa0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Roman=20=C5=A0trobl?=
Date: Mon, 19 Feb 2024 22:50:59 +0800
Subject: [PATCH 076/146] Fix #1304: FIDO2: Use templateName instead of
operationType parameter for challenges (#1305)
---
.../client/model/request/fido2/AssertionChallengeRequest.java | 2 +-
.../fido2/rest/model/request/AssertionChallengeRequest.java | 3 ++-
.../com/wultra/powerauth/fido2/service/AssertionService.java | 2 +-
.../app/server/service/fido2/PowerAuthAssertionProvider.java | 4 ++--
.../com/wultra/powerauth/fido2/Fido2AuthenticatorTest.java | 4 ++--
5 files changed, 8 insertions(+), 7 deletions(-)
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/fido2/AssertionChallengeRequest.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/fido2/AssertionChallengeRequest.java
index 78cf11325..d5f7687d2 100644
--- a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/fido2/AssertionChallengeRequest.java
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/fido2/AssertionChallengeRequest.java
@@ -38,7 +38,7 @@ public class AssertionChallengeRequest {
private List<@NotBlank String> applicationIds;
private String externalId;
@NotBlank
- private String operationType;
+ private String templateName;
private Map parameters = new HashMap<>();
}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/AssertionChallengeRequest.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/AssertionChallengeRequest.java
index ddc5a19fc..07a91097d 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/AssertionChallengeRequest.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/request/AssertionChallengeRequest.java
@@ -33,11 +33,12 @@
*/
@Data
public class AssertionChallengeRequest {
+
@NotEmpty
private List<@NotBlank String> applicationIds;
private String externalId;
@NotBlank
- private String operationType;
+ private String templateName;
private Map parameters = new HashMap<>();
}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/AssertionService.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/AssertionService.java
index 0534b44be..fcde27cf7 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/AssertionService.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/AssertionService.java
@@ -78,7 +78,7 @@ public AssertionService(CryptographyService cryptographyService, AuthenticatorPr
*/
public AssertionChallengeResponse requestAssertionChallenge(AssertionChallengeRequest request) throws Exception {
final AssertionChallenge assertionChallenge = assertionProvider.provideChallengeForAssertion(
- request.getApplicationIds(), request.getOperationType(), request.getParameters(), request.getExternalId()
+ request.getApplicationIds(), request.getTemplateName(), request.getParameters(), request.getExternalId()
);
if (assertionChallenge == null) {
throw new Fido2AuthenticationFailedException("Unable to obtain challenge with provided parameters.");
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAssertionProvider.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAssertionProvider.java
index d8d19995d..ea47bcf84 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAssertionProvider.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAssertionProvider.java
@@ -73,10 +73,10 @@ public PowerAuthAssertionProvider(ServiceBehaviorCatalogue serviceBehaviorCatalo
@Override
@Transactional
- public AssertionChallenge provideChallengeForAssertion(List applicationIds, String operationType, Map parameters, String externalAuthenticationId) throws GenericServiceException {
+ public AssertionChallenge provideChallengeForAssertion(List applicationIds, String templateName, Map parameters, String externalAuthenticationId) throws GenericServiceException {
final OperationCreateRequest operationCreateRequest = new OperationCreateRequest();
operationCreateRequest.setApplications(applicationIds);
- operationCreateRequest.setTemplateName(operationType);
+ operationCreateRequest.setTemplateName(templateName);
operationCreateRequest.getParameters().putAll(parameters);
final OperationDetailResponse operationDetailResponse = serviceBehaviorCatalogue.getOperationBehavior().createOperation(operationCreateRequest);
diff --git a/powerauth-java-server/src/test/java/com/wultra/powerauth/fido2/Fido2AuthenticatorTest.java b/powerauth-java-server/src/test/java/com/wultra/powerauth/fido2/Fido2AuthenticatorTest.java
index ee478485a..991a41d14 100644
--- a/powerauth-java-server/src/test/java/com/wultra/powerauth/fido2/Fido2AuthenticatorTest.java
+++ b/powerauth-java-server/src/test/java/com/wultra/powerauth/fido2/Fido2AuthenticatorTest.java
@@ -228,7 +228,7 @@ public void packedAuthenticatorInvalidSignatureTest() throws Exception {
// Obtain authentication challenge from PowerAuth server
final AssertionChallengeRequest challengeRequest = new AssertionChallengeRequest();
challengeRequest.setApplicationIds(Collections.singletonList(APPLICATION_ID));
- challengeRequest.setOperationType("login");
+ challengeRequest.setTemplateName("login");
challengeRequest.setExternalId(UUID.randomUUID().toString());
final AssertionChallengeResponse challengeResponse = assertionService.requestAssertionChallenge(challengeRequest);
assertEquals(APPLICATION_ID, challengeResponse.getApplicationIds().get(0));
@@ -375,7 +375,7 @@ private void authenticate() throws Exception {
// Obtain authentication challenge from PowerAuth server
final AssertionChallengeRequest challengeRequest = new AssertionChallengeRequest();
challengeRequest.setApplicationIds(Collections.singletonList(APPLICATION_ID));
- challengeRequest.setOperationType("login");
+ challengeRequest.setTemplateName("login");
challengeRequest.setExternalId(UUID.randomUUID().toString());
final AssertionChallengeResponse challengeResponse = assertionService.requestAssertionChallenge(challengeRequest);
assertEquals(APPLICATION_ID, challengeResponse.getApplicationIds().get(0));
From afeda68ae5a43c84fdbf671e2ed7a55c804801c6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Lubo=C5=A1=20Ra=C4=8Dansk=C3=BD?=
Date: Wed, 21 Feb 2024 10:33:48 +0100
Subject: [PATCH 077/146] Fix #1306: Failing tests when Tomcat is running on
port 8080 (#1307)
* Fix #1306: Failing tests when Tomcat is running on port 8080
---
.../api/PowerAuthControllerTest.java | 33 ++++++++++++-------
.../api/PowerAuthControllerTestConfig.java | 24 +-------------
2 files changed, 22 insertions(+), 35 deletions(-)
diff --git a/powerauth-java-server/src/test/java/io/getlime/security/powerauth/app/server/controller/api/PowerAuthControllerTest.java b/powerauth-java-server/src/test/java/io/getlime/security/powerauth/app/server/controller/api/PowerAuthControllerTest.java
index a8de53129..8d14a05ec 100644
--- a/powerauth-java-server/src/test/java/io/getlime/security/powerauth/app/server/controller/api/PowerAuthControllerTest.java
+++ b/powerauth-java-server/src/test/java/io/getlime/security/powerauth/app/server/controller/api/PowerAuthControllerTest.java
@@ -24,6 +24,7 @@
import com.wultra.security.powerauth.client.model.error.PowerAuthClientException;
import com.wultra.security.powerauth.client.model.request.*;
import com.wultra.security.powerauth.client.model.response.*;
+import com.wultra.security.powerauth.rest.client.PowerAuthRestClient;
import io.getlime.security.powerauth.app.server.service.model.request.ActivationLayer2Request;
import io.getlime.security.powerauth.crypto.lib.encryptor.ClientEncryptor;
import io.getlime.security.powerauth.crypto.lib.encryptor.EncryptorFactory;
@@ -35,9 +36,12 @@
import io.getlime.security.powerauth.crypto.lib.encryptor.model.v3.ServerEncryptorSecrets;
import io.getlime.security.powerauth.crypto.lib.generator.KeyGenerator;
import io.getlime.security.powerauth.crypto.lib.util.KeyConvertor;
-import org.junit.jupiter.api.*;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.transaction.annotation.Transactional;
@@ -60,13 +64,17 @@
*
* @author Lubos Racansky, lubos.racansky@wultra.com
*/
-@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles("test")
@Transactional
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class PowerAuthControllerTest {
- @Autowired
+ private static final String POWERAUTH_REST_URL = "http://localhost:%d/rest";
+
+ @LocalServerPort
+ private int serverPort;
+
private PowerAuthClient powerAuthClient;
@Autowired
@@ -78,6 +86,7 @@ class PowerAuthControllerTest {
@BeforeAll
void initializeData() throws Exception {
+ powerAuthClient = new PowerAuthRestClient(POWERAUTH_REST_URL.formatted(serverPort));
createApplication();
createLoginOperationTemplate();
}
@@ -1112,7 +1121,7 @@ private CreateCallbackUrlRequest createCallbackUrlRequest() {
*
* @throws Exception if any error occurs during activation initialization or verification
*/
- protected void initActivation() throws Exception {
+ private void initActivation() throws Exception {
final InitActivationRequest initActivationRequest = new InitActivationRequest();
initActivationRequest.setUserId(PowerAuthControllerTestConfig.USER_ID);
initActivationRequest.setApplicationId(config.getApplicationId());
@@ -1143,7 +1152,7 @@ protected void initActivation() throws Exception {
*
* @throws Exception if any error occurs during application creation or setup
*/
- protected void createApplication() throws Exception {
+ private void createApplication() throws Exception {
final GetApplicationListResponse applicationsListResponse = powerAuthClient.getApplicationList();
final var applicationOptional = applicationsListResponse.getApplications().stream()
.filter(app -> app.getApplicationId().equals(config.getApplicationName()))
@@ -1204,7 +1213,7 @@ protected void createApplication() throws Exception {
* @return the response containing the created callback URL details
* @throws Exception if any error occurs during callback URL creation
*/
- protected CreateCallbackUrlResponse createCallback() throws Exception {
+ private CreateCallbackUrlResponse createCallback() throws Exception {
final CreateCallbackUrlRequest callbackUrlRequest = createCallbackUrlRequest();
final CreateCallbackUrlResponse response = powerAuthClient.createCallbackUrl(callbackUrlRequest);
assertEquals(PowerAuthControllerTestConfig.CALLBACK_NAME, response.getName());
@@ -1223,7 +1232,7 @@ protected CreateCallbackUrlResponse createCallback() throws Exception {
* @param callbackId the ID of the callback URL to be removed
* @throws Exception if any error occurs during callback URL removal
*/
- protected void removeCallback(final String callbackId) throws Exception {
+ private void removeCallback(final String callbackId) throws Exception {
final RemoveCallbackUrlRequest removeCallbackUrlRequest = new RemoveCallbackUrlRequest();
removeCallbackUrlRequest.setId(callbackId);
@@ -1240,7 +1249,7 @@ protected void removeCallback(final String callbackId) throws Exception {
*
* @throws Exception if any error occurs during activation removal
*/
- protected void removeActivation() throws Exception {
+ private void removeActivation() throws Exception {
final RemoveActivationRequest removeActivationRequest = new RemoveActivationRequest();
removeActivationRequest.setActivationId(config.getActivationId());
final RemoveActivationResponse removeActivationResponse = powerAuthClient.removeActivation(removeActivationRequest);
@@ -1257,7 +1266,7 @@ protected void removeActivation() throws Exception {
* @return the response containing the created operation details
* @throws Exception if any error occurs during operation creation
*/
- protected OperationDetailResponse createOperation() throws Exception {
+ private OperationDetailResponse createOperation() throws Exception {
final OperationDetailResponse operationDetailResponse = powerAuthClient
.createOperation(createOperationCreateRequest(false));
assertNotNull(operationDetailResponse.getId());
@@ -1275,7 +1284,7 @@ protected OperationDetailResponse createOperation() throws Exception {
*
* @throws Exception if any error occurs during operation template creation
*/
- protected void createLoginOperationTemplate() throws Exception {
+ private void createLoginOperationTemplate() throws Exception {
final OperationTemplateCreateRequest request = new OperationTemplateCreateRequest();
request.setTemplateName(UUID.randomUUID().toString());
request.setOperationType("login");
@@ -1298,7 +1307,7 @@ protected void createLoginOperationTemplate() throws Exception {
* @return The {@link PublicKey} object corresponding to the decoded master public key.
* @throws Exception if there is an error during the conversion process.
*/
- protected PublicKey wrapPublicKeyString() throws Exception {
+ private PublicKey wrapPublicKeyString() throws Exception {
return keyConvertor.convertBytesToPublicKey(Base64.getDecoder().decode(config.getMasterPublicKey()));
}
@@ -1320,7 +1329,7 @@ protected PublicKey wrapPublicKeyString() throws Exception {
* @return The {@link EncryptedRequest} containing the encrypted request data.
* @throws Exception if there is an error during the encryption or serialization process.
*/
- protected EncryptedRequest generateEncryptedRequestActivationLayer(final String activationName) throws Exception {
+ private EncryptedRequest generateEncryptedRequestActivationLayer(final String activationName) throws Exception {
final KeyPair keyPair = keyGenerator.generateKeyPair();
final PublicKey publicKey = keyPair.getPublic();
final byte[] publicKeyBytes = keyConvertor.convertPublicKeyToBytes(publicKey);
diff --git a/powerauth-java-server/src/test/java/io/getlime/security/powerauth/app/server/controller/api/PowerAuthControllerTestConfig.java b/powerauth-java-server/src/test/java/io/getlime/security/powerauth/app/server/controller/api/PowerAuthControllerTestConfig.java
index a6675d81f..ece409faa 100644
--- a/powerauth-java-server/src/test/java/io/getlime/security/powerauth/app/server/controller/api/PowerAuthControllerTestConfig.java
+++ b/powerauth-java-server/src/test/java/io/getlime/security/powerauth/app/server/controller/api/PowerAuthControllerTestConfig.java
@@ -17,14 +17,10 @@
*/
package io.getlime.security.powerauth.app.server.controller.api;
-import com.wultra.security.powerauth.client.PowerAuthClient;
-import com.wultra.security.powerauth.rest.client.PowerAuthRestClient;
-import com.wultra.security.powerauth.rest.client.PowerAuthRestClientConfiguration;
import lombok.Data;
-import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
-import java.util.*;
+import java.util.UUID;
/**
* Configuration class for PowerAuth Controller tests.
@@ -41,7 +37,6 @@
@Data
public class PowerAuthControllerTestConfig {
- private static final String POWERAUTH_REST_URL = "http://localhost:8080/rest";
protected static final String PUBLIC_KEY_RECOVERY_POSTCARD_BASE64 = "BABXgGoj4Lizl3GN0rjrtileEEwekFkpX1ERS9yyYjyuM1Iqdti3ihtATBxk5XGvjetPO1YC+qXciUYjIsETtbI=";
protected static final String USER_ID = "test-user";
protected static final String DATA = "A2";
@@ -62,21 +57,4 @@ public class PowerAuthControllerTestConfig {
private String activationCode;
private String activationName;
- /**
- * Creates and configures a new {@link PowerAuthClient} bean.
- *
- * The method configures and returns a PowerAuthClient instance for interacting with
- * the PowerAuth Server. It sets up the client with the necessary configurations such as
- * accepting invalid SSL certificates for testing purposes.
- *
- * @return A configured instance of PowerAuthClient
- * @throws Exception if there is an issue creating the PowerAuthClient instance
- */
- @Bean
- public PowerAuthClient powerAuthClient() throws Exception {
- final PowerAuthRestClientConfiguration config = new PowerAuthRestClientConfiguration();
- config.setAcceptInvalidSslCertificate(true);
- return new PowerAuthRestClient(POWERAUTH_REST_URL);
- }
-
}
From 163a0aa9557cd8343059d19d8fda2690ce1ff847 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Roman=20=C5=A0trobl?=
Date: Wed, 21 Feb 2024 17:42:48 +0800
Subject: [PATCH 078/146] Fix #1299: FIDO2: Check AAGUID during registration
(#1302)
---
docs/Database-Structure.md | 27 +++
docs/PowerAuth-Server-1.7.0.md | 12 ++
docs/WebServices-Methods.md | 79 ++++++++
.../1.7.x/20240115-add-columns-fido2.xml | 2 +-
.../1.7.x/20240212-application-config.xml | 68 +++++++
.../1.7.x/db.changelog-version.xml | 1 +
docs/sql/mssql/migration_1.6.0_1.7.0.sql | 15 ++
docs/sql/oracle/migration_1.6.0_1.7.0.sql | 12 ++
docs/sql/postgresql/migration_1.6.0_1.7.0.sql | 11 +
.../powerauth/client/PowerAuthClient.java | 75 +++++++
.../entity/ApplicationConfigurationItem.java | 36 ++++
.../CreateApplicationConfigRequest.java | 41 ++++
.../request/GetApplicationConfigRequest.java | 35 ++++
.../RemoveApplicationConfigRequest.java | 37 ++++
.../CreateApplicationConfigResponse.java | 38 ++++
.../GetApplicationConfigResponse.java | 38 ++++
.../model/enumeration/Fido2ConfigKeys.java | 32 +++
.../fido2/service/RegistrationService.java | 17 +-
.../provider/RegistrationProvider.java | 10 +
.../controller/api/PowerAuthController.java | 37 ++++
.../server/database/RepositoryCatalogue.java | 9 +-
.../model/converter/ListToJsonConverter.java | 82 ++++++++
.../model/entity/ApplicationConfigEntity.java | 107 ++++++++++
.../model/entity/ApplicationEntity.java | 1 -
.../model/entity/SignatureEntity.java | 1 -
.../ApplicationConfigRepository.java | 43 ++++
.../app/server/service/PowerAuthService.java | 46 +++++
.../behavior/ServiceBehaviorCatalogue.java | 11 +
.../tasks/ActivationFlagsServiceBehavior.java | 2 +-
.../tasks/ActivationServiceBehavior.java | 2 +-
.../ApplicationConfigServiceBehavior.java | 190 ++++++++++++++++++
.../ApplicationRolesServiceBehavior.java | 2 +-
.../service/behavior/tasks/TokenBehavior.java | 2 +-
.../tasks/VaultUnlockServiceBehavior.java | 2 +-
.../fido2/PowerAuthRegistrationProvider.java | 44 ++++
.../fido2/Fido2AuthenticatorTest.java | 123 +++++++++++-
.../rest/client/PowerAuthRestClient.java | 54 +++++
37 files changed, 1330 insertions(+), 14 deletions(-)
create mode 100644 docs/db/changelog/changesets/powerauth-java-server/1.7.x/20240212-application-config.xml
create mode 100644 powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/ApplicationConfigurationItem.java
create mode 100644 powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/CreateApplicationConfigRequest.java
create mode 100644 powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/GetApplicationConfigRequest.java
create mode 100644 powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/RemoveApplicationConfigRequest.java
create mode 100644 powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/response/CreateApplicationConfigResponse.java
create mode 100644 powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/response/GetApplicationConfigResponse.java
create mode 100644 powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/enumeration/Fido2ConfigKeys.java
create mode 100644 powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/converter/ListToJsonConverter.java
create mode 100644 powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/ApplicationConfigEntity.java
create mode 100644 powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/repository/ApplicationConfigRepository.java
create mode 100644 powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/ApplicationConfigServiceBehavior.java
diff --git a/docs/Database-Structure.md b/docs/Database-Structure.md
index 31d5cbb2e..ad1f8d9a8 100644
--- a/docs/Database-Structure.md
+++ b/docs/Database-Structure.md
@@ -86,6 +86,33 @@ CREATE TABLE pa_application_version
| supported | INT(11) | - | Flag indicating if this version is supported or not (0 = not supported, 1..N = supported) |
+
+### Application Configuration Table
+
+Stores configurations for the applications stored in `pa_application` table.
+
+#### Schema
+
+```sql
+CREATE TABLE pa_application_config
+(
+ id INTEGER NOT NULL PRIMARY KEY,
+ application_id INTEGER NOT NULL,
+ config_key VARCHAR(255),
+ config_values TEXT
+);
+```
+
+#### Columns
+
+| Name | Type | Info | Note |
+|------|------|---------|-----------------------------------------------------------------------------------------------------------------------------------------|
+| id | BIGINT(20) | primary key, autoincrement | Unique application configuration identifier. |
+| application_id | BIGINT(20) | foreign key: pa\_application.id | Related application ID. |
+| config_key | VARCHAR(255) | index | Configuration key names such as `fido2_attestation_fmt_allowed` and `fido2_aaguids_allowed`. |
+| config_values | TEXT | - | Configuration values serialized in JSON format. |
+
+
### Activations Table
diff --git a/docs/PowerAuth-Server-1.7.0.md b/docs/PowerAuth-Server-1.7.0.md
index ff4879b28..90083ef11 100644
--- a/docs/PowerAuth-Server-1.7.0.md
+++ b/docs/PowerAuth-Server-1.7.0.md
@@ -19,3 +19,15 @@ Following columns have been added to table `pa_activation` for FIDO2 support:
- `protocol` - protocol enumeration: `powerauth` or `fido2`
The data type for column `extras` in table `pa_activation` was changed to `TEXT` / `CLOB` to support larger data.
+
+### New Database Table for Application Configuration
+
+A new database table `pa_application_config` has been added:
+- `id` - application configuration row identifier
+- `application_id` - application identifier
+- `config_key` - configuration key
+- `config_values` - list of configuration values
+
+Following parameters can be configured:
+- `fido2_attestation_fmt_allowed` - allowed attestation formats for FIDO2 registrations, unset value means all attestation formats are allowed
+- `fido2_aaguids_allowed` - allowed AAGUIDs for FIDO2 registration, unset value means all AAGUIDs are allowed
diff --git a/docs/WebServices-Methods.md b/docs/WebServices-Methods.md
index c6d64ba8a..9ee46c9f8 100644
--- a/docs/WebServices-Methods.md
+++ b/docs/WebServices-Methods.md
@@ -19,6 +19,9 @@ The following `v3` methods are published using the service:
- [createApplicationVersion](#method-createapplicationversion)
- [unsupportApplicationVersion](#method-unsupportapplicationversion)
- [supportApplicationVersion](#method-supportapplicationversion)
+ - [getApplicationConfig](#method-getapplicationconfig)
+ - [createApplicationConfig](#method-createapplicationconfig)
+ - [removeApplicationConfig](#method-removeapplicationconfig)
- Activation Management
- [getActivationListForUser](#method-getactivationlistforuser)
- [initActivation](#method-initactivation)
@@ -343,6 +346,82 @@ REST endpoint: `POST /rest/v3/application/version/support`
| `String` | `applicationVersionId` | An identifier of an application version |
| `Boolean` | `supported` | Flag indicating if this application is supported |
+### Method 'getApplicationConfig'
+
+Get application configuration detail.
+
+#### Request
+
+REST endpoint: `POST /rest/v3/application/config/detail`
+
+`GetApplicationConfigRequest`
+
+| Type | Name | Description |
+|----------|------|-------------|
+| `String` | `applicationId` | An identifier of an application |
+
+#### Response
+
+`GetApplicationConfigResponse`
+
+| Type | Name | Description |
+|-----------|------|-------------|
+| `String` | `applicationId` | An identifier of an application |
+| `List` | `applicationConfigs` | List of application configurations |
+
+The `ApplicationConfigurationItem` record contains following parameters:
+ - `String key` - configuration key name
+ - `List values` - configuration values
+
+### Method 'createApplicationConfig'
+
+Create application configuration.
+
+#### Request
+
+REST endpoint: `POST /rest/v3/application/config/create`
+
+`CreateApplicationConfigRequest`
+
+| Type | Name | Description |
+|----------|------|-------------|
+| `String` | `applicationId` | An identifier of an application |
+| `String` | `key` | Application configuration key name |
+| `List` | `values` | Application configuration values |
+
+Following configuration keys are accepted:
+- `fido2_attestation_fmt_allowed` - allowed attestation formats for FIDO2 registrations, unset value means all attestation formats are allowed
+- `fido2_aaguids_allowed` - allowed AAGUIDs for FIDO2 registration, unset value means all AAGUIDs are allowed
+
+#### Response
+
+`CreateApplicationConfigResponse`
+
+| Type | Name | Description |
+|----------|------|-------------|
+| `String` | `applicationId` | An identifier of an application |
+| `String` | `key` | Application configuration key name |
+| `List` | `values` | Application configuration values |
+
+### Method 'removeApplicationConfig'
+
+Delete an application configuration.
+
+#### Request
+
+REST endpoint: `POST /rest/v3/application/config/remove`
+
+`RemoveApplicationConfigRequest`
+
+| Type | Name | Description |
+|----------|------|-------------|
+| `String` | `applicationId` | An identifier of an application |
+| `String` | `key` | Application configuration key name |
+
+#### Response
+
+_empty response_
+
## Activation management
Methods related to activation management.
diff --git a/docs/db/changelog/changesets/powerauth-java-server/1.7.x/20240115-add-columns-fido2.xml b/docs/db/changelog/changesets/powerauth-java-server/1.7.x/20240115-add-columns-fido2.xml
index 125b77a3e..e42b7e6af 100644
--- a/docs/db/changelog/changesets/powerauth-java-server/1.7.x/20240115-add-columns-fido2.xml
+++ b/docs/db/changelog/changesets/powerauth-java-server/1.7.x/20240115-add-columns-fido2.xml
@@ -1,7 +1,7 @@
+
+
+
+
+
+
+
+
+
+
+ Create a new table pa_application_config
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Create a new index on pa_application_config(key)
+
+
+
+
+
+
+
+
+
+
+
+ Create a new sequence pa_app_conf_seq
+
+
+
+
diff --git a/docs/db/changelog/changesets/powerauth-java-server/1.7.x/db.changelog-version.xml b/docs/db/changelog/changesets/powerauth-java-server/1.7.x/db.changelog-version.xml
index 3adca5752..0838ac2b2 100644
--- a/docs/db/changelog/changesets/powerauth-java-server/1.7.x/db.changelog-version.xml
+++ b/docs/db/changelog/changesets/powerauth-java-server/1.7.x/db.changelog-version.xml
@@ -4,5 +4,6 @@
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.9.xsd">
+
diff --git a/docs/sql/mssql/migration_1.6.0_1.7.0.sql b/docs/sql/mssql/migration_1.6.0_1.7.0.sql
index 25a5b4719..67faff720 100644
--- a/docs/sql/mssql/migration_1.6.0_1.7.0.sql
+++ b/docs/sql/mssql/migration_1.6.0_1.7.0.sql
@@ -11,3 +11,18 @@ GO
-- Changeset powerauth-java-server/1.7.x/20240115-add-columns-fido2::3::Roman Strobl
ALTER TABLE pa_activation ALTER COLUMN extras varchar (max);
GO
+
+-- Changeset powerauth-java-server/1.7.x/20240212-application-config.xml::1::Roman Strobl
+-- Create a new table pa_application_config
+CREATE TABLE pa_application_config (id int NOT NULL, application_id int NOT NULL, config_key varchar(255) NOT NULL, config_values varchar (max), CONSTRAINT PK_PA_APPLICATION_CONFIG PRIMARY KEY (id), CONSTRAINT pa_app_config_app_fk FOREIGN KEY (application_id) REFERENCES pa_application(id));
+GO
+
+-- Changeset powerauth-java-server/1.7.x/20240212-application-config.xml::2::Roman Strobl
+-- Create a new index on pa_application_config(key)
+CREATE NONCLUSTERED INDEX pa_app_config_key_idx ON pa_application_config([key]);
+GO
+
+-- Changeset powerauth-java-server/1.7.x/20240212-application-config.xml::3::Lubos Racansky
+-- Create a new sequence pa_app_conf_seq
+CREATE SEQUENCE pa_app_conf_seq START WITH 1 INCREMENT BY 1;
+GO
\ No newline at end of file
diff --git a/docs/sql/oracle/migration_1.6.0_1.7.0.sql b/docs/sql/oracle/migration_1.6.0_1.7.0.sql
index 7cf04bcd1..760ac1e1b 100644
--- a/docs/sql/oracle/migration_1.6.0_1.7.0.sql
+++ b/docs/sql/oracle/migration_1.6.0_1.7.0.sql
@@ -8,3 +8,15 @@ ALTER TABLE pa_activation ADD protocol VARCHAR2(32) DEFAULT 'powerauth';
-- Changeset powerauth-java-server/1.7.x/20240115-add-columns-fido2::3::Roman Strobl
ALTER TABLE pa_activation MODIFY extras CLOB;
+
+-- Changeset powerauth-java-server/1.7.x/20240212-application-config.xml::1::Roman Strobl
+-- Create a new table pa_application_config
+CREATE TABLE pa_application_config (id INTEGER NOT NULL, application_id INTEGER NOT NULL, config_key VARCHAR2(255) NOT NULL, config_values CLOB, CONSTRAINT PK_PA_APPLICATION_CONFIG PRIMARY KEY (id), CONSTRAINT pa_app_config_app_fk FOREIGN KEY (application_id) REFERENCES pa_application(id));
+
+-- Changeset powerauth-java-server/1.7.x/20240212-application-config.xml::2::Roman Strobl
+-- Create a new index on pa_application_config(key)
+CREATE INDEX pa_app_config_key_idx ON pa_application_config(key);
+
+-- Changeset powerauth-java-server/1.7.x/20240212-application-config.xml::3::Lubos Racansky
+-- Create a new sequence pa_app_conf_seq
+CREATE SEQUENCE pa_app_conf_seq START WITH 1 INCREMENT BY 1 CACHE 20;
diff --git a/docs/sql/postgresql/migration_1.6.0_1.7.0.sql b/docs/sql/postgresql/migration_1.6.0_1.7.0.sql
index 4d022a85f..b1fb025ba 100644
--- a/docs/sql/postgresql/migration_1.6.0_1.7.0.sql
+++ b/docs/sql/postgresql/migration_1.6.0_1.7.0.sql
@@ -9,3 +9,14 @@ ALTER TABLE pa_activation ADD protocol VARCHAR(32) DEFAULT 'powerauth';
-- Changeset powerauth-java-server/1.7.x/20240115-add-columns-fido2::3::Roman Strobl
ALTER TABLE pa_activation ALTER COLUMN extras TYPE TEXT USING (extras::TEXT);
+-- Changeset powerauth-java-server/1.7.x/20240212-application-config.xml::1::Roman Strobl
+-- Create a new table pa_application_config
+CREATE TABLE pa_application_config (id INTEGER NOT NULL, application_id INTEGER NOT NULL, config_key VARCHAR(255) NOT NULL, config_values TEXT, CONSTRAINT pa_application_config_pkey PRIMARY KEY (id), CONSTRAINT pa_app_config_app_fk FOREIGN KEY (application_id) REFERENCES pa_application(id));
+
+-- Changeset powerauth-java-server/1.7.x/20240212-application-config.xml::2::Roman Strobl
+-- Create a new index on pa_application_config(key)
+CREATE INDEX pa_app_config_key_idx ON pa_application_config(key);
+
+-- Changeset powerauth-java-server/1.7.x/20240212-application-config.xml::3::Lubos Racansky
+-- Create a new sequence pa_app_conf_seq
+CREATE SEQUENCE IF NOT EXISTS pa_app_conf_seq START WITH 1 INCREMENT BY 1 CACHE 20;
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/PowerAuthClient.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/PowerAuthClient.java
index 77e26fe12..159ba8157 100644
--- a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/PowerAuthClient.java
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/PowerAuthClient.java
@@ -2135,4 +2135,79 @@ RecoveryCodeActivationResponse createActivationUsingRecoveryCode(String recovery
*/
TelemetryReportResponse requestTelemetryReport(TelemetryReportRequest request, MultiValueMap queryParams, MultiValueMap httpHeaders) throws PowerAuthClientException;
+ /**
+ * Create an application configuration.
+ * @param request Create application configuration request.
+ * @return Create application configuration response.
+ * @throws PowerAuthClientException In case REST API call fails.
+ */
+ CreateApplicationConfigResponse createApplicationConfig(CreateApplicationConfigRequest request) throws PowerAuthClientException;
+
+ /**
+ * Create an application configuration.
+ * @param request Create application configuration request.
+ * @return Create application configuration response.
+ * @throws PowerAuthClientException In case REST API call fails.
+ */
+ CreateApplicationConfigResponse createApplicationConfig(CreateApplicationConfigRequest request, MultiValueMap queryParams, MultiValueMap httpHeaders) throws PowerAuthClientException;
+
+ /**
+ * Create an application configuration.
+ * @param applicationId Application identifier.
+ * @param key Configuration key.
+ * @param values Configuration values.
+ * @return Create application configuration response.
+ * @throws PowerAuthClientException In case REST API call fails.
+ */
+ CreateApplicationConfigResponse createApplicationConfig(String applicationId, String key, List values) throws PowerAuthClientException;
+
+ /**
+ * Remove an application configuration record.
+ * @param request Remove application configuration request.
+ * @return Remove application configuration response.
+ * @throws PowerAuthClientException In case REST API call fails.
+ */
+ Response removeApplicationConfig(RemoveApplicationConfigRequest request) throws PowerAuthClientException;
+
+ /**
+ * Remove an application configuration record.
+ * @param request Remove application configuration request.
+ * @return Remove application configuration response.
+ * @throws PowerAuthClientException In case REST API call fails.
+ */
+ Response removeApplicationConfig(RemoveApplicationConfigRequest request, MultiValueMap queryParams, MultiValueMap httpHeaders) throws PowerAuthClientException;
+
+ /**
+ * Remove an application configuration record.
+ * @param applicationId Application identifier.
+ * @param key Configuration key.
+ * @return Response.
+ * @throws PowerAuthClientException In case REST API call fails.
+ */
+ Response removeApplicationConfig(String applicationId, String key) throws PowerAuthClientException;
+
+ /**
+ * Get application configuration.
+ * @param request Get application configuration request.
+ * @return Application configuration response.
+ * @throws PowerAuthClientException In case REST API call fails.
+ */
+ GetApplicationConfigResponse getApplicationConfig(GetApplicationConfigRequest request) throws PowerAuthClientException;
+
+ /**
+ * Get application configuration.
+ * @param request Get application configuration request.
+ * @return Application configuration response.
+ * @throws PowerAuthClientException In case REST API call fails.
+ */
+ GetApplicationConfigResponse getApplicationConfig(GetApplicationConfigRequest request, MultiValueMap queryParams, MultiValueMap httpHeaders) throws PowerAuthClientException;
+
+ /**
+ * Get application configuration.
+ * @param applicationId Application identifier.
+ * @return Application configuration response.
+ * @throws PowerAuthClientException In case REST API call fails.
+ */
+ GetApplicationConfigResponse getApplicationConfig(String applicationId) throws PowerAuthClientException;
+
}
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/ApplicationConfigurationItem.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/ApplicationConfigurationItem.java
new file mode 100644
index 000000000..f096ab4d7
--- /dev/null
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/entity/ApplicationConfigurationItem.java
@@ -0,0 +1,36 @@
+/*
+ * PowerAuth Server 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 com.wultra.security.powerauth.client.model.entity;
+
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Model class representing an application configuration record.
+ *
+ * @author Roman Strobl, roman.strobl@wultra.com
+ */
+@Data
+public class ApplicationConfigurationItem {
+
+ private String key;
+ private List values = new ArrayList<>();
+
+}
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/CreateApplicationConfigRequest.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/CreateApplicationConfigRequest.java
new file mode 100644
index 000000000..b6cb372fe
--- /dev/null
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/CreateApplicationConfigRequest.java
@@ -0,0 +1,41 @@
+/*
+ * PowerAuth Server 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 com.wultra.security.powerauth.client.model.request;
+
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Model class representing request for adding an application configuration.
+ *
+ * @author Roman Strobl, roman.strobl@wultra.com
+ */
+@Data
+public class CreateApplicationConfigRequest {
+
+ @NotBlank
+ private String applicationId;
+ @NotBlank
+ private String key;
+ private List values = new ArrayList<>();
+
+}
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/GetApplicationConfigRequest.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/GetApplicationConfigRequest.java
new file mode 100644
index 000000000..2a2f0b136
--- /dev/null
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/GetApplicationConfigRequest.java
@@ -0,0 +1,35 @@
+/*
+ * PowerAuth Server 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 com.wultra.security.powerauth.client.model.request;
+
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+/**
+ * Model class representing request for obtaining application configuration.
+ *
+ * @author Roman Strobl, roman.strobl@wultra.com
+ */
+@Data
+public class GetApplicationConfigRequest {
+
+ @NotBlank
+ private String applicationId;
+
+}
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/RemoveApplicationConfigRequest.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/RemoveApplicationConfigRequest.java
new file mode 100644
index 000000000..a2394be23
--- /dev/null
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/RemoveApplicationConfigRequest.java
@@ -0,0 +1,37 @@
+/*
+ * PowerAuth Server 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 com.wultra.security.powerauth.client.model.request;
+
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+/**
+ * Model class representing request for removing an application configuration.
+ *
+ * @author Roman Strobl, roman.strobl@wultra.com
+ */
+@Data
+public class RemoveApplicationConfigRequest {
+
+ @NotBlank
+ private String applicationId;
+ @NotBlank
+ private String key;
+
+}
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/response/CreateApplicationConfigResponse.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/response/CreateApplicationConfigResponse.java
new file mode 100644
index 000000000..8cfa7d31a
--- /dev/null
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/response/CreateApplicationConfigResponse.java
@@ -0,0 +1,38 @@
+/*
+ * PowerAuth Server 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 com.wultra.security.powerauth.client.model.response;
+
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Model class representing response for creating an application configuration.
+ *
+ * @author Roman Strobl, roman.strobl@wultra.com
+ */
+@Data
+public class CreateApplicationConfigResponse {
+
+ private String applicationId;
+ private String key;
+ private List values = new ArrayList<>();
+
+}
diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/response/GetApplicationConfigResponse.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/response/GetApplicationConfigResponse.java
new file mode 100644
index 000000000..191f1182c
--- /dev/null
+++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/response/GetApplicationConfigResponse.java
@@ -0,0 +1,38 @@
+/*
+ * PowerAuth Server 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 com.wultra.security.powerauth.client.model.response;
+
+import com.wultra.security.powerauth.client.model.entity.ApplicationConfigurationItem;
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Model class representing response for obtaining an application configuration.
+ *
+ * @author Roman Strobl, roman.strobl@wultra.com
+ */
+@Data
+public class GetApplicationConfigResponse {
+
+ private String applicationId;
+ private List applicationConfigs = new ArrayList<>();
+
+}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/enumeration/Fido2ConfigKeys.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/enumeration/Fido2ConfigKeys.java
new file mode 100644
index 000000000..12a31c990
--- /dev/null
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/enumeration/Fido2ConfigKeys.java
@@ -0,0 +1,32 @@
+/*
+ * PowerAuth Server 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 com.wultra.powerauth.fido2.rest.model.enumeration;
+
+/**
+ * FIDO2 configuration key strings.
+ *
+ * @author Roman Strobl, roman.strobl@wultra.com
+ */
+public class Fido2ConfigKeys {
+
+ public static final String CONFIG_KEY_ALLOWED_ATTESTATION_FMT = "fido2_attestation_fmt_allowed";
+ public static final String CONFIG_KEY_ALLOWED_AAGUIDS = "fido2_aaguids_allowed";
+
+}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/RegistrationService.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/RegistrationService.java
index 5d8f20c5b..7aa8c1474 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/RegistrationService.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/RegistrationService.java
@@ -22,6 +22,7 @@
import com.wultra.powerauth.fido2.rest.model.converter.RegistrationChallengeConverter;
import com.wultra.powerauth.fido2.rest.model.converter.RegistrationConverter;
import com.wultra.powerauth.fido2.rest.model.entity.*;
+import com.wultra.powerauth.fido2.rest.model.enumeration.Fmt;
import com.wultra.powerauth.fido2.rest.model.request.RegistrationRequest;
import com.wultra.powerauth.fido2.rest.model.response.RegisteredAuthenticatorsResponse;
import com.wultra.powerauth.fido2.rest.model.response.RegistrationChallengeResponse;
@@ -127,13 +128,18 @@ public RegistrationResponse register(RegistrationRequest requestObject) throws E
final AttestedCredentialData attestedCredentialData = authData.getAttestedCredentialData();
final String fmt = attestationObject.getFmt();
- if ("packed".equals(fmt)) {
+ final byte[] aaguid = attestationObject.getAuthData().getAttestedCredentialData().getAaguid();
+
+ validateRegistrationRequest(applicationId, fmt, aaguid, challengeValue);
+
+ if (Fmt.FMT_PACKED.getValue().equals(fmt)) {
final boolean verifySignature = cryptographyService.verifySignatureForRegistration(applicationId, clientDataJSON, authData, signature, attestedCredentialData);
if (!verifySignature) {
// Immediately revoke the challenge
registrationProvider.revokeRegistrationByChallengeValue(applicationId, challengeValue);
throw new Fido2AuthenticationFailedException("Registration failed");
}
+ logger.info("Signature verification on registration performed using packed attestation format");
} else {
logger.info("No signature verification on registration");
}
@@ -145,4 +151,13 @@ public RegistrationResponse register(RegistrationRequest requestObject) throws E
return registrationConverter.convertRegistrationResponse(authenticatorDetailResponse);
}
+ private void validateRegistrationRequest(final String applicationId, final String attestationFormat, final byte[] aaguid, final String challengeValue) throws Exception {
+ if (!registrationProvider.registrationAllowed(applicationId, attestationFormat, aaguid)) {
+ logger.warn("Invalid request for FIDO2 registration");
+ // Immediately revoke the challenge
+ registrationProvider.revokeRegistrationByChallengeValue(applicationId, challengeValue);
+ throw new Fido2AuthenticationFailedException("Registration failed");
+ }
+ }
+
}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/provider/RegistrationProvider.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/provider/RegistrationProvider.java
index f1918e309..0349baafc 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/provider/RegistrationProvider.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/provider/RegistrationProvider.java
@@ -56,4 +56,14 @@ public interface RegistrationProvider {
*/
void revokeRegistrationByChallengeValue(String applicationId, String challengeValue) throws Exception;
+ /**
+ * Verify registration parameters and determine whether registration is allowed.
+ * @param applicationId Application ID.
+ * @param attestationFormat FIDO2 registration attestation format.
+ * @param aaguid FIDO2 registration AAGUID value.
+ * @return Whether registration is allowed.
+ * @throws Exception In case any issue occur during processing.
+ */
+ boolean registrationAllowed(String applicationId, String attestationFormat, byte[] aaguid) throws Exception;
+
}
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/controller/api/PowerAuthController.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/controller/api/PowerAuthController.java
index 40ac6143b..01202c3ff 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/controller/api/PowerAuthController.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/controller/api/PowerAuthController.java
@@ -433,6 +433,43 @@ public ObjectResponse supportApplicationVersi
return new ObjectResponse<>("OK", powerAuthService.supportApplicationVersion(request.getRequestObject()));
}
+ /**
+ * Call {@link PowerAuthService#getApplicationConfig(GetApplicationConfigRequest)} method and
+ * return the response.
+ * @param request Get application configuration.
+ * @return Application configuration response.
+ * @throws Exception In case the service throws exception.
+ */
+ @PostMapping("/application/config/detail")
+ public ObjectResponse getApplicationConfig(@RequestBody ObjectRequest request) throws Exception {
+ return new ObjectResponse<>(powerAuthService.getApplicationConfig(request.getRequestObject()));
+ }
+
+ /**
+ * Call {@link PowerAuthService#createApplicationConfig(CreateApplicationConfigRequest)} method and
+ * return the response.
+ * @param request Create an application configuration.
+ * @return Create application configuration response.
+ * @throws Exception In case the service throws exception.
+ */
+ @PostMapping("/application/config/create")
+ public ObjectResponse createApplicationConfig(@RequestBody ObjectRequest request) throws Exception {
+ return new ObjectResponse<>(powerAuthService.createApplicationConfig(request.getRequestObject()));
+ }
+
+ /**
+ * Call {@link PowerAuthService#removeApplicationConfig(RemoveApplicationConfigRequest)} method and
+ * return the response.
+ * @param request Delete an application configuration.
+ * @return Delete application configuration response.
+ * @throws Exception In case the service throws exception.
+ */
+ @PostMapping("/application/config/remove")
+ public Response removeApplicationConfig(@RequestBody ObjectRequest request) throws Exception {
+ powerAuthService.removeApplicationConfig(request.getRequestObject());
+ return new Response();
+ }
+
/**
* Call {@link PowerAuthService#createIntegration(CreateIntegrationRequest)} method and
* return the response.
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/RepositoryCatalogue.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/RepositoryCatalogue.java
index 26b63502e..3391ecc2e 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/RepositoryCatalogue.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/RepositoryCatalogue.java
@@ -37,6 +37,8 @@ public class RepositoryCatalogue {
private final ApplicationVersionRepository applicationVersionRepository;
+ private final ApplicationConfigRepository applicationConfigRepository;
+
private final CallbackUrlRepository callbackUrlRepository;
private final IntegrationRepository integrationRepository;
@@ -57,7 +59,7 @@ public RepositoryCatalogue(
ActivationHistoryRepository activationHistoryRepository,
ApplicationRepository applicationRepository,
ApplicationVersionRepository applicationVersionRepository,
- CallbackUrlRepository callbackUrlRepository,
+ ApplicationConfigRepository applicationConfigRepository, CallbackUrlRepository callbackUrlRepository,
IntegrationRepository integrationRepository,
MasterKeyPairRepository masterKeyPairRepository,
SignatureAuditRepository signatureAuditRepository,
@@ -69,6 +71,7 @@ public RepositoryCatalogue(
this.activationHistoryRepository = activationHistoryRepository;
this.applicationRepository = applicationRepository;
this.applicationVersionRepository = applicationVersionRepository;
+ this.applicationConfigRepository = applicationConfigRepository;
this.callbackUrlRepository = callbackUrlRepository;
this.integrationRepository = integrationRepository;
this.masterKeyPairRepository = masterKeyPairRepository;
@@ -96,6 +99,10 @@ public ApplicationVersionRepository getApplicationVersionRepository() {
return applicationVersionRepository;
}
+ public ApplicationConfigRepository getApplicationConfigRepository() {
+ return applicationConfigRepository;
+ }
+
public CallbackUrlRepository getCallbackUrlRepository() {
return callbackUrlRepository;
}
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/converter/ListToJsonConverter.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/converter/ListToJsonConverter.java
new file mode 100644
index 000000000..78f4b0c96
--- /dev/null
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/converter/ListToJsonConverter.java
@@ -0,0 +1,82 @@
+/*
+ * PowerAuth Server 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.server.database.model.converter;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import jakarta.persistence.AttributeConverter;
+import jakarta.persistence.Converter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Converts between list of strings and JSON serialized storage in a database column.
+ *
+ * @author Roman Strobl, roman.strobl@wultra.com
+ */
+@Converter
+@Component
+public class ListToJsonConverter implements AttributeConverter, String> {
+
+ private static final Logger logger = LoggerFactory.getLogger(ListToJsonConverter.class);
+
+ private static final String EMPTY_LIST = "[]";
+
+ private final ObjectMapper objectMapper;
+
+ /**
+ * Converter constructor.
+ * @param objectMapper Object mapper.
+ */
+ public ListToJsonConverter(ObjectMapper objectMapper) {
+ this.objectMapper = objectMapper;
+ }
+
+ @Override
+ public String convertToDatabaseColumn(List attributes) {
+ if (attributes == null) {
+ return EMPTY_LIST;
+ }
+ try {
+ return objectMapper.writeValueAsString(attributes);
+ } catch (JsonProcessingException ex) {
+ logger.warn("Conversion failed for attribute list, error: {}", ex.getMessage(), ex);
+ return EMPTY_LIST;
+ }
+ }
+
+ @Override
+ public List convertToEntityAttribute(String dbValue) {
+ if (dbValue == null) {
+ return null;
+ }
+ try {
+ return objectMapper.readValue(dbValue, new TypeReference<>() {});
+ } catch (JsonProcessingException ex) {
+ logger.warn("Conversion failed for attribute list, error: {}", ex.getMessage(), ex);
+ return Collections.emptyList();
+ }
+ }
+
+}
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/ApplicationConfigEntity.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/ApplicationConfigEntity.java
new file mode 100644
index 000000000..8ee8cde1c
--- /dev/null
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/ApplicationConfigEntity.java
@@ -0,0 +1,107 @@
+/*
+ * PowerAuth Server 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.server.database.model.entity;
+
+import io.getlime.security.powerauth.app.server.database.model.converter.ListToJsonConverter;
+import jakarta.persistence.*;
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.data.util.ProxyUtils;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Entity class representing an application configuration.
+ *
+ * @author Roman Strobl, roman.strobl@wultra.com
+ */
+@Entity
+@Getter
+@Setter
+@Table(name = "pa_application_config")
+public class ApplicationConfigEntity implements Serializable {
+
+ @Serial
+ private static final long serialVersionUID = -7670843254389928550L;
+
+ @Id
+ @SequenceGenerator(name = "pa_application_config", sequenceName = "pa_app_conf_seq", allocationSize = 1)
+ @GeneratedValue(strategy = GenerationType.AUTO, generator = "pa_application_config")
+ @Column(name = "id")
+ private Long rid;
+
+ @OneToOne
+ @JoinColumn(name = "application_id", referencedColumnName = "id", nullable = false, updatable = false)
+ private ApplicationEntity application;
+
+ @Column(name = "config_key", nullable = false)
+ private String key;
+
+ @Column(name = "config_values")
+ @Convert(converter = ListToJsonConverter.class)
+ private List values = new ArrayList<>();
+
+ /**
+ * No-arg constructor.
+ */
+ public ApplicationConfigEntity() {
+ }
+
+ /**
+ * Constructor for a new application configuration.
+ *
+ * @param application Application entity.
+ * @param key Configuration key.
+ * @param values Configuration values.
+ */
+ public ApplicationConfigEntity(ApplicationEntity application, String key, List values) {
+ this.application = application;
+ this.key = key;
+ this.values = values;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null) return false;
+ if (!this.getClass().equals(ProxyUtils.getUserClass(o))) return false;
+ ApplicationConfigEntity that = (ApplicationConfigEntity) o;
+ return Objects.equals(application, that.application) &&
+ Objects.equals(key, that.key) &&
+ Objects.equals(values, that.values);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(application, key, values);
+ }
+
+ @Override
+ public String toString() {
+ return "ApplicationConfigEntity{" +
+ "rid=" + rid +
+ ", appId='" + application.getId() + '\'' +
+ ", key=" + key +
+ ", values=" + values +
+ '}';
+ }
+}
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/ApplicationEntity.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/ApplicationEntity.java
index 193d979f7..a7f49ce76 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/ApplicationEntity.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/ApplicationEntity.java
@@ -75,7 +75,6 @@ public ApplicationEntity() {
* @param versions Collection of versions.
*/
public ApplicationEntity(Long rid, String id, List roles, List versions) {
- super();
this.rid = rid;
this.id = id;
this.roles.addAll(roles);
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/SignatureEntity.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/SignatureEntity.java
index e0798e6e4..dc30d1622 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/SignatureEntity.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/SignatureEntity.java
@@ -137,7 +137,6 @@ public SignatureEntity(
Boolean valid,
Date timestampCreated,
Integer version) {
- super();
this.id = id;
this.activation = activation;
this.activationCounter = activationCounter;
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/repository/ApplicationConfigRepository.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/repository/ApplicationConfigRepository.java
new file mode 100644
index 000000000..9694f2fc8
--- /dev/null
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/repository/ApplicationConfigRepository.java
@@ -0,0 +1,43 @@
+/*
+ * PowerAuth Server 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.server.database.repository;
+
+import io.getlime.security.powerauth.app.server.database.model.entity.ApplicationConfigEntity;
+import io.getlime.security.powerauth.app.server.database.model.entity.ApplicationEntity;
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+/**
+ * Repository for application configurations.
+ *
+ * @author Roman Strobl, roman.strobl@wultra.com
+ */
+@Repository
+public interface ApplicationConfigRepository extends CrudRepository {
+
+ /**
+ * Find application configuration by application ID.
+ *
+ * @param applicationId Application ID.
+ * @return Optional application config entity.
+ */
+ List findByApplicationId(String applicationId);
+
+}
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/PowerAuthService.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/PowerAuthService.java
index 51da89ab0..6608aba47 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/PowerAuthService.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/PowerAuthService.java
@@ -24,6 +24,7 @@
import com.wultra.security.powerauth.client.model.request.*;
import com.wultra.security.powerauth.client.model.response.*;
import com.wultra.security.powerauth.client.model.validator.*;
+import io.getlime.core.rest.model.base.response.Response;
import io.getlime.security.powerauth.app.server.configuration.PowerAuthPageableConfiguration;
import io.getlime.security.powerauth.app.server.configuration.PowerAuthServiceConfiguration;
import io.getlime.security.powerauth.app.server.converter.ActivationStatusConverter;
@@ -2010,6 +2011,51 @@ public UpdateActivationNameResponse updateActivationName(final UpdateActivationN
}
}
+ @Transactional(readOnly = true)
+ public GetApplicationConfigResponse getApplicationConfig(final GetApplicationConfigRequest request) throws GenericServiceException {
+ try {
+ final String applicationId = request.getApplicationId();
+ logger.info("GetApplicationConfig call received, application ID: {}", applicationId);
+ logger.debug("Obtaining application config: {}", request);
+ final GetApplicationConfigResponse response = behavior.getApplicationConfigServiceBehavior().getApplicationConfig(request);
+ logger.info("GetApplicationConfig succeeded, application ID: {}", applicationId);
+ return response;
+ } catch (RuntimeException ex) {
+ logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
+ throw ex;
+ }
+ }
+
+ @Transactional
+ public CreateApplicationConfigResponse createApplicationConfig(final CreateApplicationConfigRequest request) throws GenericServiceException {
+ try {
+ final String applicationId = request.getApplicationId();
+ logger.info("CreateApplicationConfig call received, application ID: {}", applicationId);
+ logger.debug("Creating application config: {}", request);
+ final CreateApplicationConfigResponse response = behavior.getApplicationConfigServiceBehavior().createApplicationConfig(request);
+ logger.info("CreateApplicationConfig succeeded, application ID: {}", applicationId);
+ return response;
+ } catch (RuntimeException ex) {
+ logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
+ throw ex;
+ }
+ }
+
+ @Transactional
+ public Response removeApplicationConfig(final RemoveApplicationConfigRequest request) throws GenericServiceException {
+ try {
+ final String applicationId = request.getApplicationId();
+ logger.info("RemoveApplicationConfig call received, application ID: {}", applicationId);
+ logger.debug("Removing application config: {}", request);
+ final Response response = behavior.getApplicationConfigServiceBehavior().removeApplicationConfig(request);
+ logger.info("RemoveApplicationConfig succeeded, application ID: {}", applicationId);
+ return response;
+ } catch (RuntimeException ex) {
+ logger.error("Runtime exception or error occurred, transaction will be rolled back", ex);
+ throw ex;
+ }
+ }
+
private Set convert(final Set source) {
if (CollectionUtils.isEmpty(source)) {
return Set.of(ActivationStatus.values());
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/ServiceBehaviorCatalogue.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/ServiceBehaviorCatalogue.java
index c49dd83fd..805bceb3b 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/ServiceBehaviorCatalogue.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/ServiceBehaviorCatalogue.java
@@ -40,6 +40,8 @@ public class ServiceBehaviorCatalogue {
private ApplicationRolesServiceBehavior applicationRolesServiceBehavior;
+ private ApplicationConfigServiceBehavior applicationConfigServiceBehavior;
+
private AuditingServiceBehavior auditingServiceBehavior;
private OnlineSignatureServiceBehavior onlineSignatureServiceBehavior;
@@ -91,6 +93,11 @@ public void setApplicationRolesServiceBehavior(@Lazy ApplicationRolesServiceBeha
this.applicationRolesServiceBehavior = applicationRolesServiceBehavior;
}
+ @Autowired
+ public void setApplicationConfigServiceBehavior(@Lazy ApplicationConfigServiceBehavior applicationConfigServiceBehavior) {
+ this.applicationConfigServiceBehavior = applicationConfigServiceBehavior;
+ }
+
@Autowired
public void setAuditingServiceBehavior(@Lazy AuditingServiceBehavior auditingServiceBehavior) {
this.auditingServiceBehavior = auditingServiceBehavior;
@@ -177,6 +184,10 @@ public ApplicationRolesServiceBehavior getApplicationRolesServiceBehavior() {
return applicationRolesServiceBehavior;
}
+ public ApplicationConfigServiceBehavior getApplicationConfigServiceBehavior() {
+ return applicationConfigServiceBehavior;
+ }
+
public ActivationHistoryServiceBehavior getActivationHistoryServiceBehavior() {
return activationHistoryServiceBehavior;
}
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/ActivationFlagsServiceBehavior.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/ActivationFlagsServiceBehavior.java
index fb75de870..2d83bf22e 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/ActivationFlagsServiceBehavior.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/ActivationFlagsServiceBehavior.java
@@ -44,7 +44,7 @@
*
* @author Roman Strobl, roman.strobl@wultra.com
*/
-@Component("activationFlagsServiceBehavior")
+@Component
public class ActivationFlagsServiceBehavior {
private static final Logger logger = LoggerFactory.getLogger(ActivationFlagsServiceBehavior.class);
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/ActivationServiceBehavior.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/ActivationServiceBehavior.java
index 9b1791912..5e911e023 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/ActivationServiceBehavior.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/ActivationServiceBehavior.java
@@ -91,7 +91,7 @@
*
* @author Petr Dvorak, petr@wultra.com
*/
-@Component("activationServiceBehavior")
+@Component
public class ActivationServiceBehavior {
/**
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/ApplicationConfigServiceBehavior.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/ApplicationConfigServiceBehavior.java
new file mode 100644
index 000000000..47b430f42
--- /dev/null
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/ApplicationConfigServiceBehavior.java
@@ -0,0 +1,190 @@
+/*
+ * PowerAuth Server 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.server.service.behavior.tasks;
+
+import com.wultra.security.powerauth.client.model.entity.ApplicationConfigurationItem;
+import com.wultra.security.powerauth.client.model.request.CreateApplicationConfigRequest;
+import com.wultra.security.powerauth.client.model.request.GetApplicationConfigRequest;
+import com.wultra.security.powerauth.client.model.request.RemoveApplicationConfigRequest;
+import com.wultra.security.powerauth.client.model.response.*;
+import io.getlime.core.rest.model.base.response.Response;
+import io.getlime.security.powerauth.app.server.database.RepositoryCatalogue;
+import io.getlime.security.powerauth.app.server.database.model.entity.ApplicationConfigEntity;
+import io.getlime.security.powerauth.app.server.database.model.entity.ApplicationEntity;
+import io.getlime.security.powerauth.app.server.database.repository.ApplicationConfigRepository;
+import io.getlime.security.powerauth.app.server.database.repository.ApplicationRepository;
+import io.getlime.security.powerauth.app.server.service.exceptions.GenericServiceException;
+import io.getlime.security.powerauth.app.server.service.i18n.LocalizationProvider;
+import io.getlime.security.powerauth.app.server.service.model.ServiceError;
+import lombok.extern.slf4j.Slf4j;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+import static com.wultra.powerauth.fido2.rest.model.enumeration.Fido2ConfigKeys.CONFIG_KEY_ALLOWED_AAGUIDS;
+import static com.wultra.powerauth.fido2.rest.model.enumeration.Fido2ConfigKeys.CONFIG_KEY_ALLOWED_ATTESTATION_FMT;
+
+/**
+ * Behavior class implementing management of application configuration.
+ *
+ * @author Roman Strobl, roman.strobl@wultra.com
+ */
+@Component
+@Slf4j
+public class ApplicationConfigServiceBehavior {
+
+ private final RepositoryCatalogue repositoryCatalogue;
+ private final LocalizationProvider localizationProvider;
+
+ /**
+ * Behaviour class constructor.
+ * @param repositoryCatalogue Repository catalogue.
+ * @param localizationProvider Localization provider.
+ */
+ @Autowired
+ public ApplicationConfigServiceBehavior(final RepositoryCatalogue repositoryCatalogue, final LocalizationProvider localizationProvider) {
+ this.repositoryCatalogue = repositoryCatalogue;
+ this.localizationProvider = localizationProvider;
+ }
+
+ /**
+ * Get application configuration.
+ * @param request Request for obtaining an application configuration.
+ * @return Get application configuration response.
+ * @throws GenericServiceException In case of a business logic error.
+ */
+ public GetApplicationConfigResponse getApplicationConfig(final GetApplicationConfigRequest request) throws GenericServiceException {
+ final String applicationId = request.getApplicationId();
+ if (applicationId == null) {
+ logger.warn("Invalid application ID in getApplicationConfig");
+ // Rollback is not required, error occurs before writing to database
+ throw localizationProvider.buildExceptionForCode(ServiceError.INVALID_REQUEST);
+ }
+ final List applicationConfigs = repositoryCatalogue.getApplicationConfigRepository().findByApplicationId(applicationId);
+ final GetApplicationConfigResponse response = new GetApplicationConfigResponse();
+ response.setApplicationId(applicationId);
+ final List responseConfigs = new ArrayList<>();
+ applicationConfigs.forEach(config -> {
+ final ApplicationConfigurationItem item = new ApplicationConfigurationItem();
+ item.setKey(config.getKey());
+ item.setValues(config.getValues());
+ responseConfigs.add(item);
+ });
+ response.setApplicationConfigs(responseConfigs);
+ return response;
+ }
+
+ /**
+ * Create an application configuration.
+ * @param request Request for creating application configuration
+ * @return Create application configuration response.
+ * @throws GenericServiceException In case of a business logic error.
+ */
+ public CreateApplicationConfigResponse createApplicationConfig(final CreateApplicationConfigRequest request) throws GenericServiceException {
+ final String applicationId = request.getApplicationId();
+ final String key = request.getKey();
+ final List values = request.getValues();
+ if (applicationId == null) {
+ logger.warn("Invalid application ID in createApplicationConfig");
+ // Rollback is not required, error occurs before writing to database
+ throw localizationProvider.buildExceptionForCode(ServiceError.INVALID_REQUEST);
+ }
+ validateConfigKey(key);
+ final ApplicationRepository appRepository = repositoryCatalogue.getApplicationRepository();
+ final Optional appOptional = appRepository.findById(applicationId);
+ if (appOptional.isEmpty()) {
+ logger.info("Application not found, application ID: {}", applicationId);
+ // Rollback is not required, error occurs before writing to database
+ throw localizationProvider.buildExceptionForCode(ServiceError.INVALID_APPLICATION);
+ }
+ final ApplicationEntity appEntity = appOptional.get();
+ final ApplicationConfigRepository configRepository = repositoryCatalogue.getApplicationConfigRepository();
+ final List configs = configRepository.findByApplicationId(applicationId);
+ final Optional matchedConfig = configs.stream()
+ .filter(config -> config.getKey().equals(key))
+ .findFirst();
+ matchedConfig.ifPresentOrElse(config -> {
+ config.setValues(values);
+ configRepository.save(config);
+ }, () -> {
+ final ApplicationConfigEntity config = new ApplicationConfigEntity();
+ config.setApplication(appEntity);
+ config.setKey(key);
+ config.setValues(values);
+ configRepository.save(config);
+ });
+ final CreateApplicationConfigResponse response = new CreateApplicationConfigResponse();
+ response.setApplicationId(applicationId);
+ response.setKey(key);
+ response.setValues(values);
+ return response;
+ }
+
+ /**
+ * Delete an application configuration.
+ * @param request Remove application config request.
+ * @return Response.
+ * @throws GenericServiceException In case of a business logic error.
+ */
+ public Response removeApplicationConfig(final RemoveApplicationConfigRequest request) throws GenericServiceException {
+ final String applicationId = request.getApplicationId();
+ final String key = request.getKey();
+ if (applicationId == null) {
+ logger.warn("Invalid application ID in deleteApplicationConfig");
+ // Rollback is not required, error occurs before writing to database
+ throw localizationProvider.buildExceptionForCode(ServiceError.INVALID_REQUEST);
+ }
+ validateConfigKey(key);
+ final ApplicationRepository appRepository = repositoryCatalogue.getApplicationRepository();
+ final Optional appOptional = appRepository.findById(applicationId);
+ if (appOptional.isEmpty()) {
+ logger.info("Application not found, application ID: {}", applicationId);
+ // Rollback is not required, error occurs before writing to database
+ throw localizationProvider.buildExceptionForCode(ServiceError.INVALID_APPLICATION);
+ }
+ final ApplicationConfigRepository configRepository = repositoryCatalogue.getApplicationConfigRepository();
+ final List configs = configRepository.findByApplicationId(applicationId);
+ configs.stream().filter(config -> config.getKey().equals(key)).forEach(configRepository::delete);
+ return new Response();
+ }
+
+ /**
+ * Validate that the configuration key is valid.
+ * @param key Configuration key.
+ * @throws GenericServiceException Thrown in case configuration key is invalid.
+ */
+ private void validateConfigKey(String key) throws GenericServiceException {
+ if (key == null) {
+ logger.warn("Missing configuration key in FIDO2 request");
+ // Rollback is not required, error occurs before writing to database
+ throw localizationProvider.buildExceptionForCode(ServiceError.INVALID_REQUEST);
+ }
+ if (!CONFIG_KEY_ALLOWED_ATTESTATION_FMT.equals(key)
+ && !CONFIG_KEY_ALLOWED_AAGUIDS.equals(key)) {
+ logger.warn("Unknown configuration key in FIDO2 request: {}", key);
+ // Rollback is not required, error occurs before writing to database
+ throw localizationProvider.buildExceptionForCode(ServiceError.INVALID_REQUEST);
+ }
+ }
+
+}
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/ApplicationRolesServiceBehavior.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/ApplicationRolesServiceBehavior.java
index c4d9898c3..74aa921cc 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/ApplicationRolesServiceBehavior.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/ApplicationRolesServiceBehavior.java
@@ -42,7 +42,7 @@
*
* @author Roman Strobl, roman.strobl@wultra.com
*/
-@Component("applicationRolesServiceBehavior")
+@Component
public class ApplicationRolesServiceBehavior {
private static final Logger logger = LoggerFactory.getLogger(ApplicationRolesServiceBehavior.class);
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/TokenBehavior.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/TokenBehavior.java
index 601b928e1..5fa2d0a4e 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/TokenBehavior.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/TokenBehavior.java
@@ -75,7 +75,7 @@
*
* @author Petr Dvorak, petr@wultra.com
*/
-@Component("tokenBehavior")
+@Component
public class TokenBehavior {
private final RepositoryCatalogue repositoryCatalogue;
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/VaultUnlockServiceBehavior.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/VaultUnlockServiceBehavior.java
index 902ca1d34..3539dcff7 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/VaultUnlockServiceBehavior.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/VaultUnlockServiceBehavior.java
@@ -77,7 +77,7 @@
*
* @author Roman Strobl, roman.strobl@wultra.com
*/
-@Component("vaultUnlockServiceBehavior")
+@Component
public class VaultUnlockServiceBehavior {
private final RepositoryCatalogue repositoryCatalogue;
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthRegistrationProvider.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthRegistrationProvider.java
index 3b16f7c94..820022630 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthRegistrationProvider.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthRegistrationProvider.java
@@ -21,14 +21,18 @@
import com.wultra.powerauth.fido2.errorhandling.Fido2AuthenticationFailedException;
import com.wultra.powerauth.fido2.rest.model.entity.RegistrationChallenge;
import com.wultra.powerauth.fido2.service.provider.RegistrationProvider;
+import com.wultra.security.powerauth.client.model.entity.ApplicationConfigurationItem;
import com.wultra.security.powerauth.client.model.enumeration.ActivationOtpValidation;
import com.wultra.security.powerauth.client.model.enumeration.Protocols;
+import com.wultra.security.powerauth.client.model.request.GetApplicationConfigRequest;
+import com.wultra.security.powerauth.client.model.response.GetApplicationConfigResponse;
import com.wultra.security.powerauth.client.model.response.InitActivationResponse;
import io.getlime.security.powerauth.app.server.database.RepositoryCatalogue;
import io.getlime.security.powerauth.app.server.database.model.entity.ActivationRecordEntity;
import io.getlime.security.powerauth.app.server.database.model.entity.ApplicationEntity;
import io.getlime.security.powerauth.app.server.database.model.enumeration.ActivationStatus;
import io.getlime.security.powerauth.app.server.service.behavior.ServiceBehaviorCatalogue;
+import io.getlime.security.powerauth.app.server.service.behavior.tasks.ApplicationConfigServiceBehavior;
import io.getlime.security.powerauth.app.server.service.exceptions.GenericServiceException;
import io.getlime.security.powerauth.crypto.lib.util.KeyConvertor;
import lombok.extern.slf4j.Slf4j;
@@ -36,10 +40,14 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
+import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.List;
import java.util.Optional;
+import static com.wultra.powerauth.fido2.rest.model.enumeration.Fido2ConfigKeys.CONFIG_KEY_ALLOWED_AAGUIDS;
+import static com.wultra.powerauth.fido2.rest.model.enumeration.Fido2ConfigKeys.CONFIG_KEY_ALLOWED_ATTESTATION_FMT;
+
/**
* Challenge provider based on the PowerAuth core implementations.
*
@@ -141,4 +149,40 @@ public void revokeRegistrationByChallengeValue(String applicationId, String chal
}
}
+
+ @Override
+ @Transactional(readOnly = true)
+ public boolean registrationAllowed(String applicationId, String attestationFormat, byte[] aaguid) throws Exception {
+ final ApplicationConfigServiceBehavior configService = serviceBehaviorCatalogue.getApplicationConfigServiceBehavior();
+ final GetApplicationConfigRequest configRequest = new GetApplicationConfigRequest();
+ configRequest.setApplicationId(applicationId);
+ final GetApplicationConfigResponse configResponse = configService.getApplicationConfig(configRequest);
+ final String aaguidStr = new String(aaguid, StandardCharsets.UTF_8);
+ Optional configFmt = configResponse.getApplicationConfigs().stream()
+ .filter(cfg -> CONFIG_KEY_ALLOWED_ATTESTATION_FMT.equals(cfg.getKey()))
+ .findFirst();
+
+ if (configFmt.isPresent()) {
+ List allowedFmts = configFmt.get().getValues();
+ if (!allowedFmts.contains(attestationFormat)) {
+ logger.warn("Rejected attestation format for FIDO2 registration: {}", attestationFormat);
+ return false;
+ }
+ }
+
+ Optional configAaguids = configResponse.getApplicationConfigs().stream()
+ .filter(cfg -> CONFIG_KEY_ALLOWED_AAGUIDS.equals(cfg.getKey()))
+ .findFirst();
+
+ if (configAaguids.isPresent()) {
+ System.out.println(aaguidStr);
+ List allowedAaguids = configAaguids.get().getValues();
+ if (!allowedAaguids.contains(aaguidStr)) {
+ logger.warn("Rejected AAGUID value for FIDO2 registration: {}", aaguidStr);
+ return false;
+ }
+ }
+
+ return true;
+ }
}
diff --git a/powerauth-java-server/src/test/java/com/wultra/powerauth/fido2/Fido2AuthenticatorTest.java b/powerauth-java-server/src/test/java/com/wultra/powerauth/fido2/Fido2AuthenticatorTest.java
index 991a41d14..8c7a9b9ce 100644
--- a/powerauth-java-server/src/test/java/com/wultra/powerauth/fido2/Fido2AuthenticatorTest.java
+++ b/powerauth-java-server/src/test/java/com/wultra/powerauth/fido2/Fido2AuthenticatorTest.java
@@ -47,9 +47,7 @@
import com.wultra.powerauth.fido2.service.RegistrationService;
import com.wultra.security.powerauth.client.model.enumeration.ActivationStatus;
import com.wultra.security.powerauth.client.model.enumeration.SignatureType;
-import com.wultra.security.powerauth.client.model.request.CreateApplicationRequest;
-import com.wultra.security.powerauth.client.model.request.GetActivationStatusRequest;
-import com.wultra.security.powerauth.client.model.request.OperationTemplateCreateRequest;
+import com.wultra.security.powerauth.client.model.request.*;
import com.wultra.security.powerauth.client.model.response.OperationTemplateDetailResponse;
import io.getlime.security.powerauth.app.server.Application;
import io.getlime.security.powerauth.app.server.service.PowerAuthService;
@@ -64,6 +62,8 @@
import java.util.*;
import java.util.stream.Collectors;
+import static com.wultra.powerauth.fido2.rest.model.enumeration.Fido2ConfigKeys.CONFIG_KEY_ALLOWED_AAGUIDS;
+import static com.wultra.powerauth.fido2.rest.model.enumeration.Fido2ConfigKeys.CONFIG_KEY_ALLOWED_ATTESTATION_FMT;
import static org.junit.jupiter.api.Assertions.*;
/**
@@ -103,7 +103,7 @@ public Fido2AuthenticatorTest(PowerAuthService powerAuthService, RegistrationSer
}
@Test
- void packedAuthenticatorSuccessTest() throws Exception {
+ void packedAuthenticatorSuccessTest() throws Exception {
registerCredential();
authenticate();
}
@@ -271,6 +271,120 @@ public void packedAuthenticatorInvalidSignatureTest() throws Exception {
assertThrows(Fido2AuthenticationFailedException.class, () -> assertionService.authenticate(authRequest));
}
+ @Test
+ void packedAuthenticatorUnsupportedAaguidTest() throws Exception {
+ // Configure server not to allow any AAGUIDs
+ final CreateApplicationConfigRequest requestCreate = new CreateApplicationConfigRequest();
+ requestCreate.setApplicationId(APPLICATION_ID);
+ requestCreate.setKey(CONFIG_KEY_ALLOWED_AAGUIDS);
+ requestCreate.setValues(Collections.emptyList());
+ powerAuthService.createApplicationConfig(requestCreate);
+
+ // Registration should fail
+ assertThrows(Fido2AuthenticationFailedException.class, this::registerCredential);
+
+ // Remove configuration
+ final RemoveApplicationConfigRequest requestRemove = new RemoveApplicationConfigRequest();
+ requestRemove.setApplicationId(APPLICATION_ID);
+ requestRemove.setKey(CONFIG_KEY_ALLOWED_AAGUIDS);
+ powerAuthService.removeApplicationConfig(requestRemove);
+ }
+
+ @Test
+ void packedAuthenticatorInvalidAaguidTest() throws Exception {
+ // Configure server not to allow only one AAGUID which differs from registreation request AAGUID
+ final CreateApplicationConfigRequest requestCreate = new CreateApplicationConfigRequest();
+ requestCreate.setApplicationId(APPLICATION_ID);
+ requestCreate.setKey(CONFIG_KEY_ALLOWED_AAGUIDS);
+ requestCreate.setValues(List.of("\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0001"));
+ powerAuthService.createApplicationConfig(requestCreate);
+
+ // Registration should fail
+ assertThrows(Fido2AuthenticationFailedException.class, this::registerCredential);
+
+ // Remove configuration
+ final RemoveApplicationConfigRequest requestRemove = new RemoveApplicationConfigRequest();
+ requestRemove.setApplicationId(APPLICATION_ID);
+ requestRemove.setKey(CONFIG_KEY_ALLOWED_AAGUIDS);
+ powerAuthService.removeApplicationConfig(requestRemove);
+ }
+
+ @Test
+ void packedAuthenticatorValidAaguidTest() throws Exception {
+ // Configure server not to allow valid AAGUID only
+ final CreateApplicationConfigRequest requestCreate = new CreateApplicationConfigRequest();
+ requestCreate.setApplicationId(APPLICATION_ID);
+ requestCreate.setKey(CONFIG_KEY_ALLOWED_AAGUIDS);
+ requestCreate.setValues(List.of("\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000"));
+ powerAuthService.createApplicationConfig(requestCreate);
+
+ // Registration should succeed
+ registerCredential();
+
+ // Remove configuration
+ final RemoveApplicationConfigRequest requestRemove = new RemoveApplicationConfigRequest();
+ requestRemove.setApplicationId(APPLICATION_ID);
+ requestRemove.setKey(CONFIG_KEY_ALLOWED_AAGUIDS);
+ powerAuthService.removeApplicationConfig(requestRemove);
+ }
+
+ @Test
+ void packedAuthenticatorUnsupportedAttestationFormatTest() throws Exception {
+ // Configure server not to allow any attestation formats
+ final CreateApplicationConfigRequest requestCreate = new CreateApplicationConfigRequest();
+ requestCreate.setApplicationId(APPLICATION_ID);
+ requestCreate.setKey(CONFIG_KEY_ALLOWED_ATTESTATION_FMT);
+ requestCreate.setValues(Collections.emptyList());
+ powerAuthService.createApplicationConfig(requestCreate);
+
+ // Registration should fail
+ assertThrows(Fido2AuthenticationFailedException.class, this::registerCredential);
+
+ // Remove configuration
+ final RemoveApplicationConfigRequest requestRemove = new RemoveApplicationConfigRequest();
+ requestRemove.setApplicationId(APPLICATION_ID);
+ requestRemove.setKey(CONFIG_KEY_ALLOWED_ATTESTATION_FMT);
+ powerAuthService.removeApplicationConfig(requestRemove);
+ }
+
+ @Test
+ void packedAuthenticatorInvalidAttestationFormatTest() throws Exception {
+ // Configure server not to allow only an attestation format which differs from request attestation format
+ final CreateApplicationConfigRequest requestCreate = new CreateApplicationConfigRequest();
+ requestCreate.setApplicationId(APPLICATION_ID);
+ requestCreate.setKey(CONFIG_KEY_ALLOWED_ATTESTATION_FMT);
+ requestCreate.setValues(List.of("none"));
+ powerAuthService.createApplicationConfig(requestCreate);
+
+ // Registration should fail
+ assertThrows(Fido2AuthenticationFailedException.class, this::registerCredential);
+
+ // Remove configuration
+ final RemoveApplicationConfigRequest requestRemove = new RemoveApplicationConfigRequest();
+ requestRemove.setApplicationId(APPLICATION_ID);
+ requestRemove.setKey(CONFIG_KEY_ALLOWED_ATTESTATION_FMT);
+ powerAuthService.removeApplicationConfig(requestRemove);
+ }
+
+ @Test
+ void packedAuthenticatorValidAttestationFormatTest() throws Exception {
+ // Configure server not to allow only an attestation format which matches request attestation format
+ final CreateApplicationConfigRequest requestCreate = new CreateApplicationConfigRequest();
+ requestCreate.setApplicationId(APPLICATION_ID);
+ requestCreate.setKey(CONFIG_KEY_ALLOWED_ATTESTATION_FMT);
+ requestCreate.setValues(List.of("packed"));
+ powerAuthService.createApplicationConfig(requestCreate);
+
+ // Registration should succeed
+ registerCredential();
+
+ // Remove configuration
+ final RemoveApplicationConfigRequest requestRemove = new RemoveApplicationConfigRequest();
+ requestRemove.setApplicationId(APPLICATION_ID);
+ requestRemove.setKey(CONFIG_KEY_ALLOWED_ATTESTATION_FMT);
+ powerAuthService.removeApplicationConfig(requestRemove);
+ }
+
private void createApplication() throws Exception {
// Search if application for FIDO2 tests exists
final boolean applicationFound = powerAuthService.getApplicationList().getApplications().stream()
@@ -458,4 +572,5 @@ private boolean isFlagOn(byte flags, int position) throws IOException {
}
return ((flags >> position) & 1) == 1;
}
+
}
diff --git a/powerauth-rest-client-spring/src/main/java/com/wultra/security/powerauth/rest/client/PowerAuthRestClient.java b/powerauth-rest-client-spring/src/main/java/com/wultra/security/powerauth/rest/client/PowerAuthRestClient.java
index 06435d2fd..ff8b184c0 100644
--- a/powerauth-rest-client-spring/src/main/java/com/wultra/security/powerauth/rest/client/PowerAuthRestClient.java
+++ b/powerauth-rest-client-spring/src/main/java/com/wultra/security/powerauth/rest/client/PowerAuthRestClient.java
@@ -1477,4 +1477,58 @@ public TelemetryReportResponse requestTelemetryReport(TelemetryReportRequest req
return callV3RestApi("/telemetry/report", request, queryParams, httpHeaders, TelemetryReportResponse.class);
}
+ @Override
+ public CreateApplicationConfigResponse createApplicationConfig(CreateApplicationConfigRequest request) throws PowerAuthClientException {
+ return createApplicationConfig(request, EMPTY_MULTI_MAP, EMPTY_MULTI_MAP);
+ }
+
+ @Override
+ public CreateApplicationConfigResponse createApplicationConfig(CreateApplicationConfigRequest request, MultiValueMap queryParams, MultiValueMap httpHeaders) throws PowerAuthClientException {
+ return callV3RestApi("/application/config/create", request, queryParams, httpHeaders, CreateApplicationConfigResponse.class);
+ }
+
+ @Override
+ public CreateApplicationConfigResponse createApplicationConfig(String applicationId, String key, List values) throws PowerAuthClientException {
+ final CreateApplicationConfigRequest request = new CreateApplicationConfigRequest();
+ request.setApplicationId(applicationId);
+ request.setKey(key);
+ request.setValues(values);
+ return createApplicationConfig(request);
+ }
+
+ @Override
+ public Response removeApplicationConfig(RemoveApplicationConfigRequest request) throws PowerAuthClientException {
+ return removeApplicationConfig(request, EMPTY_MULTI_MAP, EMPTY_MULTI_MAP);
+ }
+
+ @Override
+ public Response removeApplicationConfig(RemoveApplicationConfigRequest request, MultiValueMap queryParams, MultiValueMap httpHeaders) throws PowerAuthClientException {
+ return callV3RestApi("/application/config/remove", request, queryParams, httpHeaders, Response.class);
+ }
+
+ @Override
+ public Response removeApplicationConfig(String applicationId, String key) throws PowerAuthClientException {
+ final RemoveApplicationConfigRequest request = new RemoveApplicationConfigRequest();
+ request.setApplicationId(applicationId);
+ request.setKey(key);
+ return removeApplicationConfig(request);
+ }
+
+ @Override
+ public GetApplicationConfigResponse getApplicationConfig(GetApplicationConfigRequest request) throws PowerAuthClientException {
+ return getApplicationConfig(request, EMPTY_MULTI_MAP, EMPTY_MULTI_MAP);
+ }
+
+ @Override
+ public GetApplicationConfigResponse getApplicationConfig(GetApplicationConfigRequest request, MultiValueMap queryParams, MultiValueMap httpHeaders) throws PowerAuthClientException {
+ return callV3RestApi("/application/config/detail", request, queryParams, httpHeaders, GetApplicationConfigResponse.class);
+ }
+
+ @Override
+ public GetApplicationConfigResponse getApplicationConfig(String applicationId) throws PowerAuthClientException {
+ final GetApplicationConfigRequest request = new GetApplicationConfigRequest();
+ request.setApplicationId(applicationId);
+ return getApplicationConfig(request);
+ }
+
}
From a9bec0228255e1abae14c498ceee123bc3f73a8b Mon Sep 17 00:00:00 2001
From: Roman Strobl
Date: Wed, 21 Feb 2024 18:55:21 +0800
Subject: [PATCH 079/146] Fix #1311: FIDO2: Improve format of extras stored for
FIDO2
---
.../fido2/rest/model/converter/RegistrationConverter.java | 1 +
1 file changed, 1 insertion(+)
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/RegistrationConverter.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/RegistrationConverter.java
index 8407f2484..4f2e5883c 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/RegistrationConverter.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/RegistrationConverter.java
@@ -112,6 +112,7 @@ private Map convertExtras(RegistrationRequest requestObject) thr
params.put("topOrigin", authenticatorParameters.getResponse().getClientDataJSON().getTopOrigin());
params.put("isCrossOrigin", authenticatorParameters.getResponse().getClientDataJSON().isCrossOrigin());
params.put("aaguid", authenticatorParameters.getResponse().getAttestationObject().getAuthData().getAttestedCredentialData().getAaguid());
+ params.put("transports", authenticatorParameters.getResponse().getTransports());
return params;
}
From 3cd088e5d3d3a5aecc19e9771ba7b0ac2eba34ca Mon Sep 17 00:00:00 2001
From: Roman Strobl
Date: Wed, 21 Feb 2024 19:38:34 +0800
Subject: [PATCH 080/146] Fix #1312: FIDO2: Add additionalData for FIDO2
operations
---
.../fido2/PowerAuthAssertionProvider.java | 23 +++++++++++++++++--
1 file changed, 21 insertions(+), 2 deletions(-)
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAssertionProvider.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAssertionProvider.java
index ea47bcf84..0410e98f6 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAssertionProvider.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAssertionProvider.java
@@ -47,6 +47,7 @@
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -60,6 +61,10 @@ public class PowerAuthAssertionProvider implements AssertionProvider {
private static final String AUDIT_TYPE_FIDO2 = "fido2";
+ private static final String ATTR_ACTIVATION_ID = "activationId";
+ private static final String ATTR_APPLICATION_ID = "applicationId";
+ private static final String ATTR_AUTH_FACTOR = "authFactor";
+
private final ServiceBehaviorCatalogue serviceBehaviorCatalogue;
private final RepositoryCatalogue repositoryCatalogue;
private final AuditingServiceBehavior audit;
@@ -107,7 +112,7 @@ public AssertionChallenge approveAssertion(String challengeValue, AuthenticatorD
operationApproveRequest.setApplicationId(authenticatorDetail.getApplicationId());
operationApproveRequest.setUserId(authenticatorDetail.getUserId());
operationApproveRequest.setSignatureType(SignatureType.POSSESSION_KNOWLEDGE); //TODO: Use correct type
- //operationApproveRequest.getAdditionalData(); // TODO: Use context data from request
+ operationApproveRequest.getAdditionalData().putAll(prepareAdditionalData(authenticatorDetail, operationApproveRequest.getSignatureType()));
final OperationUserActionResponse approveOperation = serviceBehaviorCatalogue.getOperationBehavior().attemptApproveOperation(operationApproveRequest);
final UserActionResult result = approveOperation.getResult();
final OperationDetailResponse operation = approveOperation.getOperation();
@@ -140,7 +145,7 @@ public AssertionChallenge failAssertion(String challengeValue, AuthenticatorDeta
final OperationFailApprovalRequest operationFailApprovalRequest = new OperationFailApprovalRequest();
operationFailApprovalRequest.setOperationId(operationId);
- //operationApproveRequest.getAdditionalData(); // TODO: Use context data from request
+ operationFailApprovalRequest.getAdditionalData().putAll(prepareAdditionalData(authenticatorDetail, SignatureType.POSSESSION_KNOWLEDGE));
final ActivationRecordEntity activationWithLock = repositoryCatalogue.getActivationRepository().findActivationWithLock(authenticatorDetail.getActivationId());
@@ -222,6 +227,20 @@ private void handleInvalidSignatureImpl(ActivationRecordEntity activation, Signa
}
}
+ /**
+ * Prepare map with additional data stored with the operation.
+ * @param authenticatorDetail Authenticator detail.
+ * @param signatureType Used signature type.
+ * @return Additional data map.
+ */
+ private Map prepareAdditionalData(final AuthenticatorDetail authenticatorDetail, final SignatureType signatureType) {
+ final Map additionalData = new LinkedHashMap<>();
+ additionalData.put(ATTR_ACTIVATION_ID, authenticatorDetail.getActivationId());
+ additionalData.put(ATTR_APPLICATION_ID, authenticatorDetail.getApplicationId());
+ additionalData.put(ATTR_AUTH_FACTOR, signatureType);
+ return additionalData;
+ }
+
/**
* Handle operation status.
*
From e3971c6c801b5d3dca9187ff78733807d69daf7a Mon Sep 17 00:00:00 2001
From: Lubos Racansky
Date: Thu, 22 Feb 2024 09:59:22 +0100
Subject: [PATCH 081/146] Fix #1322: Update Wultra dependencies
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index eb8843bc3..e7faad1b7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -85,8 +85,8 @@
4.1.0
- 1.7.0-SNAPSHOT
- 1.9.0-SNAPSHOT
+ 1.7.0
+ 1.9.0
1.77
From e0779cd3d326b880773b9c0ae2800ee0e2c9ee6b Mon Sep 17 00:00:00 2001
From: Lubos Racansky
Date: Thu, 22 Feb 2024 10:07:39 +0100
Subject: [PATCH 082/146] Fix #1320: Set release version to 1.7.0
---
pom.xml | 2 +-
powerauth-admin/pom.xml | 2 +-
powerauth-client-model/pom.xml | 2 +-
powerauth-fido2/pom.xml | 2 +-
powerauth-java-server/pom.xml | 2 +-
powerauth-rest-client-spring/pom.xml | 2 +-
6 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/pom.xml b/pom.xml
index eb8843bc3..992c3f207 100644
--- a/pom.xml
+++ b/pom.xml
@@ -26,7 +26,7 @@
io.getlime.security
powerauth-server-parent
- 1.7.0-SNAPSHOT
+ 1.7.0
pom
diff --git a/powerauth-admin/pom.xml b/powerauth-admin/pom.xml
index 3f144a415..ab13d163f 100644
--- a/powerauth-admin/pom.xml
+++ b/powerauth-admin/pom.xml
@@ -11,7 +11,7 @@
io.getlime.security
powerauth-server-parent
- 1.7.0-SNAPSHOT
+ 1.7.0
diff --git a/powerauth-client-model/pom.xml b/powerauth-client-model/pom.xml
index 17d80aa9b..b61296445 100644
--- a/powerauth-client-model/pom.xml
+++ b/powerauth-client-model/pom.xml
@@ -28,7 +28,7 @@
io.getlime.security
powerauth-server-parent
- 1.7.0-SNAPSHOT
+ 1.7.0
diff --git a/powerauth-fido2/pom.xml b/powerauth-fido2/pom.xml
index 139030213..3742d0fe4 100644
--- a/powerauth-fido2/pom.xml
+++ b/powerauth-fido2/pom.xml
@@ -27,7 +27,7 @@
io.getlime.security
powerauth-server-parent
- 1.7.0-SNAPSHOT
+ 1.7.0
diff --git a/powerauth-java-server/pom.xml b/powerauth-java-server/pom.xml
index 077458476..687b487ed 100644
--- a/powerauth-java-server/pom.xml
+++ b/powerauth-java-server/pom.xml
@@ -29,7 +29,7 @@
io.getlime.security
powerauth-server-parent
- 1.7.0-SNAPSHOT
+ 1.7.0
diff --git a/powerauth-rest-client-spring/pom.xml b/powerauth-rest-client-spring/pom.xml
index 31302c4f4..26d8bdc78 100644
--- a/powerauth-rest-client-spring/pom.xml
+++ b/powerauth-rest-client-spring/pom.xml
@@ -28,7 +28,7 @@
io.getlime.security
powerauth-server-parent
- 1.7.0-SNAPSHOT
+ 1.7.0
From 8b8442aa16cfbeb367382b8526b9e8357d3e13c7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Lubo=C5=A1=20Ra=C4=8Dansk=C3=BD?=
Date: Thu, 22 Feb 2024 10:58:14 +0100
Subject: [PATCH 083/146] Fix #1308: Operation list fails on Oracle (#1317)
* Fix #1308: Operation list fails on Oracle
---
.../repository/OperationRepository.java | 47 +++++++++++--------
.../repository/OperationRepositoryTest.java | 24 +++++-----
.../repository/OperationRepositoryTest.sql | 17 ++++---
3 files changed, 50 insertions(+), 38 deletions(-)
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/repository/OperationRepository.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/repository/OperationRepository.java
index 4339dd57a..fac985f5a 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/repository/OperationRepository.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/repository/OperationRepository.java
@@ -36,6 +36,7 @@
* Database repository for the operations.
*
* @author Petr Dvorak, petr@wultra.com
+ * @implSpec Oracle does not support {@code DISTINCT} on {@code CLOB} so subselects have to be used.
*/
@Repository
public interface OperationRepository extends CrudRepository {
@@ -48,32 +49,40 @@ public interface OperationRepository extends CrudRepository findOperation(String operationId);
@Query("""
- SELECT DISTINCT o FROM OperationEntity o INNER JOIN o.applications a
- WHERE o.userId = :userId
- AND a.id in :applicationIds
- AND (:activationId IS NULL OR o.activationId IS NULL OR o.activationId = :activationId)
- AND (:activationFlags IS NULL OR o.activationFlag IS NULL OR o.activationFlag IN :activationFlags)
- ORDER BY o.timestampCreated DESC
- """)
+ SELECT o FROM OperationEntity o WHERE o.id IN (SELECT o.id FROM OperationEntity o INNER JOIN o.applications a
+ WHERE o.userId = :userId
+ AND a.id in :applicationIds
+ AND (:activationId IS NULL OR o.activationId IS NULL OR o.activationId = :activationId)
+ AND (:activationFlags IS NULL OR o.activationFlag IS NULL OR o.activationFlag IN :activationFlags))
+ ORDER BY o.timestampCreated DESC
+ """)
Stream findAllOperationsForUser(String userId, List applicationIds, String activationId, List activationFlags, final Pageable pageable);
@Query("""
- SELECT DISTINCT o FROM OperationEntity o INNER JOIN o.applications a
- WHERE o.userId = :userId
- AND a.id IN :applicationIds
- AND o.status = io.getlime.security.powerauth.app.server.database.model.enumeration.OperationStatusDo.PENDING
- AND (:activationId IS NULL OR o.activationId IS NULL OR o.activationId = :activationId)
- AND (:activationFlags IS NULL OR o.activationFlag IS NULL OR o.activationFlag IN :activationFlags)
- ORDER BY o.timestampCreated DESC
- """)
+ SELECT o FROM OperationEntity o WHERE o.id IN (SELECT o.id FROM OperationEntity o INNER JOIN o.applications a
+ WHERE o.userId = :userId
+ AND a.id IN :applicationIds
+ AND o.status = io.getlime.security.powerauth.app.server.database.model.enumeration.OperationStatusDo.PENDING
+ AND (:activationId IS NULL OR o.activationId IS NULL OR o.activationId = :activationId)
+ AND (:activationFlags IS NULL OR o.activationFlag IS NULL OR o.activationFlag IN :activationFlags))
+ ORDER BY o.timestampCreated DESC
+ """)
Stream findPendingOperationsForUser(String userId, List applicationIds, String activationId, List activationFlags, final Pageable pageable);
- @Query("SELECT DISTINCT o FROM OperationEntity o INNER JOIN o.applications a WHERE o.externalId = :externalId AND a.id IN :applicationIds ORDER BY o.timestampCreated DESC")
+ @Query("""
+ SELECT o FROM OperationEntity o WHERE o.id IN (SELECT o.id FROM OperationEntity o INNER JOIN o.applications a
+ WHERE o.externalId = :externalId
+ AND a.id IN :applicationIds)
+ ORDER BY o.timestampCreated DESC
+ """)
Stream findOperationsByExternalId(String externalId, List applicationIds, final Pageable pageable);
- @Query("SELECT DISTINCT o FROM OperationEntity o " +
- "WHERE o.timestampExpires < :timestamp AND o.status = io.getlime.security.powerauth.app.server.database.model.enumeration.OperationStatusDo.PENDING " +
- "ORDER BY o.timestampCreated")
+ @Query("""
+ SELECT o FROM OperationEntity o
+ WHERE o.timestampExpires < :timestamp
+ AND o.status = io.getlime.security.powerauth.app.server.database.model.enumeration.OperationStatusDo.PENDING
+ ORDER BY o.timestampCreated
+ """)
Stream findExpiredPendingOperations(Date timestamp);
}
diff --git a/powerauth-java-server/src/test/java/io/getlime/security/powerauth/app/server/database/repository/OperationRepositoryTest.java b/powerauth-java-server/src/test/java/io/getlime/security/powerauth/app/server/database/repository/OperationRepositoryTest.java
index 38c9e6f73..3e2226e37 100644
--- a/powerauth-java-server/src/test/java/io/getlime/security/powerauth/app/server/database/repository/OperationRepositoryTest.java
+++ b/powerauth-java-server/src/test/java/io/getlime/security/powerauth/app/server/database/repository/OperationRepositoryTest.java
@@ -45,11 +45,11 @@
@Sql
class OperationRepositoryTest {
- private static final String userId = "testUser";
- private static final List applicationIds = Arrays.asList("PA_Tests", "PA_Tests2");
- private static final String activationId1 = "e43a5dec-afea-4a10-a80b-b2183399f16b";
- private static final String activationId2 = "68c5ca56-b419-4653-949f-49061a4be886";
- private static final Pageable pageable = PageRequest.of(0, 10);
+ private static final String USER_ID = "testUser";
+ private static final List APPLICATION_IDS = Arrays.asList("PA_Tests", "PA_Tests2", "PA_Tests3");
+ private static final String ACTIVATION_ID1 = "e43a5dec-afea-4a10-a80b-b2183399f16b";
+ private static final String ACTIVATION_ID2 = "68c5ca56-b419-4653-949f-49061a4be886";
+ private static final Pageable PAGEABLE = PageRequest.of(0, 10);
@Autowired
private OperationRepository operationRepository;
@@ -74,13 +74,13 @@ void testFindOperationById() {
@Test
void testFindOperationsWithActivationIdFilter() {
final List operations1 = operationRepository.
- findAllOperationsForUser(userId, applicationIds, activationId1, null, pageable).toList();
+ findAllOperationsForUser(USER_ID, APPLICATION_IDS, ACTIVATION_ID1, null, PAGEABLE).toList();
assertNotNull(operations1);
assertEquals(3, operations1.size());
final List operations2 = operationRepository.
- findAllOperationsForUser(userId, applicationIds, activationId2, null, pageable).toList();
+ findAllOperationsForUser(USER_ID, APPLICATION_IDS, ACTIVATION_ID2, null, PAGEABLE).toList();
assertNotNull(operations2);
assertEquals(4, operations2.size());
@@ -92,23 +92,23 @@ void testFindOperationsWithActivationIdFilter() {
*/
@Test
void testFindOperationsWithActivationFlagFilter() {
- final List activationFlags1 = activationRepository.findActivationWithoutLock(activationId1).getFlags();
- final List activationFlags2 = activationRepository.findActivationWithoutLock(activationId2).getFlags();
+ final List activationFlags1 = activationRepository.findActivationWithoutLock(ACTIVATION_ID1).getFlags();
+ final List activationFlags2 = activationRepository.findActivationWithoutLock(ACTIVATION_ID2).getFlags();
final List nonExistingFlags = List.of("NOT_EXISTING");
final List operations1 = operationRepository.
- findAllOperationsForUser(userId, applicationIds, null, activationFlags1, pageable).toList();
+ findAllOperationsForUser(USER_ID, APPLICATION_IDS, null, activationFlags1, PAGEABLE).toList();
assertNotNull(operations1);
assertEquals(6, operations1.size());
final List operations2 = operationRepository.
- findAllOperationsForUser(userId, applicationIds, null, activationFlags2, pageable).toList();
+ findAllOperationsForUser(USER_ID, APPLICATION_IDS, null, activationFlags2, PAGEABLE).toList();
assertNotNull(operations2);
assertEquals(5, operations2.size());
final List operations3 = operationRepository.
- findAllOperationsForUser(userId, applicationIds, null, nonExistingFlags, pageable).toList();
+ findAllOperationsForUser(USER_ID, APPLICATION_IDS, null, nonExistingFlags, PAGEABLE).toList();
assertNotNull(operations3);
assertEquals(2, operations3.size());
diff --git a/powerauth-java-server/src/test/resources/io/getlime/security/powerauth/app/server/database/repository/OperationRepositoryTest.sql b/powerauth-java-server/src/test/resources/io/getlime/security/powerauth/app/server/database/repository/OperationRepositoryTest.sql
index a46787cfe..983c32631 100644
--- a/powerauth-java-server/src/test/resources/io/getlime/security/powerauth/app/server/database/repository/OperationRepositoryTest.sql
+++ b/powerauth-java-server/src/test/resources/io/getlime/security/powerauth/app/server/database/repository/OperationRepositoryTest.sql
@@ -1,8 +1,10 @@
INSERT INTO pa_application (id, name, roles)
-VALUES (21, 'PA_Tests', '[ "ROLE3", "ROLE4" ]');
+VALUES (21, 'PA_Tests', '[ "ROLE3", "ROLE4" ]'),
+ (22, 'PA_Tests2', '[ "ROLE_ADMIN" ]');
INSERT INTO pa_master_keypair (id, application_id, master_key_private_base64, master_key_public_base64, name, timestamp_created) VALUES
- (21, 21, 'KdcJHQAT/BBF+26uBGNhGC0GQ93ncTx7V6kusNA8AdE=', 'BP8ZZ0LjiwRCQPob3NFwF9pPDLhxCjnPNmENzayEeeGCiDdk0gl3UzUhYk9ntMg18LZdhpvYnprZ8mk/71WlQqo=', 'PA_Tests Default Keypair', '2022-06-07 09:13:27.599000');
+ (21, 21, 'KdcJHQAT/BBF+26uBGNhGC0GQ93ncTx7V6kusNA8AdE=', 'BP8ZZ0LjiwRCQPob3NFwF9pPDLhxCjnPNmENzayEeeGCiDdk0gl3UzUhYk9ntMg18LZdhpvYnprZ8mk/71WlQqo=', 'PA_Tests Default Keypair', '2022-06-07 09:13:27.599000'),
+ (22, 22, 'ALMo+ZbnbelAs7oL5FsD8IocDC15nITu9+RO1xvfWFaY', 'BEr2c6ikVz6fQ2FL7SXcl1vxcksGB+/EbNbI1Ma7al/1jr455+b6f9dKhAupA3WIqcCRx6qpw9CoWdSVEbVZxzY=', 'PA_Tests2 Default Keypair', '2022-06-07 09:13:27.599000');
INSERT INTO pa_activation (activation_id, application_id, user_id, activation_name, activation_code, activation_status, activation_otp, activation_otp_validation, blocked_reason, counter, ctr_data, device_public_key_base64, extras, platform, device_info, flags, failed_attempts, max_failed_attempts, server_private_key_base64, server_private_key_encryption, server_public_key_base64, timestamp_activation_expire, timestamp_created, timestamp_last_used, timestamp_last_change, master_keypair_id, version)
VALUES ('e43a5dec-afea-4a10-a80b-b2183399f16b', 21, 'testUser', 'test v4', 'PXSNR-E2B46-7TY3G-TMR2Q', 3, null, 0, null, 0, 'D5XibWWPCv+nOOfcdfnUGQ==', 'BF3Sc/vqg8Zk70Y8rbT45xzAIxblGoWgLqknCHuNj7f6QFBNi2UnLbG7yMqf2eWShhyBJdu9zqx7DG2qzlqhbBE=', null, 'unknown', 'backend-tests', '[ "test-flag1", "test-flag2", "test-flag3" ]', 0, 1, 'PUz/He8+RFoOPS1NG6Gw3TDXIQ/DnS1skNBOQWzXX60=', 0, 'BPHJ4N90NUuLDq92FJUPcaKZOMad1KH2HrwQEN9DB5ST5fiJU4baYF1VlK1JHglnnN1miL3/Qb6IyW3YSMBySYM=', '2023-04-03 14:04:06.015000', '2023-04-03 13:59:06.015000', '2023-04-03 13:59:16.293000', '2023-04-03 13:59:16.343000', 21, 3);
@@ -30,8 +32,9 @@ VALUES ('2067b5d1-1c50-43eb-99df-847830e4807a', 'testUser', null, null, 'login',
insert into pa_operation_application (application_id, operation_id)
VALUES (21, '0f038bac-6c94-45eb-b3a9-f92e809e8ea4'),
- (21, '70702b41-7b5a-4a6d-9bc2-e99949c2df53'),
- (21, 'f451bcb7-9d76-42d6-97a6-76a9ecaf25813'),
- (21, 'b6e41a83-6357-4670-ac4c-1f7dcaf2aa9e'),
- (21, 'e708e33e-e1cb-48e2-9b51-521a3d205330'),
- (21, '2067b5d1-1c50-43eb-99df-847830e4807a');
\ No newline at end of file
+ (21, '70702b41-7b5a-4a6d-9bc2-e99949c2df53'),
+ (21, 'f451bcb7-9d76-42d6-97a6-76a9ecaf25813'),
+ (21, 'b6e41a83-6357-4670-ac4c-1f7dcaf2aa9e'),
+ (21, 'e708e33e-e1cb-48e2-9b51-521a3d205330'),
+ (21, '2067b5d1-1c50-43eb-99df-847830e4807a'),
+ (22, '0f038bac-6c94-45eb-b3a9-f92e809e8ea4');
\ No newline at end of file
From 27792c34a5ee626f692cada7ff455bae101e44c5 Mon Sep 17 00:00:00 2001
From: Lubos Racansky
Date: Thu, 22 Feb 2024 11:11:56 +0100
Subject: [PATCH 084/146] Fix #1330: Add liquibase tag 1.7.0
---
.../1.7.x/20240222-add-tag-1.7.0.xml | 10 ++++++++++
.../1.7.x/db.changelog-version.xml | 1 +
2 files changed, 11 insertions(+)
create mode 100644 docs/db/changelog/changesets/powerauth-java-server/1.7.x/20240222-add-tag-1.7.0.xml
diff --git a/docs/db/changelog/changesets/powerauth-java-server/1.7.x/20240222-add-tag-1.7.0.xml b/docs/db/changelog/changesets/powerauth-java-server/1.7.x/20240222-add-tag-1.7.0.xml
new file mode 100644
index 000000000..ddc21c862
--- /dev/null
+++ b/docs/db/changelog/changesets/powerauth-java-server/1.7.x/20240222-add-tag-1.7.0.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
diff --git a/docs/db/changelog/changesets/powerauth-java-server/1.7.x/db.changelog-version.xml b/docs/db/changelog/changesets/powerauth-java-server/1.7.x/db.changelog-version.xml
index 0838ac2b2..219d5fbdb 100644
--- a/docs/db/changelog/changesets/powerauth-java-server/1.7.x/db.changelog-version.xml
+++ b/docs/db/changelog/changesets/powerauth-java-server/1.7.x/db.changelog-version.xml
@@ -5,5 +5,6 @@
+
From 38ea2d116516c596d99449f8415b960809ac82a0 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 23 Feb 2024 05:59:48 +0000
Subject: [PATCH 085/146] Bump
org.springframework.boot:spring-boot-starter-parent
Bumps [org.springframework.boot:spring-boot-starter-parent](https://github.com/spring-projects/spring-boot) from 3.2.2 to 3.2.3.
- [Release notes](https://github.com/spring-projects/spring-boot/releases)
- [Commits](https://github.com/spring-projects/spring-boot/compare/v3.2.2...v3.2.3)
---
updated-dependencies:
- dependency-name: org.springframework.boot:spring-boot-starter-parent
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
---
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index eb8843bc3..5af74d3d5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -32,7 +32,7 @@
org.springframework.boot
spring-boot-starter-parent
- 3.2.2
+ 3.2.3
From 403ccd9ce1ea5c85f7c81f85fa5e66a6aa1d541b Mon Sep 17 00:00:00 2001
From: Roman Strobl
Date: Fri, 23 Feb 2024 15:35:34 +0800
Subject: [PATCH 086/146] Fix #1337: Missing NOT NULL constraint in migration
guide
---
docs/Database-Structure.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/Database-Structure.md b/docs/Database-Structure.md
index ad1f8d9a8..5654bf88b 100644
--- a/docs/Database-Structure.md
+++ b/docs/Database-Structure.md
@@ -98,7 +98,7 @@ CREATE TABLE pa_application_config
(
id INTEGER NOT NULL PRIMARY KEY,
application_id INTEGER NOT NULL,
- config_key VARCHAR(255),
+ config_key VARCHAR(255) NOT NULL,
config_values TEXT
);
```
From ce86fcf56acbaba32da96a023e0b2a071a1ef393 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 26 Feb 2024 01:22:33 +0000
Subject: [PATCH 087/146] Bump nl.jqno.equalsverifier:equalsverifier from
3.15.6 to 3.15.7
Bumps [nl.jqno.equalsverifier:equalsverifier](https://github.com/jqno/equalsverifier) from 3.15.6 to 3.15.7.
- [Release notes](https://github.com/jqno/equalsverifier/releases)
- [Changelog](https://github.com/jqno/equalsverifier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/jqno/equalsverifier/compare/equalsverifier-3.15.6...equalsverifier-3.15.7)
---
updated-dependencies:
- dependency-name: nl.jqno.equalsverifier:equalsverifier
dependency-type: direct:development
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
---
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index 5af74d3d5..89bc66961 100644
--- a/pom.xml
+++ b/pom.xml
@@ -102,7 +102,7 @@
1.11.0
7.4
- 3.15.6
+ 3.15.7
3.5.3
From a0f632f267177ba89d19368da8fb62dfc017ae0c Mon Sep 17 00:00:00 2001
From: Lubos Racansky
Date: Mon, 26 Feb 2024 07:37:36 +0100
Subject: [PATCH 088/146] Fix #1341: Coverity: Useless call
---
.../powerauth/rest/client/PowerAuthFido2RestClient.java | 2 --
1 file changed, 2 deletions(-)
diff --git a/powerauth-rest-client-spring/src/main/java/com/wultra/security/powerauth/rest/client/PowerAuthFido2RestClient.java b/powerauth-rest-client-spring/src/main/java/com/wultra/security/powerauth/rest/client/PowerAuthFido2RestClient.java
index 3fda9e846..558bc7f2b 100644
--- a/powerauth-rest-client-spring/src/main/java/com/wultra/security/powerauth/rest/client/PowerAuthFido2RestClient.java
+++ b/powerauth-rest-client-spring/src/main/java/com/wultra/security/powerauth/rest/client/PowerAuthFido2RestClient.java
@@ -30,7 +30,6 @@
import com.wultra.security.powerauth.client.model.error.PowerAuthClientException;
import com.wultra.security.powerauth.client.model.error.PowerAuthError;
import com.wultra.security.powerauth.client.model.request.fido2.*;
-import com.wultra.security.powerauth.client.model.response.InitActivationResponse;
import com.wultra.security.powerauth.client.model.response.fido2.*;
import io.getlime.core.rest.model.base.request.ObjectRequest;
import io.getlime.core.rest.model.base.response.ObjectResponse;
@@ -84,7 +83,6 @@ public PowerAuthFido2RestClient(String baseUrl, PowerAuthRestClientConfiguration
if (config.getProxyUsername() != null) {
proxyBuilder.username(config.getProxyUsername()).password(config.getProxyPassword());
}
- proxyBuilder.build();
}
if (config.getPowerAuthClientToken() != null) {
builder.httpBasicAuth().username(config.getPowerAuthClientToken()).password(config.getPowerAuthClientSecret()).build();
From 9782d548fd323ea850a5c8b35b579deee1affbbc Mon Sep 17 00:00:00 2001
From: Lubos Racansky
Date: Mon, 26 Feb 2024 07:53:18 +0100
Subject: [PATCH 089/146] Fix #1343: Coverity: Dereference null return value
---
.../serialization/AttestationStatementDeserializer.java | 4 ++++
.../serialization/AuthenticatorDataDeserializer.java | 6 ++++++
2 files changed, 10 insertions(+)
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AttestationStatementDeserializer.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AttestationStatementDeserializer.java
index d2a0b2c4a..b99b6392b 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AttestationStatementDeserializer.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AttestationStatementDeserializer.java
@@ -70,6 +70,10 @@ public AttestationStatementDeserializer(Class vc) {
public AttestationStatement deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws Fido2DeserializationException {
try {
final Map map = jsonParser.readValueAs(new TypeReference<>() {});
+ if (map == null) {
+ throw new Fido2DeserializationException("JSON deserialized into null.");
+ }
+
final AttestationStatement result = new AttestationStatement();
final Integer alg = (Integer) map.get("alg");
if (alg != null && -7 == alg) {
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AuthenticatorDataDeserializer.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AuthenticatorDataDeserializer.java
index a14a52455..0b7f5a9fc 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AuthenticatorDataDeserializer.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/AuthenticatorDataDeserializer.java
@@ -82,6 +82,9 @@ public AuthenticatorData deserialize(JsonParser jsonParser, DeserializationConte
// Serialize Auth Data
final byte[] authData = jsonParser.getBinaryValue();
+ if (authData == null) {
+ throw new Fido2DeserializationException("JSON binary value deserialized into null.");
+ }
result.setEncoded(authData);
// Get RP ID Hash
@@ -131,6 +134,9 @@ public AuthenticatorData deserialize(JsonParser jsonParser, DeserializationConte
final byte[] credentialPublicKey = new byte[remainingLength];
System.arraycopy(authData, 55 + credentialIdLengthValue, credentialPublicKey, 0, remainingLength);
final Map credentialPublicKeyMap = cborMapper.readValue(credentialPublicKey, new TypeReference<>() {});
+ if (credentialPublicKeyMap == null) {
+ throw new Fido2DeserializationException("JSON credentialPublicKey deserialized into null.");
+ }
final PublicKeyObject publicKeyObject = new PublicKeyObject();
final Integer algorithm = (Integer) credentialPublicKeyMap.get("3");
From a5e8dba91a90cf791033def23747864aced4d1cb Mon Sep 17 00:00:00 2001
From: Lubos Racansky
Date: Mon, 26 Feb 2024 08:00:38 +0100
Subject: [PATCH 090/146] Fix #1345: Coverity: Reliance on default encoding
---
.../serialization/CollectedClientDataDeserializer.java | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/CollectedClientDataDeserializer.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/CollectedClientDataDeserializer.java
index ddb5615fa..0066dbac6 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/CollectedClientDataDeserializer.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/serialization/CollectedClientDataDeserializer.java
@@ -30,6 +30,7 @@
import java.io.IOException;
import java.io.Serial;
+import java.nio.charset.StandardCharsets;
import java.util.Base64;
/**
@@ -74,7 +75,7 @@ public CollectedClientData deserialize(JsonParser jsonParser, DeserializationCon
final String originalTextValue = jsonParser.getText();
final byte[] decodedClientDataJSON = Base64.getDecoder().decode(originalTextValue);
final CollectedClientData collectedClientData = objectMapper.readValue(decodedClientDataJSON, CollectedClientData.class);
- collectedClientData.setEncoded(new String(decodedClientDataJSON));
+ collectedClientData.setEncoded(new String(decodedClientDataJSON, StandardCharsets.UTF_8));
return collectedClientData;
} catch (IOException e) {
logger.debug(e.getMessage(), e);
From 776d17d1208e39820ee56105814b7a4e6243b755 Mon Sep 17 00:00:00 2001
From: Lubos Racansky
Date: Mon, 26 Feb 2024 08:51:03 +0100
Subject: [PATCH 091/146] Fix #1349: Coverity: Unlogged security exception
---
.../server/service/fido2/PowerAuthAuthenticatorProvider.java | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAuthenticatorProvider.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAuthenticatorProvider.java
index b96355997..4213a059e 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAuthenticatorProvider.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAuthenticatorProvider.java
@@ -192,6 +192,8 @@ public AuthenticatorDetail storeAuthenticator(String applicationId, String activ
try {
devicePublicKey = keyConvertor.convertBytesToPublicKey(devicePublicKeyBytes);
} catch (InvalidKeySpecException ex) {
+ logger.warn("Invalid public key, activation ID: {}, {}", activation.getActivationId(), ex.getMessage());
+ logger.debug("Invalid public key, activation ID: {}", activation.getActivationId(), ex);
handleInvalidPublicKey(activation);
}
@@ -377,7 +379,6 @@ private void handleInvalidPublicKey(ActivationRecordEntity activation) throws Ge
activation.setActivationStatus(io.getlime.security.powerauth.app.server.database.model.enumeration.ActivationStatus.REMOVED);
serviceBehaviorCatalogue.getActivationHistoryServiceBehavior().saveActivationAndLogChange(activation);
serviceBehaviorCatalogue.getCallbackUrlBehavior().notifyCallbackListenersOnActivationChange(activation);
- logger.warn("Invalid public key, activation ID: {}", activation.getActivationId());
// Exception must not be rollbacking, otherwise data written to database in this method would be lost
throw localizationProvider.buildExceptionForCode(ServiceError.ACTIVATION_NOT_FOUND);
}
From 03d8f53bfb0663ef7518bb5cfd7cc3c6abba8783 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Lubo=C5=A1=20Ra=C4=8Dansk=C3=BD?=
Date: Mon, 26 Feb 2024 09:20:58 +0100
Subject: [PATCH 092/146] Fix #1347: Make AaguidList#vendors immutable (#1348)
* Fix #1347: Make AaguidList#vendors immutable
* Remove duplicate keys
---
.../converter/RegistrationConverter.java | 4 +-
.../fido2/rest/model/entity/AaguidList.java | 296 +++++++++---------
2 files changed, 148 insertions(+), 152 deletions(-)
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/RegistrationConverter.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/RegistrationConverter.java
index 4f2e5883c..303dd3bd6 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/RegistrationConverter.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/RegistrationConverter.java
@@ -43,8 +43,6 @@
@Slf4j
public class RegistrationConverter {
- private final AaguidList aaguidRegistry = new AaguidList();
-
/**
* Convert registration challenge to authenticator detail.
* @param challenge Registration challenge.
@@ -64,7 +62,7 @@ public Optional convert(RegistrationChallenge challenge, Re
authenticatorDetail.setExtras(convertExtras(requestObject));
authenticatorDetail.setActivationName(requestObject.getActivationName());
authenticatorDetail.setPlatform(requestObject.getAuthenticatorParameters().getAuthenticatorAttachment());
- authenticatorDetail.setDeviceInfo(aaguidRegistry.vendorName(aaguid));
+ authenticatorDetail.setDeviceInfo(AaguidList.vendorName(aaguid));
authenticatorDetail.setActivationStatus(ActivationStatus.ACTIVE);
authenticatorDetail.setActivationFlags(new ArrayList<>());
authenticatorDetail.setApplicationRoles(new ArrayList<>());
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AaguidList.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AaguidList.java
index ed4af7695..2d39f2848 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AaguidList.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AaguidList.java
@@ -18,7 +18,6 @@
package com.wultra.powerauth.fido2.rest.model.entity;
-import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
@@ -28,167 +27,166 @@
*
* @author Petr Dvorak, petr@wultra.com
*/
-public class AaguidList {
+public final class AaguidList {
- private static final Map vendors = new HashMap<>();
+ private static final Map VENDORS = Map.ofEntries(
- public AaguidList() {
+ // Android
+ Map.entry(UUID.fromString("b93fd961-f2e6-462f-b122-82002247de78"), "Android Authenticator with SafetyNet Attestation"),
- // Android
- vendors.put(UUID.fromString("b93fd961-f2e6-462f-b122-82002247de78"), "Android Authenticator with SafetyNet Attestation");
+ // ATKey
+ Map.entry(UUID.fromString("ba76a271-6eb6-4171-874d-b6428dbe3437"), "ATKey.ProS"),
+ Map.entry(UUID.fromString("d41f5a69-b817-4144-a13c-9ebd6d9254d6"), "ATKey.Card CTAP2.0"),
+ Map.entry(UUID.fromString("e1a96183-5016-4f24-b55b-e3ae23614cc6"), "ATKey.Pro CTAP2.0"),
+ Map.entry(UUID.fromString("e416201b-afeb-41ca-a03d-2281c28322aa"), "ATKey.Pro CTAP2.1"),
- // ATKey
- vendors.put(UUID.fromString("ba76a271-6eb6-4171-874d-b6428dbe3437"), "ATKey.ProS");
- vendors.put(UUID.fromString("d41f5a69-b817-4144-a13c-9ebd6d9254d6"), "ATKey.Card CTAP2.0");
- vendors.put(UUID.fromString("e1a96183-5016-4f24-b55b-e3ae23614cc6"), "ATKey.Pro CTAP2.0");
- vendors.put(UUID.fromString("e416201b-afeb-41ca-a03d-2281c28322aa"), "ATKey.Pro CTAP2.1");
+ // Atos
+ Map.entry(UUID.fromString("1c086528-58d5-f211-823c-356786e36140"), "Atos CardOS FIDO2"),
- // Atos
- vendors.put(UUID.fromString("1c086528-58d5-f211-823c-356786e36140"), "Atos CardOS FIDO2");
+ // Crayonic
+ Map.entry(UUID.fromString("be727034-574a-f799-5c76-0929e0430973"), "Crayonic KeyVault K1 (USB-NFC-BLE FIDO2 Authenticator)"),
- // Crayonic
- vendors.put(UUID.fromString("be727034-574a-f799-5c76-0929e0430973"), "Crayonic KeyVault K1 (USB-NFC-BLE FIDO2 Authenticator)");
+ // Cryptnox
+ Map.entry(UUID.fromString("9c835346-796b-4c27-8898-d6032f515cc5"), "Cryptnox FIDO2"),
- // Cryptnox
- vendors.put(UUID.fromString("9c835346-796b-4c27-8898-d6032f515cc5"), "Cryptnox FIDO2");
+ // Ensurity
+ Map.entry(UUID.fromString("454e5346-4944-4ffd-6c93-8e9267193e9a"), "Ensurity ThinC"),
- // Ensurity
- vendors.put(UUID.fromString("454e5346-4944-4ffd-6c93-8e9267193e9a"), "Ensurity ThinC");
+ // ESS
+ Map.entry(UUID.fromString("5343502d-5343-5343-6172-644649444f32"), "ESS Smart Card Inc. Authenticator"),
- // ESS
- vendors.put(UUID.fromString("5343502d-5343-5343-6172-644649444f32"), "ESS Smart Card Inc. Authenticator");
+ // eWBM
+ Map.entry(UUID.fromString("61250591-b2bc-4456-b719-0b17be90bb30"), "eWBM eFPA FIDO2 Authenticator"),
- // eWBM
- vendors.put(UUID.fromString("61250591-b2bc-4456-b719-0b17be90bb30"), "eWBM eFPA FIDO2 Authenticator");
- vendors.put(UUID.fromString("87dbc5a1-4c94-4dc8-8a47-97d800fd1f3c"), "eWBM eFA320 FIDO2 Authenticator");
- vendors.put(UUID.fromString("95442b2e-f15e-4def-b270-efb106facb4e"), "eWBM eFA310 FIDO2 Authenticator");
-
- // FEITIAN
- vendors.put(UUID.fromString("12ded745-4bed-47d4-abaa-e713f51d6393"), "AllinPass FIDO");
- vendors.put(UUID.fromString("2c0df832-92de-4be1-8412-88a8f074df4a"), "FIDO Java Card");
- vendors.put(UUID.fromString("310b2830-bd4a-4da5-832e-9a0dfc90abf2"), "MultiPass FIDO");
- vendors.put(UUID.fromString("3e22415d-7fdf-4ea4-8a0c-dd60c4249b9d"), "Feitian iePass FIDO Authenticator");
- vendors.put(UUID.fromString("6e22415d-7fdf-4ea4-8a0c-dd60c4249b9d"), "iePass FIDO");
- vendors.put(UUID.fromString("77010bd7-212a-4fc9-b236-d2ca5e9d4084"), "BioPass FIDO");
- vendors.put(UUID.fromString("833b721a-ff5f-4d00-bb2e-bdda3ec01e29"), "ePassFIDO K10, A4B, K28");
- vendors.put(UUID.fromString("8c97a730-3f7b-41a6-87d6-1e9b62bda6f0"), "FIDO Fingerprint Card");
- vendors.put(UUID.fromString("b6ede29c-3772-412c-8a78-539c1f4c62d2"), "BioPass FIDO Plus");
- vendors.put(UUID.fromString("ee041bce-25e5-4cdb-8f86-897fd6418464"), "ePassFIDO K39, NFC, NFC Plus");
-
- // GoTrust
- vendors.put(UUID.fromString("3b1adb99-0dfe-46fd-90b8-7f7614a4de2a"), "GoTrust Idem Key FIDO2 Authenticator");
- vendors.put(UUID.fromString("9f0d8150-baa5-4c00-9299-ad62c8bb4e87"), "GoTrust Idem Card FIDO2 Authenticator");
-
- // HID Global
- vendors.put(UUID.fromString("54d9fee8-e621-4291-8b18-7157b99c5bec"), "HID Crescendo Enabled");
- vendors.put(UUID.fromString("692db549-7ae5-44d5-a1e5-dd20a493b723"), "HID Crescendo Key");
- vendors.put(UUID.fromString("aeb6569c-f8fb-4950-ac60-24ca2bbe2e52"), "HID Crescendo C2300");
-
- // Hideez
- vendors.put(UUID.fromString("3e078ffd-4c54-4586-8baa-a77da113aec5"), "Hideez Key 3 FIDO2");
- vendors.put(UUID.fromString("4e768f2c-5fab-48b3-b300-220eb487752b"), "Hideez Key 4 FIDO2 SDK");
-
- // Hyper
- vendors.put(UUID.fromString("9f77e279-a6e2-4d58-b700-31e5943c6a98"), "Hyper FIDO Pro");
- vendors.put(UUID.fromString("d821a7d4-e97c-4cb6-bd82-4237731fd4be"), "Hyper FIDO Bio Security Key");
-
- // MKGroup
- vendors.put(UUID.fromString("f4c63eff-d26c-4248-801c-3736c7eaa93a"), "FIDO KeyPass S3");
-
- // KEY-ID
- vendors.put(UUID.fromString("d91c5288-0ef0-49b7-b8ae-21ca0aa6b3f3"), "KEY-ID FIDO2 Authenticator");
-
- // NEOWAVE
- vendors.put(UUID.fromString("3789da91-f943-46bc-95c3-50ea2012f03a"), "NEOWAVE Winkeo FIDO2");
- vendors.put(UUID.fromString("c5703116-972b-4851-a3e7-ae1259843399"), "NEOWAVE Badgeo FIDO2");
-
- // NXP Semiconductors
- vendors.put(UUID.fromString("07a9f89c-6407-4594-9d56-621d5f1e358b"), "NXP Semiconductors FIDO2 Conformance Testing CTAP2 Authenticator");
-
- // OCTATCO
- vendors.put(UUID.fromString("a1f52be5-dfab-4364-b51c-2bd496b14a56"), "OCTATCO EzFinger2 FIDO2 AUTHENTICATOR");
- vendors.put(UUID.fromString("bc2fe499-0d8e-4ffe-96f3-94a82840cf8c"), "OCTATCO EzQuant FIDO2 AUTHENTICATOR");
-
- // OneSpan
- vendors.put(UUID.fromString("30b5035e-d297-4fc1-b00b-addc96ba6a97"), "OneSpan FIDO Touch");
-
- // Precision InnaIT
- vendors.put(UUID.fromString("88bbd2f0-342a-42e7-9729-dd158be5407a"), "Precision InnaIT Key FIDO 2 Level 2 certified");
-
- // SmartDisplayer
- vendors.put(UUID.fromString("516d3969-5a57-5651-5958-4e7a49434167"), "SmartDisplayer BobeePass (NFC-BLE FIDO2 Authenticator)");
-
- // Solo
- vendors.put(UUID.fromString("8876631b-d4a0-427f-5773-0ec71c9e0279"), "Solo Secp256R1 FIDO2 CTAP2 Authenticator");
- vendors.put(UUID.fromString("8976631b-d4a0-427f-5773-0ec71c9e0279"), "Solo Tap Secp256R1 FIDO2 CTAP2 Authenticator");
-
- // Somu
- vendors.put(UUID.fromString("9876631b-d4a0-427f-5773-0ec71c9e0279"), "Somu Secp256R1 FIDO2 CTAP2 Authenticator");
-
- // Swissbit
- vendors.put(UUID.fromString("931327dd-c89b-406c-a81e-ed7058ef36c6"), "Swissbit iShield FIDO2");
-
- // Thales
- vendors.put(UUID.fromString("b50d5e0a-7f81-4959-9b12-f45407407503"), "Thales IDPrime MD 3940 FIDO");
- vendors.put(UUID.fromString("efb96b10-a9ee-4b6c-a4a9-d32125ccd4a4"), "Thales eToken FIDO");
-
- // TrustKey
- vendors.put(UUID.fromString("95442b2e-f15e-4def-b270-efb106facb4e"), "TrustKey G310(H)");
- vendors.put(UUID.fromString("87dbc5a1-4c94-4dc8-8a47-97d800fd1f3c"), "TrustKey G320(H)");
- vendors.put(UUID.fromString("da776f39-f6c8-4a89-b252-1d86137a46ba"), "TrustKey T110");
- vendors.put(UUID.fromString("e3512a8a-62ae-11ea-bc55-0242ac130003"), "TrustKey T120");
-
- // TOKEN2
- vendors.put(UUID.fromString("ab32f0c6-2239-afbb-c470-d2ef4e254db7"), "TOKEN2 FIDO2 Security Key");
-
- // uTrust
- vendors.put(UUID.fromString("73402251-f2a8-4f03-873e-3cb6db604b03"), "uTrust FIDO2 Security Key");
-
- // Vancosys
- vendors.put(UUID.fromString("39a5647e-1853-446c-a1f6-a79bae9f5bc7"), "Vancosys Android Authenticator");
- vendors.put(UUID.fromString("820d89ed-d65a-409e-85cb-f73f0578f82a"), "Vancosys iOS Authenticator");
-
- // VinCSS
- vendors.put(UUID.fromString("5fdb81b8-53f0-4967-a881-f5ec26fe4d18"), "VinCSS FIDO2 Authenticator");
-
- // VivoKey
- vendors.put(UUID.fromString("d7a423ad-3e19-4492-9200-78137dccc136"), "VivoKey Apex FIDO2");
-
- // Windows Hello
- vendors.put(UUID.fromString("08987058-cadc-4b81-b6e1-30de50dcbe96"), "Windows Hello Hardware Authenticator");
- vendors.put(UUID.fromString("6028b017-b1d4-4c02-b4b3-afcdafc96bb2"), "Windows Hello Software Authenticator");
- vendors.put(UUID.fromString("9ddd1817-af5a-4672-a2b9-3e3dd95000a9"), "Windows Hello VBS Hardware Authenticator");
-
- // WiSECURE
- vendors.put(UUID.fromString("504d7149-4e4c-3841-4555-55445a677357"), "WiSECURE AuthTron USB FIDO2 Authenticator");
-
- // Yubico
- vendors.put(UUID.fromString("0bb43545-fd2c-4185-87dd-feb0b2916ace"), "Security Key NFC by Yubico - Enterprise Edition");
- vendors.put(UUID.fromString("149a2021-8ef6-4133-96b8-81f8d5b7f1f5"), "Security Key by Yubico with NFC");
- vendors.put(UUID.fromString("2fc0579f-8113-47ea-b116-bb5a8db9202a"), "YubiKey 5 Series with NFC");
- vendors.put(UUID.fromString("6d44ba9b-f6ec-2e49-b930-0c8fe920cb73"), "Security Key by Yubico with NFC");
- vendors.put(UUID.fromString("73bb0cd4-e502-49b8-9c6f-b59445bf720b"), "YubiKey 5 FIPS Series");
- vendors.put(UUID.fromString("85203421-48f9-4355-9bc8-8a53846e5083"), "YubiKey 5Ci FIPS");
- vendors.put(UUID.fromString("a4e9fc6d-4cbe-4758-b8ba-37598bb5bbaa"), "Security Key by Yubico with NFC");
- vendors.put(UUID.fromString("b92c3f9a-c014-4056-887f-140a2501163b"), "Security Key by Yubico");
- vendors.put(UUID.fromString("c1f9a0bc-1dd2-404a-b27f-8e29047a43fd"), "YubiKey 5 FIPS Series with NFC");
- vendors.put(UUID.fromString("c5ef55ff-ad9a-4b9f-b580-adebafe026d0"), "YubiKey 5Ci");
- vendors.put(UUID.fromString("cb69481e-8ff7-4039-93ec-0a2729a154a8"), "YubiKey 5 Series");
- vendors.put(UUID.fromString("d8522d9f-575b-4866-88a9-ba99fa02f35b"), "YubiKey Bio Series");
- vendors.put(UUID.fromString("ee882879-721c-4913-9775-3dfcce97072a"), "YubiKey 5 Series");
- vendors.put(UUID.fromString("f8a011f3-8c0a-4d15-8006-17111f9edc7d"), "Security Key by Yubico");
- vendors.put(UUID.fromString("fa2b99dc-9e39-4257-8f92-4a30d23c4118"), "YubiKey 5 Series with NFC");
- vendors.put(UUID.fromString("34f5766d-1536-4a24-9033-0e294e510fb0"), "YubiKey 5 Series CTAP2.1 Preview Expired");
- vendors.put(UUID.fromString("83c47309-aabb-4108-8470-8be838b573cb"), "YubiKey Bio Series (Enterprise Profile)");
-
- // Other authenticators
- vendors.put(UUID.fromString("ad784498-1902-3f54-b99a-10bb7dbd9588"), "Apple MacBook Pro 14-inch, 2021");
- vendors.put(UUID.nameUUIDFromBytes(new byte[16]), "Apple Passkeys");
+ // FEITIAN
+ Map.entry(UUID.fromString("12ded745-4bed-47d4-abaa-e713f51d6393"), "AllinPass FIDO"),
+ Map.entry(UUID.fromString("2c0df832-92de-4be1-8412-88a8f074df4a"), "FIDO Java Card"),
+ Map.entry(UUID.fromString("310b2830-bd4a-4da5-832e-9a0dfc90abf2"), "MultiPass FIDO"),
+ Map.entry(UUID.fromString("3e22415d-7fdf-4ea4-8a0c-dd60c4249b9d"), "Feitian iePass FIDO Authenticator"),
+ Map.entry(UUID.fromString("6e22415d-7fdf-4ea4-8a0c-dd60c4249b9d"), "iePass FIDO"),
+ Map.entry(UUID.fromString("77010bd7-212a-4fc9-b236-d2ca5e9d4084"), "BioPass FIDO"),
+ Map.entry(UUID.fromString("833b721a-ff5f-4d00-bb2e-bdda3ec01e29"), "ePassFIDO K10, A4B, K28"),
+ Map.entry(UUID.fromString("8c97a730-3f7b-41a6-87d6-1e9b62bda6f0"), "FIDO Fingerprint Card"),
+ Map.entry(UUID.fromString("b6ede29c-3772-412c-8a78-539c1f4c62d2"), "BioPass FIDO Plus"),
+ Map.entry(UUID.fromString("ee041bce-25e5-4cdb-8f86-897fd6418464"), "ePassFIDO K39, NFC, NFC Plus"),
+ // GoTrust
+ Map.entry(UUID.fromString("3b1adb99-0dfe-46fd-90b8-7f7614a4de2a"), "GoTrust Idem Key FIDO2 Authenticator"),
+ Map.entry(UUID.fromString("9f0d8150-baa5-4c00-9299-ad62c8bb4e87"), "GoTrust Idem Card FIDO2 Authenticator"),
+
+ // HID Global
+ Map.entry(UUID.fromString("54d9fee8-e621-4291-8b18-7157b99c5bec"), "HID Crescendo Enabled"),
+ Map.entry(UUID.fromString("692db549-7ae5-44d5-a1e5-dd20a493b723"), "HID Crescendo Key"),
+ Map.entry(UUID.fromString("aeb6569c-f8fb-4950-ac60-24ca2bbe2e52"), "HID Crescendo C2300"),
+
+ // Hideez
+ Map.entry(UUID.fromString("3e078ffd-4c54-4586-8baa-a77da113aec5"), "Hideez Key 3 FIDO2"),
+ Map.entry(UUID.fromString("4e768f2c-5fab-48b3-b300-220eb487752b"), "Hideez Key 4 FIDO2 SDK"),
+
+ // Hyper
+ Map.entry(UUID.fromString("9f77e279-a6e2-4d58-b700-31e5943c6a98"), "Hyper FIDO Pro"),
+ Map.entry(UUID.fromString("d821a7d4-e97c-4cb6-bd82-4237731fd4be"), "Hyper FIDO Bio Security Key"),
+
+ // MKGroup
+ Map.entry(UUID.fromString("f4c63eff-d26c-4248-801c-3736c7eaa93a"), "FIDO KeyPass S3"),
+
+ // KEY-ID
+ Map.entry(UUID.fromString("d91c5288-0ef0-49b7-b8ae-21ca0aa6b3f3"), "KEY-ID FIDO2 Authenticator"),
+
+ // NEOWAVE
+ Map.entry(UUID.fromString("3789da91-f943-46bc-95c3-50ea2012f03a"), "NEOWAVE Winkeo FIDO2"),
+ Map.entry(UUID.fromString("c5703116-972b-4851-a3e7-ae1259843399"), "NEOWAVE Badgeo FIDO2"),
+
+ // NXP Semiconductors
+ Map.entry(UUID.fromString("07a9f89c-6407-4594-9d56-621d5f1e358b"), "NXP Semiconductors FIDO2 Conformance Testing CTAP2 Authenticator"),
+
+ // OCTATCO
+ Map.entry(UUID.fromString("a1f52be5-dfab-4364-b51c-2bd496b14a56"), "OCTATCO EzFinger2 FIDO2 AUTHENTICATOR"),
+ Map.entry(UUID.fromString("bc2fe499-0d8e-4ffe-96f3-94a82840cf8c"), "OCTATCO EzQuant FIDO2 AUTHENTICATOR"),
+
+ // OneSpan
+ Map.entry(UUID.fromString("30b5035e-d297-4fc1-b00b-addc96ba6a97"), "OneSpan FIDO Touch"),
+
+ // Precision InnaIT
+ Map.entry(UUID.fromString("88bbd2f0-342a-42e7-9729-dd158be5407a"), "Precision InnaIT Key FIDO 2 Level 2 certified"),
+
+ // SmartDisplayer
+ Map.entry(UUID.fromString("516d3969-5a57-5651-5958-4e7a49434167"), "SmartDisplayer BobeePass (NFC-BLE FIDO2 Authenticator)"),
+
+ // Solo
+ Map.entry(UUID.fromString("8876631b-d4a0-427f-5773-0ec71c9e0279"), "Solo Secp256R1 FIDO2 CTAP2 Authenticator"),
+ Map.entry(UUID.fromString("8976631b-d4a0-427f-5773-0ec71c9e0279"), "Solo Tap Secp256R1 FIDO2 CTAP2 Authenticator"),
+
+ // Somu
+ Map.entry(UUID.fromString("9876631b-d4a0-427f-5773-0ec71c9e0279"), "Somu Secp256R1 FIDO2 CTAP2 Authenticator"),
+
+ // Swissbit
+ Map.entry(UUID.fromString("931327dd-c89b-406c-a81e-ed7058ef36c6"), "Swissbit iShield FIDO2"),
+
+ // Thales
+ Map.entry(UUID.fromString("b50d5e0a-7f81-4959-9b12-f45407407503"), "Thales IDPrime MD 3940 FIDO"),
+ Map.entry(UUID.fromString("efb96b10-a9ee-4b6c-a4a9-d32125ccd4a4"), "Thales eToken FIDO"),
+
+ // TrustKey
+ Map.entry(UUID.fromString("95442b2e-f15e-4def-b270-efb106facb4e"), "TrustKey G310(H)"),
+ Map.entry(UUID.fromString("87dbc5a1-4c94-4dc8-8a47-97d800fd1f3c"), "TrustKey G320(H)"),
+ Map.entry(UUID.fromString("da776f39-f6c8-4a89-b252-1d86137a46ba"), "TrustKey T110"),
+ Map.entry(UUID.fromString("e3512a8a-62ae-11ea-bc55-0242ac130003"), "TrustKey T120"),
+
+ // TOKEN2
+ Map.entry(UUID.fromString("ab32f0c6-2239-afbb-c470-d2ef4e254db7"), "TOKEN2 FIDO2 Security Key"),
+
+ // uTrust
+ Map.entry(UUID.fromString("73402251-f2a8-4f03-873e-3cb6db604b03"), "uTrust FIDO2 Security Key"),
+
+ // Vancosys
+ Map.entry(UUID.fromString("39a5647e-1853-446c-a1f6-a79bae9f5bc7"), "Vancosys Android Authenticator"),
+ Map.entry(UUID.fromString("820d89ed-d65a-409e-85cb-f73f0578f82a"), "Vancosys iOS Authenticator"),
+
+ // VinCSS
+ Map.entry(UUID.fromString("5fdb81b8-53f0-4967-a881-f5ec26fe4d18"), "VinCSS FIDO2 Authenticator"),
+
+ // VivoKey
+ Map.entry(UUID.fromString("d7a423ad-3e19-4492-9200-78137dccc136"), "VivoKey Apex FIDO2"),
+
+ // Windows Hello
+ Map.entry(UUID.fromString("08987058-cadc-4b81-b6e1-30de50dcbe96"), "Windows Hello Hardware Authenticator"),
+ Map.entry(UUID.fromString("6028b017-b1d4-4c02-b4b3-afcdafc96bb2"), "Windows Hello Software Authenticator"),
+ Map.entry(UUID.fromString("9ddd1817-af5a-4672-a2b9-3e3dd95000a9"), "Windows Hello VBS Hardware Authenticator"),
+
+ // WiSECURE
+ Map.entry(UUID.fromString("504d7149-4e4c-3841-4555-55445a677357"), "WiSECURE AuthTron USB FIDO2 Authenticator"),
+
+ // Yubico
+ Map.entry(UUID.fromString("0bb43545-fd2c-4185-87dd-feb0b2916ace"), "Security Key NFC by Yubico - Enterprise Edition"),
+ Map.entry(UUID.fromString("149a2021-8ef6-4133-96b8-81f8d5b7f1f5"), "Security Key by Yubico with NFC"),
+ Map.entry(UUID.fromString("2fc0579f-8113-47ea-b116-bb5a8db9202a"), "YubiKey 5 Series with NFC"),
+ Map.entry(UUID.fromString("6d44ba9b-f6ec-2e49-b930-0c8fe920cb73"), "Security Key by Yubico with NFC"),
+ Map.entry(UUID.fromString("73bb0cd4-e502-49b8-9c6f-b59445bf720b"), "YubiKey 5 FIPS Series"),
+ Map.entry(UUID.fromString("85203421-48f9-4355-9bc8-8a53846e5083"), "YubiKey 5Ci FIPS"),
+ Map.entry(UUID.fromString("a4e9fc6d-4cbe-4758-b8ba-37598bb5bbaa"), "Security Key by Yubico with NFC"),
+ Map.entry(UUID.fromString("b92c3f9a-c014-4056-887f-140a2501163b"), "Security Key by Yubico"),
+ Map.entry(UUID.fromString("c1f9a0bc-1dd2-404a-b27f-8e29047a43fd"), "YubiKey 5 FIPS Series with NFC"),
+ Map.entry(UUID.fromString("c5ef55ff-ad9a-4b9f-b580-adebafe026d0"), "YubiKey 5Ci"),
+ Map.entry(UUID.fromString("cb69481e-8ff7-4039-93ec-0a2729a154a8"), "YubiKey 5 Series"),
+ Map.entry(UUID.fromString("d8522d9f-575b-4866-88a9-ba99fa02f35b"), "YubiKey Bio Series"),
+ Map.entry(UUID.fromString("ee882879-721c-4913-9775-3dfcce97072a"), "YubiKey 5 Series"),
+ Map.entry(UUID.fromString("f8a011f3-8c0a-4d15-8006-17111f9edc7d"), "Security Key by Yubico"),
+ Map.entry(UUID.fromString("fa2b99dc-9e39-4257-8f92-4a30d23c4118"), "YubiKey 5 Series with NFC"),
+ Map.entry(UUID.fromString("34f5766d-1536-4a24-9033-0e294e510fb0"), "YubiKey 5 Series CTAP2.1 Preview Expired"),
+ Map.entry(UUID.fromString("83c47309-aabb-4108-8470-8be838b573cb"), "YubiKey Bio Series (Enterprise Profile)"),
+
+ // Other authenticators
+ Map.entry(UUID.fromString("ad784498-1902-3f54-b99a-10bb7dbd9588"), "Apple MacBook Pro 14-inch, 2021"),
+ Map.entry(UUID.fromString("4ae71336-e44b-39bf-b9d2-752e234818a5"), "Apple Passkeys")
+ );
+
+ private AaguidList() {
+ throw new IllegalStateException("Should not be instantiated");
}
- public String vendorName(byte[] aaguid) {
- final String vendor = vendors.get(UUID.nameUUIDFromBytes(aaguid));
+ public static String vendorName(final byte[] aaguid) {
+ final String vendor = VENDORS.get(UUID.nameUUIDFromBytes(aaguid));
return Objects.requireNonNullElse(vendor, "Unknown FIDO2 Authenticator");
}
From 3df009fe0ef6b66bd211202f60ecabad506b008c Mon Sep 17 00:00:00 2001
From: Lubos Racansky
Date: Mon, 26 Feb 2024 09:56:43 +0100
Subject: [PATCH 093/146] Fix #1351: Coverity: Useless call
---
.../powerauth/rest/client/PowerAuthFido2RestClient.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/powerauth-rest-client-spring/src/main/java/com/wultra/security/powerauth/rest/client/PowerAuthFido2RestClient.java b/powerauth-rest-client-spring/src/main/java/com/wultra/security/powerauth/rest/client/PowerAuthFido2RestClient.java
index 558bc7f2b..3d7156736 100644
--- a/powerauth-rest-client-spring/src/main/java/com/wultra/security/powerauth/rest/client/PowerAuthFido2RestClient.java
+++ b/powerauth-rest-client-spring/src/main/java/com/wultra/security/powerauth/rest/client/PowerAuthFido2RestClient.java
@@ -85,7 +85,7 @@ public PowerAuthFido2RestClient(String baseUrl, PowerAuthRestClientConfiguration
}
}
if (config.getPowerAuthClientToken() != null) {
- builder.httpBasicAuth().username(config.getPowerAuthClientToken()).password(config.getPowerAuthClientSecret()).build();
+ builder.httpBasicAuth().username(config.getPowerAuthClientToken()).password(config.getPowerAuthClientSecret());
}
if (config.getDefaultHttpHeaders() != null) {
builder.defaultHttpHeaders(config.getDefaultHttpHeaders());
From 05d08746ca5aeb7f7924024130d9dcdea759931d Mon Sep 17 00:00:00 2001
From: Lubos Racansky
Date: Mon, 26 Feb 2024 14:32:32 +0100
Subject: [PATCH 094/146] Fix column name pa_application_config.config_key in
db scripts
A follow-up to #1299
---
.../1.7.x/20240212-application-config.xml | 4 ++--
docs/sql/mssql/migration_1.6.0_1.7.0.sql | 4 ++--
docs/sql/oracle/migration_1.6.0_1.7.0.sql | 4 ++--
docs/sql/postgresql/migration_1.6.0_1.7.0.sql | 4 ++--
4 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/docs/db/changelog/changesets/powerauth-java-server/1.7.x/20240212-application-config.xml b/docs/db/changelog/changesets/powerauth-java-server/1.7.x/20240212-application-config.xml
index c495dfdd2..485818c85 100644
--- a/docs/db/changelog/changesets/powerauth-java-server/1.7.x/20240212-application-config.xml
+++ b/docs/db/changelog/changesets/powerauth-java-server/1.7.x/20240212-application-config.xml
@@ -49,9 +49,9 @@
- Create a new index on pa_application_config(key)
+ Create a new index on pa_application_config(config_key)
-
+
diff --git a/docs/sql/mssql/migration_1.6.0_1.7.0.sql b/docs/sql/mssql/migration_1.6.0_1.7.0.sql
index 67faff720..db82de320 100644
--- a/docs/sql/mssql/migration_1.6.0_1.7.0.sql
+++ b/docs/sql/mssql/migration_1.6.0_1.7.0.sql
@@ -18,8 +18,8 @@ CREATE TABLE pa_application_config (id int NOT NULL, application_id int NOT NULL
GO
-- Changeset powerauth-java-server/1.7.x/20240212-application-config.xml::2::Roman Strobl
--- Create a new index on pa_application_config(key)
-CREATE NONCLUSTERED INDEX pa_app_config_key_idx ON pa_application_config([key]);
+-- Create a new index on pa_application_config(config_key)
+CREATE NONCLUSTERED INDEX pa_app_config_key_idx ON pa_application_config([config_key]);
GO
-- Changeset powerauth-java-server/1.7.x/20240212-application-config.xml::3::Lubos Racansky
diff --git a/docs/sql/oracle/migration_1.6.0_1.7.0.sql b/docs/sql/oracle/migration_1.6.0_1.7.0.sql
index 760ac1e1b..10a4f6da1 100644
--- a/docs/sql/oracle/migration_1.6.0_1.7.0.sql
+++ b/docs/sql/oracle/migration_1.6.0_1.7.0.sql
@@ -14,8 +14,8 @@ ALTER TABLE pa_activation MODIFY extras CLOB;
CREATE TABLE pa_application_config (id INTEGER NOT NULL, application_id INTEGER NOT NULL, config_key VARCHAR2(255) NOT NULL, config_values CLOB, CONSTRAINT PK_PA_APPLICATION_CONFIG PRIMARY KEY (id), CONSTRAINT pa_app_config_app_fk FOREIGN KEY (application_id) REFERENCES pa_application(id));
-- Changeset powerauth-java-server/1.7.x/20240212-application-config.xml::2::Roman Strobl
--- Create a new index on pa_application_config(key)
-CREATE INDEX pa_app_config_key_idx ON pa_application_config(key);
+-- Create a new index on pa_application_config(config_key)
+CREATE INDEX pa_app_config_key_idx ON pa_application_config(config_key);
-- Changeset powerauth-java-server/1.7.x/20240212-application-config.xml::3::Lubos Racansky
-- Create a new sequence pa_app_conf_seq
diff --git a/docs/sql/postgresql/migration_1.6.0_1.7.0.sql b/docs/sql/postgresql/migration_1.6.0_1.7.0.sql
index b1fb025ba..5a4e29ac8 100644
--- a/docs/sql/postgresql/migration_1.6.0_1.7.0.sql
+++ b/docs/sql/postgresql/migration_1.6.0_1.7.0.sql
@@ -14,8 +14,8 @@ ALTER TABLE pa_activation ALTER COLUMN extras TYPE TEXT USING (extras::TEXT);
CREATE TABLE pa_application_config (id INTEGER NOT NULL, application_id INTEGER NOT NULL, config_key VARCHAR(255) NOT NULL, config_values TEXT, CONSTRAINT pa_application_config_pkey PRIMARY KEY (id), CONSTRAINT pa_app_config_app_fk FOREIGN KEY (application_id) REFERENCES pa_application(id));
-- Changeset powerauth-java-server/1.7.x/20240212-application-config.xml::2::Roman Strobl
--- Create a new index on pa_application_config(key)
-CREATE INDEX pa_app_config_key_idx ON pa_application_config(key);
+-- Create a new index on pa_application_config(config_key)
+CREATE INDEX pa_app_config_key_idx ON pa_application_config(config_key);
-- Changeset powerauth-java-server/1.7.x/20240212-application-config.xml::3::Lubos Racansky
-- Create a new sequence pa_app_conf_seq
From 8018c5446a4ea9bdf4669491f4a516dd4731b988 Mon Sep 17 00:00:00 2001
From: Roman Strobl
Date: Tue, 27 Feb 2024 15:00:26 +0800
Subject: [PATCH 095/146] Fix #1310: FIDO2: Persist and check counter value
---
.../fido2/PowerAuthCryptographyService.java | 32 +++++++++++++++++++
1 file changed, 32 insertions(+)
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthCryptographyService.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthCryptographyService.java
index a2aecfce4..3e53e7cd5 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthCryptographyService.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthCryptographyService.java
@@ -20,16 +20,20 @@
import com.wultra.powerauth.fido2.rest.model.entity.*;
import com.wultra.powerauth.fido2.service.provider.CryptographyService;
+import io.getlime.security.powerauth.app.server.database.model.entity.ActivationRecordEntity;
+import io.getlime.security.powerauth.app.server.database.repository.ActivationRepository;
import io.getlime.security.powerauth.crypto.lib.model.exception.CryptoProviderException;
import io.getlime.security.powerauth.crypto.lib.model.exception.GenericCryptoException;
import io.getlime.security.powerauth.crypto.lib.util.Hash;
import io.getlime.security.powerauth.crypto.lib.util.KeyConvertor;
import io.getlime.security.powerauth.crypto.lib.util.SignatureUtils;
+import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.security.InvalidKeyException;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
+import java.util.List;
/**
* Service providing FIDO2 cryptographic functionality.
@@ -37,11 +41,20 @@
* @author Petr Dvorak, petr@wultra.com
*/
@Service
+@Slf4j
public class PowerAuthCryptographyService implements CryptographyService {
private final KeyConvertor keyConvertor = new KeyConvertor();
+ private final ActivationRepository activationRepository;
+
+ public PowerAuthCryptographyService(ActivationRepository activationRepository) {
+ this.activationRepository = activationRepository;
+ }
public boolean verifySignatureForAssertion(String applicationId, String authenticatorId, CollectedClientData clientDataJSON, AuthenticatorData authData, byte[] signature, AuthenticatorDetail authenticatorDetail) throws GenericCryptoException, InvalidKeySpecException, CryptoProviderException, InvalidKeyException {
+ if (!checkAndPersistCounter(applicationId, authenticatorId, authData.getSignCount())) {
+ return false;
+ }
final byte[] publicKeyBytes = authenticatorDetail.getPublicKeyBytes();
final PublicKey publicKey = keyConvertor.convertBytesToPublicKey(publicKeyBytes);
return verifySignature(clientDataJSON, authData, signature, publicKey);
@@ -68,6 +81,25 @@ private boolean verifySignature(CollectedClientData clientDataJSON, Authenticato
return signatureUtils.validateECDSASignature(clientDataJSONEncodedHash, signature, publicKey);
}
+ private boolean checkAndPersistCounter(String applicationId, String authenticatorId, int signCount) {
+ final List activations = activationRepository.findByExternalId(applicationId, authenticatorId);
+ if (activations.size() != 1) {
+ logger.warn("Multiple activations with same external ID found, external ID: {}", authenticatorId);
+ return false;
+ }
+ final ActivationRecordEntity activation = activations.get(0);
+ if (signCount == 0 && activation.getCounter() == 0) {
+ return true;
+ }
+ if (activation.getCounter() >= signCount) {
+ logger.warn("Invalid counter value for activation, activation ID: {}, stored counter value: {}, received counter value: {}", activation.getActivationId(), activation.getCounter(), signCount);
+ return false;
+ }
+ activation.setCounter((long) signCount);
+ activationRepository.save(activation);
+ return true;
+ }
+
private byte[] concat(byte[] a, byte[] b) {
final byte[] combined = new byte[a.length + b.length];
System.arraycopy(a, 0, combined, 0, a.length);
From d7a5209846de8246845f284d39d29a66fa896a53 Mon Sep 17 00:00:00 2001
From: Roman Strobl
Date: Tue, 27 Feb 2024 15:27:48 +0800
Subject: [PATCH 096/146] Handle different cases for activations found by
external ID
---
.../server/service/fido2/PowerAuthCryptographyService.java | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthCryptographyService.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthCryptographyService.java
index 3e53e7cd5..535c0c2a5 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthCryptographyService.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthCryptographyService.java
@@ -83,7 +83,11 @@ private boolean verifySignature(CollectedClientData clientDataJSON, Authenticato
private boolean checkAndPersistCounter(String applicationId, String authenticatorId, int signCount) {
final List activations = activationRepository.findByExternalId(applicationId, authenticatorId);
- if (activations.size() != 1) {
+ if (activations.isEmpty()) {
+ logger.warn("Activation not found, external ID: {}", authenticatorId);
+ return false;
+ }
+ if (activations.size() > 1) {
logger.warn("Multiple activations with same external ID found, external ID: {}", authenticatorId);
return false;
}
From 9af222e7bf5c63c9917e594d4de93d5d7b469db8 Mon Sep 17 00:00:00 2001
From: Roman Strobl
Date: Wed, 28 Feb 2024 15:37:12 +0800
Subject: [PATCH 097/146] Fix #1289: FIDO2: Do not allow duplicate registration
of same authenticator
---
.../powerauth/fido2/service/RegistrationService.java | 7 ++++---
.../fido2/service/provider/RegistrationProvider.java | 3 ++-
.../service/fido2/PowerAuthRegistrationProvider.java | 11 +++++++++--
3 files changed, 15 insertions(+), 6 deletions(-)
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/RegistrationService.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/RegistrationService.java
index 7aa8c1474..ece894b97 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/RegistrationService.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/RegistrationService.java
@@ -115,6 +115,7 @@ public RegistrationResponse register(RegistrationRequest requestObject) throws E
throw new Fido2AuthenticationFailedException(error);
}
+ final String authenticatorId = requestObject.getAuthenticatorParameters().getId();
final AuthenticatorAttestationResponse response = requestObject.getAuthenticatorParameters().getResponse();
final CollectedClientData clientDataJSON = response.getClientDataJSON();
@@ -130,7 +131,7 @@ public RegistrationResponse register(RegistrationRequest requestObject) throws E
final String fmt = attestationObject.getFmt();
final byte[] aaguid = attestationObject.getAuthData().getAttestedCredentialData().getAaguid();
- validateRegistrationRequest(applicationId, fmt, aaguid, challengeValue);
+ validateRegistrationRequest(applicationId, authenticatorId, fmt, aaguid, challengeValue);
if (Fmt.FMT_PACKED.getValue().equals(fmt)) {
final boolean verifySignature = cryptographyService.verifySignatureForRegistration(applicationId, clientDataJSON, authData, signature, attestedCredentialData);
@@ -151,8 +152,8 @@ public RegistrationResponse register(RegistrationRequest requestObject) throws E
return registrationConverter.convertRegistrationResponse(authenticatorDetailResponse);
}
- private void validateRegistrationRequest(final String applicationId, final String attestationFormat, final byte[] aaguid, final String challengeValue) throws Exception {
- if (!registrationProvider.registrationAllowed(applicationId, attestationFormat, aaguid)) {
+ private void validateRegistrationRequest(final String applicationId, final String authenticatorId, final String attestationFormat, final byte[] aaguid, final String challengeValue) throws Exception {
+ if (!registrationProvider.registrationAllowed(applicationId, authenticatorId, attestationFormat, aaguid)) {
logger.warn("Invalid request for FIDO2 registration");
// Immediately revoke the challenge
registrationProvider.revokeRegistrationByChallengeValue(applicationId, challengeValue);
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/provider/RegistrationProvider.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/provider/RegistrationProvider.java
index 0349baafc..e9a18488a 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/provider/RegistrationProvider.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/provider/RegistrationProvider.java
@@ -59,11 +59,12 @@ public interface RegistrationProvider {
/**
* Verify registration parameters and determine whether registration is allowed.
* @param applicationId Application ID.
+ * @param authenticatorId Authenticator ID.
* @param attestationFormat FIDO2 registration attestation format.
* @param aaguid FIDO2 registration AAGUID value.
* @return Whether registration is allowed.
* @throws Exception In case any issue occur during processing.
*/
- boolean registrationAllowed(String applicationId, String attestationFormat, byte[] aaguid) throws Exception;
+ boolean registrationAllowed(String applicationId, String authenticatorId, String attestationFormat, byte[] aaguid) throws Exception;
}
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthRegistrationProvider.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthRegistrationProvider.java
index 820022630..e9f57f229 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthRegistrationProvider.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthRegistrationProvider.java
@@ -31,6 +31,7 @@
import io.getlime.security.powerauth.app.server.database.model.entity.ActivationRecordEntity;
import io.getlime.security.powerauth.app.server.database.model.entity.ApplicationEntity;
import io.getlime.security.powerauth.app.server.database.model.enumeration.ActivationStatus;
+import io.getlime.security.powerauth.app.server.database.repository.ActivationRepository;
import io.getlime.security.powerauth.app.server.service.behavior.ServiceBehaviorCatalogue;
import io.getlime.security.powerauth.app.server.service.behavior.tasks.ApplicationConfigServiceBehavior;
import io.getlime.security.powerauth.app.server.service.exceptions.GenericServiceException;
@@ -152,7 +153,7 @@ public void revokeRegistrationByChallengeValue(String applicationId, String chal
@Override
@Transactional(readOnly = true)
- public boolean registrationAllowed(String applicationId, String attestationFormat, byte[] aaguid) throws Exception {
+ public boolean registrationAllowed(String applicationId, String authenticatorId, String attestationFormat, byte[] aaguid) throws Exception {
final ApplicationConfigServiceBehavior configService = serviceBehaviorCatalogue.getApplicationConfigServiceBehavior();
final GetApplicationConfigRequest configRequest = new GetApplicationConfigRequest();
configRequest.setApplicationId(applicationId);
@@ -175,7 +176,6 @@ public boolean registrationAllowed(String applicationId, String attestationForma
.findFirst();
if (configAaguids.isPresent()) {
- System.out.println(aaguidStr);
List allowedAaguids = configAaguids.get().getValues();
if (!allowedAaguids.contains(aaguidStr)) {
logger.warn("Rejected AAGUID value for FIDO2 registration: {}", aaguidStr);
@@ -183,6 +183,13 @@ public boolean registrationAllowed(String applicationId, String attestationForma
}
}
+ final ActivationRepository activationRepository = repositoryCatalogue.getActivationRepository();
+ final List existingActivations = activationRepository.findByExternalId(applicationId, authenticatorId);
+ if (!existingActivations.isEmpty()) {
+ logger.warn("Rejected duplicate external ID for registration, application ID: {}, external ID: {}", applicationId, authenticatorId);
+ return false;
+ }
+
return true;
}
}
From 30e1e1919b718aaaad47e4f11f6b2b1a66524566 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 4 Mar 2024 01:45:44 +0000
Subject: [PATCH 098/146] Bump nl.jqno.equalsverifier:equalsverifier from
3.15.7 to 3.15.8
Bumps [nl.jqno.equalsverifier:equalsverifier](https://github.com/jqno/equalsverifier) from 3.15.7 to 3.15.8.
- [Release notes](https://github.com/jqno/equalsverifier/releases)
- [Changelog](https://github.com/jqno/equalsverifier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/jqno/equalsverifier/compare/equalsverifier-3.15.7...equalsverifier-3.15.8)
---
updated-dependencies:
- dependency-name: nl.jqno.equalsverifier:equalsverifier
dependency-type: direct:development
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
---
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index c683f8fef..f773224be 100644
--- a/pom.xml
+++ b/pom.xml
@@ -102,7 +102,7 @@
1.11.0
7.4
- 3.15.7
+ 3.15.8
3.5.3
From 42c6ce84ec7c517766294464cd08156732d3e2f2 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 4 Mar 2024 01:46:03 +0000
Subject: [PATCH 099/146] Bump net.javacrumbs.shedlock:shedlock-bom from 5.11.0
to 5.12.0
Bumps [net.javacrumbs.shedlock:shedlock-bom](https://github.com/lukas-krecan/ShedLock) from 5.11.0 to 5.12.0.
- [Commits](https://github.com/lukas-krecan/ShedLock/compare/shedlock-parent-5.11.0...shedlock-parent-5.12.0)
---
updated-dependencies:
- dependency-name: net.javacrumbs.shedlock:shedlock-bom
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot]
---
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index c683f8fef..8bd35f584 100644
--- a/pom.xml
+++ b/pom.xml
@@ -97,7 +97,7 @@
2.2.20
- 5.11.0
+ 5.12.0
1.11.0
From 987f28234be6a808246cd5e55435bd1e9a571683 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 4 Mar 2024 01:46:44 +0000
Subject: [PATCH 100/146] Bump com.webauthn4j:webauthn4j-test
Bumps [com.webauthn4j:webauthn4j-test](https://github.com/webauthn4j/webauthn4j) from 0.22.1.RELEASE to 0.22.2.RELEASE.
- [Release notes](https://github.com/webauthn4j/webauthn4j/releases)
- [Changelog](https://github.com/webauthn4j/webauthn4j/blob/master/github-release-notes-generator.yml)
- [Commits](https://github.com/webauthn4j/webauthn4j/compare/0.22.1.RELEASE...0.22.2.RELEASE)
---
updated-dependencies:
- dependency-name: com.webauthn4j:webauthn4j-test
dependency-type: direct:development
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
---
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index c683f8fef..ef3ce7d43 100644
--- a/pom.xml
+++ b/pom.xml
@@ -106,7 +106,7 @@
3.5.3
- 0.22.1.RELEASE
+ 0.22.2.RELEASE
From a018e5b0f7a4c5a95ed11cda0a8fe3427c11da93 Mon Sep 17 00:00:00 2001
From: Lubos Racansky
Date: Mon, 4 Mar 2024 08:35:15 +0100
Subject: [PATCH 101/146] Fix #1366: testUpdateActivation_badRequest fails for
a non-English locale
---
.../powerauth/app/server/controller/RESTControllerAdvice.java | 2 +-
.../src/test/resources/application-test.properties | 2 ++
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/controller/RESTControllerAdvice.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/controller/RESTControllerAdvice.java
index a9ec39b73..c8b8f9365 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/controller/RESTControllerAdvice.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/controller/RESTControllerAdvice.java
@@ -138,7 +138,7 @@ public class RESTControllerAdvice {
}
/**
- * Resolver for validation xception.
+ * Resolver for validation exception.
*
* @param ex Exception.
* @return Activation recovery error.
diff --git a/powerauth-java-server/src/test/resources/application-test.properties b/powerauth-java-server/src/test/resources/application-test.properties
index e1d502878..e6ddcbfee 100644
--- a/powerauth-java-server/src/test/resources/application-test.properties
+++ b/powerauth-java-server/src/test/resources/application-test.properties
@@ -31,3 +31,5 @@ powerauth.server.db.master.encryption.key=MTIzNDU2Nzg5MDEyMzQ1Ng==
#logging.level.org.hibernate.orm.jdbc.bind=trace
spring.liquibase.enabled=false
+
+spring.web.locale=en
From e5eb8a0d7b5de357c27b6ece130b4a0419bbe879 Mon Sep 17 00:00:00 2001
From: Jan Dusil <134381434+jandusil@users.noreply.github.com>
Date: Mon, 4 Mar 2024 13:40:10 +0100
Subject: [PATCH 102/146] Fix #1368: Modify datatype of pa_activation.extras
(#1369)
---
.../powerauth-java-server/1.7.x/20240115-add-columns-fido2.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/db/changelog/changesets/powerauth-java-server/1.7.x/20240115-add-columns-fido2.xml b/docs/db/changelog/changesets/powerauth-java-server/1.7.x/20240115-add-columns-fido2.xml
index e42b7e6af..5a9a77f08 100644
--- a/docs/db/changelog/changesets/powerauth-java-server/1.7.x/20240115-add-columns-fido2.xml
+++ b/docs/db/changelog/changesets/powerauth-java-server/1.7.x/20240115-add-columns-fido2.xml
@@ -46,7 +46,7 @@
-
+
From 6da2b4f1c3519a7c2bae1d9be83932635e5046fb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Petr=20Dvo=C5=99=C3=A1k?=
Date: Mon, 4 Mar 2024 18:19:39 +0100
Subject: [PATCH 103/146] Fix #1372: Log a successfull callback call as an info
---
.../app/server/service/behavior/tasks/CallbackUrlBehavior.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/CallbackUrlBehavior.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/CallbackUrlBehavior.java
index dbdad10d1..d423c92d3 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/CallbackUrlBehavior.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/CallbackUrlBehavior.java
@@ -413,7 +413,7 @@ private Map prepareCallbackDataOperation(CallbackUrlEntity callb
* @throws RestClientException Thrown when HTTP request fails.
*/
private void notifyCallbackUrl(CallbackUrlEntity callbackUrlEntity, Map callbackData) throws RestClientException {
- final Consumer> onSuccess = response -> logger.debug("Callback succeeded, URL: {}", callbackUrlEntity.getCallbackUrl());
+ final Consumer> onSuccess = response -> logger.info("Callback succeeded, URL: {}", callbackUrlEntity.getCallbackUrl());
final Consumer onError = error -> logger.warn("Callback failed, URL: {}, error: {}", callbackUrlEntity.getCallbackUrl(), error.getMessage());
final ParameterizedTypeReference responseType = new ParameterizedTypeReference<>(){};
final RestClient restClient = getRestClient(callbackUrlEntity);
From 8863190225fab8cfc4f6d058fd03f7a745b7e804 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Petr=20Dvo=C5=99=C3=A1k?=
Date: Mon, 4 Mar 2024 18:22:30 +0100
Subject: [PATCH 104/146] Fix #1314: FIDO2: Add new signature types (#1356)
* Fix #1314: FIDO2: Add new signature types
* Fix comments from the code review
---
.../converter/RegistrationConverter.java | 6 +-
.../fido2/rest/model/entity/AaguidList.java | 193 -------------
.../model/entity/Fido2Authenticators.java | 253 ++++++++++++++++++
.../fido2/service/AssertionService.java | 14 +-
.../service/provider/AssertionProvider.java | 14 +-
.../fido2/PowerAuthAssertionProvider.java | 43 ++-
6 files changed, 303 insertions(+), 220 deletions(-)
delete mode 100644 powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AaguidList.java
create mode 100644 powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/Fido2Authenticators.java
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/RegistrationConverter.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/RegistrationConverter.java
index 303dd3bd6..fbe39406a 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/RegistrationConverter.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/converter/RegistrationConverter.java
@@ -19,7 +19,7 @@
package com.wultra.powerauth.fido2.rest.model.converter;
import com.fasterxml.jackson.core.JsonProcessingException;
-import com.wultra.powerauth.fido2.rest.model.entity.AaguidList;
+import com.wultra.powerauth.fido2.rest.model.entity.Fido2Authenticators;
import com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorDetail;
import com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorParameters;
import com.wultra.powerauth.fido2.rest.model.entity.RegistrationChallenge;
@@ -58,11 +58,13 @@ public Optional convert(RegistrationChallenge challenge, Re
authenticatorDetail.setActivationId(challenge.getActivationId());
authenticatorDetail.setApplicationId(challenge.getApplicationId());
+ final Fido2Authenticators.Model model = Fido2Authenticators.modelByAaguid(aaguid);
+
authenticatorDetail.setExternalId(requestObject.getAuthenticatorParameters().getId());
authenticatorDetail.setExtras(convertExtras(requestObject));
authenticatorDetail.setActivationName(requestObject.getActivationName());
authenticatorDetail.setPlatform(requestObject.getAuthenticatorParameters().getAuthenticatorAttachment());
- authenticatorDetail.setDeviceInfo(AaguidList.vendorName(aaguid));
+ authenticatorDetail.setDeviceInfo(model.description());
authenticatorDetail.setActivationStatus(ActivationStatus.ACTIVE);
authenticatorDetail.setActivationFlags(new ArrayList<>());
authenticatorDetail.setApplicationRoles(new ArrayList<>());
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AaguidList.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AaguidList.java
deleted file mode 100644
index 2d39f2848..000000000
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/AaguidList.java
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- * PowerAuth Server and related software components
- * Copyright (C) 2023 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 com.wultra.powerauth.fido2.rest.model.entity;
-
-import java.util.Map;
-import java.util.Objects;
-import java.util.UUID;
-
-/**
- * Class containing map of all known FIDO2 AAGUID authenticator identifiers.
- *
- * @author Petr Dvorak, petr@wultra.com
- */
-public final class AaguidList {
-
- private static final Map VENDORS = Map.ofEntries(
-
- // Android
- Map.entry(UUID.fromString("b93fd961-f2e6-462f-b122-82002247de78"), "Android Authenticator with SafetyNet Attestation"),
-
- // ATKey
- Map.entry(UUID.fromString("ba76a271-6eb6-4171-874d-b6428dbe3437"), "ATKey.ProS"),
- Map.entry(UUID.fromString("d41f5a69-b817-4144-a13c-9ebd6d9254d6"), "ATKey.Card CTAP2.0"),
- Map.entry(UUID.fromString("e1a96183-5016-4f24-b55b-e3ae23614cc6"), "ATKey.Pro CTAP2.0"),
- Map.entry(UUID.fromString("e416201b-afeb-41ca-a03d-2281c28322aa"), "ATKey.Pro CTAP2.1"),
-
- // Atos
- Map.entry(UUID.fromString("1c086528-58d5-f211-823c-356786e36140"), "Atos CardOS FIDO2"),
-
- // Crayonic
- Map.entry(UUID.fromString("be727034-574a-f799-5c76-0929e0430973"), "Crayonic KeyVault K1 (USB-NFC-BLE FIDO2 Authenticator)"),
-
- // Cryptnox
- Map.entry(UUID.fromString("9c835346-796b-4c27-8898-d6032f515cc5"), "Cryptnox FIDO2"),
-
- // Ensurity
- Map.entry(UUID.fromString("454e5346-4944-4ffd-6c93-8e9267193e9a"), "Ensurity ThinC"),
-
- // ESS
- Map.entry(UUID.fromString("5343502d-5343-5343-6172-644649444f32"), "ESS Smart Card Inc. Authenticator"),
-
- // eWBM
- Map.entry(UUID.fromString("61250591-b2bc-4456-b719-0b17be90bb30"), "eWBM eFPA FIDO2 Authenticator"),
-
- // FEITIAN
- Map.entry(UUID.fromString("12ded745-4bed-47d4-abaa-e713f51d6393"), "AllinPass FIDO"),
- Map.entry(UUID.fromString("2c0df832-92de-4be1-8412-88a8f074df4a"), "FIDO Java Card"),
- Map.entry(UUID.fromString("310b2830-bd4a-4da5-832e-9a0dfc90abf2"), "MultiPass FIDO"),
- Map.entry(UUID.fromString("3e22415d-7fdf-4ea4-8a0c-dd60c4249b9d"), "Feitian iePass FIDO Authenticator"),
- Map.entry(UUID.fromString("6e22415d-7fdf-4ea4-8a0c-dd60c4249b9d"), "iePass FIDO"),
- Map.entry(UUID.fromString("77010bd7-212a-4fc9-b236-d2ca5e9d4084"), "BioPass FIDO"),
- Map.entry(UUID.fromString("833b721a-ff5f-4d00-bb2e-bdda3ec01e29"), "ePassFIDO K10, A4B, K28"),
- Map.entry(UUID.fromString("8c97a730-3f7b-41a6-87d6-1e9b62bda6f0"), "FIDO Fingerprint Card"),
- Map.entry(UUID.fromString("b6ede29c-3772-412c-8a78-539c1f4c62d2"), "BioPass FIDO Plus"),
- Map.entry(UUID.fromString("ee041bce-25e5-4cdb-8f86-897fd6418464"), "ePassFIDO K39, NFC, NFC Plus"),
-
- // GoTrust
- Map.entry(UUID.fromString("3b1adb99-0dfe-46fd-90b8-7f7614a4de2a"), "GoTrust Idem Key FIDO2 Authenticator"),
- Map.entry(UUID.fromString("9f0d8150-baa5-4c00-9299-ad62c8bb4e87"), "GoTrust Idem Card FIDO2 Authenticator"),
-
- // HID Global
- Map.entry(UUID.fromString("54d9fee8-e621-4291-8b18-7157b99c5bec"), "HID Crescendo Enabled"),
- Map.entry(UUID.fromString("692db549-7ae5-44d5-a1e5-dd20a493b723"), "HID Crescendo Key"),
- Map.entry(UUID.fromString("aeb6569c-f8fb-4950-ac60-24ca2bbe2e52"), "HID Crescendo C2300"),
-
- // Hideez
- Map.entry(UUID.fromString("3e078ffd-4c54-4586-8baa-a77da113aec5"), "Hideez Key 3 FIDO2"),
- Map.entry(UUID.fromString("4e768f2c-5fab-48b3-b300-220eb487752b"), "Hideez Key 4 FIDO2 SDK"),
-
- // Hyper
- Map.entry(UUID.fromString("9f77e279-a6e2-4d58-b700-31e5943c6a98"), "Hyper FIDO Pro"),
- Map.entry(UUID.fromString("d821a7d4-e97c-4cb6-bd82-4237731fd4be"), "Hyper FIDO Bio Security Key"),
-
- // MKGroup
- Map.entry(UUID.fromString("f4c63eff-d26c-4248-801c-3736c7eaa93a"), "FIDO KeyPass S3"),
-
- // KEY-ID
- Map.entry(UUID.fromString("d91c5288-0ef0-49b7-b8ae-21ca0aa6b3f3"), "KEY-ID FIDO2 Authenticator"),
-
- // NEOWAVE
- Map.entry(UUID.fromString("3789da91-f943-46bc-95c3-50ea2012f03a"), "NEOWAVE Winkeo FIDO2"),
- Map.entry(UUID.fromString("c5703116-972b-4851-a3e7-ae1259843399"), "NEOWAVE Badgeo FIDO2"),
-
- // NXP Semiconductors
- Map.entry(UUID.fromString("07a9f89c-6407-4594-9d56-621d5f1e358b"), "NXP Semiconductors FIDO2 Conformance Testing CTAP2 Authenticator"),
-
- // OCTATCO
- Map.entry(UUID.fromString("a1f52be5-dfab-4364-b51c-2bd496b14a56"), "OCTATCO EzFinger2 FIDO2 AUTHENTICATOR"),
- Map.entry(UUID.fromString("bc2fe499-0d8e-4ffe-96f3-94a82840cf8c"), "OCTATCO EzQuant FIDO2 AUTHENTICATOR"),
-
- // OneSpan
- Map.entry(UUID.fromString("30b5035e-d297-4fc1-b00b-addc96ba6a97"), "OneSpan FIDO Touch"),
-
- // Precision InnaIT
- Map.entry(UUID.fromString("88bbd2f0-342a-42e7-9729-dd158be5407a"), "Precision InnaIT Key FIDO 2 Level 2 certified"),
-
- // SmartDisplayer
- Map.entry(UUID.fromString("516d3969-5a57-5651-5958-4e7a49434167"), "SmartDisplayer BobeePass (NFC-BLE FIDO2 Authenticator)"),
-
- // Solo
- Map.entry(UUID.fromString("8876631b-d4a0-427f-5773-0ec71c9e0279"), "Solo Secp256R1 FIDO2 CTAP2 Authenticator"),
- Map.entry(UUID.fromString("8976631b-d4a0-427f-5773-0ec71c9e0279"), "Solo Tap Secp256R1 FIDO2 CTAP2 Authenticator"),
-
- // Somu
- Map.entry(UUID.fromString("9876631b-d4a0-427f-5773-0ec71c9e0279"), "Somu Secp256R1 FIDO2 CTAP2 Authenticator"),
-
- // Swissbit
- Map.entry(UUID.fromString("931327dd-c89b-406c-a81e-ed7058ef36c6"), "Swissbit iShield FIDO2"),
-
- // Thales
- Map.entry(UUID.fromString("b50d5e0a-7f81-4959-9b12-f45407407503"), "Thales IDPrime MD 3940 FIDO"),
- Map.entry(UUID.fromString("efb96b10-a9ee-4b6c-a4a9-d32125ccd4a4"), "Thales eToken FIDO"),
-
- // TrustKey
- Map.entry(UUID.fromString("95442b2e-f15e-4def-b270-efb106facb4e"), "TrustKey G310(H)"),
- Map.entry(UUID.fromString("87dbc5a1-4c94-4dc8-8a47-97d800fd1f3c"), "TrustKey G320(H)"),
- Map.entry(UUID.fromString("da776f39-f6c8-4a89-b252-1d86137a46ba"), "TrustKey T110"),
- Map.entry(UUID.fromString("e3512a8a-62ae-11ea-bc55-0242ac130003"), "TrustKey T120"),
-
- // TOKEN2
- Map.entry(UUID.fromString("ab32f0c6-2239-afbb-c470-d2ef4e254db7"), "TOKEN2 FIDO2 Security Key"),
-
- // uTrust
- Map.entry(UUID.fromString("73402251-f2a8-4f03-873e-3cb6db604b03"), "uTrust FIDO2 Security Key"),
-
- // Vancosys
- Map.entry(UUID.fromString("39a5647e-1853-446c-a1f6-a79bae9f5bc7"), "Vancosys Android Authenticator"),
- Map.entry(UUID.fromString("820d89ed-d65a-409e-85cb-f73f0578f82a"), "Vancosys iOS Authenticator"),
-
- // VinCSS
- Map.entry(UUID.fromString("5fdb81b8-53f0-4967-a881-f5ec26fe4d18"), "VinCSS FIDO2 Authenticator"),
-
- // VivoKey
- Map.entry(UUID.fromString("d7a423ad-3e19-4492-9200-78137dccc136"), "VivoKey Apex FIDO2"),
-
- // Windows Hello
- Map.entry(UUID.fromString("08987058-cadc-4b81-b6e1-30de50dcbe96"), "Windows Hello Hardware Authenticator"),
- Map.entry(UUID.fromString("6028b017-b1d4-4c02-b4b3-afcdafc96bb2"), "Windows Hello Software Authenticator"),
- Map.entry(UUID.fromString("9ddd1817-af5a-4672-a2b9-3e3dd95000a9"), "Windows Hello VBS Hardware Authenticator"),
-
- // WiSECURE
- Map.entry(UUID.fromString("504d7149-4e4c-3841-4555-55445a677357"), "WiSECURE AuthTron USB FIDO2 Authenticator"),
-
- // Yubico
- Map.entry(UUID.fromString("0bb43545-fd2c-4185-87dd-feb0b2916ace"), "Security Key NFC by Yubico - Enterprise Edition"),
- Map.entry(UUID.fromString("149a2021-8ef6-4133-96b8-81f8d5b7f1f5"), "Security Key by Yubico with NFC"),
- Map.entry(UUID.fromString("2fc0579f-8113-47ea-b116-bb5a8db9202a"), "YubiKey 5 Series with NFC"),
- Map.entry(UUID.fromString("6d44ba9b-f6ec-2e49-b930-0c8fe920cb73"), "Security Key by Yubico with NFC"),
- Map.entry(UUID.fromString("73bb0cd4-e502-49b8-9c6f-b59445bf720b"), "YubiKey 5 FIPS Series"),
- Map.entry(UUID.fromString("85203421-48f9-4355-9bc8-8a53846e5083"), "YubiKey 5Ci FIPS"),
- Map.entry(UUID.fromString("a4e9fc6d-4cbe-4758-b8ba-37598bb5bbaa"), "Security Key by Yubico with NFC"),
- Map.entry(UUID.fromString("b92c3f9a-c014-4056-887f-140a2501163b"), "Security Key by Yubico"),
- Map.entry(UUID.fromString("c1f9a0bc-1dd2-404a-b27f-8e29047a43fd"), "YubiKey 5 FIPS Series with NFC"),
- Map.entry(UUID.fromString("c5ef55ff-ad9a-4b9f-b580-adebafe026d0"), "YubiKey 5Ci"),
- Map.entry(UUID.fromString("cb69481e-8ff7-4039-93ec-0a2729a154a8"), "YubiKey 5 Series"),
- Map.entry(UUID.fromString("d8522d9f-575b-4866-88a9-ba99fa02f35b"), "YubiKey Bio Series"),
- Map.entry(UUID.fromString("ee882879-721c-4913-9775-3dfcce97072a"), "YubiKey 5 Series"),
- Map.entry(UUID.fromString("f8a011f3-8c0a-4d15-8006-17111f9edc7d"), "Security Key by Yubico"),
- Map.entry(UUID.fromString("fa2b99dc-9e39-4257-8f92-4a30d23c4118"), "YubiKey 5 Series with NFC"),
- Map.entry(UUID.fromString("34f5766d-1536-4a24-9033-0e294e510fb0"), "YubiKey 5 Series CTAP2.1 Preview Expired"),
- Map.entry(UUID.fromString("83c47309-aabb-4108-8470-8be838b573cb"), "YubiKey Bio Series (Enterprise Profile)"),
-
- // Other authenticators
- Map.entry(UUID.fromString("ad784498-1902-3f54-b99a-10bb7dbd9588"), "Apple MacBook Pro 14-inch, 2021"),
- Map.entry(UUID.fromString("4ae71336-e44b-39bf-b9d2-752e234818a5"), "Apple Passkeys")
- );
-
- private AaguidList() {
- throw new IllegalStateException("Should not be instantiated");
- }
-
- public static String vendorName(final byte[] aaguid) {
- final String vendor = VENDORS.get(UUID.nameUUIDFromBytes(aaguid));
- return Objects.requireNonNullElse(vendor, "Unknown FIDO2 Authenticator");
- }
-
-}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/Fido2Authenticators.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/Fido2Authenticators.java
new file mode 100644
index 000000000..e17818383
--- /dev/null
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/rest/model/entity/Fido2Authenticators.java
@@ -0,0 +1,253 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 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 com.wultra.powerauth.fido2.rest.model.entity;
+
+import com.wultra.security.powerauth.client.model.enumeration.SignatureType;
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+
+import java.nio.ByteBuffer;
+import java.util.*;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+/**
+ * Class containing map of all known FIDO2 AAGUID authenticator identifiers.
+ *
+ * @author Petr Dvorak, petr@wultra.com
+ */
+@Slf4j
+public final class Fido2Authenticators {
+
+ /**
+ * Model record representing an authenticator model. It associates the AAGUID value to a descriptive name
+ * and expected authentication factors available with a given authenticator. Most authenticators are set to
+ * provide only the possession factor on approval. In case the authenticator has a biometric sensor, it will be
+ * represented
+ */
+ public record Model (UUID aaguid, String description, SignatureType signatureType) {
+
+ public static Model of(String aaguid, String description) {
+ return new Model(UUID.fromString(aaguid), description, SignatureType.POSSESSION);
+ }
+
+ public static Model of(String aaguid, String description, SignatureType signatureType) {
+ return new Model(UUID.fromString(aaguid), description, signatureType);
+ }
+
+ public static Model unknown(UUID aaguid) {
+ return new Model(aaguid, "Unknown FIDO2 Authenticator", SignatureType.POSSESSION);
+ }
+
+ public static Model unknown() {
+ return new Model(null, "Unknown FIDO2 Authenticator", SignatureType.POSSESSION);
+ }
+
+ }
+
+ private static final Set MODELS = Set.of(
+ // Android
+ Model.of("b93fd961-f2e6-462f-b122-82002247de78", "Android Authenticator with SafetyNet Attestation"),
+
+ // ATKey
+ Model.of("ba76a271-6eb6-4171-874d-b6428dbe3437", "ATKey.ProS"),
+ Model.of("d41f5a69-b817-4144-a13c-9ebd6d9254d6", "ATKey.Card CTAP2.0"),
+ Model.of("e1a96183-5016-4f24-b55b-e3ae23614cc6", "ATKey.Pro CTAP2.0"),
+ Model.of("e416201b-afeb-41ca-a03d-2281c28322aa", "ATKey.Pro CTAP2.1"),
+
+ // Atos
+ Model.of("1c086528-58d5-f211-823c-356786e36140", "Atos CardOS FIDO2"),
+
+ // Crayonic
+ Model.of("be727034-574a-f799-5c76-0929e0430973", "Crayonic KeyVault K1 (USB-NFC-BLE FIDO2 Authenticator)"),
+
+ // Cryptnox
+ Model.of("9c835346-796b-4c27-8898-d6032f515cc5", "Cryptnox FIDO2"),
+
+ // Ensurity
+ Model.of("454e5346-4944-4ffd-6c93-8e9267193e9a", "Ensurity ThinC"),
+
+ // ESS
+ Model.of("5343502d-5343-5343-6172-644649444f32", "ESS Smart Card Inc. Authenticator"),
+
+ // eWBM
+ Model.of("61250591-b2bc-4456-b719-0b17be90bb30", "eWBM eFPA FIDO2 Authenticator"),
+
+ // FEITIAN
+ Model.of("12ded745-4bed-47d4-abaa-e713f51d6393", "AllinPass FIDO"),
+ Model.of("2c0df832-92de-4be1-8412-88a8f074df4a", "FIDO Java Card"),
+ Model.of("310b2830-bd4a-4da5-832e-9a0dfc90abf2", "MultiPass FIDO"),
+ Model.of("3e22415d-7fdf-4ea4-8a0c-dd60c4249b9d", "Feitian iePass FIDO Authenticator"),
+ Model.of("6e22415d-7fdf-4ea4-8a0c-dd60c4249b9d", "iePass FIDO"),
+ Model.of("77010bd7-212a-4fc9-b236-d2ca5e9d4084", "BioPass FIDO"),
+ Model.of("833b721a-ff5f-4d00-bb2e-bdda3ec01e29", "ePassFIDO K10, A4B, K28"),
+ Model.of("8c97a730-3f7b-41a6-87d6-1e9b62bda6f0", "FIDO Fingerprint Card"),
+ Model.of("b6ede29c-3772-412c-8a78-539c1f4c62d2", "BioPass FIDO Plus"),
+ Model.of("ee041bce-25e5-4cdb-8f86-897fd6418464", "ePassFIDO K39, NFC, NFC Plus"),
+
+ // GoTrust
+ Model.of("3b1adb99-0dfe-46fd-90b8-7f7614a4de2a", "GoTrust Idem Key FIDO2 Authenticator"),
+ Model.of("9f0d8150-baa5-4c00-9299-ad62c8bb4e87", "GoTrust Idem Card FIDO2 Authenticator"),
+
+ // HID Global
+ Model.of("54d9fee8-e621-4291-8b18-7157b99c5bec", "HID Crescendo Enabled"),
+ Model.of("692db549-7ae5-44d5-a1e5-dd20a493b723", "HID Crescendo Key"),
+ Model.of("aeb6569c-f8fb-4950-ac60-24ca2bbe2e52", "HID Crescendo C2300"),
+
+ // Hideez
+ Model.of("3e078ffd-4c54-4586-8baa-a77da113aec5", "Hideez Key 3 FIDO2"),
+ Model.of("4e768f2c-5fab-48b3-b300-220eb487752b", "Hideez Key 4 FIDO2 SDK"),
+
+ // Hyper
+ Model.of("9f77e279-a6e2-4d58-b700-31e5943c6a98", "Hyper FIDO Pro"),
+ Model.of("d821a7d4-e97c-4cb6-bd82-4237731fd4be", "Hyper FIDO Bio Security Key"),
+
+ // MKGroup
+ Model.of("f4c63eff-d26c-4248-801c-3736c7eaa93a", "FIDO KeyPass S3"),
+
+ // KEY-ID
+ Model.of("d91c5288-0ef0-49b7-b8ae-21ca0aa6b3f3", "KEY-ID FIDO2 Authenticator"),
+
+ // NEOWAVE
+ Model.of("3789da91-f943-46bc-95c3-50ea2012f03a", "NEOWAVE Winkeo FIDO2"),
+ Model.of("c5703116-972b-4851-a3e7-ae1259843399", "NEOWAVE Badgeo FIDO2"),
+
+ // NXP Semiconductors
+ Model.of("07a9f89c-6407-4594-9d56-621d5f1e358b", "NXP Semiconductors FIDO2 Conformance Testing CTAP2 Authenticator"),
+
+ // OCTATCO
+ Model.of("a1f52be5-dfab-4364-b51c-2bd496b14a56", "OCTATCO EzFinger2 FIDO2 AUTHENTICATOR"),
+ Model.of("bc2fe499-0d8e-4ffe-96f3-94a82840cf8c", "OCTATCO EzQuant FIDO2 AUTHENTICATOR"),
+
+ // OneSpan
+ Model.of("30b5035e-d297-4fc1-b00b-addc96ba6a97", "OneSpan FIDO Touch"),
+
+ // Precision InnaIT
+ Model.of("88bbd2f0-342a-42e7-9729-dd158be5407a", "Precision InnaIT Key FIDO 2 Level 2 certified"),
+
+ // SmartDisplayer
+ Model.of("516d3969-5a57-5651-5958-4e7a49434167", "SmartDisplayer BobeePass (NFC-BLE FIDO2 Authenticator)"),
+
+ // Solo
+ Model.of("8876631b-d4a0-427f-5773-0ec71c9e0279", "Solo Secp256R1 FIDO2 CTAP2 Authenticator"),
+ Model.of("8976631b-d4a0-427f-5773-0ec71c9e0279", "Solo Tap Secp256R1 FIDO2 CTAP2 Authenticator"),
+
+ // Somu
+ Model.of("9876631b-d4a0-427f-5773-0ec71c9e0279", "Somu Secp256R1 FIDO2 CTAP2 Authenticator"),
+
+ // Swissbit
+ Model.of("931327dd-c89b-406c-a81e-ed7058ef36c6", "Swissbit iShield FIDO2"),
+
+ // Thales
+ Model.of("b50d5e0a-7f81-4959-9b12-f45407407503", "Thales IDPrime MD 3940 FIDO"),
+ Model.of("efb96b10-a9ee-4b6c-a4a9-d32125ccd4a4", "Thales eToken FIDO"),
+
+ // TrustKey
+ Model.of("95442b2e-f15e-4def-b270-efb106facb4e", "TrustKey G310(H)"),
+ Model.of("87dbc5a1-4c94-4dc8-8a47-97d800fd1f3c", "TrustKey G320(H)"),
+ Model.of("da776f39-f6c8-4a89-b252-1d86137a46ba", "TrustKey T110"),
+ Model.of("e3512a8a-62ae-11ea-bc55-0242ac130003", "TrustKey T120"),
+
+ // TOKEN2
+ Model.of("ab32f0c6-2239-afbb-c470-d2ef4e254db7", "TOKEN2 FIDO2 Security Key"),
+
+ // uTrust
+ Model.of("73402251-f2a8-4f03-873e-3cb6db604b03", "uTrust FIDO2 Security Key"),
+
+ // Vancosys
+ Model.of("39a5647e-1853-446c-a1f6-a79bae9f5bc7", "Vancosys Android Authenticator"),
+ Model.of("820d89ed-d65a-409e-85cb-f73f0578f82a", "Vancosys iOS Authenticator"),
+
+ // VinCSS
+ Model.of("5fdb81b8-53f0-4967-a881-f5ec26fe4d18", "VinCSS FIDO2 Authenticator"),
+
+ // VivoKey
+ Model.of("d7a423ad-3e19-4492-9200-78137dccc136", "VivoKey Apex FIDO2"),
+
+ // Windows Hello
+ Model.of("08987058-cadc-4b81-b6e1-30de50dcbe96", "Windows Hello Hardware Authenticator"),
+ Model.of("6028b017-b1d4-4c02-b4b3-afcdafc96bb2", "Windows Hello Software Authenticator"),
+ Model.of("9ddd1817-af5a-4672-a2b9-3e3dd95000a9", "Windows Hello VBS Hardware Authenticator"),
+
+ // WiSECURE
+ Model.of("504d7149-4e4c-3841-4555-55445a677357", "WiSECURE AuthTron USB FIDO2 Authenticator"),
+
+ // Wultra
+ Model.of("57415531-2e31-4020-a020-323032343032", "Wultra Authenticator 1", SignatureType.POSSESSION_KNOWLEDGE),
+
+ // Yubico
+ Model.of("0bb43545-fd2c-4185-87dd-feb0b2916ace", "Security Key NFC by Yubico - Enterprise Edition"),
+ Model.of("149a2021-8ef6-4133-96b8-81f8d5b7f1f5", "Security Key by Yubico with NFC"),
+ Model.of("2fc0579f-8113-47ea-b116-bb5a8db9202a", "YubiKey 5 Series with NFC"),
+ Model.of("6d44ba9b-f6ec-2e49-b930-0c8fe920cb73", "Security Key by Yubico with NFC"),
+ Model.of("73bb0cd4-e502-49b8-9c6f-b59445bf720b", "YubiKey 5 FIPS Series"),
+ Model.of("85203421-48f9-4355-9bc8-8a53846e5083", "YubiKey 5Ci FIPS"),
+ Model.of("a4e9fc6d-4cbe-4758-b8ba-37598bb5bbaa", "Security Key by Yubico with NFC"),
+ Model.of("b92c3f9a-c014-4056-887f-140a2501163b", "Security Key by Yubico"),
+ Model.of("c1f9a0bc-1dd2-404a-b27f-8e29047a43fd", "YubiKey 5 FIPS Series with NFC"),
+ Model.of("c5ef55ff-ad9a-4b9f-b580-adebafe026d0", "YubiKey 5Ci"),
+ Model.of("cb69481e-8ff7-4039-93ec-0a2729a154a8", "YubiKey 5 Series"),
+ Model.of("d8522d9f-575b-4866-88a9-ba99fa02f35b", "YubiKey Bio Series"),
+ Model.of("ee882879-721c-4913-9775-3dfcce97072a", "YubiKey 5 Series"),
+ Model.of("f8a011f3-8c0a-4d15-8006-17111f9edc7d", "Security Key by Yubico"),
+ Model.of("fa2b99dc-9e39-4257-8f92-4a30d23c4118", "YubiKey 5 Series with NFC"),
+ Model.of("34f5766d-1536-4a24-9033-0e294e510fb0", "YubiKey 5 Series CTAP2.1 Preview Expired"),
+ Model.of("83c47309-aabb-4108-8470-8be838b573cb", "YubiKey Bio Series (Enterprise Profile)"),
+
+ // Other authenticators
+ Model.of("ad784498-1902-3f54-b99a-10bb7dbd9588", "Apple MacBook Pro 14-inch, 2021"),
+ Model.of("4ae71336-e44b-39bf-b9d2-752e234818a5", "Apple Passkeys")
+
+ );
+
+ private static final Map MODEL_MAP = initializeFromModels(); // prepare a quick to query map with models addressed by AAGUID
+
+ private static Map initializeFromModels() {
+ return MODELS.stream().collect(Collectors.toMap(Model::aaguid, Function.identity()));
+ }
+
+ private Fido2Authenticators() {
+ throw new IllegalStateException("Should not be instantiated");
+ }
+
+ /**
+ * Return model by AAGUID value. In case the AAGUID is not known, the method returns model with the provided
+ * AAGUID and generic name for unknown authenticator.
+ *
+ * @param aaguid AAGUID value.
+ * @return Model.
+ */
+ public static Model modelByAaguid(final byte[] aaguid) {
+ final UUID uuid = uuidFromBytes(aaguid);
+ if (uuid == null) {
+ return Model.unknown();
+ }
+ return Objects.requireNonNullElse(MODEL_MAP.get(uuid), Model.unknown(uuid));
+ }
+
+ private static UUID uuidFromBytes(byte[] bytes) {
+ if (bytes == null || bytes.length != 16) { // strange byte array length, UUID requires 16 bytes
+ logger.debug("Invalid byte length provided for UUID: {}", bytes);
+ return null;
+ }
+ final ByteBuffer bb = ByteBuffer.wrap(bytes);
+ return new UUID(bb.getLong(), bb.getLong());
+ }
+
+}
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/AssertionService.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/AssertionService.java
index fcde27cf7..2efa15b75 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/AssertionService.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/AssertionService.java
@@ -21,9 +21,7 @@
import com.wultra.powerauth.fido2.errorhandling.Fido2AuthenticationFailedException;
import com.wultra.powerauth.fido2.rest.model.converter.AssertionChallengeConverter;
import com.wultra.powerauth.fido2.rest.model.converter.AssertionConverter;
-import com.wultra.powerauth.fido2.rest.model.entity.AssertionChallenge;
-import com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorAssertionResponse;
-import com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorDetail;
+import com.wultra.powerauth.fido2.rest.model.entity.*;
import com.wultra.powerauth.fido2.rest.model.request.AssertionChallengeRequest;
import com.wultra.powerauth.fido2.rest.model.request.AssertionVerificationRequest;
import com.wultra.powerauth.fido2.rest.model.response.AssertionChallengeResponse;
@@ -101,17 +99,19 @@ public AssertionVerificationResponse authenticate(AssertionVerificationRequest r
final Optional authenticatorOptional = authenticatorProvider.findByCredentialId(applicationId, authenticatorId);
authenticatorOptional.orElseThrow(() -> new Fido2AuthenticationFailedException("Invalid request"));
final AuthenticatorDetail authenticatorDetail = authenticatorOptional.get();
+ final AuthenticatorData authenticatorData = response.getAuthenticatorData();
+ final CollectedClientData clientDataJSON = response.getClientDataJSON();
if (authenticatorDetail.getActivationStatus() == ActivationStatus.ACTIVE) {
- final boolean signatureCorrect = cryptographyService.verifySignatureForAssertion(applicationId, authenticatorId, response.getClientDataJSON(), response.getAuthenticatorData(), response.getSignature(), authenticatorDetail);
+ final boolean signatureCorrect = cryptographyService.verifySignatureForAssertion(applicationId, authenticatorId, clientDataJSON, authenticatorData, response.getSignature(), authenticatorDetail);
if (signatureCorrect) {
- assertionProvider.approveAssertion(challenge, authenticatorDetail);
+ assertionProvider.approveAssertion(challenge, authenticatorDetail, authenticatorData, clientDataJSON);
return assertionConverter.fromAuthenticatorDetail(authenticatorDetail, signatureCorrect);
} else {
- assertionProvider.failAssertion(challenge, authenticatorDetail);
+ assertionProvider.failAssertion(challenge, authenticatorDetail, authenticatorData, clientDataJSON);
throw new Fido2AuthenticationFailedException("Authentication failed due to incorrect signature.");
}
} else {
- assertionProvider.failAssertion(challenge, authenticatorDetail);
+ assertionProvider.failAssertion(challenge, authenticatorDetail, authenticatorData, clientDataJSON);
throw new Fido2AuthenticationFailedException("Authentication failed due to incorrect authenticator state.");
}
} catch (Exception e) {
diff --git a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/provider/AssertionProvider.java b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/provider/AssertionProvider.java
index b6491f773..8577f37ef 100644
--- a/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/provider/AssertionProvider.java
+++ b/powerauth-fido2/src/main/java/com/wultra/powerauth/fido2/service/provider/AssertionProvider.java
@@ -20,7 +20,9 @@
import com.wultra.powerauth.fido2.errorhandling.Fido2AuthenticationFailedException;
import com.wultra.powerauth.fido2.rest.model.entity.AssertionChallenge;
+import com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorData;
import com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorDetail;
+import com.wultra.powerauth.fido2.rest.model.entity.CollectedClientData;
import java.util.List;
import java.util.Map;
@@ -47,21 +49,25 @@ public interface AssertionProvider {
/**
* Approve assertion.
*
- * @param challengeValue Challenge value.
+ * @param challengeValue Challenge value.
* @param authenticatorDetail Authenticator information.
+ * @param authenticatorData Authenticator data.
+ * @param clientDataJSON Client data.
* @return Assertion challenge.
* @throws Fido2AuthenticationFailedException In case assertion approval fails.
*/
- AssertionChallenge approveAssertion(String challengeValue, AuthenticatorDetail authenticatorDetail) throws Fido2AuthenticationFailedException;
+ AssertionChallenge approveAssertion(String challengeValue, AuthenticatorDetail authenticatorDetail, AuthenticatorData authenticatorData, CollectedClientData clientDataJSON) throws Fido2AuthenticationFailedException;
/**
* Fail assertion approval.
*
- * @param challenge Challenge for assertion.
+ * @param challenge Challenge for assertion.
* @param authenticatorDetail Authenticator detail.
+ * @param authenticatorData Authenticator data.
+ * @param clientDataJSON Client data.
* @return Info about the assertion.
* @throws Fido2AuthenticationFailedException In case assertion approval fails.
*/
- AssertionChallenge failAssertion(String challenge, AuthenticatorDetail authenticatorDetail) throws Fido2AuthenticationFailedException;
+ AssertionChallenge failAssertion(String challenge, AuthenticatorDetail authenticatorDetail, AuthenticatorData authenticatorData, CollectedClientData clientDataJSON) throws Fido2AuthenticationFailedException;
}
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAssertionProvider.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAssertionProvider.java
index 0410e98f6..31b4dbd8d 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAssertionProvider.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/fido2/PowerAuthAssertionProvider.java
@@ -21,8 +21,7 @@
import com.wultra.core.audit.base.model.AuditDetail;
import com.wultra.core.audit.base.model.AuditLevel;
import com.wultra.powerauth.fido2.errorhandling.Fido2AuthenticationFailedException;
-import com.wultra.powerauth.fido2.rest.model.entity.AssertionChallenge;
-import com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorDetail;
+import com.wultra.powerauth.fido2.rest.model.entity.*;
import com.wultra.powerauth.fido2.service.provider.AssertionProvider;
import com.wultra.security.powerauth.client.model.entity.KeyValue;
import com.wultra.security.powerauth.client.model.enumeration.OperationStatus;
@@ -46,10 +45,7 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
-import java.util.Date;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
/**
* Service responsible for assertion verification.
@@ -64,6 +60,8 @@ public class PowerAuthAssertionProvider implements AssertionProvider {
private static final String ATTR_ACTIVATION_ID = "activationId";
private static final String ATTR_APPLICATION_ID = "applicationId";
private static final String ATTR_AUTH_FACTOR = "authFactor";
+ private static final String ATTR_ORIGIN = "origin";
+ private static final String ATTR_TOP_ORIGIN = "topOrigin";
private final ServiceBehaviorCatalogue serviceBehaviorCatalogue;
private final RepositoryCatalogue repositoryCatalogue;
@@ -96,7 +94,7 @@ public AssertionChallenge provideChallengeForAssertion(List applicationI
@Override
@Transactional
- public AssertionChallenge approveAssertion(String challengeValue, AuthenticatorDetail authenticatorDetail) throws Fido2AuthenticationFailedException {
+ public AssertionChallenge approveAssertion(String challengeValue, AuthenticatorDetail authenticatorDetail, AuthenticatorData authenticatorData, CollectedClientData clientDataJSON) throws Fido2AuthenticationFailedException {
try {
final String[] split = challengeValue.split("&", 2);
@@ -111,8 +109,8 @@ public AssertionChallenge approveAssertion(String challengeValue, AuthenticatorD
operationApproveRequest.setData(operationData);
operationApproveRequest.setApplicationId(authenticatorDetail.getApplicationId());
operationApproveRequest.setUserId(authenticatorDetail.getUserId());
- operationApproveRequest.setSignatureType(SignatureType.POSSESSION_KNOWLEDGE); //TODO: Use correct type
- operationApproveRequest.getAdditionalData().putAll(prepareAdditionalData(authenticatorDetail, operationApproveRequest.getSignatureType()));
+ operationApproveRequest.setSignatureType(supportedSignatureType(authenticatorDetail, authenticatorData.getFlags().isUserVerified()));
+ operationApproveRequest.getAdditionalData().putAll(prepareAdditionalData(authenticatorDetail, authenticatorData, clientDataJSON));
final OperationUserActionResponse approveOperation = serviceBehaviorCatalogue.getOperationBehavior().attemptApproveOperation(operationApproveRequest);
final UserActionResult result = approveOperation.getResult();
final OperationDetailResponse operation = approveOperation.getOperation();
@@ -136,7 +134,7 @@ public AssertionChallenge approveAssertion(String challengeValue, AuthenticatorD
@Override
@Transactional
- public AssertionChallenge failAssertion(String challengeValue, AuthenticatorDetail authenticatorDetail) throws Fido2AuthenticationFailedException {
+ public AssertionChallenge failAssertion(String challengeValue, AuthenticatorDetail authenticatorDetail, AuthenticatorData authenticatorData, CollectedClientData clientDataJSON) throws Fido2AuthenticationFailedException {
try {
final Date currentTimestamp = new Date();
@@ -145,7 +143,7 @@ public AssertionChallenge failAssertion(String challengeValue, AuthenticatorDeta
final OperationFailApprovalRequest operationFailApprovalRequest = new OperationFailApprovalRequest();
operationFailApprovalRequest.setOperationId(operationId);
- operationFailApprovalRequest.getAdditionalData().putAll(prepareAdditionalData(authenticatorDetail, SignatureType.POSSESSION_KNOWLEDGE));
+ operationFailApprovalRequest.getAdditionalData().putAll(prepareAdditionalData(authenticatorDetail, authenticatorData, clientDataJSON));
final ActivationRecordEntity activationWithLock = repositoryCatalogue.getActivationRepository().findActivationWithLock(authenticatorDetail.getActivationId());
@@ -229,15 +227,22 @@ private void handleInvalidSignatureImpl(ActivationRecordEntity activation, Signa
/**
* Prepare map with additional data stored with the operation.
+ *
* @param authenticatorDetail Authenticator detail.
- * @param signatureType Used signature type.
+ * @param authenticatorData Authenticator data.
+ * @param clientDataJSON Client data.
* @return Additional data map.
*/
- private Map prepareAdditionalData(final AuthenticatorDetail authenticatorDetail, final SignatureType signatureType) {
+ private Map prepareAdditionalData(
+ final AuthenticatorDetail authenticatorDetail,
+ final AuthenticatorData authenticatorData,
+ final CollectedClientData clientDataJSON) {
final Map additionalData = new LinkedHashMap<>();
additionalData.put(ATTR_ACTIVATION_ID, authenticatorDetail.getActivationId());
additionalData.put(ATTR_APPLICATION_ID, authenticatorDetail.getApplicationId());
- additionalData.put(ATTR_AUTH_FACTOR, signatureType);
+ additionalData.put(ATTR_AUTH_FACTOR, supportedSignatureType(authenticatorDetail, authenticatorData.getFlags().isUserVerified()));
+ additionalData.put(ATTR_ORIGIN, clientDataJSON.getOrigin());
+ additionalData.put(ATTR_TOP_ORIGIN, clientDataJSON.getTopOrigin());
return additionalData;
}
@@ -262,6 +267,16 @@ private void handleStatus(OperationStatus status) throws Fido2AuthenticationFail
}
}
+ private SignatureType supportedSignatureType(AuthenticatorDetail authenticatorDetail, boolean userVerified) {
+ final String aaguidBase64 = (String) authenticatorDetail.getExtras().get("aaguid");
+ if (aaguidBase64 != null) {
+ final byte[] aaguid = Base64.getDecoder().decode(aaguidBase64);
+ return userVerified ? Fido2Authenticators.modelByAaguid(aaguid).signatureType() : SignatureType.POSSESSION;
+ } else {
+ return SignatureType.POSSESSION;
+ }
+ }
+
private static AuditingServiceBehavior.ActivationRecordDto createActivationDtoFrom(ActivationRecordEntity activation) {
return AuditingServiceBehavior.ActivationRecordDto.builder()
.activationId(activation.getActivationId())
From f7beef585aa229d7404726ff86ea484dbdf4939a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Lubo=C5=A1=20Ra=C4=8Dansk=C3=BD?=
Date: Tue, 5 Mar 2024 09:49:39 +0100
Subject: [PATCH 105/146] Fix #1376: Liquibase on MSSQL throws error (#1377)
* Set onError="MARK_RAN" for pa_operation_application_application_id_fk
---
docs/PowerAuth-Server-1.6.0.md | 6 ++++--
.../1.6.x/20231106-add-foreign-keys.xml | 2 +-
2 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/docs/PowerAuth-Server-1.6.0.md b/docs/PowerAuth-Server-1.6.0.md
index cc40ad151..c413c2a89 100644
--- a/docs/PowerAuth-Server-1.6.0.md
+++ b/docs/PowerAuth-Server-1.6.0.md
@@ -31,8 +31,10 @@ Add foreign key constraints to relating table `pa_operation_application`.
Applying this change may fail if there is an inconsistency between tables `pa_operation_application`
and `pa_application` or `pa_operation`. Make sure that `pa_operation_application.application_id` contains references to
existing `pa_application.id` and `pa_operation_application.operation_id` contains references to
-existing `pa_operation.id`. If necessary, manually remove orphaned records in `pa_operation_application`. Consider
-creating a backup before this operation.
+existing `pa_operation.id`.
+Also the column type of `pa_operation_application.application_id` must be the same as the type of `pa_operation.id`.
+If necessary, manually remove orphaned records in `pa_operation_application`.
+Consider creating a backup before this operation.
### Add activation_id Column
diff --git a/docs/db/changelog/changesets/powerauth-java-server/1.6.x/20231106-add-foreign-keys.xml b/docs/db/changelog/changesets/powerauth-java-server/1.6.x/20231106-add-foreign-keys.xml
index ce6a54098..acece2348 100644
--- a/docs/db/changelog/changesets/powerauth-java-server/1.6.x/20231106-add-foreign-keys.xml
+++ b/docs/db/changelog/changesets/powerauth-java-server/1.6.x/20231106-add-foreign-keys.xml
@@ -4,7 +4,7 @@
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.9.xsd">
-
+
From 1e6b9543b76ea2d735dffb12310df035ff4936ce Mon Sep 17 00:00:00 2001
From: Lubos Racansky
Date: Tue, 5 Mar 2024 10:45:04 +0100
Subject: [PATCH 106/146] Fix #1370: Update arch_db_structure diagram
---
docs/images/arch_db_structure.png | Bin 941345 -> 911303 bytes
1 file changed, 0 insertions(+), 0 deletions(-)
diff --git a/docs/images/arch_db_structure.png b/docs/images/arch_db_structure.png
index 26c1bff15d496eaddadf7dcdd33784e50f4ca5bf..711f7b2cea0b91f386a1b396ac68d454ff632341 100644
GIT binary patch
literal 911303
zcmeEuhg*}+wl1JxiP#BBM@3YcQl&Q)0RaUG5C}y&N$5p7sGz76LArn_MFrv@67jy(-!Vy_>F@3LUDQW19BI3u{Al8!aum8?^FKI!5}lbccRd
zq5aa)v(YjBt4v3yLC=0r)}_Dp?>Y>0bT94c4*$CjnD+Vm=RWO4yZhgthd$E(Y4Ib&
ze(fVc9}gXrtAF2SHSf1a`#9!&-^h)Q4sh=Gi=Hm&!v$JvM)r>l-3_%K$XGhTg)OX{
zo>&V%g**Sgi%#yT46O*acDLYp3Wqtm$vl<6^sgE+wDRxEBA0mnRmB}Df5}kmA&;t)
zt2K|L@J->HmlTfk@bJjFTG`0x-nsYh+i8EvU$S#|ca{+m@$&K#_7W3za|7v8vojcZUmag{B?)FZOJiptuc;e*YE`RCL?*|?H
z{8vBS?QQ-%$9sx;QZDt)_OgF|>>hKjrl*BT;O!VzOfBa-hPC>akqa9eWhYLabl{Pjs51uu`hIEhoDI7V28Wg@
z3@u4W*gW4C)Stn(I?yCYcQAH4dIS@u*=*qr-Np}XG4FhNOXy15U*%@Tu+5sb
z;gj0RwBN13_Wen0
ztWjzD`t=Uv)NM~*f7vzS`qKqVMgFhE`;A=_6R2~?XDjkViIRY(&p*HT`j-q<6c7OY
z$Qy+9i*Aoe;?>s4gaxV)j_%OqznG+ob2O1HypSQ>+3$kQmzoH8@|Y5IGI?<4*``}&
zNuG*I`YmR$(a+NlG+P3e>~AK=n_c1Lgo}!bU-M?X8yy{;rM&p_WsO3bnei?DvYP&9
zL%f`&h|l`EU1EG9`86S}PRHBn|GJVM96wFBg80*~#ViKU60vK5FAY2WMpqwlIlb@}
ztKn6~=Ya=|$d)Bb_%fyUmcSD~mgUJJlEViiCH
z`*_f;_HL0sRXZm)_ubt$Kxl|#6##z7Cve@GaDn?&Il+@t |