APIs for links and references in the ATmosphere

refresh cookie + frame headers

+55 -31
+55 -31
who-am-i/src/server.rs
··· 4 extract::{FromRef, Query, State}, 5 http::{ 6 StatusCode, 7 - header::{CONTENT_TYPE, HeaderMap, REFERER}, 8 }, 9 response::{IntoResponse, Json, Redirect, Response}, 10 routing::get, ··· 29 30 const DID_COOKIE_KEY: &str = "did"; 31 32 type AppEngine = Engine<Handlebars<'static>>; 33 - type Rendered = RenderHtml<&'static str, AppEngine, Value>; 34 35 #[derive(Clone)] 36 struct AppState { ··· 96 .unwrap(); 97 } 98 99 - async fn hello(State(AppState { engine, .. }): State<AppState>) -> Rendered { 100 - RenderHtml("hello", engine, json!({})) 101 } 102 103 async fn css() -> impl IntoResponse { ··· 110 111 async fn favicon() -> impl IntoResponse { 112 ([(CONTENT_TYPE, "image/x-icon")], FAVICON) 113 } 114 115 async fn prompt( ··· 149 if parent_origin == "null" { 150 return err("Referer origin is opaque", true); 151 } 152 if let Some(did) = jar.get(DID_COOKIE_KEY) { 153 let Ok(did) = Did::new(did.value_trimmed().to_string()) else { 154 return err("Bad cookie", false); 155 }; 156 157 let fetch_key = resolve_handles.dispatch( 158 { 159 let oauth = oauth.clone(); ··· 163 shutdown.child_token(), 164 ); 165 166 - RenderHtml( 167 - "prompt", 168 - engine, 169 - json!({ 170 - "did": did, 171 - "fetch_key": fetch_key, 172 - "parent_host": parent_host, 173 - "parent_origin": parent_origin, 174 - }), 175 - ) 176 - .into_response() 177 } else { 178 - RenderHtml( 179 - "prompt", 180 - engine, 181 - json!({ 182 - "parent_host": parent_host, 183 - "parent_origin": parent_origin, 184 - }), 185 - ) 186 - .into_response() 187 } 188 } 189 ··· 316 } 317 }; 318 319 - let cookie = Cookie::build((DID_COOKIE_KEY, did.to_string())) 320 - .http_only(true) 321 - .secure(true) 322 - .same_site(SameSite::None) 323 - .max_age(std::time::Duration::from_secs(86_400).try_into().unwrap()); 324 - 325 - let jar = jar.add(cookie); 326 327 let fetch_key = resolve_handles.dispatch( 328 {
··· 4 extract::{FromRef, Query, State}, 5 http::{ 6 StatusCode, 7 + header::{CONTENT_SECURITY_POLICY, CONTENT_TYPE, HeaderMap, REFERER, X_FRAME_OPTIONS}, 8 }, 9 response::{IntoResponse, Json, Redirect, Response}, 10 routing::get, ··· 29 30 const DID_COOKIE_KEY: &str = "did"; 31 32 + const COOKIE_EXPIRATION: Duration = Duration::from_secs(30 * 86_400); 33 + 34 type AppEngine = Engine<Handlebars<'static>>; 35 36 #[derive(Clone)] 37 struct AppState { ··· 97 .unwrap(); 98 } 99 100 + async fn hello( 101 + State(AppState { engine, .. }): State<AppState>, 102 + mut jar: SignedCookieJar, 103 + ) -> Response { 104 + // push expiry (or clean up) the current cookie 105 + if let Some(did) = jar.get(DID_COOKIE_KEY) { 106 + if let Ok(did) = Did::new(did.value_trimmed().to_string()) { 107 + jar = jar.add(cookie(&did)); 108 + } else { 109 + jar = jar.remove(DID_COOKIE_KEY); 110 + } 111 + } 112 + let frame_headers = [ 113 + (X_FRAME_OPTIONS, "deny"), 114 + (CONTENT_SECURITY_POLICY, "frame-ancestors 'none'"), 115 + ]; 116 + (frame_headers, jar, RenderHtml("hello", engine, json!({}))).into_response() 117 } 118 119 async fn css() -> impl IntoResponse { ··· 126 127 async fn favicon() -> impl IntoResponse { 128 ([(CONTENT_TYPE, "image/x-icon")], FAVICON) 129 + } 130 + 131 + fn cookie(did: &Did) -> Cookie<'static> { 132 + Cookie::build((DID_COOKIE_KEY, did.to_string())) 133 + .http_only(true) 134 + .secure(true) 135 + .same_site(SameSite::None) 136 + .max_age(COOKIE_EXPIRATION.try_into().unwrap()) 137 + .into() 138 } 139 140 async fn prompt( ··· 174 if parent_origin == "null" { 175 return err("Referer origin is opaque", true); 176 } 177 + 178 + let frame_headers = [ 179 + (X_FRAME_OPTIONS, format!("allow-from {parent_origin}")), 180 + ( 181 + CONTENT_SECURITY_POLICY, 182 + format!("frame-ancestors {parent_host}"), 183 + ), 184 + ]; 185 + 186 if let Some(did) = jar.get(DID_COOKIE_KEY) { 187 let Ok(did) = Did::new(did.value_trimmed().to_string()) else { 188 return err("Bad cookie", false); 189 }; 190 191 + // push cookie expiry 192 + let jar = jar.add(cookie(&did)); 193 + 194 let fetch_key = resolve_handles.dispatch( 195 { 196 let oauth = oauth.clone(); ··· 200 shutdown.child_token(), 201 ); 202 203 + let info = json!({ 204 + "did": did, 205 + "fetch_key": fetch_key, 206 + "parent_host": parent_host, 207 + "parent_origin": parent_origin, 208 + }); 209 + 210 + (frame_headers, jar, RenderHtml("prompt", engine, info)).into_response() 211 } else { 212 + let info = json!({ 213 + "parent_host": parent_host, 214 + "parent_origin": parent_origin, 215 + }); 216 + (frame_headers, RenderHtml("prompt", engine, info)).into_response() 217 } 218 } 219 ··· 346 } 347 }; 348 349 + let jar = jar.add(cookie(&did)); 350 351 let fetch_key = resolve_handles.dispatch( 352 {