context) throws PowerAuthActivationException {
+ return true;
}
/**
diff --git a/powerauth-restful-security-spring-annotation/src/main/java/io/getlime/security/powerauth/rest/api/spring/provider/MinimalClaimsUserInfoProvider.java b/powerauth-restful-security-spring-annotation/src/main/java/io/getlime/security/powerauth/rest/api/spring/provider/MinimalClaimsUserInfoProvider.java
new file mode 100644
index 00000000..6d457291
--- /dev/null
+++ b/powerauth-restful-security-spring-annotation/src/main/java/io/getlime/security/powerauth/rest/api/spring/provider/MinimalClaimsUserInfoProvider.java
@@ -0,0 +1,75 @@
+/*
+ * PowerAuth integration libraries for RESTful API applications, examples 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.rest.api.spring.provider;
+
+import com.wultra.core.annotations.PublicSpi;
+import io.getlime.security.powerauth.rest.api.spring.model.UserInfoContext;
+
+import javax.annotation.Nonnull;
+import java.time.Instant;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * Specialization of {@link UserInfoProvider}.
+ * Claims {@code sub, jti, iat} are filled.
+ * UserInfo is always returned, even for activation.
+ *
+ * @author Lubos Racansky, lubos.racansky@wultra.com
+ */
+@PublicSpi
+public class MinimalClaimsUserInfoProvider implements UserInfoProvider{
+
+ /**
+ * Always true, even for activation.
+ *
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean shouldReturnUserInfo(@Nonnull UserInfoContext context) {
+ return true;
+ }
+
+ /**
+ * Fill claims {@code sub, jti, iat}.
+ *
+ * {@inheritDoc}
+ */
+ @Override
+ public Map fetchUserClaimsForUserId(@Nonnull UserInfoContext context) {
+ return minimalClaims(context);
+ }
+
+ /**
+ * Prepare a set of minimal claims sub
, jti
and iat
.
+ *
+ * @param context User info context object.
+ * @return Map of claims obtained for a given user ID.
+ */
+ private static Map minimalClaims(@Nonnull UserInfoContext context) {
+ final Map defaultClaims = new LinkedHashMap<>();
+ defaultClaims.put("sub", context.getUserId());
+ defaultClaims.put("jti", UUID.randomUUID().toString());
+ defaultClaims.put("iat", Instant.now().getEpochSecond());
+ return Collections.unmodifiableMap(defaultClaims);
+ }
+}
diff --git a/powerauth-restful-security-spring-annotation/src/main/java/io/getlime/security/powerauth/rest/api/spring/provider/PowerAuthAuthenticationProvider.java b/powerauth-restful-security-spring-annotation/src/main/java/io/getlime/security/powerauth/rest/api/spring/provider/PowerAuthAuthenticationProvider.java
index e499b167..e7ce01d4 100644
--- a/powerauth-restful-security-spring-annotation/src/main/java/io/getlime/security/powerauth/rest/api/spring/provider/PowerAuthAuthenticationProvider.java
+++ b/powerauth-restful-security-spring-annotation/src/main/java/io/getlime/security/powerauth/rest/api/spring/provider/PowerAuthAuthenticationProvider.java
@@ -19,10 +19,13 @@
*/
package io.getlime.security.powerauth.rest.api.spring.provider;
-import com.google.common.io.BaseEncoding;
import com.wultra.security.powerauth.client.PowerAuthClient;
+import com.wultra.security.powerauth.client.model.enumeration.SignatureType;
import com.wultra.security.powerauth.client.model.error.PowerAuthClientException;
-import com.wultra.security.powerauth.client.v3.*;
+import com.wultra.security.powerauth.client.model.request.ValidateTokenRequest;
+import com.wultra.security.powerauth.client.model.request.VerifySignatureRequest;
+import com.wultra.security.powerauth.client.model.response.ValidateTokenResponse;
+import com.wultra.security.powerauth.client.model.response.VerifySignatureResponse;
import io.getlime.security.powerauth.crypto.lib.enums.PowerAuthSignatureTypes;
import io.getlime.security.powerauth.http.PowerAuthHttpBody;
import io.getlime.security.powerauth.http.PowerAuthHttpHeader;
@@ -37,8 +40,8 @@
import io.getlime.security.powerauth.rest.api.spring.authentication.impl.PowerAuthApiAuthenticationImpl;
import io.getlime.security.powerauth.rest.api.spring.authentication.impl.PowerAuthSignatureAuthenticationImpl;
import io.getlime.security.powerauth.rest.api.spring.authentication.impl.PowerAuthTokenAuthenticationImpl;
-import io.getlime.security.powerauth.rest.api.spring.converter.v3.ActivationStatusConverter;
-import io.getlime.security.powerauth.rest.api.spring.converter.v3.SignatureTypeConverter;
+import io.getlime.security.powerauth.rest.api.spring.converter.ActivationStatusConverter;
+import io.getlime.security.powerauth.rest.api.spring.converter.SignatureTypeConverter;
import io.getlime.security.powerauth.rest.api.spring.exception.PowerAuthAuthenticationException;
import io.getlime.security.powerauth.rest.api.spring.exception.authentication.PowerAuthHeaderMissingException;
import io.getlime.security.powerauth.rest.api.spring.exception.authentication.PowerAuthSignatureInvalidException;
@@ -56,6 +59,7 @@
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
+import java.util.Base64;
import java.util.List;
/**
@@ -157,7 +161,7 @@ private PowerAuthApiAuthenticationImpl validateSignatureAuthentication(PowerAuth
final AuthenticationContext authenticationContext = new AuthenticationContext();
authenticationContext.setValid(response.isSignatureValid());
authenticationContext.setRemainingAttempts(response.getRemainingAttempts() != null ? response.getRemainingAttempts().intValue() : null);
- authenticationContext.setSignatureType(response.getSignatureType() != null ? PowerAuthSignatureTypes.getEnumFromString(response.getSignatureType().value()) : null);
+ authenticationContext.setSignatureType(response.getSignatureType() != null ? PowerAuthSignatureTypes.getEnumFromString(response.getSignatureType().name()) : null);
final PowerAuthActivation activationContext = copyActivationAttributes(response.getActivationId(), response.getUserId(),
activationStatus, response.getBlockedReason(),
response.getActivationFlags(), authenticationContext, authentication.getVersion());
@@ -183,6 +187,7 @@ private PowerAuthApiAuthenticationImpl validateTokenAuthentication(PowerAuthToke
validateRequest.setTokenDigest(authentication.getTokenDigest());
validateRequest.setNonce(authentication.getNonce());
validateRequest.setTimestamp(Long.parseLong(authentication.getTimestamp()));
+ validateRequest.setProtocolVersion(authentication.getVersion());
final ValidateTokenResponse response = powerAuthClient.validateToken(
validateRequest,
@@ -194,7 +199,7 @@ private PowerAuthApiAuthenticationImpl validateTokenAuthentication(PowerAuthToke
final AuthenticationContext authenticationContext = new AuthenticationContext();
authenticationContext.setValid(response.isTokenValid());
authenticationContext.setRemainingAttempts(null);
- authenticationContext.setSignatureType(response.getSignatureType() != null ? PowerAuthSignatureTypes.getEnumFromString(response.getSignatureType().value()) : null);
+ authenticationContext.setSignatureType(response.getSignatureType() != null ? PowerAuthSignatureTypes.getEnumFromString(response.getSignatureType().name()) : null);
final PowerAuthActivation activationContext = copyActivationAttributes(response.getActivationId(), response.getUserId(),
activationStatus, response.getBlockedReason(),
response.getActivationFlags(), authenticationContext, authentication.getVersion());
@@ -326,7 +331,7 @@ public PowerAuthApiAuthentication validateRequestSignature(
final PowerAuthSignatureAuthenticationImpl powerAuthAuthentication = new PowerAuthSignatureAuthenticationImpl();
powerAuthAuthentication.setActivationId(header.getActivationId());
powerAuthAuthentication.setApplicationKey(header.getApplicationKey());
- powerAuthAuthentication.setNonce(BaseEncoding.base64().decode(header.getNonce()));
+ powerAuthAuthentication.setNonce(Base64.getDecoder().decode(header.getNonce()));
powerAuthAuthentication.setSignatureType(header.getSignatureType());
powerAuthAuthentication.setSignature(header.getSignature());
powerAuthAuthentication.setHttpMethod(httpMethod);
diff --git a/powerauth-restful-security-spring-annotation/src/main/java/io/getlime/security/powerauth/rest/api/spring/provider/PowerAuthAuthenticationProviderBase.java b/powerauth-restful-security-spring-annotation/src/main/java/io/getlime/security/powerauth/rest/api/spring/provider/PowerAuthAuthenticationProviderBase.java
index c5798279..ee04280c 100644
--- a/powerauth-restful-security-spring-annotation/src/main/java/io/getlime/security/powerauth/rest/api/spring/provider/PowerAuthAuthenticationProviderBase.java
+++ b/powerauth-restful-security-spring-annotation/src/main/java/io/getlime/security/powerauth/rest/api/spring/provider/PowerAuthAuthenticationProviderBase.java
@@ -21,7 +21,7 @@
import io.getlime.security.powerauth.crypto.lib.enums.PowerAuthSignatureTypes;
import io.getlime.security.powerauth.rest.api.spring.authentication.PowerAuthApiAuthentication;
-import io.getlime.security.powerauth.rest.api.spring.encryption.PowerAuthEciesEncryption;
+import io.getlime.security.powerauth.rest.api.spring.encryption.PowerAuthEncryptorData;
import io.getlime.security.powerauth.rest.api.spring.exception.PowerAuthAuthenticationException;
import io.getlime.security.powerauth.rest.api.spring.exception.authentication.PowerAuthRequestFilterException;
import io.getlime.security.powerauth.rest.api.spring.model.PowerAuthRequestBody;
@@ -31,7 +31,7 @@
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
-import javax.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
@@ -198,8 +198,8 @@ public abstract class PowerAuthAuthenticationProviderBase {
*/
public @Nullable byte[] extractRequestBodyBytes(@Nonnull HttpServletRequest servletRequest) throws PowerAuthAuthenticationException {
if (servletRequest.getAttribute(PowerAuthRequestObjects.ENCRYPTION_OBJECT) != null) {
- // Implementation of sign-then-encrypt - in case the encryption object is present and signature is validate, use decrypted request data
- PowerAuthEciesEncryption eciesEncryption = (PowerAuthEciesEncryption) servletRequest.getAttribute(PowerAuthRequestObjects.ENCRYPTION_OBJECT);
+ // Implementation of sign-then-encrypt - in case the encryption object is present and signature is valid, use decrypted request data
+ PowerAuthEncryptorData eciesEncryption = (PowerAuthEncryptorData) servletRequest.getAttribute(PowerAuthRequestObjects.ENCRYPTION_OBJECT);
return eciesEncryption.getDecryptedRequest();
} else {
// Request data was not encrypted - use regular PowerAuth request body for signature validation
diff --git a/powerauth-restful-security-spring-annotation/src/main/java/io/getlime/security/powerauth/rest/api/spring/provider/PowerAuthEncryptionProvider.java b/powerauth-restful-security-spring-annotation/src/main/java/io/getlime/security/powerauth/rest/api/spring/provider/PowerAuthEncryptionProvider.java
index 8451c560..9b04439d 100644
--- a/powerauth-restful-security-spring-annotation/src/main/java/io/getlime/security/powerauth/rest/api/spring/provider/PowerAuthEncryptionProvider.java
+++ b/powerauth-restful-security-spring-annotation/src/main/java/io/getlime/security/powerauth/rest/api/spring/provider/PowerAuthEncryptionProvider.java
@@ -20,9 +20,9 @@
package io.getlime.security.powerauth.rest.api.spring.provider;
import com.wultra.security.powerauth.client.PowerAuthClient;
-import com.wultra.security.powerauth.client.v3.GetEciesDecryptorRequest;
-import com.wultra.security.powerauth.client.v3.GetEciesDecryptorResponse;
-import io.getlime.security.powerauth.rest.api.spring.encryption.PowerAuthEciesDecryptorParameters;
+import com.wultra.security.powerauth.client.model.request.GetEciesDecryptorRequest;
+import com.wultra.security.powerauth.client.model.response.GetEciesDecryptorResponse;
+import io.getlime.security.powerauth.rest.api.spring.encryption.PowerAuthEncryptorParameters;
import io.getlime.security.powerauth.rest.api.spring.exception.PowerAuthEncryptionException;
import io.getlime.security.powerauth.rest.api.spring.service.HttpCustomizationService;
import org.slf4j.Logger;
@@ -59,19 +59,22 @@ public PowerAuthEncryptionProvider(PowerAuthClient powerAuthClient, HttpCustomiz
}
@Override
- public @Nonnull PowerAuthEciesDecryptorParameters getEciesDecryptorParameters(@Nullable String activationId, @Nonnull String applicationKey, @Nonnull String ephemeralPublicKey) throws PowerAuthEncryptionException {
+ public @Nonnull PowerAuthEncryptorParameters getEciesDecryptorParameters(@Nullable String activationId, @Nonnull String applicationKey, @Nonnull String ephemeralPublicKey, @Nonnull String version, String nonce, Long timestamp) throws PowerAuthEncryptionException {
try {
final GetEciesDecryptorRequest eciesDecryptorRequest = new GetEciesDecryptorRequest();
eciesDecryptorRequest.setActivationId(activationId);
eciesDecryptorRequest.setApplicationKey(applicationKey);
eciesDecryptorRequest.setEphemeralPublicKey(ephemeralPublicKey);
+ eciesDecryptorRequest.setProtocolVersion(version);
+ eciesDecryptorRequest.setNonce(nonce);
+ eciesDecryptorRequest.setTimestamp(timestamp);
final GetEciesDecryptorResponse eciesDecryptorResponse = powerAuthClient.getEciesDecryptor(
eciesDecryptorRequest,
httpCustomizationService.getQueryParams(),
httpCustomizationService.getHttpHeaders()
);
- return new PowerAuthEciesDecryptorParameters(eciesDecryptorResponse.getSecretKey(), eciesDecryptorResponse.getSharedInfo2());
+ return new PowerAuthEncryptorParameters(eciesDecryptorResponse.getSecretKey(), eciesDecryptorResponse.getSharedInfo2());
} catch (Exception ex) {
logger.warn("Get ECIES decryptor call failed, error: {}", ex.getMessage());
logger.debug(ex.getMessage(), ex);
diff --git a/powerauth-restful-security-spring-annotation/src/main/java/io/getlime/security/powerauth/rest/api/spring/provider/PowerAuthEncryptionProviderBase.java b/powerauth-restful-security-spring-annotation/src/main/java/io/getlime/security/powerauth/rest/api/spring/provider/PowerAuthEncryptionProviderBase.java
index 04b100b4..5376a3da 100644
--- a/powerauth-restful-security-spring-annotation/src/main/java/io/getlime/security/powerauth/rest/api/spring/provider/PowerAuthEncryptionProviderBase.java
+++ b/powerauth-restful-security-spring-annotation/src/main/java/io/getlime/security/powerauth/rest/api/spring/provider/PowerAuthEncryptionProviderBase.java
@@ -23,33 +23,33 @@
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;
-import com.google.common.io.BaseEncoding;
-import io.getlime.security.powerauth.crypto.lib.encryptor.ecies.EciesDecryptor;
-import io.getlime.security.powerauth.crypto.lib.encryptor.ecies.EciesEnvelopeKey;
-import io.getlime.security.powerauth.crypto.lib.encryptor.ecies.EciesFactory;
-import io.getlime.security.powerauth.crypto.lib.encryptor.ecies.model.EciesCryptogram;
-import io.getlime.security.powerauth.crypto.lib.encryptor.ecies.model.EciesScope;
+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.*;
+import io.getlime.security.powerauth.crypto.lib.encryptor.model.v3.ServerEncryptorSecrets;
import io.getlime.security.powerauth.http.PowerAuthEncryptionHttpHeader;
import io.getlime.security.powerauth.http.PowerAuthSignatureHttpHeader;
import io.getlime.security.powerauth.http.validator.InvalidPowerAuthHttpHeaderException;
import io.getlime.security.powerauth.http.validator.PowerAuthEncryptionHttpHeaderValidator;
import io.getlime.security.powerauth.http.validator.PowerAuthSignatureHttpHeaderValidator;
-import io.getlime.security.powerauth.rest.api.spring.encryption.EciesEncryptionContext;
-import io.getlime.security.powerauth.rest.api.spring.encryption.PowerAuthEciesDecryptorParameters;
-import io.getlime.security.powerauth.rest.api.spring.encryption.PowerAuthEciesEncryption;
+import io.getlime.security.powerauth.rest.api.model.request.EciesEncryptedRequest;
+import io.getlime.security.powerauth.rest.api.model.response.EciesEncryptedResponse;
+import io.getlime.security.powerauth.rest.api.spring.encryption.EncryptionContext;
+import io.getlime.security.powerauth.rest.api.spring.encryption.EncryptionScope;
+import io.getlime.security.powerauth.rest.api.spring.encryption.PowerAuthEncryptorParameters;
+import io.getlime.security.powerauth.rest.api.spring.encryption.PowerAuthEncryptorData;
import io.getlime.security.powerauth.rest.api.spring.exception.PowerAuthEncryptionException;
import io.getlime.security.powerauth.rest.api.spring.model.PowerAuthRequestBody;
import io.getlime.security.powerauth.rest.api.spring.model.PowerAuthRequestObjects;
-import io.getlime.security.powerauth.rest.api.model.request.v3.EciesEncryptedRequest;
-import io.getlime.security.powerauth.rest.api.model.response.v3.EciesEncryptedResponse;
+import jakarta.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
-import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.lang.reflect.Type;
+import java.util.Base64;
/**
* Abstract class for PowerAuth encryption provider with common HTTP header parsing logic. The class is available for
@@ -63,30 +63,33 @@ public abstract class PowerAuthEncryptionProviderBase {
private static final Logger logger = LoggerFactory.getLogger(PowerAuthEncryptionProviderBase.class);
private final ObjectMapper objectMapper = new ObjectMapper();
- private final EciesFactory eciesFactory = new EciesFactory();
+ private final EncryptorFactory encryptorFactory = new EncryptorFactory();
/**
* Get ECIES decryptor parameters from PowerAuth server.
*
- * @param activationId Activation ID (only used in activation scope, in application scope use null).
- * @param applicationKey Application key.
+ * @param activationId Activation ID (only used in activation scope, in application scope use null).
+ * @param applicationKey Application key.
* @param ephemeralPublicKey Ephemeral public key for ECIES.
+ * @param version ECIES protocol version.
+ * @param nonce ECIES nonce.
+ * @param timestamp Timestamp for ECIES.
* @return ECIES decryptor parameters.
* @throws PowerAuthEncryptionException In case PowerAuth server call fails.
*/
- public abstract @Nonnull PowerAuthEciesDecryptorParameters getEciesDecryptorParameters(@Nullable String activationId, @Nonnull String applicationKey, @Nonnull String ephemeralPublicKey) throws PowerAuthEncryptionException;
+ public abstract @Nonnull
+ PowerAuthEncryptorParameters getEciesDecryptorParameters(@Nullable String activationId, @Nonnull String applicationKey, @Nonnull String ephemeralPublicKey, @Nonnull String version, String nonce, Long timestamp) throws PowerAuthEncryptionException;
/**
* Decrypt HTTP request body and construct object with ECIES data. Use the requestType parameter to specify
* the type of decrypted object.
*
- * @param request HTTP request.
- * @param requestType Class of request object.
- * @param eciesScope ECIES scope.
- * @return Object with ECIES data.
+ * @param request HTTP request.
+ * @param requestType Class of request object.
+ * @param encryptionScope Encryption scope.
* @throws PowerAuthEncryptionException In case request decryption fails.
*/
- public @Nonnull PowerAuthEciesEncryption decryptRequest(@Nonnull HttpServletRequest request, @Nonnull Type requestType, @Nonnull EciesScope eciesScope) throws PowerAuthEncryptionException {
+ public void decryptRequest(@Nonnull HttpServletRequest request, @Nonnull Type requestType, @Nonnull EncryptionScope encryptionScope) throws PowerAuthEncryptionException {
// Only POST HTTP method is supported for ECIES
if (!"POST".equals(request.getMethod())) {
logger.warn("Invalid HTTP method: {}", request.getMethod());
@@ -94,13 +97,10 @@ public abstract class PowerAuthEncryptionProviderBase {
}
// Resolve either signature or encryption HTTP header for ECIES
- final EciesEncryptionContext encryptionContext = extractEciesEncryptionContext(request);
+ final EncryptionContext encryptionContext = extractEciesEncryptionContext(request, encryptionScope);
// Construct ECIES encryption object from HTTP header
- final PowerAuthEciesEncryption eciesEncryption = new PowerAuthEciesEncryption(encryptionContext);
-
- // Save ECIES scope in context
- eciesEncryption.getContext().setEciesScope(eciesScope);
+ final PowerAuthEncryptorData encryptorData = new PowerAuthEncryptorData(encryptionContext);
try {
// Parse ECIES cryptogram from request body
@@ -122,103 +122,108 @@ public abstract class PowerAuthEncryptionProviderBase {
logger.debug(ex.getMessage(), ex);
throw new PowerAuthEncryptionException();
}
-
if (eciesRequest == null) {
logger.warn("Deserialization of request body bytes resulted in null value.");
throw new PowerAuthEncryptionException();
}
- // Prepare ephemeral public key
- final String ephemeralPublicKey = eciesRequest.getEphemeralPublicKey();
- final String encryptedData = eciesRequest.getEncryptedData();
- final String mac = eciesRequest.getMac();
- final String nonce = eciesRequest.getNonce();
+ // Extract useful properties in advance
+ final String version = encryptionContext.getVersion();
+ final String applicationKey = encryptionContext.getApplicationKey();
+ final String activationId = encryptionContext.getActivationId();
- // Verify ECIES request data. Nonce is required for protocol 3.1+
- if (ephemeralPublicKey == null || encryptedData == null || mac == null) {
- logger.warn("Invalid ECIES request data");
+ // Prepare and validate EncryptedRequest object
+ final EncryptedRequest encryptedRequest = new EncryptedRequest(
+ eciesRequest.getEphemeralPublicKey(),
+ eciesRequest.getEncryptedData(),
+ eciesRequest.getMac(),
+ eciesRequest.getNonce(),
+ eciesRequest.getTimestamp()
+ );
+ if (!encryptorFactory.getRequestResponseValidator(version).validateEncryptedRequest(encryptedRequest)) {
+ logger.warn("Invalid encrypted request data");
throw new PowerAuthEncryptionException();
}
- if (nonce == null && !"3.0".equals(encryptionContext.getVersion())) {
- logger.warn("Missing nonce in ECIES request data");
+ // Validate presence of activation id for activation scope.
+ if (encryptionScope == EncryptionScope.ACTIVATION_SCOPE && activationId == null) {
+ logger.warn("Activation ID is required for activation scope");
throw new PowerAuthEncryptionException();
}
+ // Get encryptor parameters from the PowerAuth Server.
+ final PowerAuthEncryptorParameters encryptorParameters = getEciesDecryptorParameters(
+ activationId,
+ applicationKey,
+ encryptedRequest.getEphemeralPublicKey(),
+ version,
+ encryptedRequest.getNonce(),
+ encryptedRequest.getTimestamp()
+ );
+ // Build server encryptor with obtained encryptor parameters
+ final byte[] secretKeyBytes = Base64.getDecoder().decode(encryptorParameters.secretKey());
+ final byte[] sharedInfo2Base = Base64.getDecoder().decode(encryptorParameters.sharedInfo2());
+ final ServerEncryptor serverEncryptor = encryptorFactory.getServerEncryptor(
+ encryptorData.getEncryptorId(),
+ new EncryptorParameters(version, applicationKey, activationId),
+ new ServerEncryptorSecrets(secretKeyBytes, sharedInfo2Base)
+ );
- final byte[] ephemeralPublicKeyBytes = BaseEncoding.base64().decode(ephemeralPublicKey);
- final byte[] encryptedDataBytes = BaseEncoding.base64().decode(encryptedData);
- final byte[] macBytes = BaseEncoding.base64().decode(mac);
- final byte[] nonceBytes = nonce != null ? BaseEncoding.base64().decode(nonce) : null;
-
- final String applicationKey = eciesEncryption.getContext().getApplicationKey();
- final PowerAuthEciesDecryptorParameters decryptorParameters;
- // Obtain ECIES decryptor parameters from PowerAuth server
- switch (eciesScope) {
- case ACTIVATION_SCOPE:
- final String activationId = eciesEncryption.getContext().getActivationId();
- if (activationId == null) {
- logger.warn("Activation ID is required in ECIES activation scope");
- throw new PowerAuthEncryptionException();
- }
- decryptorParameters = getEciesDecryptorParameters(activationId, applicationKey, ephemeralPublicKey);
- break;
- case APPLICATION_SCOPE:
- decryptorParameters = getEciesDecryptorParameters(null, applicationKey, ephemeralPublicKey);
- break;
- default:
- logger.warn("Unsupported ECIES scope: {}", eciesScope);
- throw new PowerAuthEncryptionException();
- }
-
- // Prepare envelope key and sharedInfo2 parameter for decryptor
- final byte[] secretKey = BaseEncoding.base64().decode(decryptorParameters.getSecretKey());
- final EciesEnvelopeKey envelopeKey = new EciesEnvelopeKey(secretKey, ephemeralPublicKeyBytes);
- final byte[] sharedInfo2 = BaseEncoding.base64().decode(decryptorParameters.getSharedInfo2());
+ // Try to decrypt request data
+ final byte[] decryptedData = serverEncryptor.decryptRequest(encryptedRequest);
- // Construct decryptor and set it to the request for later encryption of response
- final EciesDecryptor eciesDecryptor = eciesFactory.getEciesDecryptor(envelopeKey, sharedInfo2);
- eciesEncryption.setEciesDecryptor(eciesDecryptor);
+ encryptorData.setEncryptedRequest(encryptedRequest);
+ encryptorData.setDecryptedRequest(decryptedData);
+ encryptorData.setServerEncryptor(serverEncryptor);
- // Decrypt request data
- final EciesCryptogram cryptogram = new EciesCryptogram(ephemeralPublicKeyBytes, macBytes, encryptedDataBytes, nonceBytes);
- final byte[] decryptedData = eciesDecryptor.decryptRequest(cryptogram);
- eciesEncryption.setEncryptedRequest(encryptedDataBytes);
- eciesEncryption.setDecryptedRequest(decryptedData);
// Set the request object only in case when request data is sent
if (decryptedData.length != 0) {
- eciesEncryption.setRequestObject(deserializeRequestData(decryptedData, requestType));
+ encryptorData.setRequestObject(deserializeRequestData(decryptedData, requestType));
}
// Set encryption object in HTTP servlet request
- request.setAttribute(PowerAuthRequestObjects.ENCRYPTION_OBJECT, eciesEncryption);
+ request.setAttribute(PowerAuthRequestObjects.ENCRYPTION_OBJECT, encryptorData);
} catch (Exception ex) {
logger.warn("Request decryption failed, error: " + ex.getMessage());
logger.debug(ex.getMessage(), ex);
throw new PowerAuthEncryptionException();
}
- return eciesEncryption;
}
/**
- * Encrypt response using ECIES.
+ * Encrypt response using End-To-End Encryptor.
*
- * @param responseObject Response object which should be encrypted.
- * @param eciesEncryption PowerAuth encryption object.
+ * @param responseObject Response object which should be encrypted.
+ * @param encryption PowerAuth encryption object.
* @return ECIES encrypted response.
*/
- public @Nullable EciesEncryptedResponse encryptResponse(@Nonnull Object responseObject, @Nonnull PowerAuthEciesEncryption eciesEncryption) {
+ public @Nullable
+ EciesEncryptedResponse encryptResponse(@Nonnull Object responseObject, @Nonnull PowerAuthEncryptorData encryption) {
try {
+ final EncryptionContext encryptionContext = encryption.getContext();
+ final ServerEncryptor serverEncryptor = encryption.getServerEncryptor();
+ if (encryptionContext == null) {
+ logger.warn("Encryption context is not prepared");
+ throw new PowerAuthEncryptionException();
+ }
+ if (serverEncryptor == null || serverEncryptor.canEncryptResponse()) {
+ logger.warn("Encryptor is not available or not prepared for encryption. Scope: {}", encryptionContext.getEncryptionScope());
+ throw new PowerAuthEncryptionException();
+ }
+ // Serialize response data
final byte[] responseData = serializeResponseData(responseObject);
- // Encrypt response using decryptor and return ECIES cryptogram
- final EciesCryptogram cryptogram = eciesEncryption.getEciesDecryptor().encryptResponse(responseData);
- final String encryptedDataBase64 = BaseEncoding.base64().encode(cryptogram.getEncryptedData());
- final String macBase64 = BaseEncoding.base64().encode(cryptogram.getMac());
- return new EciesEncryptedResponse(encryptedDataBase64, macBase64);
+ // Encrypt response
+ final EncryptedResponse encryptedResponse = serverEncryptor.encryptResponse(responseData);
+ return new EciesEncryptedResponse(
+ encryptedResponse.getEncryptedData(),
+ encryptedResponse.getMac(),
+ encryptedResponse.getNonce(),
+ encryptedResponse.getTimestamp()
+ );
} catch (Exception ex) {
logger.debug("Response encryption failed, error: " + ex.getMessage(), ex);
return null;
}
}
-
+
/**
* Convert byte[] request data to Object with given type.
*
@@ -259,10 +264,11 @@ private byte[] serializeResponseData(Object responseObject) throws JsonProcessin
* Extract context required for ECIES encryption from either encryption or signature HTTP header.
*
* @param request HTTP servlet request.
+ * @param encryptorScope Scope of encryption.
* @return Context for ECIES encryption.
* @throws PowerAuthEncryptionException Thrown when HTTP header with ECIES data is invalid.
*/
- private EciesEncryptionContext extractEciesEncryptionContext(HttpServletRequest request) throws PowerAuthEncryptionException {
+ private EncryptionContext extractEciesEncryptionContext(HttpServletRequest request, EncryptionScope encryptorScope) throws PowerAuthEncryptionException {
final String encryptionHttpHeader = request.getHeader(PowerAuthEncryptionHttpHeader.HEADER_NAME);
final String signatureHttpHeader = request.getHeader(PowerAuthSignatureHttpHeader.HEADER_NAME);
@@ -290,14 +296,14 @@ private EciesEncryptionContext extractEciesEncryptionContext(HttpServletRequest
final String applicationKey = header.getApplicationKey();
final String activationId = header.getActivationId();
final String version = header.getVersion();
- return new EciesEncryptionContext(applicationKey, activationId, version, header);
+ return new EncryptionContext(applicationKey, activationId, version, header, encryptorScope);
} else {
// Parse encryption HTTP header
final PowerAuthEncryptionHttpHeader header = new PowerAuthEncryptionHttpHeader().fromValue(encryptionHttpHeader);
// Validate the encryption HTTP header
try {
- PowerAuthEncryptionHttpHeaderValidator.validate(header);
+ PowerAuthEncryptionHttpHeaderValidator.validate(header, encryptorScope.toEncryptorScope());
} catch (InvalidPowerAuthHttpHeaderException ex) {
logger.warn("Encryption validation failed, error: {}", ex.getMessage());
logger.debug(ex.getMessage(), ex);
@@ -308,8 +314,7 @@ private EciesEncryptionContext extractEciesEncryptionContext(HttpServletRequest
final String applicationKey = header.getApplicationKey();
final String activationId = header.getActivationId();
final String version = header.getVersion();
- return new EciesEncryptionContext(applicationKey, activationId, version, header);
+ return new EncryptionContext(applicationKey, activationId, version, header, encryptorScope);
}
}
-
}
diff --git a/powerauth-restful-security-spring-annotation/src/main/java/io/getlime/security/powerauth/rest/api/spring/provider/UserInfoProvider.java b/powerauth-restful-security-spring-annotation/src/main/java/io/getlime/security/powerauth/rest/api/spring/provider/UserInfoProvider.java
new file mode 100644
index 00000000..ae7be0f4
--- /dev/null
+++ b/powerauth-restful-security-spring-annotation/src/main/java/io/getlime/security/powerauth/rest/api/spring/provider/UserInfoProvider.java
@@ -0,0 +1,60 @@
+/*
+ * PowerAuth integration libraries for RESTful API applications, examples 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.rest.api.spring.provider;
+
+import com.wultra.core.annotations.PublicSpi;
+import io.getlime.security.powerauth.rest.api.model.entity.UserInfoStage;
+import io.getlime.security.powerauth.rest.api.spring.model.UserInfoContext;
+
+import javax.annotation.Nonnull;
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * Interface for bean that provides information about a given user.
+ *
+ * @author Petr Dvorak, petr@wultra.com
+ */
+@PublicSpi
+public interface UserInfoProvider {
+
+ /**
+ * Determine if the user info should be returned during the provided stage. By default, the user info is only
+ * available via a specialized /pa/v3/user/info
endpoint. By overriding this method, the user info claims
+ * might be also returned in the activation response body (inside the outer-encrypted layer).
+ *
+ * @param context User info context object.
+ * @return True if the user info should be returned during the activation, false otherwise (user info is only
+ * returned in the separate user info endpoint).
+ */
+ default boolean shouldReturnUserInfo(@Nonnull UserInfoContext context) {
+ return UserInfoStage.USER_INFO_ENDPOINT == context.getStage();
+ }
+
+ /**
+ * Return claims (as used, for example, in JWT) for a given user ID. Default implementation returns minimal claims.
+ *
+ * @param context User info context object.
+ * @return Map of claims obtained for a given user ID.
+ */
+ default Map fetchUserClaimsForUserId(@Nonnull UserInfoContext context) {
+ return Collections.emptyMap();
+ }
+}
diff --git a/powerauth-restful-security-spring/pom.xml b/powerauth-restful-security-spring/pom.xml
index a4965be6..8c6df5bc 100644
--- a/powerauth-restful-security-spring/pom.xml
+++ b/powerauth-restful-security-spring/pom.xml
@@ -30,7 +30,7 @@
io.getlime.security
powerauth-restful-integration-parent
- 1.4.0
+ 1.5.0
@@ -45,7 +45,7 @@
io.getlime.security
powerauth-rest-client-spring
- ${powerauth-rest-client-spring.version}
+ ${powerauth.version}
log4j-to-slf4j
@@ -54,6 +54,13 @@
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
diff --git a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/v3/ActivationController.java b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/ActivationController.java
similarity index 78%
rename from powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/v3/ActivationController.java
rename to powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/ActivationController.java
index ab6f80a9..93e41720 100644
--- a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/v3/ActivationController.java
+++ b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/ActivationController.java
@@ -17,34 +17,34 @@
* 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.rest.api.spring.controller.v3;
+package io.getlime.security.powerauth.rest.api.spring.controller;
import io.getlime.core.rest.model.base.request.ObjectRequest;
import io.getlime.core.rest.model.base.response.ObjectResponse;
-import io.getlime.security.powerauth.crypto.lib.encryptor.ecies.model.EciesScope;
import io.getlime.security.powerauth.http.PowerAuthSignatureHttpHeader;
import io.getlime.security.powerauth.rest.api.spring.authentication.PowerAuthApiAuthentication;
-import io.getlime.security.powerauth.rest.api.spring.encryption.EciesEncryptionContext;
+import io.getlime.security.powerauth.rest.api.spring.encryption.EncryptionContext;
+import io.getlime.security.powerauth.rest.api.spring.encryption.EncryptionScope;
import io.getlime.security.powerauth.rest.api.spring.exception.PowerAuthActivationException;
import io.getlime.security.powerauth.rest.api.spring.exception.PowerAuthAuthenticationException;
import io.getlime.security.powerauth.rest.api.spring.exception.PowerAuthRecoveryException;
-import io.getlime.security.powerauth.rest.api.spring.exception.authentication.PowerAuthInvalidRequestException;
import io.getlime.security.powerauth.rest.api.spring.exception.authentication.PowerAuthSignatureInvalidException;
-import io.getlime.security.powerauth.rest.api.model.request.v3.ActivationLayer1Request;
-import io.getlime.security.powerauth.rest.api.model.request.v3.ActivationStatusRequest;
-import io.getlime.security.powerauth.rest.api.model.response.v3.ActivationLayer1Response;
-import io.getlime.security.powerauth.rest.api.model.response.v3.ActivationRemoveResponse;
-import io.getlime.security.powerauth.rest.api.model.response.v3.ActivationStatusResponse;
+import io.getlime.security.powerauth.rest.api.model.request.ActivationLayer1Request;
+import io.getlime.security.powerauth.rest.api.model.request.ActivationStatusRequest;
+import io.getlime.security.powerauth.rest.api.model.response.ActivationLayer1Response;
+import io.getlime.security.powerauth.rest.api.model.response.ActivationRemoveResponse;
+import io.getlime.security.powerauth.rest.api.model.response.ActivationStatusResponse;
import io.getlime.security.powerauth.rest.api.spring.annotation.EncryptedRequestBody;
import io.getlime.security.powerauth.rest.api.spring.annotation.PowerAuthEncryption;
import io.getlime.security.powerauth.rest.api.spring.provider.PowerAuthAuthenticationProvider;
-import io.getlime.security.powerauth.rest.api.spring.service.v3.ActivationService;
+import io.getlime.security.powerauth.rest.api.spring.service.ActivationService;
+import io.getlime.security.powerauth.rest.api.spring.util.PowerAuthVersionUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
-import javax.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletRequest;
/**
* Controller implementing activation related end-points from the PowerAuth
@@ -89,20 +89,20 @@ public void setAuthenticationProvider(PowerAuthAuthenticationProvider authentica
/**
* Create activation.
* @param request Encrypted activation layer 1 request.
- * @param eciesContext ECIES encryption context.
+ * @param context Encryption context.
* @return Activation layer 1 response.
* @throws PowerAuthActivationException In case activation fails.
* @throws PowerAuthRecoveryException In case recovery PUK is invalid.
*/
- @RequestMapping(value = "create", method = RequestMethod.POST)
- @PowerAuthEncryption(scope = EciesScope.APPLICATION_SCOPE)
+ @PostMapping("create")
+ @PowerAuthEncryption(scope = EncryptionScope.APPLICATION_SCOPE)
public ActivationLayer1Response createActivation(@EncryptedRequestBody ActivationLayer1Request request,
- EciesEncryptionContext eciesContext) throws PowerAuthActivationException, PowerAuthRecoveryException {
- if (request == null || eciesContext == null) {
+ EncryptionContext context) throws PowerAuthActivationException, PowerAuthRecoveryException {
+ if (request == null || context == null) {
logger.warn("Invalid request in activation create");
throw new PowerAuthActivationException();
}
- return activationServiceV3.createActivation(request, eciesContext);
+ return activationServiceV3.createActivation(request, context);
}
/**
@@ -111,7 +111,7 @@ public ActivationLayer1Response createActivation(@EncryptedRequestBody Activatio
* @return PowerAuth RESTful response with {@link ActivationStatusResponse} payload.
* @throws PowerAuthActivationException In case request fails.
*/
- @RequestMapping(value = "status", method = RequestMethod.POST)
+ @PostMapping("status")
public ObjectResponse getActivationStatus(@RequestBody ObjectRequest request)
throws PowerAuthActivationException {
if (request.getRequestObject() == null || request.getRequestObject().getActivationId() == null) {
@@ -130,7 +130,7 @@ public ObjectResponse getActivationStatus(@RequestBody
* @throws PowerAuthActivationException In case activation access fails.
* @throws PowerAuthAuthenticationException In case the signature validation fails.
*/
- @RequestMapping(value = "remove", method = RequestMethod.POST)
+ @PostMapping("remove")
public ObjectResponse removeActivation(
@RequestHeader(value = PowerAuthSignatureHttpHeader.HEADER_NAME) String signatureHeader,
HttpServletRequest httpServletRequest)
@@ -141,10 +141,8 @@ public ObjectResponse removeActivation(
logger.debug("Signature validation failed");
throw new PowerAuthSignatureInvalidException();
}
- if (!"3.0".equals(apiAuthentication.getVersion()) && !"3.1".equals(apiAuthentication.getVersion())) {
- logger.warn("Endpoint does not support PowerAuth protocol version {}", apiAuthentication.getVersion());
- throw new PowerAuthInvalidRequestException();
- }
+ PowerAuthVersionUtil.checkUnsupportedVersion(apiAuthentication.getVersion());
+
ActivationRemoveResponse response = activationServiceV3.removeActivation(apiAuthentication);
return new ObjectResponse<>(response);
}
diff --git a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/v3/RecoveryController.java b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/RecoveryController.java
similarity index 75%
rename from powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/v3/RecoveryController.java
rename to powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/RecoveryController.java
index b4794e98..45c1258d 100644
--- a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/v3/RecoveryController.java
+++ b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/RecoveryController.java
@@ -17,23 +17,22 @@
* 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.rest.api.spring.controller.v3;
+package io.getlime.security.powerauth.rest.api.spring.controller;
import io.getlime.security.powerauth.crypto.lib.enums.PowerAuthSignatureTypes;
import io.getlime.security.powerauth.rest.api.spring.authentication.PowerAuthApiAuthentication;
import io.getlime.security.powerauth.rest.api.spring.exception.PowerAuthAuthenticationException;
import io.getlime.security.powerauth.rest.api.spring.exception.authentication.PowerAuthInvalidRequestException;
import io.getlime.security.powerauth.rest.api.spring.exception.authentication.PowerAuthSignatureInvalidException;
-import io.getlime.security.powerauth.rest.api.model.request.v3.EciesEncryptedRequest;
-import io.getlime.security.powerauth.rest.api.model.response.v3.EciesEncryptedResponse;
+import io.getlime.security.powerauth.rest.api.model.request.EciesEncryptedRequest;
+import io.getlime.security.powerauth.rest.api.model.response.EciesEncryptedResponse;
import io.getlime.security.powerauth.rest.api.spring.annotation.PowerAuth;
-import io.getlime.security.powerauth.rest.api.spring.service.v3.RecoveryService;
+import io.getlime.security.powerauth.rest.api.spring.service.RecoveryService;
+import io.getlime.security.powerauth.rest.api.spring.util.PowerAuthVersionUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
+
/**
* Controller implementing recovery related end-points from the PowerAuth
@@ -70,7 +69,7 @@ public RecoveryController(RecoveryService recoveryService) {
* @return ECIES encrypted response.
* @throws PowerAuthAuthenticationException In case confirm recovery fails.
*/
- @RequestMapping(value = "confirm", method = RequestMethod.POST)
+ @PostMapping("confirm")
@PowerAuth(resourceId = "/pa/recovery/confirm", signatureType = {
PowerAuthSignatureTypes.POSSESSION_KNOWLEDGE
})
@@ -83,14 +82,11 @@ public EciesEncryptedResponse confirmRecoveryCode(@RequestBody EciesEncryptedReq
if (authentication == null || authentication.getActivationContext().getActivationId() == null) {
throw new PowerAuthSignatureInvalidException();
}
- if (!"3.0".equals(authentication.getVersion()) && !"3.1".equals(authentication.getVersion())) {
- logger.warn("Endpoint does not support PowerAuth protocol version {}", authentication.getVersion());
- throw new PowerAuthInvalidRequestException();
- }
- if (request.getNonce() == null && !"3.0".equals(authentication.getVersion())) {
- logger.warn("Missing nonce in ECIES request data");
- throw new PowerAuthInvalidRequestException();
- }
+
+ PowerAuthVersionUtil.checkUnsupportedVersion(authentication.getVersion());
+ PowerAuthVersionUtil.checkMissingRequiredNonce(authentication.getVersion(), request.getNonce());
+ PowerAuthVersionUtil.checkMissingRequiredTimestamp(authentication.getVersion(), request.getTimestamp());
+
return recoveryService.confirmRecoveryCode(request, authentication);
}
diff --git a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/v3/SecureVaultController.java b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/SecureVaultController.java
similarity index 82%
rename from powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/v3/SecureVaultController.java
rename to powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/SecureVaultController.java
index d74593f3..875c6cc6 100644
--- a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/v3/SecureVaultController.java
+++ b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/SecureVaultController.java
@@ -17,7 +17,7 @@
* 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.rest.api.spring.controller.v3;
+package io.getlime.security.powerauth.rest.api.spring.controller;
import io.getlime.security.powerauth.http.PowerAuthSignatureHttpHeader;
import io.getlime.security.powerauth.http.validator.InvalidPowerAuthHttpHeaderException;
@@ -26,15 +26,16 @@
import io.getlime.security.powerauth.rest.api.spring.exception.PowerAuthSecureVaultException;
import io.getlime.security.powerauth.rest.api.spring.exception.authentication.PowerAuthInvalidRequestException;
import io.getlime.security.powerauth.rest.api.spring.exception.authentication.PowerAuthSignatureInvalidException;
-import io.getlime.security.powerauth.rest.api.model.request.v3.EciesEncryptedRequest;
-import io.getlime.security.powerauth.rest.api.model.response.v3.EciesEncryptedResponse;
-import io.getlime.security.powerauth.rest.api.spring.service.v3.SecureVaultService;
+import io.getlime.security.powerauth.rest.api.model.request.EciesEncryptedRequest;
+import io.getlime.security.powerauth.rest.api.model.response.EciesEncryptedResponse;
+import io.getlime.security.powerauth.rest.api.spring.service.SecureVaultService;
+import io.getlime.security.powerauth.rest.api.spring.util.PowerAuthVersionUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
-import javax.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletRequest;
/**
* Controller implementing secure vault related end-points from the
@@ -74,7 +75,7 @@ public void setSecureVaultServiceV3(SecureVaultService secureVaultServiceV3) {
* @throws PowerAuthAuthenticationException In case authentication fails.
* @throws PowerAuthSecureVaultException In case unlocking the vault fails.
*/
- @RequestMapping(value = "unlock", method = RequestMethod.POST)
+ @PostMapping("unlock")
public EciesEncryptedResponse unlockVault(
@RequestHeader(value = PowerAuthSignatureHttpHeader.HEADER_NAME, defaultValue = "unknown") String signatureHeader,
@RequestBody EciesEncryptedRequest request,
@@ -98,14 +99,10 @@ public EciesEncryptedResponse unlockVault(
throw new PowerAuthSignatureInvalidException();
}
- if (!"3.0".equals(header.getVersion()) && !"3.1".equals(header.getVersion())) {
- logger.warn("Endpoint does not support PowerAuth protocol version {}", header.getVersion());
- throw new PowerAuthInvalidRequestException();
- }
- if (request.getNonce() == null && !"3.0".equals(header.getVersion())) {
- logger.warn("Missing nonce in ECIES request data");
- throw new PowerAuthInvalidRequestException();
- }
+ PowerAuthVersionUtil.checkUnsupportedVersion(header.getVersion());
+ PowerAuthVersionUtil.checkMissingRequiredNonce(header.getVersion(), request.getNonce());
+ PowerAuthVersionUtil.checkMissingRequiredTimestamp(header.getVersion(), request.getTimestamp());
+
return secureVaultServiceV3.vaultUnlock(header, request, httpServletRequest);
}
diff --git a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/ServerStatusController.java b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/ServerStatusController.java
new file mode 100644
index 00000000..9e422a91
--- /dev/null
+++ b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/ServerStatusController.java
@@ -0,0 +1,58 @@
+/*
+ * PowerAuth integration libraries for RESTful API applications, examples 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.rest.api.spring.controller;
+
+import io.getlime.core.rest.model.base.response.ObjectResponse;
+import io.getlime.security.powerauth.rest.api.model.response.ServerStatusResponse;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.Date;
+
+/**
+ * Controller that provides a user information.
+ * PowerAuth protocol versions:
+ *
+ * - 3.0
+ * - 3.1
+ * - 3.2
+ *
+ *
+ * @author Petr Dvorak, petr@wultra.com
+ */
+@RestController
+@RequestMapping("pa/v3")
+@Slf4j
+public class ServerStatusController {
+
+ /**
+ * Obtain server status.
+ * @return Server status.
+ */
+ @PostMapping("status")
+ public ObjectResponse getServerStatus() {
+ final long serverTime = new Date().getTime();
+ final ServerStatusResponse response = new ServerStatusResponse(serverTime);
+ return new ObjectResponse<>(response);
+ }
+
+}
diff --git a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/v3/SignatureController.java b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/SignatureController.java
similarity index 89%
rename from powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/v3/SignatureController.java
rename to powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/SignatureController.java
index 242cc9d8..a30526f4 100644
--- a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/v3/SignatureController.java
+++ b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/SignatureController.java
@@ -17,13 +17,12 @@
* 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.rest.api.spring.controller.v3;
+package io.getlime.security.powerauth.rest.api.spring.controller;
import io.getlime.core.rest.model.base.response.Response;
import io.getlime.security.powerauth.crypto.lib.enums.PowerAuthSignatureTypes;
import io.getlime.security.powerauth.rest.api.spring.authentication.PowerAuthApiAuthentication;
import io.getlime.security.powerauth.rest.api.spring.exception.PowerAuthAuthenticationException;
-import io.getlime.security.powerauth.rest.api.spring.exception.authentication.PowerAuthInvalidRequestException;
import io.getlime.security.powerauth.rest.api.spring.exception.authentication.PowerAuthSignatureInvalidException;
import io.getlime.security.powerauth.rest.api.spring.annotation.PowerAuth;
import org.slf4j.Logger;
@@ -32,6 +31,8 @@
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
+import io.getlime.security.powerauth.rest.api.spring.util.PowerAuthVersionUtil;
+
/**
* End-point for validating signatures.
*
@@ -68,10 +69,9 @@ public Response validateSignature(PowerAuthApiAuthentication auth) throws PowerA
logger.debug("Signature validation failed");
throw new PowerAuthSignatureInvalidException();
}
- if (!"3.0".equals(auth.getVersion()) && !"3.1".equals(auth.getVersion())) {
- logger.warn("Endpoint does not support PowerAuth protocol version {}", auth.getVersion());
- throw new PowerAuthInvalidRequestException();
- }
+
+ PowerAuthVersionUtil.checkUnsupportedVersion(auth.getVersion());
+
return new Response();
}
diff --git a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/v3/TokenController.java b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/TokenController.java
similarity index 76%
rename from powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/v3/TokenController.java
rename to powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/TokenController.java
index 48f3d8e7..23433749 100644
--- a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/v3/TokenController.java
+++ b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/TokenController.java
@@ -17,7 +17,7 @@
* 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.rest.api.spring.controller.v3;
+package io.getlime.security.powerauth.rest.api.spring.controller;
import io.getlime.core.rest.model.base.request.ObjectRequest;
import io.getlime.core.rest.model.base.response.ObjectResponse;
@@ -26,19 +26,17 @@
import io.getlime.security.powerauth.rest.api.spring.exception.PowerAuthAuthenticationException;
import io.getlime.security.powerauth.rest.api.spring.exception.authentication.PowerAuthInvalidRequestException;
import io.getlime.security.powerauth.rest.api.spring.exception.authentication.PowerAuthSignatureInvalidException;
-import io.getlime.security.powerauth.rest.api.model.request.v3.EciesEncryptedRequest;
-import io.getlime.security.powerauth.rest.api.model.request.v3.TokenRemoveRequest;
-import io.getlime.security.powerauth.rest.api.model.response.v3.EciesEncryptedResponse;
-import io.getlime.security.powerauth.rest.api.model.response.v3.TokenRemoveResponse;
+import io.getlime.security.powerauth.rest.api.model.request.EciesEncryptedRequest;
+import io.getlime.security.powerauth.rest.api.model.request.TokenRemoveRequest;
+import io.getlime.security.powerauth.rest.api.model.response.EciesEncryptedResponse;
+import io.getlime.security.powerauth.rest.api.model.response.TokenRemoveResponse;
import io.getlime.security.powerauth.rest.api.spring.annotation.PowerAuth;
-import io.getlime.security.powerauth.rest.api.spring.service.v3.TokenService;
+import io.getlime.security.powerauth.rest.api.spring.service.TokenService;
+import io.getlime.security.powerauth.rest.api.spring.util.PowerAuthVersionUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
/**
* Controller responsible for publishing services related to simple token-based authentication.
@@ -74,7 +72,7 @@ public void setTokenServiceV3(TokenService tokenServiceV3) {
* @return ECIES encrypted create token response.
* @throws PowerAuthAuthenticationException In case authentication fails or request is invalid.
*/
- @RequestMapping(value = "create", method = RequestMethod.POST)
+ @PostMapping("create")
@PowerAuth(resourceId = "/pa/token/create", signatureType = {
PowerAuthSignatureTypes.POSSESSION,
PowerAuthSignatureTypes.POSSESSION_KNOWLEDGE,
@@ -92,14 +90,10 @@ public EciesEncryptedResponse createToken(@RequestBody EciesEncryptedRequest req
logger.debug("Signature validation failed");
throw new PowerAuthSignatureInvalidException();
}
- if (!"3.0".equals(authentication.getVersion()) && !"3.1".equals(authentication.getVersion())) {
- logger.warn("Endpoint does not support PowerAuth protocol version {}", authentication.getVersion());
- throw new PowerAuthInvalidRequestException();
- }
- if (request.getNonce() == null && !"3.0".equals(authentication.getVersion())) {
- logger.warn("Missing nonce in ECIES request data");
- throw new PowerAuthInvalidRequestException();
- }
+ PowerAuthVersionUtil.checkUnsupportedVersion(authentication.getVersion());
+ PowerAuthVersionUtil.checkMissingRequiredNonce(authentication.getVersion(), request.getNonce());
+ PowerAuthVersionUtil.checkMissingRequiredTimestamp(authentication.getVersion(), request.getTimestamp());
+
return tokenServiceV3.createToken(request, authentication);
}
@@ -110,7 +104,7 @@ public EciesEncryptedResponse createToken(@RequestBody EciesEncryptedRequest req
* @return Remove token response.
* @throws PowerAuthAuthenticationException In case authentication fails or request is invalid.
*/
- @RequestMapping(value = "remove", method = RequestMethod.POST)
+ @PostMapping("remove")
@PowerAuth(resourceId = "/pa/token/remove", signatureType = {
PowerAuthSignatureTypes.POSSESSION,
PowerAuthSignatureTypes.POSSESSION_KNOWLEDGE,
@@ -126,10 +120,9 @@ public ObjectResponse removeToken(@RequestBody ObjectReques
if (authentication == null || authentication.getActivationContext().getActivationId() == null) {
throw new PowerAuthSignatureInvalidException();
}
- if (!"3.0".equals(authentication.getVersion()) && !"3.1".equals(authentication.getVersion())) {
- logger.warn("Endpoint does not support PowerAuth protocol version {}", authentication.getVersion());
- throw new PowerAuthInvalidRequestException();
- }
+
+ PowerAuthVersionUtil.checkUnsupportedVersion(authentication.getVersion());
+
TokenRemoveResponse response = tokenServiceV3.removeToken(request.getRequestObject(), authentication);
return new ObjectResponse<>(response);
}
diff --git a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/v3/UpgradeController.java b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/UpgradeController.java
similarity index 77%
rename from powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/v3/UpgradeController.java
rename to powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/UpgradeController.java
index f0938ffd..4fb94ea8 100644
--- a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/v3/UpgradeController.java
+++ b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/UpgradeController.java
@@ -17,9 +17,10 @@
* 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.rest.api.spring.controller.v3;
+package io.getlime.security.powerauth.rest.api.spring.controller;
import io.getlime.core.rest.model.base.response.Response;
+import io.getlime.security.powerauth.crypto.lib.encryptor.model.EncryptorScope;
import io.getlime.security.powerauth.http.PowerAuthEncryptionHttpHeader;
import io.getlime.security.powerauth.http.PowerAuthSignatureHttpHeader;
import io.getlime.security.powerauth.http.validator.InvalidPowerAuthHttpHeaderException;
@@ -28,15 +29,16 @@
import io.getlime.security.powerauth.rest.api.spring.exception.PowerAuthAuthenticationException;
import io.getlime.security.powerauth.rest.api.spring.exception.PowerAuthUpgradeException;
import io.getlime.security.powerauth.rest.api.spring.exception.authentication.PowerAuthInvalidRequestException;
-import io.getlime.security.powerauth.rest.api.model.request.v3.EciesEncryptedRequest;
-import io.getlime.security.powerauth.rest.api.model.response.v3.EciesEncryptedResponse;
-import io.getlime.security.powerauth.rest.api.spring.service.v3.UpgradeService;
+import io.getlime.security.powerauth.rest.api.model.request.EciesEncryptedRequest;
+import io.getlime.security.powerauth.rest.api.model.response.EciesEncryptedResponse;
+import io.getlime.security.powerauth.rest.api.spring.service.UpgradeService;
+import io.getlime.security.powerauth.rest.api.spring.util.PowerAuthVersionUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
-import javax.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletRequest;
/**
* Controller responsible for upgrade.
@@ -73,10 +75,10 @@ public void setUpgradeService(UpgradeService upgradeService) {
* @return ECIES encrypted response.
* @throws PowerAuthUpgradeException In case upgrade fails.
*/
- @RequestMapping(value = "start", method = RequestMethod.POST)
+ @PostMapping("start")
public EciesEncryptedResponse upgradeStart(@RequestBody EciesEncryptedRequest request,
- @RequestHeader(value = PowerAuthEncryptionHttpHeader.HEADER_NAME, defaultValue = "unknown") String encryptionHeader)
- throws PowerAuthUpgradeException {
+ @RequestHeader(value = PowerAuthEncryptionHttpHeader.HEADER_NAME, defaultValue = "unknown") String encryptionHeader)
+ throws PowerAuthUpgradeException, PowerAuthInvalidRequestException {
if (request == null) {
logger.warn("Invalid request object in upgrade start");
@@ -88,22 +90,16 @@ public EciesEncryptedResponse upgradeStart(@RequestBody EciesEncryptedRequest re
// Validate the encryption header
try {
- PowerAuthEncryptionHttpHeaderValidator.validate(header);
+ PowerAuthEncryptionHttpHeaderValidator.validate(header, EncryptorScope.ACTIVATION_SCOPE);
} catch (InvalidPowerAuthHttpHeaderException ex) {
logger.warn("Encryption validation failed, error: {}", ex.getMessage());
logger.debug(ex.getMessage(), ex);
throw new PowerAuthUpgradeException();
}
- if (!"3.0".equals(header.getVersion()) && !"3.1".equals(header.getVersion())) {
- logger.warn("Endpoint does not support PowerAuth protocol version {}", header.getVersion());
- throw new PowerAuthUpgradeException();
- }
-
- if (request.getNonce() == null && !"3.0".equals(header.getVersion())) {
- logger.warn("Missing nonce in ECIES request data");
- throw new PowerAuthUpgradeException();
- }
+ PowerAuthVersionUtil.checkUnsupportedVersion(header.getVersion());
+ PowerAuthVersionUtil.checkMissingRequiredNonce(header.getVersion(), request.getNonce());
+ PowerAuthVersionUtil.checkMissingRequiredTimestamp(header.getVersion(), request.getTimestamp());
return upgradeService.upgradeStart(request, header);
@@ -118,7 +114,7 @@ public EciesEncryptedResponse upgradeStart(@RequestBody EciesEncryptedRequest re
* @throws PowerAuthAuthenticationException In case request signature is invalid.
* @throws PowerAuthUpgradeException In case commit fails.
*/
- @RequestMapping(value = "commit", method = RequestMethod.POST)
+ @PostMapping("commit")
public Response upgradeCommit(@RequestHeader(value = PowerAuthSignatureHttpHeader.HEADER_NAME) String signatureHeader,
HttpServletRequest httpServletRequest)
throws PowerAuthAuthenticationException, PowerAuthUpgradeException {
@@ -135,10 +131,7 @@ public Response upgradeCommit(@RequestHeader(value = PowerAuthSignatureHttpHeade
throw new PowerAuthUpgradeException();
}
- if (!"3.0".equals(header.getVersion()) && !"3.1".equals(header.getVersion())) {
- logger.warn("Endpoint does not support PowerAuth protocol version {}", header.getVersion());
- throw new PowerAuthInvalidRequestException();
- }
+ PowerAuthVersionUtil.checkUnsupportedVersion(header.getVersion());
return upgradeService.upgradeCommit(signatureHeader, httpServletRequest);
}
diff --git a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/UserInfoController.java b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/UserInfoController.java
new file mode 100644
index 00000000..f922524b
--- /dev/null
+++ b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/UserInfoController.java
@@ -0,0 +1,87 @@
+/*
+ * PowerAuth integration libraries for RESTful API applications, examples 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.rest.api.spring.controller;
+
+import io.getlime.security.powerauth.rest.api.model.request.UserInfoRequest;
+import io.getlime.security.powerauth.rest.api.spring.annotation.EncryptedRequestBody;
+import io.getlime.security.powerauth.rest.api.spring.annotation.PowerAuthEncryption;
+import io.getlime.security.powerauth.rest.api.spring.encryption.EncryptionContext;
+import io.getlime.security.powerauth.rest.api.spring.encryption.EncryptionScope;
+import io.getlime.security.powerauth.rest.api.spring.exception.PowerAuthEncryptionException;
+import io.getlime.security.powerauth.rest.api.spring.exception.PowerAuthUserInfoException;
+import io.getlime.security.powerauth.rest.api.spring.service.UserInfoService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.Map;
+
+/**
+ * Controller that provides a user information.
+ * PowerAuth protocol versions:
+ *
+ * - 3.0
+ * - 3.1
+ * - 3.2
+ *
+ *
+ * @author Petr Dvorak, petr@wultra.com
+ */
+@RestController
+@RequestMapping("/pa/v3/user")
+@Slf4j
+public class UserInfoController {
+
+ private final UserInfoService userInfoService;
+
+ /**
+ * Default constructor.
+ * @param userInfoService User info service.
+ */
+ @Autowired
+ public UserInfoController(UserInfoService userInfoService) {
+ this.userInfoService = userInfoService;
+ }
+
+ /**
+ * Fetch user info.
+ *
+ * @param request Request with user info service.
+ * @param encryptionContext PowerAuth ECIES encryption context.
+ * @return Encrypted user info claims.
+ * @throws PowerAuthUserInfoException In case there is an error while fetching claims.
+ * @throws PowerAuthEncryptionException In case of failed encryption.
+ */
+ @PowerAuthEncryption(scope = EncryptionScope.ACTIVATION_SCOPE)
+ @PostMapping("info")
+ public Map claims(@EncryptedRequestBody UserInfoRequest request, EncryptionContext encryptionContext) throws PowerAuthUserInfoException, PowerAuthEncryptionException {
+ if (encryptionContext == null) {
+ logger.error("Encryption failed");
+ throw new PowerAuthEncryptionException("Encryption failed");
+ }
+
+ return userInfoService.fetchUserClaimsByActivationId(
+ encryptionContext.getActivationId()
+ );
+ }
+
+}
diff --git a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/v2/ActivationController.java b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/v2/ActivationController.java
deleted file mode 100644
index 145335b0..00000000
--- a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/v2/ActivationController.java
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * PowerAuth integration libraries for RESTful API applications, examples and
- * related software components
- *
- * Copyright (C) 2018 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.rest.api.spring.controller.v2;
-
-import io.getlime.core.rest.model.base.request.ObjectRequest;
-import io.getlime.core.rest.model.base.response.ObjectResponse;
-import io.getlime.security.powerauth.http.PowerAuthSignatureHttpHeader;
-import io.getlime.security.powerauth.rest.api.spring.authentication.PowerAuthApiAuthentication;
-import io.getlime.security.powerauth.rest.api.spring.exception.PowerAuthActivationException;
-import io.getlime.security.powerauth.rest.api.spring.exception.PowerAuthAuthenticationException;
-import io.getlime.security.powerauth.rest.api.spring.exception.authentication.PowerAuthInvalidRequestException;
-import io.getlime.security.powerauth.rest.api.spring.exception.authentication.PowerAuthSignatureInvalidException;
-import io.getlime.security.powerauth.rest.api.model.request.v2.ActivationCreateRequest;
-import io.getlime.security.powerauth.rest.api.model.request.v3.ActivationStatusRequest;
-import io.getlime.security.powerauth.rest.api.model.response.v2.ActivationCreateResponse;
-import io.getlime.security.powerauth.rest.api.model.response.v3.ActivationRemoveResponse;
-import io.getlime.security.powerauth.rest.api.model.response.v3.ActivationStatusResponse;
-import io.getlime.security.powerauth.rest.api.spring.provider.PowerAuthAuthenticationProvider;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.*;
-
-/**
- * Controller implementing activation related end-points from the PowerAuth
- * Standard API.
- *
- * PowerAuth protocol versions:
- *
- *
- * @author Petr Dvorak, petr@wultra.com
- *
- */
-@RestController("activationControllerV2")
-@RequestMapping(value = "/pa/activation")
-public class ActivationController {
-
- private static final Logger logger = LoggerFactory.getLogger(ActivationController.class);
-
- private io.getlime.security.powerauth.rest.api.spring.service.v2.ActivationService activationServiceV2;
- private io.getlime.security.powerauth.rest.api.spring.service.v3.ActivationService activationServiceV3;
-
- private PowerAuthAuthenticationProvider authenticationProvider;
-
- /**
- * Set the activation service via setter injection.
- * @param activationServiceV2 Activation service (v2).
- */
- @Autowired
- public void setActivationServiceV2(io.getlime.security.powerauth.rest.api.spring.service.v2.ActivationService activationServiceV2) {
- this.activationServiceV2 = activationServiceV2;
- }
-
- /**
- * Set the activation service via setter injection.
- * @param activationServiceV3 Activation service (v3).
- */
- @Autowired
- public void setActivationServiceV3(io.getlime.security.powerauth.rest.api.spring.service.v3.ActivationService activationServiceV3) {
- this.activationServiceV3 = activationServiceV3;
- }
-
- /**
- * Set the authentication provider via setter injection.
- * @param authenticationProvider Authentication provider.
- */
- @Autowired
- public void setAuthenticationProvider(PowerAuthAuthenticationProvider authenticationProvider) {
- this.authenticationProvider = authenticationProvider;
- }
-
- /**
- * Create a new activation.
- * @param request PowerAuth RESTful request with {@link ActivationCreateRequest} payload.
- * @return PowerAuth RESTful response with {@link ActivationCreateResponse} payload.
- * @throws PowerAuthActivationException In case creating activation fails.
- */
- @RequestMapping(value = "create", method = RequestMethod.POST)
- public ObjectResponse createActivation(
- @RequestBody ObjectRequest request
- ) throws PowerAuthActivationException {
- if (request.getRequestObject() == null || request.getRequestObject().getActivationIdShort() == null) {
- logger.warn("Invalid request object in activation create");
- throw new PowerAuthActivationException();
- }
- ActivationCreateResponse response = activationServiceV2.createActivation(request.getRequestObject());
- return new ObjectResponse<>(response);
- }
-
- /**
- * Get activation status.
- * @param request PowerAuth RESTful request with {@link ActivationStatusRequest} payload.
- * @return PowerAuth RESTful response with {@link ActivationStatusResponse} payload.
- * @throws PowerAuthActivationException In case request fails.
- */
- @RequestMapping(value = "status", method = RequestMethod.POST)
- public ObjectResponse getActivationStatus(
- @RequestBody ObjectRequest request
- ) throws PowerAuthActivationException {
- if (request.getRequestObject() == null || request.getRequestObject().getActivationId() == null) {
- logger.warn("Invalid request object in activation status");
- throw new PowerAuthActivationException();
- }
- ActivationStatusResponse response = activationServiceV3.getActivationStatus(request.getRequestObject());
- return new ObjectResponse<>(response);
- }
-
- /**
- * Remove activation.
- * @param signatureHeader PowerAuth signature HTTP header.
- * @return PowerAuth RESTful response with {@link ActivationRemoveResponse} payload.
- * @throws PowerAuthActivationException In case activation access fails.
- * @throws PowerAuthAuthenticationException In case the signature validation fails.
- */
- @RequestMapping(value = "remove", method = RequestMethod.POST)
- public ObjectResponse removeActivation(
- @RequestHeader(value = PowerAuthSignatureHttpHeader.HEADER_NAME) String signatureHeader
- ) throws PowerAuthActivationException, PowerAuthAuthenticationException {
- // Request body needs to be set to null because the SDK uses null for the signature, although {} is sent as request body
- PowerAuthApiAuthentication apiAuthentication = authenticationProvider.validateRequestSignature("POST", null, "/pa/activation/remove", signatureHeader);
- if (apiAuthentication == null || apiAuthentication.getActivationContext().getActivationId() == null) {
- logger.debug("Signature validation failed");
- throw new PowerAuthSignatureInvalidException();
- }
- if (!"2.0".equals(apiAuthentication.getVersion()) && !"2.1".equals(apiAuthentication.getVersion())) {
- logger.warn("Endpoint does not support PowerAuth protocol version {}", apiAuthentication.getVersion());
- throw new PowerAuthInvalidRequestException();
- }
- ActivationRemoveResponse response = activationServiceV3.removeActivation(apiAuthentication);
- return new ObjectResponse<>(response);
- }
-
-}
diff --git a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/v2/SecureVaultController.java b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/v2/SecureVaultController.java
deleted file mode 100644
index bfd57d36..00000000
--- a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/v2/SecureVaultController.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * PowerAuth integration libraries for RESTful API applications, examples and
- * related software components
- *
- * Copyright (C) 2018 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.rest.api.spring.controller.v2;
-
-import io.getlime.core.rest.model.base.request.ObjectRequest;
-import io.getlime.core.rest.model.base.response.ObjectResponse;
-import io.getlime.security.powerauth.http.PowerAuthSignatureHttpHeader;
-import io.getlime.security.powerauth.http.validator.InvalidPowerAuthHttpHeaderException;
-import io.getlime.security.powerauth.http.validator.PowerAuthSignatureHttpHeaderValidator;
-import io.getlime.security.powerauth.rest.api.spring.exception.PowerAuthAuthenticationException;
-import io.getlime.security.powerauth.rest.api.spring.exception.PowerAuthSecureVaultException;
-import io.getlime.security.powerauth.rest.api.spring.exception.authentication.PowerAuthInvalidRequestException;
-import io.getlime.security.powerauth.rest.api.spring.exception.authentication.PowerAuthSignatureInvalidException;
-import io.getlime.security.powerauth.rest.api.model.request.v2.VaultUnlockRequest;
-import io.getlime.security.powerauth.rest.api.model.response.v2.VaultUnlockResponse;
-import io.getlime.security.powerauth.rest.api.spring.service.v2.SecureVaultService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.*;
-
-import javax.servlet.http.HttpServletRequest;
-
-/**
- * Controller implementing secure vault related end-points from the
- * PowerAuth Standard API.
- *
- * PowerAuth protocol versions:
- *
- *
- * @author Petr Dvorak, petr@wultra.com
- */
-@RestController("secureVaultControllerV2")
-@RequestMapping(value = "/pa/vault")
-public class SecureVaultController {
-
- private static final Logger logger = LoggerFactory.getLogger(SecureVaultController.class);
-
- private SecureVaultService secureVaultServiceV2;
-
- /**
- * Set the secure vault service via setter injection.
- * @param secureVaultServiceV2 Secure vault service.
- */
- @Autowired
- public void setSecureVaultServiceV2(SecureVaultService secureVaultServiceV2) {
- this.secureVaultServiceV2 = secureVaultServiceV2;
- }
-
- /**
- * Request the vault unlock key.
- * @param signatureHeader PowerAuth signature HTTP header.
- * @param request Vault unlock request data.
- * @param httpServletRequest HTTP servlet request.
- * @return PowerAuth RESTful response with {@link VaultUnlockResponse} payload.
- * @throws PowerAuthAuthenticationException In case authentication fails.
- * @throws PowerAuthSecureVaultException In case unlocking the vault fails.
- */
- @RequestMapping(value = "unlock", method = RequestMethod.POST)
- public ObjectResponse unlockVault(
- @RequestHeader(value = PowerAuthSignatureHttpHeader.HEADER_NAME, defaultValue = "unknown") String signatureHeader,
- @RequestBody(required=false) ObjectRequest request,
- HttpServletRequest httpServletRequest)
- throws PowerAuthAuthenticationException, PowerAuthSecureVaultException {
-
- // Request object is not validated - it is optional for version 2
-
- // Parse the header
- PowerAuthSignatureHttpHeader header = new PowerAuthSignatureHttpHeader().fromValue(signatureHeader);
-
- // Validate the header
- try {
- PowerAuthSignatureHttpHeaderValidator.validate(header);
- } catch (InvalidPowerAuthHttpHeaderException ex) {
- logger.warn("Signature HTTP header validation failed, error: {}", ex.getMessage());
- logger.debug(ex.getMessage(), ex);
- throw new PowerAuthSignatureInvalidException();
- }
-
- if (!"2.0".equals(header.getVersion()) && !"2.1".equals(header.getVersion())) {
- logger.warn("Endpoint does not support PowerAuth protocol version {}", header.getVersion());
- throw new PowerAuthInvalidRequestException();
- }
-
- VaultUnlockResponse response = secureVaultServiceV2.vaultUnlock(signatureHeader, request.getRequestObject(), httpServletRequest);
- return new ObjectResponse<>(response);
- }
-
-}
diff --git a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/v2/SignatureController.java b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/v2/SignatureController.java
deleted file mode 100644
index 751b6166..00000000
--- a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/v2/SignatureController.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * PowerAuth integration libraries for RESTful API applications, examples and
- * related software components
- *
- * Copyright (C) 2018 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.rest.api.spring.controller.v2;
-
-import io.getlime.core.rest.model.base.response.Response;
-import io.getlime.security.powerauth.crypto.lib.enums.PowerAuthSignatureTypes;
-import io.getlime.security.powerauth.rest.api.spring.authentication.PowerAuthApiAuthentication;
-import io.getlime.security.powerauth.rest.api.spring.exception.PowerAuthAuthenticationException;
-import io.getlime.security.powerauth.rest.api.spring.exception.authentication.PowerAuthInvalidRequestException;
-import io.getlime.security.powerauth.rest.api.spring.exception.authentication.PowerAuthSignatureInvalidException;
-import io.getlime.security.powerauth.rest.api.spring.annotation.PowerAuth;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.bind.annotation.RestController;
-
-/**
- * End-point for validating signatures.
- *
- * PowerAuth protocol versions:
- *
- *
- * @author Roman Strobl, roman.strobl@wultra.com
- *
- */
-@RestController("signatureControllerV2")
-@RequestMapping(value = "/pa/signature")
-public class SignatureController {
-
- private static final Logger logger = LoggerFactory.getLogger(SignatureController.class);
-
- /**
- * Validate signature by validating any data sent in request to this end-point.
- * @param auth Automatically injected PowerAuth authentication object.
- * @return API response with success.
- * @throws PowerAuthAuthenticationException In case any error occurs, including signature validation errors.
- */
- @RequestMapping(value = "validate", method = {RequestMethod.GET, RequestMethod.POST, RequestMethod.PUT, RequestMethod.DELETE})
- @PowerAuth(resourceId = "/pa/signature/validate", signatureType = {
- PowerAuthSignatureTypes.POSSESSION_KNOWLEDGE,
- PowerAuthSignatureTypes.POSSESSION_BIOMETRY,
- PowerAuthSignatureTypes.POSSESSION_KNOWLEDGE_BIOMETRY
- })
- public Response validateSignature(PowerAuthApiAuthentication auth) throws PowerAuthAuthenticationException {
-
- if (auth == null || auth.getActivationContext().getActivationId() == null) {
- logger.debug("Signature validation failed");
- throw new PowerAuthSignatureInvalidException();
- }
- if (!"2.0".equals(auth.getVersion()) && !"2.1".equals(auth.getVersion())) {
- logger.warn("Endpoint does not support PowerAuth protocol version {}", auth.getVersion());
- throw new PowerAuthInvalidRequestException();
- }
- return new Response();
- }
-
-}
diff --git a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/v2/TokenController.java b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/v2/TokenController.java
deleted file mode 100644
index 837be333..00000000
--- a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/v2/TokenController.java
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * PowerAuth integration libraries for RESTful API applications, examples and
- * related software components
- *
- * Copyright (C) 2018 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.rest.api.spring.controller.v2;
-
-import io.getlime.core.rest.model.base.request.ObjectRequest;
-import io.getlime.core.rest.model.base.response.ObjectResponse;
-import io.getlime.security.powerauth.crypto.lib.enums.PowerAuthSignatureTypes;
-import io.getlime.security.powerauth.rest.api.spring.authentication.PowerAuthApiAuthentication;
-import io.getlime.security.powerauth.rest.api.spring.exception.PowerAuthAuthenticationException;
-import io.getlime.security.powerauth.rest.api.spring.exception.authentication.PowerAuthInvalidRequestException;
-import io.getlime.security.powerauth.rest.api.spring.exception.authentication.PowerAuthSignatureInvalidException;
-import io.getlime.security.powerauth.rest.api.model.request.v2.TokenCreateRequest;
-import io.getlime.security.powerauth.rest.api.model.request.v3.TokenRemoveRequest;
-import io.getlime.security.powerauth.rest.api.model.response.v2.TokenCreateResponse;
-import io.getlime.security.powerauth.rest.api.model.response.v3.TokenRemoveResponse;
-import io.getlime.security.powerauth.rest.api.spring.annotation.PowerAuth;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.bind.annotation.RestController;
-
-/**
- * Controller responsible for publishing services related to simple token-based authentication.
- *
- * PowerAuth protocol versions:
- *
- *
- * @author Petr Dvorak, petr@wultra.com
- */
-@RestController("tokenControllerV2")
-@RequestMapping("/pa/token")
-public class TokenController {
-
- private static final Logger logger = LoggerFactory.getLogger(TokenController.class);
-
- private io.getlime.security.powerauth.rest.api.spring.service.v2.TokenService tokenServiceV2;
- private io.getlime.security.powerauth.rest.api.spring.service.v3.TokenService tokenServiceV3;
-
- /**
- * Set the token verification service via setter injection.
- * @param tokenServiceV2 Token verification service (v2).
- */
- @Autowired
- public void setTokenServiceV2(io.getlime.security.powerauth.rest.api.spring.service.v2.TokenService tokenServiceV2) {
- this.tokenServiceV2 = tokenServiceV2;
- }
-
- /**
- * Set the token verification service via setter injection.
- * @param tokenServiceV3 Token verification service (v3).
- */
- @Autowired
- public void setTokenServiceV3(io.getlime.security.powerauth.rest.api.spring.service.v3.TokenService tokenServiceV3) {
- this.tokenServiceV3 = tokenServiceV3;
- }
-
- /**
- * Create token.
- * @param request Create token request.
- * @param authentication PowerAuth API authentication object.
- * @return Create token response.
- * @throws PowerAuthAuthenticationException In case authentication fails or request is invalid.
- */
- @RequestMapping(value = "create", method = RequestMethod.POST)
- @PowerAuth(resourceId = "/pa/token/create", signatureType = {
- PowerAuthSignatureTypes.POSSESSION,
- PowerAuthSignatureTypes.POSSESSION_KNOWLEDGE,
- PowerAuthSignatureTypes.POSSESSION_BIOMETRY,
- PowerAuthSignatureTypes.POSSESSION_KNOWLEDGE_BIOMETRY
- })
- public ObjectResponse createToken(
- @RequestBody ObjectRequest request, PowerAuthApiAuthentication authentication) throws PowerAuthAuthenticationException {
- if (request.getRequestObject() == null) {
- logger.warn("Invalid request object in create token");
- throw new PowerAuthInvalidRequestException();
- }
- if (authentication == null || authentication.getActivationContext().getActivationId() == null) {
- logger.debug("Signature validation failed");
- throw new PowerAuthSignatureInvalidException();
- }
- if (!"2.0".equals(authentication.getVersion()) && !"2.1".equals(authentication.getVersion())) {
- logger.warn("Endpoint does not support PowerAuth protocol version {}", authentication.getVersion());
- throw new PowerAuthInvalidRequestException();
- }
- TokenCreateResponse response = tokenServiceV2.createToken(request.getRequestObject(), authentication);
- return new ObjectResponse<>(response);
- }
-
- /**
- * Remove token.
- * @param request Remove token request.
- * @param authentication PowerAuth API authentication object.
- * @return Remove token response.
- * @throws PowerAuthAuthenticationException In case authentication fails or request is invalid.
- */
- @RequestMapping(value = "remove", method = RequestMethod.POST)
- @PowerAuth(resourceId = "/pa/token/remove", signatureType = {
- PowerAuthSignatureTypes.POSSESSION,
- PowerAuthSignatureTypes.POSSESSION_KNOWLEDGE,
- PowerAuthSignatureTypes.POSSESSION_BIOMETRY,
- PowerAuthSignatureTypes.POSSESSION_KNOWLEDGE_BIOMETRY
- })
- public ObjectResponse removeToken(@RequestBody ObjectRequest request, PowerAuthApiAuthentication authentication) throws PowerAuthAuthenticationException {
- if (request.getRequestObject() == null) {
- logger.warn("Invalid request object in create token");
- throw new PowerAuthInvalidRequestException();
- }
- if (authentication == null || authentication.getActivationContext().getActivationId() == null) {
- logger.debug("Signature validation failed");
- throw new PowerAuthSignatureInvalidException();
- }
- if (!"2.0".equals(authentication.getVersion()) && !"2.1".equals(authentication.getVersion())) {
- logger.warn("Endpoint does not support PowerAuth protocol version {}", authentication.getVersion());
- throw new PowerAuthInvalidRequestException();
- }
- TokenRemoveResponse response = tokenServiceV3.removeToken(request.getRequestObject(), authentication);
- return new ObjectResponse<>(response);
- }
-
-}
diff --git a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/exception/PowerAuthExceptionHandler.java b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/exception/PowerAuthExceptionHandler.java
index c1a0967d..20635bd2 100644
--- a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/exception/PowerAuthExceptionHandler.java
+++ b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/exception/PowerAuthExceptionHandler.java
@@ -120,4 +120,16 @@ public class PowerAuthExceptionHandler {
return new ErrorResponse(ex.getDefaultCode(), ex.getDefaultError());
}
+ /**
+ * Handle {@link PowerAuthUserInfoException} exceptions.
+ * @param ex Exception instance.
+ * @return Error response.
+ */
+ @ExceptionHandler(value = PowerAuthUserInfoException.class)
+ @ResponseStatus(value = HttpStatus.BAD_REQUEST)
+ public @ResponseBody ErrorResponse handlePowerAuthUserInfoException(PowerAuthUserInfoException ex) {
+ logger.warn(ex.getMessage(), ex);
+ return new ErrorResponse(ex.getDefaultCode(), ex.getMessage());
+ }
+
}
diff --git a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/v3/ActivationService.java b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/ActivationService.java
similarity index 75%
rename from powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/v3/ActivationService.java
rename to powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/ActivationService.java
index 1c50bc0d..1d2b9cf0 100644
--- a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/v3/ActivationService.java
+++ b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/ActivationService.java
@@ -17,37 +17,39 @@
* 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.rest.api.spring.service.v3;
+package io.getlime.security.powerauth.rest.api.spring.service;
import com.wultra.security.powerauth.client.PowerAuthClient;
+import com.wultra.security.powerauth.client.model.enumeration.ActivationStatus;
import com.wultra.security.powerauth.client.model.error.PowerAuthClientException;
import com.wultra.security.powerauth.client.model.error.PowerAuthErrorRecovery;
-import com.wultra.security.powerauth.client.v3.*;
+import com.wultra.security.powerauth.client.model.request.*;
+import com.wultra.security.powerauth.client.model.response.*;
+import io.getlime.security.powerauth.rest.api.model.entity.ActivationType;
+import io.getlime.security.powerauth.rest.api.model.entity.UserInfoStage;
+import io.getlime.security.powerauth.rest.api.model.request.ActivationLayer1Request;
+import io.getlime.security.powerauth.rest.api.model.request.ActivationStatusRequest;
+import io.getlime.security.powerauth.rest.api.model.request.EciesEncryptedRequest;
+import io.getlime.security.powerauth.rest.api.model.response.ActivationLayer1Response;
+import io.getlime.security.powerauth.rest.api.model.response.ActivationRemoveResponse;
+import io.getlime.security.powerauth.rest.api.model.response.ActivationStatusResponse;
+import io.getlime.security.powerauth.rest.api.model.response.EciesEncryptedResponse;
import io.getlime.security.powerauth.rest.api.spring.application.PowerAuthApplicationConfiguration;
import io.getlime.security.powerauth.rest.api.spring.authentication.PowerAuthApiAuthentication;
-import io.getlime.security.powerauth.rest.api.spring.converter.v3.ActivationContextConverter;
-import io.getlime.security.powerauth.rest.api.spring.encryption.EciesEncryptionContext;
+import io.getlime.security.powerauth.rest.api.spring.converter.ActivationContextConverter;
+import io.getlime.security.powerauth.rest.api.spring.encryption.EncryptionContext;
import io.getlime.security.powerauth.rest.api.spring.exception.PowerAuthActivationException;
import io.getlime.security.powerauth.rest.api.spring.exception.PowerAuthRecoveryException;
import io.getlime.security.powerauth.rest.api.spring.exception.authentication.PowerAuthInvalidRequestException;
import io.getlime.security.powerauth.rest.api.spring.model.ActivationContext;
+import io.getlime.security.powerauth.rest.api.spring.model.UserInfoContext;
import io.getlime.security.powerauth.rest.api.spring.provider.CustomActivationProvider;
-import io.getlime.security.powerauth.rest.api.model.entity.ActivationType;
-import io.getlime.security.powerauth.rest.api.model.request.v3.ActivationLayer1Request;
-import io.getlime.security.powerauth.rest.api.model.request.v3.ActivationStatusRequest;
-import io.getlime.security.powerauth.rest.api.model.request.v3.EciesEncryptedRequest;
-import io.getlime.security.powerauth.rest.api.model.response.v3.ActivationLayer1Response;
-import io.getlime.security.powerauth.rest.api.model.response.v3.ActivationRemoveResponse;
-import io.getlime.security.powerauth.rest.api.model.response.v3.ActivationStatusResponse;
-import io.getlime.security.powerauth.rest.api.model.response.v3.EciesEncryptedResponse;
-import io.getlime.security.powerauth.rest.api.spring.service.HttpCustomizationService;
+import io.getlime.security.powerauth.rest.api.spring.provider.UserInfoProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
-import javax.xml.datatype.DatatypeFactory;
-import javax.xml.datatype.XMLGregorianCalendar;
import java.time.Instant;
import java.util.*;
@@ -72,6 +74,7 @@ public class ActivationService {
private PowerAuthApplicationConfiguration applicationConfiguration;
private CustomActivationProvider activationProvider;
+ private UserInfoProvider userInfoProvider;
/**
@@ -105,6 +108,15 @@ public void setPowerAuthActivationProvider(CustomActivationProvider activationPr
this.activationProvider = activationProvider;
}
+ /**
+ * Set user info provider via setter injection.
+ * @param userInfoProvider User info provider.
+ */
+ @Autowired(required = false)
+ public void setUserInfoProvider(UserInfoProvider userInfoProvider) {
+ this.userInfoProvider = userInfoProvider;
+ }
+
/**
* Create activation.
*
@@ -114,7 +126,7 @@ public void setPowerAuthActivationProvider(CustomActivationProvider activationPr
* @throws PowerAuthActivationException In case create activation fails.
* @throws PowerAuthRecoveryException In case activation recovery fails.
*/
- public ActivationLayer1Response createActivation(ActivationLayer1Request request, EciesEncryptionContext eciesContext) throws PowerAuthActivationException, PowerAuthRecoveryException {
+ public ActivationLayer1Response createActivation(ActivationLayer1Request request, EncryptionContext eciesContext) throws PowerAuthActivationException, PowerAuthRecoveryException {
try {
final String applicationKey = eciesContext.getApplicationKey();
@@ -123,18 +135,13 @@ public ActivationLayer1Response createActivation(ActivationLayer1Request request
final String encryptedData = activationData.getEncryptedData();
final String mac = activationData.getMac();
final String nonce = activationData.getNonce();
+ final Long timestamp = activationData.getTimestamp();
final Map identity = request.getIdentityAttributes();
final Map customAttributes = (request.getCustomAttributes() != null) ? request.getCustomAttributes() : new HashMap<>();
- // Validate inner encryption
- if (nonce == null && !"3.0".equals(eciesContext.getVersion())) {
- logger.warn("Missing nonce for protocol version: {}", eciesContext.getVersion());
- throw new PowerAuthActivationException();
- }
-
switch (request.getType()) {
// Regular activation which uses "code" identity attribute
- case CODE: {
+ case CODE -> {
// Check if identity attributes are present
if (identity == null || identity.isEmpty()) {
@@ -153,35 +160,49 @@ public ActivationLayer1Response createActivation(ActivationLayer1Request request
// Create context for passing parameters between activation provider calls
final Map context = new LinkedHashMap<>();
- // Decide if the recovery codes should be generated
- Boolean shouldGenerateRecoveryCodes = null;
- if (activationProvider != null) {
- shouldGenerateRecoveryCodes = activationProvider.shouldCreateRecoveryCodes(identity, customAttributes, ActivationType.CODE, context);
- }
-
// Call PrepareActivation method on PA server
final PrepareActivationRequest prepareRequest = new PrepareActivationRequest();
prepareRequest.setActivationCode(activationCode);
prepareRequest.setApplicationKey(applicationKey);
- prepareRequest.setGenerateRecoveryCodes(shouldGenerateRecoveryCodes);
+ prepareRequest.setGenerateRecoveryCodes(shouldGenerateRecoveryCodes(identity, customAttributes, context));
prepareRequest.setEphemeralPublicKey(ephemeralPublicKey);
prepareRequest.setEncryptedData(encryptedData);
prepareRequest.setMac(mac);
prepareRequest.setNonce(nonce);
+ prepareRequest.setProtocolVersion(eciesContext.getVersion());
+ prepareRequest.setTimestamp(timestamp);
final PrepareActivationResponse response = powerAuthClient.prepareActivation(
prepareRequest,
httpCustomizationService.getQueryParams(),
httpCustomizationService.getHttpHeaders()
);
+ final String userId = response.getUserId();
+ final String activationId = response.getActivationId();
+ final String applicationId = response.getApplicationId();
+
+ // Process user info
+ Map userInfo = null;
+ if (userInfoProvider != null) {
+ final UserInfoContext userInfoContext = UserInfoContext.builder()
+ .stage(UserInfoStage.ACTIVATION_PROCESS_ACTIVATION_CODE)
+ .userId(userId)
+ .activationId(activationId)
+ .applicationId(applicationId)
+ .build();
+ if (userInfoProvider.shouldReturnUserInfo(userInfoContext)) {
+ userInfo = userInfoProvider.fetchUserClaimsForUserId(userInfoContext);
+ }
+ }
+
Map processedCustomAttributes = customAttributes;
// In case a custom activation provider is enabled, process custom attributes and save any flags
if (activationProvider != null) {
- processedCustomAttributes = activationProvider.processCustomActivationAttributes(customAttributes, response.getActivationId(), response.getUserId(), response.getApplicationId(), ActivationType.CODE, context);
- List activationFlags = activationProvider.getActivationFlags(identity, processedCustomAttributes, response.getActivationId(), response.getUserId(), response.getApplicationId(), ActivationType.CODE, context);
+ processedCustomAttributes = activationProvider.processCustomActivationAttributes(customAttributes, activationId, userId, applicationId, ActivationType.CODE, context);
+ final List activationFlags = activationProvider.getActivationFlags(identity, processedCustomAttributes, activationId, userId, applicationId, ActivationType.CODE, context);
if (activationFlags != null && !activationFlags.isEmpty()) {
final AddActivationFlagsRequest flagsRequest = new AddActivationFlagsRequest();
- flagsRequest.setActivationId(response.getActivationId());
+ flagsRequest.setActivationId(activationId);
flagsRequest.getActivationFlags().addAll(activationFlags);
powerAuthClient.addActivationFlags(
flagsRequest,
@@ -197,9 +218,9 @@ public ActivationLayer1Response createActivation(ActivationLayer1Request request
notifyActivationCommit = true;
} else {
// Otherwise check if activation should be committed instantly and if yes, perform commit.
- if (activationProvider != null && activationProvider.shouldAutoCommitActivation(identity, customAttributes, response.getActivationId(), response.getUserId(), response.getApplicationId(), ActivationType.CODE, context)) {
+ if (activationProvider != null && activationProvider.shouldAutoCommitActivation(identity, customAttributes, activationId, userId, applicationId, ActivationType.CODE, context)) {
final CommitActivationRequest commitRequest = new CommitActivationRequest();
- commitRequest.setActivationId(response.getActivationId());
+ commitRequest.setActivationId(activationId);
commitRequest.setExternalUserId(null);
final CommitActivationResponse commitResponse = powerAuthClient.commitActivation(
commitRequest,
@@ -212,15 +233,17 @@ public ActivationLayer1Response createActivation(ActivationLayer1Request request
}
// Notify activation provider about an activation commit.
if (activationProvider != null && notifyActivationCommit) {
- activationProvider.activationWasCommitted(identity, customAttributes, response.getActivationId(), response.getUserId(), response.getApplicationId(), ActivationType.CODE, context);
+ activationProvider.activationWasCommitted(identity, customAttributes, activationId, userId, applicationId, ActivationType.CODE, context);
}
// Prepare and return encrypted response
- return prepareEncryptedResponse(response.getEncryptedData(), response.getMac(), processedCustomAttributes);
+ return prepareEncryptedResponse(response.getEncryptedData(), response.getMac(),
+ response.getNonce(), response.getTimestamp(), processedCustomAttributes, userInfo);
}
+
// Custom activation
- case CUSTOM: {
+ case CUSTOM -> {
// Check if there is a custom activation provider available, return an error in case it is not available.
// Only for CUSTOM activations, proceeding without an activation provider does not make a sensible use-case.
if (activationProvider == null) {
@@ -247,25 +270,22 @@ public ActivationLayer1Response createActivation(ActivationLayer1Request request
}
// Decide if the recovery codes should be generated
- final Boolean shouldGenerateRecoveryCodes = activationProvider.shouldCreateRecoveryCodes(identity, customAttributes, ActivationType.CODE, context);
+ final boolean shouldGenerateRecoveryCodes = activationProvider.shouldCreateRecoveryCodes(identity, customAttributes, ActivationType.CODE, context);
// Resolve maxFailedCount and activationExpireTimestamp parameters, null value means use value configured on PowerAuth server
final Integer maxFailed = activationProvider.getMaxFailedAttemptCount(identity, customAttributes, userId, ActivationType.CUSTOM, context);
final Long maxFailedCount = maxFailed == null ? null : maxFailed.longValue();
final Long activationValidityPeriod = activationProvider.getValidityPeriodDuringActivation(identity, customAttributes, userId, ActivationType.CUSTOM, context);
- XMLGregorianCalendar activationExpireXml = null;
+ Date activationExpire = null;
if (activationValidityPeriod != null) {
- Instant now = Instant.now();
- Instant expiration = now.plusMillis(activationValidityPeriod);
- GregorianCalendar c = new GregorianCalendar();
- c.setTimeInMillis(expiration.toEpochMilli());
- activationExpireXml = DatatypeFactory.newInstance().newXMLGregorianCalendar(c);
+ final Instant expiration = Instant.now().plusMillis(activationValidityPeriod);
+ activationExpire = Date.from(expiration);
}
// Create activation for a looked up user and application related to the given application key
final CreateActivationRequest createRequest = new CreateActivationRequest();
createRequest.setUserId(userId);
- createRequest.setTimestampActivationExpire(activationExpireXml);
+ createRequest.setTimestampActivationExpire(activationExpire);
createRequest.setGenerateRecoveryCodes(shouldGenerateRecoveryCodes);
createRequest.setMaxFailureCount(maxFailedCount);
createRequest.setApplicationKey(applicationKey);
@@ -273,20 +293,39 @@ public ActivationLayer1Response createActivation(ActivationLayer1Request request
createRequest.setEncryptedData(encryptedData);
createRequest.setMac(mac);
createRequest.setNonce(nonce);
+ createRequest.setProtocolVersion(eciesContext.getVersion());
+ createRequest.setTimestamp(timestamp);
final CreateActivationResponse response = powerAuthClient.createActivation(
createRequest,
httpCustomizationService.getQueryParams(),
httpCustomizationService.getHttpHeaders()
);
+ final String activationId = response.getActivationId();
+ final String applicationId = response.getApplicationId();
+
+ // Process user info
+ Map userInfo = null;
+ if (userInfoProvider != null) {
+ final UserInfoContext userInfoContext = UserInfoContext.builder()
+ .stage(UserInfoStage.ACTIVATION_PROCESS_CUSTOM)
+ .userId(userId)
+ .activationId(activationId)
+ .applicationId(applicationId)
+ .build();
+ if (userInfoProvider.shouldReturnUserInfo(userInfoContext)) {
+ userInfo = userInfoProvider.fetchUserClaimsForUserId(userInfoContext);
+ }
+ }
+
// Process custom attributes using a custom logic
- final Map processedCustomAttributes = activationProvider.processCustomActivationAttributes(customAttributes, response.getActivationId(), userId, response.getApplicationId(), ActivationType.CUSTOM, context);
+ final Map processedCustomAttributes = activationProvider.processCustomActivationAttributes(customAttributes, activationId, userId, applicationId, ActivationType.CUSTOM, context);
// Save activation flags in case the provider specified any flags
- final List activationFlags = activationProvider.getActivationFlags(identity, processedCustomAttributes, response.getActivationId(), userId, response.getApplicationId(), ActivationType.CUSTOM, context);
+ final List activationFlags = activationProvider.getActivationFlags(identity, processedCustomAttributes, activationId, userId, applicationId, ActivationType.CUSTOM, context);
if (activationFlags != null && !activationFlags.isEmpty()) {
final AddActivationFlagsRequest flagsRequest = new AddActivationFlagsRequest();
- flagsRequest.setActivationId(response.getActivationId());
+ flagsRequest.setActivationId(activationId);
flagsRequest.getActivationFlags().addAll(activationFlags);
powerAuthClient.addActivationFlags(
flagsRequest,
@@ -296,9 +335,9 @@ public ActivationLayer1Response createActivation(ActivationLayer1Request request
}
// Check if activation should be committed instantly and if yes, perform commit
- if (activationProvider.shouldAutoCommitActivation(identity, customAttributes, response.getActivationId(), userId, response.getApplicationId(), ActivationType.CUSTOM, context)) {
+ if (activationProvider.shouldAutoCommitActivation(identity, customAttributes, activationId, userId, applicationId, ActivationType.CUSTOM, context)) {
final CommitActivationRequest commitRequest = new CommitActivationRequest();
- commitRequest.setActivationId(response.getActivationId());
+ commitRequest.setActivationId(activationId);
commitRequest.setExternalUserId(null);
final CommitActivationResponse commitResponse = powerAuthClient.commitActivation(
commitRequest,
@@ -306,24 +345,18 @@ public ActivationLayer1Response createActivation(ActivationLayer1Request request
httpCustomizationService.getHttpHeaders()
);
if (commitResponse.isActivated()) {
- activationProvider.activationWasCommitted(identity, customAttributes, response.getActivationId(), userId, response.getApplicationId(), ActivationType.CUSTOM, context);
+ activationProvider.activationWasCommitted(identity, customAttributes, activationId, userId, applicationId, ActivationType.CUSTOM, context);
}
}
// Prepare encrypted activation data
- final EciesEncryptedResponse encryptedActivationData = new EciesEncryptedResponse(response.getEncryptedData(), response.getMac());
-
- // Prepare the created activation response data
- final ActivationLayer1Response responseL1 = new ActivationLayer1Response();
- responseL1.setCustomAttributes(processedCustomAttributes);
- responseL1.setActivationData(encryptedActivationData);
-
- // Return response
- return responseL1;
+ return prepareEncryptedResponse(response.getEncryptedData(), response.getMac(),
+ response.getNonce(), response.getTimestamp(), processedCustomAttributes, userInfo);
}
+
// Activation using recovery code
- case RECOVERY: {
+ case RECOVERY -> {
// Check if identity attributes are present
if (identity == null || identity.isEmpty()) {
@@ -368,20 +401,40 @@ public ActivationLayer1Response createActivation(ActivationLayer1Request request
recoveryRequest.setEncryptedData(encryptedData);
recoveryRequest.setMac(mac);
recoveryRequest.setNonce(nonce);
- final RecoveryCodeActivationResponse recoveryResponse = powerAuthClient.createActivationUsingRecoveryCode(
+ recoveryRequest.setProtocolVersion(eciesContext.getVersion());
+ recoveryRequest.setTimestamp(timestamp);
+ final RecoveryCodeActivationResponse response = powerAuthClient.createActivationUsingRecoveryCode(
recoveryRequest,
httpCustomizationService.getQueryParams(),
httpCustomizationService.getHttpHeaders()
);
+ final String userId = response.getUserId();
+ final String activationId = response.getActivationId();
+ final String applicationId = response.getApplicationId();
+
+ // Process user info
+ Map userInfo = null;
+ if (userInfoProvider != null) {
+ final UserInfoContext userInfoContext = UserInfoContext.builder()
+ .stage(UserInfoStage.ACTIVATION_PROCESS_RECOVERY)
+ .userId(userId)
+ .activationId(activationId)
+ .applicationId(applicationId)
+ .build();
+ if (userInfoProvider.shouldReturnUserInfo(userInfoContext)) {
+ userInfo = userInfoProvider.fetchUserClaimsForUserId(userInfoContext);
+ }
+ }
+
Map processedCustomAttributes = customAttributes;
// In case a custom activation provider is enabled, process custom attributes and save any flags
if (activationProvider != null) {
- processedCustomAttributes = activationProvider.processCustomActivationAttributes(customAttributes, recoveryResponse.getActivationId(), recoveryResponse.getUserId(), recoveryResponse.getApplicationId(), ActivationType.RECOVERY, context);
- final List activationFlags = activationProvider.getActivationFlags(identity, processedCustomAttributes, recoveryResponse.getActivationId(), recoveryResponse.getUserId(), recoveryResponse.getApplicationId(), ActivationType.RECOVERY, context);
+ processedCustomAttributes = activationProvider.processCustomActivationAttributes(customAttributes, activationId, userId, applicationId, ActivationType.RECOVERY, context);
+ final List activationFlags = activationProvider.getActivationFlags(identity, processedCustomAttributes, activationId, userId, applicationId, ActivationType.RECOVERY, context);
if (activationFlags != null && !activationFlags.isEmpty()) {
final AddActivationFlagsRequest flagsRequest = new AddActivationFlagsRequest();
- flagsRequest.setActivationId(recoveryResponse.getActivationId());
+ flagsRequest.setActivationId(activationId);
flagsRequest.getActivationFlags().addAll(activationFlags);
powerAuthClient.addActivationFlags(
flagsRequest,
@@ -392,9 +445,9 @@ public ActivationLayer1Response createActivation(ActivationLayer1Request request
}
// Automatically commit activation by default, the optional activation provider can override automatic commit
- if (activationProvider == null || activationProvider.shouldAutoCommitActivation(identity, customAttributes, recoveryResponse.getActivationId(), recoveryResponse.getUserId(), recoveryResponse.getApplicationId(), ActivationType.RECOVERY, context)) {
+ if (activationProvider == null || activationProvider.shouldAutoCommitActivation(identity, customAttributes, activationId, userId, applicationId, ActivationType.RECOVERY, context)) {
final CommitActivationRequest commitRequest = new CommitActivationRequest();
- commitRequest.setActivationId(recoveryResponse.getActivationId());
+ commitRequest.setActivationId(activationId);
commitRequest.setExternalUserId(null);
final CommitActivationResponse commitResponse = powerAuthClient.commitActivation(
commitRequest,
@@ -402,21 +455,21 @@ public ActivationLayer1Response createActivation(ActivationLayer1Request request
httpCustomizationService.getHttpHeaders()
);
if (activationProvider != null && commitResponse.isActivated()) {
- activationProvider.activationWasCommitted(identity, customAttributes, recoveryResponse.getActivationId(), recoveryResponse.getUserId(), recoveryResponse.getApplicationId(), ActivationType.RECOVERY, context);
+ activationProvider.activationWasCommitted(identity, customAttributes, activationId, userId, applicationId, ActivationType.RECOVERY, context);
}
}
// Prepare and return encrypted response
- return prepareEncryptedResponse(recoveryResponse.getEncryptedData(), recoveryResponse.getMac(), processedCustomAttributes);
+ return prepareEncryptedResponse(response.getEncryptedData(), response.getMac(),
+ response.getNonce(), response.getTimestamp(), processedCustomAttributes, userInfo);
}
-
- default:
+ default -> {
logger.warn("Invalid activation request");
throw new PowerAuthInvalidRequestException();
+ }
}
} catch (PowerAuthClientException ex) {
- if (ex.getPowerAuthError() instanceof PowerAuthErrorRecovery) {
- final PowerAuthErrorRecovery errorRecovery = (PowerAuthErrorRecovery) ex.getPowerAuthError();
+ if (ex.getPowerAuthError() instanceof final PowerAuthErrorRecovery errorRecovery) {
logger.debug("Invalid recovery code, current PUK index: {}", errorRecovery.getCurrentRecoveryPukIndex());
throw new PowerAuthRecoveryException(ex.getMessage(), "INVALID_RECOVERY_CODE", errorRecovery.getCurrentRecoveryPukIndex());
}
@@ -435,6 +488,13 @@ public ActivationLayer1Response createActivation(ActivationLayer1Request request
}
}
+ private boolean shouldGenerateRecoveryCodes(final Map identity, final Map customAttributes, final Map context) throws PowerAuthActivationException {
+ if (activationProvider == null) {
+ return true;
+ }
+ return activationProvider.shouldCreateRecoveryCodes(identity, customAttributes, ActivationType.CODE, context);
+ }
+
/**
* Get activation status.
*
@@ -529,14 +589,17 @@ public ActivationRemoveResponse removeActivation(PowerAuthApiAuthentication apiA
* @param processedCustomAttributes Custom attributes to be returned.
* @return Encrypted response object.
*/
- private ActivationLayer1Response prepareEncryptedResponse(String encryptedData, String mac, Map processedCustomAttributes) {
+ private ActivationLayer1Response prepareEncryptedResponse(String encryptedData, String mac, String nonce, Long timestmap, Map processedCustomAttributes, Map userInfo) {
// Prepare encrypted response object for layer 2
final EciesEncryptedResponse encryptedResponseL2 = new EciesEncryptedResponse();
encryptedResponseL2.setEncryptedData(encryptedData);
encryptedResponseL2.setMac(mac);
+ encryptedResponseL2.setNonce(nonce);
+ encryptedResponseL2.setTimestamp(timestmap);
// The response is encrypted once more before sent to client using ResponseBodyAdvice
final ActivationLayer1Response responseL1 = new ActivationLayer1Response();
+ responseL1.setUserInfo(userInfo);
responseL1.setCustomAttributes(processedCustomAttributes);
responseL1.setActivationData(encryptedResponseL2);
return responseL1;
diff --git a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/v3/RecoveryService.java b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/RecoveryService.java
similarity index 84%
rename from powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/v3/RecoveryService.java
rename to powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/RecoveryService.java
index 7517c094..0ea79a6f 100644
--- a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/v3/RecoveryService.java
+++ b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/RecoveryService.java
@@ -17,19 +17,18 @@
* 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.rest.api.spring.service.v3;
+package io.getlime.security.powerauth.rest.api.spring.service;
import com.wultra.security.powerauth.client.PowerAuthClient;
-import com.wultra.security.powerauth.client.v3.ConfirmRecoveryCodeRequest;
-import com.wultra.security.powerauth.client.v3.ConfirmRecoveryCodeResponse;
+import com.wultra.security.powerauth.client.model.request.ConfirmRecoveryCodeRequest;
+import com.wultra.security.powerauth.client.model.response.ConfirmRecoveryCodeResponse;
import io.getlime.security.powerauth.http.PowerAuthSignatureHttpHeader;
+import io.getlime.security.powerauth.rest.api.model.request.EciesEncryptedRequest;
+import io.getlime.security.powerauth.rest.api.model.response.EciesEncryptedResponse;
import io.getlime.security.powerauth.rest.api.spring.authentication.PowerAuthApiAuthentication;
import io.getlime.security.powerauth.rest.api.spring.exception.PowerAuthAuthenticationException;
import io.getlime.security.powerauth.rest.api.spring.exception.authentication.PowerAuthInvalidRequestException;
import io.getlime.security.powerauth.rest.api.spring.exception.authentication.PowerAuthRecoveryConfirmationException;
-import io.getlime.security.powerauth.rest.api.model.request.v3.EciesEncryptedRequest;
-import io.getlime.security.powerauth.rest.api.model.response.v3.EciesEncryptedResponse;
-import io.getlime.security.powerauth.rest.api.spring.service.HttpCustomizationService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -77,8 +76,7 @@ public EciesEncryptedResponse confirmRecoveryCode(EciesEncryptedRequest request,
final String activationId = authentication.getActivationContext().getActivationId();
final PowerAuthSignatureHttpHeader httpHeader = (PowerAuthSignatureHttpHeader) authentication.getHttpHeader();
final String applicationKey = httpHeader.getApplicationKey();
- if (activationId == null || applicationKey == null || request.getEphemeralPublicKey() == null
- || request.getEncryptedData() == null || request.getMac() == null) {
+ if (activationId == null || applicationKey == null) {
logger.warn("PowerAuth confirm recovery failed because of invalid request");
throw new PowerAuthInvalidRequestException();
}
@@ -89,6 +87,8 @@ public EciesEncryptedResponse confirmRecoveryCode(EciesEncryptedRequest request,
confirmRequest.setEncryptedData(request.getEncryptedData());
confirmRequest.setMac(request.getMac());
confirmRequest.setNonce(request.getNonce());
+ confirmRequest.setProtocolVersion(httpHeader.getVersion());
+ confirmRequest.setTimestamp(request.getTimestamp());
final ConfirmRecoveryCodeResponse paResponse = powerAuthClient.confirmRecoveryCode(
confirmRequest,
httpCustomizationService.getQueryParams(),
@@ -98,7 +98,11 @@ public EciesEncryptedResponse confirmRecoveryCode(EciesEncryptedRequest request,
logger.warn("PowerAuth confirm recovery failed because of invalid activation ID in response");
throw new PowerAuthInvalidRequestException();
}
- return new EciesEncryptedResponse(paResponse.getEncryptedData(), paResponse.getMac());
+ return new EciesEncryptedResponse(
+ paResponse.getEncryptedData(),
+ paResponse.getMac(),
+ paResponse.getNonce(),
+ paResponse.getTimestamp());
} catch (Exception ex) {
logger.warn("PowerAuth confirm recovery failed, error: {}", ex.getMessage());
logger.debug(ex.getMessage(), ex);
diff --git a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/v3/SecureVaultService.java b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/SecureVaultService.java
similarity index 80%
rename from powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/v3/SecureVaultService.java
rename to powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/SecureVaultService.java
index 5d6f8cf9..f0304ba6 100644
--- a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/v3/SecureVaultService.java
+++ b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/SecureVaultService.java
@@ -17,30 +17,29 @@
* 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.rest.api.spring.service.v3;
+package io.getlime.security.powerauth.rest.api.spring.service;
-import com.google.common.io.BaseEncoding;
import com.wultra.security.powerauth.client.PowerAuthClient;
-import com.wultra.security.powerauth.client.v3.SignatureType;
-import com.wultra.security.powerauth.client.v3.VaultUnlockRequest;
-import com.wultra.security.powerauth.client.v3.VaultUnlockResponse;
+import com.wultra.security.powerauth.client.model.enumeration.SignatureType;
+import com.wultra.security.powerauth.client.model.request.VaultUnlockRequest;
+import com.wultra.security.powerauth.client.model.response.VaultUnlockResponse;
import io.getlime.security.powerauth.http.PowerAuthHttpBody;
import io.getlime.security.powerauth.http.PowerAuthSignatureHttpHeader;
+import io.getlime.security.powerauth.rest.api.model.request.EciesEncryptedRequest;
+import io.getlime.security.powerauth.rest.api.model.response.EciesEncryptedResponse;
+import io.getlime.security.powerauth.rest.api.spring.converter.SignatureTypeConverter;
import io.getlime.security.powerauth.rest.api.spring.exception.PowerAuthAuthenticationException;
import io.getlime.security.powerauth.rest.api.spring.exception.PowerAuthSecureVaultException;
import io.getlime.security.powerauth.rest.api.spring.exception.authentication.PowerAuthSignatureInvalidException;
import io.getlime.security.powerauth.rest.api.spring.exception.authentication.PowerAuthSignatureTypeInvalidException;
-import io.getlime.security.powerauth.rest.api.model.request.v3.EciesEncryptedRequest;
-import io.getlime.security.powerauth.rest.api.model.response.v3.EciesEncryptedResponse;
-import io.getlime.security.powerauth.rest.api.spring.converter.v3.SignatureTypeConverter;
import io.getlime.security.powerauth.rest.api.spring.provider.PowerAuthAuthenticationProvider;
-import io.getlime.security.powerauth.rest.api.spring.service.HttpCustomizationService;
+import jakarta.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
-import javax.servlet.http.HttpServletRequest;
+import java.util.Base64;
/**
* Service implementing secure vault functionality.
@@ -101,15 +100,9 @@ public EciesEncryptedResponse vaultUnlock(PowerAuthSignatureHttpHeader header,
final String signatureVersion = header.getVersion();
final String nonce = header.getNonce();
- // Fetch data from the request
- final String ephemeralPublicKey = request.getEphemeralPublicKey();
- final String encryptedData = request.getEncryptedData();
- final String mac = request.getMac();
- final String eciesNonce = request.getNonce();
-
// Prepare data for signature to allow signature verification on PowerAuth server
final byte[] requestBodyBytes = authenticationProvider.extractRequestBodyBytes(httpServletRequest);
- final String data = PowerAuthHttpBody.getSignatureBaseString("POST", "/pa/vault/unlock", BaseEncoding.base64().decode(nonce), requestBodyBytes);
+ final String data = PowerAuthHttpBody.getSignatureBaseString("POST", "/pa/vault/unlock", Base64.getDecoder().decode(nonce), requestBodyBytes);
// Verify signature and get encrypted vault encryption key from PowerAuth server
final VaultUnlockRequest unlockRequest = new VaultUnlockRequest();
@@ -119,10 +112,11 @@ public EciesEncryptedResponse vaultUnlock(PowerAuthSignatureHttpHeader header,
unlockRequest.setSignatureType(signatureType);
unlockRequest.setSignatureVersion(signatureVersion);
unlockRequest.setSignedData(data);
- unlockRequest.setEphemeralPublicKey(ephemeralPublicKey);
- unlockRequest.setEncryptedData(encryptedData);
- unlockRequest.setMac(mac);
- unlockRequest.setNonce(eciesNonce);
+ unlockRequest.setEphemeralPublicKey(request.getEphemeralPublicKey());
+ unlockRequest.setEncryptedData(request.getEncryptedData());
+ unlockRequest.setMac(request.getMac());
+ unlockRequest.setNonce(request.getNonce());
+ unlockRequest.setTimestamp(request.getTimestamp());
final VaultUnlockResponse paResponse = powerAuthClient.unlockVault(
unlockRequest,
httpCustomizationService.getQueryParams(),
@@ -134,7 +128,11 @@ public EciesEncryptedResponse vaultUnlock(PowerAuthSignatureHttpHeader header,
throw new PowerAuthSignatureInvalidException();
}
- return new EciesEncryptedResponse(paResponse.getEncryptedData(), paResponse.getMac());
+ return new EciesEncryptedResponse(
+ paResponse.getEncryptedData(),
+ paResponse.getMac(),
+ paResponse.getNonce(),
+ paResponse.getTimestamp());
} catch (PowerAuthAuthenticationException ex) {
throw ex;
} catch (Exception ex) {
diff --git a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/v3/TokenService.java b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/TokenService.java
similarity index 83%
rename from powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/v3/TokenService.java
rename to powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/TokenService.java
index 05a8d8c1..56312032 100644
--- a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/v3/TokenService.java
+++ b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/TokenService.java
@@ -17,25 +17,24 @@
* 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.rest.api.spring.service.v3;
+package io.getlime.security.powerauth.rest.api.spring.service;
import com.wultra.security.powerauth.client.PowerAuthClient;
-import com.wultra.security.powerauth.client.v3.CreateTokenRequest;
-import com.wultra.security.powerauth.client.v3.CreateTokenResponse;
-import com.wultra.security.powerauth.client.v3.RemoveTokenRequest;
-import com.wultra.security.powerauth.client.v3.SignatureType;
+import com.wultra.security.powerauth.client.model.enumeration.SignatureType;
+import com.wultra.security.powerauth.client.model.request.CreateTokenRequest;
+import com.wultra.security.powerauth.client.model.request.RemoveTokenRequest;
+import com.wultra.security.powerauth.client.model.response.CreateTokenResponse;
import io.getlime.security.powerauth.crypto.lib.enums.PowerAuthSignatureTypes;
import io.getlime.security.powerauth.http.PowerAuthSignatureHttpHeader;
+import io.getlime.security.powerauth.rest.api.model.request.EciesEncryptedRequest;
+import io.getlime.security.powerauth.rest.api.model.request.TokenRemoveRequest;
+import io.getlime.security.powerauth.rest.api.model.response.EciesEncryptedResponse;
+import io.getlime.security.powerauth.rest.api.model.response.TokenRemoveResponse;
import io.getlime.security.powerauth.rest.api.spring.authentication.PowerAuthApiAuthentication;
+import io.getlime.security.powerauth.rest.api.spring.converter.SignatureTypeConverter;
import io.getlime.security.powerauth.rest.api.spring.exception.PowerAuthAuthenticationException;
import io.getlime.security.powerauth.rest.api.spring.exception.authentication.PowerAuthSignatureTypeInvalidException;
import io.getlime.security.powerauth.rest.api.spring.exception.authentication.PowerAuthTokenErrorException;
-import io.getlime.security.powerauth.rest.api.model.request.v3.EciesEncryptedRequest;
-import io.getlime.security.powerauth.rest.api.model.request.v3.TokenRemoveRequest;
-import io.getlime.security.powerauth.rest.api.model.response.v3.EciesEncryptedResponse;
-import io.getlime.security.powerauth.rest.api.model.response.v3.TokenRemoveResponse;
-import io.getlime.security.powerauth.rest.api.spring.converter.v3.SignatureTypeConverter;
-import io.getlime.security.powerauth.rest.api.spring.service.HttpCustomizationService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -85,12 +84,6 @@ public EciesEncryptedResponse createToken(EciesEncryptedRequest request,
// Fetch activation ID and signature type
final PowerAuthSignatureTypes signatureFactors = authentication.getAuthenticationContext().getSignatureType();
- // Fetch data from the request
- final String ephemeralPublicKey = request.getEphemeralPublicKey();
- final String encryptedData = request.getEncryptedData();
- final String mac = request.getMac();
- final String nonce = request.getNonce();
-
// Prepare a signature type converter
final SignatureTypeConverter converter = new SignatureTypeConverter();
final SignatureType signatureType = converter.convertFrom(signatureFactors);
@@ -108,11 +101,13 @@ public EciesEncryptedResponse createToken(EciesEncryptedRequest request,
final CreateTokenRequest tokenRequest = new CreateTokenRequest();
tokenRequest.setActivationId(activationId);
tokenRequest.setApplicationKey(applicationKey);
- tokenRequest.setEphemeralPublicKey(ephemeralPublicKey);
- tokenRequest.setEncryptedData(encryptedData);
- tokenRequest.setMac(mac);
- tokenRequest.setNonce(nonce);
+ tokenRequest.setEphemeralPublicKey(request.getEphemeralPublicKey());
+ tokenRequest.setEncryptedData(request.getEncryptedData());
+ tokenRequest.setMac(request.getMac());
+ tokenRequest.setNonce(request.getNonce());
tokenRequest.setSignatureType(signatureType);
+ tokenRequest.setProtocolVersion(httpHeader.getVersion());
+ tokenRequest.setTimestamp(request.getTimestamp());
final CreateTokenResponse token = powerAuthClient.createToken(
tokenRequest,
httpCustomizationService.getQueryParams(),
@@ -123,6 +118,8 @@ public EciesEncryptedResponse createToken(EciesEncryptedRequest request,
final EciesEncryptedResponse response = new EciesEncryptedResponse();
response.setMac(token.getMac());
response.setEncryptedData(token.getEncryptedData());
+ response.setNonce(token.getNonce());
+ response.setTimestamp(token.getTimestamp());
return response;
} catch (Exception ex) {
logger.warn("Creating PowerAuth token failed, error: {}", ex.getMessage());
diff --git a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/v3/UpgradeService.java b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/UpgradeService.java
similarity index 87%
rename from powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/v3/UpgradeService.java
rename to powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/UpgradeService.java
index 15aca850..c8bfa948 100644
--- a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/v3/UpgradeService.java
+++ b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/UpgradeService.java
@@ -17,29 +17,31 @@
* 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.rest.api.spring.service.v3;
+package io.getlime.security.powerauth.rest.api.spring.service;
import com.wultra.security.powerauth.client.PowerAuthClient;
-import com.wultra.security.powerauth.client.v3.*;
+import com.wultra.security.powerauth.client.model.request.CommitUpgradeRequest;
+import com.wultra.security.powerauth.client.model.request.StartUpgradeRequest;
+import com.wultra.security.powerauth.client.model.response.CommitUpgradeResponse;
+import com.wultra.security.powerauth.client.model.response.StartUpgradeResponse;
import io.getlime.core.rest.model.base.response.Response;
import io.getlime.security.powerauth.crypto.lib.enums.PowerAuthSignatureTypes;
import io.getlime.security.powerauth.http.PowerAuthEncryptionHttpHeader;
import io.getlime.security.powerauth.http.PowerAuthSignatureHttpHeader;
+import io.getlime.security.powerauth.rest.api.model.request.EciesEncryptedRequest;
+import io.getlime.security.powerauth.rest.api.model.response.EciesEncryptedResponse;
import io.getlime.security.powerauth.rest.api.spring.authentication.PowerAuthApiAuthentication;
import io.getlime.security.powerauth.rest.api.spring.exception.PowerAuthAuthenticationException;
import io.getlime.security.powerauth.rest.api.spring.exception.PowerAuthUpgradeException;
import io.getlime.security.powerauth.rest.api.spring.exception.authentication.PowerAuthInvalidRequestException;
import io.getlime.security.powerauth.rest.api.spring.exception.authentication.PowerAuthSignatureInvalidException;
-import io.getlime.security.powerauth.rest.api.model.request.v3.EciesEncryptedRequest;
-import io.getlime.security.powerauth.rest.api.model.response.v3.EciesEncryptedResponse;
import io.getlime.security.powerauth.rest.api.spring.provider.PowerAuthAuthenticationProvider;
-import io.getlime.security.powerauth.rest.api.spring.service.HttpCustomizationService;
+import jakarta.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
-import javax.servlet.http.HttpServletRequest;
import java.util.Collections;
import java.util.List;
@@ -87,12 +89,6 @@ public EciesEncryptedResponse upgradeStart(EciesEncryptedRequest request, PowerA
throws PowerAuthUpgradeException {
try {
- // Fetch data from the request
- final String ephemeralPublicKey = request.getEphemeralPublicKey();
- final String encryptedData = request.getEncryptedData();
- final String mac = request.getMac();
- final String nonce = request.getNonce();
-
// Get ECIES headers
final String activationId = header.getActivationId();
final String applicationKey = header.getApplicationKey();
@@ -101,10 +97,12 @@ public EciesEncryptedResponse upgradeStart(EciesEncryptedRequest request, PowerA
final StartUpgradeRequest upgradeRequest = new StartUpgradeRequest();
upgradeRequest.setActivationId(activationId);
upgradeRequest.setApplicationKey(applicationKey);
- upgradeRequest.setEphemeralPublicKey(ephemeralPublicKey);
- upgradeRequest.setEncryptedData(encryptedData);
- upgradeRequest.setMac(mac);
- upgradeRequest.setNonce(nonce);
+ upgradeRequest.setEphemeralPublicKey(request.getEphemeralPublicKey());
+ upgradeRequest.setEncryptedData(request.getEncryptedData());
+ upgradeRequest.setMac(request.getMac());
+ upgradeRequest.setNonce(request.getNonce());
+ upgradeRequest.setProtocolVersion(header.getVersion());
+ upgradeRequest.setTimestamp(request.getTimestamp());
final StartUpgradeResponse upgradeResponse = powerAuthClient.startUpgrade(
upgradeRequest,
httpCustomizationService.getQueryParams(),
@@ -115,6 +113,8 @@ public EciesEncryptedResponse upgradeStart(EciesEncryptedRequest request, PowerA
final EciesEncryptedResponse response = new EciesEncryptedResponse();
response.setMac(upgradeResponse.getMac());
response.setEncryptedData(upgradeResponse.getEncryptedData());
+ response.setNonce(upgradeResponse.getNonce());
+ response.setTimestamp(upgradeResponse.getTimestamp());
return response;
} catch (Exception ex) {
logger.warn("PowerAuth upgrade start failed, error: {}", ex.getMessage());
diff --git a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/UserInfoService.java b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/UserInfoService.java
new file mode 100644
index 00000000..126730d8
--- /dev/null
+++ b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/UserInfoService.java
@@ -0,0 +1,106 @@
+/*
+ * PowerAuth integration libraries for RESTful API applications, examples 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.rest.api.spring.service;
+
+import com.wultra.security.powerauth.client.PowerAuthClient;
+import com.wultra.security.powerauth.client.model.enumeration.ActivationStatus;
+import com.wultra.security.powerauth.client.model.error.PowerAuthClientException;
+import com.wultra.security.powerauth.client.model.response.GetActivationStatusResponse;
+import io.getlime.security.powerauth.rest.api.model.entity.UserInfoStage;
+import io.getlime.security.powerauth.rest.api.spring.exception.PowerAuthUserInfoException;
+import io.getlime.security.powerauth.rest.api.spring.model.UserInfoContext;
+import io.getlime.security.powerauth.rest.api.spring.provider.UserInfoProvider;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * Service for obtaining user info as claims map.
+ *
+ * @author Petr Dvorak, petr@wultra.com
+ */
+@Service
+public class UserInfoService {
+
+ private UserInfoProvider userInfoProvider;
+ private final PowerAuthClient powerAuthClient;
+
+ /**
+ * Service constructor.
+ * @param powerAuthClient PowerAuthClient instance.
+ */
+ @Autowired
+ public UserInfoService(PowerAuthClient powerAuthClient) {
+ this.powerAuthClient = powerAuthClient;
+ }
+
+ /**
+ * Setter with optional user info provider bean.
+ * @param userInfoProvider User info provider.
+ */
+ @Autowired(required = false)
+ public void setActivationProvider(UserInfoProvider userInfoProvider) {
+ this.userInfoProvider = userInfoProvider;
+ }
+
+ /**
+ * Fetch user info as a map of claims. Returns empty map by default, i.e., if user info provider is not registered.
+ *
+ * @param activationId Activation ID.
+ * @return Map of claims.
+ * @throws PowerAuthUserInfoException In case there is an error while fetching claims.
+ */
+ public Map fetchUserClaimsByActivationId(String activationId) throws PowerAuthUserInfoException {
+ try {
+
+ if (userInfoProvider == null) {
+ return Collections.emptyMap();
+ }
+
+ // Fetch activation details
+ final GetActivationStatusResponse activationStatusResponse = powerAuthClient.getActivationStatus(activationId);
+ final String userId = activationStatusResponse.getUserId();
+ final String applicationId = activationStatusResponse.getApplicationId();
+ final ActivationStatus activationStatus = activationStatusResponse.getActivationStatus();
+
+ if (ActivationStatus.ACTIVE != activationStatus) { // only allow active state for now
+ throw new PowerAuthUserInfoException("Invalid activation status: " + activationStatus + ", for activation: " + activationId);
+ }
+
+ final UserInfoContext userInfoContext = UserInfoContext.builder()
+ .stage(UserInfoStage.USER_INFO_ENDPOINT)
+ .userId(userId)
+ .activationId(activationId)
+ .applicationId(applicationId)
+ .build();
+ if (userInfoProvider.shouldReturnUserInfo(userInfoContext)) {
+ return userInfoProvider.fetchUserClaimsForUserId(userInfoContext);
+ } else {
+ return Collections.emptyMap();
+ }
+
+ } catch (PowerAuthClientException ex) {
+ throw new PowerAuthUserInfoException("Fetching user claims failed, activation ID: " + activationId, ex);
+ }
+ }
+
+}
diff --git a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/v2/ActivationService.java b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/v2/ActivationService.java
deleted file mode 100644
index 018da600..00000000
--- a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/v2/ActivationService.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * PowerAuth integration libraries for RESTful API applications, examples and
- * related software components
- *
- * Copyright (C) 2018 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.rest.api.spring.service.v2;
-
-import com.wultra.security.powerauth.client.PowerAuthClient;
-import com.wultra.security.powerauth.client.v2.PrepareActivationRequest;
-import com.wultra.security.powerauth.client.v2.PrepareActivationResponse;
-import io.getlime.security.powerauth.rest.api.spring.exception.PowerAuthActivationException;
-import io.getlime.security.powerauth.rest.api.model.request.v2.ActivationCreateRequest;
-import io.getlime.security.powerauth.rest.api.model.response.v2.ActivationCreateResponse;
-import io.getlime.security.powerauth.rest.api.spring.service.HttpCustomizationService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-
-/**
- * Service implementing activation functionality.
- *
- * PowerAuth protocol versions:
- *
- *
- * @author Roman Strobl, roman.strobl@wultra.com
- *
- */
-@Service("activationServiceV2")
-public class ActivationService {
-
- private static final Logger logger = LoggerFactory.getLogger(ActivationService.class);
-
- private final PowerAuthClient powerAuthClient;
- private final HttpCustomizationService httpCustomizationService;
-
- /**
- * Service constructor.
- * @param powerAuthClient PowerAuth client.
- * @param httpCustomizationService HTTP customization service.
- */
- @Autowired
- public ActivationService(PowerAuthClient powerAuthClient, HttpCustomizationService httpCustomizationService) {
- this.powerAuthClient = powerAuthClient;
- this.httpCustomizationService = httpCustomizationService;
- }
-
- /**
- * Create activation.
- * @param request Create activation request.
- * @return Create activation response.
- * @throws PowerAuthActivationException In case create activation fails.
- */
- public ActivationCreateResponse createActivation(ActivationCreateRequest request) throws PowerAuthActivationException {
- try {
- final String activationIDShort = request.getActivationIdShort();
- final String activationNonce = request.getActivationNonce();
- final String cDevicePublicKey = request.getEncryptedDevicePublicKey();
- final String activationName = request.getActivationName();
- final String extras = request.getExtras();
- final String applicationKey = request.getApplicationKey();
- final String applicationSignature = request.getApplicationSignature();
- final String clientEphemeralKey = request.getEphemeralPublicKey();
-
- final PrepareActivationRequest prepareRequest = new PrepareActivationRequest();
- prepareRequest.setActivationIdShort(activationIDShort);
- prepareRequest.setActivationName(activationName);
- prepareRequest.setActivationNonce(activationNonce);
- prepareRequest.setEphemeralPublicKey(clientEphemeralKey);
- prepareRequest.setEncryptedDevicePublicKey(cDevicePublicKey);
- prepareRequest.setExtras(extras);
- prepareRequest.setApplicationKey(applicationKey);
- prepareRequest.setApplicationSignature(applicationSignature);
- final PrepareActivationResponse paResponse = powerAuthClient.v2().prepareActivation(
- prepareRequest,
- httpCustomizationService.getQueryParams(),
- httpCustomizationService.getHttpHeaders()
- );
-
- final ActivationCreateResponse response = new ActivationCreateResponse();
- response.setActivationId(paResponse.getActivationId());
- response.setActivationNonce(paResponse.getActivationNonce());
- response.setEncryptedServerPublicKey(paResponse.getEncryptedServerPublicKey());
- response.setEncryptedServerPublicKeySignature(paResponse.getEncryptedServerPublicKeySignature());
- response.setEphemeralPublicKey(paResponse.getEphemeralPublicKey());
-
- return response;
- } catch (Exception ex) {
- logger.warn("Creating PowerAuth activation failed, error: {}", ex.getMessage());
- logger.debug(ex.getMessage(), ex);
- throw new PowerAuthActivationException();
- }
- }
-
-}
diff --git a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/v2/SecureVaultService.java b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/v2/SecureVaultService.java
deleted file mode 100644
index 67233b91..00000000
--- a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/v2/SecureVaultService.java
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- * PowerAuth integration libraries for RESTful API applications, examples and
- * related software components
- *
- * Copyright (C) 2018 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.rest.api.spring.service.v2;
-
-import com.google.common.io.BaseEncoding;
-import com.wultra.security.powerauth.client.PowerAuthClient;
-import com.wultra.security.powerauth.client.v2.SignatureType;
-import io.getlime.security.powerauth.http.PowerAuthHttpBody;
-import io.getlime.security.powerauth.http.PowerAuthSignatureHttpHeader;
-import io.getlime.security.powerauth.http.validator.InvalidPowerAuthHttpHeaderException;
-import io.getlime.security.powerauth.http.validator.PowerAuthSignatureHttpHeaderValidator;
-import io.getlime.security.powerauth.rest.api.spring.exception.PowerAuthAuthenticationException;
-import io.getlime.security.powerauth.rest.api.spring.exception.PowerAuthSecureVaultException;
-import io.getlime.security.powerauth.rest.api.spring.exception.authentication.PowerAuthSignatureInvalidException;
-import io.getlime.security.powerauth.rest.api.spring.exception.authentication.PowerAuthSignatureTypeInvalidException;
-import io.getlime.security.powerauth.rest.api.model.request.v2.VaultUnlockRequest;
-import io.getlime.security.powerauth.rest.api.model.response.v2.VaultUnlockResponse;
-import io.getlime.security.powerauth.rest.api.spring.converter.v2.SignatureTypeConverter;
-import io.getlime.security.powerauth.rest.api.spring.provider.PowerAuthAuthenticationProvider;
-import io.getlime.security.powerauth.rest.api.spring.service.HttpCustomizationService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-
-import javax.servlet.http.HttpServletRequest;
-
-/**
- * Service implementing secure vault functionality.
- *
- * PowerAuth protocol versions:
- *
- *
- * @author Roman Strobl, roman.strobl@wultra.com
- *
- */
-@Service("secureVaultServiceV2")
-public class SecureVaultService {
-
- private static final Logger logger = LoggerFactory.getLogger(SecureVaultService.class);
-
- private final PowerAuthClient powerAuthClient;
- private final PowerAuthAuthenticationProvider authenticationProvider;
- private final HttpCustomizationService httpCustomizationService;
-
- /**
- * Service constructor.
- * @param powerAuthClient PowerAuth client.
- * @param authenticationProvider Authentication provider.
- * @param httpCustomizationService HTTP customization service.
- */
- @Autowired
- public SecureVaultService(PowerAuthClient powerAuthClient, PowerAuthAuthenticationProvider authenticationProvider, HttpCustomizationService httpCustomizationService) {
- this.powerAuthClient = powerAuthClient;
- this.authenticationProvider = authenticationProvider;
- this.httpCustomizationService = httpCustomizationService;
- }
-
- /**
- * Unlock secure vault.
- * @param signatureHeader PowerAuth signature HTTP header.
- * @param request Vault unlock request.
- * @param httpServletRequest HTTP servlet request.
- * @return Vault unlock response.
- * @throws PowerAuthSecureVaultException In case vault unlock fails.
- * @throws PowerAuthAuthenticationException In case authentication fails.
- */
- public VaultUnlockResponse vaultUnlock(String signatureHeader,
- VaultUnlockRequest request,
- HttpServletRequest httpServletRequest) throws PowerAuthSecureVaultException, PowerAuthAuthenticationException {
- try {
- // Parse the header
- final PowerAuthSignatureHttpHeader header = new PowerAuthSignatureHttpHeader().fromValue(signatureHeader);
-
- // Validate the header
- try {
- PowerAuthSignatureHttpHeaderValidator.validate(header);
- } catch (InvalidPowerAuthHttpHeaderException ex) {
- logger.warn("Signature HTTP header validation failed, error: {}", ex.getMessage());
- logger.debug(ex.getMessage(), ex);
- throw new PowerAuthSignatureTypeInvalidException();
- }
-
- final SignatureTypeConverter converter = new SignatureTypeConverter();
-
- final String activationId = header.getActivationId();
- final String applicationKey = header.getApplicationKey();
- final String signature = header.getSignature();
- final SignatureType signatureType = converter.convertFrom(header.getSignatureType());
- if (signatureType == null) {
- logger.warn("Invalid signature type: {}", header.getSignatureType());
- throw new PowerAuthSignatureTypeInvalidException();
- }
- final String nonce = header.getNonce();
-
- String reason = null;
- byte[] requestBodyBytes;
-
- if ("2.0".equals(header.getVersion())) {
- // Version 2.0 requires null data in signature for vault unlock.
- requestBodyBytes = null;
- } else if ("2.1".equals(header.getVersion())) {
- // Version 2.1 or higher requires request data in signature (POST request body) for vault unlock.
- if (request != null) {
- // Send vault unlock reason, in case it is available.
- if (request.getReason() != null) {
- reason = request.getReason();
- }
- }
-
- // Use POST request body as data for signature.
- requestBodyBytes = authenticationProvider.extractRequestBodyBytes(httpServletRequest);
- } else {
- logger.warn("Invalid protocol version in secure vault: {}", header.getVersion());
- throw new PowerAuthSecureVaultException();
- }
-
- final String data = PowerAuthHttpBody.getSignatureBaseString("POST", "/pa/vault/unlock", BaseEncoding.base64().decode(nonce), requestBodyBytes);
-
- final com.wultra.security.powerauth.client.v2.VaultUnlockRequest unlockRequest = new com.wultra.security.powerauth.client.v2.VaultUnlockRequest();
- unlockRequest.setActivationId(activationId);
- unlockRequest.setApplicationKey(applicationKey);
- unlockRequest.setData(data);
- unlockRequest.setSignature(signature);
- unlockRequest.setSignatureType(signatureType);
- unlockRequest.setReason(reason);
- final com.wultra.security.powerauth.client.v2.VaultUnlockResponse paResponse = powerAuthClient.v2().unlockVault(
- unlockRequest,
- httpCustomizationService.getQueryParams(),
- httpCustomizationService.getHttpHeaders()
- );
-
- if (!paResponse.isSignatureValid()) {
- logger.debug("Signature validation failed");
- throw new PowerAuthSignatureInvalidException();
- }
-
- final VaultUnlockResponse response = new VaultUnlockResponse();
- response.setActivationId(paResponse.getActivationId());
- response.setEncryptedVaultEncryptionKey(paResponse.getEncryptedVaultEncryptionKey());
-
- return response;
- } catch (PowerAuthAuthenticationException ex) {
- throw ex;
- } catch (Exception ex) {
- logger.warn("PowerAuth vault unlock failed, error: {}", ex.getMessage());
- logger.debug(ex.getMessage(), ex);
- throw new PowerAuthSecureVaultException();
- }
- }
-
-}
diff --git a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/v2/TokenService.java b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/v2/TokenService.java
deleted file mode 100644
index 0f5c5be7..00000000
--- a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/v2/TokenService.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * PowerAuth integration libraries for RESTful API applications, examples and
- * related software components
- *
- * Copyright (C) 2018 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.rest.api.spring.service.v2;
-
-import com.wultra.security.powerauth.client.PowerAuthClient;
-import com.wultra.security.powerauth.client.v2.CreateTokenResponse;
-import com.wultra.security.powerauth.client.v2.CreateTokenRequest;
-import io.getlime.security.powerauth.crypto.lib.enums.PowerAuthSignatureTypes;
-import io.getlime.security.powerauth.rest.api.spring.authentication.PowerAuthApiAuthentication;
-import io.getlime.security.powerauth.rest.api.spring.exception.PowerAuthAuthenticationException;
-import io.getlime.security.powerauth.rest.api.spring.exception.authentication.PowerAuthTokenErrorException;
-import io.getlime.security.powerauth.rest.api.model.request.v2.TokenCreateRequest;
-import io.getlime.security.powerauth.rest.api.model.response.v2.TokenCreateResponse;
-import io.getlime.security.powerauth.rest.api.spring.converter.v2.SignatureTypeConverter;
-import io.getlime.security.powerauth.rest.api.spring.service.HttpCustomizationService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-
-/**
- * Service implementing token functionality.
- *
- * PowerAuth protocol versions:
- *
- *
- * @author Roman Strobl, roman.strobl@wultra.com
- *
- */
-@Service("tokenServiceV2")
-public class TokenService {
-
- private static final Logger logger = LoggerFactory.getLogger(TokenService.class);
-
- private final PowerAuthClient powerAuthClient;
- private final HttpCustomizationService httpCustomizationService;
-
- /**
- * Service constructor.
- * @param powerAuthClient PowerAuth client.
- * @param httpCustomizationService HTTP customization service.
- */
- @Autowired
- public TokenService(PowerAuthClient powerAuthClient, HttpCustomizationService httpCustomizationService) {
- this.powerAuthClient = powerAuthClient;
- this.httpCustomizationService = httpCustomizationService;
- }
-
- /**
- * Create token.
- * @param request Create token request.
- * @param authentication PowerAuth API authentication.
- * @return Create token response.
- * @throws PowerAuthAuthenticationException In case token could not be created.
- */
- public TokenCreateResponse createToken(TokenCreateRequest request, PowerAuthApiAuthentication authentication) throws PowerAuthAuthenticationException {
- try {
- // Fetch activation ID and signature type
- final String activationId = authentication.getActivationContext().getActivationId();
- final PowerAuthSignatureTypes signatureFactors = authentication.getAuthenticationContext().getSignatureType();
-
- // Fetch data from the request
- final String ephemeralPublicKey = request.getEphemeralPublicKey();
-
- // Prepare a signature type converter
- SignatureTypeConverter converter = new SignatureTypeConverter();
-
- // Create a token
- final CreateTokenRequest tokenRequest = new CreateTokenRequest();
- tokenRequest.setActivationId(activationId);
- tokenRequest.setEphemeralPublicKey(ephemeralPublicKey);
- tokenRequest.setSignatureType(converter.convertFrom(signatureFactors));
- final CreateTokenResponse token = powerAuthClient.v2().createToken(
- tokenRequest,
- httpCustomizationService.getQueryParams(),
- httpCustomizationService.getHttpHeaders()
- );
-
- // Prepare a response
- final TokenCreateResponse response = new TokenCreateResponse();
- response.setMac(token.getMac());
- response.setEncryptedData(token.getEncryptedData());
- return response;
- } catch (Exception ex) {
- logger.warn("Creating PowerAuth token failed, error: {}", ex.getMessage());
- logger.debug(ex.getMessage(), ex);
- throw new PowerAuthTokenErrorException();
- }
- }
-
-}
diff --git a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/util/PowerAuthVersionUtil.java b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/util/PowerAuthVersionUtil.java
new file mode 100644
index 00000000..cad9ecf7
--- /dev/null
+++ b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/util/PowerAuthVersionUtil.java
@@ -0,0 +1,149 @@
+/*
+ * PowerAuth integration libraries for RESTful API applications, examples 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.rest.api.spring.util;
+
+import io.getlime.security.powerauth.rest.api.spring.exception.authentication.PowerAuthInvalidRequestException;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.Set;
+
+/**
+ * Utility class to assist with PowerAuth version checks and related functionalities.
+ * This class provides methods to validate different aspects of the PowerAuth protocol,
+ * such as version checks, nonce verification, and timestamp checks.
+ *
+ * Note: The usage of these utility methods ensures the protocol adheres to the correct
+ * PowerAuth versions and avoids potential issues in processing requests.
+ *
+ * @author Jan Dusil, jan.dusil@wultra.com
+ */
+@Slf4j
+public final class PowerAuthVersionUtil {
+
+ /**
+ * Prevent instantiation of utility class.
+ */
+ private PowerAuthVersionUtil() {
+ throw new IllegalStateException("Utility class");
+ }
+
+ /**
+ * Set containing all the supported versions of PowerAuth.
+ */
+ private static final Set SUPPORTED_VERSIONS = Set.of("3.0", "3.1", "3.2");
+
+ /**
+ * Check if the provided version string is "3.0".
+ *
+ * @param version Version string to be checked.
+ * @return true if the version is "3.0", false otherwise.
+ */
+ private static boolean isVersion3_0(final String version) {
+ return "3.0".equals(version);
+ }
+
+ /**
+ * Check if the provided version string is "3.1".
+ *
+ * @param version Version string to be checked.
+ * @return true if the version is "3.1", false otherwise.
+ */
+ private static boolean isVersion3_1(final String version) {
+ return "3.1".equals(version);
+ }
+
+ /**
+ * Checks if the provided PowerAuth protocol version is unsupported.
+ * Throws an exception if the version is unsupported.
+ *
+ * @param version Version string to be checked.
+ * @throws PowerAuthInvalidRequestException If the provided version is unsupported.
+ */
+ public static void checkUnsupportedVersion(String version) throws PowerAuthInvalidRequestException {
+ if (isUnsupportedVersion(version)) {
+ logger.warn("Endpoint does not support PowerAuth protocol version {}", version);
+ throw new PowerAuthInvalidRequestException("Endpoint does not support PowerAuth protocol version " + version);
+ }
+ }
+
+ /**
+ * Checks if nonce is missing for the provided PowerAuth protocol version.
+ * Throws an exception if nonce is required and missing.
+ *
+ * @param version Version string to be checked.
+ * @param nonce Nonce string to be verified.
+ * @throws PowerAuthInvalidRequestException If nonce is required and missing.
+ */
+ public static void checkMissingRequiredNonce(String version, String nonce) throws PowerAuthInvalidRequestException {
+ if (isMissingRequiredNonce(version, nonce)) {
+ logger.warn("Missing nonce in ECIES request data");
+ throw new PowerAuthInvalidRequestException("Missing nonce in ECIES request data");
+ }
+ }
+
+ /**
+ * Checks if timestamp is missing for the provided PowerAuth protocol version.
+ * Throws an exception if the timestamp is required and missing.
+ *
+ * @param version Version string to be checked.
+ * @param timestamp Timestamp value to be verified.
+ * @throws PowerAuthInvalidRequestException If timestamp is required and missing.
+ */
+ public static void checkMissingRequiredTimestamp(String version, Long timestamp) throws PowerAuthInvalidRequestException {
+ if (isMissingRequiredTimestamp(version, timestamp)) {
+ logger.warn("Missing timestamp in ECIES request data for version {}", version);
+ throw new PowerAuthInvalidRequestException("Missing timestamp in ECIES request data for version " + version);
+ }
+ }
+
+ /**
+ * Checks if the provided PowerAuth protocol version is unsupported.
+ *
+ * @param version Version string to be checked.
+ * @return true if the version is unsupported, false otherwise.
+ */
+ private static boolean isUnsupportedVersion(String version) {
+ return !SUPPORTED_VERSIONS.contains(version);
+ }
+
+ /**
+ * Checks if nonce is missing for the provided PowerAuth protocol version.
+ *
+ * @param version Version string to be checked.
+ * @param nonce Nonce string to be verified.
+ * @return true if nonce is required and missing, false otherwise.
+ */
+ private static boolean isMissingRequiredNonce(String version, String nonce) {
+ return nonce == null && !isVersion3_0(version);
+ }
+
+ /**
+ * Checks if timestamp is missing for the provided PowerAuth protocol version.
+ *
+ * @param version Version string to be checked.
+ * @param timestamp Timestamp value to be verified.
+ * @return true if timestamp is required and missing, false otherwise.
+ */
+ private static boolean isMissingRequiredTimestamp(String version, Long timestamp) {
+ return timestamp == null &&
+ !isVersion3_0(version) &&
+ !isVersion3_1(version);
+ }
+}
diff --git a/powerauth-restful-security-spring/src/test/java/io/getlime/security/powerauth/rest/api/spring/PowerAuthVersionUtilTest.java b/powerauth-restful-security-spring/src/test/java/io/getlime/security/powerauth/rest/api/spring/PowerAuthVersionUtilTest.java
new file mode 100644
index 00000000..6d15f81a
--- /dev/null
+++ b/powerauth-restful-security-spring/src/test/java/io/getlime/security/powerauth/rest/api/spring/PowerAuthVersionUtilTest.java
@@ -0,0 +1,65 @@
+/*
+ * PowerAuth integration libraries for RESTful API applications, examples 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.rest.api.spring;
+
+import io.getlime.security.powerauth.rest.api.spring.exception.authentication.PowerAuthInvalidRequestException;
+import io.getlime.security.powerauth.rest.api.spring.util.PowerAuthVersionUtil;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+/**
+ * This class provides tests for the {@link PowerAuthVersionUtil} utility class,
+ * ensuring that the PowerAuth version checks and related functionalities work as expected.
+ *
+ * @author Jan Dusil, jan.dusil@wultra.com
+ */
+class PowerAuthVersionUtilTest {
+
+ /**
+ * Tests the behavior of checking unsupported PowerAuth versions.
+ */
+ @Test
+ void testUnsupportedVersion() {
+ assertThrows(PowerAuthInvalidRequestException.class, () -> PowerAuthVersionUtil.checkUnsupportedVersion("4.0"));
+ assertDoesNotThrow(() -> PowerAuthVersionUtil.checkUnsupportedVersion("3.1"));
+ }
+
+ /**
+ * Tests the behavior of checking for missing required nonces based on the PowerAuth version.
+ */
+ @Test
+ void testMissingRequiredNonce() {
+ assertThrows(PowerAuthInvalidRequestException.class, () -> PowerAuthVersionUtil.checkMissingRequiredNonce("3.1", null));
+ assertDoesNotThrow(() -> PowerAuthVersionUtil.checkMissingRequiredNonce("3.0", null));
+ assertDoesNotThrow(() -> PowerAuthVersionUtil.checkMissingRequiredNonce("3.1", "testNonce"));
+ }
+
+ /**
+ * Tests the behavior of checking for missing required timestamps based on the PowerAuth version.
+ */
+ @Test
+ void testMissingRequiredTimestamp() {
+ assertThrows(PowerAuthInvalidRequestException.class, () -> PowerAuthVersionUtil.checkMissingRequiredTimestamp("3.2", null));
+ assertDoesNotThrow(() -> PowerAuthVersionUtil.checkMissingRequiredTimestamp("3.1", null));
+ assertDoesNotThrow(() -> PowerAuthVersionUtil.checkMissingRequiredTimestamp("3.2", 1630234567890L));
+ }
+}
\ No newline at end of file