appview/router/src/wellknown/oauth/authorization_server.rs

104 lines
3.4 KiB
Rust
Raw Normal View History

use serde::de::{
Error,
Unexpected,
};
use serde_json::{
json,
Result,
Value
};
use bon::Builder;
trait Metadata {
fn format_metadata(self, required: RequiredMetadata) -> Result<Value>;
}
pub struct RequiredMetadata {
issuer: String,
authorization_endpoint: String,
token_endpoint: String,
}
impl RequiredMetadata {
fn new(
issuer: String,
authorization_endpoint: String,
token_endpoint: String
) -> Self {
RequiredMetadata {
issuer, authorization_endpoint, token_endpoint
}
}
}
#[derive(Builder)]
struct AtprotoMetadata {
additional_response_types_supported: Option<Vec<String>>,
additional_grant_types_supported: Option<Vec<String>>,
additional_code_challenge_methods_supported: Option<Vec<String>>,
additional_token_endpoint_auth_methods_supported: Option<Vec<String>>,
additional_token_endpoint_auth_signing_alg_values_supported: Option<Vec<String>>,
additional_scopes_supported: Option<Vec<String>>,
pushed_authorization_request_endpoint: String,
additional_dpop_signing_alg_values_supported: Option<Vec<String>>,
}
impl AtprotoMetadata {
fn check_fields(&self) -> Result<()> {
// TODO: Issuer check (https scheme, no default port, no path segments
if self.additional_token_endpoint_auth_signing_alg_values_supported
.as_ref()
.is_none_or(|vec| vec.iter().any(|s| s == "none")) {
return Err(Error::invalid_value(
Unexpected::Other("\"none\" in token_endpoint_auth_signing_alg_values_supported"),
&"\"none\" to be omitted from token_endpoint_auth_signing_alg_values_supported"
));
}
Ok(())
}
}
impl Metadata for AtprotoMetadata {
fn format_metadata(self, required: RequiredMetadata) -> Result<Value> {
self.check_fields()?;
Ok(json!({
"issuer": required.issuer,
"authorization_endpoint": required.authorization_endpoint,
"token_endpoint": required.token_endpoint,
"response_types_supported":
self.additional_response_types_supported.unwrap_or_default()
.extend(["code".to_string()]),
"grant_types_supported":
self.additional_grant_types_supported.unwrap_or_default()
.extend([
"authorization_code".to_string(),
"refresh_token".to_string()
]),
"code_challenge_methods_supported":
self.additional_code_challenge_methods_supported.unwrap_or_default()
.extend(["S256".to_string()]),
"token_endpoint_auth_methods_supported":
self.additional_token_endpoint_auth_methods_supported.unwrap_or_default()
.extend([
"none".to_string(),
"private_key_jwt".to_string()
]),
"token_endpoint_auth_signing_alg_values_supported":
self.additional_token_endpoint_auth_signing_alg_values_supported.unwrap_or_default()
.extend(["ES256".to_string()]),
"scopes_supported":
self.additional_scopes_supported.unwrap_or_default()
.extend(["atproto".to_string()]),
"authorization_response_iss_parameter_supported": true,
"require_pushed_authorization_requests": true,
"pushed_authorization_request_endpoint": self.pushed_authorization_request_endpoint,
"dpop_signing_alg_values_supported":
self.additional_dpop_signing_alg_values_supported.unwrap_or_default()
.extend(["ES256".to_string()]),
"client_id_metadata_document_supported": true,
}))
}
}