diff --git a/Cargo.lock b/Cargo.lock
index 79046a59..9a809bd9 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -77,7 +77,9 @@ dependencies = [
  "futures",
  "http-api-problem",
  "hyper 1.4.1",
+ "identity_core",
  "identity_credential",
+ "identity_did",
  "jsonwebtoken",
  "lazy_static",
  "mime",
@@ -94,7 +96,7 @@ dependencies = [
  "siopv2",
  "tokio",
  "tower",
- "tower-http 0.5.2",
+ "tower-http 0.6.1",
  "tracing",
  "tracing-test",
  "url",
@@ -127,6 +129,7 @@ version = "0.1.0"
 dependencies = [
  "agent_event_publisher_http",
  "agent_holder",
+ "agent_identity",
  "agent_issuance",
  "agent_shared",
  "agent_store",
@@ -7790,15 +7793,14 @@ dependencies = [
 
 [[package]]
 name = "tower-http"
-version = "0.5.2"
+version = "0.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5"
+checksum = "8437150ab6bbc8c5f0f519e3d5ed4aa883a83dd4cdd3d1b21f9482936046cb97"
 dependencies = [
  "bitflags 2.5.0",
  "bytes",
  "http 1.1.0",
  "http-body 1.0.0",
- "http-body-util",
  "pin-project-lite",
  "tower-layer",
  "tower-service",
@@ -7807,9 +7809,9 @@ dependencies = [
 
 [[package]]
 name = "tower-layer"
-version = "0.3.2"
+version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0"
+checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
 
 [[package]]
 name = "tower-service"
diff --git a/Cargo.toml b/Cargo.toml
index 17bc2ae4..0c2a1829 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -38,6 +38,7 @@ identity_credential = { version = "1.3", default-features = false, features = [
     "presentation",
     "domain-linkage",
 ] }
+identity_did = { version = "1.3" }
 identity_iota = { version = "1.3" }
 identity_verification = { version = "1.3", default-features = false }
 jsonwebtoken = "9.3"
@@ -53,7 +54,7 @@ serde_yaml = "0.9"
 thiserror = "1.0"
 tokio = { version = "1", features = ["full"] }
 tower = { version = "0.4" }
-tower-http = { version = "0.5", features = ["cors", "trace"] }
+tower-http = { version = "0.6", features = ["cors", "trace"] }
 tracing = { version = "0.1" }
 tracing-subscriber = { version = "0.3", features = ["json", "env-filter"] }
 tracing-test = { version = "0.2" }
diff --git a/agent_api_rest/Cargo.toml b/agent_api_rest/Cargo.toml
index 9390adbf..d0445b6d 100644
--- a/agent_api_rest/Cargo.toml
+++ b/agent_api_rest/Cargo.toml
@@ -17,7 +17,9 @@ axum-macros = "0.4"
 did_manager.workspace = true
 http-api-problem = "0.57"
 hyper = { version = "1.2" }
+identity_core.workspace = true
 identity_credential.workspace = true
+identity_did.workspace = true
 oid4vc-core.workspace = true
 oid4vci.workspace = true
 oid4vp.workspace = true
diff --git a/agent_api_rest/postman/ssi-agent.postman_collection.json b/agent_api_rest/postman/ssi-agent.postman_collection.json
index 08e9731a..cdef0a25 100644
--- a/agent_api_rest/postman/ssi-agent.postman_collection.json
+++ b/agent_api_rest/postman/ssi-agent.postman_collection.json
@@ -26,6 +26,16 @@
 								"type": "text/javascript",
 								"packages": {}
 							}
+						},
+						{
+							"listen": "prerequest",
+							"script": {
+								"exec": [
+									""
+								],
+								"type": "text/javascript",
+								"packages": {}
+							}
 						}
 					],
 					"request": {
@@ -912,6 +922,13 @@
 								"type": "text/javascript",
 								"packages": {}
 							}
+						},
+						{
+							"listen": "prerequest",
+							"script": {
+								"exec": [],
+								"type": "text/javascript"
+							}
 						}
 					],
 					"request": {
@@ -971,6 +988,13 @@
 								"type": "text/javascript",
 								"packages": {}
 							}
+						},
+						{
+							"listen": "prerequest",
+							"script": {
+								"exec": [],
+								"type": "text/javascript"
+							}
 						}
 					],
 					"request": {
@@ -1043,6 +1067,308 @@
 		{
 			"name": "Identity",
 			"item": [
+				{
+					"name": "Create a new Connection",
+					"event": [
+						{
+							"listen": "test",
+							"script": {
+								"exec": [
+									""
+								],
+								"type": "text/javascript",
+								"packages": {}
+							}
+						},
+						{
+							"listen": "prerequest",
+							"script": {
+								"exec": [
+									""
+								],
+								"type": "text/javascript",
+								"packages": {}
+							}
+						}
+					],
+					"request": {
+						"method": "POST",
+						"header": [],
+						"body": {
+							"mode": "raw",
+							"raw": "{\n    \"alias\": \"My Connection\",\n    \"domain\": \"http://example.org\",\n    \"dids\": [\"did:example:123\"],\n    \"credentialOfferEndpoint\": \"{{HOST}}/openid4vci/offers\"\n}",
+							"options": {
+								"raw": {
+									"language": "json"
+								}
+							}
+						},
+						"url": {
+							"raw": "{{HOST}}/v0/connections",
+							"host": [
+								"{{HOST}}"
+							],
+							"path": [
+								"v0",
+								"connections"
+							]
+						}
+					},
+					"response": []
+				},
+				{
+					"name": "List all Connections",
+					"event": [
+						{
+							"listen": "test",
+							"script": {
+								"exec": [
+									"const jsonData = JSON.parse(responseBody);",
+									"",
+									"if (jsonData && typeof jsonData === 'object') {",
+									"    const connectionId = Object.keys(jsonData)[0];",
+									"",
+									"    if (connectionId) {",
+									"        pm.collectionVariables.set(\"CONNECTION_ID\", connectionId);",
+									"    }",
+									"}"
+								],
+								"type": "text/javascript",
+								"packages": {}
+							}
+						},
+						{
+							"listen": "prerequest",
+							"script": {
+								"exec": [
+									""
+								],
+								"type": "text/javascript",
+								"packages": {}
+							}
+						}
+					],
+					"request": {
+						"method": "GET",
+						"header": [],
+						"url": {
+							"raw": "{{HOST}}/v0/connections",
+							"host": [
+								"{{HOST}}"
+							],
+							"path": [
+								"v0",
+								"connections"
+							]
+						}
+					},
+					"response": []
+				},
+				{
+					"name": "Query Connection by Alias",
+					"event": [
+						{
+							"listen": "test",
+							"script": {
+								"exec": [
+									"const jsonData = JSON.parse(responseBody);",
+									"",
+									"if (jsonData && typeof jsonData === 'object') {",
+									"    const connectionId = Object.keys(jsonData)[0];",
+									"",
+									"    if (connectionId) {",
+									"        pm.collectionVariables.set(\"CONNECTION_ID\", connectionId);",
+									"    }",
+									"}"
+								],
+								"type": "text/javascript"
+							}
+						}
+					],
+					"request": {
+						"method": "GET",
+						"header": [],
+						"url": {
+							"raw": "{{HOST}}/v0/connections?alias=My+Connection",
+							"host": [
+								"{{HOST}}"
+							],
+							"path": [
+								"v0",
+								"connections"
+							],
+							"query": [
+								{
+									"key": "alias",
+									"value": "My+Connection"
+								}
+							]
+						}
+					},
+					"response": []
+				},
+				{
+					"name": "Query Connection by DID",
+					"event": [
+						{
+							"listen": "test",
+							"script": {
+								"exec": [
+									"const jsonData = JSON.parse(responseBody);",
+									"",
+									"if (jsonData && typeof jsonData === 'object') {",
+									"    const connectionId = Object.keys(jsonData)[0];",
+									"",
+									"    if (connectionId) {",
+									"        pm.collectionVariables.set(\"CONNECTION_ID\", connectionId);",
+									"    }",
+									"}"
+								],
+								"type": "text/javascript",
+								"packages": {}
+							}
+						},
+						{
+							"listen": "prerequest",
+							"script": {
+								"exec": [
+									""
+								],
+								"type": "text/javascript",
+								"packages": {}
+							}
+						}
+					],
+					"request": {
+						"method": "GET",
+						"header": [],
+						"url": {
+							"raw": "{{HOST}}/v0/connections?did=did:example:123",
+							"host": [
+								"{{HOST}}"
+							],
+							"path": [
+								"v0",
+								"connections"
+							],
+							"query": [
+								{
+									"key": "did",
+									"value": "did:example:123"
+								}
+							]
+						}
+					},
+					"response": []
+				},
+				{
+					"name": "Query Connection by Domain",
+					"event": [
+						{
+							"listen": "test",
+							"script": {
+								"exec": [
+									"const jsonData = JSON.parse(responseBody);",
+									"",
+									"if (jsonData && typeof jsonData === 'object') {",
+									"    const connectionId = Object.keys(jsonData)[0];",
+									"",
+									"    if (connectionId) {",
+									"        pm.collectionVariables.set(\"CONNECTION_ID\", connectionId);",
+									"    }",
+									"}"
+								],
+								"type": "text/javascript",
+								"packages": {}
+							}
+						},
+						{
+							"listen": "prerequest",
+							"script": {
+								"exec": [
+									""
+								],
+								"type": "text/javascript",
+								"packages": {}
+							}
+						}
+					],
+					"request": {
+						"method": "GET",
+						"header": [],
+						"url": {
+							"raw": "{{HOST}}/v0/connections?domain=http://example.org",
+							"host": [
+								"{{HOST}}"
+							],
+							"path": [
+								"v0",
+								"connections"
+							],
+							"query": [
+								{
+									"key": "domain",
+									"value": "http://example.org"
+								}
+							]
+						}
+					},
+					"response": []
+				},
+				{
+					"name": "Connection by ID",
+					"event": [
+						{
+							"listen": "test",
+							"script": {
+								"exec": [
+									""
+								],
+								"type": "text/javascript",
+								"packages": {}
+							}
+						},
+						{
+							"listen": "prerequest",
+							"script": {
+								"exec": [
+									""
+								],
+								"type": "text/javascript",
+								"packages": {}
+							}
+						}
+					],
+					"protocolProfileBehavior": {
+						"disableBodyPruning": true
+					},
+					"request": {
+						"method": "GET",
+						"header": [],
+						"body": {
+							"mode": "formdata",
+							"formdata": [
+								{
+									"key": "test",
+									"value": "test-value",
+									"type": "text"
+								}
+							]
+						},
+						"url": {
+							"raw": "{{HOST}}/v0/connections/{{CONNECTION_ID}}",
+							"host": [
+								"{{HOST}}"
+							],
+							"path": [
+								"v0",
+								"connections",
+								"{{CONNECTION_ID}}"
+							]
+						}
+					},
+					"response": []
+				},
 				{
 					"name": "Create new Linked Verifiable Presentation Service",
 					"request": {
@@ -1237,6 +1563,11 @@
 			"key": "PRESENTATION_ID",
 			"value": "INITIAL_VALUE",
 			"type": "string"
+		},
+		{
+			"key": "CONNECTION_ID",
+			"value": "INITIAL_VALUE",
+			"type": "string"
 		}
 	]
 }
\ No newline at end of file
diff --git a/agent_api_rest/src/identity/connections/mod.rs b/agent_api_rest/src/identity/connections/mod.rs
new file mode 100644
index 00000000..81bdda12
--- /dev/null
+++ b/agent_api_rest/src/identity/connections/mod.rs
@@ -0,0 +1,128 @@
+use crate::API_VERSION;
+use agent_identity::{connection::command::ConnectionCommand, state::IdentityState};
+use agent_shared::handlers::{command_handler, query_handler};
+use axum::{
+    extract::{Path, State},
+    response::{IntoResponse, Response},
+    Form, Json,
+};
+use hyper::{header, StatusCode};
+use identity_core::common::Url;
+use identity_did::DIDUrl;
+use serde::{Deserialize, Serialize};
+use serde_json::json;
+use std::collections::HashMap;
+use tracing::info;
+
+#[derive(Deserialize, Serialize)]
+#[serde(rename_all = "camelCase")]
+pub struct PostConnectionsEndpointRequest {
+    #[serde(default)]
+    pub alias: Option<String>,
+    #[serde(default)]
+    pub domain: Option<Url>,
+    #[serde(default)]
+    pub dids: Vec<DIDUrl>,
+    #[serde(default)]
+    pub credential_offer_endpoint: Option<Url>,
+}
+
+#[axum_macros::debug_handler]
+pub(crate) async fn post_connections(
+    State(state): State<IdentityState>,
+    Json(payload): Json<serde_json::Value>,
+) -> Response {
+    // TODO: implement a body consuming extractor that logs the body so that we don't need to log it in each handler.
+    // This way we can also immediately deserialize the body here into a typed struct instead of deserializing into a
+    // `serde_json::Value` first. See:
+    // https://github.com/tokio-rs/axum/blob/main/examples/consume-body-in-extractor-or-middleware/src/main.rs
+    info!("Request Body: {}", payload);
+
+    let Ok(PostConnectionsEndpointRequest {
+        alias,
+        domain,
+        dids,
+        credential_offer_endpoint,
+    }) = serde_json::from_value(payload)
+    else {
+        return (StatusCode::BAD_REQUEST, "invalid payload").into_response();
+    };
+
+    let connection_id = uuid::Uuid::new_v4().to_string();
+
+    let command = ConnectionCommand::AddConnection {
+        connection_id: connection_id.clone(),
+        alias,
+        domain,
+        dids,
+        credential_offer_endpoint,
+    };
+
+    if command_handler(&connection_id, &state.command.connection, command)
+        .await
+        .is_err()
+    {
+        return StatusCode::INTERNAL_SERVER_ERROR.into_response();
+    }
+
+    // Return the connection.
+    match query_handler(&connection_id, &state.query.connection).await {
+        Ok(Some(connection)) => (
+            StatusCode::CREATED,
+            [(header::LOCATION, &format!("{API_VERSION}/connections/{connection_id}"))],
+            Json(connection),
+        )
+            .into_response(),
+        _ => StatusCode::INTERNAL_SERVER_ERROR.into_response(),
+    }
+}
+
+#[derive(Deserialize, Serialize)]
+#[serde(rename_all = "camelCase")]
+pub struct GetConnectionsEndpointRequest {
+    #[serde(default)]
+    pub alias: Option<String>,
+    #[serde(default)]
+    pub domain: Option<Url>,
+    #[serde(default)]
+    pub did: Option<DIDUrl>,
+}
+
+#[axum_macros::debug_handler]
+pub(crate) async fn get_connections(
+    State(state): State<IdentityState>,
+    Form(GetConnectionsEndpointRequest { alias, domain, did }): Form<GetConnectionsEndpointRequest>,
+) -> Response {
+    info!("Request Params - alias: {alias:?}, domain: {domain:?}, did: {did:?}");
+
+    match query_handler("all_connections", &state.query.all_connections).await {
+        Ok(Some(all_connections_view)) => {
+            let filtered_connections: HashMap<_, _> = all_connections_view
+                .connections
+                .into_iter()
+                .filter(|(_, connection)| {
+                    alias
+                        .as_ref()
+                        .map_or(true, |alias| connection.alias.as_ref() == Some(alias))
+                        && domain
+                            .as_ref()
+                            .map_or(true, |domain| connection.domain.as_ref() == Some(domain))
+                        && did.as_ref().map_or(true, |did| connection.dids.contains(did))
+                })
+                .collect();
+
+            (StatusCode::OK, Json(filtered_connections)).into_response()
+        }
+        Ok(None) => (StatusCode::OK, Json(json!({}))).into_response(),
+        _ => StatusCode::INTERNAL_SERVER_ERROR.into_response(),
+    }
+}
+
+#[axum_macros::debug_handler]
+pub(crate) async fn get_connection(State(state): State<IdentityState>, Path(connection_id): Path<String>) -> Response {
+    match query_handler(&connection_id, &state.query.connection).await {
+        Ok(Some(connection_view)) => (StatusCode::OK, Json(connection_view)).into_response(),
+        Ok(None) => StatusCode::NOT_FOUND.into_response(),
+        _ => StatusCode::INTERNAL_SERVER_ERROR.into_response(),
+    }
+}
diff --git a/agent_api_rest/src/identity/mod.rs b/agent_api_rest/src/identity/mod.rs
index c6f6f991..a10a6e28 100644
--- a/agent_api_rest/src/identity/mod.rs
+++ b/agent_api_rest/src/identity/mod.rs
@@ -1,3 +1,4 @@
+pub mod connections;
 pub mod services;
 pub mod well_known;
 
@@ -6,6 +7,7 @@ use axum::{
     routing::{get, post},
     Router,
 };
+use connections::{get_connection, get_connections, post_connections};
 use services::{linked_vp::linked_vp, service, services};
 use well_known::{did::did, did_configuration::did_configuration};
 
@@ -16,6 +18,8 @@ pub fn router(identity_state: IdentityState) -> Router {
         .nest(
             API_VERSION,
             Router::new()
+                .route("/connections", get(get_connections).post(post_connections))
+                .route("/connections/:connection_id", get(get_connection))
                 .route("/services", get(services))
                 .route("/services/:service_id", get(service))
                 .route("/services/linked-vp", post(linked_vp)),
diff --git a/agent_api_rest/src/verification/relying_party/redirect.rs b/agent_api_rest/src/verification/relying_party/redirect.rs
index af050e6d..6a13e3ed 100644
--- a/agent_api_rest/src/verification/relying_party/redirect.rs
+++ b/agent_api_rest/src/verification/relying_party/redirect.rs
@@ -1,7 +1,8 @@
 use agent_shared::handlers::{command_handler, query_handler};
 use agent_verification::{
-    authorization_request::views::AuthorizationRequestView, connection::command::ConnectionCommand,
-    generic_oid4vc::GenericAuthorizationResponse, state::VerificationState,
+    authorization_request::{command::AuthorizationRequestCommand, views::AuthorizationRequestView},
+    generic_oid4vc::GenericAuthorizationResponse,
+    state::VerificationState,
 };
 use axum::{
     extract::State,
@@ -35,17 +36,19 @@ pub(crate) async fn redirect(
         _ => return StatusCode::INTERNAL_SERVER_ERROR.into_response(),
     };
 
-    let connection_id = authorization_request.client_id();
-
-    let command = ConnectionCommand::VerifyAuthorizationResponse {
+    let command = AuthorizationRequestCommand::VerifyAuthorizationResponse {
         authorization_request,
         authorization_response,
     };
 
     // Verify the authorization response.
-    if command_handler(&connection_id, &verification_state.command.connection, command)
-        .await
-        .is_err()
+    if command_handler(
+        &authorization_request_id,
+        &verification_state.command.authorization_request,
+        command,
+    )
+    .await
+    .is_err()
     {
         return StatusCode::INTERNAL_SERVER_ERROR.into_response();
     }
@@ -153,7 +156,9 @@ pub mod tests {
         set_config().enable_event_publisher_http();
         set_config().set_event_publisher_http_target_url(target_url.clone());
         set_config().set_event_publisher_http_target_events(Events {
-            connection: vec![agent_shared::config::ConnectionEvent::SIOPv2AuthorizationResponseVerified],
+            authorization_request: vec![
+                agent_shared::config::AuthorizationRequestEvent::SIOPv2AuthorizationResponseVerified,
+            ],
             ..Default::default()
         });
 
diff --git a/agent_application/docker/db/init.sql b/agent_application/docker/db/init.sql
index fd70d4df..49be6ff2 100644
--- a/agent_application/docker/db/init.sql
+++ b/agent_application/docker/db/init.sql
@@ -10,6 +10,23 @@ CREATE TABLE events
     PRIMARY KEY (aggregate_type, aggregate_id, sequence)
 );
 
+CREATE TABLE connection
+(
+    view_id           text                        NOT NULL,
+    version           bigint CHECK (version >= 0) NOT NULL,
+    payload           json                        NOT NULL,
+    PRIMARY KEY (view_id)
+);
+
+
+CREATE TABLE all_connections
+(
+    view_id           text                        NOT NULL,
+    version           bigint CHECK (version >= 0) NOT NULL,
+    payload           json                        NOT NULL,
+    PRIMARY KEY (view_id)
+);
+
 CREATE TABLE document
 (
     view_id           text                        NOT NULL,
@@ -154,13 +171,5 @@ CREATE TABLE all_authorization_requests
     PRIMARY KEY (view_id)
 );
 
-CREATE TABLE connection
-(
-    view_id           text                        NOT NULL,
-    version           bigint CHECK (version >= 0) NOT NULL,
-    payload           json                        NOT NULL,
-    PRIMARY KEY (view_id)
-);
-
 CREATE USER demo_user WITH ENCRYPTED PASSWORD 'demo_pass';
 GRANT ALL PRIVILEGES ON DATABASE postgres TO demo_user;
diff --git a/agent_event_publisher_http/Cargo.toml b/agent_event_publisher_http/Cargo.toml
index a9811bf7..63628490 100644
--- a/agent_event_publisher_http/Cargo.toml
+++ b/agent_event_publisher_http/Cargo.toml
@@ -6,6 +6,7 @@ rust-version.workspace = true
 
 [dependencies]
 agent_holder = { path = "../agent_holder" }
+agent_identity = { path = "../agent_identity" }
 agent_issuance = { path = "../agent_issuance" }
 agent_shared = { path = "../agent_shared" }
 agent_store = { path = "../agent_store" }
diff --git a/agent_event_publisher_http/README.md b/agent_event_publisher_http/README.md
index cdc0a663..c83c7253 100644
--- a/agent_event_publisher_http/README.md
+++ b/agent_event_publisher_http/README.md
@@ -21,6 +21,12 @@ event_publishers:
 
 ### Available events
 
+#### `connection`
+
+```
+ConnectionAdded
+```
+
 #### `document`
 
 ```
@@ -89,11 +95,6 @@ CredentialOfferRejected
 AuthorizationRequestCreated
 FormUrlEncodedAuthorizationRequestCreated
 AuthorizationRequestObjectSigned
-```
-
-#### `connection`
-
-```
 SIOPv2AuthorizationResponseVerified
 OID4VPAuthorizationResponseVerified
 ```
diff --git a/agent_event_publisher_http/src/lib.rs b/agent_event_publisher_http/src/lib.rs
index e18be595..0d9b4190 100644
--- a/agent_event_publisher_http/src/lib.rs
+++ b/agent_event_publisher_http/src/lib.rs
@@ -1,3 +1,4 @@
+use agent_identity::connection::aggregate::Connection;
 use agent_issuance::{
     credential::aggregate::Credential, offer::aggregate::Offer, server_config::aggregate::ServerConfig,
 };
@@ -6,7 +7,7 @@ use agent_store::{
     AuthorizationRequestEventPublisher, ConnectionEventPublisher, CredentialEventPublisher, EventPublisher,
     HolderCredentialEventPublisher, OfferEventPublisher, ReceivedOfferEventPublisher, ServerConfigEventPublisher,
 };
-use agent_verification::{authorization_request::aggregate::AuthorizationRequest, connection::aggregate::Connection};
+use agent_verification::authorization_request::aggregate::AuthorizationRequest;
 use async_trait::async_trait;
 use cqrs_es::{Aggregate, DomainEvent, EventEnvelope, Query};
 use serde::Deserialize;
@@ -17,6 +18,9 @@ use tracing::info;
 #[skip_serializing_none]
 #[derive(Debug, Deserialize, Default)]
 pub struct EventPublisherHttp {
+    // Identity
+    pub connection: Option<AggregateEventPublisherHttp<Connection>>,
+
     // Issuance
     pub server_config: Option<AggregateEventPublisherHttp<ServerConfig>>,
     pub credential: Option<AggregateEventPublisherHttp<Credential>>,
@@ -27,7 +31,6 @@ pub struct EventPublisherHttp {
     pub received_offer: Option<AggregateEventPublisherHttp<agent_holder::offer::aggregate::Offer>>,
 
     // Verification
-    pub connection: Option<AggregateEventPublisherHttp<Connection>>,
     pub authorization_request: Option<AggregateEventPublisherHttp<AuthorizationRequest>>,
 }
 
@@ -40,6 +43,18 @@ impl EventPublisherHttp {
             return Ok(EventPublisherHttp::default());
         }
 
+        let connection = (!event_publisher_http.events.connection.is_empty()).then(|| {
+            AggregateEventPublisherHttp::<Connection>::new(
+                event_publisher_http.target_url.clone(),
+                event_publisher_http
+                    .events
+                    .connection
+                    .iter()
+                    .map(ToString::to_string)
+                    .collect(),
+            )
+        });
+
         let server_config = (!event_publisher_http.events.server_config.is_empty()).then(|| {
             AggregateEventPublisherHttp::<ServerConfig>::new(
                 event_publisher_http.target_url.clone(),
@@ -100,18 +115,6 @@ impl EventPublisherHttp {
             )
         });
 
-        let connection = (!event_publisher_http.events.connection.is_empty()).then(|| {
-            AggregateEventPublisherHttp::<Connection>::new(
-                event_publisher_http.target_url.clone(),
-                event_publisher_http
-                    .events
-                    .connection
-                    .iter()
-                    .map(ToString::to_string)
-                    .collect(),
-            )
-        });
-
         let authorization_request = (!event_publisher_http.events.authorization_request.is_empty()).then(|| {
             AggregateEventPublisherHttp::<AuthorizationRequest>::new(
                 event_publisher_http.target_url.clone(),
@@ -141,6 +144,12 @@ impl EventPublisherHttp {
 }
 
 impl EventPublisher for EventPublisherHttp {
+    fn connection(&mut self) -> Option<ConnectionEventPublisher> {
+        self.connection
+            .take()
+            .map(|publisher| Box::new(publisher) as ConnectionEventPublisher)
+    }
+
     fn server_config(&mut self) -> Option<ServerConfigEventPublisher> {
         self.server_config
             .take()
@@ -171,12 +180,6 @@ impl EventPublisher for EventPublisherHttp {
             .map(|publisher| Box::new(publisher) as ReceivedOfferEventPublisher)
     }
 
-    fn connection(&mut self) -> Option<ConnectionEventPublisher> {
-        self.connection
-            .take()
-            .map(|publisher| Box::new(publisher) as ConnectionEventPublisher)
-    }
-
     fn authorization_request(&mut self) -> Option<AuthorizationRequestEventPublisher> {
         self.authorization_request
             .take()
diff --git a/agent_identity/Cargo.toml b/agent_identity/Cargo.toml
index 2436e921..37156612 100644
--- a/agent_identity/Cargo.toml
+++ b/agent_identity/Cargo.toml
@@ -15,7 +15,7 @@ derivative = "2.2"
 did_manager.workspace = true
 identity_credential.workspace = true
 identity_core.workspace = true
-identity_did = { version = "1.3" }
+identity_did.workspace = true
 identity_document = { version = "1.3" }
 jsonwebtoken.workspace = true
 oid4vc-core.workspace = true
diff --git a/agent_identity/src/connection/README.md b/agent_identity/src/connection/README.md
new file mode 100644
index 00000000..2e126ff8
--- /dev/null
+++ b/agent_identity/src/connection/README.md
@@ -0,0 +1,9 @@
+# Connection
+
+This aggregate holds everything related to a connection:
+- connection_id
+- domain
+- dids (list of associated DIDs)
+- first_interacted
+- last_interacted
+- credential_offer_endpoint 
diff --git a/agent_identity/src/connection/aggregate.rs b/agent_identity/src/connection/aggregate.rs
new file mode 100644
index 00000000..6ad490e3
--- /dev/null
+++ b/agent_identity/src/connection/aggregate.rs
@@ -0,0 +1,157 @@
+use async_trait::async_trait;
+use cqrs_es::Aggregate;
+use identity_core::common::{Timestamp, Url};
+use identity_did::DIDUrl;
+use serde::{Deserialize, Serialize};
+use std::sync::Arc;
+use tracing::info;
+
+use crate::services::IdentityServices;
+
+use super::{command::ConnectionCommand, error::ConnectionError, event::ConnectionEvent};
+
+#[derive(Debug, Clone, Serialize, Deserialize, Default)]
+pub struct Connection {
+    pub connection_id: String,
+    pub alias: Option<String>,
+    pub domain: Option<Url>,
+    pub dids: Vec<DIDUrl>,
+    pub first_interacted: Option<Timestamp>,
+    pub last_interacted: Option<Timestamp>,
+
+    // TODO: How do we want to make distinction between issuer, holder, and verifier capabilities of the `Connection`?
+    pub credential_offer_endpoint: Option<Url>,
+    // pub issuer_options: Option<IssuerOptions>,
+    // pub holder_options: Option<HolderOptions>,
+    // pub verifier_options: Option<VerifierOptions>,
+}
+
+#[async_trait]
+impl Aggregate for Connection {
+    type Command = ConnectionCommand;
+    type Event = ConnectionEvent;
+    type Error = ConnectionError;
+    type Services = Arc<IdentityServices>;
+
+    fn aggregate_type() -> String {
+        "connection".to_string()
+    }
+
+    async fn handle(
+        &self,
+        command: Self::Command,
+        _services: &Self::Services,
+    ) -> Result<Vec<Self::Event>, Self::Error> {
+        use ConnectionCommand::*;
+        use ConnectionEvent::*;
+
+        info!("Handling command: {:?}", command);
+
+        match command {
+            AddConnection {
+                connection_id,
+                alias,
+                domain,
+                dids,
+                credential_offer_endpoint,
+            } => Ok(vec![ConnectionAdded {
+                connection_id,
+                alias,
+                domain,
+                dids,
+                credential_offer_endpoint,
+            }]),
+        }
+    }
+
+    fn apply(&mut self, event: Self::Event) {
+        use ConnectionEvent::*;
+
+        info!("Applying event: {:?}", event);
+
+        match event {
+            ConnectionAdded {
+                connection_id,
+                alias,
+                domain,
+                dids,
+                credential_offer_endpoint,
+            } => {
+                self.connection_id = connection_id;
+                self.alias = alias;
+                self.domain = domain;
+                self.dids = dids;
+                self.credential_offer_endpoint = credential_offer_endpoint;
+            }
+        }
+    }
+}
+
+#[cfg(test)]
+pub mod document_tests {
+    use super::test_utils::*;
+    use super::*;
+    use cqrs_es::test::TestFramework;
+    use rstest::rstest;
+
+    type ConnectionTestFramework = TestFramework<Connection>;
+
+    #[rstest]
+    #[serial_test::serial]
+    async fn test_add_connection(
+        connection_id: String,
+        alias: String,
+        domain: Url,
+        dids: Vec<DIDUrl>,
+        credential_offer_endpoint: Url,
+    ) {
+        ConnectionTestFramework::with(IdentityServices::default())
+            .given_no_previous_events()
+            .when(ConnectionCommand::AddConnection {
+                connection_id: connection_id.clone(),
+                alias: Some(alias.clone()),
+                domain: Some(domain.clone()),
+                dids: dids.clone(),
+                credential_offer_endpoint: Some(credential_offer_endpoint.clone()),
+            })
+            .then_expect_events(vec![ConnectionEvent::ConnectionAdded {
+                connection_id: connection_id.clone(),
+                alias: Some(alias),
+                domain: Some(domain.clone()),
+                dids: dids.clone(),
+                credential_offer_endpoint: Some(credential_offer_endpoint.clone()),
+            }])
+    }
+}
+
+#[cfg(feature = "test_utils")]
+pub mod test_utils {
+    use identity_core::common::Url;
+    use identity_did::DIDUrl;
+    use rstest::fixture;
+
+    #[fixture]
+    pub fn connection_id() -> String {
+        "connection_id".to_string()
+    }
+
+    #[fixture]
+    pub fn alias() -> String {
+        "My Connection".to_string()
+    }
+
+    #[fixture]
+    pub fn domain() -> Url {
+        "http://example.org".parse().unwrap()
+    }
+
+    #[fixture]
+    pub fn dids() -> Vec<DIDUrl> {
+        vec!["did:example:123".parse().unwrap()]
+    }
+
+    #[fixture]
+    pub fn credential_offer_endpoint() -> Url {
+        "http://example.org/openid4vci/offers".parse().unwrap()
+    }
+}
diff --git a/agent_identity/src/connection/command.rs b/agent_identity/src/connection/command.rs
new file mode 100644
index 00000000..d6b0ee26
--- /dev/null
+++ b/agent_identity/src/connection/command.rs
@@ -0,0 +1,15 @@
+use identity_core::common::Url;
+use identity_did::DIDUrl;
+use serde::Deserialize;
+
+#[derive(Debug, Deserialize)]
+#[serde(untagged)]
+pub enum ConnectionCommand {
+    AddConnection {
+        connection_id: String,
+        alias: Option<String>,
+        domain: Option<Url>,
+        dids: Vec<DIDUrl>,
+        credential_offer_endpoint: Option<Url>,
+    },
+}
diff --git a/agent_identity/src/connection/error.rs b/agent_identity/src/connection/error.rs
new file mode 100644
index 00000000..fa46229b
--- /dev/null
+++ b/agent_identity/src/connection/error.rs
@@ -0,0 +1,4 @@
+use thiserror::Error;
+
+#[derive(Error, Debug)]
+pub enum ConnectionError {}
diff --git a/agent_verification/src/connection/event.rs b/agent_identity/src/connection/event.rs
similarity index 53%
rename from agent_verification/src/connection/event.rs
rename to agent_identity/src/connection/event.rs
index 0f08becc..558ea6f2 100644
--- a/agent_verification/src/connection/event.rs
+++ b/agent_identity/src/connection/event.rs
@@ -1,10 +1,17 @@
 use cqrs_es::DomainEvent;
+use identity_core::common::Url;
+use identity_did::DIDUrl;
 use serde::{Deserialize, Serialize};
 
 #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
 pub enum ConnectionEvent {
-    SIOPv2AuthorizationResponseVerified { id_token: String, state: Option<String> },
-    OID4VPAuthorizationResponseVerified { vp_token: String, state: Option<String> },
+    ConnectionAdded {
+        connection_id: String,
+        alias: Option<String>,
+        domain: Option<Url>,
+        dids: Vec<DIDUrl>,
+        credential_offer_endpoint: Option<Url>,
+    },
 }
 
 impl DomainEvent for ConnectionEvent {
@@ -12,8 +19,7 @@ impl DomainEvent for ConnectionEvent {
         use ConnectionEvent::*;
 
         let event_type: &str = match self {
-            SIOPv2AuthorizationResponseVerified { .. } => "SIOPv2AuthorizationResponseVerified",
-            OID4VPAuthorizationResponseVerified { .. } => "OID4VPAuthorizationResponseVerified",
+            ConnectionAdded { .. } => "ConnectionAdded",
         };
         event_type.to_string()
     }
diff --git a/agent_verification/src/connection/mod.rs b/agent_identity/src/connection/mod.rs
similarity index 79%
rename from agent_verification/src/connection/mod.rs
rename to agent_identity/src/connection/mod.rs
index 7d8a943f..7cbc4ed7 100644
--- a/agent_verification/src/connection/mod.rs
+++ b/agent_identity/src/connection/mod.rs
@@ -2,4 +2,4 @@ pub mod aggregate;
 pub mod command;
 pub mod error;
 pub mod event;
-pub mod queries;
+pub mod views;
diff --git a/agent_identity/src/connection/views/all_connections.rs b/agent_identity/src/connection/views/all_connections.rs
new file mode 100644
index 00000000..4e623022
--- /dev/null
+++ b/agent_identity/src/connection/views/all_connections.rs
@@ -0,0 +1,23 @@
+use super::ConnectionView;
+use crate::connection::views::Connection;
+use cqrs_es::{EventEnvelope, View};
+use serde::{Deserialize, Serialize};
+use std::collections::HashMap;
+
+#[derive(Debug, Default, Serialize, Deserialize, Clone)]
+pub struct AllConnectionsView {
+    #[serde(flatten)]
+    pub connections: HashMap<String, ConnectionView>,
+}
+
+impl View<Connection> for AllConnectionsView {
+    fn update(&mut self, event: &EventEnvelope<Connection>) {
+        self.connections
+            // Get the entry for the aggregate_id
+            .entry(event.aggregate_id.clone())
+            // or insert a new one if it doesn't exist
+            .or_default()
+            // update the view with the event
+            .update(event);
+    }
+}
diff --git a/agent_identity/src/connection/views/mod.rs b/agent_identity/src/connection/views/mod.rs
new file mode 100644
index 00000000..c951dd09
--- /dev/null
+++ b/agent_identity/src/connection/views/mod.rs
@@ -0,0 +1,29 @@
+pub mod all_connections;
+
+use super::event::ConnectionEvent;
+use crate::connection::aggregate::Connection;
+use cqrs_es::{EventEnvelope, View};
+
+pub type ConnectionView = Connection;
+
+impl View<Connection> for Connection {
+    fn update(&mut self, event: &EventEnvelope<Connection>) {
+        use ConnectionEvent::*;
+
+        match &event.payload {
+            ConnectionAdded {
+                connection_id,
+                alias,
+                domain,
+                dids,
+                credential_offer_endpoint,
+            } => {
+                self.connection_id.clone_from(connection_id);
+                self.alias.clone_from(alias);
+                self.domain.clone_from(domain);
+                self.dids.clone_from(dids);
+                self.credential_offer_endpoint.clone_from(credential_offer_endpoint);
+            }
+        }
+    }
+}
diff --git a/agent_identity/src/lib.rs b/agent_identity/src/lib.rs
index f2de33fd..cdf392f3 100644
--- a/agent_identity/src/lib.rs
+++ b/agent_identity/src/lib.rs
@@ -1,4 +1,5 @@
 // Aggregates
+pub mod connection;
 pub mod document;
 pub mod service;
 
diff --git a/agent_identity/src/state.rs b/agent_identity/src/state.rs
index c6400198..ec3c2f22 100644
--- a/agent_identity/src/state.rs
+++ b/agent_identity/src/state.rs
@@ -6,6 +6,9 @@ use did_manager::DidMethod;
 use std::sync::Arc;
 use tracing::{info, warn};
 
+use crate::connection::aggregate::Connection;
+use crate::connection::views::all_connections::AllConnectionsView;
+use crate::connection::views::ConnectionView;
 use crate::document::command::DocumentCommand;
 use crate::service::views::all_services::AllServicesView;
 use crate::{
@@ -22,6 +25,7 @@ pub struct IdentityState {
 /// The command handlers are used to execute commands on the aggregates.
 #[derive(Clone)]
 pub struct CommandHandlers {
+    pub connection: CommandHandler<Connection>,
     pub document: CommandHandler<Document>,
     pub service: CommandHandler<Service>,
 }
@@ -30,17 +34,23 @@ pub struct CommandHandlers {
 /// that any type of repository that implements the `ViewRepository` trait can be used, but the corresponding `View` and
 /// `Aggregate` types must be the same.
 type Queries = ViewRepositories<
+    dyn ViewRepository<ConnectionView, Connection>,
+    dyn ViewRepository<AllConnectionsView, Connection>,
     dyn ViewRepository<DocumentView, Document>,
     dyn ViewRepository<ServiceView, Service>,
     dyn ViewRepository<AllServicesView, Service>,
 >;
 
-pub struct ViewRepositories<D, S1, S2>
+pub struct ViewRepositories<C1, C2, D, S1, S2>
 where
+    C1: ViewRepository<ConnectionView, Connection> + ?Sized,
+    C2: ViewRepository<AllConnectionsView, Connection> + ?Sized,
     D: ViewRepository<DocumentView, Document> + ?Sized,
     S1: ViewRepository<ServiceView, Service> + ?Sized,
     S2: ViewRepository<AllServicesView, Service> + ?Sized,
 {
+    pub connection: Arc<C1>,
+    pub all_connections: Arc<C2>,
     pub document: Arc<D>,
     pub service: Arc<S1>,
     pub all_services: Arc<S2>,
@@ -49,6 +59,8 @@ where
 impl Clone for Queries {
     fn clone(&self) -> Self {
         ViewRepositories {
+            connection: self.connection.clone(),
+            all_connections: self.all_connections.clone(),
             document: self.document.clone(),
             service: self.service.clone(),
             all_services: self.all_services.clone(),
diff --git a/agent_shared/src/config.rs b/agent_shared/src/config.rs
index 4eb1d2e3..54512b4e 100644
--- a/agent_shared/src/config.rs
+++ b/agent_shared/src/config.rs
@@ -116,6 +116,8 @@ pub struct EventPublisherHttp {
 
 #[derive(Debug, Deserialize, Clone, Default)]
 pub struct Events {
+    #[serde(default)]
+    pub connection: Vec<ConnectionEvent>,
     #[serde(default)]
     pub document: Vec<DocumentEvent>,
     #[serde(default)]
@@ -131,11 +133,14 @@ pub struct Events {
     #[serde(default)]
     pub received_offer: Vec<ReceivedOfferEvent>,
     #[serde(default)]
-    pub connection: Vec<ConnectionEvent>,
-    #[serde(default)]
     pub authorization_request: Vec<AuthorizationRequestEvent>,
 }
 
+#[derive(Debug, Serialize, Deserialize, Clone, strum::Display)]
+pub enum ConnectionEvent {
+    ConnectionAdded,
+}
+
 #[derive(Debug, Serialize, Deserialize, Clone, strum::Display)]
 pub enum DocumentEvent {
     DocumentCreated,
@@ -185,17 +190,13 @@ pub enum ReceivedOfferEvent {
     CredentialOfferRejected,
 }
 
-#[derive(Debug, Serialize, Deserialize, Clone, strum::Display)]
-pub enum ConnectionEvent {
-    SIOPv2AuthorizationResponseVerified,
-    OID4VPAuthorizationResponseVerified,
-}
-
 #[derive(Debug, Serialize, Deserialize, Clone, strum::Display)]
 pub enum AuthorizationRequestEvent {
     AuthorizationRequestCreated,
     FormUrlEncodedAuthorizationRequestCreated,
     AuthorizationRequestObjectSigned,
+    SIOPv2AuthorizationResponseVerified,
+    OID4VPAuthorizationResponseVerified,
 }
 
 /// All DID methods supported by UniCore
diff --git a/agent_store/src/in_memory.rs b/agent_store/src/in_memory.rs
index 027dfe4f..6d90575b 100644
--- a/agent_store/src/in_memory.rs
+++ b/agent_store/src/in_memory.rs
@@ -121,15 +121,18 @@ pub async fn identity_state(
     event_publishers: Vec<Box<dyn EventPublisher>>,
 ) -> IdentityState {
     // Initialize the in-memory repositories.
+    let connection = Arc::new(MemRepository::default());
+    let all_connections = Arc::new(MemRepository::default());
     let document = Arc::new(MemRepository::default());
     let service = Arc::new(MemRepository::default());
     let all_services = Arc::new(MemRepository::default());
 
-    // Create custom-queries for the offer aggregate.
+    let all_connections_query = ListAllQuery::new(all_connections.clone(), "all_connections");
     let all_services_query = ListAllQuery::new(all_services.clone(), "all_services");
 
     // Partition the event_publishers into the different aggregates.
     let Partitions {
+        connection_event_publishers,
         document_event_publishers,
         service_event_publishers,
         ..
@@ -137,6 +140,15 @@ pub async fn identity_state(
 
     IdentityState {
         command: agent_identity::state::CommandHandlers {
+            connection: Arc::new(
+                connection_event_publishers.into_iter().fold(
+                    AggregateHandler::new(identity_services.clone())
+                        .append_query(SimpleLoggingQuery {})
+                        .append_query(generic_query(connection.clone()))
+                        .append_query(all_connections_query),
+                    |aggregate_handler, event_publisher| aggregate_handler.append_event_publisher(event_publisher),
+                ),
+            ),
             document: Arc::new(
                 document_event_publishers.into_iter().fold(
                     AggregateHandler::new(identity_services.clone())
@@ -156,6 +168,8 @@ pub async fn identity_state(
             ),
         },
         query: agent_identity::state::ViewRepositories {
+            connection,
+            all_connections,
             document,
             service,
             all_services,
@@ -307,7 +321,6 @@ pub async fn verification_state(
     // Initialize the in-memory repositories.
     let authorization_request = Arc::new(MemRepository::default());
     let all_authorization_requests = Arc::new(MemRepository::default());
-    let connection = Arc::new(MemRepository::default());
 
     // Create custom-queries for the offer aggregate.
     let all_authorization_requests_query =
@@ -316,7 +329,6 @@ pub async fn verification_state(
     // Partition the event_publishers into the different aggregates.
     let Partitions {
         authorization_request_event_publishers,
-        connection_event_publishers,
         ..
     } = partition_event_publishers(event_publishers);
 
@@ -331,19 +343,10 @@ pub async fn verification_state(
                     |aggregate_handler, event_publisher| aggregate_handler.append_event_publisher(event_publisher),
                 ),
             ),
-            connection: Arc::new(
-                connection_event_publishers.into_iter().fold(
-                    AggregateHandler::new(verification_services)
-                        .append_query(SimpleLoggingQuery {})
-                        .append_query(generic_query(connection.clone())),
-                    |aggregate_handler, event_publisher| aggregate_handler.append_event_publisher(event_publisher),
-                ),
-            ),
         },
         query: agent_verification::state::ViewRepositories {
             authorization_request,
             all_authorization_requests,
-            connection,
         },
     }
 }
diff --git a/agent_store/src/lib.rs b/agent_store/src/lib.rs
index 530dfcbb..2f158491 100644
--- a/agent_store/src/lib.rs
+++ b/agent_store/src/lib.rs
@@ -1,13 +1,14 @@
-use agent_identity::{document::aggregate::Document, service::aggregate::Service};
+use agent_identity::{connection::aggregate::Connection, document::aggregate::Document, service::aggregate::Service};
 use agent_issuance::{
     credential::aggregate::Credential, offer::aggregate::Offer, server_config::aggregate::ServerConfig,
 };
-use agent_verification::{authorization_request::aggregate::AuthorizationRequest, connection::aggregate::Connection};
+use agent_verification::authorization_request::aggregate::AuthorizationRequest;
 use cqrs_es::Query;
 
 pub mod in_memory;
 pub mod postgres;
 
+pub type ConnectionEventPublisher = Box<dyn Query<Connection>>;
 pub type DocumentEventPublisher = Box<dyn Query<Document>>;
 pub type ServiceEventPublisher = Box<dyn Query<Service>>;
 pub type ServerConfigEventPublisher = Box<dyn Query<ServerConfig>>;
@@ -17,11 +18,11 @@ pub type HolderCredentialEventPublisher = Box<dyn Query<agent_holder::credential
 pub type PresentationEventPublisher = Box<dyn Query<agent_holder::presentation::aggregate::Presentation>>;
 pub type ReceivedOfferEventPublisher = Box<dyn Query<agent_holder::offer::aggregate::Offer>>;
 pub type AuthorizationRequestEventPublisher = Box<dyn Query<AuthorizationRequest>>;
-pub type ConnectionEventPublisher = Box<dyn Query<Connection>>;
 
 /// Contains all the event_publishers for each aggregate.
 #[derive(Default)]
 pub struct Partitions {
+    pub connection_event_publishers: Vec<ConnectionEventPublisher>,
     pub document_event_publishers: Vec<DocumentEventPublisher>,
     pub service_event_publishers: Vec<ServiceEventPublisher>,
     pub server_config_event_publishers: Vec<ServerConfigEventPublisher>,
@@ -31,7 +32,6 @@ pub struct Partitions {
     pub presentation_event_publishers: Vec<PresentationEventPublisher>,
     pub received_offer_event_publishers: Vec<ReceivedOfferEventPublisher>,
     pub authorization_request_event_publishers: Vec<AuthorizationRequestEventPublisher>,
-    pub connection_event_publishers: Vec<ConnectionEventPublisher>,
 }
 
 /// An outbound event_publisher is a component that listens to events and dispatches them to the appropriate service. For each
@@ -39,6 +39,9 @@ pub struct Partitions {
 /// `Some` with the appropriate query.
 // TODO: move this to a separate crate that will include all the logic for event_publishers, i.e. `agent_event_publisher`.
 pub trait EventPublisher {
+    fn connection(&mut self) -> Option<ConnectionEventPublisher> {
+        None
+    }
     fn document(&mut self) -> Option<DocumentEventPublisher> {
         None
     }
@@ -66,9 +69,6 @@ pub trait EventPublisher {
         None
     }
 
-    fn connection(&mut self) -> Option<ConnectionEventPublisher> {
-        None
-    }
     fn authorization_request(&mut self) -> Option<AuthorizationRequestEventPublisher> {
         None
     }
@@ -78,6 +78,9 @@ pub(crate) fn partition_event_publishers(event_publishers: Vec<Box<dyn EventPubl
     event_publishers
         .into_iter()
         .fold(Partitions::default(), |mut partitions, mut event_publisher| {
+            if let Some(connection) = event_publisher.connection() {
+                partitions.connection_event_publishers.push(connection);
+            }
             if let Some(document) = event_publisher.document() {
                 partitions.document_event_publishers.push(document);
             }
@@ -110,9 +113,6 @@ pub(crate) fn partition_event_publishers(event_publishers: Vec<Box<dyn EventPubl
                     .authorization_request_event_publishers
                     .push(authorization_request);
             }
-            if let Some(connection) = event_publisher.connection() {
-                partitions.connection_event_publishers.push(connection);
-            }
             partitions
         })
 }
@@ -170,6 +170,7 @@ mod test {
             vec![Box::new(FooEventPublisher), Box::new(BarEventPublisher)];
 
         let Partitions {
+            connection_event_publishers,
             document_event_publishers,
             service_event_publishers,
             server_config_event_publishers,
@@ -179,9 +180,9 @@ mod test {
             presentation_event_publishers,
             received_offer_event_publishers,
             authorization_request_event_publishers,
-            connection_event_publishers,
         } = partition_event_publishers(event_publishers);
 
+        assert_eq!(connection_event_publishers.len(), 2);
         assert_eq!(document_event_publishers.len(), 0);
         assert_eq!(service_event_publishers.len(), 0);
         assert_eq!(server_config_event_publishers.len(), 1);
@@ -191,6 +192,5 @@ mod test {
         assert_eq!(presentation_event_publishers.len(), 0);
         assert_eq!(received_offer_event_publishers.len(), 0);
         assert_eq!(authorization_request_event_publishers.len(), 0);
-        assert_eq!(connection_event_publishers.len(), 2);
     }
 }
diff --git a/agent_store/src/postgres.rs b/agent_store/src/postgres.rs
index 06d987b3..0dbfe145 100644
--- a/agent_store/src/postgres.rs
+++ b/agent_store/src/postgres.rs
@@ -76,15 +76,18 @@ pub async fn identity_state(
     let pool = default_postgress_pool(&connection_string).await;
 
     // Initialize the postgres repositories.
+    let connection = Arc::new(PostgresViewRepository::new("connection", pool.clone()));
+    let all_connections = Arc::new(PostgresViewRepository::new("all_connections", pool.clone()));
     let document = Arc::new(PostgresViewRepository::new("document", pool.clone()));
     let service = Arc::new(PostgresViewRepository::new("service", pool.clone()));
     let all_services = Arc::new(PostgresViewRepository::new("all_services", pool.clone()));
 
-    // Create custom-queries for the offer aggregate.
+    let all_connections_query = ListAllQuery::new(all_connections.clone(), "all_connections");
     let all_services_query = ListAllQuery::new(all_services.clone(), "all_services");
 
     // Partition the event_publishers into the different aggregates.
     let Partitions {
+        connection_event_publishers,
         document_event_publishers,
         service_event_publishers,
         ..
@@ -92,6 +95,15 @@ pub async fn identity_state(
 
     IdentityState {
         command: agent_identity::state::CommandHandlers {
+            connection: Arc::new(
+                connection_event_publishers.into_iter().fold(
+                    AggregateHandler::new(pool.clone(), identity_services.clone())
+                        .append_query(SimpleLoggingQuery {})
+                        .append_query(generic_query(connection.clone()))
+                        .append_query(all_connections_query),
+                    |aggregate_handler, event_publisher| aggregate_handler.append_event_publisher(event_publisher),
+                ),
+            ),
             document: Arc::new(
                 document_event_publishers.into_iter().fold(
                     AggregateHandler::new(pool.clone(), identity_services.clone())
@@ -111,6 +123,8 @@ pub async fn identity_state(
             ),
         },
         query: agent_identity::state::ViewRepositories {
+            connection,
+            all_connections,
             document,
             service,
             all_services,
@@ -280,7 +294,6 @@ pub async fn verification_state(
     // Initialize the postgres repositories.
     let authorization_request = Arc::new(PostgresViewRepository::new("authorization_request", pool.clone()));
     let all_authorization_requests = Arc::new(PostgresViewRepository::new("all_authorization_requests", pool.clone()));
-    let connection = Arc::new(PostgresViewRepository::new("connection", pool.clone()));
 
     // Create custom-queries for the offer aggregate.
     let all_authorization_requests_query =
@@ -289,7 +302,6 @@ pub async fn verification_state(
     // Partition the event_publishers into the different aggregates.
     let Partitions {
         authorization_request_event_publishers,
-        connection_event_publishers,
         ..
     } = partition_event_publishers(event_publishers);
 
@@ -304,19 +316,10 @@ pub async fn verification_state(
                     |aggregate_handler, event_publisher| aggregate_handler.append_event_publisher(event_publisher),
                 ),
             ),
-            connection: Arc::new(
-                connection_event_publishers.into_iter().fold(
-                    AggregateHandler::new(pool, verification_services)
-                        .append_query(SimpleLoggingQuery {})
-                        .append_query(generic_query(connection.clone())),
-                    |aggregate_handler, event_publisher| aggregate_handler.append_event_publisher(event_publisher),
-                ),
-            ),
         },
         query: agent_verification::state::ViewRepositories {
             authorization_request,
             all_authorization_requests,
-            connection,
         },
     }
 }
diff --git a/agent_verification/src/authorization_request/README.md b/agent_verification/src/authorization_request/README.md
new file mode 100644
index 00000000..6de0ade7
--- /dev/null
+++ b/agent_verification/src/authorization_request/README.md
@@ -0,0 +1,9 @@
+# Authorization Request
+
+This aggregate holds everything related to an Authorization Request:
+- authorization_request
+- form_url_encoded_authorization_request
+- signed_authorization_request_object
+- id_token
+- vp_token
+- state 
diff --git a/agent_verification/src/authorization_request/aggregate.rs b/agent_verification/src/authorization_request/aggregate.rs
index 76d5cb3d..9d2ea720 100644
--- a/agent_verification/src/authorization_request/aggregate.rs
+++ b/agent_verification/src/authorization_request/aggregate.rs
@@ -1,13 +1,16 @@
 use super::{command::AuthorizationRequestCommand, error::AuthorizationRequestError, event::AuthorizationRequestEvent};
 use crate::{
-    generic_oid4vc::{GenericAuthorizationRequest, OID4VPAuthorizationRequest, SIOPv2AuthorizationRequest},
+    generic_oid4vc::{
+        GenericAuthorizationRequest, GenericAuthorizationResponse, OID4VPAuthorizationRequest,
+        SIOPv2AuthorizationRequest,
+    },
     services::VerificationServices,
 };
 use agent_shared::config::{config, get_preferred_signing_algorithm};
 use async_trait::async_trait;
 use cqrs_es::Aggregate;
 use oid4vc_core::{authorization_request::ByReference, scope::Scope};
-use oid4vp::authorization_request::ClientIdScheme;
+use oid4vp::{authorization_request::ClientIdScheme, Oid4vpParams};
 use serde::{Deserialize, Serialize};
 use std::sync::Arc;
 use tracing::info;
@@ -17,6 +20,9 @@ pub struct AuthorizationRequest {
     pub authorization_request: Option<GenericAuthorizationRequest>,
     pub form_url_encoded_authorization_request: Option<String>,
     pub signed_authorization_request_object: Option<String>,
+    pub id_token: Option<String>,
+    pub vp_token: Option<String>,
+    pub state: Option<String>,
 }
 
 #[async_trait]
@@ -127,6 +133,45 @@ impl Aggregate for AuthorizationRequest {
                     signed_authorization_request_object,
                 }])
             }
+            VerifyAuthorizationResponse {
+                // TODO: use this once `RelyingPartyManager` uses the official SIOPv2 validation logic.
+                authorization_request: _,
+                authorization_response,
+            } => {
+                let relying_party = &services.relying_party;
+
+                match authorization_response {
+                    GenericAuthorizationResponse::SIOPv2(authorization_response) => {
+                        let _ = relying_party
+                            .validate_response(&authorization_response)
+                            .await
+                            .map_err(InvalidSIOPv2AuthorizationResponse)?;
+
+                        let id_token = authorization_response.extension.id_token.clone();
+
+                        Ok(vec![SIOPv2AuthorizationResponseVerified {
+                            id_token,
+                            state: authorization_response.state,
+                        }])
+                    }
+                    GenericAuthorizationResponse::OID4VP(oid4vp_authorization_response) => {
+                        let _ = relying_party
+                            .validate_response(&oid4vp_authorization_response)
+                            .await
+                            .map_err(InvalidOID4VPAuthorizationResponse)?;
+
+                        let vp_token = match oid4vp_authorization_response.extension.oid4vp_parameters {
+                            Oid4vpParams::Params { vp_token, .. } => vp_token,
+                            Oid4vpParams::Jwt { .. } => return Err(UnsupportedJwtParameterError),
+                        };
+
+                        Ok(vec![OID4VPAuthorizationResponseVerified {
+                            vp_token,
+                            state: oid4vp_authorization_response.state,
+                        }])
+                    }
+                }
+            }
         }
     }
 
@@ -151,6 +196,14 @@ impl Aggregate for AuthorizationRequest {
                 self.signed_authorization_request_object
                     .replace(signed_authorization_request_object);
             }
+            SIOPv2AuthorizationResponseVerified { id_token, state } => {
+                self.id_token.replace(id_token);
+                self.state = state;
+            }
+            OID4VPAuthorizationResponseVerified { vp_token, state } => {
+                self.vp_token.replace(vp_token);
+                self.state = state;
+            }
         }
     }
 }
@@ -165,10 +218,16 @@ pub mod tests {
     use agent_shared::config::set_config;
     use agent_shared::config::SupportedDidMethod;
     use cqrs_es::test::TestFramework;
+    use identity_credential::credential::Jwt;
+    use identity_credential::presentation::Presentation;
     use jsonwebtoken::Algorithm;
     use lazy_static::lazy_static;
     use oid4vc_core::Subject as _;
     use oid4vc_core::{client_metadata::ClientMetadataResource, SubjectSyntaxType};
+    use oid4vc_manager::managers::presentation::create_presentation_submission;
+    use oid4vc_manager::ProviderManager;
+    use oid4vci::VerifiableCredentialJwt;
+    use oid4vp::oid4vp::AuthorizationResponseInput;
     use oid4vp::PresentationDefinition;
     use rstest::rstest;
     use serde_json::json;
@@ -255,6 +314,145 @@ pub mod tests {
             }]);
     }
 
+    #[rstest]
+    #[serial_test::serial]
+    async fn test_verify_authorization_response(
+        // "id_token" represents the `SIOPv2` flow, and "vp_token" represents the `OID4VP` flow.
+        #[values("id_token", "vp_token")] response_type: &str,
+        // TODO: add `did:web`, check for other tests as well. Probably should be moved to E2E test.
+        #[values(SupportedDidMethod::Key, SupportedDidMethod::Jwk, SupportedDidMethod::IotaRms)]
+        verifier_did_method: SupportedDidMethod,
+        #[values(SupportedDidMethod::Key, SupportedDidMethod::Jwk, SupportedDidMethod::IotaRms)]
+        provider_did_method: SupportedDidMethod,
+    ) {
+        set_config().set_preferred_did_method(verifier_did_method.clone());
+
+        let verification_services = VerificationServices::default();
+        let siopv2_client_metadata = verification_services.siopv2_client_metadata.clone();
+        let oid4vp_client_metadata = verification_services.oid4vp_client_metadata.clone();
+
+        let authorization_request = authorization_request(
+            response_type,
+            &verifier_did_method.to_string(),
+            siopv2_client_metadata,
+            oid4vp_client_metadata,
+        )
+        .await;
+
+        let authorization_response =
+            authorization_response(&provider_did_method.to_string(), &authorization_request).await;
+        let token = authorization_response.token();
+
+        AuthorizationRequestTestFramework::with(verification_services)
+            .given_no_previous_events()
+            .when(AuthorizationRequestCommand::VerifyAuthorizationResponse {
+                authorization_request,
+                authorization_response,
+            })
+            .then_expect_events(vec![match response_type {
+                "id_token" => AuthorizationRequestEvent::SIOPv2AuthorizationResponseVerified {
+                    id_token: token,
+                    state: Some("state".to_string()),
+                },
+                "vp_token" => AuthorizationRequestEvent::OID4VPAuthorizationResponseVerified {
+                    vp_token: token,
+                    state: Some("state".to_string()),
+                },
+                _ => unreachable!("Invalid response type."),
+            }]);
+    }
+
+    async fn authorization_response(
+        did_method: &str,
+        authorization_request: &GenericAuthorizationRequest,
+    ) -> GenericAuthorizationResponse {
+        let provider_manager = ProviderManager::new(
+            Arc::new(futures::executor::block_on(async {
+                Subject {
+                    secret_manager: Arc::new(tokio::sync::Mutex::new(secret_manager().await)),
+                }
+            })),
+            vec![did_method],
+            vec![Algorithm::EdDSA],
+        )
+        .unwrap();
+
+        let default_did_method = provider_manager.default_subject_syntax_types()[0].to_string();
+
+        match authorization_request {
+            GenericAuthorizationRequest::SIOPv2(siopv2_authorization_request) => GenericAuthorizationResponse::SIOPv2(
+                provider_manager
+                    .generate_response(siopv2_authorization_request, Default::default())
+                    .await
+                    .unwrap(),
+            ),
+            GenericAuthorizationRequest::OID4VP(oid4vp_authorization_request) => {
+                // TODO: implement test fixture for subject and issuer instead of using the same did as verifier.
+                // Fixtures can be implemented using the `rstest` crate as described here: https://docs.rs/rstest/latest/rstest/attr.fixture.html
+                let issuer_did = verifier_did(&default_did_method).await;
+                let subject_did = issuer_did.clone();
+
+                // Create a new verifiable credential.
+                let verifiable_credential = VerifiableCredentialJwt::builder()
+                    .sub(&subject_did)
+                    .iss(&issuer_did)
+                    .iat(0)
+                    .exp(9999999999i64)
+                    .verifiable_credential(serde_json::json!({
+                        "@context": [
+                            "https://www.w3.org/2018/credentials/v1",
+                            "https://www.w3.org/2018/credentials/examples/v1"
+                        ],
+                        "type": [
+                            "VerifiableCredential",
+                            "TestCredential"
+                        ],
+                        "issuanceDate": "2022-01-01T00:00:00Z",
+                        "issuer": issuer_did,
+                        "credentialSubject": {
+                        "id": subject_did,
+                        "givenName": "Ferris",
+                        "familyName": "Crabman",
+                        "email": "ferris.crabman@crabmail.com",
+                        "birthdate": "1985-05-21"
+                        }
+                    }))
+                    .build()
+                    .unwrap();
+
+                // Encode the verifiable credential as a JWT.
+                let jwt = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFZERTQSIsImtpZCI6ImRpZDprZXk6ejZNa2lpZXlvTE1TVnNKQVp2N0pqZTV3V1NrREV5bVVna3lGOGtiY3JqWnBYM3FkI3o2TWtpaWV5b0xNU1ZzSkFadjdKamU1d1dTa0RFeW1VZ2t5RjhrYmNyalpwWDNxZCJ9.eyJpc3MiOiJkaWQ6a2V5Ono2TWtpaWV5b0xNU1ZzSkFadjdKamU1d1dTa0RFeW1VZ2t5RjhrYmNyalpwWDNxZCIsInN1YiI6ImRpZDprZXk6ejZNa2lpZXlvTE1TVnNKQVp2N0pqZTV3V1NrREV5bVVna3lGOGtiY3JqWnBYM3FkIiwiZXhwIjo5OTk5OTk5OTk5LCJpYXQiOjAsInZjIjp7IkBjb250ZXh0IjpbImh0dHBzOi8vd3d3LnczLm9yZy8yMDE4L2NyZWRlbnRpYWxzL3YxIiwiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvZXhhbXBsZXMvdjEiXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCIsIlRlc3RDcmVkZW50aWFsIl0sImlzc3VhbmNlRGF0ZSI6IjIwMjItMDEtMDFUMDA6MDA6MDBaIiwiaXNzdWVyIjoiZGlkOmtleTp6Nk1raWlleW9MTVNWc0pBWnY3SmplNXdXU2tERXltVWdreUY4a2JjcmpacFgzcWQiLCJjcmVkZW50aWFsU3ViamVjdCI6eyJpZCI6ImRpZDprZXk6ejZNa2lpZXlvTE1TVnNKQVp2N0pqZTV3V1NrREV5bVVna3lGOGtiY3JqWnBYM3FkIiwiZ2l2ZW5OYW1lIjoiRmVycmlzIiwiZmFtaWx5TmFtZSI6IkNyYWJtYW4iLCJlbWFpbCI6ImZlcnJpcy5jcmFibWFuQGNyYWJtYWlsLmNvbSIsImJpcnRoZGF0ZSI6IjE5ODUtMDUtMjEifX19.6guSHngBj_QQYom3kXKmxKrHExoyW1eObBsBg8ACYn-H30YD6eub56zsWnnMzw8IznGDYAguuo3V1D37-A_vCQ".to_string();
+
+                // Create presentation submission using the presentation definition and the verifiable credential.
+                let presentation_submission = create_presentation_submission(
+                    &PRESENTATION_DEFINITION,
+                    &[serde_json::to_value(&verifiable_credential).unwrap()],
+                )
+                .unwrap();
+
+                // Create a verifiable presentation using the JWT.
+                let verifiable_presentation =
+                    Presentation::builder(subject_did.parse().unwrap(), identity_core::common::Object::new())
+                        .credential(Jwt::from(jwt))
+                        .build()
+                        .unwrap();
+
+                GenericAuthorizationResponse::OID4VP(
+                    provider_manager
+                        .generate_response(
+                            oid4vp_authorization_request,
+                            AuthorizationResponseInput {
+                                verifiable_presentation,
+                                presentation_submission,
+                            },
+                        )
+                        .await
+                        .unwrap(),
+                )
+            }
+        }
+    }
+
     pub async fn verifier_did(did_method: &str) -> String {
         VERIFIER.identifier(did_method, Algorithm::EdDSA).await.unwrap()
     }
diff --git a/agent_verification/src/authorization_request/command.rs b/agent_verification/src/authorization_request/command.rs
index c48ab99b..0601c8b2 100644
--- a/agent_verification/src/authorization_request/command.rs
+++ b/agent_verification/src/authorization_request/command.rs
@@ -1,6 +1,8 @@
 use oid4vp::PresentationDefinition;
 use serde::Deserialize;
 
+use crate::generic_oid4vc::{GenericAuthorizationRequest, GenericAuthorizationResponse};
+
 #[derive(Debug, Deserialize)]
 #[serde(untagged)]
 pub enum AuthorizationRequestCommand {
@@ -10,4 +12,8 @@ pub enum AuthorizationRequestCommand {
         presentation_definition: Option<PresentationDefinition>,
     },
     SignAuthorizationRequestObject,
+    VerifyAuthorizationResponse {
+        authorization_request: GenericAuthorizationRequest,
+        authorization_response: GenericAuthorizationResponse,
+    },
 }
diff --git a/agent_verification/src/authorization_request/error.rs b/agent_verification/src/authorization_request/error.rs
index af2077aa..1f8ac2c9 100644
--- a/agent_verification/src/authorization_request/error.rs
+++ b/agent_verification/src/authorization_request/error.rs
@@ -8,4 +8,10 @@ pub enum AuthorizationRequestError {
     MissingAuthorizationRequest,
     #[error("Failed to sign authorization request: {0}")]
     AuthorizationRequestSigningError(#[source] anyhow::Error),
+    #[error("Invalid SIOPv2 authorization response: {0}")]
+    InvalidSIOPv2AuthorizationResponse(#[source] anyhow::Error),
+    #[error("Invalid OID4VP authorization response: {0}")]
+    InvalidOID4VPAuthorizationResponse(#[source] anyhow::Error),
+    #[error("`jwt` parameter is not supported yet")]
+    UnsupportedJwtParameterError,
 }
diff --git a/agent_verification/src/authorization_request/event.rs b/agent_verification/src/authorization_request/event.rs
index 2d36fee5..2f66ec4e 100644
--- a/agent_verification/src/authorization_request/event.rs
+++ b/agent_verification/src/authorization_request/event.rs
@@ -13,6 +13,14 @@ pub enum AuthorizationRequestEvent {
     AuthorizationRequestObjectSigned {
         signed_authorization_request_object: String,
     },
+    SIOPv2AuthorizationResponseVerified {
+        id_token: String,
+        state: Option<String>,
+    },
+    OID4VPAuthorizationResponseVerified {
+        vp_token: String,
+        state: Option<String>,
+    },
 }
 
 impl DomainEvent for AuthorizationRequestEvent {
@@ -23,6 +31,8 @@ impl DomainEvent for AuthorizationRequestEvent {
             AuthorizationRequestCreated { .. } => "AuthorizationRequestCreated",
             FormUrlEncodedAuthorizationRequestCreated { .. } => "FormUrlEncodedAuthorizationRequestCreated",
             AuthorizationRequestObjectSigned { .. } => "AuthorizationRequestObjectSigned",
+            SIOPv2AuthorizationResponseVerified { .. } => "SIOPv2AuthorizationResponseVerified",
+            OID4VPAuthorizationResponseVerified { .. } => "OID4VPAuthorizationResponseVerified",
         };
         event_type.to_string()
     }
diff --git a/agent_verification/src/authorization_request/views/mod.rs b/agent_verification/src/authorization_request/views/mod.rs
index 055757a3..10e258b2 100644
--- a/agent_verification/src/authorization_request/views/mod.rs
+++ b/agent_verification/src/authorization_request/views/mod.rs
@@ -25,6 +25,14 @@ impl View<AuthorizationRequest> for AuthorizationRequest {
                 self.signed_authorization_request_object
                     .replace(signed_authorization_request_object.clone());
             }
+            SIOPv2AuthorizationResponseVerified { id_token, state } => {
+                self.id_token.replace(id_token.clone());
+                self.state.clone_from(state);
+            }
+            OID4VPAuthorizationResponseVerified { vp_token, state } => {
+                self.vp_token.replace(vp_token.clone());
+                self.state.clone_from(state);
+            }
         }
     }
 }
diff --git a/agent_verification/src/connection/aggregate.rs b/agent_verification/src/connection/aggregate.rs
deleted file mode 100644
index 3a37b438..00000000
--- a/agent_verification/src/connection/aggregate.rs
+++ /dev/null
@@ -1,264 +0,0 @@
-use super::{command::ConnectionCommand, error::ConnectionError, event::ConnectionEvent};
-use crate::{generic_oid4vc::GenericAuthorizationResponse, services::VerificationServices};
-use async_trait::async_trait;
-use cqrs_es::Aggregate;
-use oid4vp::Oid4vpParams;
-use serde::{Deserialize, Serialize};
-use std::{sync::Arc, vec};
-use tracing::info;
-
-#[derive(Debug, Serialize, Deserialize, Default)]
-pub struct Connection {
-    // TODO: Does user data need to be stored in UniCore at all?
-    id_token: Option<String>,
-    vp_token: Option<String>,
-    state: Option<String>,
-}
-
-#[async_trait]
-impl Aggregate for Connection {
-    type Command = ConnectionCommand;
-    type Event = ConnectionEvent;
-    type Error = ConnectionError;
-    type Services = Arc<VerificationServices>;
-
-    fn aggregate_type() -> String {
-        "connection".to_string()
-    }
-
-    async fn handle(&self, command: Self::Command, services: &Self::Services) -> Result<Vec<Self::Event>, Self::Error> {
-        use ConnectionCommand::*;
-        use ConnectionError::*;
-        use ConnectionEvent::*;
-
-        info!("Handling command: {:?}", command);
-
-        match command {
-            VerifyAuthorizationResponse {
-                // TODO: use this once `RelyingPartyManager` uses the official SIOPv2 validation logic.
-                authorization_request: _,
-                authorization_response,
-            } => {
-                let relying_party = &services.relying_party;
-
-                match authorization_response {
-                    GenericAuthorizationResponse::SIOPv2(authorization_response) => {
-                        let _ = relying_party
-                            .validate_response(&authorization_response)
-                            .await
-                            .map_err(InvalidSIOPv2AuthorizationResponse)?;
-
-                        let id_token = authorization_response.extension.id_token.clone();
-
-                        Ok(vec![SIOPv2AuthorizationResponseVerified {
-                            id_token,
-                            state: authorization_response.state,
-                        }])
-                    }
-                    GenericAuthorizationResponse::OID4VP(oid4vp_authorization_response) => {
-                        let _ = relying_party
-                            .validate_response(&oid4vp_authorization_response)
-                            .await
-                            .map_err(InvalidOID4VPAuthorizationResponse)?;
-
-                        let vp_token = match oid4vp_authorization_response.extension.oid4vp_parameters {
-                            Oid4vpParams::Params { vp_token, .. } => vp_token,
-                            Oid4vpParams::Jwt { .. } => return Err(UnsupportedJwtParameterError),
-                        };
-
-                        Ok(vec![OID4VPAuthorizationResponseVerified {
-                            vp_token,
-                            state: oid4vp_authorization_response.state,
-                        }])
-                    }
-                }
-            }
-        }
-    }
-
-    fn apply(&mut self, event: Self::Event) {
-        use ConnectionEvent::*;
-
-        info!("Applying event: {:?}", event);
-
-        match event {
-            SIOPv2AuthorizationResponseVerified { id_token, state } => {
-                self.id_token.replace(id_token);
-                self.state = state;
-            }
-            OID4VPAuthorizationResponseVerified { vp_token, state } => {
-                self.vp_token.replace(vp_token);
-                self.state = state;
-            }
-        }
-    }
-}
-
-#[cfg(test)]
-pub mod tests {
-    use std::sync::Arc;
-
-    use agent_secret_manager::secret_manager;
-    use agent_secret_manager::subject::Subject;
-    use agent_shared::config::SupportedDidMethod;
-    use cqrs_es::test::TestFramework;
-    use identity_credential::credential::Jwt;
-    use identity_credential::presentation::Presentation;
-
-    use agent_shared::config::set_config;
-    use jsonwebtoken::Algorithm;
-    use oid4vc_manager::managers::presentation::create_presentation_submission;
-    use oid4vc_manager::ProviderManager;
-    use oid4vci::VerifiableCredentialJwt;
-    use oid4vp::oid4vp::AuthorizationResponseInput;
-    use rstest::rstest;
-
-    use crate::authorization_request::aggregate::tests::{
-        authorization_request, verifier_did, PRESENTATION_DEFINITION,
-    };
-    use crate::generic_oid4vc::GenericAuthorizationRequest;
-    use agent_secret_manager::service::Service as _;
-
-    use super::*;
-
-    type ConnectionTestFramework = TestFramework<Connection>;
-
-    #[rstest]
-    #[serial_test::serial]
-    async fn test_verify_authorization_response(
-        // "id_token" represents the `SIOPv2` flow, and "vp_token" represents the `OID4VP` flow.
-        #[values("id_token", "vp_token")] response_type: &str,
-        // TODO: add `did:web`, check for other tests as well. Probably should be moved to E2E test.
-        #[values(SupportedDidMethod::Key, SupportedDidMethod::Jwk, SupportedDidMethod::IotaRms)]
-        verifier_did_method: SupportedDidMethod,
-        #[values(SupportedDidMethod::Key, SupportedDidMethod::Jwk, SupportedDidMethod::IotaRms)]
-        provider_did_method: SupportedDidMethod,
-    ) {
-        set_config().set_preferred_did_method(verifier_did_method.clone());
-
-        let verification_services = VerificationServices::default();
-        let siopv2_client_metadata = verification_services.siopv2_client_metadata.clone();
-        let oid4vp_client_metadata = verification_services.oid4vp_client_metadata.clone();
-
-        let authorization_request = authorization_request(
-            response_type,
-            &verifier_did_method.to_string(),
-            siopv2_client_metadata,
-            oid4vp_client_metadata,
-        )
-        .await;
-
-        let authorization_response =
-            authorization_response(&provider_did_method.to_string(), &authorization_request).await;
-        let token = authorization_response.token();
-
-        ConnectionTestFramework::with(verification_services)
-            .given_no_previous_events()
-            .when(ConnectionCommand::VerifyAuthorizationResponse {
-                authorization_request,
-                authorization_response,
-            })
-            .then_expect_events(vec![match response_type {
-                "id_token" => ConnectionEvent::SIOPv2AuthorizationResponseVerified {
-                    id_token: token,
-                    state: Some("state".to_string()),
-                },
-                "vp_token" => ConnectionEvent::OID4VPAuthorizationResponseVerified {
-                    vp_token: token,
-                    state: Some("state".to_string()),
-                },
-                _ => unreachable!("Invalid response type."),
-            }]);
-    }
-
-    async fn authorization_response(
-        did_method: &str,
-        authorization_request: &GenericAuthorizationRequest,
-    ) -> GenericAuthorizationResponse {
-        let provider_manager = ProviderManager::new(
-            Arc::new(futures::executor::block_on(async {
-                Subject {
-                    secret_manager: Arc::new(tokio::sync::Mutex::new(secret_manager().await)),
-                }
-            })),
-            vec![did_method],
-            vec![Algorithm::EdDSA],
-        )
-        .unwrap();
-
-        let default_did_method = provider_manager.default_subject_syntax_types()[0].to_string();
-
-        match authorization_request {
-            GenericAuthorizationRequest::SIOPv2(siopv2_authorization_request) => GenericAuthorizationResponse::SIOPv2(
-                provider_manager
-                    .generate_response(siopv2_authorization_request, Default::default())
-                    .await
-                    .unwrap(),
-            ),
-            GenericAuthorizationRequest::OID4VP(oid4vp_authorization_request) => {
-                // TODO: implement test fixture for subject and issuer instead of using the same did as verifier.
-                // Fixtures can be implemented using the `rstest` crate as described here: https://docs.rs/rstest/latest/rstest/attr.fixture.html
-                let issuer_did = verifier_did(&default_did_method).await;
-                let subject_did = issuer_did.clone();
-
-                // Create a new verifiable credential.
-                let verifiable_credential = VerifiableCredentialJwt::builder()
-                    .sub(&subject_did)
-                    .iss(&issuer_did)
-                    .iat(0)
-                    .exp(9999999999i64)
-                    .verifiable_credential(serde_json::json!({
-                        "@context": [
-                            "https://www.w3.org/2018/credentials/v1",
-                            "https://www.w3.org/2018/credentials/examples/v1"
-                        ],
-                        "type": [
-                            "VerifiableCredential",
-                            "TestCredential"
-                        ],
-                        "issuanceDate": "2022-01-01T00:00:00Z",
-                        "issuer": issuer_did,
-                        "credentialSubject": {
-                        "id": subject_did,
-                        "givenName": "Ferris",
-                        "familyName": "Crabman",
-                        "email": "ferris.crabman@crabmail.com",
-                        "birthdate": "1985-05-21"
-                        }
-                    }))
-                    .build()
-                    .unwrap();
-
-                // Encode the verifiable credential as a JWT.
-                let jwt = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFZERTQSIsImtpZCI6ImRpZDprZXk6ejZNa2lpZXlvTE1TVnNKQVp2N0pqZTV3V1NrREV5bVVna3lGOGtiY3JqWnBYM3FkI3o2TWtpaWV5b0xNU1ZzSkFadjdKamU1d1dTa0RFeW1VZ2t5RjhrYmNyalpwWDNxZCJ9.eyJpc3MiOiJkaWQ6a2V5Ono2TWtpaWV5b0xNU1ZzSkFadjdKamU1d1dTa0RFeW1VZ2t5RjhrYmNyalpwWDNxZCIsInN1YiI6ImRpZDprZXk6ejZNa2lpZXlvTE1TVnNKQVp2N0pqZTV3V1NrREV5bVVna3lGOGtiY3JqWnBYM3FkIiwiZXhwIjo5OTk5OTk5OTk5LCJpYXQiOjAsInZjIjp7IkBjb250ZXh0IjpbImh0dHBzOi8vd3d3LnczLm9yZy8yMDE4L2NyZWRlbnRpYWxzL3YxIiwiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvZXhhbXBsZXMvdjEiXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCIsIlRlc3RDcmVkZW50aWFsIl0sImlzc3VhbmNlRGF0ZSI6IjIwMjItMDEtMDFUMDA6MDA6MDBaIiwiaXNzdWVyIjoiZGlkOmtleTp6Nk1raWlleW9MTVNWc0pBWnY3SmplNXdXU2tERXltVWdreUY4a2JjcmpacFgzcWQiLCJjcmVkZW50aWFsU3ViamVjdCI6eyJpZCI6ImRpZDprZXk6ejZNa2lpZXlvTE1TVnNKQVp2N0pqZTV3V1NrREV5bVVna3lGOGtiY3JqWnBYM3FkIiwiZ2l2ZW5OYW1lIjoiRmVycmlzIiwiZmFtaWx5TmFtZSI6IkNyYWJtYW4iLCJlbWFpbCI6ImZlcnJpcy5jcmFibWFuQGNyYWJtYWlsLmNvbSIsImJpcnRoZGF0ZSI6IjE5ODUtMDUtMjEifX19.6guSHngBj_QQYom3kXKmxKrHExoyW1eObBsBg8ACYn-H30YD6eub56zsWnnMzw8IznGDYAguuo3V1D37-A_vCQ".to_string();
-
-                // Create presentation submission using the presentation definition and the verifiable credential.
-                let presentation_submission = create_presentation_submission(
-                    &PRESENTATION_DEFINITION,
-                    &[serde_json::to_value(&verifiable_credential).unwrap()],
-                )
-                .unwrap();
-
-                // Create a verifiable presentation using the JWT.
-                let verifiable_presentation =
-                    Presentation::builder(subject_did.parse().unwrap(), identity_core::common::Object::new())
-                        .credential(Jwt::from(jwt))
-                        .build()
-                        .unwrap();
-
-                GenericAuthorizationResponse::OID4VP(
-                    provider_manager
-                        .generate_response(
-                            oid4vp_authorization_request,
-                            AuthorizationResponseInput {
-                                verifiable_presentation,
-                                presentation_submission,
-                            },
-                        )
-                        .await
-                        .unwrap(),
-                )
-            }
-        }
-    }
-}
diff --git a/agent_verification/src/connection/command.rs b/agent_verification/src/connection/command.rs
deleted file mode 100644
index 07a590e2..00000000
--- a/agent_verification/src/connection/command.rs
+++ /dev/null
@@ -1,11 +0,0 @@
-use crate::generic_oid4vc::{GenericAuthorizationRequest, GenericAuthorizationResponse};
-use serde::Deserialize;
-
-#[derive(Debug, Deserialize)]
-#[serde(untagged)]
-pub enum ConnectionCommand {
-    VerifyAuthorizationResponse {
-        authorization_request: GenericAuthorizationRequest,
-        authorization_response: GenericAuthorizationResponse,
-    },
-}
diff --git a/agent_verification/src/connection/error.rs b/agent_verification/src/connection/error.rs
deleted file mode 100644
index 9ec29a54..00000000
--- a/agent_verification/src/connection/error.rs
+++ /dev/null
@@ -1,11 +0,0 @@
-use thiserror::Error;
-
-#[derive(Error, Debug)]
-pub enum ConnectionError {
-    #[error("Invalid SIOPv2 authorization response: {0}")]
-    InvalidSIOPv2AuthorizationResponse(#[source] anyhow::Error),
-    #[error("Invalid OID4VP authorization response: {0}")]
-    InvalidOID4VPAuthorizationResponse(#[source] anyhow::Error),
-    #[error("`jwt` parameter is not supported yet")]
-    UnsupportedJwtParameterError,
-}
diff --git a/agent_verification/src/connection/queries.rs b/agent_verification/src/connection/queries.rs
deleted file mode 100644
index 8dc9cf15..00000000
--- a/agent_verification/src/connection/queries.rs
+++ /dev/null
@@ -1,32 +0,0 @@
-use cqrs_es::{EventEnvelope, View};
-use oid4vc_core::authorization_request::Object;
-use serde::{Deserialize, Serialize};
-use siopv2::siopv2::SIOPv2;
-
-use super::aggregate::Connection;
-
-pub type SIOPv2AuthorizationRequest = oid4vc_core::authorization_request::AuthorizationRequest<Object<SIOPv2>>;
-
-#[derive(Debug, Default, Serialize, Deserialize, Clone)]
-pub struct ConnectionView {
-    id_token: Option<String>,
-    vp_token: Option<String>,
-    state: Option<String>,
-}
-
-impl View<Connection> for ConnectionView {
-    fn update(&mut self, event: &EventEnvelope<Connection>) {
-        use crate::connection::event::ConnectionEvent::*;
-
-        match &event.payload {
-            SIOPv2AuthorizationResponseVerified { id_token, state } => {
-                self.id_token.replace(id_token.clone());
-                self.state.clone_from(state);
-            }
-            OID4VPAuthorizationResponseVerified { vp_token, state } => {
-                self.vp_token.replace(vp_token.clone());
-                self.state.clone_from(state);
-            }
-        }
-    }
-}
diff --git a/agent_verification/src/lib.rs b/agent_verification/src/lib.rs
index 37d478cc..43aabae2 100644
--- a/agent_verification/src/lib.rs
+++ b/agent_verification/src/lib.rs
@@ -1,5 +1,4 @@
 pub mod authorization_request;
-pub mod connection;
 pub mod generic_oid4vc;
 pub mod services;
 pub mod state;
diff --git a/agent_verification/src/state.rs b/agent_verification/src/state.rs
index 48eb6851..09ed29be 100644
--- a/agent_verification/src/state.rs
+++ b/agent_verification/src/state.rs
@@ -5,8 +5,6 @@ use std::sync::Arc;
 use crate::authorization_request::aggregate::AuthorizationRequest;
 use crate::authorization_request::views::all_authorization_requests::AllAuthorizationRequestsView;
 use crate::authorization_request::views::AuthorizationRequestView;
-use crate::connection::aggregate::Connection;
-use crate::connection::queries::ConnectionView;
 
 #[derive(Clone)]
 pub struct VerificationState {
@@ -18,7 +16,6 @@ pub struct VerificationState {
 #[derive(Clone)]
 pub struct CommandHandlers {
     pub authorization_request: CommandHandler<AuthorizationRequest>,
-    pub connection: CommandHandler<Connection>,
 }
 /// This type is used to define the queries that are used to query the view repositories. We make use of `dyn` here, so
 /// that any type of repository that implements the `ViewRepository` trait can be used, but the corresponding `View` and
@@ -26,18 +23,15 @@ pub struct CommandHandlers {
 type Queries = ViewRepositories<
     dyn ViewRepository<AuthorizationRequestView, AuthorizationRequest>,
     dyn ViewRepository<AllAuthorizationRequestsView, AuthorizationRequest>,
-    dyn ViewRepository<ConnectionView, Connection>,
 >;
 
-pub struct ViewRepositories<AR1, AR2, C>
+pub struct ViewRepositories<AR1, AR2>
 where
     AR1: ViewRepository<AuthorizationRequestView, AuthorizationRequest> + ?Sized,
     AR2: ViewRepository<AllAuthorizationRequestsView, AuthorizationRequest> + ?Sized,
-    C: ViewRepository<ConnectionView, Connection> + ?Sized,
 {
     pub authorization_request: Arc<AR1>,
     pub all_authorization_requests: Arc<AR2>,
-    pub connection: Arc<C>,
 }
 
 impl Clone for Queries {
@@ -45,7 +39,6 @@ impl Clone for Queries {
         ViewRepositories {
             authorization_request: self.authorization_request.clone(),
             all_authorization_requests: self.all_authorization_requests.clone(),
-            connection: self.connection.clone(),
         }
     }
 }