Skip to content

Commit 28ef59f

Browse files
committed
WIP
1 parent 4086252 commit 28ef59f

19 files changed

+737
-181
lines changed

oid4vc-core/Cargo.toml

+2-1
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,6 @@ url = { version = "2.3.1", features = ["serde"] }
2020
is_empty = "0.2.0"
2121
serde_urlencoded = "0.7.1"
2222
derive_more = "0.99.16"
23-
identity_credential = { git = "https://git@github.com/iotaledger/identity.rs", rev = "4f27434" }
23+
# identity_credential = { git = "https://git@github.com/iotaledger/identity.rs", rev = "4f27434" }
24+
identity_credential = { git = "https://git@github.com/iotaledger/identity.rs", rev = "4f27434", default-features = false, features = ["validator", "credential", "presentation"] }
2425
futures = "0.3"

oid4vc-core/src/lib.rs

+8
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ pub use authentication::{
1414
pub use collection::Collection;
1515
pub use decoder::Decoder;
1616
pub use rfc7519_claims::RFC7519Claims;
17+
use serde::Serialize;
1718
pub use subject_syntax_type::{DidMethod, SubjectSyntaxType};
1819

1920
#[macro_export]
@@ -33,3 +34,10 @@ macro_rules! builder_fn {
3334
}
3435
};
3536
}
37+
38+
// Helper function that allows to serialize custom structs into a query string.
39+
pub fn to_query_value<T: Serialize>(value: &T) -> anyhow::Result<String> {
40+
serde_json::to_string(value)
41+
.map(|s| s.chars().filter(|c| !c.is_whitespace()).collect::<String>())
42+
.map_err(|e| e.into())
43+
}

oid4vc-manager/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ serde_urlencoded = "0.7"
2222
did-key = "0.2"
2323
identity_iota = "0.6"
2424
identity_core = { git = "https://git@github.com/iotaledger/identity.rs", rev = "4f27434" }
25-
identity_credential = { git = "https://git@github.com/iotaledger/identity.rs", rev = "4f27434" }
25+
identity_credential = { git = "https://git@github.com/iotaledger/identity.rs", rev = "4f27434", default-features = false, features = ["validator", "credential", "presentation"] }
2626
axum = "0.6"
2727
reqwest = "0.11"
2828
rand = "0.8"

oid4vc-manager/src/routers/credential_issuer.rs

+114-71
Original file line numberDiff line numberDiff line change
@@ -7,64 +7,83 @@ use axum::{
77
routing::{get, post},
88
Form, Json, Router,
99
};
10+
use identity_credential::credential::Credential;
1011
use oid4vci::{
11-
credential_issuer_metadata::CredentialsSupportedObject,
12+
authorization_server_metadata::AuthorizationServerMetadata,
13+
credential_definition::CredentialDefinition,
14+
credential_issuer::{self, CredentialIssuer, MemStorage, Storage},
15+
credential_issuer_metadata::CredentialIssuerMetadata,
1216
credential_offer::{AuthorizationCode, CredentialOffer, CredentialOfferQuery, Grants, PreAuthorizedCode},
1317
credential_request::CredentialRequest,
1418
credential_response::CredentialResponse,
1519
token_request::TokenRequest,
1620
token_response::TokenResponse,
21+
CredentialFormat, JwtVcJson, JwtVcJsonParameters,
1722
};
18-
use oid4vp::ClaimFormatDesignation;
23+
use oid4vp::{token, ClaimFormatDesignation};
24+
use reqwest::Url;
1925
use std::{
2026
net::TcpListener,
2127
sync::{Arc, Mutex},
2228
};
2329

24-
pub struct Server {
30+
pub struct Server<S: Storage> {
2531
pub listener: TcpListener,
26-
pub credential_types: Arc<Mutex<Vec<String>>>,
27-
pub credentials_supported: Arc<Mutex<CredentialsSupportedObject>>,
28-
pub authorization_code: Arc<Mutex<Option<AuthorizationCode>>>,
29-
pub pre_authorized_code: Arc<Mutex<Option<PreAuthorizedCode>>>,
32+
pub credential_issuer: Arc<Mutex<CredentialIssuer<S>>>,
33+
pub credential_types: Arc<Mutex<Vec<serde_json::Value>>>,
3034
pub nonce: Arc<Mutex<Option<String>>>,
3135
pub access_token: Arc<Mutex<Option<String>>>,
3236
}
3337

3438
#[derive(Debug, Clone)]
35-
pub struct ServerState {
36-
pub credential_types: Arc<Mutex<Vec<String>>>,
37-
pub credentials_supported: Arc<Mutex<CredentialsSupportedObject>>,
38-
pub authorization_code: Arc<Mutex<Option<AuthorizationCode>>>,
39-
pub pre_authorized_code: Arc<Mutex<Option<PreAuthorizedCode>>>,
39+
pub struct ServerState<S: Storage> {
40+
pub credential_issuer: Arc<Mutex<CredentialIssuer<S>>>,
41+
pub credential_types: Arc<Mutex<Vec<serde_json::Value>>>,
4042
pub nonce: Arc<Mutex<Option<String>>>,
4143
pub access_token: Arc<Mutex<Option<String>>>,
4244
}
4345

44-
impl Server {
45-
pub fn new(credentials_supported: CredentialsSupportedObject, listener: Option<TcpListener>) -> Result<Self> {
46-
let listener = listener.unwrap_or_else(|| TcpListener::bind("0.0.0.0:0").unwrap());
46+
impl<S: Storage + Clone> Server<S> {
47+
pub fn new(
48+
mut credential_issuer_metadata: CredentialIssuerMetadata,
49+
listener: Option<TcpListener>,
50+
storage: S,
51+
) -> Result<Self> {
52+
let listener = listener.unwrap_or_else(|| TcpListener::bind("127.0.0.1:0").unwrap());
53+
let issuer_url: Url = format!("http://{:?}", listener.local_addr()?).parse()?;
54+
credential_issuer_metadata.credential_issuer = issuer_url.clone();
55+
credential_issuer_metadata.credential_endpoint = format!("{issuer_url}credential").parse()?;
4756
Ok(Self {
4857
listener,
49-
credential_types: Arc::new(Mutex::new(vec!["UniversityDegree_JWT".to_string()])),
50-
credentials_supported: Arc::new(Mutex::new(credentials_supported)),
51-
authorization_code: Arc::new(Mutex::new(None)),
52-
pre_authorized_code: Arc::new(Mutex::new(Some(PreAuthorizedCode {
53-
pre_authorized_code: generate_authorization_code(10),
54-
user_pin_required: true,
55-
interval: 5,
56-
}))),
58+
credential_issuer: Arc::new(Mutex::new(CredentialIssuer {
59+
metadata: credential_issuer_metadata.clone(),
60+
authorization_server_metadata: AuthorizationServerMetadata {
61+
issuer: issuer_url.clone(),
62+
authorization_endpoint: format!("{issuer_url}authorize").parse()?,
63+
token_endpoint: format!("{issuer_url}token").parse()?,
64+
..Default::default()
65+
},
66+
storage,
67+
})),
68+
credential_types: Arc::new(Mutex::new(vec![serde_json::to_value(CredentialFormat {
69+
format: JwtVcJson,
70+
parameters: JwtVcJsonParameters {
71+
credential_definition: CredentialDefinition {
72+
type_: vec!["VerifiableCredential".into(), "UniversityDegreeCredential".into()],
73+
credential_subject: None,
74+
},
75+
},
76+
})
77+
.unwrap()])),
5778
nonce: Arc::new(Mutex::new(None)),
5879
access_token: Arc::new(Mutex::new(None)),
5980
})
6081
}
6182

6283
pub async fn start(&self) {
6384
let router = self.router(ServerState {
85+
credential_issuer: self.credential_issuer.clone(),
6486
credential_types: self.credential_types.clone(),
65-
credentials_supported: self.credentials_supported.clone(),
66-
authorization_code: self.authorization_code.clone(),
67-
pre_authorized_code: self.pre_authorized_code.clone(),
6887
nonce: self.nonce.clone(),
6988
access_token: self.access_token.clone(),
7089
});
@@ -79,58 +98,77 @@ impl Server {
7998
});
8099
}
81100

82-
pub fn uri(&self) -> String {
83-
format!("http://{}", self.listener.local_addr().unwrap())
84-
}
85-
86101
pub fn credential_offer_uri(&self) -> String {
102+
let credential_issuer = self
103+
.credential_issuer
104+
.lock()
105+
.unwrap()
106+
.metadata
107+
.credential_issuer
108+
.clone();
109+
110+
let credentials = self.credential_types.lock().unwrap().clone();
111+
let authorization_code = self.credential_issuer.lock().unwrap().storage.get_authorization_code();
112+
let pre_authorized_code = self.credential_issuer.lock().unwrap().storage.get_pre_authorized_code();
113+
87114
// TODO: dynamically create this.
88115
CredentialOfferQuery::CredentialOffer(CredentialOffer {
89-
credential_issuer: self.uri(),
90-
credentials: self.credential_types.lock().unwrap().clone(),
116+
credential_issuer,
117+
credentials,
91118
grants: Some(Grants {
92-
authorization_code: self.authorization_code.lock().unwrap().clone(),
93-
pre_authorized_code: self.pre_authorized_code.lock().unwrap().clone(),
119+
authorization_code,
120+
pre_authorized_code,
94121
}),
95122
})
96123
.to_string()
97124
}
98125

99-
fn router(&self, server_state: ServerState) -> Router {
126+
fn router(&self, server_state: ServerState<S>) -> Router {
100127
Router::new()
128+
.route(
129+
"/.well-known/oauth-authorization-server",
130+
get(|State(server_state): State<ServerState<S>>| async move {
131+
(
132+
StatusCode::OK,
133+
Json(
134+
server_state
135+
.credential_issuer
136+
.lock()
137+
.unwrap()
138+
.authorization_server_metadata
139+
.clone(),
140+
),
141+
)
142+
}),
143+
)
101144
.route(
102145
"/.well-known/openid-credential-issuer",
103-
get(|State(server_state): State<ServerState>| async move {
146+
get(|State(server_state): State<ServerState<S>>| async move {
104147
(
105148
StatusCode::OK,
106-
Json(server_state.credentials_supported.lock().unwrap().clone()),
149+
Json(server_state.credential_issuer.lock().unwrap().metadata.clone()),
107150
)
108151
}),
109152
)
110153
.route(
111154
"/token",
112155
post(
113-
|State(server_state): State<ServerState>, Form(token_request): Form<TokenRequest>| async move {
114-
match server_state.pre_authorized_code.lock().unwrap().take() {
115-
Some(pre_authorized_code)
116-
if pre_authorized_code.pre_authorized_code == token_request.pre_authorized_code =>
117-
{
118-
(
119-
StatusCode::OK,
120-
AppendHeaders([("Cache-Control", "no-store")]),
121-
Json(TokenResponse {
122-
// TODO: dynamically create this.
123-
access_token: "eyJhbGciOiJSUzI1NiIsInR5cCI6Ikp..sHQ".to_string(),
124-
token_type: "bearer".to_string(),
125-
expires_in: Some(86400),
126-
refresh_token: None,
127-
scope: None,
128-
c_nonce: Some(generate_nonce(16)),
129-
c_nonce_expires_in: Some(86400),
130-
}),
131-
)
132-
.into_response()
133-
}
156+
|State(server_state): State<ServerState<S>>, Form(token_request): Form<TokenRequest>| async move {
157+
match server_state
158+
.credential_issuer
159+
.lock()
160+
.unwrap()
161+
.storage
162+
.get_token_response(token_request.pre_authorized_code)
163+
.take()
164+
{
165+
Some(token_response) => (
166+
StatusCode::OK,
167+
AppendHeaders([("Cache-Control", "no-store")]),
168+
Json(token_response),
169+
)
170+
.into_response(),
171+
134172
// TODO: handle error response
135173
_ => (
136174
StatusCode::BAD_REQUEST,
@@ -144,19 +182,24 @@ impl Server {
144182
)
145183
.route(
146184
"/credential",
147-
post(|Json(_credential_request): Json<CredentialRequest>| async move {
148-
(
149-
StatusCode::OK,
150-
AppendHeaders([("Cache-Control", "no-store")]),
151-
Json(CredentialResponse {
152-
format: ClaimFormatDesignation::JwtVcJson,
153-
credential: Some(serde_json::json!("\"LUpixVCWJk0eOt4CXQe1NXK....WZwmhmn9OQp6YxX0a2L\"")),
154-
transaction_id: None,
155-
c_nonce: Some(generate_nonce(16)),
156-
c_nonce_expires_in: Some(86400),
157-
}),
158-
)
159-
}),
185+
post(
186+
|Json(credential_request): Json<CredentialRequest<JwtVcJson>>| async move {
187+
dbg!(&credential_request);
188+
(
189+
StatusCode::OK,
190+
AppendHeaders([("Cache-Control", "no-store")]),
191+
Json(CredentialResponse {
192+
format: ClaimFormatDesignation::JwtVcJson,
193+
credential: Some(serde_json::json!(
194+
"\"LUpixVCWJk0eOt4CXQe1NXK....WZwmhmn9OQp6YxX0a2L\""
195+
)),
196+
transaction_id: None,
197+
c_nonce: Some(generate_nonce(16)),
198+
c_nonce_expires_in: Some(86400),
199+
}),
200+
)
201+
},
202+
),
160203
)
161204
.with_state(server_state)
162205
}

0 commit comments

Comments
 (0)