diff --git a/src/main/java/de/ozgcloud/nachrichten/antragsraum/AntragsraumGrpcService.java b/src/main/java/de/ozgcloud/nachrichten/antragsraum/AntragsraumGrpcService.java
index dccea1f32c20d3196e5921f628c8a392483edfc7..e07d5fc75492ba41cc2d3278aa3c975d275b39a2 100644
--- a/src/main/java/de/ozgcloud/nachrichten/antragsraum/AntragsraumGrpcService.java
+++ b/src/main/java/de/ozgcloud/nachrichten/antragsraum/AntragsraumGrpcService.java
@@ -25,18 +25,23 @@ import de.ozgcloud.nachrichten.postfach.antragraum.*;
 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;
-import org.springframework.stereotype.Service;
 
-@Service
+@GrpcService
 @RequiredArgsConstructor
 public class AntragsraumGrpcService extends AntragraumServiceGrpc.AntragraumServiceImplBase {
     private final PersistPostfachNachrichtService postfachNachrichtService;
     private final NachrichtMapper mapper;
+    private final Saml2Verifier verifier;
+    private final Saml2Parser parser;
+    private final Saml2Decrypter decrypter;
 
     @Override
     public void findRueckfragen(GrpcFindRueckfragenRequest request, StreamObserver<GrpcFindRueckfragenResponse> streamObserver) {
-        String postfachId = verifyToken(request.getSamlToken(), request.getPostfachId());
+        verifyToken(request.getSamlToken());
+        String postfachId = decrypter.decryptPostfachId(parser.parse(request.getSamlToken()));
 
         var rueckfragen = postfachNachrichtService.findRueckfragen(postfachId).map(mapper::toGrpc).toList();
         var response = GrpcFindRueckfragenResponse.newBuilder().addAllRueckfrage(rueckfragen).build();
@@ -63,13 +68,9 @@ public class AntragsraumGrpcService extends AntragraumServiceGrpc.AntragraumServ
     }
 
     void verifyToken(String token) {
-        throw new SecurityException("not validated yet");
+        var errors = verifier.verify(token);
+        if (CollectionUtils.isNotEmpty(errors)) {
+            throw new SecurityException("SAML Token verification failed. Errors: %s".formatted(errors));
+        }
     }
-
-    String verifyToken(String token, String postfachId) {
-        verifyToken(token);
-
-        return postfachId;
-    }
-
 }
diff --git a/src/main/java/de/ozgcloud/nachrichten/antragsraum/BayernIdSamlConfiguration.java b/src/main/java/de/ozgcloud/nachrichten/antragsraum/BayernIdSamlConfiguration.java
index 728c4bbc23878e8f7824de5c6b756f38b0336293..429b962d387a9d4671d3e403dc394e215db4a799 100644
--- a/src/main/java/de/ozgcloud/nachrichten/antragsraum/BayernIdSamlConfiguration.java
+++ b/src/main/java/de/ozgcloud/nachrichten/antragsraum/BayernIdSamlConfiguration.java
@@ -22,9 +22,11 @@ package de.ozgcloud.nachrichten.antragsraum;
 
 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.BasicParserPool;
 import net.shibboleth.utilities.java.support.xml.ParserPool;
+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;
@@ -76,10 +78,6 @@ public class BayernIdSamlConfiguration {
     private String entityId;
     @Value("${ozgcloud.bayernid.saml.metadata-uri}")
     private Resource metadataUri;
-    @Value("${ozgcloud.bayernid.saml.signing.private-key-location}")
-    private Resource signingPrivateKey;
-    @Value("${ozgcloud.bayernid.saml.signing.certificate-location}")
-    private Resource signingCertificate;
     @Value("${ozgcloud.bayernid.saml.decryption.private-key-location}")
     private Resource decryptionPrivateKey;
     @Value("${ozgcloud.bayernid.saml.decryption.certificate-location}")
@@ -113,6 +111,37 @@ public class BayernIdSamlConfiguration {
         }
     }
 
+    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;
+    }
+
     @Bean
     Saml2Verifier verifier() {
         return new Saml2Verifier(parser(), trustEngine(), verificationCriteria());
@@ -181,15 +210,16 @@ public class BayernIdSamlConfiguration {
                 }
             }
         } catch (IOException e) {
-            LOG.error("Error reading idp metadata. ", e);
-            throw new Saml2Exception("Error reading medatadata.", 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) {
-        Document document = document(inputStream);
+    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) {
@@ -202,45 +232,6 @@ public class BayernIdSamlConfiguration {
         }
     }
 
-    private Document document(InputStream inputStream) {
-        try {
-            return getParserPool().parse(inputStream);
-        } catch (Exception ex) {
-            throw new Saml2Exception(ex);
-        }
-    }
-
-    static ParserPool getParserPool() throws Exception {
-        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;
-    }
-
     List<Saml2X509Credential> getVerificationCertificates(EntityDescriptor descriptor) {
         IDPSSODescriptor idpssoDescriptor = descriptor.getIDPSSODescriptor(SAMLConstants.SAML20P_NS);
         if (idpssoDescriptor == null) {
diff --git a/src/test/java/de/ozgcloud/nachrichten/antragsraum/AntragsraumGrpcServiceTest.java b/src/test/java/de/ozgcloud/nachrichten/antragsraum/AntragsraumGrpcServiceTest.java
index 27e3f85376469e9657409e9fc6beaa2197d336e8..e9d257fff49c2cdb0f5bcd21edf27cbd8020ce9b 100644
--- a/src/test/java/de/ozgcloud/nachrichten/antragsraum/AntragsraumGrpcServiceTest.java
+++ b/src/test/java/de/ozgcloud/nachrichten/antragsraum/AntragsraumGrpcServiceTest.java
@@ -20,11 +20,104 @@
 
 package de.ozgcloud.nachrichten.antragsraum;
 
+import de.ozgcloud.nachrichten.postfach.PersistPostfachNachrichtService;
+import de.ozgcloud.nachrichten.postfach.PostfachNachricht;
+import de.ozgcloud.nachrichten.postfach.PostfachNachrichtTestFactory;
+import de.ozgcloud.nachrichten.postfach.antragraum.GrpcFindRueckfragenResponse;
+import de.ozgcloud.nachrichten.postfach.antragraum.GrpcRueckfrage;
+import io.grpc.stub.StreamObserver;
 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.*;
 
 class AntragsraumGrpcServiceTest {
+    @Spy
+    @InjectMocks
+    private AntragsraumGrpcService antragsraumGrpcService;
+    @Mock
+    private PersistPostfachNachrichtService postfachNachrichtService;
+    @Mock
+    private NachrichtMapper 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 {
+            @BeforeEach
+            void setup() {
+                when(postfachNachrichtService.findRueckfragen(any())).thenReturn(Stream.of(PostfachNachrichtTestFactory.create()));
+                when(mapper.toGrpc(any(PostfachNachricht.class))).thenReturn(GrpcRueckfrage.getDefaultInstance());
+            }
+
+            @Test
+            void shouldCallMapper() {
+                antragsraumGrpcService.findRueckfragen(GrpcFindRueckfrageRequestTestFactory.create(), streamObserver);
+
+                verify(mapper).toGrpc(any(PostfachNachricht.class));
+            }
+
+            @Test
+            void shouldCallOnNext() {
+                antragsraumGrpcService.findRueckfragen(GrpcFindRueckfrageRequestTestFactory.create(), streamObserver);
+
+                verify(streamObserver).onNext(any(GrpcFindRueckfragenResponse.class));
+            }
+
+            @Test
+            void shouldCallOnCompleted() {
+                antragsraumGrpcService.findRueckfragen(GrpcFindRueckfrageRequestTestFactory.create(), streamObserver);
 
-    @BeforeEach
-    void setUp() {
+                verify(streamObserver).onCompleted();
+            }
+        }
     }
 }
\ No newline at end of file
diff --git a/src/test/java/de/ozgcloud/nachrichten/antragsraum/GrpcFindRueckfrageRequestTestFactory.java b/src/test/java/de/ozgcloud/nachrichten/antragsraum/GrpcFindRueckfrageRequestTestFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..3e60f8a6769c653725d220ca05af6d1b4999c367
--- /dev/null
+++ b/src/test/java/de/ozgcloud/nachrichten/antragsraum/GrpcFindRueckfrageRequestTestFactory.java
@@ -0,0 +1,41 @@
+/*
+ * 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.antragsraum;
+
+import de.ozgcloud.common.test.TestUtils;
+import de.ozgcloud.nachrichten.postfach.antragraum.GrpcFindRueckfragenRequest;
+
+import java.util.UUID;
+
+public class GrpcFindRueckfrageRequestTestFactory {
+    static final String POSTFACH_ID = UUID.randomUUID().toString();
+    static final String SAML_TOKEN = TestUtils.loadTextFile("SamlResponse.xml");
+
+    static GrpcFindRueckfragenRequest create() {
+        return createBuilder().build();
+    }
+
+    static GrpcFindRueckfragenRequest.Builder createBuilder() {
+        return GrpcFindRueckfragenRequest.newBuilder()
+                .setPostfachId(POSTFACH_ID)
+                .setSamlToken(SAML_TOKEN);
+    }
+}