this repo has no description
1use crate::oauth::jwks::{JwkSet, create_jwk_set}; 2use crate::state::AppState; 3use axum::{Json, extract::State}; 4use serde::{Deserialize, Serialize}; 5 6#[derive(Debug, Serialize, Deserialize)] 7pub struct ProtectedResourceMetadata { 8 pub resource: String, 9 pub authorization_servers: Vec<String>, 10 pub bearer_methods_supported: Vec<String>, 11 pub scopes_supported: Vec<String>, 12 #[serde(skip_serializing_if = "Option::is_none")] 13 pub resource_documentation: Option<String>, 14} 15 16#[derive(Debug, Serialize, Deserialize)] 17pub struct AuthorizationServerMetadata { 18 pub issuer: String, 19 pub authorization_endpoint: String, 20 pub token_endpoint: String, 21 pub jwks_uri: String, 22 #[serde(skip_serializing_if = "Option::is_none")] 23 pub registration_endpoint: Option<String>, 24 #[serde(skip_serializing_if = "Option::is_none")] 25 pub scopes_supported: Option<Vec<String>>, 26 pub response_types_supported: Vec<String>, 27 #[serde(skip_serializing_if = "Option::is_none")] 28 pub response_modes_supported: Option<Vec<String>>, 29 #[serde(skip_serializing_if = "Option::is_none")] 30 pub grant_types_supported: Option<Vec<String>>, 31 #[serde(skip_serializing_if = "Option::is_none")] 32 pub token_endpoint_auth_methods_supported: Option<Vec<String>>, 33 #[serde(skip_serializing_if = "Option::is_none")] 34 pub token_endpoint_auth_signing_alg_values_supported: Option<Vec<String>>, 35 #[serde(skip_serializing_if = "Option::is_none")] 36 pub code_challenge_methods_supported: Option<Vec<String>>, 37 #[serde(skip_serializing_if = "Option::is_none")] 38 pub pushed_authorization_request_endpoint: Option<String>, 39 #[serde(skip_serializing_if = "Option::is_none")] 40 pub require_pushed_authorization_requests: Option<bool>, 41 #[serde(skip_serializing_if = "Option::is_none")] 42 pub dpop_signing_alg_values_supported: Option<Vec<String>>, 43 #[serde(skip_serializing_if = "Option::is_none")] 44 pub authorization_response_iss_parameter_supported: Option<bool>, 45 #[serde(skip_serializing_if = "Option::is_none")] 46 pub revocation_endpoint: Option<String>, 47 #[serde(skip_serializing_if = "Option::is_none")] 48 pub introspection_endpoint: Option<String>, 49 #[serde(skip_serializing_if = "Option::is_none")] 50 pub client_id_metadata_document_supported: Option<bool>, 51} 52 53pub async fn oauth_protected_resource( 54 State(_state): State<AppState>, 55) -> Json<ProtectedResourceMetadata> { 56 let pds_hostname = std::env::var("PDS_HOSTNAME").unwrap_or_else(|_| "localhost".to_string()); 57 let public_url = format!("https://{}", pds_hostname); 58 Json(ProtectedResourceMetadata { 59 resource: public_url.clone(), 60 authorization_servers: vec![public_url], 61 bearer_methods_supported: vec!["header".to_string()], 62 scopes_supported: vec![], 63 resource_documentation: Some("https://atproto.com".to_string()), 64 }) 65} 66 67pub async fn oauth_authorization_server( 68 State(_state): State<AppState>, 69) -> Json<AuthorizationServerMetadata> { 70 let pds_hostname = std::env::var("PDS_HOSTNAME").unwrap_or_else(|_| "localhost".to_string()); 71 let issuer = format!("https://{}", pds_hostname); 72 Json(AuthorizationServerMetadata { 73 issuer: issuer.clone(), 74 authorization_endpoint: format!("{}/oauth/authorize", issuer), 75 token_endpoint: format!("{}/oauth/token", issuer), 76 jwks_uri: format!("{}/oauth/jwks", issuer), 77 registration_endpoint: None, 78 scopes_supported: Some(vec![ 79 "atproto".to_string(), 80 "transition:generic".to_string(), 81 "transition:chat.bsky".to_string(), 82 ]), 83 response_types_supported: vec!["code".to_string()], 84 response_modes_supported: Some(vec!["query".to_string(), "fragment".to_string()]), 85 grant_types_supported: Some(vec![ 86 "authorization_code".to_string(), 87 "refresh_token".to_string(), 88 ]), 89 token_endpoint_auth_methods_supported: Some(vec![ 90 "none".to_string(), 91 "private_key_jwt".to_string(), 92 ]), 93 token_endpoint_auth_signing_alg_values_supported: Some(vec![ 94 "ES256".to_string(), 95 "ES384".to_string(), 96 "ES512".to_string(), 97 "EdDSA".to_string(), 98 ]), 99 code_challenge_methods_supported: Some(vec!["S256".to_string()]), 100 pushed_authorization_request_endpoint: Some(format!("{}/oauth/par", issuer)), 101 require_pushed_authorization_requests: Some(true), 102 dpop_signing_alg_values_supported: Some(vec![ 103 "ES256".to_string(), 104 "ES384".to_string(), 105 "ES512".to_string(), 106 "EdDSA".to_string(), 107 ]), 108 authorization_response_iss_parameter_supported: Some(true), 109 revocation_endpoint: Some(format!("{}/oauth/revoke", issuer)), 110 introspection_endpoint: Some(format!("{}/oauth/introspect", issuer)), 111 client_id_metadata_document_supported: Some(true), 112 }) 113} 114 115pub async fn oauth_jwks(State(_state): State<AppState>) -> Json<JwkSet> { 116 use crate::config::AuthConfig; 117 use crate::oauth::jwks::Jwk; 118 let config = AuthConfig::get(); 119 let server_key = Jwk { 120 kty: "EC".to_string(), 121 key_use: Some("sig".to_string()), 122 kid: Some(config.signing_key_id.clone()), 123 alg: Some("ES256".to_string()), 124 crv: Some("P-256".to_string()), 125 x: Some(config.signing_key_x.clone()), 126 y: Some(config.signing_key_y.clone()), 127 }; 128 Json(create_jwk_set(vec![server_key])) 129} 130 131#[derive(Debug, Serialize, Deserialize)] 132pub struct FrontendClientMetadata { 133 pub client_id: String, 134 pub client_name: String, 135 pub client_uri: String, 136 pub redirect_uris: Vec<String>, 137 pub grant_types: Vec<String>, 138 pub response_types: Vec<String>, 139 pub scope: String, 140 pub token_endpoint_auth_method: String, 141 pub application_type: String, 142 pub dpop_bound_access_tokens: bool, 143} 144 145pub async fn frontend_client_metadata( 146 State(_state): State<AppState>, 147) -> Json<FrontendClientMetadata> { 148 let pds_hostname = std::env::var("PDS_HOSTNAME").unwrap_or_else(|_| "localhost".to_string()); 149 let base_url = format!("https://{}", pds_hostname); 150 let client_id = format!("{}/oauth/client-metadata.json", base_url); 151 Json(FrontendClientMetadata { 152 client_id, 153 client_name: "PDS Account Manager".to_string(), 154 client_uri: base_url.clone(), 155 redirect_uris: vec![format!("{}/", base_url)], 156 grant_types: vec![ 157 "authorization_code".to_string(), 158 "refresh_token".to_string(), 159 ], 160 response_types: vec!["code".to_string()], 161 scope: "atproto transition:generic".to_string(), 162 token_endpoint_auth_method: "none".to_string(), 163 application_type: "web".to_string(), 164 dpop_bound_access_tokens: false, 165 }) 166}