+1
Cargo.lock
+1
Cargo.lock
+1
core/Cargo.toml
+1
core/Cargo.toml
+160
-44
core/src/main.rs
+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
+
}