+89
-182
core/src/main.rs
+89
-182
core/src/main.rs
···
1
1
use std::sync::Arc;
2
+
use tokio::sync::Mutex;
2
3
3
4
use atrium_api::{
4
-
client::AtpServiceClient, did_doc::DidDocument, types::string::AtIdentifier,
5
+
agent::{AtpAgent, store::MemorySessionStore},
6
+
client::AtpServiceClient,
7
+
did_doc::DidDocument,
8
+
types::string::AtIdentifier,
5
9
xrpc::types::AuthorizationToken,
6
10
};
7
11
use atrium_common::resolver::Resolver;
···
29
33
let app_state = AppState::new();
30
34
let service = Router::new()
31
35
.route("/", routing::get(index::get))
32
-
.route("/test-auth", routing::get(test_atproto::get))
36
+
// .route("/test-auth", routing::get(test_atproto::get))
33
37
.route("/login", routing::get(login::get).post(login::post))
34
-
.route("/callback", routing::get(callback::get))
38
+
// .route("/callback", routing::get(callback::get))
35
39
.layer(session_layer)
36
40
.with_state(app_state);
37
41
···
41
45
42
46
#[derive(Clone)]
43
47
struct AppState {
44
-
inner: Arc<AppStateInner>,
48
+
inner: Arc<Mutex<AppStateInner>>,
49
+
did_resolver: Arc<CommonDidResolver<DefaultHttpClient>>,
50
+
handle_resolver: Arc<AtprotoHandleResolver<HickoryDnsTxtResolver, DefaultHttpClient>>,
45
51
}
46
52
47
53
impl AppState {
48
54
fn new() -> Self {
49
-
Self {
50
-
inner: Arc::new(AppStateInner::new()),
51
-
}
52
-
}
53
-
}
54
-
55
-
struct AppStateInner {
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
-
>,
63
-
}
64
-
65
-
impl AppStateInner {
66
-
fn new() -> Self {
67
55
let client = Arc::new(DefaultHttpClient::default());
68
-
let did_resolver = Self::did_resolver(client.clone());
69
-
let handle_resolver = Self::handle_resolver(client.clone());
70
-
let config = OAuthClientConfig {
71
-
client_metadata: AtprotoLocalhostClientMetadata {
72
-
// TODO: change this
73
-
redirect_uris: Some(vec![String::from("http://127.0.0.1:3000/callback")]),
74
-
scopes: Some(vec![
75
-
Scope::Known(KnownScope::Atproto),
76
-
Scope::Known(KnownScope::TransitionGeneric),
77
-
]),
78
-
},
79
-
keys: None,
80
-
resolver: OAuthResolverConfig {
81
-
did_resolver: Self::did_resolver(client.clone()),
82
-
handle_resolver: Self::handle_resolver(client.clone()),
83
-
authorization_server_metadata: Default::default(),
84
-
protected_resource_metadata: Default::default(),
85
-
},
86
-
state_store: MemoryStateStore::default(),
87
-
};
88
-
let oauth_client = OAuthClient::new(config).unwrap();
89
56
Self {
90
-
oauth_client,
91
-
did_resolver,
92
-
handle_resolver,
57
+
inner: Arc::new(Mutex::new(AppStateInner::new(client.clone()))),
58
+
did_resolver: Arc::new(did_resolver(client.clone())),
59
+
handle_resolver: Arc::new(handle_resolver(client.clone())),
93
60
}
94
61
}
95
62
96
-
fn did_resolver<H: HttpClient>(http_client: Arc<H>) -> CommonDidResolver<H> {
97
-
CommonDidResolver::new(CommonDidResolverConfig {
98
-
plc_directory_url: DEFAULT_PLC_DIRECTORY_URL.to_string(),
99
-
http_client,
100
-
})
101
-
}
102
-
103
-
fn handle_resolver<H: HttpClient>(
104
-
http_client: Arc<H>,
105
-
) -> AtprotoHandleResolver<HickoryDnsTxtResolver, H> {
106
-
AtprotoHandleResolver::new(AtprotoHandleResolverConfig {
107
-
dns_txt_resolver: HickoryDnsTxtResolver::default(),
108
-
http_client,
109
-
})
110
-
}
111
-
112
63
async fn resolve_did_document(
113
64
&self,
114
65
ident: &AtIdentifier,
···
123
74
}
124
75
}
125
76
77
+
fn did_resolver<H: HttpClient>(http_client: Arc<H>) -> CommonDidResolver<H> {
78
+
CommonDidResolver::new(CommonDidResolverConfig {
79
+
plc_directory_url: DEFAULT_PLC_DIRECTORY_URL.to_string(),
80
+
http_client,
81
+
})
82
+
}
83
+
84
+
fn handle_resolver<H: HttpClient>(
85
+
http_client: Arc<H>,
86
+
) -> AtprotoHandleResolver<HickoryDnsTxtResolver, H> {
87
+
AtprotoHandleResolver::new(AtprotoHandleResolverConfig {
88
+
dns_txt_resolver: HickoryDnsTxtResolver::default(),
89
+
http_client,
90
+
})
91
+
}
92
+
93
+
struct AppStateInner {
94
+
agent: Option<AtpAgent<MemorySessionStore, IsahcClient>>,
95
+
}
96
+
97
+
impl AppStateInner {
98
+
fn new(http_client: Arc<DefaultHttpClient>) -> Self {
99
+
// let config = OAuthClientConfig {
100
+
// client_metadata: AtprotoLocalhostClientMetadata {
101
+
// // TODO: change this
102
+
// redirect_uris: Some(vec![String::from("http://127.0.0.1:3000/callback")]),
103
+
// scopes: Some(vec![
104
+
// Scope::Known(KnownScope::Atproto),
105
+
// Scope::Known(KnownScope::TransitionGeneric),
106
+
// ]),
107
+
// },
108
+
// keys: None,
109
+
// resolver: OAuthResolverConfig {
110
+
// did_resolver: did_resolver(http_client.clone()),
111
+
// handle_resolver: handle_resolver(http_client.clone()),
112
+
// authorization_server_metadata: Default::default(),
113
+
// protected_resource_metadata: Default::default(),
114
+
// },
115
+
// state_store: MemoryStateStore::default(),
116
+
// };
117
+
// let oauth_client = OAuthClient::new(config).unwrap();
118
+
Self { agent: None }
119
+
}
120
+
}
121
+
126
122
mod login {
127
123
use axum::{
128
124
extract::{Form, State},
129
-
response::{IntoResponse, Redirect, Result},
125
+
http::StatusCode,
126
+
response::IntoResponse,
130
127
};
131
128
use serde::Deserialize;
132
129
···
139
136
#[derive(Deserialize, Debug)]
140
137
pub struct Req {
141
138
handle: AtIdentifier,
139
+
app_password: String,
142
140
}
143
141
144
142
pub async fn post(
145
143
State(state): State<AppState>,
144
+
session: tower_sessions::Session,
146
145
Form(req): Form<Req>,
147
-
) -> Result<impl IntoResponse> {
148
-
let did_document = state.inner.resolve_did_document(&req.handle).await.unwrap();
149
-
dbg!(&did_document);
150
-
let res = state
151
-
.inner
152
-
.oauth_client
153
-
.authorize(did_document.get_pds_endpoint().unwrap(), AuthorizeOptions {
154
-
scopes: vec![
155
-
Scope::Known(KnownScope::Atproto),
156
-
Scope::Known(KnownScope::TransitionGeneric),
157
-
],
158
-
prompt: Some(AuthorizeOptionPrompt::Login),
159
-
..Default::default()
160
-
})
161
-
.await
162
-
.unwrap();
163
-
Ok(Redirect::to(&res))
164
-
}
165
-
}
166
-
167
-
mod callback {
168
-
use axum::{
169
-
extract::{Query, State},
170
-
http::StatusCode,
171
-
};
172
-
173
-
use super::*;
174
-
175
-
pub async fn get(
176
-
Query(params): Query<atrium_oauth_client::CallbackParams>,
177
-
State(state): State<AppState>,
178
-
session: tower_sessions::Session,
179
-
) -> StatusCode {
180
-
let ts = state.inner.oauth_client.callback(params).await.unwrap();
181
-
let _ = session.insert("bild", &ts).await;
146
+
) -> impl IntoResponse {
147
+
let did_document = state.resolve_did_document(&req.handle).await.unwrap();
148
+
let agent = AtpAgent::new(
149
+
IsahcClient::new(did_document.get_pds_endpoint().unwrap()),
150
+
MemorySessionStore::default(),
151
+
);
152
+
let res = agent.login(req.handle, req.app_password).await.unwrap();
153
+
println!("logged in as {:?} ({:?})", res.handle, res.did);
154
+
session.insert("at_session", res).await.unwrap();
155
+
println!("stored session");
182
156
StatusCode::OK
183
157
}
184
158
}
185
159
186
160
// dummy endpoint to test if sessions are working
187
161
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
-
);
197
-
StatusCode::OK
198
-
}
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
162
use super::*;
237
163
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")
164
+
pub async fn get(session: tower_sessions::Session) -> &'static str {
165
+
match session
166
+
.get::<atrium_api::agent::Session>("at_session")
260
167
.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
168
+
.unwrap()
169
+
{
170
+
None => "no session",
171
+
Some(s) => {
172
+
// let did_doc = s.did_doc.unwrap();
173
+
let agent = AtpAgent::new(
174
+
IsahcClient::new("https://bsky.social"),
175
+
MemorySessionStore::default(),
176
+
);
177
+
println!("resuming session of {:?} ({:?})", s.handle, s.did);
178
+
agent.resume_session(s).await.unwrap();
179
+
"resuming session"
180
+
}
181
+
}
275
182
}
276
183
}
277
184