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 require_request_uri_registration: Option<bool>, 43 #[serde(skip_serializing_if = "Option::is_none")] 44 pub dpop_signing_alg_values_supported: Option<Vec<String>>, 45 #[serde(skip_serializing_if = "Option::is_none")] 46 pub authorization_response_iss_parameter_supported: Option<bool>, 47 #[serde(skip_serializing_if = "Option::is_none")] 48 pub revocation_endpoint: Option<String>, 49 #[serde(skip_serializing_if = "Option::is_none")] 50 pub introspection_endpoint: Option<String>, 51 #[serde(skip_serializing_if = "Option::is_none")] 52 pub client_id_metadata_document_supported: Option<bool>, 53} 54 55pub async fn oauth_protected_resource( 56 State(_state): State<AppState>, 57) -> Json<ProtectedResourceMetadata> { 58 let pds_hostname = std::env::var("PDS_HOSTNAME").unwrap_or_else(|_| "localhost".to_string()); 59 let public_url = format!("https://{}", pds_hostname); 60 Json(ProtectedResourceMetadata { 61 resource: public_url.clone(), 62 authorization_servers: vec![public_url], 63 bearer_methods_supported: vec!["header".to_string()], 64 scopes_supported: vec![], 65 resource_documentation: Some("https://atproto.com".to_string()), 66 }) 67} 68 69pub async fn oauth_authorization_server( 70 State(_state): State<AppState>, 71) -> Json<AuthorizationServerMetadata> { 72 let pds_hostname = std::env::var("PDS_HOSTNAME").unwrap_or_else(|_| "localhost".to_string()); 73 let issuer = format!("https://{}", pds_hostname); 74 Json(AuthorizationServerMetadata { 75 issuer: issuer.clone(), 76 authorization_endpoint: format!("{}/oauth/authorize", issuer), 77 token_endpoint: format!("{}/oauth/token", issuer), 78 jwks_uri: format!("{}/oauth/jwks", issuer), 79 registration_endpoint: None, 80 scopes_supported: Some(vec![ 81 "atproto".to_string(), 82 "transition:generic".to_string(), 83 "transition:chat.bsky".to_string(), 84 "repo:*".to_string(), 85 "repo:*?action=create".to_string(), 86 "repo:*?action=read".to_string(), 87 "repo:*?action=update".to_string(), 88 "repo:*?action=delete".to_string(), 89 "blob:*/*".to_string(), 90 "rpc:*".to_string(), 91 "account:*".to_string(), 92 "account:*?action=read".to_string(), 93 "account:*?action=write".to_string(), 94 "identity:*".to_string(), 95 ]), 96 response_types_supported: vec!["code".to_string()], 97 response_modes_supported: Some(vec!["query".to_string(), "fragment".to_string()]), 98 grant_types_supported: Some(vec![ 99 "authorization_code".to_string(), 100 "refresh_token".to_string(), 101 ]), 102 token_endpoint_auth_methods_supported: Some(vec![ 103 "none".to_string(), 104 "private_key_jwt".to_string(), 105 ]), 106 token_endpoint_auth_signing_alg_values_supported: Some(vec![ 107 "ES256".to_string(), 108 "ES384".to_string(), 109 "ES512".to_string(), 110 "EdDSA".to_string(), 111 ]), 112 code_challenge_methods_supported: Some(vec!["S256".to_string()]), 113 pushed_authorization_request_endpoint: Some(format!("{}/oauth/par", issuer)), 114 require_pushed_authorization_requests: Some(true), 115 require_request_uri_registration: Some(true), 116 dpop_signing_alg_values_supported: Some(vec![ 117 "ES256".to_string(), 118 "ES384".to_string(), 119 "ES512".to_string(), 120 "EdDSA".to_string(), 121 ]), 122 authorization_response_iss_parameter_supported: Some(true), 123 revocation_endpoint: Some(format!("{}/oauth/revoke", issuer)), 124 introspection_endpoint: Some(format!("{}/oauth/introspect", issuer)), 125 client_id_metadata_document_supported: Some(true), 126 }) 127} 128 129pub async fn oauth_jwks(State(_state): State<AppState>) -> Json<JwkSet> { 130 use crate::config::AuthConfig; 131 use crate::oauth::jwks::Jwk; 132 let config = AuthConfig::get(); 133 let server_key = Jwk { 134 kty: "EC".to_string(), 135 key_use: Some("sig".to_string()), 136 kid: Some(config.signing_key_id.clone()), 137 alg: Some("ES256".to_string()), 138 crv: Some("P-256".to_string()), 139 x: Some(config.signing_key_x.clone()), 140 y: Some(config.signing_key_y.clone()), 141 }; 142 Json(create_jwk_set(vec![server_key])) 143} 144 145#[derive(Debug, Serialize, Deserialize)] 146pub struct FrontendClientMetadata { 147 pub client_id: String, 148 pub client_name: String, 149 pub client_uri: String, 150 pub redirect_uris: Vec<String>, 151 pub grant_types: Vec<String>, 152 pub response_types: Vec<String>, 153 pub scope: String, 154 pub token_endpoint_auth_method: String, 155 pub application_type: String, 156 pub dpop_bound_access_tokens: bool, 157} 158 159pub async fn frontend_client_metadata( 160 State(_state): State<AppState>, 161) -> Json<FrontendClientMetadata> { 162 let pds_hostname = std::env::var("PDS_HOSTNAME").unwrap_or_else(|_| "localhost".to_string()); 163 let base_url = format!("https://{}", pds_hostname); 164 let client_id = format!("{}/oauth/client-metadata.json", base_url); 165 Json(FrontendClientMetadata { 166 client_id, 167 client_name: "PDS Account Manager".to_string(), 168 client_uri: base_url.clone(), 169 redirect_uris: vec![format!("{}/", base_url)], 170 grant_types: vec![ 171 "authorization_code".to_string(), 172 "refresh_token".to_string(), 173 ], 174 response_types: vec!["code".to_string()], 175 scope: "atproto transition:generic".to_string(), 176 token_endpoint_auth_method: "none".to_string(), 177 application_type: "web".to_string(), 178 dpop_bound_access_tokens: true, 179 }) 180}