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