diff --git a/src/main/java/de/ozgcloud/nachrichten/antragraum/AntragraumService.java b/src/main/java/de/ozgcloud/nachrichten/antragraum/AntragraumService.java
index 0bbdf0ab26d16ad9f9c14f164f63f241995963f1..ee758e35fe044f497d04d2781b3fc10375575c7b 100644
--- a/src/main/java/de/ozgcloud/nachrichten/antragraum/AntragraumService.java
+++ b/src/main/java/de/ozgcloud/nachrichten/antragraum/AntragraumService.java
@@ -2,18 +2,27 @@ package de.ozgcloud.nachrichten.antragraum;
 
 import static java.util.Objects.*;
 
+import java.util.stream.Stream;
+
 import jakarta.annotation.PostConstruct;
 
+import org.apache.commons.collections.CollectionUtils;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 import org.springframework.stereotype.Service;
 
 import de.ozgcloud.nachrichten.NachrichtenManagerProperties;
+import de.ozgcloud.nachrichten.postfach.PersistPostfachNachrichtService;
+import de.ozgcloud.nachrichten.postfach.PostfachNachricht;
 import lombok.RequiredArgsConstructor;
 
 @Service
 @RequiredArgsConstructor
 @ConditionalOnProperty(AntragraumProperties.PROPERTY_ANTRAGSRAUM_URL)
 public class AntragraumService {
+	private final PersistPostfachNachrichtService postfachNachrichtService;
+	private final Saml2Verifier verifier;
+	private final Saml2Parser parser;
+	private final Saml2Decrypter decrypter;
 
 	static final String USER_NOTIFICATION_TEMPLATE = """
 			Guten Tag,
@@ -44,4 +53,18 @@ public class AntragraumService {
 		return USER_NOTIFICATION_TEMPLATE.formatted(getAntragsraumUrl());
 	}
 
+	public Stream<PostfachNachricht> findRueckfragen(String samlToken) {
+		verifyToken(samlToken);
+		var postfachId = decrypter.decryptPostfachId(parser.parse(samlToken));
+
+		return postfachNachrichtService.findRueckfragen(postfachId);
+	}
+
+	void verifyToken(String token) {
+		var errors = verifier.verify(token);
+		if (CollectionUtils.isNotEmpty(errors)) {
+			throw new SecurityException("SAML Token verification failed. Errors: %s".formatted(errors));
+		}
+	}
+
 }
diff --git a/src/main/java/de/ozgcloud/nachrichten/antragraum/AntragsraumGrpcService.java b/src/main/java/de/ozgcloud/nachrichten/antragraum/AntragsraumGrpcService.java
index 013b0f7e1ff09d930d2a03cac5426d3f9b0b6080..50bf5a243a16929fd93b3f54fbfac837943c0276 100644
--- a/src/main/java/de/ozgcloud/nachrichten/antragraum/AntragsraumGrpcService.java
+++ b/src/main/java/de/ozgcloud/nachrichten/antragraum/AntragsraumGrpcService.java
@@ -20,29 +20,22 @@
 
 package de.ozgcloud.nachrichten.antragraum;
 
-import de.ozgcloud.nachrichten.postfach.PersistPostfachNachrichtService;
+import org.apache.commons.lang3.NotImplementedException;
+
 import de.ozgcloud.vorgang.grpc.command.GrpcCommand;
 import io.grpc.stub.StreamObserver;
 import lombok.RequiredArgsConstructor;
 import net.devh.boot.grpc.server.service.GrpcService;
-import org.apache.commons.collections.CollectionUtils;
-import org.apache.commons.lang3.NotImplementedException;
 
 @GrpcService
 @RequiredArgsConstructor
 public class AntragsraumGrpcService extends AntragraumServiceGrpc.AntragraumServiceImplBase {
-	private final PersistPostfachNachrichtService postfachNachrichtService;
+	private final AntragraumService antragraumService;
 	private final AntragraumNachrichtMapper mapper;
-	private final Saml2Verifier verifier;
-	private final Saml2Parser parser;
-	private final Saml2Decrypter decrypter;
 
 	@Override
 	public void findRueckfragen(GrpcFindRueckfragenRequest request, StreamObserver<GrpcFindRueckfragenResponse> streamObserver) {
-		verifyToken(request.getSamlToken());
-		String postfachId = decrypter.decryptPostfachId(parser.parse(request.getSamlToken()));
-
-		var rueckfragen = postfachNachrichtService.findRueckfragen(postfachId).map(mapper::toGrpc).toList();
+		var rueckfragen = antragraumService.findRueckfragen(request.getSamlToken()).map(mapper::toGrpc).toList();
 		var response = GrpcFindRueckfragenResponse.newBuilder().addAllRueckfrage(rueckfragen).build();
 
 		streamObserver.onNext(response);
@@ -51,15 +44,7 @@ public class AntragsraumGrpcService extends AntragraumServiceGrpc.AntragraumServ
 
 	@Override
 	public void sendRueckfrageAnswer(GrpcSendRueckfrageAnswerRequest request, StreamObserver<GrpcCommand> streamObserver) {
-		verifyToken(request.getSamlToken());
-
 		throw new NotImplementedException("sendRueckfrageAnswer not implemented yet");
 	}
 
-	void verifyToken(String token) {
-		var errors = verifier.verify(token);
-		if (CollectionUtils.isNotEmpty(errors)) {
-			throw new SecurityException("SAML Token verification failed. Errors: %s".formatted(errors));
-		}
-	}
 }
diff --git a/src/main/java/de/ozgcloud/nachrichten/antragraum/BayernIdProperties.java b/src/main/java/de/ozgcloud/nachrichten/antragraum/BayernIdProperties.java
new file mode 100644
index 0000000000000000000000000000000000000000..fca321c52f30d9075856970aec0b5d747da366a4
--- /dev/null
+++ b/src/main/java/de/ozgcloud/nachrichten/antragraum/BayernIdProperties.java
@@ -0,0 +1,42 @@
+package de.ozgcloud.nachrichten.antragraum;
+
+import jakarta.validation.constraints.NotEmpty;
+
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.io.Resource;
+import org.springframework.validation.annotation.Validated;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@Configuration
+@ConditionalOnProperty(AntragraumProperties.PROPERTY_ANTRAGSRAUM_URL)
+@ConfigurationProperties(prefix = "ozgcloud.bayernid.saml")
+@Validated
+public class BayernIdProperties {
+	/**
+	 * The entityId as defined the BayernId SAML Metadata
+	 */
+	@NotEmpty
+	private String entityId;
+	/**
+	 * The uri where to load the idp Metadata from
+	 */
+	@NotEmpty
+	private Resource metadataUri;
+	/**
+	 * The location of the private key for decrypting the saml token data
+	 */
+	@NotEmpty
+	private Resource decryptionPrivateKey;
+	/**
+	 * The location of the cretificate for decrypting the saml token data
+	 */
+	@NotEmpty
+	private Resource decryptionCertificate;
+
+}
diff --git a/src/main/java/de/ozgcloud/nachrichten/antragraum/BayernIdSamlConfiguration.java b/src/main/java/de/ozgcloud/nachrichten/antragraum/BayernIdSamlConfiguration.java
index db27510dcee7d9c24325edca059dbc9ead771e03..10382dfbca35e94c38deb9bfda617ded5d427515 100644
--- a/src/main/java/de/ozgcloud/nachrichten/antragraum/BayernIdSamlConfiguration.java
+++ b/src/main/java/de/ozgcloud/nachrichten/antragraum/BayernIdSamlConfiguration.java
@@ -20,23 +20,32 @@
 
 package de.ozgcloud.nachrichten.antragraum;
 
+import static de.ozgcloud.nachrichten.antragraum.Saml2Parser.*;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.security.interfaces.RSAPrivateKey;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
 import jakarta.annotation.PostConstruct;
-import lombok.extern.log4j.Log4j2;
-import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
-import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
-import net.shibboleth.utilities.java.support.xml.XMLParserException;
+
 import org.opensaml.core.config.ConfigurationService;
 import org.opensaml.core.config.InitializationService;
 import org.opensaml.core.criterion.EntityIdCriterion;
 import org.opensaml.core.xml.XMLObject;
 import org.opensaml.core.xml.config.XMLObjectProviderRegistry;
-import org.opensaml.core.xml.io.Unmarshaller;
 import org.opensaml.saml.common.xml.SAMLConstants;
 import org.opensaml.saml.criterion.ProtocolCriterion;
 import org.opensaml.saml.metadata.criteria.role.impl.EvaluableProtocolRoleDescriptorCriterion;
 import org.opensaml.saml.saml2.metadata.EntitiesDescriptor;
 import org.opensaml.saml.saml2.metadata.EntityDescriptor;
-import org.opensaml.saml.saml2.metadata.IDPSSODescriptor;
 import org.opensaml.saml.saml2.metadata.KeyDescriptor;
 import org.opensaml.security.credential.Credential;
 import org.opensaml.security.credential.CredentialResolver;
@@ -50,180 +59,150 @@ import org.opensaml.xmlsec.config.impl.DefaultSecurityConfigurationBootstrap;
 import org.opensaml.xmlsec.keyinfo.KeyInfoSupport;
 import org.opensaml.xmlsec.signature.support.SignatureTrustEngine;
 import org.opensaml.xmlsec.signature.support.impl.ExplicitKeySignatureTrustEngine;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.core.io.Resource;
 import org.springframework.security.converter.RsaKeyConverters;
 import org.springframework.security.saml2.Saml2Exception;
 import org.springframework.security.saml2.core.Saml2X509Credential;
 import org.springframework.util.Assert;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateFactory;
-import java.security.cert.X509Certificate;
-import java.security.interfaces.RSAPrivateKey;
-import java.util.*;
 
-import static de.ozgcloud.nachrichten.antragraum.Saml2Parser.*;
+import lombok.RequiredArgsConstructor;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+import net.shibboleth.utilities.java.support.xml.XMLParserException;
 
+@RequiredArgsConstructor
 @Configuration
-@Log4j2
 public class BayernIdSamlConfiguration {
-    @Value("${ozgcloud.bayernid.saml.entity-id}")
-    private String entityId;
-    @Value("${ozgcloud.bayernid.saml.metadata-uri}")
-    private Resource metadataUri;
-    @Value("${ozgcloud.bayernid.saml.decryption.private-key-location}")
-    private Resource decryptionPrivateKey;
-    @Value("${ozgcloud.bayernid.saml.decryption.certificate-location}")
-    private Resource decryptionCertificate;
-    private XMLObjectProviderRegistry registry;
-
-    @PostConstruct
-    void initOpenSAML() {
-        try {
-            registry = new XMLObjectProviderRegistry();
-            ConfigurationService.register(XMLObjectProviderRegistry.class, registry);
-
-            registry.setParserPool(getParserPool());
-            InitializationService.initialize();
-        } catch (Exception e) {
-            throw new RuntimeException("Initialization failed");
-        }
-    }
-
-    @Bean
-    Saml2Decrypter decrypter() {
-        return new Saml2Decrypter(asDecryptionCredential());
-    }
-
-    @Bean
-    Saml2Parser parser() {
-        return new Saml2Parser();
-    }
-
-    @Bean
-    Saml2Verifier verifier() {
-        return new Saml2Verifier(parser(), trustEngine(), verificationCriteria());
-    }
-
-    SignatureTrustEngine trustEngine() {
-        Set<Credential> credentials = new HashSet<>();
-        Collection<Saml2X509Credential> keys = getCertificatesFromMetadata();
-
-        for (Saml2X509Credential key : keys) {
-            BasicX509Credential cred = new BasicX509Credential(key.getCertificate());
-            cred.setUsageType(UsageType.SIGNING);
-            cred.setEntityId(entityId);
-            credentials.add(cred);
-        }
-
-        CredentialResolver credentialsResolver = new CollectionCredentialResolver(credentials);
-        return new ExplicitKeySignatureTrustEngine(credentialsResolver, DefaultSecurityConfigurationBootstrap.buildBasicInlineKeyInfoCredentialResolver());
-    }
-
-    CriteriaSet verificationCriteria() {
-        CriteriaSet criteria = new CriteriaSet();
-        criteria.add(new EvaluableEntityIDCredentialCriterion(new EntityIdCriterion(entityId)));
-        criteria.add(new EvaluableProtocolRoleDescriptorCriterion(new ProtocolCriterion("urn:oasis:names:tc:SAML:2.0:protocol")));
-        criteria.add(new EvaluableUsageCredentialCriterion(new UsageCriterion(UsageType.SIGNING)));
-        return criteria;
-    }
-
-    private Saml2X509Credential asDecryptionCredential() {
-        RSAPrivateKey privateKey = readPrivateKey(decryptionPrivateKey);
-        X509Certificate certificate = readCertificateFromResource(decryptionCertificate);
-        return new Saml2X509Credential(privateKey, certificate, Saml2X509Credential.Saml2X509CredentialType.DECRYPTION);
-    }
-
-    private RSAPrivateKey readPrivateKey(Resource location) {
-        Assert.state(location != null, "No private key location specified");
-        Assert.state(location.exists(), () -> "Private key location '" + location + "' does not exist");
-
-        try (InputStream inputStream = location.getInputStream()) {
-            return RsaKeyConverters.pkcs8().convert(inputStream);
-        } catch (IOException e) {
-            throw new IllegalArgumentException(e);
-        }
-    }
-
-    private X509Certificate readCertificateFromResource(Resource location) {
-        Assert.state(location != null, "No certificate location specified");
-        Assert.state(location.exists(), () -> "Certificate  location '" + location + "' does not exist");
-
-        try (InputStream inputStream = location.getInputStream()) {
-
-            return (X509Certificate) CertificateFactory.getInstance("X.509").generateCertificate(inputStream);
-        } catch (IOException | CertificateException e) {
-            throw new IllegalArgumentException(e);
-        }
-    }
-
-    List<Saml2X509Credential> getCertificatesFromMetadata() {
-        try (InputStream metadata = metadataUri.getInputStream()) {
-            XMLObject xmlObject = xmlObject(metadata);
-            if (xmlObject instanceof EntitiesDescriptor descriptors) {
-                for (EntityDescriptor descriptor : descriptors.getEntityDescriptors()) {
-                    if (descriptor.getIDPSSODescriptor(SAMLConstants.SAML20P_NS) != null) {
-                        return getVerificationCertificates(descriptor);
-                    }
-                }
-            }
-        } catch (IOException e) {
-            throw new Saml2Exception("Error reading idp metadata.", e);
-        } catch (ComponentInitializationException | XMLParserException e) {
-            throw new Saml2Exception("Error initializing parser pool.", e);
-        }
-
-        throw new Saml2Exception("No IDPSSO Descriptors found");
-    }
-
-    private XMLObject xmlObject(InputStream inputStream) throws ComponentInitializationException, XMLParserException {
-        Document document = getParserPool().parse(inputStream);
-        Element element = document.getDocumentElement();
-        Unmarshaller unmarshaller = this.registry.getUnmarshallerFactory().getUnmarshaller(element);
-        if (unmarshaller == null) {
-            throw new Saml2Exception("Unsupported element of type " + element.getTagName());
-        }
-        try {
-            return unmarshaller.unmarshall(element);
-        } catch (Exception ex) {
-            throw new Saml2Exception(ex);
-        }
-    }
-
-    List<Saml2X509Credential> getVerificationCertificates(EntityDescriptor descriptor) {
-        IDPSSODescriptor idpssoDescriptor = descriptor.getIDPSSODescriptor(SAMLConstants.SAML20P_NS);
-        if (idpssoDescriptor == null) {
-            throw new Saml2Exception("Metadata response is missing the necessary IDPSSODescriptor element");
-        }
-        List<Saml2X509Credential> verification = new ArrayList<>();
-        for (KeyDescriptor keyDescriptor : idpssoDescriptor.getKeyDescriptors()) {
-            if (keyDescriptor.getUse().equals(UsageType.SIGNING)) {
-                List<X509Certificate> certificates = certificates(keyDescriptor);
-                for (X509Certificate certificate : certificates) {
-                    verification.add(Saml2X509Credential.verification(certificate));
-                }
-            }
-        }
-        if (verification.isEmpty()) {
-            throw new Saml2Exception(
-                    "Metadata response is missing verification certificates, necessary for verifying SAML assertions");
-        }
-
-        return verification;
-    }
-
-    private List<X509Certificate> certificates(KeyDescriptor keyDescriptor) {
-        try {
-            return KeyInfoSupport.getCertificates(keyDescriptor.getKeyInfo());
-        } catch (CertificateException ex) {
-            throw new Saml2Exception(ex);
-        }
-    }
+	private final BayernIdProperties bayernIdProperties;
+	private XMLObjectProviderRegistry registry;
+
+	@PostConstruct
+	void initOpenSAML() {
+		try {
+			registry = new XMLObjectProviderRegistry();
+			ConfigurationService.register(XMLObjectProviderRegistry.class, registry);
+
+			registry.setParserPool(getParserPool());
+			InitializationService.initialize();
+		} catch (Exception e) {
+			throw new RuntimeException("Initialization failed");
+		}
+	}
+
+	SignatureTrustEngine getTrustEngine() {
+		Set<Credential> credentials = new HashSet<>();
+		Collection<Saml2X509Credential> keys = getCertificatesFromMetadata();
+
+		for (Saml2X509Credential key : keys) {
+			var cred = new BasicX509Credential(key.getCertificate());
+			cred.setUsageType(UsageType.SIGNING);
+			cred.setEntityId(bayernIdProperties.getEntityId());
+			credentials.add(cred);
+		}
+
+		CredentialResolver credentialsResolver = new CollectionCredentialResolver(credentials);
+		return new ExplicitKeySignatureTrustEngine(credentialsResolver,
+				DefaultSecurityConfigurationBootstrap.buildBasicInlineKeyInfoCredentialResolver());
+	}
+
+	CriteriaSet getVerificationCriteria() {
+		var criteria = new CriteriaSet();
+		criteria.add(new EvaluableEntityIDCredentialCriterion(new EntityIdCriterion(bayernIdProperties.getEntityId())));
+		criteria.add(new EvaluableProtocolRoleDescriptorCriterion(new ProtocolCriterion("urn:oasis:names:tc:SAML:2.0:protocol")));
+		criteria.add(new EvaluableUsageCredentialCriterion(new UsageCriterion(UsageType.SIGNING)));
+		return criteria;
+	}
+
+	Saml2X509Credential getDecryptionCredential() {
+		var privateKey = readPrivateKey(bayernIdProperties.getDecryptionPrivateKey());
+		var certificate = readCertificateFromResource(bayernIdProperties.getDecryptionCertificate());
+		return new Saml2X509Credential(privateKey, certificate, Saml2X509Credential.Saml2X509CredentialType.DECRYPTION);
+	}
+
+	private RSAPrivateKey readPrivateKey(Resource location) {
+		Assert.state(location != null, "No private key location specified");
+		Assert.state(location.exists(), () -> "Private key location '" + location + "' does not exist");
+
+		try (var inputStream = location.getInputStream()) {
+			return RsaKeyConverters.pkcs8().convert(inputStream);
+		} catch (IOException e) {
+			throw new IllegalArgumentException(e);
+		}
+	}
+
+	private X509Certificate readCertificateFromResource(Resource location) {
+		Assert.state(location != null, "No certificate location specified");
+		Assert.state(location.exists(), () -> "Certificate  location '" + location + "' does not exist");
+
+		try (var inputStream = location.getInputStream()) {
+
+			return (X509Certificate) CertificateFactory.getInstance("X.509").generateCertificate(inputStream);
+		} catch (IOException | CertificateException e) {
+			throw new IllegalArgumentException(e);
+		}
+	}
+
+	List<Saml2X509Credential> getCertificatesFromMetadata() {
+		try (var metadata = bayernIdProperties.getMetadataUri().getInputStream()) {
+			var xmlObject = xmlObject(metadata);
+			if (xmlObject instanceof EntitiesDescriptor descriptors) {
+				for (EntityDescriptor descriptor : descriptors.getEntityDescriptors()) {
+					if (descriptor.getIDPSSODescriptor(SAMLConstants.SAML20P_NS) != null) {
+						return getVerificationCertificates(descriptor);
+					}
+				}
+			}
+		} catch (IOException e) {
+			throw new Saml2Exception("Error reading idp metadata.", e);
+		} catch (ComponentInitializationException | XMLParserException e) {
+			throw new Saml2Exception("Error initializing parser pool.", e);
+		}
+
+		throw new Saml2Exception("No IDPSSO Descriptors found");
+	}
+
+	private XMLObject xmlObject(InputStream inputStream) throws ComponentInitializationException, XMLParserException {
+		var document = getParserPool().parse(inputStream);
+		var element = document.getDocumentElement();
+		var unmarshaller = this.registry.getUnmarshallerFactory().getUnmarshaller(element);
+		if (unmarshaller == null) {
+			throw new Saml2Exception("Unsupported element of type " + element.getTagName());
+		}
+		try {
+			return unmarshaller.unmarshall(element);
+		} catch (Exception ex) {
+			throw new Saml2Exception(ex);
+		}
+	}
+
+	List<Saml2X509Credential> getVerificationCertificates(EntityDescriptor descriptor) {
+		var idpssoDescriptor = descriptor.getIDPSSODescriptor(SAMLConstants.SAML20P_NS);
+		if (idpssoDescriptor == null) {
+			throw new Saml2Exception("Metadata response is missing the necessary IDPSSODescriptor element");
+		}
+		List<Saml2X509Credential> verification = new ArrayList<>();
+		for (KeyDescriptor keyDescriptor : idpssoDescriptor.getKeyDescriptors()) {
+			if (keyDescriptor.getUse().equals(UsageType.SIGNING)) {
+				var certificates = certificates(keyDescriptor);
+				for (X509Certificate certificate : certificates) {
+					verification.add(Saml2X509Credential.verification(certificate));
+				}
+			}
+		}
+		if (verification.isEmpty()) {
+			throw new Saml2Exception(
+					"Metadata response is missing verification certificates, necessary for verifying SAML assertions");
+		}
+
+		return verification;
+	}
+
+	private List<X509Certificate> certificates(KeyDescriptor keyDescriptor) {
+		try {
+			return KeyInfoSupport.getCertificates(keyDescriptor.getKeyInfo());
+		} catch (CertificateException ex) {
+			throw new Saml2Exception(ex);
+		}
+	}
 }
diff --git a/src/main/java/de/ozgcloud/nachrichten/antragraum/Saml2Decrypter.java b/src/main/java/de/ozgcloud/nachrichten/antragraum/Saml2Decrypter.java
index 62e6b589c6c99e5262cb92a8c57c415b07d5b4d2..41575e12f278e08dc763488fd80d0048e0c7d29c 100644
--- a/src/main/java/de/ozgcloud/nachrichten/antragraum/Saml2Decrypter.java
+++ b/src/main/java/de/ozgcloud/nachrichten/antragraum/Saml2Decrypter.java
@@ -20,9 +20,13 @@
 
 package de.ozgcloud.nachrichten.antragraum;
 
-import org.opensaml.core.xml.XMLObject;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+
+import jakarta.annotation.PostConstruct;
+
 import org.opensaml.core.xml.schema.XSString;
-import org.opensaml.saml.saml2.core.Assertion;
 import org.opensaml.saml.saml2.core.AttributeStatement;
 import org.opensaml.saml.saml2.core.EncryptedAssertion;
 import org.opensaml.saml.saml2.core.Response;
@@ -37,56 +41,59 @@ import org.opensaml.xmlsec.encryption.support.SimpleRetrievalMethodEncryptedKeyR
 import org.opensaml.xmlsec.keyinfo.KeyInfoCredentialResolver;
 import org.opensaml.xmlsec.keyinfo.impl.CollectionKeyInfoCredentialResolver;
 import org.springframework.security.saml2.Saml2Exception;
-import org.springframework.security.saml2.core.Saml2X509Credential;
+import org.springframework.stereotype.Service;
 
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
+import lombok.RequiredArgsConstructor;
 
+@Service
+@RequiredArgsConstructor
 class Saml2Decrypter {
-    public static final String LEGACY_POSTKORB_HANDLE_KEY = "legacyPostkorbHandle";
-    private final Decrypter decrypter;
-    private static final EncryptedKeyResolver encryptedKeyResolver = new ChainingEncryptedKeyResolver(
-            Arrays.asList(new InlineEncryptedKeyResolver(), new EncryptedElementTypeEncryptedKeyResolver(),
-                    new SimpleRetrievalMethodEncryptedKeyResolver()));
-
-    public Saml2Decrypter(Saml2X509Credential decryptionX509Credential) {
-        Collection<Credential> credentials = new ArrayList<>();
-
-        Credential cred = CredentialSupport.getSimpleCredential(decryptionX509Credential.getCertificate(), decryptionX509Credential.getPrivateKey());
-        credentials.add(cred);
-
-        KeyInfoCredentialResolver resolver = new CollectionKeyInfoCredentialResolver(credentials);
-        Decrypter decrypter = new Decrypter(null, resolver, encryptedKeyResolver);
-        decrypter.setRootInNewDocument(true);
-
-        this.decrypter = decrypter;
-    }
-
-    void decryptResponseElements(Response response) {
-        for (EncryptedAssertion encryptedAssertion : response.getEncryptedAssertions()) {
-            try {
-                Assertion assertion = decrypter.decrypt(encryptedAssertion);
-                response.getAssertions().add(assertion);
-            } catch (Exception ex) {
-                throw new Saml2Exception(ex);
-            }
-        }
-    }
-
-    String decryptPostfachId(Response response) {
-        decryptResponseElements(response);
-
-        var samlAssertion = response.getAssertions().get(0);
-        var statements = (AttributeStatement) samlAssertion.getStatements().get(1);
-        var attributes = statements.getAttributes();
-        var postfachIdOptional = attributes.stream().filter(attribute -> LEGACY_POSTKORB_HANDLE_KEY.equals(attribute.getFriendlyName())).findFirst();
-
-        return postfachIdOptional.map(postfachIdAttribute -> {
-            List<XMLObject> values = postfachIdAttribute.getAttributeValues();
-            return ((XSString) values.get(0)).getValue();
-        }).orElseThrow();
-
-    }
+	private final BayernIdSamlConfiguration configuration;
+
+	public static final String LEGACY_POSTKORB_HANDLE_KEY = "legacyPostkorbHandle";
+	private Decrypter decrypter;
+	private static final EncryptedKeyResolver encryptedKeyResolver = new ChainingEncryptedKeyResolver(
+			Arrays.asList(new InlineEncryptedKeyResolver(), new EncryptedElementTypeEncryptedKeyResolver(),
+					new SimpleRetrievalMethodEncryptedKeyResolver()));
+
+	@PostConstruct
+	void init() {
+		Collection<Credential> credentials = new ArrayList<>();
+		var decryptionX509Credential = configuration.getDecryptionCredential();
+
+		Credential cred = CredentialSupport.getSimpleCredential(decryptionX509Credential.getCertificate(), decryptionX509Credential.getPrivateKey());
+		credentials.add(cred);
+
+		KeyInfoCredentialResolver resolver = new CollectionKeyInfoCredentialResolver(credentials);
+		var setupDecrypter = new Decrypter(null, resolver, encryptedKeyResolver);
+		setupDecrypter.setRootInNewDocument(true);
+
+		decrypter = setupDecrypter;
+	}
+
+	void decryptResponseElements(Response response) {
+		for (EncryptedAssertion encryptedAssertion : response.getEncryptedAssertions()) {
+			try {
+				var assertion = decrypter.decrypt(encryptedAssertion);
+				response.getAssertions().add(assertion);
+			} catch (Exception ex) {
+				throw new Saml2Exception(ex);
+			}
+		}
+	}
+
+	String decryptPostfachId(Response response) {
+		decryptResponseElements(response);
+
+		var samlAssertion = response.getAssertions().get(0);
+		var statements = (AttributeStatement) samlAssertion.getStatements().get(1);
+		var attributes = statements.getAttributes();
+		var postfachIdOptional = attributes.stream().filter(attribute -> LEGACY_POSTKORB_HANDLE_KEY.equals(attribute.getFriendlyName())).findFirst();
+
+		return postfachIdOptional.map(postfachIdAttribute -> {
+			var values = postfachIdAttribute.getAttributeValues();
+			return ((XSString) values.get(0)).getValue();
+		}).orElseThrow();
+
+	}
 }
diff --git a/src/main/java/de/ozgcloud/nachrichten/antragraum/Saml2Parser.java b/src/main/java/de/ozgcloud/nachrichten/antragraum/Saml2Parser.java
index 21ebf47040a4f247cf733668b40a1377cf02db06..8c0a82f75231829d21d60085328f70d965f986b3 100644
--- a/src/main/java/de/ozgcloud/nachrichten/antragraum/Saml2Parser.java
+++ b/src/main/java/de/ozgcloud/nachrichten/antragraum/Saml2Parser.java
@@ -20,84 +20,86 @@
 
 package de.ozgcloud.nachrichten.antragraum;
 
-import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
-import net.shibboleth.utilities.java.support.xml.BasicParserPool;
-import net.shibboleth.utilities.java.support.xml.ParserPool;
-import net.shibboleth.utilities.java.support.xml.XMLParserException;
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.Map;
+
 import org.opensaml.core.xml.XMLObject;
 import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
 import org.opensaml.core.xml.io.UnmarshallingException;
 import org.opensaml.saml.saml2.core.Response;
 import org.opensaml.saml.saml2.core.impl.ResponseUnmarshaller;
 import org.springframework.security.saml2.Saml2Exception;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
+import org.springframework.stereotype.Service;
 
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
-import java.nio.charset.StandardCharsets;
-import java.util.HashMap;
-import java.util.Map;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.xml.BasicParserPool;
+import net.shibboleth.utilities.java.support.xml.ParserPool;
+import net.shibboleth.utilities.java.support.xml.XMLParserException;
 
+@Service
 class Saml2Parser {
-    private ParserPool parserPool;
-    private ResponseUnmarshaller unmarshaller;
-
-    Saml2Parser() {
-        init();
-    }
-
-    void init() {
-        try {
-            this.parserPool = getParserPool();
-            unmarshaller = (ResponseUnmarshaller) XMLObjectProviderRegistrySupport.getUnmarshallerFactory().getUnmarshaller(Response.DEFAULT_ELEMENT_NAME);
-        } catch (Exception e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    static ParserPool getParserPool() throws ComponentInitializationException {
-        BasicParserPool parserPool = new BasicParserPool();
-        parserPool.setMaxPoolSize(100);
-        parserPool.setCoalescing(true);
-        parserPool.setIgnoreComments(true);
-        parserPool.setIgnoreElementContentWhitespace(true);
-        parserPool.setNamespaceAware(true);
-        parserPool.setExpandEntityReferences(false);
-        parserPool.setXincludeAware(false);
-
-        final Map<String, Boolean> features = createFeatureMap();
-
-        parserPool.setBuilderFeatures(features);
-
-        parserPool.setBuilderAttributes(new HashMap<>());
-
-        parserPool.initialize();
-
-        return parserPool;
-    }
-
-    private static Map<String, Boolean> createFeatureMap() {
-        final Map<String, Boolean> features = new HashMap<>();
-        features.put("http://xml.org/sax/features/external-general-entities", Boolean.FALSE);
-        features.put("http://xml.org/sax/features/external-parameter-entities", Boolean.FALSE);
-        features.put("http://apache.org/xml/features/disallow-doctype-decl", Boolean.TRUE);
-        features.put("http://apache.org/xml/features/validation/schema/normalized-value", Boolean.FALSE);
-        features.put("http://javax.xml.XMLConstants/feature/secure-processing", Boolean.TRUE);
-        return features;
-    }
-
-    Response parse(String request) {
-        return (Response) xmlObject(new ByteArrayInputStream(request.getBytes(StandardCharsets.UTF_8)));
-    }
-
-    XMLObject xmlObject(InputStream inputStream) throws Saml2Exception {
-        try {
-            Document document = parserPool.parse(inputStream);
-            Element element = document.getDocumentElement();
-            return unmarshaller.unmarshall(element);
-        } catch (XMLParserException | UnmarshallingException e) {
-            throw new Saml2Exception("Failed to deserialize LogoutRequest", e);
-        }
-    }
+	private ParserPool parserPool;
+	private ResponseUnmarshaller unmarshaller;
+
+	Saml2Parser() {
+		init();
+	}
+
+	void init() {
+		try {
+			this.parserPool = getParserPool();
+			unmarshaller = (ResponseUnmarshaller) XMLObjectProviderRegistrySupport.getUnmarshallerFactory()
+					.getUnmarshaller(Response.DEFAULT_ELEMENT_NAME);
+		} catch (Exception e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	static ParserPool getParserPool() throws ComponentInitializationException {
+		var parserPool = new BasicParserPool();
+		parserPool.setMaxPoolSize(100);
+		parserPool.setCoalescing(true);
+		parserPool.setIgnoreComments(true);
+		parserPool.setIgnoreElementContentWhitespace(true);
+		parserPool.setNamespaceAware(true);
+		parserPool.setExpandEntityReferences(false);
+		parserPool.setXincludeAware(false);
+
+		final var features = createFeatureMap();
+
+		parserPool.setBuilderFeatures(features);
+
+		parserPool.setBuilderAttributes(new HashMap<>());
+
+		parserPool.initialize();
+
+		return parserPool;
+	}
+
+	private static Map<String, Boolean> createFeatureMap() {
+		final Map<String, Boolean> features = new HashMap<>();
+		features.put("http://xml.org/sax/features/external-general-entities", Boolean.FALSE);
+		features.put("http://xml.org/sax/features/external-parameter-entities", Boolean.FALSE);
+		features.put("http://apache.org/xml/features/disallow-doctype-decl", Boolean.TRUE);
+		features.put("http://apache.org/xml/features/validation/schema/normalized-value", Boolean.FALSE);
+		features.put("http://javax.xml.XMLConstants/feature/secure-processing", Boolean.TRUE);
+		return features;
+	}
+
+	Response parse(String request) {
+		return (Response) xmlObject(new ByteArrayInputStream(request.getBytes(StandardCharsets.UTF_8)));
+	}
+
+	XMLObject xmlObject(InputStream inputStream) throws Saml2Exception {
+		try {
+			var document = parserPool.parse(inputStream);
+			var element = document.getDocumentElement();
+			return unmarshaller.unmarshall(element);
+		} catch (XMLParserException | UnmarshallingException e) {
+			throw new Saml2Exception("Failed to deserialize LogoutRequest", e);
+		}
+	}
 }
diff --git a/src/main/java/de/ozgcloud/nachrichten/antragraum/Saml2Verifier.java b/src/main/java/de/ozgcloud/nachrichten/antragraum/Saml2Verifier.java
index 4377d65c97a3489bfe46c039842455c7a7d3493e..029f6a015c9cb3312588af52fcecad88881339e1 100644
--- a/src/main/java/de/ozgcloud/nachrichten/antragraum/Saml2Verifier.java
+++ b/src/main/java/de/ozgcloud/nachrichten/antragraum/Saml2Verifier.java
@@ -20,54 +20,65 @@
 
 package de.ozgcloud.nachrichten.antragraum;
 
-import lombok.RequiredArgsConstructor;
-import lombok.extern.log4j.Log4j2;
-import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+import jakarta.annotation.PostConstruct;
+
 import org.opensaml.saml.security.impl.SAMLSignatureProfileValidator;
-import org.opensaml.xmlsec.signature.Signature;
 import org.opensaml.xmlsec.signature.support.SignatureTrustEngine;
 import org.springframework.security.saml2.core.Saml2Error;
 import org.springframework.security.saml2.core.Saml2ErrorCodes;
+import org.springframework.stereotype.Service;
 
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.log4j.Log4j2;
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
 
 @Log4j2
 @RequiredArgsConstructor
+@Service
 class Saml2Verifier {
-    public static final String INVALID_SIGNATURE = "Invalid signature for object [%s]: ";
-    public static final String SIGNATURE_MISSING = "Signature missing";
-    private final Saml2Parser parser;
-    private final SignatureTrustEngine trustEngine;
-    private final CriteriaSet verificationCriteria;
+	public static final String INVALID_SIGNATURE = "Invalid signature for object [%s]: ";
+	public static final String SIGNATURE_MISSING = "Signature missing";
+	private final Saml2Parser parser;
+	private final BayernIdSamlConfiguration configuration;
+	private SignatureTrustEngine trustEngine;
+	private CriteriaSet verificationCriteria;
+
+	@PostConstruct
+	void init() {
+		trustEngine = configuration.getTrustEngine();
+		verificationCriteria = configuration.getVerificationCriteria();
+	}
 
-    List<Saml2Error> verify(String samlToken) {
-        var response = parser.parse(samlToken);
+	List<Saml2Error> verify(String samlToken) {
+		var response = parser.parse(samlToken);
 
-        List<Saml2Error> errors = new ArrayList<>();
-        Signature signature = response.getSignature();
-        SAMLSignatureProfileValidator profileValidator = new SAMLSignatureProfileValidator();
-        if (Objects.nonNull(signature)) {
-            try {
-                profileValidator.validate(signature);
-            } catch (Exception ex) {
-                LOG.error("Error validating SAML Token: ", ex);
-                errors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE, INVALID_SIGNATURE.formatted(response.getID())));
-            }
+		List<Saml2Error> errors = new ArrayList<>();
+		var signature = response.getSignature();
+		var profileValidator = new SAMLSignatureProfileValidator();
+		if (Objects.nonNull(signature)) {
+			try {
+				profileValidator.validate(signature);
+			} catch (Exception ex) {
+				LOG.error("Error validating SAML Token: ", ex);
+				errors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE, INVALID_SIGNATURE.formatted(response.getID())));
+			}
 
-            try {
-                if (!trustEngine.validate(signature, verificationCriteria)) {
-                    errors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE, INVALID_SIGNATURE.formatted(response.getID())));
-                }
-            } catch (Exception ex) {
-                LOG.error("Error validating SAML Token: ", ex);
-                errors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE, INVALID_SIGNATURE.formatted(response.getID())));
-            }
-        } else {
-            errors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE, SIGNATURE_MISSING));
-        }
+			try {
+				if (!trustEngine.validate(signature, verificationCriteria)) {
+					errors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE, INVALID_SIGNATURE.formatted(response.getID())));
+				}
+			} catch (Exception ex) {
+				LOG.error("Error validating SAML Token: ", ex);
+				errors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE, INVALID_SIGNATURE.formatted(response.getID())));
+			}
+		} else {
+			errors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE, SIGNATURE_MISSING));
+		}
 
-        return errors;
-    }
+		return errors;
+	}
 }
diff --git a/src/main/resources/application-bayernId.yaml b/src/main/resources/application-bayernId.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..8c90a64fc001ba9fdf129f03b842dc747592f18c
--- /dev/null
+++ b/src/main/resources/application-bayernId.yaml
@@ -0,0 +1,8 @@
+ozgcloud:
+  bayernid:
+    saml:
+      entity-id: https://antragsraum.ozgcloud.de/
+      metadata-uri: "classpath:/bayernid/metadata/bayernid-idp-infra.xml"
+      decryption:
+        private-key-location: "classpath:/bayernid/bayernid-test-enc.key"
+        certificate-location: "classpath:/bayernid/bayernid-test-enc.crt"
\ No newline at end of file
diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml
index 8c90a64fc001ba9fdf129f03b842dc747592f18c..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644
--- a/src/main/resources/application.yaml
+++ b/src/main/resources/application.yaml
@@ -1,8 +0,0 @@
-ozgcloud:
-  bayernid:
-    saml:
-      entity-id: https://antragsraum.ozgcloud.de/
-      metadata-uri: "classpath:/bayernid/metadata/bayernid-idp-infra.xml"
-      decryption:
-        private-key-location: "classpath:/bayernid/bayernid-test-enc.key"
-        certificate-location: "classpath:/bayernid/bayernid-test-enc.crt"
\ No newline at end of file
diff --git a/src/main/resources/bayernid/metadata/bayernid-idp.xml b/src/main/resources/bayernid/metadata/bayernid-idp.xml
deleted file mode 100644
index ebcc84c287b6dc26ed23c56a7ab359fc109f942e..0000000000000000000000000000000000000000
--- a/src/main/resources/bayernid/metadata/bayernid-idp.xml
+++ /dev/null
@@ -1,47 +0,0 @@
-<md:EntitiesDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata">
-    <md:EntityDescriptor entityID="https://id.bayernportal.de/idp">
-        <md:IDPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
-            <md:KeyDescriptor use="signing">
-                <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
-                    <ds:X509Data>
-                        <ds:X509Certificate>MIIFbzCCA1egAwIBAgIJAIpEXcxsS1nEMA0GCSqGSIb3DQEBCwUAME4xCzAJBgNV
-                            BAYTAkRFMQ8wDQYDVQQIDAZCYXllcm4xETAPBgNVBAcMCE11ZW5jaGVuMQ0wCwYD
-                            VQQKDARBS0RCMQwwCgYDVQQLDANJRE0wHhcNMjAxMDI3MTMxNzE3WhcNMjUxMDI2
-                            MTMxNzE3WjBOMQswCQYDVQQGEwJERTEPMA0GA1UECAwGQmF5ZXJuMREwDwYDVQQH
-                            DAhNdWVuY2hlbjENMAsGA1UECgwEQUtEQjEMMAoGA1UECwwDSURNMIICIjANBgkq
-                            hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA4MvmcEZhH4gcNhLLsEBhbB24fbU7ex1K
-                            ZROJr4s8OMqAHryfenCFxxVKPI8Vm2+qg6SY+D+MbKHc3lITYecq0EAQQoV1M1bc
-                            D31qVRtxTVb1Q9qGTv2V72LZB12qCCcgyWT4LOKTnGYUv5dIyj6EkEgVn8+Ia5Ow
-                            Q5KeoCsh9yVo5PJBJXkerQEA/M4/LkT/obeaK+3+8eRGFmoGLb+bFM40ths9St2j
-                            eeYT3UWl9NTY66mq4YVDZcaimUCMJQ4X9SjQ8HZeLgRzLx5X/6PwAJysyKVseN4j
-                            SV+4sm/HnMrPcQLLLQCD0ltdp1jk5zvoZ6C8cZ8FJ7kCJ+Kvz1zEmH32Pw0bxPwB
-                            QsKabyRxg5ESTIWwE2lWOnuooqSWk8lZIaGf8Sn8JFjRaVqVKN6JRFxgryurreJL
-                            9igN1e5cOdKYwXLfuqygcdVW40wYTjsL1MxKVf9Pb8J4EuOC95Hb85X6VckwRCx8
-                            Yroyr/UeDM8SKgsBJuSqTuFCo6oTf3zjDaD+BBzdVqBmQELfY81laG3at1A3vz0M
-                            E9UkY5kv9/LH161YIDELgir63fBNsQSXPir5awP8xihgUlmCIfOXiMU+m9vp60KL
-                            8LzA2AODo2H5AY7BMxAlM+LF+KvPqUBxdq2QyTnhQFaeiP9l7Lk1xagnloiR6rW/
-                            GP+hsBAVO5UCAwEAAaNQME4wHQYDVR0OBBYEFAS91fX286nxHd+4kJSFE4clWiuC
-                            MB8GA1UdIwQYMBaAFAS91fX286nxHd+4kJSFE4clWiuCMAwGA1UdEwQFMAMBAf8w
-                            DQYJKoZIhvcNAQELBQADggIBAMt8FUGWNqtKAuVAklNojt9JXk476XvZNXn4lTjx
-                            IaIyQLd+SpJ4vKPDAb78mKF4PtTdG2pqDIarvuxyLvG4Ut8Cd/NHTI9j9V7Yuw9g
-                            MKPKzXfI9Zb4pOIantmEspRr/BApLPJzZMHI8KmeQ3V0Ip3i8QHMuHfkFuTN9hdS
-                            mKTqoWZTo6dLawOkOpgjKh5RQjqjbXWUovMWQc/tffzUved0LePbPllJgSjUZRxm
-                            +723ToZ1frFCYpFj1xJiOuqr+GJdtWz0DFRt+nXxRfznYtXd1YRC5BzpVwNtH8GL
-                            OZfyH1HZtn1x5hcQOnRHwgcx37lJRW8yprAMiqLHtNYNlY/SD1KNMmqT07pkBwMq
-                            Cgvic7T2yj9Jg38s6XjiwWiFRMNhC2+z9G1uf63tfXLi3J8HS8YyTSjEK+9aXMEj
-                            57HWHBsLAiAzO+RyrVdTEnp0f5qbUeu0d4DKvHocOWkxu0jp220ZlCHPm6f/nWM/
-                            Eo5TGT0htLo0vidoj36PmCu2KOJ+AhdEml81/M5ENg8ofhhkidiQo2he7eGjLyn4
-                            4DPvSAiK/S1rvQmF65j/6mg0guFbGLpmLFQJXPztok2xTA+Vwvd/T5FxMNkKR9mh
-                            P9E8rUGOu4hGB4KSHD8mo8nYyp1cCke72e7dMobhLGsCqxEnIeO+NTBzSbd/gqGm 2fd0
-                        </ds:X509Certificate>
-                    </ds:X509Data>
-                </ds:KeyInfo>
-            </md:KeyDescriptor>
-            <md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</md:NameIDFormat>
-            <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
-                                    Location="https://id.bayernportal.de/idp/profile/SAML2/POST/SSO"/>
-            <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
-                                    Location="https://id.bayernportal.de/idp/profile/SAML2/Redirect/SSO"/>
-        </md:IDPSSODescriptor>
-    </md:EntityDescriptor>
-</md:EntitiesDescriptor>
\ No newline at end of file
diff --git a/src/test/java/de/ozgcloud/nachrichten/antragraum/AntragraumNachrichtMapperTest.java b/src/test/java/de/ozgcloud/nachrichten/antragraum/AntragraumNachrichtMapperTest.java
index 3053701849da83da9c83a2dd886d620e1e9b9fa7..662034f677347ff99db12406bd7e9500d201c456 100644
--- a/src/test/java/de/ozgcloud/nachrichten/antragraum/AntragraumNachrichtMapperTest.java
+++ b/src/test/java/de/ozgcloud/nachrichten/antragraum/AntragraumNachrichtMapperTest.java
@@ -20,12 +20,15 @@
 
 package de.ozgcloud.nachrichten.antragraum;
 
-import de.ozgcloud.nachrichten.postfach.PostfachNachrichtTestFactory;
-import de.ozgcloud.nachrichten.postfach.osi.MessageTestFactory;
+import static org.assertj.core.api.Assertions.*;
+
+import java.time.format.DateTimeFormatter;
+
 import org.junit.jupiter.api.Test;
 import org.mapstruct.factory.Mappers;
 
-import static org.assertj.core.api.Assertions.assertThat;
+import de.ozgcloud.nachrichten.postfach.PostfachNachrichtTestFactory;
+import de.ozgcloud.nachrichten.postfach.osi.MessageTestFactory;
 
 class AntragraumNachrichtMapperTest {
 
@@ -63,7 +66,7 @@ class AntragraumNachrichtMapperTest {
 	void shouldMapSendAt() {
 		var result = map();
 
-		assertThat(result.getSendAt()).isEqualTo(PostfachNachrichtTestFactory.SENT_AT.toString());
+		assertThat(result.getSendAt()).isEqualTo(PostfachNachrichtTestFactory.SENT_AT.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
 	}
 
 	@Test
diff --git a/src/test/java/de/ozgcloud/nachrichten/antragraum/AntragraumServiceTest.java b/src/test/java/de/ozgcloud/nachrichten/antragraum/AntragraumServiceTest.java
index fb81269bda54f4e645399c75c7cf666bfaeaa11c..887e0f460bca95ca85d196645e9db1a88c9bf968 100644
--- a/src/test/java/de/ozgcloud/nachrichten/antragraum/AntragraumServiceTest.java
+++ b/src/test/java/de/ozgcloud/nachrichten/antragraum/AntragraumServiceTest.java
@@ -1,19 +1,21 @@
 package de.ozgcloud.nachrichten.antragraum;
 
 import static org.assertj.core.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.*;
 import static org.mockito.Mockito.*;
 
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
+import org.mockito.Spy;
+import org.opensaml.saml.saml2.core.Response;
 
 import de.ozgcloud.nachrichten.NachrichtenManagerProperties;
-import de.ozgcloud.nachrichten.antragraum.AntragraumProperties;
-import de.ozgcloud.nachrichten.antragraum.AntragraumService;
+import de.ozgcloud.nachrichten.postfach.PersistPostfachNachrichtService;
 
 class AntragraumServiceTest {
-
+	@Spy
 	@InjectMocks
 	private AntragraumService service;
 
@@ -21,6 +23,14 @@ class AntragraumServiceTest {
 	private AntragraumProperties properties;
 	@Mock
 	private NachrichtenManagerProperties nachrichtenManagerProperties;
+	@Mock
+	private PersistPostfachNachrichtService postfachNachrichtService;
+	@Mock
+	private Saml2Verifier verifier;
+	@Mock
+	private Saml2Parser parser;
+	@Mock
+	private Saml2Decrypter decrypter;
 
 	@Nested
 	class TestGetAntragsraumUrl {
@@ -38,4 +48,40 @@ class AntragraumServiceTest {
 
 	}
 
+	@Nested
+	class TestFindRueckfragen {
+		static final String SAML_TOKEN = "TOKEN";
+
+		@Test
+		void shouldCallVerify() {
+			service.findRueckfragen(SAML_TOKEN);
+
+			verify(service).verifyToken(anyString());
+		}
+
+		@Test
+		void shouldCallVerifier() {
+			service.findRueckfragen(SAML_TOKEN);
+
+			verify(verifier).verify(anyString());
+		}
+
+		@Test
+		void shouldCallDecrypt() {
+			when(parser.parse(anyString())).thenReturn(mock(Response.class));
+
+			service.findRueckfragen(SAML_TOKEN);
+
+			verify(decrypter).decryptPostfachId(any(Response.class));
+		}
+
+		@Test
+		void shouldCallPostfachService() {
+			when(decrypter.decryptPostfachId(any())).thenReturn(GrpcFindRueckfrageRequestTestFactory.POSTFACH_ID);
+
+			service.findRueckfragen(SAML_TOKEN);
+
+			verify(postfachNachrichtService).findRueckfragen(anyString());
+		}
+	}
 }
\ No newline at end of file
diff --git a/src/test/java/de/ozgcloud/nachrichten/antragraum/AntragsraumGrpcServiceTest.java b/src/test/java/de/ozgcloud/nachrichten/antragraum/AntragsraumGrpcServiceTest.java
index 44852c4ee5fa3121500fb084af71ad2383ecde97..3aa2115e867a1e42201bd4eccb5b7bf20df5c440 100644
--- a/src/test/java/de/ozgcloud/nachrichten/antragraum/AntragsraumGrpcServiceTest.java
+++ b/src/test/java/de/ozgcloud/nachrichten/antragraum/AntragsraumGrpcServiceTest.java
@@ -20,79 +20,42 @@
 
 package de.ozgcloud.nachrichten.antragraum;
 
-import de.ozgcloud.nachrichten.postfach.PersistPostfachNachrichtService;
-import de.ozgcloud.nachrichten.postfach.PostfachNachricht;
-import de.ozgcloud.nachrichten.postfach.PostfachNachrichtTestFactory;
-import io.grpc.stub.StreamObserver;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.*;
+
+import java.util.stream.Stream;
+
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
 import org.mockito.Spy;
-import org.opensaml.saml.saml2.core.Response;
-
-import java.util.stream.Stream;
 
-import static org.mockito.Mockito.*;
+import de.ozgcloud.nachrichten.postfach.PostfachNachricht;
+import de.ozgcloud.nachrichten.postfach.PostfachNachrichtTestFactory;
+import io.grpc.stub.StreamObserver;
 
 class AntragsraumGrpcServiceTest {
 	@Spy
 	@InjectMocks
 	private AntragsraumGrpcService antragsraumGrpcService;
 	@Mock
-	private PersistPostfachNachrichtService postfachNachrichtService;
+	private AntragraumService antragraumService;
 	@Mock
 	private AntragraumNachrichtMapper mapper;
-	@Mock
-	private Saml2Verifier verifier;
-	@Mock
-	private Saml2Parser parser;
-	@Mock
-	private Saml2Decrypter decrypter;
 
 	@Nested
 	class TestFindRueckfragen {
-		@Mock
-		private StreamObserver<GrpcFindRueckfragenResponse> streamObserver;
-
-		@Test
-		void shouldCallVerify() {
-			antragsraumGrpcService.findRueckfragen(GrpcFindRueckfrageRequestTestFactory.create(), streamObserver);
-
-			verify(antragsraumGrpcService).verifyToken(anyString());
-		}
-
-		@Test
-		void shouldCallVerifier() {
-			antragsraumGrpcService.findRueckfragen(GrpcFindRueckfrageRequestTestFactory.create(), streamObserver);
-
-			verify(verifier).verify(anyString());
-		}
-
-		@Test
-		void shouldCallDecrypt() {
-			when(parser.parse(anyString())).thenReturn(mock(Response.class));
-
-			antragsraumGrpcService.findRueckfragen(GrpcFindRueckfrageRequestTestFactory.create(), streamObserver);
-
-			verify(decrypter).decryptPostfachId(any(Response.class));
-		}
-
-		@Test
-		void shouldCallPostfachService() {
-			when(decrypter.decryptPostfachId(any())).thenReturn(GrpcFindRueckfrageRequestTestFactory.POSTFACH_ID);
-
-			antragsraumGrpcService.findRueckfragen(GrpcFindRueckfrageRequestTestFactory.create(), streamObserver);
-
-			verify(postfachNachrichtService).findRueckfragen(anyString());
-		}
 
 		@Nested
 		class TestFindRueckfragenGrpc {
+			@Mock
+			private StreamObserver<GrpcFindRueckfragenResponse> streamObserver;
+
 			@BeforeEach
 			void setup() {
-				when(postfachNachrichtService.findRueckfragen(any())).thenReturn(Stream.of(PostfachNachrichtTestFactory.create()));
+				when(antragraumService.findRueckfragen(any())).thenReturn(Stream.of(PostfachNachrichtTestFactory.create()));
 				when(mapper.toGrpc(any(PostfachNachricht.class))).thenReturn(GrpcRueckfrage.getDefaultInstance());
 			}
 
diff --git a/src/test/java/de/ozgcloud/nachrichten/antragraum/BayernIdSamlConfigurationITCase.java b/src/test/java/de/ozgcloud/nachrichten/antragraum/BayernIdSamlConfigurationITCase.java
deleted file mode 100644
index 711012919215c4d0e0e634b7a52e0614f5c0b339..0000000000000000000000000000000000000000
--- a/src/test/java/de/ozgcloud/nachrichten/antragraum/BayernIdSamlConfigurationITCase.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (c) 2024.
- * Lizenziert unter der EUPL, Version 1.2 oder - sobald
- * diese von der Europäischen Kommission genehmigt wurden -
- * Folgeversionen der EUPL ("Lizenz");
- * Sie dürfen dieses Werk ausschließlich gemäß
- * dieser Lizenz nutzen.
- * Eine Kopie der Lizenz finden Sie hier:
- *
- * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
- *
- * Sofern nicht durch anwendbare Rechtsvorschriften
- * gefordert oder in schriftlicher Form vereinbart, wird
- * die unter der Lizenz verbreitete Software "so wie sie
- * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
- * ausdrücklich oder stillschweigend - verbreitet.
- * Die sprachspezifischen Genehmigungen und Beschränkungen
- * unter der Lizenz sind dem Lizenztext zu entnehmen.
- */
-
-package de.ozgcloud.nachrichten.antragraum;
-
-import de.ozgcloud.common.test.ITCase;
-import org.junit.jupiter.api.Test;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.test.context.SpringBootTest;
-
-import static org.assertj.core.api.Assertions.*;
-
-@ITCase
-@SpringBootTest(classes = {BayernIdSamlConfiguration.class})
-class BayernIdSamlConfigurationITCase {
-    @Autowired
-    private BayernIdSamlConfiguration bayernIdSamlConfiguration;
-
-    @Test
-    void shouldCreateTrustEngine() {
-        assertThat(bayernIdSamlConfiguration.trustEngine()).isNotNull();
-    }
-
-    @Test
-    void shouldCreateVerificationCriteria() {
-        assertThat(bayernIdSamlConfiguration.verificationCriteria()).isNotNull();
-    }
-
-    @Test
-    void shouldLoadVerificationCert() throws Exception {
-        assertThat(bayernIdSamlConfiguration.getCertificatesFromMetadata()).isNotNull();
-    }
-}
\ No newline at end of file
diff --git a/src/test/java/de/ozgcloud/nachrichten/antragraum/BayernIdSamlConfigurationTest.java b/src/test/java/de/ozgcloud/nachrichten/antragraum/BayernIdSamlConfigurationTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..efe6f243454255e165261a248e631863d21f1a76
--- /dev/null
+++ b/src/test/java/de/ozgcloud/nachrichten/antragraum/BayernIdSamlConfigurationTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2024.
+ * Lizenziert unter der EUPL, Version 1.2 oder - sobald
+ * diese von der Europäischen Kommission genehmigt wurden -
+ * Folgeversionen der EUPL ("Lizenz");
+ * Sie dürfen dieses Werk ausschließlich gemäß
+ * dieser Lizenz nutzen.
+ * Eine Kopie der Lizenz finden Sie hier:
+ *
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Sofern nicht durch anwendbare Rechtsvorschriften
+ * gefordert oder in schriftlicher Form vereinbart, wird
+ * die unter der Lizenz verbreitete Software "so wie sie
+ * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
+ * ausdrücklich oder stillschweigend - verbreitet.
+ * Die sprachspezifischen Genehmigungen und Beschränkungen
+ * unter der Lizenz sind dem Lizenztext zu entnehmen.
+ */
+
+package de.ozgcloud.nachrichten.antragraum;
+
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.springframework.core.io.InputStreamResource;
+
+import de.ozgcloud.common.test.TestUtils;
+
+class BayernIdSamlConfigurationTest {
+	@Mock
+	private BayernIdProperties properties;
+	@InjectMocks
+	private BayernIdSamlConfiguration bayernIdSamlConfiguration;
+
+	@Nested
+	class TestCreatingTrustEngine {
+		@BeforeEach
+		void setup() throws Exception {
+			when(properties.getMetadataUri()).thenReturn(new InputStreamResource(TestUtils.loadFile("bayernid-idp-infra.xml")));
+			when(properties.getEntityId()).thenReturn("https://antragsraum.ozgcloud.de/");
+			bayernIdSamlConfiguration.initOpenSAML();
+		}
+
+		@Test
+		void shouldCreateTrustEngine() {
+			assertThat(bayernIdSamlConfiguration.getTrustEngine()).isNotNull();
+		}
+	}
+
+	@Nested
+	class TestLoadingVerficationData {
+		@Test
+		void shouldCreateVerificationCriteria() {
+			when(properties.getEntityId()).thenReturn("https://antragsraum.ozgcloud.de/");
+			bayernIdSamlConfiguration.initOpenSAML();
+
+			assertThat(bayernIdSamlConfiguration.getVerificationCriteria()).isNotNull();
+		}
+
+		@Test
+		void shouldLoadVerificationCert() throws Exception {
+			when(properties.getMetadataUri()).thenReturn(new InputStreamResource(TestUtils.loadFile("bayernid-idp-infra.xml")));
+			bayernIdSamlConfiguration.initOpenSAML();
+
+			assertThat(bayernIdSamlConfiguration.getCertificatesFromMetadata()).isNotNull();
+		}
+	}
+}
\ No newline at end of file
diff --git a/src/test/java/de/ozgcloud/nachrichten/antragraum/GrpcNachrichtTestFactory.java b/src/test/java/de/ozgcloud/nachrichten/antragraum/GrpcNachrichtTestFactory.java
index 1218f303b5e0c3dd25a8d11b30a7aef43adfb0af..cc500c7d3f9a12521445388101bd7c0d6af1a5bd 100644
--- a/src/test/java/de/ozgcloud/nachrichten/antragraum/GrpcNachrichtTestFactory.java
+++ b/src/test/java/de/ozgcloud/nachrichten/antragraum/GrpcNachrichtTestFactory.java
@@ -5,7 +5,7 @@ import de.ozgcloud.nachrichten.info.InfoManagerRequestTestFactory;
 import de.ozgcloud.nachrichten.postfach.PostfachNachrichtTestFactory;
 import de.ozgcloud.nachrichten.postfach.osi.MessageTestFactory;
 
-public class GrpcNachrichtTestFactory {
+class GrpcNachrichtTestFactory {
 
 	public static GrpcNachricht create() {
 		return createBuilder().build();
diff --git a/src/test/java/de/ozgcloud/nachrichten/antragraum/Saml2DecrypterITCase.java b/src/test/java/de/ozgcloud/nachrichten/antragraum/Saml2DecrypterITCase.java
deleted file mode 100644
index bd4861d04d28a886938bbdd15cd0d2001ffedaba..0000000000000000000000000000000000000000
--- a/src/test/java/de/ozgcloud/nachrichten/antragraum/Saml2DecrypterITCase.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright (c) 2024.
- * Lizenziert unter der EUPL, Version 1.2 oder - sobald
- * diese von der Europäischen Kommission genehmigt wurden -
- * Folgeversionen der EUPL ("Lizenz");
- * Sie dürfen dieses Werk ausschließlich gemäß
- * dieser Lizenz nutzen.
- * Eine Kopie der Lizenz finden Sie hier:
- *
- * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
- *
- * Sofern nicht durch anwendbare Rechtsvorschriften
- * gefordert oder in schriftlicher Form vereinbart, wird
- * die unter der Lizenz verbreitete Software "so wie sie
- * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
- * ausdrücklich oder stillschweigend - verbreitet.
- * Die sprachspezifischen Genehmigungen und Beschränkungen
- * unter der Lizenz sind dem Lizenztext zu entnehmen.
- */
-
-package de.ozgcloud.nachrichten.antragraum;
-
-import de.ozgcloud.common.test.ITCase;
-import de.ozgcloud.common.test.TestUtils;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.opensaml.saml.saml2.core.Attribute;
-import org.opensaml.saml.saml2.core.AttributeStatement;
-import org.opensaml.saml.saml2.core.Response;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.test.context.SpringBootTest;
-
-import static org.assertj.core.api.Assertions.*;
-
-@ITCase
-@SpringBootTest(classes = {BayernIdSamlConfiguration.class})
-public class Saml2DecrypterITCase {
-    private Response samlResponse;
-
-    @Autowired
-    private Saml2Parser parser;
-
-    @Autowired
-    private Saml2Decrypter decrypter;
-
-    @BeforeEach
-    void setup() {
-        var samlResponseString = TestUtils.loadTextFile("SamlResponse.xml");
-        samlResponse = parser.parse(samlResponseString);
-    }
-
-    @Test
-    void shouldDecrypt() {
-        assertThat(samlResponse.getAssertions()).isEmpty();
-
-        decrypter.decryptResponseElements(samlResponse);
-
-        assertThat(samlResponse.getAssertions()).isNotEmpty();
-    }
-
-    @Test
-    void shouldHaveSubject() {
-        decrypter.decryptResponseElements(samlResponse);
-        var samlAssertion = samlResponse.getAssertions().get(0);
-
-        assertThat(samlAssertion.getSubject()).isNotNull();
-    }
-
-    @Test
-    void shouldHaveAuthnStatements() {
-        decrypter.decryptResponseElements(samlResponse);
-        var samlAssertion = samlResponse.getAssertions().get(0);
-        var authnStatements = samlAssertion.getAuthnStatements();
-
-        assertThat(authnStatements).isNotNull();
-    }
-
-    @Test
-    void shouldHaveStatements() {
-        decrypter.decryptResponseElements(samlResponse);
-        var samlAssertion = samlResponse.getAssertions().get(0);
-        var statements = samlAssertion.getStatements();
-
-        assertThat(statements).isNotNull();
-    }
-
-    @Test
-    void shouldHaveAttributes() {
-        decrypter.decryptResponseElements(samlResponse);
-        var samlAssertion = samlResponse.getAssertions().get(0);
-        var statements = (AttributeStatement) samlAssertion.getStatements().get(1);
-        var attributes = statements.getAttributes();
-        assertThat(attributes).hasSize(7);
-    }
-
-    @Test
-    void shouldHavePostfachId() {
-        decrypter.decryptResponseElements(samlResponse);
-        var samlAssertion = samlResponse.getAssertions().get(0);
-        var statements = (AttributeStatement) samlAssertion.getStatements().get(1);
-        var attributes = statements.getAttributes();
-        var postfachIdOptional = attributes.stream().filter(attribute -> "legacyPostkorbHandle".equals(attribute.getFriendlyName())).findFirst();
-        assertThat(postfachIdOptional).map(Attribute::getAttributeValues).isNotNull();
-    }
-
-    @Test
-    void shouldGetPostfachId() {
-        var postfachId = decrypter.decryptPostfachId(samlResponse);
-
-        assertThat(postfachId).isEqualTo("28721c6f-b78f-4d5c-a048-19fd2fc429d2");
-    }
-}
\ No newline at end of file
diff --git a/src/test/java/de/ozgcloud/nachrichten/antragraum/Saml2DecrypterTest.java b/src/test/java/de/ozgcloud/nachrichten/antragraum/Saml2DecrypterTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..5c5d511b49a113e4d43e146eafea8bcf41ca9835
--- /dev/null
+++ b/src/test/java/de/ozgcloud/nachrichten/antragraum/Saml2DecrypterTest.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2024.
+ * Lizenziert unter der EUPL, Version 1.2 oder - sobald
+ * diese von der Europäischen Kommission genehmigt wurden -
+ * Folgeversionen der EUPL ("Lizenz");
+ * Sie dürfen dieses Werk ausschließlich gemäß
+ * dieser Lizenz nutzen.
+ * Eine Kopie der Lizenz finden Sie hier:
+ *
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Sofern nicht durch anwendbare Rechtsvorschriften
+ * gefordert oder in schriftlicher Form vereinbart, wird
+ * die unter der Lizenz verbreitete Software "so wie sie
+ * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
+ * ausdrücklich oder stillschweigend - verbreitet.
+ * Die sprachspezifischen Genehmigungen und Beschränkungen
+ * unter der Lizenz sind dem Lizenztext zu entnehmen.
+ */
+
+package de.ozgcloud.nachrichten.antragraum;
+
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+import org.opensaml.saml.saml2.core.Attribute;
+import org.opensaml.saml.saml2.core.AttributeStatement;
+import org.opensaml.saml.saml2.core.Response;
+import org.springframework.core.io.InputStreamResource;
+
+import de.ozgcloud.common.test.TestUtils;
+
+class Saml2DecrypterTest {
+	private Response samlResponse;
+
+	@Mock
+	private BayernIdProperties properties;
+
+	private BayernIdSamlConfiguration configuration;
+
+	private Saml2Decrypter decrypter;
+
+	@BeforeEach
+	void setup() throws Exception {
+		when(properties.getDecryptionCertificate()).thenReturn(new InputStreamResource(TestUtils.loadFile("bayernid-test-enc.crt")));
+		when(properties.getDecryptionPrivateKey()).thenReturn(new InputStreamResource(TestUtils.loadFile("bayernid-test-enc.key")));
+
+		configuration = new BayernIdSamlConfiguration(properties);
+		configuration.initOpenSAML();
+
+		var samlResponseString = TestUtils.loadTextFile("SamlResponse.xml");
+		samlResponse = new Saml2Parser().parse(samlResponseString);
+
+		decrypter = new Saml2Decrypter(configuration);
+		decrypter.init();
+	}
+
+	@Test
+	void shouldDecrypt() {
+		assertThat(samlResponse.getAssertions()).isEmpty();
+
+		decrypter.decryptResponseElements(samlResponse);
+
+		assertThat(samlResponse.getAssertions()).isNotEmpty();
+	}
+
+	@Test
+	void shouldHaveSubject() {
+		decrypter.decryptResponseElements(samlResponse);
+		var samlAssertion = samlResponse.getAssertions().get(0);
+
+		assertThat(samlAssertion.getSubject()).isNotNull();
+	}
+
+	@Test
+	void shouldHaveAuthnStatements() {
+		decrypter.decryptResponseElements(samlResponse);
+		var samlAssertion = samlResponse.getAssertions().get(0);
+		var authnStatements = samlAssertion.getAuthnStatements();
+
+		assertThat(authnStatements).isNotNull();
+	}
+
+	@Test
+	void shouldHaveStatements() {
+		decrypter.decryptResponseElements(samlResponse);
+		var samlAssertion = samlResponse.getAssertions().get(0);
+		var statements = samlAssertion.getStatements();
+
+		assertThat(statements).isNotNull();
+	}
+
+	@Test
+	void shouldHaveAttributes() {
+		decrypter.decryptResponseElements(samlResponse);
+		var samlAssertion = samlResponse.getAssertions().get(0);
+		var statements = (AttributeStatement) samlAssertion.getStatements().get(1);
+		var attributes = statements.getAttributes();
+		assertThat(attributes).hasSize(7);
+	}
+
+	@Test
+	void shouldHavePostfachId() {
+		decrypter.decryptResponseElements(samlResponse);
+		var samlAssertion = samlResponse.getAssertions().get(0);
+		var statements = (AttributeStatement) samlAssertion.getStatements().get(1);
+		var attributes = statements.getAttributes();
+		var postfachIdOptional = attributes.stream().filter(attribute -> "legacyPostkorbHandle".equals(attribute.getFriendlyName())).findFirst();
+		assertThat(postfachIdOptional).map(Attribute::getAttributeValues).isNotNull();
+	}
+
+	@Test
+	void shouldGetPostfachId() {
+		var postfachId = decrypter.decryptPostfachId(samlResponse);
+
+		assertThat(postfachId).isEqualTo("28721c6f-b78f-4d5c-a048-19fd2fc429d2");
+	}
+}
\ No newline at end of file
diff --git a/src/test/java/de/ozgcloud/nachrichten/antragraum/Saml2ParserTest.java b/src/test/java/de/ozgcloud/nachrichten/antragraum/Saml2ParserTest.java
index bd64e2a3c9763c170bf06da60af712ba19c79057..c9efc057f7f8f41a945ba39bc864d8c9a93dbba0 100644
--- a/src/test/java/de/ozgcloud/nachrichten/antragraum/Saml2ParserTest.java
+++ b/src/test/java/de/ozgcloud/nachrichten/antragraum/Saml2ParserTest.java
@@ -1,84 +1,66 @@
-/*
- * Copyright (c) 2024.
- * Lizenziert unter der EUPL, Version 1.2 oder - sobald
- * diese von der Europäischen Kommission genehmigt wurden -
- * Folgeversionen der EUPL ("Lizenz");
- * Sie dürfen dieses Werk ausschließlich gemäß
- * dieser Lizenz nutzen.
- * Eine Kopie der Lizenz finden Sie hier:
- *
- * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
- *
- * Sofern nicht durch anwendbare Rechtsvorschriften
- * gefordert oder in schriftlicher Form vereinbart, wird
- * die unter der Lizenz verbreitete Software "so wie sie
- * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
- * ausdrücklich oder stillschweigend - verbreitet.
- * Die sprachspezifischen Genehmigungen und Beschränkungen
- * unter der Lizenz sind dem Lizenztext zu entnehmen.
- */
 
 package de.ozgcloud.nachrichten.antragraum;
 
-import de.ozgcloud.common.test.TestUtils;
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.opensaml.saml.saml2.core.Response;
 
-import java.io.InputStream;
-
-import static org.assertj.core.api.Assertions.*;
+import de.ozgcloud.common.test.TestUtils;
 
 class Saml2ParserTest {
-    @BeforeEach
-    void setup() {
-        new BayernIdSamlConfiguration().initOpenSAML();
-    }
+	@BeforeEach
+	void setup() {
+		var properties = mock(BayernIdProperties.class);
+		new BayernIdSamlConfiguration(properties).initOpenSAML();
+	}
 
-    @Test
-    void shouldInit() {
-        Saml2Parser parser = new Saml2Parser();
-        assertThat(parser).isNotNull();
-    }
+	@Test
+	void shouldInit() {
+		var parser = new Saml2Parser();
+		assertThat(parser).isNotNull();
+	}
 
-    @Test
-    void shouldParseSamlToken() throws Exception {
-        var response = getResponse();
-        assertThat(response).isNotNull();
-    }
+	@Test
+	void shouldParseSamlToken() throws Exception {
+		var response = getResponse();
+		assertThat(response).isNotNull();
+	}
 
-    @Test
-    void shouldHaveAssertions() throws Exception {
-        var response = getResponse();
-        assertThat(response.getAssertions()).isNotNull();
-    }
+	@Test
+	void shouldHaveAssertions() throws Exception {
+		var response = getResponse();
+		assertThat(response.getAssertions()).isNotNull();
+	}
 
-    @Test
-    void shouldHaveEncryptedAssertions() throws Exception {
-        var response = getResponse();
-        assertThat(response.getEncryptedAssertions()).isNotNull();
-    }
+	@Test
+	void shouldHaveEncryptedAssertions() throws Exception {
+		var response = getResponse();
+		assertThat(response.getEncryptedAssertions()).isNotNull();
+	}
 
-    @Test
-    void shouldHaveIssuer() throws Exception {
-        var response = getResponse();
-        assertThat(response.getIssuer().getValue()).isEqualTo("https://infra-pre-id.bayernportal.de/idp");
-    }
+	@Test
+	void shouldHaveIssuer() throws Exception {
+		var response = getResponse();
+		assertThat(response.getIssuer().getValue()).isEqualTo("https://infra-pre-id.bayernportal.de/idp");
+	}
 
-    @Test
-    void shouldGetXMLObject() throws Exception {
-        Saml2Parser parser = new Saml2Parser();
+	@Test
+	void shouldGetXMLObject() throws Exception {
+		var parser = new Saml2Parser();
 
-        try (InputStream tokenStream = TestUtils.loadFile("SamlResponse.xml")) {
-            assertThat(parser.xmlObject(tokenStream)).isNotNull();
-        }
-    }
+		try (var tokenStream = TestUtils.loadFile("SamlResponse.xml")) {
+			assertThat(parser.xmlObject(tokenStream)).isNotNull();
+		}
+	}
 
-    private static Response getResponse() throws Exception {
-        var token = TestUtils.loadTextFile("SamlResponse.xml");
-        Saml2Parser parser = new Saml2Parser(); //BayernIdSamlConfiguration.getParserPool()
+	private static Response getResponse() throws Exception {
+		var token = TestUtils.loadTextFile("SamlResponse.xml");
+		var parser = new Saml2Parser();
 
-        return parser.parse(token);
-    }
+		return parser.parse(token);
+	}
 
 }
\ No newline at end of file
diff --git a/src/test/java/de/ozgcloud/nachrichten/antragraum/Saml2VerifierITCase.java b/src/test/java/de/ozgcloud/nachrichten/antragraum/Saml2VerifierTest.java
similarity index 51%
rename from src/test/java/de/ozgcloud/nachrichten/antragraum/Saml2VerifierITCase.java
rename to src/test/java/de/ozgcloud/nachrichten/antragraum/Saml2VerifierTest.java
index f342a89b65c72aa3269c4c09d65e32cb931c4f27..7e94a28d5a8cef80aafc442389aa15da919513bc 100644
--- a/src/test/java/de/ozgcloud/nachrichten/antragraum/Saml2VerifierITCase.java
+++ b/src/test/java/de/ozgcloud/nachrichten/antragraum/Saml2VerifierTest.java
@@ -20,32 +20,43 @@
 
 package de.ozgcloud.nachrichten.antragraum;
 
-import de.ozgcloud.common.test.ITCase;
-import de.ozgcloud.common.test.TestUtils;
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.test.context.SpringBootTest;
+import org.mockito.Mock;
+import org.springframework.core.io.InputStreamResource;
 
-import static org.assertj.core.api.Assertions.*;
+import de.ozgcloud.common.test.TestUtils;
+
+class Saml2VerifierTest {
+	private String samlResponse;
+
+	private Saml2Verifier verifier;
+
+	@Mock
+	private BayernIdProperties properties;
+
+	private BayernIdSamlConfiguration configuration;
 
-@ITCase
-@SpringBootTest(classes = {BayernIdSamlConfiguration.class})
-public class Saml2VerifierITCase {
-    private String samlResponse;
+	@BeforeEach
+	void setup() throws Exception {
+		when(properties.getMetadataUri()).thenReturn(new InputStreamResource(TestUtils.loadFile("bayernid-idp-infra.xml")));
+		when(properties.getEntityId()).thenReturn("https://antragsraum.ozgcloud.de");
+		samlResponse = TestUtils.loadTextFile("SamlResponse.xml");
 
-    @Autowired
-    private Saml2Verifier verifier;
+		configuration = new BayernIdSamlConfiguration(properties);
+		configuration.initOpenSAML();
 
-    @BeforeEach
-    void setup() {
-        samlResponse = TestUtils.loadTextFile("SamlResponse.xml");
-    }
+		verifier = new Saml2Verifier(new Saml2Parser(), configuration);
+		verifier.init();
+	}
 
-    @Test
-    void shouldGetVerificationError() {
-        var res = verifier.verify(samlResponse);
+	@Test
+	void shouldGetVerificationError() {
+		var res = verifier.verify(samlResponse);
 
-        assertThat(res).isNotEmpty();
-    }
+		assertThat(res).isNotEmpty();
+	}
 }
\ No newline at end of file
diff --git a/src/test/resources/application-bayernId.yaml b/src/test/resources/application-bayernId.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..6b68c25e625e679dbf6ce0cccb7d0c2cb7e94931
--- /dev/null
+++ b/src/test/resources/application-bayernId.yaml
@@ -0,0 +1,9 @@
+ozgcloud:
+  antragraum.url: "https://antragsraum.de"
+  bayernid:
+    saml:
+      entityid: https://antragsraum.ozgcloud.de/
+      metadatauri: "classpath:/bayernid/metadata/bayernid-idp-infra.xml"
+      decryption:
+        private-key-location: "classpath:/bayernid/bayernid-test-enc.key"
+        certificate-location: "classpath:/bayernid/bayernid-test-enc.crt"
\ No newline at end of file
diff --git a/src/test/resources/bayernid-idp-infra.xml b/src/test/resources/bayernid-idp-infra.xml
new file mode 100644
index 0000000000000000000000000000000000000000..ec1ed7ca7099b8be7a8cff7448a740f0b9404c34
--- /dev/null
+++ b/src/test/resources/bayernid-idp-infra.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?><md:EntitiesDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata">
+    <md:EntityDescriptor entityID="https://infra-pre-id.bayernportal.de/idp">
+        <md:IDPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
+            <md:KeyDescriptor use="signing">
+                <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
+                    <ds:X509Data>
+                        <ds:X509Certificate>MIIFbzCCA1egAwIBAgIJAPdFXXarkBN2MA0GCSqGSIb3DQEBCwUAME4xCzAJBgNV
+                            BAYTAkRFMQ8wDQYDVQQIDAZCYXllcm4xETAPBgNVBAcMCE11ZW5jaGVuMQ0wCwYD
+                            VQQKDARBS0RCMQwwCgYDVQQLDANJRE0wHhcNMjAxMDI3MTMxODQxWhcNMjUxMDI2
+                            MTMxODQxWjBOMQswCQYDVQQGEwJERTEPMA0GA1UECAwGQmF5ZXJuMREwDwYDVQQH
+                            DAhNdWVuY2hlbjENMAsGA1UECgwEQUtEQjEMMAoGA1UECwwDSURNMIICIjANBgkq
+                            hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAzDtWAEdC3J9FD+ti1exRhN1lzNgKWqO2
+                            gQNdJvlt7KGHA2VGGO7tqRogTuoqi/ydtiHJ8+lhp4kcWqyfv7i9HXOncvcsRRmR
+                            dZjUY2Iui6ozJqD5LVm/vP5YfdP7vQPdbqyyfpoJhf3mbMEtdNDdGRnGIPUfDn+C
+                            Fbo37f9tPwMgf3jgh4gxaujtLIhhr9gevVTEeZAFu9EvzLNd3kEtRb7MuXqIOdu1
+                            rW8HlGYFwwVLqEyBn8XG0QAIfhMmGjFMG7z+Kco2quwOmmZVzWQfeH/3AlN2KbcP
+                            t7j+pl+6Bew2AAivP7O+95YKORqQjTu3rPWMF4txPId37MSjoytwBRyd5EACTvhQ
+                            BOGrDFKQUOx6fTtRc8+7XGVz8MdQaZQWQXXh1ByU783twNdnRSrSVIyLdjiy1uCb
+                            jvsSAtbzGBygPIvDo3skCNLNFXsChtHIfFFDK20KPGb0ghEDf2q3hDbFG3ZDGGyn
+                            ZmJcZKuZhJqodJ/++sAXADyTJNAPVYDjKCF4ypELp2Eu/p1gaQPJEb74L/ZFZVOE
+                            JFyXIiaqB9J+fcn/biqHHOmcCi8n9aIiNt1fatr1Z4lQRWoGtKaGU0+bzUSH4Bgs
+                            2EG4u1CI2MKDWqK2aEsHrtu8tbS9LrUmDVKtaEUOeul8xWVa036vp/YUIdiJNZSx
+                            ZG4iTmSOATECAwEAAaNQME4wHQYDVR0OBBYEFFYeltslkaolOmcINXQeSe7nURwp
+                            MB8GA1UdIwQYMBaAFFYeltslkaolOmcINXQeSe7nURwpMAwGA1UdEwQFMAMBAf8w
+                            DQYJKoZIhvcNAQELBQADggIBAKqAlXoO41SAiycYUOrR90pfwTCysmbtHF5RWSCM
+                            jF2aCG8URJ7bXwC0lBH8E5zCetFZwdqZziQtxzRkIOfhS5uWbH0RDhwuxZG+5RTP
+                            yaHPAZI6e5xHDu8vHl/VbC3lnL/6K8l+Purr/yo8qkJqrPgThZRL9jBQyYRhDSsJ
+                            UyIw5zcKKUQC/JWtMQAQcopbjekCs6xDT1HqIN90Sc/gOfYjNo0dGMNmro9mxcw8
+                            2Iow18KNVdtEexfD+/6x4NPD61pzuQEe09TR+Cv3XyzBoGQ/2arijcPnGvth79ff
+                            VFtRSf3fSs7wEKV9g3mEWXFDtPBhDj6K0kKU/kJfEZixkXl92MY+bmugrtTIrazj
+                            tfrgMglIAHu9XCYWd/gef0J+PNfHsxgbTEr3XSC+5/xoFKPQSw3PgV8lkUDq4mJU
+                            Ky/q4YmA37XQxourFR5pWvF03YACdtq6zPjtVeI7Cvkte6k0YW5S3cx9RmPv6YZh
+                            laZ5ERpWNiv6IjokLsvNeemf2PApjO7Q2EDBIoHBYH31wwJSsyRDrSVmbaqLFI15
+                            fLXeh2A4YbaBDZdGvDiLOAk+dG1wdZ2aGw/uNBzMtc8VeKqI1HPcqIluBA3uUPpy
+                            LLA+9hDPf6Pp4j0gkXxBikz+/h22bFxE1HmDiOSkEn+2NmOHuEFeA+D8jsCAL5VJ
+                            3emK</ds:X509Certificate>
+                    </ds:X509Data>
+                </ds:KeyInfo>
+            </md:KeyDescriptor>
+            <md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</md:NameIDFormat>
+            <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://infra-pre-id.bayernportal.de/idp/profile/SAML2/POST/SSO"/>
+            <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://infra-pre-id.bayernportal.de/idp/profile/SAML2/Redirect/SSO"/>
+        </md:IDPSSODescriptor>
+    </md:EntityDescriptor>
+</md:EntitiesDescriptor>
\ No newline at end of file
diff --git a/src/test/resources/bayernid-test-enc.crt b/src/test/resources/bayernid-test-enc.crt
new file mode 100644
index 0000000000000000000000000000000000000000..fe04bc8d5ad148f0e84ca1c98160c111f3aeba5d
--- /dev/null
+++ b/src/test/resources/bayernid-test-enc.crt
@@ -0,0 +1,22 @@
+-----BEGIN CERTIFICATE-----
+MIIDsTCCApmgAwIBAgIUdw/27be5+2vj+MhGtoJjDsMsdDEwDQYJKoZIhvcNAQEL
+BQAwaDELMAkGA1UEBhMCREUxDzANBgNVBAgMBkJheWVybjERMA8GA1UEBwwITXVl
+bmNoZW4xDzANBgNVBAoMBm1nbSB0cDEkMCIGCSqGSIb3DQEJARYVamVucy5yZWVz
+ZUBtZ20tdHAuY29tMB4XDTI0MDExNjEyMjI0OVoXDTI1MDExNTEyMjI0OVowaDEL
+MAkGA1UEBhMCREUxDzANBgNVBAgMBkJheWVybjERMA8GA1UEBwwITXVlbmNoZW4x
+DzANBgNVBAoMBm1nbSB0cDEkMCIGCSqGSIb3DQEJARYVamVucy5yZWVzZUBtZ20t
+dHAuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/HBBWBDSrEgd
+wXkSy15V00EaVTyLgc4vh/JcDiGIYZSqmcMwBd+B1u36xbdBf/duEtCUymMNP48O
+MjgFZtR6xn0meuR4NR6Ykn9mYGdU/GhldGuGv9XLAEAkVuTlo0H1QYyBS/6JwKQo
+SsHDkJ3YwDwKcyOt7QtpSadRZjQEN3gDvWoRYjgXTxj2I1ovllmi0zOHsFi5PBIu
+iPWUdJvBrHxpD/XVS9R/qzJpHPu3bjQ6UVRmhiZCUF7H5F/PQNwk+qXvjV0ooBeS
+WWO5hywhk4OP4QEgbYMOSo20YukYX8TJEsum1pwIcQrw7kW4GyKaAycyRsa1fbM3
+tEkj+TiBKwIDAQABo1MwUTAdBgNVHQ4EFgQUfDL/6R33SJodsONCvxKy96AtU18w
+HwYDVR0jBBgwFoAUfDL/6R33SJodsONCvxKy96AtU18wDwYDVR0TAQH/BAUwAwEB
+/zANBgkqhkiG9w0BAQsFAAOCAQEA+PCnvSwKU+bArTCIg5lfrwONbzKkjvPUymDN
+YX3oj1wVEN75hNf0RD7Rr0//ZYT3Rt0G193gjDcH1gbGIYhMLeGGkxEous2l3O+p
+RIQRR+hprjr6HzF8IphaJy1RbDwyGsXyLcyOylPL4cX9IjUdhklHiLZusBq95LSy
+w7hsCOAL2+vn816O7yv+28EWXXbnP2XEUjW36nxcZvR6oTJUplXyHRuuJJTsOxGR
+NuXA3UVgNbkdm1HnoSGpnsGdUKsUFoEmEJkcSdQRwxeH21WzYGOZmKMcvx2gObaS
+P8tafWh5z4Jx+Z7z5WP72Jt44/lnVjaV8aGo0KHXwgqQOtYftQ==
+-----END CERTIFICATE-----
diff --git a/src/test/resources/bayernid-test-enc.key b/src/test/resources/bayernid-test-enc.key
new file mode 100644
index 0000000000000000000000000000000000000000..8d1c8b69c3fce7bea45c73efd06983e3c419a92f
--- /dev/null
+++ b/src/test/resources/bayernid-test-enc.key
@@ -0,0 +1 @@
+