Skip to content

Commit

Permalink
Merge branch 'dev' into chore/bump-oid4vc
Browse files Browse the repository at this point in the history
  • Loading branch information
nanderstabel committed Apr 17, 2024
2 parents c3aef9e + bbf42d0 commit abd2277
Show file tree
Hide file tree
Showing 16 changed files with 105 additions and 61 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
### 11-04-2024
`/v1/offers` incorrectly returned with Content-Type `application/json`. The Content-Type has now been changed to `application/x-www-form-urlencoded`.

### 24-01-2024

Environment variable `AGENT_APPLICATION_HOST` has changed to `AGENT_APPLICATION_URL` and requires the complete URL. e.g.:
Expand Down
16 changes: 15 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 0 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,10 @@ rust-version = "1.76.0"

[workspace.dependencies]
did_manager = { git = "https://git@github.com/impierce/did-manager.git", rev = "54959ac" }
# did_manager = { path = "../did-manager" }
siopv2 = { git = "https://git@github.com/impierce/openid4vc.git", rev = "13f24f5" }
# siopv2 = { path = "../openid4vc/siopv2" }
oid4vci = { git = "https://git@github.com/impierce/openid4vc.git", rev = "13f24f5" }
# oid4vci = { path = "../openid4vc/oid4vci" }
oid4vc-core = { git = "https://git@github.com/impierce/openid4vc.git", rev = "13f24f5" }
# oid4vc-core = { path = "../openid4vc/oid4vc-core" }
oid4vc-manager = { git = "https://git@github.com/impierce/openid4vc.git", rev = "13f24f5" }
# oid4vc-manager = { path = "../openid4vc/oid4vc-manager" }

async-trait = "0.1"
axum = { version = "0.7", features = ["tracing"] }
Expand Down
6 changes: 3 additions & 3 deletions agent_api_rest/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ paths:
"200":
description: Offer created successfully. Response value should be displayed to the user in the form of a QR code.
content:
text/plain:
application/x-www-form-urlencoded:
schema:
type: string
example: openid-credential-offer://?credential_offer=%7B%22credential_issuer%22%3A%22https%3A%2F%2Fcredential-issuer.example.com%2F%22%2C%22credentials%22%3A%5B%7B%22format%22%3A%22ldp_vc%22%2C%22credential_definition%22%3A%7B%22%40context%22%3A%5B%22https%3A%2F%2Fwww.w3.org%2F2018%2Fcredentials%2Fv1%22%2C%22https%3A%2F%2Fwww.w3.org%2F2018%2Fcredentials%2Fexamples%2Fv1%22%5D%2C%22type%22%3A%5B%22VerifiableCredential%22%2C%22UniversityDegreeCredential%22%5D%7D%7D%5D%7D
Expand Down Expand Up @@ -168,10 +168,10 @@ paths:
example: "/request/43482b98aa2e071231082fc29db7a59f342a643b0c590f71083af3c7ae83f3c3"
description: URL of the created resource
content:
text/plain:
application/x-www-form-urlencoded:
schema:
type: string
example: "openid://?client_id=did%3Akey%3Az6MkiieyoLMSVsJAZv7Jje5wWSkDEymUgkyF8kbcrjZpX3qd&request_uri=http%3A%2F%2F192.168.1.127%3A3033%2Frequest%2F43482b98aa2e071231082fc29db7a59f342a643b0c590f71083af3c7ae83f3c3"
example: "siopv2://idtoken?client_id=did%3Akey%3Az6MkiieyoLMSVsJAZv7Jje5wWSkDEymUgkyF8kbcrjZpX3qd&request_uri=http%3A%2F%2F192.168.1.127%3A3033%2Frequest%2F43482b98aa2e071231082fc29db7a59f342a643b0c590f71083af3c7ae83f3c3"
/v1/authorization_requests/{authorization_requests_id}:
get:
summary: Get the Authorization Request with the given Authorization Request ID
Expand Down
11 changes: 4 additions & 7 deletions agent_api_rest/postman/ssi-agent.postman_collection.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,19 +66,16 @@
"// Split the string on the first '=' character and take the second item",
"const [, secondItem] = decodedString.split('=', 2);",
"",
"// Remove the last character from secondItem",
"const secondItemWithoutLastChar = secondItem.slice(0, -1);",
"",
"const { grants } = JSON.parse(secondItemWithoutLastChar);",
"",
"const pre_authorized_code = grants['urn:ietf:params:oauth:grant-type:pre-authorized_code']['pre-authorized_code'];",
"var jsonObject = JSON.parse(secondItem);",
"const pre_authorized_code = jsonObject.grants['urn:ietf:params:oauth:grant-type:pre-authorized_code']['pre-authorized_code'];",
"",
"if(pre_authorized_code){",
" pm.collectionVariables.set(\"PRE_AUTHORIZED_CODE\",pre_authorized_code)",
"}",
""
],
"type": "text/javascript"
"type": "text/javascript",
"packages": {}
}
}
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ mod tests {
.unwrap();

assert_eq!(response.status(), StatusCode::OK);
assert_eq!(response.headers().get("Content-Type").unwrap(), "application/json");

let body = axum::body::to_bytes(response.into_body(), usize::MAX).await.unwrap();
let body: Value = serde_json::from_slice(&body).unwrap();
Expand Down
1 change: 1 addition & 0 deletions agent_api_rest/src/issuance/credential_issuer/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ pub mod tests {
.unwrap();

assert_eq!(response.status(), StatusCode::OK);
assert_eq!(response.headers().get("Content-Type").unwrap(), "application/json");

let body = axum::body::to_bytes(response.into_body(), usize::MAX).await.unwrap();
let token_response: TokenResponse = serde_json::from_slice(&body).unwrap();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ mod tests {
.unwrap();

assert_eq!(response.status(), StatusCode::OK);
assert_eq!(response.headers().get("Content-Type").unwrap(), "application/json");

let body = axum::body::to_bytes(response.into_body(), usize::MAX).await.unwrap();
let authorization_server_metadata: AuthorizationServerMetadata = serde_json::from_slice(&body).unwrap();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ mod tests {
.unwrap();

assert_eq!(response.status(), StatusCode::OK);
assert_eq!(response.headers().get("Content-Type").unwrap(), "application/json");

let body = axum::body::to_bytes(response.into_body(), usize::MAX).await.unwrap();
let credential_issuer_metadata: CredentialIssuerMetadata = serde_json::from_slice(&body).unwrap();
Expand Down
2 changes: 2 additions & 0 deletions agent_api_rest/src/issuance/credentials.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ pub mod tests {
.unwrap();

assert_eq!(response.status(), StatusCode::CREATED);
assert_eq!(response.headers().get("Content-Type").unwrap(), "application/json");

let get_credentials_endpoint = response
.headers()
Expand All @@ -186,6 +187,7 @@ pub mod tests {
.unwrap();

assert_eq!(response.status(), StatusCode::OK);
assert_eq!(response.headers().get("Content-Type").unwrap(), "application/json");

let body = axum::body::to_bytes(response.into_body(), usize::MAX).await.unwrap();
let body: Value = serde_json::from_slice(&body).unwrap();
Expand Down
53 changes: 31 additions & 22 deletions agent_api_rest/src/issuance/offers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use axum::{
http::StatusCode,
response::{IntoResponse, Response},
};
use hyper::header;
use serde_json::Value;
use tracing::info;

Expand Down Expand Up @@ -46,7 +47,12 @@ pub(crate) async fn offers(State(state): State<IssuanceState>, Json(payload): Js
Ok(Some(OfferView {
form_url_encoded_credential_offer,
..
})) => (StatusCode::OK, Json(form_url_encoded_credential_offer)).into_response(),
})) => (
StatusCode::OK,
[(header::CONTENT_TYPE, "application/x-www-form-urlencoded")],
form_url_encoded_credential_offer,
)
.into_response(),
_ => StatusCode::INTERNAL_SERVER_ERROR.into_response(),
}
}
Expand Down Expand Up @@ -93,31 +99,34 @@ pub mod tests {
.unwrap();

assert_eq!(response.status(), StatusCode::OK);
assert_eq!(
response.headers().get("Content-Type").unwrap(),
"application/x-www-form-urlencoded"
);

let body = axum::body::to_bytes(response.into_body(), usize::MAX).await.unwrap();

let value: Value = serde_json::from_slice(&body).unwrap();
let pre_authorized_code = if let CredentialOffer::CredentialOffer(credential_offer) =
CredentialOffer::from_str(value.as_str().unwrap()).unwrap()
{
let CredentialOfferParameters {
grants:
Some(Grants {
pre_authorized_code:
Some(PreAuthorizedCode {
pre_authorized_code, ..
}),
..
}),
..
} = *credential_offer
else {
let body: String = String::from_utf8(body.to_vec()).unwrap();

let pre_authorized_code =
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
} else {
unreachable!()
};

pre_authorized_code
}
Expand Down
9 changes: 8 additions & 1 deletion agent_api_rest/src/verification/authorization_requests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,10 @@ pub(crate) async fn authorization_requests(
..
})) => (
StatusCode::CREATED,
[(header::LOCATION, &format!("/v1/authorization_requests/{state}"))],
[
(header::LOCATION, format!("/v1/authorization_requests/{state}").as_str()),
(header::CONTENT_TYPE, "application/x-www-form-urlencoded"),
],
form_url_encoded_authorization_request,
)
.into_response(),
Expand Down Expand Up @@ -120,6 +123,10 @@ pub mod tests {
.unwrap();

assert_eq!(response.status(), StatusCode::CREATED);
assert_eq!(
response.headers().get("Content-Type").unwrap(),
"application/x-www-form-urlencoded"
);

let get_request_endpoint = response
.headers()
Expand Down
9 changes: 7 additions & 2 deletions agent_api_rest/src/verification/relying_party/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,23 @@ use axum::{
http::StatusCode,
response::{IntoResponse, Response},
};
use hyper::header;

/// Instead of directly embedding the Authorization Request into a QR-code or deeplink, the `Relying Party` can embed a
/// `request_uri` that points to this endpoint from where the Authorization Request Object can be retrieved.
/// As described here: https://www.rfc-editor.org/rfc/rfc9101.html#name-passing-a-request-object-by-
#[axum_macros::debug_handler]
pub(crate) async fn request(
State(verification_state): State<VerificationState>,
Path(request_id): Path<String>,
) -> Response {
// Return the authorization request object.
match query_handler(&request_id, &verification_state.query.authorization_request).await {
Ok(Some(AuthorizationRequestView {
signed_authorization_request_object: Some(signed_authorization_request_object),
..
})) => (
StatusCode::OK,
// TODO: set the content type to `application/jwt` also check if this is necessary for other endpoints
[(header::CONTENT_TYPE, "application/jwt")],
signed_authorization_request_object,
)
.into_response(),
Expand Down Expand Up @@ -54,6 +57,8 @@ pub mod tests {

assert_eq!(response.status(), StatusCode::OK);

assert_eq!(response.headers().get("Content-Type").unwrap(), "application/jwt");

let body = axum::body::to_bytes(response.into_body(), usize::MAX).await.unwrap();
let body: String = String::from_utf8(body.to_vec()).unwrap();

Expand Down
36 changes: 19 additions & 17 deletions agent_application/docker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,26 @@ Inside the folder `/agent_application/docker`:
2. Optionally, add the following environment variables:
- `AGENT_ISSUANCE_CREDENTIAL_NAME`: To set the name of the credentials that will be issued.
- `AGENT_ISSUANCE_CREDENTIAL_LOGO_URL`: To set the URL of the logo that will be used in the credentials.
3. By default, UniCore will automatically generate a temporary secure Stronghold file which will be used to sign authorization
requests and credentials. Note that using this default option, this Stronghold file will NOT be persisted. If you
want to ensure that the key material that is used for signing data will always be consistent, you will need to supply
an existing Stronghold file. This can be done by mounting the Stronghold file in the
`docker-compose.yml` file. Example:
```yaml
volumes:
- /path/to/stronghold:/app/res/stronghold
```
You will also need to set the following environment variables:
- `AGENT_SECRET_MANAGER_STRONGHOLD_PATH`: The path to the Stronghold file. This value must correspond to the path to which
the Stronghold is mounted. Set to `/app/res/stronghold` by default. It
is recommended to not change this environment variable.
- `AGENT_SECRET_MANAGER_STRONGHOLD_PASSWORD`: To set the password
- `AGENT_SECRET_MANAGER_ISSUER_KEY_ID`: To set the key id
1. Optionally it is possible to configure an HTTP Event Publisher that can listen to certain events in `UniCore`
> [!IMPORTANT]
> 3. By default, UniCore currently uses a default Stronghold file which is used for storing secrets. Using this default
> Stronghold is for testing purposes only and should not be used in production. To use your own Stronghold file, you
> need to mount it in the `docker-compose.yml` file by replacing the default volume. Example:
> ```yaml
> volumes:
> # - ../../agent_secret_manager/tests/res/test.stronghold:/app/res/stronghold # Default Stronghold file
> - /path/to/stronghold:/app/res/stronghold
> ```
> It is recommended to not change the target path `/app/res/stronghold`.
>
> You will also need to set the following environment variables:
> - `AGENT_SECRET_MANAGER_STRONGHOLD_PATH`: The path to the Stronghold file. This value must correspond to the path to which
> the Stronghold is mounted. Set to `/app/res/stronghold` by default. It
> is recommended to not change this environment variable.
> - `AGENT_SECRET_MANAGER_STRONGHOLD_PASSWORD`: To set the password
> - `AGENT_SECRET_MANAGER_ISSUER_KEY_ID`: To set the key id
4. Optionally it is possible to configure an HTTP Event Publisher that can listen to certain events in `UniCore`
and publish them to a `target_url`. More information about the HTTP Event Publisher can be found [here](../../agent_event_publisher_http/README.md).
2. To start the **SSI Agent**, a **Postgres** database along with **pgadmin** (Postgres Admin Interface) simply run:
5. To start the **SSI Agent**, a **Postgres** database along with **pgadmin** (Postgres Admin Interface) simply run:
```bash
docker compose up
Expand Down
6 changes: 5 additions & 1 deletion agent_event_publisher_http/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ agent_verification = { path = "../agent_verification" }
anyhow = "1.0"
async-trait.workspace = true
cqrs-es.workspace = true
openssl = { version = "0.10", features = ["vendored"] }
rustls = { version = "0.23", default-features = false, features = [
"logging",
"std",
"tls12"
] }
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] }
serde.workspace = true
serde_with.workspace = true
Expand Down
6 changes: 4 additions & 2 deletions agent_issuance/src/server_config/queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@ impl View<ServerConfig> for ServerConfigView {
} => {
self.credential_issuer_metadata
.as_mut()
.unwrap()
.credential_configurations_supported = credential_configurations_supported.clone()
.map(|credential_issuer_metadata| {
credential_issuer_metadata.credential_configurations_supported =
credential_configurations_supported.clone()
});
}
}
}
Expand Down

0 comments on commit abd2277

Please sign in to comment.