this repo has no description

use html form for login

- also add session mgmt
- still dbg-ing authenticated requests

Akshay aa5ab91b 4e9642a2

Changed files
+163 -44
core
+1
Cargo.lock
··· 417 417 "atrium-xrpc-client", 418 418 "axum", 419 419 "hickory-resolver", 420 + "serde", 420 421 "tokio", 421 422 "tower-sessions", 422 423 ]
+1
core/Cargo.toml
··· 15 15 hickory-resolver = "0.24.2" 16 16 anyhow = "1.0.95" 17 17 tower-sessions = "0.14.0" 18 + serde = "1.0.217" 18 19
+160 -44
core/src/main.rs
··· 1 1 use std::sync::Arc; 2 2 3 - use atrium_api::{did_doc::DidDocument, types::string::AtIdentifier}; 3 + use atrium_api::{ 4 + client::AtpServiceClient, did_doc::DidDocument, types::string::AtIdentifier, 5 + xrpc::types::AuthorizationToken, 6 + }; 4 7 use atrium_common::resolver::Resolver; 5 8 use atrium_identity::{ 6 9 did::{CommonDidResolver, CommonDidResolverConfig, DEFAULT_PLC_DIRECTORY_URL}, 7 10 handle::{AtprotoHandleResolver, AtprotoHandleResolverConfig, DnsTxtResolver}, 8 11 }; 9 12 use atrium_oauth_client::{ 10 - AtprotoLocalhostClientMetadata, AuthorizeOptions, DefaultHttpClient, KnownScope, OAuthClient, 11 - OAuthClientConfig, OAuthResolverConfig, Scope, store::state::MemoryStateStore, 13 + AtprotoClientMetadata, AtprotoLocalhostClientMetadata, AuthorizeOptionPrompt, AuthorizeOptions, 14 + DefaultHttpClient, DpopClient, KnownScope, OAuthClient, OAuthClientConfig, OAuthResolverConfig, 15 + Scope, store::state::MemoryStateStore, 12 16 }; 13 17 use atrium_xrpc::HttpClient; 18 + use atrium_xrpc_client::isahc::{IsahcClient, IsahcClientBuilder}; 14 19 use axum::{Router, routing}; 15 20 use hickory_resolver::TokioAsyncResolver; 16 21 17 - struct HickoryDnsTxtResolver { 18 - resolver: TokioAsyncResolver, 19 - } 20 - 21 - impl Default for HickoryDnsTxtResolver { 22 - fn default() -> Self { 23 - Self { 24 - resolver: TokioAsyncResolver::tokio_from_system_conf() 25 - .expect("failed to create resolver"), 26 - } 27 - } 28 - } 29 - 30 - impl DnsTxtResolver for HickoryDnsTxtResolver { 31 - async fn resolve( 32 - &self, 33 - query: &str, 34 - ) -> core::result::Result<Vec<String>, Box<dyn std::error::Error + Send + Sync + 'static>> { 35 - Ok(self 36 - .resolver 37 - .txt_lookup(query) 38 - .await? 39 - .iter() 40 - .map(|txt| txt.to_string()) 41 - .collect()) 42 - } 43 - } 44 - 45 22 #[tokio::main] 46 23 async fn main() { 47 - let state = AppState::new(); 24 + let session_store = tower_sessions::MemoryStore::default(); 25 + let session_layer = tower_sessions::SessionManagerLayer::new(session_store) 26 + .with_secure(false) 27 + .with_expiry(tower_sessions::Expiry::OnSessionEnd); 28 + 29 + let app_state = AppState::new(); 48 30 let service = Router::new() 31 + .route("/", routing::get(index::get)) 32 + .route("/test-auth", routing::get(test_atproto::get)) 49 33 .route("/login", routing::get(login::get).post(login::post)) 50 34 .route("/callback", routing::get(callback::get)) 51 - .with_state(state); 35 + .layer(session_layer) 36 + .with_state(app_state); 52 37 53 38 let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); 54 39 axum::serve(listener, service).await.unwrap(); ··· 67 52 } 68 53 } 69 54 70 - type DidResolver = CommonDidResolver<DefaultHttpClient>; 71 - type HandleResolver = AtprotoHandleResolver<HickoryDnsTxtResolver, DefaultHttpClient>; 72 - 73 55 struct AppStateInner { 74 - did_resolver: DidResolver, 75 - handle_resolver: HandleResolver, 76 - oauth_client: OAuthClient<MemoryStateStore, DidResolver, HandleResolver>, 56 + did_resolver: CommonDidResolver<DefaultHttpClient>, 57 + handle_resolver: AtprotoHandleResolver<HickoryDnsTxtResolver, DefaultHttpClient>, 58 + oauth_client: OAuthClient< 59 + atrium_oauth_client::store::state::MemoryStateStore, 60 + CommonDidResolver<DefaultHttpClient>, 61 + atrium_identity::handle::AtprotoHandleResolver<HickoryDnsTxtResolver, DefaultHttpClient>, 62 + >, 77 63 } 78 64 79 65 impl AppStateInner { ··· 83 69 let handle_resolver = Self::handle_resolver(client.clone()); 84 70 let config = OAuthClientConfig { 85 71 client_metadata: AtprotoLocalhostClientMetadata { 72 + // TODO: change this 86 73 redirect_uris: Some(vec![String::from("http://127.0.0.1:3000/callback")]), 87 74 scopes: Some(vec![ 88 75 Scope::Known(KnownScope::Atproto), ··· 138 125 139 126 mod login { 140 127 use axum::{ 141 - extract::{Json, State}, 128 + extract::{Form, State}, 142 129 response::{IntoResponse, Redirect, Result}, 143 130 }; 131 + use serde::Deserialize; 144 132 145 133 use super::*; 146 134 ··· 148 136 "hello world".to_owned() 149 137 } 150 138 139 + #[derive(Deserialize, Debug)] 140 + pub struct Req { 141 + handle: AtIdentifier, 142 + } 143 + 151 144 pub async fn post( 152 145 State(state): State<AppState>, 153 - Json(handle): Json<AtIdentifier>, 146 + Form(req): Form<Req>, 154 147 ) -> Result<impl IntoResponse> { 155 - let did_document = state.inner.resolve_did_document(&handle).await.unwrap(); 148 + let did_document = state.inner.resolve_did_document(&req.handle).await.unwrap(); 149 + dbg!(&did_document); 156 150 let res = state 157 151 .inner 158 152 .oauth_client ··· 161 155 Scope::Known(KnownScope::Atproto), 162 156 Scope::Known(KnownScope::TransitionGeneric), 163 157 ], 158 + prompt: Some(AuthorizeOptionPrompt::Login), 164 159 ..Default::default() 165 160 }) 166 161 .await ··· 180 175 pub async fn get( 181 176 Query(params): Query<atrium_oauth_client::CallbackParams>, 182 177 State(state): State<AppState>, 178 + session: tower_sessions::Session, 183 179 ) -> StatusCode { 184 - let ts = state.inner.oauth_client.callback(params).await; 185 - println!("ts: {ts:?}"); 180 + let ts = state.inner.oauth_client.callback(params).await.unwrap(); 181 + let _ = session.insert("bild", &ts).await; 182 + StatusCode::OK 183 + } 184 + } 185 + 186 + // dummy endpoint to test if sessions are working 187 + mod index { 188 + use axum::http::StatusCode; 189 + 190 + pub async fn get(session: tower_sessions::Session) -> StatusCode { 191 + dbg!( 192 + session 193 + .get::<atrium_oauth_client::TokenSet>("bild") 194 + .await 195 + .unwrap() 196 + ); 186 197 StatusCode::OK 187 198 } 188 199 } 200 + 201 + struct AuthenticatedClient { 202 + token: String, 203 + base_uri: String, 204 + inner: IsahcClient, 205 + } 206 + 207 + impl atrium_xrpc::HttpClient for AuthenticatedClient { 208 + async fn send_http( 209 + &self, 210 + request: atrium_xrpc::http::Request<Vec<u8>>, 211 + ) -> Result< 212 + atrium_xrpc::http::Response<Vec<u8>>, 213 + Box<dyn std::error::Error + Send + Sync + 'static>, 214 + > { 215 + self.inner.send_http(request).await 216 + } 217 + } 218 + 219 + impl atrium_xrpc::XrpcClient for AuthenticatedClient { 220 + fn base_uri(&self) -> String { 221 + self.base_uri.clone() 222 + } 223 + async fn authorization_token(&self, _: bool) -> Option<AuthorizationToken> { 224 + Some(AuthorizationToken::Dpop(self.token.clone())) 225 + } 226 + } 227 + 228 + // dummy endpoint to perform an atproto request with access token 229 + mod test_atproto { 230 + use atrium_api::{ 231 + agent::{AtpAgent, store::MemorySessionStore}, 232 + app, com, 233 + }; 234 + use axum::http::StatusCode; 235 + 236 + use super::*; 237 + 238 + fn get_session_client( 239 + tokenset: &atrium_oauth_client::TokenSet, 240 + ) -> AtpServiceClient<AuthenticatedClient> { 241 + //) -> AtpAgent<MemorySessionStore, AuthenticatedClient> { 242 + // AtpAgent::new( 243 + // AuthenticatedClient { 244 + // token: tokenset.access_token.clone(), 245 + // base_uri: tokenset.iss.clone(), 246 + // inner: IsahcClient::new(tokenset.iss.clone()), 247 + // }, 248 + // MemorySessionStore::default(), 249 + // ) 250 + AtpServiceClient::new(AuthenticatedClient { 251 + token: tokenset.access_token.clone(), 252 + base_uri: tokenset.aud.clone(), 253 + inner: IsahcClient::new(tokenset.aud.clone()), 254 + }) 255 + } 256 + 257 + pub async fn get(session: tower_sessions::Session) -> StatusCode { 258 + let token_set = session 259 + .get::<atrium_oauth_client::TokenSet>("bild") 260 + .await 261 + .ok() 262 + .flatten() 263 + .unwrap(); 264 + let client = get_session_client(&token_set); 265 + let res = client 266 + .service 267 + .com 268 + .atproto 269 + .repo 270 + .upload_blob(vec![0]) 271 + .await 272 + .unwrap(); 273 + dbg!(res); 274 + StatusCode::OK 275 + } 276 + } 277 + 278 + struct HickoryDnsTxtResolver { 279 + resolver: TokioAsyncResolver, 280 + } 281 + 282 + impl Default for HickoryDnsTxtResolver { 283 + fn default() -> Self { 284 + Self { 285 + resolver: TokioAsyncResolver::tokio_from_system_conf() 286 + .expect("failed to create resolver"), 287 + } 288 + } 289 + } 290 + 291 + impl DnsTxtResolver for HickoryDnsTxtResolver { 292 + async fn resolve( 293 + &self, 294 + query: &str, 295 + ) -> core::result::Result<Vec<String>, Box<dyn std::error::Error + Send + Sync + 'static>> { 296 + Ok(self 297 + .resolver 298 + .txt_lookup(query) 299 + .await? 300 + .iter() 301 + .map(|txt| txt.to_string()) 302 + .collect()) 303 + } 304 + }
+1
flake.nix
··· 40 40 pkgs.go 41 41 pkgs.air 42 42 43 + pkgs.httpie 43 44 pkgs.bacon 44 45 rust-bin 45 46 pkgs.pkg-config