this repo has no description
1use crate::auth::{extract_bearer_token_from_header, validate_bearer_token};
2use crate::state::AppState;
3use axum::{
4 Json,
5 body::Bytes,
6 extract::{Path, RawQuery, State},
7 http::{HeaderMap, Method, StatusCode},
8 response::{IntoResponse, Response},
9};
10use serde_json::json;
11
12pub async fn get_state(
13 State(state): State<AppState>,
14 headers: HeaderMap,
15 RawQuery(query): RawQuery,
16) -> Response {
17 if std::env::var("PDS_AGE_ASSURANCE_OVERRIDE").is_err() {
18 return proxy_to_appview(state, headers, "app.bsky.ageassurance.getState", query).await;
19 }
20
21 let created_at = get_account_created_at(&state, &headers).await;
22 let now = chrono::Utc::now().to_rfc3339();
23
24 (
25 StatusCode::OK,
26 Json(json!({
27 "state": {
28 "status": "assured",
29 "access": "full",
30 "lastInitiatedAt": now
31 },
32 "metadata": {
33 "accountCreatedAt": created_at
34 }
35 })),
36 )
37 .into_response()
38}
39
40pub async fn get_age_assurance_state(
41 State(state): State<AppState>,
42 headers: HeaderMap,
43 RawQuery(query): RawQuery,
44) -> Response {
45 if std::env::var("PDS_AGE_ASSURANCE_OVERRIDE").is_err() {
46 return proxy_to_appview(
47 state,
48 headers,
49 "app.bsky.unspecced.getAgeAssuranceState",
50 query,
51 )
52 .await;
53 }
54
55 (StatusCode::OK, Json(json!({"status": "assured"}))).into_response()
56}
57
58async fn get_account_created_at(state: &AppState, headers: &HeaderMap) -> Option<String> {
59 let auth_header = headers.get("Authorization").and_then(|h| h.to_str().ok());
60 tracing::debug!(?auth_header, "age assurance: extracting token");
61
62 let token = extract_bearer_token_from_header(auth_header)?;
63 tracing::debug!("age assurance: got token, validating");
64
65 let auth_user = match validate_bearer_token(&state.db, &token).await {
66 Ok(user) => {
67 tracing::debug!(did = %user.did, "age assurance: validated user");
68 user
69 }
70 Err(e) => {
71 tracing::warn!(?e, "age assurance: token validation failed");
72 return None;
73 }
74 };
75
76 let row = match sqlx::query!("SELECT created_at FROM users WHERE did = $1", auth_user.did)
77 .fetch_optional(&state.db)
78 .await
79 {
80 Ok(r) => {
81 tracing::debug!(?r, "age assurance: query result");
82 r
83 }
84 Err(e) => {
85 tracing::warn!(?e, "age assurance: query failed");
86 return None;
87 }
88 };
89
90 row.map(|r| r.created_at.to_rfc3339())
91}
92
93async fn proxy_to_appview(
94 state: AppState,
95 headers: HeaderMap,
96 method: &str,
97 query: Option<String>,
98) -> Response {
99 if headers.get("atproto-proxy").is_none() {
100 return (
101 StatusCode::BAD_REQUEST,
102 Json(json!({
103 "error": "InvalidRequest",
104 "message": "Missing required atproto-proxy header"
105 })),
106 )
107 .into_response();
108 }
109
110 crate::api::proxy::proxy_handler(
111 State(state),
112 Path(method.to_string()),
113 Method::GET,
114 headers,
115 RawQuery(query),
116 Bytes::new(),
117 )
118 .await
119}