-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* fix: remove `iota_method` * test: add test-utils feature, bump ed25519-dalek dep * feat: add `JsonObject` * refactor: remove `serialize_unit_struct`, use `#[serde(tag = ...)]` instead * style: use `JsonObject` * feat: add `Extension` trait * feat: implement `Extension` trait for `siopv2` * feat: implement `Extension` trait for `oid4vp` * fix: update manager * fix: use `MustBe` macro to enforce `response_type` values * style: sort dependencies * fix: remove `siopv2_oid4vp` * feat: derive `Clone` trait for request handlers * chore: remove `oid4vp` and `oid4vci` dependencies in `siopv2` * fix: remove obsolete assignment * chore: remove comment * chore: add TODO's * chore: remove unused method
- Loading branch information
1 parent
ad60719
commit d05bd55
Showing
56 changed files
with
1,426 additions
and
1,739 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,198 @@ | ||
use crate::{ | ||
openid4vc_extension::{Extension, Generic, OpenID4VC, RequestHandle}, | ||
JsonObject, RFC7519Claims, | ||
}; | ||
use serde::{de::DeserializeOwned, Deserialize, Serialize}; | ||
use serde_json::json; | ||
|
||
/// A `Body` is a set of claims that are sent by a client to a provider. It can be `ByValue`, `ByReference`, or an `Object`. | ||
pub trait Body: Serialize + std::fmt::Debug { | ||
fn client_id(&self) -> &String; | ||
} | ||
|
||
/// An `Object` is a set of claims that are sent by a client to a provider. On top of some generic claims, it also | ||
/// contains a set of claims specific to an [`Extension`]. | ||
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] | ||
pub struct Object<E: Extension = Generic> { | ||
#[serde(flatten)] | ||
pub rfc7519_claims: RFC7519Claims, | ||
pub client_id: String, | ||
pub redirect_uri: url::Url, | ||
pub state: Option<String>, | ||
#[serde(flatten)] | ||
pub extension: <E::RequestHandle as RequestHandle>::Parameters, | ||
} | ||
|
||
impl<E: Extension> Object<E> { | ||
/// Converts a [`Object`] with a [`Generic`] [`Extension`] to a [`Object`] with a specific [`Extension`]. | ||
fn from_generic(original: &Object<Generic>) -> anyhow::Result<Self> { | ||
Ok(Object { | ||
rfc7519_claims: original.rfc7519_claims.clone(), | ||
client_id: original.client_id.clone(), | ||
redirect_uri: original.redirect_uri.clone(), | ||
state: original.state.clone(), | ||
extension: serde_json::from_value(original.extension.clone())?, | ||
}) | ||
} | ||
} | ||
|
||
impl<E: Extension> std::str::FromStr for Object<E> { | ||
type Err = anyhow::Error; | ||
|
||
fn from_str(s: &str) -> Result<Self, Self::Err> { | ||
let url = url::Url::parse(s)?; | ||
let query = url.query().ok_or_else(|| anyhow::anyhow!("No query found."))?; | ||
let map = serde_urlencoded::from_str::<JsonObject>(query)? | ||
.into_iter() | ||
.filter_map(|(k, v)| match v { | ||
serde_json::Value::String(s) => Some(Ok(( | ||
k, | ||
serde_json::from_str(&s).unwrap_or(serde_json::Value::String(s)), | ||
))), | ||
_ => None, | ||
}) | ||
.collect::<Result<_, anyhow::Error>>()?; | ||
let authorization_request: Object<E> = serde_json::from_value(serde_json::Value::Object(map))?; | ||
Ok(authorization_request) | ||
} | ||
} | ||
|
||
impl<E: Extension> Body for Object<E> { | ||
fn client_id(&self) -> &String { | ||
&self.client_id | ||
} | ||
} | ||
|
||
#[derive(Serialize, Deserialize, Debug, PartialEq)] | ||
pub struct ByReference { | ||
pub client_id: String, | ||
pub request_uri: url::Url, | ||
} | ||
|
||
impl Body for ByReference { | ||
fn client_id(&self) -> &String { | ||
&self.client_id | ||
} | ||
} | ||
|
||
#[derive(Serialize, Deserialize, Debug, PartialEq)] | ||
pub struct ByValue { | ||
pub client_id: String, | ||
pub request: String, | ||
} | ||
impl Body for ByValue { | ||
fn client_id(&self) -> &String { | ||
&self.client_id | ||
} | ||
} | ||
|
||
/// A [`AuthorizationRequest`] is a request that is sent by a client to a provider. It contains a set of claims in the | ||
/// form of a [`Body`] which can be [`ByValue`], [`ByReference`], or an [`Object`]. | ||
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] | ||
pub struct AuthorizationRequest<B: Body> { | ||
#[serde(flatten)] | ||
pub body: B, | ||
} | ||
|
||
impl<E: Extension + OpenID4VC> AuthorizationRequest<Object<E>> { | ||
/// Converts a [`AuthorizationRequest`] with a [`Generic`] [`Extension`] to a [`AuthorizationRequest`] with a specific [`Extension`]. | ||
pub fn from_generic( | ||
original: &AuthorizationRequest<Object<Generic>>, | ||
) -> anyhow::Result<AuthorizationRequest<Object<E>>> { | ||
Ok(AuthorizationRequest { | ||
body: Object::from_generic(&original.body)?, | ||
}) | ||
} | ||
} | ||
|
||
impl<E: Extension> AuthorizationRequest<Object<E>> { | ||
/// Returns a [`AuthorizationRequest`]'s builder. | ||
pub fn builder() -> <E::RequestHandle as RequestHandle>::Builder { | ||
<E::RequestHandle as RequestHandle>::Builder::default() | ||
} | ||
} | ||
|
||
/// In order to convert a string to a [`AuthorizationRequest`], we need to try to parse each value as a JSON object. This way we | ||
/// can catch any non-primitive types. If the value is not a JSON object or an Array, we just leave it as a string. | ||
impl<B: Body + DeserializeOwned> std::str::FromStr for AuthorizationRequest<B> { | ||
type Err = anyhow::Error; | ||
|
||
fn from_str(s: &str) -> Result<Self, Self::Err> { | ||
let url = url::Url::parse(s)?; | ||
let query = url.query().ok_or_else(|| anyhow::anyhow!("No query found."))?; | ||
let map = serde_urlencoded::from_str::<JsonObject>(query)? | ||
.into_iter() | ||
.filter_map(|(k, v)| match v { | ||
serde_json::Value::String(s) => Some(Ok(( | ||
k, | ||
serde_json::from_str(&s).unwrap_or(serde_json::Value::String(s)), | ||
))), | ||
_ => None, | ||
}) | ||
.collect::<Result<_, anyhow::Error>>()?; | ||
let authorization_request: AuthorizationRequest<B> = serde_json::from_value(serde_json::Value::Object(map))?; | ||
Ok(authorization_request) | ||
} | ||
} | ||
|
||
/// In order to convert a [`AuthorizationRequest`] to a string, we need to convert all the values to strings. This is because | ||
/// `serde_urlencoded` does not support serializing non-primitive types. | ||
// TODO: Find a way to dynamically generate the `siopv2://idtoken?` part of the URL. This will require some refactoring | ||
// for the `AuthorizationRequest` struct. | ||
impl<B: Body> std::fmt::Display for AuthorizationRequest<B> { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
let map: JsonObject = json!(self) | ||
.as_object() | ||
.ok_or(std::fmt::Error)? | ||
.iter() | ||
.filter_map(|(k, v)| match v { | ||
serde_json::Value::Object(_) | serde_json::Value::Array(_) => { | ||
Some((k.to_owned(), serde_json::Value::String(serde_json::to_string(v).ok()?))) | ||
} | ||
serde_json::Value::String(_) => Some((k.to_owned(), v.to_owned())), | ||
_ => None, | ||
}) | ||
.collect(); | ||
|
||
let encoded = serde_urlencoded::to_string(map).map_err(|_| std::fmt::Error)?; | ||
write!(f, "siopv2://idtoken?{}", encoded) | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
use std::str::FromStr; | ||
|
||
#[test] | ||
fn test() { | ||
let authorization_request = AuthorizationRequest::<Object> { | ||
body: Object { | ||
rfc7519_claims: Default::default(), | ||
client_id: "did:example:123".to_string(), | ||
redirect_uri: "https://www.example.com".parse().unwrap(), | ||
state: Some("state".to_string()), | ||
extension: json!({ | ||
"response_mode": "direct_post", | ||
"nonce": "nonce", | ||
"claims": { | ||
"id_token": { | ||
"email": { | ||
"essential": true | ||
} | ||
} | ||
} | ||
}), | ||
}, | ||
}; | ||
|
||
// Convert the authorization request to a form urlencoded string. | ||
let form_urlencoded = authorization_request.to_string(); | ||
|
||
// Convert the form urlencoded string back to a authorization request. | ||
assert_eq!( | ||
AuthorizationRequest::<Object>::from_str(&form_urlencoded).unwrap(), | ||
authorization_request | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
use crate::openid4vc_extension::{Extension, ResponseHandle}; | ||
use serde::{Deserialize, Serialize}; | ||
use serde_with::skip_serializing_none; | ||
|
||
/// The [`AuthorizationResponse`] is a set of claims that are sent by a provider to a client. On top of some generic | ||
/// claims, it also contains a set of claims specific to an [`Extension`]. | ||
#[skip_serializing_none] | ||
#[derive(Serialize, Deserialize, Debug, PartialEq)] | ||
pub struct AuthorizationResponse<E: Extension> { | ||
#[serde(skip)] | ||
pub redirect_uri: String, | ||
pub state: Option<String>, | ||
#[serde(flatten)] | ||
pub extension: <E::ResponseHandle as ResponseHandle>::Parameters, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.