From e8581187c9a6611ddfc40f8aaa1f8ccef4263416 Mon Sep 17 00:00:00 2001 From: Nander Stabel Date: Wed, 24 Apr 2024 22:15:21 +0200 Subject: [PATCH] chore: bump `oid4vc` and `did_manager` dependencies (#44) * WIP * WIP * feat: re-introduce generics * WIP * feat: introduce `AppState` * style: rename `ApplicationState` and `AppState` * feat: add `Domain` trait * WIP * WIP * style: remove unused code * fix: fix some `unwrap`s, clean code * refactor: simplify agent_store * feat: add `CommandHandlers` struct * style: fix clippy warnings * refactor: simplify Commands * style: change name * chore: add log messages * fix: only update view when `CredentialOfferCreated` * chore: add comments and logging * refactor: separate queries * fix: several fixes * test: update postman collection * fix: set `oid4vc` dependencies to specific `rev` * chore: set `rust-version` and use `workspace.package` settings * feat: add `GET` method for `credentials` endpoint * test: add `GET` method for `credentials` endpoint in postman collection This includes a test for the `POST` method for the `credentials` endpoint that updates the `CREDENTIAL_LOCATION` environment variable * chore: add `/v1/credentials/{credential_id}` to `openapi.yaml` file * chore: bump `axum` dependency to `0.7` * chore: update `axum` related code to version `0.7` * feat: add `log_error_response` macro for cleaner logging of error responses * chore: update POST request to a valid OBv3 `credentialSubject` * chore: use `TraceLayer` for proper tracing in `agent_api_rest` * `on_request` makes sure that each request is traced * `on_response` makes sure that each response is traced * `on_body_chunk` makes sure that each response body is traced `TraceLayer` only accepts `Request --- .env.example | 1 + Cargo.lock | 85 ++++++++++--- Cargo.toml | 11 +- .../issuance/credential_issuer/credential.rs | 9 +- .../src/issuance/credential_issuer/token.rs | 8 +- .../well_known/oauth_authorization_server.rs | 8 +- .../well_known/openid_credential_issuer.rs | 68 ++++++----- agent_api_rest/src/issuance/credentials.rs | 8 +- agent_api_rest/src/issuance/offers.rs | 43 ++++--- agent_api_rest/src/lib.rs | 65 +++++----- .../verification/authorization_requests.rs | 10 +- .../verification/relying_party/redirect.rs | 14 ++- .../src/verification/relying_party/request.rs | 8 +- agent_application/src/main.rs | 6 +- agent_issuance/src/offer/aggregate.rs | 91 +++++++------- agent_issuance/src/server_config/aggregate.rs | 72 ++++++----- agent_issuance/src/server_config/command.rs | 9 +- agent_issuance/src/server_config/event.rs | 9 +- agent_issuance/src/server_config/queries.rs | 9 +- agent_issuance/src/startup_commands.rs | 67 +++++----- agent_secret_manager/src/aggregate.rs | 6 +- agent_secret_manager/src/commands.rs | 4 +- agent_secret_manager/src/events.rs | 4 +- agent_secret_manager/src/services.rs | 7 +- agent_verification/Cargo.toml | 1 + .../src/authorization_request/aggregate.rs | 114 +++++++++++++----- .../src/connection/aggregate.rs | 49 +++++--- agent_verification/src/services.rs | 13 +- 28 files changed, 499 insertions(+), 300 deletions(-) diff --git a/.env.example b/.env.example index 8e6e5bad..a0a61752 100644 --- a/.env.example +++ b/.env.example @@ -8,4 +8,5 @@ AGENT_SECRET_MANAGER_STRONGHOLD_PASSWORD="secure_password" AGENT_SECRET_MANAGER_ISSUER_DID="did:key:z6Mkv5KkqNHuR6bPVT8fud3m9JaHBSEjEmiLp7HuGAwtbkk6" AGENT_SECRET_MANAGER_ISSUER_FRAGMENT="key-0" AGENT_SECRET_MANAGER_ISSUER_KEY_ID="9O66nzWqYYy1LmmiOudOlh2SMIaUWoTS" +AGENT_CONFIG_DEFAULT_DID_METHOD="did:key" AGENT_STORE_DB_CONNECTION_STRING=postgresql://demo_user:demo_pass@localhost:5432/demo diff --git a/Cargo.lock b/Cargo.lock index 99844f55..3e257209 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -125,7 +125,7 @@ dependencies = [ "async-trait", "cqrs-es", "reqwest 0.12.2", - "rustls 0.23.4", + "rustls 0.23.5", "serde", "serde_json", "serde_with 3.7.0", @@ -225,6 +225,7 @@ dependencies = [ "lazy_static", "oid4vc-core", "oid4vc-manager", + "rstest", "serde", "serial_test", "siopv2", @@ -1179,13 +1180,14 @@ checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" [[package]] name = "consumer" version = "0.1.0" -source = "git+https://git@github.com/impierce/did-manager.git?rev=2765aca#2765acabc40161627105ec17b0ed7de4a7162b43" +source = "git+https://git@github.com/impierce/did-manager.git?rev=11ce737#11ce737f0c8204029b70b831c1a6ed3a54d6ae15" dependencies = [ "did_jwk", "did_key", "did_web", "identity_iota", "iota-sdk", + "shared", "signature 2.2.0", "tokio", ] @@ -1712,7 +1714,7 @@ dependencies = [ [[package]] name = "did_jwk" version = "0.1.0" -source = "git+https://git@github.com/impierce/did-manager.git?rev=2765aca#2765acabc40161627105ec17b0ed7de4a7162b43" +source = "git+https://git@github.com/impierce/did-manager.git?rev=11ce737#11ce737f0c8204029b70b831c1a6ed3a54d6ae15" dependencies = [ "did-jwk", "identity_iota", @@ -1729,7 +1731,7 @@ dependencies = [ [[package]] name = "did_key" version = "0.1.0" -source = "git+https://git@github.com/impierce/did-manager.git?rev=2765aca#2765acabc40161627105ec17b0ed7de4a7162b43" +source = "git+https://git@github.com/impierce/did-manager.git?rev=11ce737#11ce737f0c8204029b70b831c1a6ed3a54d6ae15" dependencies = [ "did-key", "did-method-key", @@ -1748,7 +1750,7 @@ dependencies = [ [[package]] name = "did_manager" version = "0.1.0" -source = "git+https://git@github.com/impierce/did-manager.git?rev=2765aca#2765acabc40161627105ec17b0ed7de4a7162b43" +source = "git+https://git@github.com/impierce/did-manager.git?rev=11ce737#11ce737f0c8204029b70b831c1a6ed3a54d6ae15" dependencies = [ "consumer", "producer", @@ -1776,7 +1778,7 @@ dependencies = [ [[package]] name = "did_web" version = "0.1.0" -source = "git+https://git@github.com/impierce/did-manager.git?rev=2765aca#2765acabc40161627105ec17b0ed7de4a7162b43" +source = "git+https://git@github.com/impierce/did-manager.git?rev=11ce737#11ce737f0c8204029b70b831c1a6ed3a54d6ae15" dependencies = [ "did-web", "identity_iota", @@ -1795,7 +1797,7 @@ dependencies = [ [[package]] name = "dif-presentation-exchange" version = "0.1.0" -source = "git+https://git@github.com/impierce/openid4vc.git?rev=0c5526c#0c5526c029f3a1dca57121b833d49803b0d3e68d" +source = "git+https://git@github.com/impierce/openid4vc.git?rev=a932af7#a932af797117bd8f042335bf29695e00905800e8" dependencies = [ "getset", "jsonpath_lib", @@ -2367,6 +2369,12 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +[[package]] +name = "futures-timer" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" + [[package]] name = "futures-util" version = "0.3.30" @@ -2448,6 +2456,12 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + [[package]] name = "gloo-timers" version = "0.2.6" @@ -4248,7 +4262,7 @@ dependencies = [ [[package]] name = "oid4vc-core" version = "0.1.0" -source = "git+https://git@github.com/impierce/openid4vc.git?rev=0c5526c#0c5526c029f3a1dca57121b833d49803b0d3e68d" +source = "git+https://git@github.com/impierce/openid4vc.git?rev=a932af7#a932af797117bd8f042335bf29695e00905800e8" dependencies = [ "anyhow", "async-trait", @@ -4272,7 +4286,7 @@ dependencies = [ [[package]] name = "oid4vc-manager" version = "0.1.0" -source = "git+https://git@github.com/impierce/openid4vc.git?rev=0c5526c#0c5526c029f3a1dca57121b833d49803b0d3e68d" +source = "git+https://git@github.com/impierce/openid4vc.git?rev=a932af7#a932af797117bd8f042335bf29695e00905800e8" dependencies = [ "anyhow", "async-trait", @@ -4304,7 +4318,7 @@ dependencies = [ [[package]] name = "oid4vci" version = "0.1.0" -source = "git+https://git@github.com/impierce/openid4vc.git?rev=0c5526c#0c5526c029f3a1dca57121b833d49803b0d3e68d" +source = "git+https://git@github.com/impierce/openid4vc.git?rev=a932af7#a932af797117bd8f042335bf29695e00905800e8" dependencies = [ "anyhow", "derivative", @@ -4327,7 +4341,7 @@ dependencies = [ [[package]] name = "oid4vp" version = "0.1.0" -source = "git+https://git@github.com/impierce/openid4vc.git?rev=0c5526c#0c5526c029f3a1dca57121b833d49803b0d3e68d" +source = "git+https://git@github.com/impierce/openid4vc.git?rev=a932af7#a932af797117bd8f042335bf29695e00905800e8" dependencies = [ "anyhow", "chrono", @@ -4987,7 +5001,7 @@ dependencies = [ [[package]] name = "producer" version = "0.1.0" -source = "git+https://git@github.com/impierce/did-manager.git?rev=2765aca#2765acabc40161627105ec17b0ed7de4a7162b43" +source = "git+https://git@github.com/impierce/did-manager.git?rev=11ce737#11ce737f0c8204029b70b831c1a6ed3a54d6ae15" dependencies = [ "anyhow", "async-trait", @@ -4997,19 +5011,16 @@ dependencies = [ "did_key", "did_web", "futures", - "hex", "identity_iota", "identity_stronghold", "iota-sdk", "iota_stronghold", "log", "oid4vc-core", - "rand 0.8.5", "serde", "serde_json", "shared", "signature 2.2.0", - "tokio", "url", ] @@ -5205,6 +5216,12 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +[[package]] +name = "relative-path" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e898588f33fdd5b9420719948f9f2a32c922a246964576f71ba7f24f80610fbc" + [[package]] name = "reqwest" version = "0.11.27" @@ -5465,6 +5482,35 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rstest" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d5316d2a1479eeef1ea21e7f9ddc67c191d497abc8fc3ba2467857abbb68330" +dependencies = [ + "futures", + "futures-timer", + "rstest_macros", + "rustc_version", +] + +[[package]] +name = "rstest_macros" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04a9df72cc1f67020b0d63ad9bfe4a323e459ea7eb68e03bd9824db49f9a4c25" +dependencies = [ + "cfg-if", + "glob", + "proc-macro2", + "quote", + "regex", + "relative-path", + "rustc_version", + "syn 2.0.58", + "unicode-ident", +] + [[package]] name = "rumqttc" version = "0.23.0" @@ -5584,9 +5630,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.4" +version = "0.23.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c4d6d8ad9f2492485e13453acbb291dd08f64441b6609c491f1c2cd2c6b4fe1" +checksum = "afabcee0551bd1aa3e18e5adbf2c0544722014b899adb31bd186ec638d3da97e" dependencies = [ "log", "once_cell", @@ -6024,7 +6070,7 @@ dependencies = [ [[package]] name = "shared" version = "0.1.0" -source = "git+https://git@github.com/impierce/did-manager.git?rev=2765aca#2765acabc40161627105ec17b0ed7de4a7162b43" +source = "git+https://git@github.com/impierce/did-manager.git?rev=11ce737#11ce737f0c8204029b70b831c1a6ed3a54d6ae15" dependencies = [ "identity_iota", "identity_stronghold", @@ -6032,6 +6078,7 @@ dependencies = [ "iota_stronghold", "log", "rand 0.8.5", + "thiserror", ] [[package]] @@ -6090,7 +6137,7 @@ dependencies = [ [[package]] name = "siopv2" version = "0.1.0" -source = "git+https://git@github.com/impierce/openid4vc.git?rev=0c5526c#0c5526c029f3a1dca57121b833d49803b0d3e68d" +source = "git+https://git@github.com/impierce/openid4vc.git?rev=a932af7#a932af797117bd8f042335bf29695e00905800e8" dependencies = [ "anyhow", "async-trait", diff --git a/Cargo.toml b/Cargo.toml index d85324eb..5c29382c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,17 +17,18 @@ edition = "2021" rust-version = "1.76.0" [workspace.dependencies] -did_manager = { git = "https://git@github.com/impierce/did-manager.git", rev = "2765aca" } -siopv2 = { git = "https://git@github.com/impierce/openid4vc.git", rev = "0c5526c" } -oid4vci = { git = "https://git@github.com/impierce/openid4vc.git", rev = "0c5526c" } -oid4vc-core = { git = "https://git@github.com/impierce/openid4vc.git", rev = "0c5526c" } -oid4vc-manager = { git = "https://git@github.com/impierce/openid4vc.git", rev = "0c5526c" } +did_manager = { git = "https://git@github.com/impierce/did-manager.git", rev = "11ce737" } +siopv2 = { git = "https://git@github.com/impierce/openid4vc.git", rev = "a932af7" } +oid4vci = { git = "https://git@github.com/impierce/openid4vc.git", rev = "a932af7" } +oid4vc-core = { git = "https://git@github.com/impierce/openid4vc.git", rev = "a932af7" } +oid4vc-manager = { git = "https://git@github.com/impierce/openid4vc.git", rev = "a932af7" } async-trait = "0.1" axum = { version = "0.7", features = ["tracing"] } cqrs-es = "0.4.2" futures = "0.3" lazy_static = "1.4" +rstest = "0.19" serde = { version = "1.0", default-features = false, features = ["derive"] } serde_json = { version = "1.0" } serde_with = "3.7" diff --git a/agent_api_rest/src/issuance/credential_issuer/credential.rs b/agent_api_rest/src/issuance/credential_issuer/credential.rs index 1ab800d6..f1c36970 100644 --- a/agent_api_rest/src/issuance/credential_issuer/credential.rs +++ b/agent_api_rest/src/issuance/credential_issuer/credential.rs @@ -91,6 +91,7 @@ mod tests { use super::*; use agent_issuance::{startup_commands::startup_commands, state::initialize}; + use agent_shared::config; use agent_store::in_memory; use agent_verification::services::test_utils::test_verification_services; use axum::{ @@ -104,8 +105,11 @@ mod tests { #[tracing_test::traced_test] async fn test_credential_endpoint() { let issuance_state = in_memory::issuance_state().await; - let verification_state = in_memory::verification_state(test_verification_services(), Default::default()).await; - + let verification_state = in_memory::verification_state( + test_verification_services(&config!("default_did_method").unwrap_or("did:key".to_string())), + Default::default(), + ) + .await; initialize(&issuance_state, startup_commands(BASE_URL.clone())).await; let mut app = app((issuance_state, verification_state)); @@ -159,7 +163,6 @@ mod tests { assert_eq!( body, json!({ - "format": "jwt_vc_json", "credential": "eyJ0eXAiOiJKV1QiLCJhbGciOiJFZERTQSIsImtpZCI6ImRpZDprZXk6ejZNa2lp\ ZXlvTE1TVnNKQVp2N0pqZTV3V1NrREV5bVVna3lGOGtiY3JqWnBYM3FkI3o2TWtp\ aWV5b0xNU1ZzSkFadjdKamU1d1dTa0RFeW1VZ2t5RjhrYmNyalpwWDNxZCJ9.eyJ\ diff --git a/agent_api_rest/src/issuance/credential_issuer/token.rs b/agent_api_rest/src/issuance/credential_issuer/token.rs index 74453701..b5ce3c18 100644 --- a/agent_api_rest/src/issuance/credential_issuer/token.rs +++ b/agent_api_rest/src/issuance/credential_issuer/token.rs @@ -61,6 +61,7 @@ pub mod tests { use super::*; use agent_issuance::{startup_commands::startup_commands, state::initialize}; + use agent_shared::config; use agent_store::in_memory; use agent_verification::services::test_utils::test_verification_services; use axum::{ @@ -104,8 +105,11 @@ pub mod tests { #[tokio::test] async fn test_token_endpoint() { let issuance_state = in_memory::issuance_state().await; - let verification_state = in_memory::verification_state(test_verification_services(), Default::default()).await; - + let verification_state = in_memory::verification_state( + test_verification_services(&config!("default_did_method").unwrap_or("did:key".to_string())), + Default::default(), + ) + .await; initialize(&issuance_state, startup_commands(BASE_URL.clone())).await; let mut app = app((issuance_state, verification_state)); diff --git a/agent_api_rest/src/issuance/credential_issuer/well_known/oauth_authorization_server.rs b/agent_api_rest/src/issuance/credential_issuer/well_known/oauth_authorization_server.rs index 09bcac67..f1fa89bc 100644 --- a/agent_api_rest/src/issuance/credential_issuer/well_known/oauth_authorization_server.rs +++ b/agent_api_rest/src/issuance/credential_issuer/well_known/oauth_authorization_server.rs @@ -27,6 +27,7 @@ mod tests { use super::*; use agent_issuance::{startup_commands::startup_commands, state::initialize}; + use agent_shared::config; use agent_store::in_memory; use agent_verification::services::test_utils::test_verification_services; use axum::{ @@ -71,8 +72,11 @@ mod tests { #[tokio::test] async fn test_oauth_authorization_server_endpoint() { let issuance_state = in_memory::issuance_state().await; - let verification_state = in_memory::verification_state(test_verification_services(), Default::default()).await; - + let verification_state = in_memory::verification_state( + test_verification_services(&config!("default_did_method").unwrap_or("did:key".to_string())), + Default::default(), + ) + .await; initialize(&issuance_state, startup_commands(BASE_URL.clone())).await; let mut app = app((issuance_state, verification_state)); diff --git a/agent_api_rest/src/issuance/credential_issuer/well_known/openid_credential_issuer.rs b/agent_api_rest/src/issuance/credential_issuer/well_known/openid_credential_issuer.rs index 877baaef..61a6073a 100644 --- a/agent_api_rest/src/issuance/credential_issuer/well_known/openid_credential_issuer.rs +++ b/agent_api_rest/src/issuance/credential_issuer/well_known/openid_credential_issuer.rs @@ -40,9 +40,9 @@ mod tests { w3c_verifiable_credentials::jwt_vc_json::CredentialDefinition, CredentialFormats, Parameters, }, credential_issuer::{ - credential_issuer_metadata::CredentialIssuerMetadata, credentials_supported::CredentialsSupportedObject, + credential_configurations_supported::CredentialConfigurationsSupportedObject, + credential_issuer_metadata::CredentialIssuerMetadata, }, - ProofType, }; use serde_json::json; use tower::Service; @@ -70,34 +70,37 @@ mod tests { credential_issuer_metadata, CredentialIssuerMetadata { credential_issuer: BASE_URL.clone(), - authorization_server: None, credential_endpoint: BASE_URL.append_path_segment("openid4vci/credential"), - batch_credential_endpoint: None, - deferred_credential_endpoint: None, - credentials_supported: vec![CredentialsSupportedObject { - id: None, - credential_format: CredentialFormats::JwtVcJson(Parameters { - parameters: ( - CredentialDefinition { - type_: vec!["VerifiableCredential".to_string(), "OpenBadgeCredential".to_string()], - credential_subject: None, - }, - None, - ) - .into(), - }), - scope: None, - cryptographic_binding_methods_supported: Some(vec!["did:key".to_string()]), - cryptographic_suites_supported: Some(vec!["EdDSA".to_string()]), - proof_types_supported: Some(vec![ProofType::Jwt]), - display: Some(vec![json!({ - "name": config!("credential_name").unwrap(), - "logo": { - "url": config!("credential_logo_url").unwrap() - } - })]), - }], - display: None, + credential_configurations_supported: vec![( + "badge".to_string(), + CredentialConfigurationsSupportedObject { + credential_format: CredentialFormats::JwtVcJson(Parameters { + parameters: ( + CredentialDefinition { + type_: vec!["VerifiableCredential".to_string(), "OpenBadgeCredential".to_string()], + credential_subject: Default::default(), + }, + None, + ) + .into(), + }), + scope: None, + cryptographic_binding_methods_supported: vec!["did:key".to_string()], + credential_signing_alg_values_supported: vec!["EdDSA".to_string()], + // TODO + // proof_types_supported: Some(vec![ProofType::Jwt]), + display: vec![json!({ + "name": config!("credential_name").unwrap(), + "logo": { + "url": config!("credential_logo_url").unwrap() + } + })], + ..Default::default() + } + )] + .into_iter() + .collect(), + ..Default::default() } ); @@ -107,8 +110,11 @@ mod tests { #[tokio::test] async fn test_oauth_authorization_server_endpoint() { let issuance_state = in_memory::issuance_state().await; - let verification_state = in_memory::verification_state(test_verification_services(), Default::default()).await; - + let verification_state = in_memory::verification_state( + test_verification_services(&config!("default_did_method").unwrap_or("did:key".to_string())), + Default::default(), + ) + .await; initialize(&issuance_state, startup_commands(BASE_URL.clone())).await; let mut app = app((issuance_state, verification_state)); diff --git a/agent_api_rest/src/issuance/credentials.rs b/agent_api_rest/src/issuance/credentials.rs index dc1d782d..00922d66 100644 --- a/agent_api_rest/src/issuance/credentials.rs +++ b/agent_api_rest/src/issuance/credentials.rs @@ -103,6 +103,7 @@ pub mod tests { tests::{BASE_URL, SUBJECT_ID}, }; use agent_issuance::{startup_commands::startup_commands, state::initialize}; + use agent_shared::config; use agent_store::in_memory; use agent_verification::services::test_utils::test_verification_services; use axum::{ @@ -197,8 +198,11 @@ pub mod tests { #[tokio::test] async fn test_credentials_endpoint() { let issuance_state = in_memory::issuance_state().await; - let verification_state = in_memory::verification_state(test_verification_services(), Default::default()).await; - + let verification_state = in_memory::verification_state( + test_verification_services(&config!("default_did_method").unwrap_or("did:key".to_string())), + Default::default(), + ) + .await; initialize(&issuance_state, startup_commands(BASE_URL.clone())).await; let mut app = app((issuance_state, verification_state)); diff --git a/agent_api_rest/src/issuance/offers.rs b/agent_api_rest/src/issuance/offers.rs index 7ed62a19..728c2f10 100644 --- a/agent_api_rest/src/issuance/offers.rs +++ b/agent_api_rest/src/issuance/offers.rs @@ -69,6 +69,7 @@ pub mod tests { use super::*; use agent_issuance::{startup_commands::startup_commands, state::initialize}; + use agent_shared::config; use agent_store::in_memory; use agent_verification::services::test_utils::test_verification_services; use axum::{ @@ -76,7 +77,7 @@ pub mod tests { http::{self, Request}, Router, }; - use oid4vci::credential_offer::{CredentialOffer, CredentialOfferQuery, Grants, PreAuthorizedCode}; + use oid4vci::credential_offer::{CredentialOffer, CredentialOfferParameters, Grants, PreAuthorizedCode}; use serde_json::json; use tower::Service; @@ -107,28 +108,36 @@ pub mod tests { let body = axum::body::to_bytes(response.into_body(), usize::MAX).await.unwrap(); let body: String = String::from_utf8(body.to_vec()).unwrap(); - let CredentialOfferQuery::CredentialOffer(CredentialOffer { - grants: - Some(Grants { - pre_authorized_code: - Some(PreAuthorizedCode { - pre_authorized_code, .. - }), - .. - }), - .. - }) = CredentialOfferQuery::from_str(&body).unwrap() - else { + if let CredentialOffer::CredentialOffer(credential_offer) = CredentialOffer::from_str(&body).unwrap() { + let CredentialOfferParameters { + grants: + Some(Grants { + pre_authorized_code: + Some(PreAuthorizedCode { + pre_authorized_code, .. + }), + .. + }), + .. + } = *credential_offer + else { + unreachable!() + }; + pre_authorized_code + } else { unreachable!() - }; - - pre_authorized_code + } } #[tokio::test] async fn test_offers_endpoint() { let issuance_state = in_memory::issuance_state().await; - let verification_state = in_memory::verification_state(test_verification_services(), Default::default()).await; + + let verification_state = in_memory::verification_state( + test_verification_services(&config!("default_did_method").unwrap_or("did:key".to_string())), + Default::default(), + ) + .await; initialize(&issuance_state, startup_commands(BASE_URL.clone())).await; diff --git a/agent_api_rest/src/lib.rs b/agent_api_rest/src/lib.rs index 73fe5c8a..c0d7fe61 100644 --- a/agent_api_rest/src/lib.rs +++ b/agent_api_rest/src/lib.rs @@ -114,11 +114,15 @@ fn get_base_path() -> Result { #[cfg(test)] mod tests { + use std::collections::HashMap; + + use agent_shared::config; use agent_store::in_memory; use agent_verification::services::test_utils::test_verification_services; use axum::routing::post; use oid4vci::credential_issuer::{ - credential_issuer_metadata::CredentialIssuerMetadata, credentials_supported::CredentialsSupportedObject, + credential_configurations_supported::CredentialConfigurationsSupportedObject, + credential_issuer_metadata::CredentialIssuerMetadata, }; use serde_json::json; @@ -128,34 +132,38 @@ mod tests { lazy_static::lazy_static! { pub static ref BASE_URL: url::Url = url::Url::parse("https://example.com").unwrap(); - static ref CREDENTIALS_SUPPORTED: Vec = vec![serde_json::from_value(json!({ - "format": "jwt_vc_json", - "cryptographic_binding_methods_supported": [ - "did:key", - ], - "cryptographic_suites_supported": [ - "EdDSA" - ], - "credential_definition":{ - "type": [ - "VerifiableCredential", - "OpenBadgeCredential" - ] - }, - "proof_types_supported": [ - "jwt" - ] - } - )) - .unwrap()]; + static ref CREDENTIAL_CONFIGURATIONS_SUPPORTED: HashMap = + vec![( + "0".to_string(), + serde_json::from_value(json!({ + "format": "jwt_vc_json", + "cryptographic_binding_methods_supported": [ + "did:key", + ], + "credential_signing_alg_values_supported": [ + "EdDSA" + ], + "credential_definition":{ + "type": [ + "VerifiableCredential", + "OpenBadgeCredential" + ] + }, + "proof_types_supported": [ + "jwt" + ] + } + )) + .unwrap() + )] + .into_iter() + .collect(); pub static ref CREDENTIAL_ISSUER_METADATA: CredentialIssuerMetadata = CredentialIssuerMetadata { credential_issuer: BASE_URL.clone(), - authorization_server: None, credential_endpoint: BASE_URL.join("credential").unwrap(), - deferred_credential_endpoint: None, batch_credential_endpoint: Some(BASE_URL.join("batch_credential").unwrap()), - credentials_supported: CREDENTIALS_SUPPORTED.clone(), - display: None, + credential_configurations_supported: CREDENTIAL_CONFIGURATIONS_SUPPORTED.clone(), + ..Default::default() }; } @@ -165,8 +173,11 @@ mod tests { #[should_panic] async fn test_base_path_routes() { let issuance_state = in_memory::issuance_state().await; - let verification_state = in_memory::verification_state(test_verification_services(), Default::default()).await; - + let verification_state = in_memory::verification_state( + test_verification_services(&config!("default_did_method").unwrap_or("did:key".to_string())), + Default::default(), + ) + .await; std::env::set_var("AGENT_APPLICATION_BASE_PATH", "unicore"); let router = app((issuance_state, verification_state)); diff --git a/agent_api_rest/src/verification/authorization_requests.rs b/agent_api_rest/src/verification/authorization_requests.rs index d44a2af4..5d3ff82a 100644 --- a/agent_api_rest/src/verification/authorization_requests.rs +++ b/agent_api_rest/src/verification/authorization_requests.rs @@ -94,6 +94,7 @@ pub(crate) async fn authorization_requests( pub mod tests { use super::*; use crate::app; + use agent_shared::config; use agent_store::in_memory; use agent_verification::services::test_utils::test_verification_services; use axum::{ @@ -140,7 +141,7 @@ pub mod tests { let body = axum::body::to_bytes(response.into_body(), usize::MAX).await.unwrap(); let form_url_encoded_authorization_request: String = String::from_utf8(body.to_vec()).unwrap(); - assert_eq!(form_url_encoded_authorization_request, format!("siopv2://idtoken?client_id=did%3Akey%3Az6MkiieyoLMSVsJAZv7Jje5wWSkDEymUgkyF8kbcrjZpX3qd&request_uri=https%3A%2F%2Fmy-domain.example.org%2Frequest%2F{state}")); + assert_eq!(form_url_encoded_authorization_request, format!("openid://?client_id=did%3Akey%3Az6MkiieyoLMSVsJAZv7Jje5wWSkDEymUgkyF8kbcrjZpX3qd&request_uri=https%3A%2F%2Fmy-domain.example.org%2Frequest%2F{state}")); let response = app .call( @@ -163,8 +164,11 @@ pub mod tests { #[tracing_test::traced_test] async fn test_authorization_requests_endpoint() { let issuance_state = in_memory::issuance_state().await; - let verification_state = in_memory::verification_state(test_verification_services(), Default::default()).await; - + let verification_state = in_memory::verification_state( + test_verification_services(&config!("default_did_method").unwrap_or("did:key".to_string())), + Default::default(), + ) + .await; let mut app = app((issuance_state, verification_state)); authorization_requests(&mut app).await; diff --git a/agent_api_rest/src/verification/relying_party/redirect.rs b/agent_api_rest/src/verification/relying_party/redirect.rs index 2d4974d9..4cc2986e 100644 --- a/agent_api_rest/src/verification/relying_party/redirect.rs +++ b/agent_api_rest/src/verification/relying_party/redirect.rs @@ -65,6 +65,7 @@ pub mod tests { }; use agent_event_publisher_http::{EventPublisherHttp, TEST_EVENT_PUBLISHER_HTTP_CONFIG}; use agent_secret_manager::secret_manager; + use agent_shared::config; use agent_store::{in_memory, EventPublisher}; use agent_verification::services::test_utils::test_verification_services; use axum::{ @@ -106,8 +107,11 @@ pub mod tests { .build() .unwrap(); - let provider_manager = - ProviderManager::new([Arc::new(futures::executor::block_on(async { secret_manager().await }))]).unwrap(); + let provider_manager = ProviderManager::new( + Arc::new(futures::executor::block_on(async { secret_manager().await })), + "did:key", + ) + .unwrap(); let authorization_response = provider_manager .generate_response(&authorization_request, Default::default()) .unwrap(); @@ -162,7 +166,11 @@ pub mod tests { let event_publishers = vec![Box::new(EventPublisherHttp::load().unwrap()) as Box]; let issuance_state = in_memory::issuance_state().await; - let verification_state = in_memory::verification_state(test_verification_services(), event_publishers).await; + let verification_state = in_memory::verification_state( + test_verification_services(&config!("default_did_method").unwrap_or("did:key".to_string())), + event_publishers, + ) + .await; let mut app = app((issuance_state, verification_state)); diff --git a/agent_api_rest/src/verification/relying_party/request.rs b/agent_api_rest/src/verification/relying_party/request.rs index a78cd03b..11173a7d 100644 --- a/agent_api_rest/src/verification/relying_party/request.rs +++ b/agent_api_rest/src/verification/relying_party/request.rs @@ -34,6 +34,7 @@ pub(crate) async fn request( pub mod tests { use super::*; use crate::{app, verification::authorization_requests::tests::authorization_requests}; + use agent_shared::config; use agent_store::in_memory; use agent_verification::services::test_utils::test_verification_services; use axum::{ @@ -70,8 +71,11 @@ pub mod tests { #[tracing_test::traced_test] async fn test_request_endpoint() { let issuance_state = in_memory::issuance_state().await; - let verification_state = in_memory::verification_state(test_verification_services(), Default::default()).await; - + let verification_state = in_memory::verification_state( + test_verification_services(&config!("default_did_method").unwrap_or("did:key".to_string())), + Default::default(), + ) + .await; let mut app = app((issuance_state, verification_state)); let form_url_encoded_authorization_request = authorization_requests(&mut app).await; diff --git a/agent_application/src/main.rs b/agent_application/src/main.rs index 63b1887f..764a42f3 100644 --- a/agent_application/src/main.rs +++ b/agent_application/src/main.rs @@ -25,6 +25,7 @@ async fn main() { _ => tracing_subscriber.with(tracing_subscriber::fmt::layer()).init(), } + let default_did_method = config!("default_did_method").unwrap_or("did:key".to_string()); let verification_services = Arc::new(VerificationServices::new( Arc::new(secret_manager().await), // TODO: Temporary solution. Remove this once `ClientMetadata` is part of `RelyingPartyManager`. @@ -32,9 +33,12 @@ async fn main() { client_name: None, logo_uri: None, extension: ClientMetadataParameters { - subject_syntax_types_supported: vec![SubjectSyntaxType::Did(DidMethod::from_str("did:key").unwrap())], + subject_syntax_types_supported: vec![SubjectSyntaxType::Did( + DidMethod::from_str(&default_did_method).unwrap(), + )], }, }, + &default_did_method, )); let event_publishers: Vec> = vec![Box::new(EventPublisherHttp::load().unwrap())]; diff --git a/agent_issuance/src/offer/aggregate.rs b/agent_issuance/src/offer/aggregate.rs index 2ece6026..83fd73f0 100644 --- a/agent_issuance/src/offer/aggregate.rs +++ b/agent_issuance/src/offer/aggregate.rs @@ -6,10 +6,9 @@ use async_trait::async_trait; use cqrs_es::Aggregate; use jsonwebtoken::{Algorithm, Header}; use oid4vc_core::authentication::subject::Subject; -use oid4vc_core::{jwt, Decoder, Subjects}; -use oid4vci::credential_format_profiles::{self, CredentialFormats}; +use oid4vc_core::{jwt, Validator}; use oid4vci::credential_issuer::CredentialIssuer; -use oid4vci::credential_offer::{CredentialOffer, CredentialOfferQuery, CredentialsObject, Grants, PreAuthorizedCode}; +use oid4vci::credential_offer::{CredentialOffer, CredentialOfferParameters, Grants, PreAuthorizedCode}; use oid4vci::credential_response::{CredentialResponse, CredentialResponseType}; use oid4vci::token_request::TokenRequest; use oid4vci::token_response::TokenResponse; @@ -74,15 +73,10 @@ impl Aggregate for Offer { CreateFormUrlEncodedCredentialOffer { credential_issuer_metadata, } => { - let credentials_supported = credential_issuer_metadata.credentials_supported.clone(); - let credential_offer = CredentialOfferQuery::CredentialOffer(CredentialOffer { + let credentials_supported = credential_issuer_metadata.credential_configurations_supported.clone(); + let credential_offer = CredentialOffer::CredentialOffer(Box::new(CredentialOfferParameters { credential_issuer: credential_issuer_metadata.credential_issuer.clone(), - credentials: credentials_supported - .iter() - .map(|credentials_supported_object| { - CredentialsObject::ByValue(credentials_supported_object.credential_format.clone()) - }) - .collect(), + credential_configuration_ids: credentials_supported.keys().cloned().collect(), grants: Some(Grants { authorization_code: None, pre_authorized_code: Some(PreAuthorizedCode { @@ -90,7 +84,7 @@ impl Aggregate for Offer { ..Default::default() }), }), - }); + })); Ok(vec![FormUrlEncodedCredentialOfferCreated { form_url_encoded_credential_offer: credential_offer.to_string(), }]) @@ -125,12 +119,15 @@ impl Aggregate for Offer { // TODO: support batch credentials. let mut credential = credentials.pop().ok_or(MissingCredentialError)?; - let issuer = Arc::new(futures::executor::block_on(async { + let (issuer, default_did_method) = futures::executor::block_on(async { let mut services = SecretManagerServices::new(None); services.init().await.unwrap(); - services.secret_manager.unwrap() - })); - let issuer_did = issuer.identifier().unwrap(); + ( + Arc::new(services.secret_manager.unwrap()), + services.default_did_method.clone(), + ) + }); + let issuer_did = issuer.identifier(&default_did_method).unwrap(); let credential_issuer = CredentialIssuer { subject: issuer.clone(), @@ -141,7 +138,7 @@ impl Aggregate for Offer { let proof = credential_issuer .validate_proof( credential_request.proof.ok_or(MissingProofError)?, - Decoder::from(&Subjects::try_from([issuer.clone() as Arc]).unwrap()), + Validator::Subject(issuer.clone()), ) .await .map_err(|e| InvalidProofError(e.to_string()))?; @@ -156,23 +153,23 @@ impl Aggregate for Offer { credential.raw["issuer"] = json!(issuer_did); credential.raw["credentialSubject"]["id"] = json!(subject_did); let credential_response = CredentialResponse { - credential: CredentialResponseType::Immediate(CredentialFormats::JwtVcJson( - credential_format_profiles::Credential { - credential: json!(jwt::encode( - issuer.clone(), - Header::new(Algorithm::EdDSA), - VerifiableCredentialJwt::builder() - .sub(subject_did) - .iss(issuer_did) - .iat(0) - .exp(9999999999i64) - .verifiable_credential(credential.raw) - .build() - .ok(), - ) - .ok()), - }, - )), + credential: CredentialResponseType::Immediate { + credential: json!(jwt::encode( + issuer.clone(), + Header::new(Algorithm::EdDSA), + VerifiableCredentialJwt::builder() + .sub(subject_did) + .iss(issuer_did) + .iat(0) + .exp(9999999999i64) + .verifiable_credential(credential.raw) + .build() + .ok(), + &default_did_method + ) + .ok()), + notification_id: None, + }, c_nonce: None, c_nonce_expires_in: None, }; @@ -226,9 +223,11 @@ pub mod tests { use did_manager::SecretManager; use lazy_static::lazy_static; use oid4vci::{ - credential_format_profiles::{w3c_verifiable_credentials::jwt_vc_json::CredentialDefinition, Parameters}, + credential_format_profiles::{ + w3c_verifiable_credentials::jwt_vc_json::CredentialDefinition, CredentialFormats, Parameters, + }, credential_request::CredentialRequest, - Proof, ProofType, + KeyProofType, ProofType, }; use std::{collections::VecDeque, sync::Mutex}; @@ -502,7 +501,7 @@ pub mod tests { credential: VERIFIABLE_CREDENTIAL_JWT_1.clone(), pre_authorized_code: pre_authorized_code.clone(), access_token: ACCESS_TOKENS.lock().unwrap()[0].clone(), - form_url_encoded_credential_offer: format!("openid-credential-offer://?credential_offer=%7B%22credential_issuer%22%3A%22https%3A%2F%2Fexample.com%2F%22%2C%22credentials%22%3A%5B%7B%22format%22%3A%22jwt_vc_json%22%2C%22credential_definition%22%3A%7B%22type%22%3A%5B%22VerifiableCredential%22%2C%22OpenBadgeCredential%22%5D%7D%7D%5D%2C%22grants%22%3A%7B%22urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Apre-authorized_code%22%3A%7B%22pre-authorized_code%22%3A%22{pre_authorized_code}%22%2C%22user_pin_required%22%3Afalse%7D%7D%7D"), + form_url_encoded_credential_offer: format!("openid-credential-offer://?credential_offer=%7B%22credential_issuer%22%3A%22https%3A%2F%2Fexample.com%2F%22%2C%22credential_configuration_ids%22%3A%5B%220%22%5D%2C%22grants%22%3A%7B%22urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Apre-authorized_code%22%3A%7B%22pre-authorized_code%22%3A%22{pre_authorized_code}%22%7D%7D%7D"), c_nonce: C_NONCES.lock().unwrap()[0].clone(), } } @@ -510,7 +509,7 @@ pub mod tests { fn token_request(subject: TestSubject) -> TokenRequest { TokenRequest::PreAuthorizedCode { pre_authorized_code: subject.pre_authorized_code, - user_pin: None, + tx_code: None, } } @@ -532,21 +531,22 @@ pub mod tests { parameters: ( CredentialDefinition { type_: vec!["VerifiableCredential".to_string(), "OpenBadgeCredential".to_string()], - credential_subject: None, + credential_subject: Default::default(), }, None, ) .into(), }), proof: Some( - Proof::builder() + KeyProofType::builder() .proof_type(ProofType::Jwt) .signer(subject.secret_manager.clone()) - .iss(subject.secret_manager.identifier().unwrap()) + .iss(subject.secret_manager.identifier("did:key").unwrap()) .aud(CREDENTIAL_ISSUER_METADATA.credential_issuer.clone()) .iat(1571324800) .exp(9999999999i64) .nonce(subject.c_nonce.clone()) + .subject_syntax_type("did:key") .build() .unwrap(), ), @@ -555,11 +555,10 @@ pub mod tests { fn credential_response(subject: TestSubject) -> CredentialResponse { CredentialResponse { - credential: CredentialResponseType::Immediate(CredentialFormats::JwtVcJson( - credential_format_profiles::Credential { - credential: json!(subject.credential.clone()), - }, - )), + credential: CredentialResponseType::Immediate { + credential: json!(subject.credential.clone()), + notification_id: None, + }, c_nonce: None, c_nonce_expires_in: None, } diff --git a/agent_issuance/src/server_config/aggregate.rs b/agent_issuance/src/server_config/aggregate.rs index 1838bc74..8b0f9821 100644 --- a/agent_issuance/src/server_config/aggregate.rs +++ b/agent_issuance/src/server_config/aggregate.rs @@ -48,9 +48,11 @@ impl Aggregate for ServerConfig { credential_issuer_metadata, }]), - CreateCredentialsSupported { credentials_supported } => { - Ok(vec![CredentialsSupportedCreated { credentials_supported }]) - } + CreateCredentialsSupported { + credential_configurations_supported, + } => Ok(vec![CredentialsSupportedCreated { + credential_configurations_supported, + }]), } } @@ -67,8 +69,11 @@ impl Aggregate for ServerConfig { self.authorization_server_metadata = *authorization_server_metadata; self.credential_issuer_metadata = credential_issuer_metadata; } - CredentialsSupportedCreated { credentials_supported } => { - self.credential_issuer_metadata.credentials_supported = credentials_supported + CredentialsSupportedCreated { + credential_configurations_supported, + } => { + self.credential_issuer_metadata.credential_configurations_supported = + credential_configurations_supported } } } @@ -76,10 +81,12 @@ impl Aggregate for ServerConfig { #[cfg(test)] pub mod server_config_tests { + use std::collections::HashMap; + use super::*; use lazy_static::lazy_static; - use oid4vci::credential_issuer::credentials_supported::CredentialsSupportedObject; + use oid4vci::credential_issuer::credential_configurations_supported::CredentialConfigurationsSupportedObject; use serde_json::json; use cqrs_es::test::TestFramework; @@ -110,35 +117,38 @@ pub mod server_config_tests { credential_issuer_metadata: CREDENTIAL_ISSUER_METADATA.clone(), }]) .when(ServerConfigCommand::CreateCredentialsSupported { - credentials_supported: CREDENTIALS_SUPPORTED.clone(), + credential_configurations_supported: CREDENTIAL_CONFIGURATIONS_SUPPORTED.clone(), }) .then_expect_events(vec![ServerConfigEvent::CredentialsSupportedCreated { - credentials_supported: CREDENTIALS_SUPPORTED.clone(), + credential_configurations_supported: CREDENTIAL_CONFIGURATIONS_SUPPORTED.clone(), }]); } lazy_static! { static ref BASE_URL: url::Url = "https://example.com/".parse().unwrap(); - static ref CREDENTIALS_SUPPORTED: Vec = vec![serde_json::from_value(json!({ - "format": "jwt_vc_json", - "cryptographic_binding_methods_supported": [ - "did:key", - ], - "cryptographic_suites_supported": [ - "EdDSA" - ], - "credential_definition":{ - "type": [ - "VerifiableCredential", - "OpenBadgeCredential" - ] - }, - "proof_types_supported": [ - "jwt" - ] - } - )) - .unwrap()]; + static ref CREDENTIAL_CONFIGURATIONS_SUPPORTED: HashMap = + vec![( + "0".to_string(), + serde_json::from_value(json!({ + "format": "jwt_vc_json", + "cryptographic_binding_methods_supported": [ + "did:key", + ], + "credential_signing_alg_values_supported": [ + "EdDSA" + ], + "credential_definition":{ + "type": [ + "VerifiableCredential", + "OpenBadgeCredential" + ] + } + } + )) + .unwrap() + )] + .into_iter() + .collect(); pub static ref AUTHORIZATION_SERVER_METADATA: Box = Box::new(AuthorizationServerMetadata { issuer: BASE_URL.clone(), @@ -147,12 +157,10 @@ pub mod server_config_tests { }); pub static ref CREDENTIAL_ISSUER_METADATA: CredentialIssuerMetadata = CredentialIssuerMetadata { credential_issuer: BASE_URL.clone(), - authorization_server: None, credential_endpoint: BASE_URL.join("credential").unwrap(), - deferred_credential_endpoint: None, batch_credential_endpoint: Some(BASE_URL.join("batch_credential").unwrap()), - credentials_supported: CREDENTIALS_SUPPORTED.clone(), - display: None, + credential_configurations_supported: CREDENTIAL_CONFIGURATIONS_SUPPORTED.clone(), + ..Default::default() }; } } diff --git a/agent_issuance/src/server_config/command.rs b/agent_issuance/src/server_config/command.rs index 09a7adb8..f12915af 100644 --- a/agent_issuance/src/server_config/command.rs +++ b/agent_issuance/src/server_config/command.rs @@ -1,6 +1,9 @@ +use std::collections::HashMap; + use oid4vci::credential_issuer::{ - authorization_server_metadata::AuthorizationServerMetadata, credential_issuer_metadata::CredentialIssuerMetadata, - credentials_supported::CredentialsSupportedObject, + authorization_server_metadata::AuthorizationServerMetadata, + credential_configurations_supported::CredentialConfigurationsSupportedObject, + credential_issuer_metadata::CredentialIssuerMetadata, }; use serde::Deserialize; @@ -12,6 +15,6 @@ pub enum ServerConfigCommand { credential_issuer_metadata: CredentialIssuerMetadata, }, CreateCredentialsSupported { - credentials_supported: Vec, + credential_configurations_supported: HashMap, }, } diff --git a/agent_issuance/src/server_config/event.rs b/agent_issuance/src/server_config/event.rs index 6f418976..7ced9e6b 100644 --- a/agent_issuance/src/server_config/event.rs +++ b/agent_issuance/src/server_config/event.rs @@ -1,7 +1,10 @@ +use std::collections::HashMap; + use cqrs_es::DomainEvent; use oid4vci::credential_issuer::{ - authorization_server_metadata::AuthorizationServerMetadata, credential_issuer_metadata::CredentialIssuerMetadata, - credentials_supported::CredentialsSupportedObject, + authorization_server_metadata::AuthorizationServerMetadata, + credential_configurations_supported::CredentialConfigurationsSupportedObject, + credential_issuer_metadata::CredentialIssuerMetadata, }; use serde::{Deserialize, Serialize}; @@ -12,7 +15,7 @@ pub enum ServerConfigEvent { credential_issuer_metadata: CredentialIssuerMetadata, }, CredentialsSupportedCreated { - credentials_supported: Vec, + credential_configurations_supported: HashMap, }, } diff --git a/agent_issuance/src/server_config/queries.rs b/agent_issuance/src/server_config/queries.rs index 4965326a..454246ab 100644 --- a/agent_issuance/src/server_config/queries.rs +++ b/agent_issuance/src/server_config/queries.rs @@ -25,8 +25,13 @@ impl View for ServerConfigView { self.credential_issuer_metadata .replace(credential_issuer_metadata.clone()); } - CredentialsSupportedCreated { credentials_supported } => { - self.credential_issuer_metadata.as_mut().unwrap().credentials_supported = credentials_supported.clone() + CredentialsSupportedCreated { + credential_configurations_supported, + } => { + if let Some(credential_issuer_metadata) = self.credential_issuer_metadata.as_mut() { + credential_issuer_metadata.credential_configurations_supported = + credential_configurations_supported.clone(); + } } } } diff --git a/agent_issuance/src/startup_commands.rs b/agent_issuance/src/startup_commands.rs index fbdfa409..824003fe 100644 --- a/agent_issuance/src/startup_commands.rs +++ b/agent_issuance/src/startup_commands.rs @@ -6,9 +6,9 @@ use oid4vci::{ }, credential_issuer::{ authorization_server_metadata::AuthorizationServerMetadata, - credential_issuer_metadata::CredentialIssuerMetadata, credentials_supported::CredentialsSupportedObject, + credential_configurations_supported::CredentialConfigurationsSupportedObject, + credential_issuer_metadata::CredentialIssuerMetadata, }, - ProofType, }; use serde_json::json; @@ -26,12 +26,8 @@ pub fn load_server_metadata(base_url: url::Url) -> ServerConfigCommand { }), credential_issuer_metadata: CredentialIssuerMetadata { credential_issuer: base_url.clone(), - authorization_server: None, credential_endpoint: base_url.append_path_segment("openid4vci/credential"), - deferred_credential_endpoint: None, - batch_credential_endpoint: None, - credentials_supported: vec![], - display: None, + ..Default::default() }, } } @@ -39,31 +35,38 @@ pub fn load_server_metadata(base_url: url::Url) -> ServerConfigCommand { // TODO: Should not be a static startup command. Should be dynamic based on the configuration and/or updatable. pub fn create_credentials_supported() -> ServerConfigCommand { ServerConfigCommand::CreateCredentialsSupported { - credentials_supported: vec![CredentialsSupportedObject { - id: None, - credential_format: CredentialFormats::JwtVcJson(Parameters { - parameters: ( - CredentialDefinition { - type_: vec!["VerifiableCredential".to_string(), "OpenBadgeCredential".to_string()], - credential_subject: None, - }, - None, - ) - .into(), - }), - scope: None, - cryptographic_binding_methods_supported: Some(vec!["did:key".to_string()]), - cryptographic_suites_supported: Some(vec!["EdDSA".to_string()]), - proof_types_supported: Some(vec![ProofType::Jwt]), - display: match (config!("credential_name"), config!("credential_logo_url")) { - (Ok(name), Ok(logo_url)) => Some(vec![json!({ - "name": name, - "logo": { - "url": logo_url - } - })]), - _ => None, + credential_configurations_supported: vec![( + "badge".to_string(), + CredentialConfigurationsSupportedObject { + credential_format: CredentialFormats::JwtVcJson(Parameters { + parameters: ( + CredentialDefinition { + type_: vec!["VerifiableCredential".to_string(), "OpenBadgeCredential".to_string()], + credential_subject: Default::default(), + }, + None, + ) + .into(), + }), + cryptographic_binding_methods_supported: vec![ + config!("default_did_method").unwrap_or("did:key".to_string()) + ], + credential_signing_alg_values_supported: vec!["EdDSA".to_string()], + // TODO + // proof_types_supported: vec![ProofType::Jwt], + display: match (config!("credential_name"), config!("credential_logo_url")) { + (Ok(name), Ok(logo_url)) => vec![json!({ + "name": name, + "logo": { + "url": logo_url + } + })], + _ => vec![], + }, + ..Default::default() }, - }], + )] + .into_iter() + .collect(), } } diff --git a/agent_secret_manager/src/aggregate.rs b/agent_secret_manager/src/aggregate.rs index 94f9b2b5..e368a8de 100644 --- a/agent_secret_manager/src/aggregate.rs +++ b/agent_secret_manager/src/aggregate.rs @@ -64,7 +64,7 @@ mod aggregate_tests { use super::*; use cqrs_es::test::TestFramework; - use did_manager::Method; + use did_manager::DidMethod; use crate::aggregate::AgentSecretManager; use crate::commands::SecretManagerCommand; @@ -87,8 +87,8 @@ mod aggregate_tests { #[test] fn successfully_enables_did_method() { - let expected = SecretManagerEvent::DidMethodEnabled { method: Method::Key }; - let command = SecretManagerCommand::EnableDidMethod { method: Method::Key }; + let expected = SecretManagerEvent::DidMethodEnabled { method: DidMethod::Key }; + let command = SecretManagerCommand::EnableDidMethod { method: DidMethod::Key }; let services = futures::executor::block_on(async { let mut services = SecretManagerServices::new(None); services.init().await.unwrap(); diff --git a/agent_secret_manager/src/commands.rs b/agent_secret_manager/src/commands.rs index d1ec69b5..a7788fef 100644 --- a/agent_secret_manager/src/commands.rs +++ b/agent_secret_manager/src/commands.rs @@ -1,8 +1,8 @@ -use did_manager::Method; +use did_manager::DidMethod; use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize, Deserialize)] pub enum SecretManagerCommand { Initialize, - EnableDidMethod { method: Method }, + EnableDidMethod { method: DidMethod }, } diff --git a/agent_secret_manager/src/events.rs b/agent_secret_manager/src/events.rs index 674d7c30..cbfcdecd 100644 --- a/agent_secret_manager/src/events.rs +++ b/agent_secret_manager/src/events.rs @@ -1,11 +1,11 @@ use cqrs_es::DomainEvent; -use did_manager::Method; +use did_manager::DidMethod; use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub enum SecretManagerEvent { Initialized, - DidMethodEnabled { method: Method }, + DidMethodEnabled { method: DidMethod }, } impl DomainEvent for SecretManagerEvent { diff --git a/agent_secret_manager/src/services.rs b/agent_secret_manager/src/services.rs index 0baf985e..d3978fce 100644 --- a/agent_secret_manager/src/services.rs +++ b/agent_secret_manager/src/services.rs @@ -4,11 +4,16 @@ use did_manager::SecretManager; pub struct SecretManagerServices { pub secret_manager: Option, + pub default_did_method: String, } impl SecretManagerServices { pub fn new(secret_manager: Option) -> Self { - Self { secret_manager } + let default_did_method = config!("default_did_method").unwrap_or("did:key".to_string()); + Self { + secret_manager, + default_did_method, + } } pub async fn init(&mut self) -> Result<(), std::io::Error> { diff --git a/agent_verification/Cargo.toml b/agent_verification/Cargo.toml index 7719a726..da15997a 100644 --- a/agent_verification/Cargo.toml +++ b/agent_verification/Cargo.toml @@ -28,6 +28,7 @@ agent_verification = { path = ".", features = ["test"] } did_manager.workspace = true lazy_static.workspace = true +rstest.workspace = true serial_test = "3.0" [features] diff --git a/agent_verification/src/authorization_request/aggregate.rs b/agent_verification/src/authorization_request/aggregate.rs index 9102f926..9af967aa 100644 --- a/agent_verification/src/authorization_request/aggregate.rs +++ b/agent_verification/src/authorization_request/aggregate.rs @@ -43,8 +43,9 @@ impl Aggregate for AuthorizationRequest { match command { CreateAuthorizationRequest { state, nonce } => { + let default_subject_syntax_type = services.relying_party.default_subject_syntax_type().to_string(); let verifier = &services.verifier; - let verifier_did = verifier.identifier().unwrap(); + let verifier_did = verifier.identifier(&default_subject_syntax_type).unwrap(); let url = config!("url").unwrap(); let request_uri = format!("{url}/request/{state}").parse().unwrap(); @@ -64,6 +65,7 @@ impl Aggregate for AuthorizationRequest { ); let form_url_encoded_authorization_request = oid4vc_core::authorization_request::AuthorizationRequest { + custom_url_scheme: "openid".to_string(), body: ByReference { client_id: verifier_did, request_uri, @@ -126,6 +128,7 @@ pub mod tests { use lazy_static::lazy_static; use oid4vc_core::Subject; use oid4vc_core::{client_metadata::ClientMetadataResource, DidMethod, SubjectSyntaxType}; + use rstest::rstest; use siopv2::authorization_request::ClientMetadataParameters; use crate::services::test_utils::test_verification_services; @@ -134,10 +137,10 @@ pub mod tests { type AuthorizationRequestTestFramework = TestFramework; - #[test] + #[rstest] #[serial_test::serial] - fn test_create_authorization_request() { - let verification_services = test_verification_services(); + fn test_create_authorization_request(#[values("did:key", "did:jwk")] verifier_did_method: &str) { + let verification_services = test_verification_services(verifier_did_method); AuthorizationRequestTestFramework::with(verification_services) .given_no_previous_events() @@ -147,64 +150,91 @@ pub mod tests { }) .then_expect_events(vec![ AuthorizationRequestEvent::AuthorizationRequestCreated { - authorization_request: Box::new(SIOPV2_AUTHORIZATION_REQUEST.clone()), + authorization_request: Box::new(siopv2_authorization_request(verifier_did_method)), }, AuthorizationRequestEvent::FormUrlEncodedAuthorizationRequestCreated { - form_url_encoded_authorization_request: FORM_URL_ENCODED_AUTHORIZATION_REQUEST.clone(), + form_url_encoded_authorization_request: form_url_encoded_authorization_request(verifier_did_method), }, ]); } - #[test] + #[rstest] #[serial_test::serial] - fn test_sign_authorization_request_object() { - let verification_services = test_verification_services(); + fn test_sign_authorization_request_object(#[values("did:key", "did:jwk")] verifier_did_method: &str) { + let verification_services = test_verification_services(verifier_did_method); AuthorizationRequestTestFramework::with(verification_services) .given(vec![ AuthorizationRequestEvent::AuthorizationRequestCreated { - authorization_request: Box::new(SIOPV2_AUTHORIZATION_REQUEST.clone()), + authorization_request: Box::new(siopv2_authorization_request(verifier_did_method)), }, AuthorizationRequestEvent::FormUrlEncodedAuthorizationRequestCreated { - form_url_encoded_authorization_request: FORM_URL_ENCODED_AUTHORIZATION_REQUEST.clone(), + form_url_encoded_authorization_request: form_url_encoded_authorization_request(verifier_did_method), }, ]) .when(AuthorizationRequestCommand::SignAuthorizationRequestObject) .then_expect_events(vec![AuthorizationRequestEvent::AuthorizationRequestObjectSigned { - signed_authorization_request_object: SIGNED_AUTHORIZATION_REQUEST_OBJECT.clone(), + signed_authorization_request_object: signed_authorization_request_object(verifier_did_method), }]); } - lazy_static! { - static ref VERIFIER: SecretManager = futures::executor::block_on(async { secret_manager().await }); - static ref VERIFIER_DID: String = VERIFIER.identifier().unwrap(); - static ref REDIRECT_URI: url::Url = "https://my-domain.example.org/redirect".parse::().unwrap(); - static ref CLIENT_METADATA: ClientMetadataResource = - ClientMetadataResource::ClientMetadata { - client_name: None, - logo_uri: None, - extension: ClientMetadataParameters { - subject_syntax_types_supported: vec![SubjectSyntaxType::Did( - DidMethod::from_str("did:key").unwrap(), - )], - }, - }; - pub static ref SIOPV2_AUTHORIZATION_REQUEST: SIOPv2AuthorizationRequest = SIOPv2AuthorizationRequest::builder() - .client_id(VERIFIER_DID.clone()) + fn verifier_did(did_method: &str) -> String { + VERIFIER.identifier(did_method).unwrap() + } + + pub fn client_metadata(did_method: &str) -> ClientMetadataResource { + ClientMetadataResource::ClientMetadata { + client_name: None, + logo_uri: None, + extension: ClientMetadataParameters { + subject_syntax_types_supported: vec![SubjectSyntaxType::Did(DidMethod::from_str(did_method).unwrap())], + }, + } + } + + pub fn siopv2_authorization_request(did_method: &str) -> SIOPv2AuthorizationRequest { + SIOPv2AuthorizationRequest::builder() + .client_id(verifier_did(did_method)) .scope(Scope::openid()) .redirect_uri(REDIRECT_URI.clone()) .response_mode("direct_post".to_string()) - .client_metadata(CLIENT_METADATA.clone()) + .client_metadata(client_metadata(did_method)) .nonce("nonce".to_string()) .state("state".to_string()) .build() - .unwrap(); - static ref FORM_URL_ENCODED_AUTHORIZATION_REQUEST: String = "\ - siopv2://idtoken?\ + .unwrap() + } + + pub fn form_url_encoded_authorization_request(did_method: &str) -> String { + match did_method { + "did:key" => FORM_URL_ENCODED_AUTHORIZATION_REQUEST_DID_KEY.clone(), + "did:jwk" => FORM_URL_ENCODED_AUTHORIZATION_REQUEST_DID_JWK.clone(), + _ => unimplemented!("Unknown DID method: {}", did_method), + } + } + + pub fn signed_authorization_request_object(did_method: &str) -> String { + match did_method { + "did:key" => SIGNED_AUTHORIZATION_REQUEST_OBJECT_DID_KEY.clone(), + "did:jwk" => SIGNED_AUTHORIZATION_REQUEST_OBJECT_DID_JWK.clone(), + _ => unimplemented!("Unknown DID method: {}", did_method), + } + } + + lazy_static! { + static ref VERIFIER: SecretManager = futures::executor::block_on(async { secret_manager().await }); + pub static ref REDIRECT_URI: url::Url = "https://my-domain.example.org/redirect".parse::().unwrap(); + static ref FORM_URL_ENCODED_AUTHORIZATION_REQUEST_DID_KEY: String = "\ + openid://?\ client_id=did%3Akey%3Az6MkiieyoLMSVsJAZv7Jje5wWSkDEymUgkyF8kbcrjZpX3qd&\ request_uri=https%3A%2F%2Fmy-domain.example.org%2Frequest%2Fstate" .to_string(); - static ref SIGNED_AUTHORIZATION_REQUEST_OBJECT: String = + static ref FORM_URL_ENCODED_AUTHORIZATION_REQUEST_DID_JWK: String = "\ + openid://?\ + client_id=did%3Ajwk%3AeyJhbGciOiJFZERTQSIsImNydiI6IkVkMjU1MTkiLCJraWQiOiJhSHEtMFBJZjZfbGpMaHl4NFc4Nkd2aXFiLTY3MU9BSTY3RTZ2WHBaYzdRIiwia3R5IjoiT0tQIiwieCI6IlAyQmtZUzZ6NFVIbXN4bjZGWDFvSHN5eDdlaVVTRkVNSjFEX1JDOE0wLXcifQ&\ + request_uri=https%3A%2F%2Fmy-domain.example.org%2Frequest%2Fstate" + .to_string(); + static ref SIGNED_AUTHORIZATION_REQUEST_OBJECT_DID_KEY: String = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFZERTQSIsImtpZCI6ImRpZDprZXk6ejZNa2lp\ ZXlvTE1TVnNKQVp2N0pqZTV3V1NrREV5bVVna3lGOGtiY3JqWnBYM3FkI3o2TWtp\ aWV5b0xNU1ZzSkFadjdKamU1d1dTa0RFeW1VZ2t5RjhrYmNyalpwWDNxZCJ9.eyJ\ @@ -217,5 +247,23 @@ pub mod tests { kOmtleSJdfX0.Q9SLE69k4qk1L72yHq3PlY0YyZm1m9do7Wlu3HjzjbHnKnzB6gT\ 5ZfG04krgRf99CgyVeDh9DKnUGrHBUQN2CA" .to_string(); + static ref SIGNED_AUTHORIZATION_REQUEST_OBJECT_DID_JWK: String = + "eyJ0eXAiOiJKV1QiLCJhbGciOiJFZERTQSIsImtpZCI6ImRpZDpqd2s6ZXlKaGJH\ + Y2lPaUpGWkVSVFFTSXNJbU55ZGlJNklrVmtNalUxTVRraUxDSnJhV1FpT2lKaFNI\ + RXRNRkJKWmpaZmJHcE1hSGw0TkZjNE5rZDJhWEZpTFRZM01VOUJTVFkzUlRaMldI\ + QmFZemRSSWl3aWEzUjVJam9pVDB0UUlpd2llQ0k2SWxBeVFtdFpVelo2TkZWSWJY\ + TjRialpHV0RGdlNITjVlRGRsYVZWVFJrVk5TakZFWDFKRE9FMHdMWGNpZlEjMCJ9\ + .eyJjbGllbnRfaWQiOiJkaWQ6andrOmV5SmhiR2NpT2lKRlpFUlRRU0lzSW1OeWR\ + pSTZJa1ZrTWpVMU1Ua2lMQ0pyYVdRaU9pSmhTSEV0TUZCSlpqWmZiR3BNYUhsNE5\ + GYzROa2QyYVhGaUxUWTNNVTlCU1RZM1JUWjJXSEJhWXpkUklpd2lhM1I1SWpvaVQ\ + wdFFJaXdpZUNJNklsQXlRbXRaVXpaNk5GVkliWE40YmpaR1dERnZTSE41ZURkbGF\ + WVlRSa1ZOU2pGRVgxSkRPRTB3TFhjaWZRIiwicmVkaXJlY3RfdXJpIjoiaHR0cHM\ + 6Ly9teS1kb21haW4uZXhhbXBsZS5vcmcvcmVkaXJlY3QiLCJzdGF0ZSI6InN0YXR\ + lIiwicmVzcG9uc2VfdHlwZSI6ImlkX3Rva2VuIiwic2NvcGUiOiJvcGVuaWQiLCJ\ + yZXNwb25zZV9tb2RlIjoiZGlyZWN0X3Bvc3QiLCJub25jZSI6Im5vbmNlIiwiY2x\ + pZW50X21ldGFkYXRhIjp7InN1YmplY3Rfc3ludGF4X3R5cGVzX3N1cHBvcnRlZCI\ + 6WyJkaWQ6andrIl19fQ.Zd-zz7WwTpitagNWUBUAV-PmZ2SP8ceEaLSh4jY-Q2Tw\ + W3NsoNGvTbd2xXy1BG8NP3xW3sqmWzObcc0UN6YqCQ" + .to_string(); } } diff --git a/agent_verification/src/connection/aggregate.rs b/agent_verification/src/connection/aggregate.rs index ac329a9c..cfa1b33f 100644 --- a/agent_verification/src/connection/aggregate.rs +++ b/agent_verification/src/connection/aggregate.rs @@ -77,42 +77,51 @@ pub mod tests { use agent_secret_manager::secret_manager; use cqrs_es::test::TestFramework; - use lazy_static::lazy_static; use oid4vc_core::authorization_response::AuthorizationResponse; use oid4vc_manager::ProviderManager; + use rstest::rstest; - use crate::authorization_request::aggregate::tests::SIOPV2_AUTHORIZATION_REQUEST; + use crate::authorization_request::aggregate::tests::siopv2_authorization_request; use crate::services::test_utils::test_verification_services; use super::*; type ConnectionTestFramework = TestFramework; - #[test] + #[rstest] #[serial_test::serial] - fn test_verify_siopv2_authorization_response() { - let verification_services = test_verification_services(); + fn test_verify_siopv2_authorization_response( + // TODO: add `did:web`, check for other tests as well. Probably should be moved to E2E test. + #[values("did:key", "did:jwk")] verifier_did_method: &str, + #[values("did:key", "did:jwk")] provider_did_method: &str, + ) { + let verification_services = test_verification_services(verifier_did_method); + + let siopv2_authorization_request = siopv2_authorization_request(verifier_did_method); + let siopv2_authorization_response = + siopv2_authorization_response(provider_did_method, &siopv2_authorization_request); + let id_token = siopv2_authorization_response.extension.id_token.clone(); ConnectionTestFramework::with(verification_services) .given_no_previous_events() .when(ConnectionCommand::VerifySIOPv2AuthorizationResponse { - siopv2_authorization_request: SIOPV2_AUTHORIZATION_REQUEST.clone(), - siopv2_authorization_response: SIOPV2_AUTHORIZATION_RESPONSE.clone(), + siopv2_authorization_request, + siopv2_authorization_response, }) - .then_expect_events(vec![ConnectionEvent::SIOPv2AuthorizationResponseVerified { - id_token: ID_TOKEN.clone(), - }]); + .then_expect_events(vec![ConnectionEvent::SIOPv2AuthorizationResponseVerified { id_token }]); } - lazy_static! { - static ref SIOPV2_AUTHORIZATION_RESPONSE: AuthorizationResponse = { - let provider_manager = - ProviderManager::new([Arc::new(futures::executor::block_on(async { secret_manager().await }))]) - .unwrap(); - provider_manager - .generate_response(&SIOPV2_AUTHORIZATION_REQUEST, Default::default()) - .unwrap() - }; - static ref ID_TOKEN: String = SIOPV2_AUTHORIZATION_RESPONSE.extension.id_token.clone(); + fn siopv2_authorization_response( + did_method: &str, + siopv2_authorization_request: &SIOPv2AuthorizationRequest, + ) -> AuthorizationResponse { + let provider_manager = ProviderManager::new( + Arc::new(futures::executor::block_on(async { secret_manager().await })), + did_method, + ) + .unwrap(); + provider_manager + .generate_response(siopv2_authorization_request, Default::default()) + .unwrap() } } diff --git a/agent_verification/src/services.rs b/agent_verification/src/services.rs index e9395215..b4920ece 100644 --- a/agent_verification/src/services.rs +++ b/agent_verification/src/services.rs @@ -12,10 +12,14 @@ pub struct VerificationServices { } impl VerificationServices { - pub fn new(verifier: Arc, client_metadata: ClientMetadataResource) -> Self { + pub fn new( + verifier: Arc, + client_metadata: ClientMetadataResource, + default_did_method: &str, + ) -> Self { Self { verifier: verifier.clone(), - relying_party: RelyingPartyManager::new([verifier]).unwrap(), + relying_party: RelyingPartyManager::new(verifier, default_did_method).unwrap(), client_metadata, } } @@ -31,7 +35,7 @@ pub mod test_utils { use super::*; - pub fn test_verification_services() -> Arc { + pub fn test_verification_services(default_did_method: &str) -> Arc { Arc::new(VerificationServices::new( Arc::new(futures::executor::block_on(async { secret_manager().await })), ClientMetadataResource::ClientMetadata { @@ -39,10 +43,11 @@ pub mod test_utils { logo_uri: None, extension: ClientMetadataParameters { subject_syntax_types_supported: vec![SubjectSyntaxType::Did( - DidMethod::from_str("did:key").unwrap(), + DidMethod::from_str(default_did_method).unwrap(), )], }, }, + default_did_method, )) } }