openid4vc

This library aims to support all the specifications under the OpenID for Verifiable Credentials works.

OpenID for Verifiable Credentials (OID4VC) consists of the following specifications: * OpenID for Verifiable Credential Issuance (OID4VCI) – Defines an API and corresponding OAuth-based authorization mechanisms for issuance of Verifiable Credentials

Description

Currently the Implicit Flow is consists of four major parts:

Example

```rust use anyhow::Result; use asynctrait::asynctrait; use chrono::{Duration, Utc}; use ed25519dalek::{Keypair, Signature, Signer}; use lazystatic::lazy_static; use siopv2::{ claims::{ClaimRequests, ClaimValue, IndividualClaimRequest}, request::ResponseType, Provider, Registration, RelyingParty, RequestUrl, AuthorizationResponse, Scope, AuthorizationRequest, StandardClaims, Subject, Validator, }; use rand::rngs::OsRng; use wiremock::{ http::Method, matchers::{method, path}, Mock, MockServer, ResponseTemplate, };

lazystatic! { pub static ref MOCKKEYPAIR: Keypair = Keypair::generate(&mut OsRng); }

// A Subject type that can be ingested by a Provider

[derive(Default)]

pub struct MySubject;

impl MySubject { pub fn new() -> Self { MySubject {} } }

[async_trait]

impl Subject for MySubject { fn did(&self) -> Result { Ok(did_url::DID::parse("did:mymethod:subject")?) }

fn key_identifier(&self) -> Option<String> {
    Some("key_identifier".to_string())
}

async fn sign<'a>(&self, message: &'a str) -> Result<Vec<u8>> {
    let signature: Signature = MOCK_KEYPAIR.sign(message.as_bytes());
    Ok(signature.to_bytes().to_vec())
}

}

[async_trait]

impl Validator for MySubject { async fn publickey<'a>(&self, _kid: &'a str) -> Result> { Ok(MOCKKEYPAIR.public.tobytes().tovec()) } }

// A Validator type that can be ingested by a RelyingParty

[derive(Default)]

pub struct MyValidator;

[async_trait]

impl Validator for MyValidator { async fn publickey<'a>(&self, _kid: &'a str) -> Result> { Ok(MOCKKEYPAIR.public.tobytes().tovec()) } }

[tokio::main]

async fn main() { // Create a new mock server and retreive it's url. let mockserver = MockServer::start().await; let serverurl = mock_server.uri();

// Create a new validator.
let validator = MySubject::default();

// Create a new relying party.
let relying_party = RelyingParty::new(validator);

// Create a new RequestUrl with response mode `post` for cross-device communication.
let request: AuthorizationRequest = RequestUrl::builder()
    .response_type(ResponseType::IdToken)
    .client_id("did:mymethod:relyingparty".to_string())
    .scope(Scope::openid())
    .redirect_uri(format!("{server_url}/redirect_uri"))
    .response_mode("post".to_string())
    .registration(
        Registration::default()
            .with_subject_syntax_types_supported(vec!["did:mymethod".to_string()])
            .with_id_token_signing_alg_values_supported(vec!["EdDSA".to_string()]),
    )
    .claims(ClaimRequests {
        id_token: Some(StandardClaims {
            name: Some(IndividualClaimRequest::default()),
            ..Default::default()
        }),
        ..Default::default()
    })
    .exp((Utc::now() + Duration::minutes(10)).timestamp())
    .nonce("n-0S6_WzA2Mj".to_string())
    .build()
    .and_then(TryInto::try_into)
    .unwrap();

// Create a new `request_uri` endpoint on the mock server and load it with the JWT encoded `AuthorizationRequest`.
Mock::given(method("GET"))
    .and(path("/request_uri"))
    .respond_with(ResponseTemplate::new(200).set_body_string(relying_party.encode(&request).await.unwrap()))
    .mount(&mock_server)
    .await;

// Create a new `redirect_uri` endpoint on the mock server where the `Provider` will send the `AuthorizationResponse`.
Mock::given(method("POST"))
    .and(path("/redirect_uri"))
    .respond_with(ResponseTemplate::new(200))
    .mount(&mock_server)
    .await;

// Create a new subject.
let subject = MySubject::default();

// Create a new provider.
let provider = Provider::new(subject).await.unwrap();

// Create a new RequestUrl which includes a `request_uri` pointing to the mock server's `request_uri` endpoint.
let request_url = RequestUrl::builder()
    .request_uri(format!("{server_url}/request_uri"))
    .build()
    .unwrap();

// The Provider obtains the reuquest url either by a deeplink or by scanning a QR code. It then validates the
// request. Since in this case the request is a JWT, the provider will fetch the request by sending a GET
// request to mock server's `request_uri` endpoint.
let request = provider.validate_request(request_url).await.unwrap();

// Assert that the request was successfully received by the mock server at the `request_uri` endpoint.
let get_request = mock_server.received_requests().await.unwrap()[0].clone();
assert_eq!(get_request.method, Method::Get);
assert_eq!(get_request.url.path(), "/request_uri");

// Let the provider generate a response based on the validated request. The response is an `IdToken` which is
// encoded as a JWT.
let response = provider
    .generate_response(
        request,
        StandardClaims {
            name: Some(ClaimValue("Jane Doe".to_string())),
            ..Default::default()
        },
    )
    .await
    .unwrap();

// The provider sends it's response to the mock server's `redirect_uri` endpoint.
provider.send_response(response).await.unwrap();

// Assert that the AuthorizationResponse was successfully received by the mock server at the expected endpoint.
let post_request = mock_server.received_requests().await.unwrap()[1].clone();
assert_eq!(post_request.method, Method::Post);
assert_eq!(post_request.url.path(), "/redirect_uri");
let response: AuthorizationResponse = serde_urlencoded::from_bytes(post_request.body.as_slice()).unwrap();

// The `RelyingParty` then validates the response by decoding the header of the id_token, by fetching the public
// key corresponding to the key identifier and finally decoding the id_token using the public key and by
// validating the signature.
let id_token = relying_party.validate_response(&response).await.unwrap();
assert_eq!(
    id_token.standard_claims(),
    &StandardClaims {
        name: Some(ClaimValue("Jane Doe".to_string())),
        ..Default::default()
    }
);

} ```