tangled
alpha
login
or
join now
bad-example.com
/
microcosm-links
7
fork
atom
APIs for links and references in the ATmosphere
7
fork
atom
overview
issues
pulls
pipelines
look up identity from did-cookie
bad-example.com
8 months ago
8ddb11f6
08b772bf
+468
-25
9 changed files
expand all
collapse all
unified
split
Cargo.lock
who-am-i
Cargo.toml
demo
index.html
src
expiring_task_map.rs
identity_resolver.rs
lib.rs
main.rs
server.rs
templates
prompt-known.hbs
+161
Cargo.lock
···
500
500
]
501
501
502
502
[[package]]
503
503
+
name = "axum-template"
504
504
+
version = "3.0.0"
505
505
+
source = "registry+https://github.com/rust-lang/crates.io-index"
506
506
+
checksum = "3df50f7d669bfc3a8c348f08f536fe37e7acfbeded3cfdffd2ad3d76725fc40c"
507
507
+
dependencies = [
508
508
+
"axum",
509
509
+
"handlebars",
510
510
+
"serde",
511
511
+
"thiserror 2.0.12",
512
512
+
]
513
513
+
514
514
+
[[package]]
503
515
name = "backtrace"
504
516
version = "0.3.74"
505
517
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1164
1176
]
1165
1177
1166
1178
[[package]]
1179
1179
+
name = "derive_builder"
1180
1180
+
version = "0.20.2"
1181
1181
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1182
1182
+
checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947"
1183
1183
+
dependencies = [
1184
1184
+
"derive_builder_macro",
1185
1185
+
]
1186
1186
+
1187
1187
+
[[package]]
1188
1188
+
name = "derive_builder_core"
1189
1189
+
version = "0.20.2"
1190
1190
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1191
1191
+
checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8"
1192
1192
+
dependencies = [
1193
1193
+
"darling",
1194
1194
+
"proc-macro2",
1195
1195
+
"quote",
1196
1196
+
"syn",
1197
1197
+
]
1198
1198
+
1199
1199
+
[[package]]
1200
1200
+
name = "derive_builder_macro"
1201
1201
+
version = "0.20.2"
1202
1202
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1203
1203
+
checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c"
1204
1204
+
dependencies = [
1205
1205
+
"derive_builder_core",
1206
1206
+
"syn",
1207
1207
+
]
1208
1208
+
1209
1209
+
[[package]]
1167
1210
name = "digest"
1168
1211
version = "0.10.7"
1169
1212
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1716
1759
"tokio",
1717
1760
"tokio-util",
1718
1761
"tracing",
1762
1762
+
]
1763
1763
+
1764
1764
+
[[package]]
1765
1765
+
name = "handlebars"
1766
1766
+
version = "6.3.2"
1767
1767
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1768
1768
+
checksum = "759e2d5aea3287cb1190c8ec394f42866cb5bf74fcbf213f354e3c856ea26098"
1769
1769
+
dependencies = [
1770
1770
+
"derive_builder",
1771
1771
+
"log",
1772
1772
+
"num-order",
1773
1773
+
"pest",
1774
1774
+
"pest_derive",
1775
1775
+
"serde",
1776
1776
+
"serde_json",
1777
1777
+
"thiserror 2.0.12",
1778
1778
+
"walkdir",
1719
1779
]
1720
1780
1721
1781
[[package]]
···
2879
2939
]
2880
2940
2881
2941
[[package]]
2942
2942
+
name = "num-modular"
2943
2943
+
version = "0.6.1"
2944
2944
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2945
2945
+
checksum = "17bb261bf36fa7d83f4c294f834e91256769097b3cb505d44831e0a179ac647f"
2946
2946
+
2947
2947
+
[[package]]
2948
2948
+
name = "num-order"
2949
2949
+
version = "1.2.0"
2950
2950
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2951
2951
+
checksum = "537b596b97c40fcf8056d153049eb22f481c17ebce72a513ec9286e4986d1bb6"
2952
2952
+
dependencies = [
2953
2953
+
"num-modular",
2954
2954
+
]
2955
2955
+
2956
2956
+
[[package]]
2882
2957
name = "num-traits"
2883
2958
version = "0.2.19"
2884
2959
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3058
3133
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
3059
3134
3060
3135
[[package]]
3136
3136
+
name = "pest"
3137
3137
+
version = "2.8.1"
3138
3138
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3139
3139
+
checksum = "1db05f56d34358a8b1066f67cbb203ee3e7ed2ba674a6263a1d5ec6db2204323"
3140
3140
+
dependencies = [
3141
3141
+
"memchr",
3142
3142
+
"thiserror 2.0.12",
3143
3143
+
"ucd-trie",
3144
3144
+
]
3145
3145
+
3146
3146
+
[[package]]
3147
3147
+
name = "pest_derive"
3148
3148
+
version = "2.8.1"
3149
3149
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3150
3150
+
checksum = "bb056d9e8ea77922845ec74a1c4e8fb17e7c218cc4fc11a15c5d25e189aa40bc"
3151
3151
+
dependencies = [
3152
3152
+
"pest",
3153
3153
+
"pest_generator",
3154
3154
+
]
3155
3155
+
3156
3156
+
[[package]]
3157
3157
+
name = "pest_generator"
3158
3158
+
version = "2.8.1"
3159
3159
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3160
3160
+
checksum = "87e404e638f781eb3202dc82db6760c8ae8a1eeef7fb3fa8264b2ef280504966"
3161
3161
+
dependencies = [
3162
3162
+
"pest",
3163
3163
+
"pest_meta",
3164
3164
+
"proc-macro2",
3165
3165
+
"quote",
3166
3166
+
"syn",
3167
3167
+
]
3168
3168
+
3169
3169
+
[[package]]
3170
3170
+
name = "pest_meta"
3171
3171
+
version = "2.8.1"
3172
3172
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3173
3173
+
checksum = "edd1101f170f5903fde0914f899bb503d9ff5271d7ba76bbb70bea63690cc0d5"
3174
3174
+
dependencies = [
3175
3175
+
"pest",
3176
3176
+
"sha2",
3177
3177
+
]
3178
3178
+
3179
3179
+
[[package]]
3061
3180
name = "pin-project-lite"
3062
3181
version = "0.2.16"
3063
3182
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3602
3721
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
3603
3722
3604
3723
[[package]]
3724
3724
+
name = "same-file"
3725
3725
+
version = "1.0.6"
3726
3726
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3727
3727
+
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
3728
3728
+
dependencies = [
3729
3729
+
"winapi-util",
3730
3730
+
]
3731
3731
+
3732
3732
+
[[package]]
3605
3733
name = "schannel"
3606
3734
version = "0.1.27"
3607
3735
source = "registry+https://github.com/rust-lang/crates.io-index"
···
4557
4685
checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
4558
4686
4559
4687
[[package]]
4688
4688
+
name = "ucd-trie"
4689
4689
+
version = "0.1.7"
4690
4690
+
source = "registry+https://github.com/rust-lang/crates.io-index"
4691
4691
+
checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971"
4692
4692
+
4693
4693
+
[[package]]
4560
4694
name = "ufos"
4561
4695
version = "0.1.0"
4562
4696
dependencies = [
···
4734
4868
]
4735
4869
4736
4870
[[package]]
4871
4871
+
name = "walkdir"
4872
4872
+
version = "2.5.0"
4873
4873
+
source = "registry+https://github.com/rust-lang/crates.io-index"
4874
4874
+
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
4875
4875
+
dependencies = [
4876
4876
+
"same-file",
4877
4877
+
"winapi-util",
4878
4878
+
]
4879
4879
+
4880
4880
+
[[package]]
4737
4881
name = "want"
4738
4882
version = "0.3.1"
4739
4883
source = "registry+https://github.com/rust-lang/crates.io-index"
···
4865
5009
version = "0.1.0"
4866
5010
dependencies = [
4867
5011
"atrium-api 0.25.4",
5012
5012
+
"atrium-common 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
4868
5013
"atrium-identity",
4869
5014
"atrium-oauth",
4870
5015
"axum",
4871
5016
"axum-extra",
5017
5017
+
"axum-template",
4872
5018
"clap",
5019
5019
+
"ctrlc",
5020
5020
+
"dashmap",
5021
5021
+
"handlebars",
4873
5022
"hickory-resolver",
4874
5023
"metrics",
5024
5024
+
"rand 0.9.1",
4875
5025
"serde",
5026
5026
+
"serde_json",
4876
5027
"tokio",
4877
5028
"tokio-util",
5029
5029
+
"url",
4878
5030
]
4879
5031
4880
5032
[[package]]
···
4898
5050
version = "0.4.0"
4899
5051
source = "registry+https://github.com/rust-lang/crates.io-index"
4900
5052
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
5053
5053
+
5054
5054
+
[[package]]
5055
5055
+
name = "winapi-util"
5056
5056
+
version = "0.1.9"
5057
5057
+
source = "registry+https://github.com/rust-lang/crates.io-index"
5058
5058
+
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
5059
5059
+
dependencies = [
5060
5060
+
"windows-sys 0.48.0",
5061
5061
+
]
4901
5062
4902
5063
[[package]]
4903
5064
name = "winapi-x86_64-pc-windows-gnu"
+8
who-am-i/Cargo.toml
···
5
5
6
6
[dependencies]
7
7
atrium-api = { version = "0.25.4", default-features = false }
8
8
+
atrium-common = "0.1.2"
8
9
atrium-identity = "0.1.5"
9
10
atrium-oauth = "0.1.3"
10
11
axum = "0.8.4"
11
12
axum-extra = { version = "0.10.1", features = ["cookie-signed", "typed-header"] }
13
13
+
axum-template = { version = "3.0.0", features = ["handlebars"] }
12
14
clap = { version = "4.5.40", features = ["derive"] }
15
15
+
ctrlc = "3.4.7"
16
16
+
dashmap = "6.1.0"
17
17
+
handlebars = { version = "6.3.2", features = ["dir_source"] }
13
18
hickory-resolver = "0.25.2"
14
19
metrics = "0.24.2"
20
20
+
rand = "0.9.1"
15
21
serde = { version = "1.0.219", features = ["derive"] }
22
22
+
serde_json = "1.0.140"
16
23
tokio = { version = "1.45.1", features = ["full", "macros"] }
17
24
tokio-util = "0.7.15"
25
25
+
url = "2.5.4"
+1
-3
who-am-i/demo/index.html
···
9
9
10
10
<h1>hey</h1>
11
11
12
12
-
<iframe src="http://127.0.0.1:9997/prompt" style="border: 2px solid #000; border-radius: 0.5em;" />
13
13
-
14
14
-
12
12
+
<iframe src="http://127.0.0.1:9997/prompt" style="border: none" height="140" width="280" />
+53
who-am-i/src/expiring_task_map.rs
···
1
1
+
use dashmap::DashMap;
2
2
+
use rand::{Rng, distr::Alphanumeric};
3
3
+
use std::sync::Arc;
4
4
+
use std::time::Duration;
5
5
+
use tokio::task::{JoinHandle, spawn};
6
6
+
use tokio::time::sleep; // 0.8
7
7
+
8
8
+
#[derive(Clone)]
9
9
+
pub struct ExpiringTaskMap<T>(Arc<TaskMap<T>>);
10
10
+
11
11
+
impl<T: Send + 'static> ExpiringTaskMap<T> {
12
12
+
pub fn new(expiration: Duration) -> Self {
13
13
+
let map = TaskMap {
14
14
+
map: DashMap::new(),
15
15
+
expiration,
16
16
+
};
17
17
+
Self(Arc::new(map))
18
18
+
}
19
19
+
20
20
+
pub fn dispatch(&self, task: impl Future<Output = T> + Send + 'static) -> String {
21
21
+
let task_key: String = rand::rng()
22
22
+
.sample_iter(&Alphanumeric)
23
23
+
.take(24)
24
24
+
.map(char::from)
25
25
+
.collect();
26
26
+
27
27
+
// spawn a tokio task and put the join handle in the map for later retrieval
28
28
+
self.0.map.insert(task_key.clone(), spawn(task));
29
29
+
30
30
+
// spawn a second task to clean up the map in case it doesn't get claimed
31
31
+
spawn({
32
32
+
let me = self.0.clone();
33
33
+
let key = task_key.clone();
34
34
+
async move {
35
35
+
sleep(me.expiration).await;
36
36
+
let _ = me.map.remove(&key);
37
37
+
// TODO: also use a cancellation token so taking and expiring can mutually cancel
38
38
+
}
39
39
+
});
40
40
+
41
41
+
task_key
42
42
+
}
43
43
+
44
44
+
pub fn take(&self, key: &str) -> Option<JoinHandle<T>> {
45
45
+
eprintln!("trying to take...");
46
46
+
self.0.map.remove(key).map(|(_, handle)| handle)
47
47
+
}
48
48
+
}
49
49
+
50
50
+
struct TaskMap<T> {
51
51
+
map: DashMap<String, JoinHandle<T>>,
52
52
+
expiration: Duration,
53
53
+
}
+20
who-am-i/src/identity_resolver.rs
···
1
1
+
use atrium_api::types::string::Did;
2
2
+
use atrium_common::resolver::Resolver;
3
3
+
use atrium_identity::did::{CommonDidResolver, CommonDidResolverConfig, DEFAULT_PLC_DIRECTORY_URL};
4
4
+
use atrium_oauth::DefaultHttpClient;
5
5
+
use std::sync::Arc;
6
6
+
7
7
+
pub async fn resolve_identity(did: String) -> String {
8
8
+
let http_client = Arc::new(DefaultHttpClient::default());
9
9
+
let resolver = CommonDidResolver::new(CommonDidResolverConfig {
10
10
+
plc_directory_url: DEFAULT_PLC_DIRECTORY_URL.to_string(),
11
11
+
http_client: Arc::clone(&http_client),
12
12
+
});
13
13
+
let doc = resolver.resolve(&Did::new(did).unwrap()).await.unwrap(); // TODO: this is only half the resolution? or is atrium checking dns?
14
14
+
if let Some(aka) = doc.also_known_as {
15
15
+
if let Some(f) = aka.first() {
16
16
+
return f.to_string();
17
17
+
}
18
18
+
}
19
19
+
"who knows".to_string()
20
20
+
}
+4
who-am-i/src/lib.rs
···
1
1
mod dns_resolver;
2
2
+
mod expiring_task_map;
3
3
+
mod identity_resolver;
2
4
mod oauth;
3
5
mod server;
4
6
5
7
pub use dns_resolver::HickoryDnsTxtResolver;
8
8
+
pub use expiring_task_map::ExpiringTaskMap;
9
9
+
pub use identity_resolver::resolve_identity;
6
10
pub use oauth::{Client, authorize, client};
7
11
pub use server::serve;
+27
-2
who-am-i/src/main.rs
···
1
1
+
use clap::Parser;
1
2
use tokio_util::sync::CancellationToken;
2
3
use who_am_i::serve;
3
4
5
5
+
/// Aggregate links in the at-mosphere
6
6
+
#[derive(Parser, Debug, Clone)]
7
7
+
#[command(version, about, long_about = None)]
8
8
+
struct Args {
9
9
+
/// secret key from which the cookie-signing key is derived
10
10
+
///
11
11
+
/// must have at least 512 bits (64 bytes) of randomness
12
12
+
///
13
13
+
/// eg: `cat /dev/urandom | head -c 64 | base64`
14
14
+
#[arg(long)]
15
15
+
app_secret: String,
16
16
+
/// Enable dev mode
17
17
+
///
18
18
+
/// enables automatic template reloading
19
19
+
#[arg(long, action)]
20
20
+
dev: bool,
21
21
+
}
22
22
+
4
23
#[tokio::main]
5
24
async fn main() {
6
6
-
let server_shutdown = CancellationToken::new();
7
7
-
serve(server_shutdown).await;
25
25
+
let shutdown = CancellationToken::new();
26
26
+
27
27
+
let ctrlc_shutdown = shutdown.clone();
28
28
+
ctrlc::set_handler(move || ctrlc_shutdown.cancel()).expect("failed to set ctrl-c handler");
29
29
+
30
30
+
let args = Args::parse();
31
31
+
32
32
+
serve(shutdown, args.app_secret, args.dev).await;
8
33
}
+98
-20
who-am-i/src/server.rs
···
3
3
use axum::{
4
4
Router,
5
5
extract::{FromRef, Query, State},
6
6
+
http::header::{HeaderMap, REFERER},
6
7
response::{Html, IntoResponse, Redirect},
7
8
routing::get,
8
9
};
9
10
use axum_extra::extract::cookie::{Cookie, Key, SameSite, SignedCookieJar};
11
11
+
use axum_template::{RenderHtml, engine::Engine};
12
12
+
use handlebars::{Handlebars, handlebars_helper};
10
13
11
11
-
use serde::Deserialize;
14
14
+
use serde::{Deserialize, Serialize};
15
15
+
use serde_json::Value;
12
16
use std::sync::Arc;
17
17
+
use std::time::Duration;
13
18
use tokio::net::TcpListener;
14
19
use tokio_util::sync::CancellationToken;
20
20
+
use url::Url;
15
21
16
16
-
use crate::{Client, authorize, client};
22
22
+
use crate::{Client, ExpiringTaskMap, authorize, client, resolve_identity};
17
23
18
24
const FAVICON: &[u8] = include_bytes!("../static/favicon.ico");
19
25
const INDEX_HTML: &str = include_str!("../static/index.html");
20
26
const LOGIN_HTML: &str = include_str!("../static/login.html");
21
27
22
22
-
pub async fn serve(shutdown: CancellationToken) {
28
28
+
const DID_COOKIE_KEY: &str = "did";
29
29
+
30
30
+
type AppEngine = Engine<Handlebars<'static>>;
31
31
+
32
32
+
#[derive(Clone)]
33
33
+
struct AppState {
34
34
+
pub key: Key,
35
35
+
pub engine: AppEngine,
36
36
+
pub client: Arc<Client>,
37
37
+
pub resolving: ExpiringTaskMap<String>,
38
38
+
}
39
39
+
40
40
+
impl FromRef<AppState> for Key {
41
41
+
fn from_ref(state: &AppState) -> Self {
42
42
+
state.key.clone()
43
43
+
}
44
44
+
}
45
45
+
46
46
+
pub async fn serve(shutdown: CancellationToken, app_secret: String, dev: bool) {
47
47
+
let mut hbs = Handlebars::new();
48
48
+
hbs.set_dev_mode(dev);
49
49
+
hbs.register_templates_directory("templates", Default::default())
50
50
+
.unwrap();
51
51
+
52
52
+
handlebars_helper!(json: |v: Value| serde_json::to_string(&v).unwrap());
53
53
+
hbs.register_helper("json", Box::new(json));
54
54
+
55
55
+
// clients have to pick up their identity-resolving tasks within this period
56
56
+
let task_pickup_expiration = Duration::from_secs(15);
57
57
+
23
58
let state = AppState {
24
24
-
key: Key::generate(), // TODO: via config
59
59
+
engine: Engine::new(hbs),
60
60
+
key: Key::from(app_secret.as_bytes()), // TODO: via config
25
61
client: Arc::new(client()),
62
62
+
resolving: ExpiringTaskMap::new(task_pickup_expiration),
26
63
};
27
64
28
65
let app = Router::new()
29
66
.route("/", get(|| async { Html(INDEX_HTML) }))
30
67
.route("/favicon.ico", get(|| async { FAVICON })) // todo MIME
31
68
.route("/prompt", get(prompt))
69
69
+
.route("/user-info", get(user_info))
32
70
.route("/auth", get(start_oauth))
33
71
.route("/authorized", get(complete_oauth))
34
72
.with_state(state);
···
43
81
.unwrap();
44
82
}
45
83
46
46
-
#[derive(Clone)]
47
47
-
struct AppState {
48
48
-
pub key: Key,
49
49
-
pub client: Arc<Client>,
84
84
+
#[derive(Debug, Serialize)]
85
85
+
struct Known {
86
86
+
did: Value,
87
87
+
fetch_key: Value,
88
88
+
parent_host: String,
50
89
}
90
90
+
async fn prompt(
91
91
+
State(AppState {
92
92
+
engine, resolving, ..
93
93
+
}): State<AppState>,
94
94
+
jar: SignedCookieJar,
95
95
+
headers: HeaderMap,
96
96
+
) -> impl IntoResponse {
97
97
+
let Some(referrer) = headers.get(REFERER) else {
98
98
+
return Html::<&'static str>("missing referrer, sorry").into_response();
99
99
+
};
100
100
+
let Ok(referrer) = referrer.to_str() else {
101
101
+
return "referer contained opaque bytes".into_response();
102
102
+
};
103
103
+
let Ok(url) = Url::parse(referrer) else {
104
104
+
return "referrer was not a url".into_response();
105
105
+
};
106
106
+
let Some(parent_host) = url.host_str() else {
107
107
+
return "could nto get host from url".into_response();
108
108
+
};
109
109
+
let m = if let Some(did) = jar.get(DID_COOKIE_KEY) {
110
110
+
let did = did.value_trimmed().to_string();
51
111
52
52
-
impl FromRef<AppState> for Key {
53
53
-
fn from_ref(state: &AppState) -> Self {
54
54
-
state.key.clone()
55
55
-
}
112
112
+
let fetch_key = resolving.dispatch(resolve_identity(did.clone()));
113
113
+
114
114
+
let json_did = Value::String(did);
115
115
+
let json_fetch_key = Value::String(fetch_key);
116
116
+
let known = Known {
117
117
+
did: json_did,
118
118
+
fetch_key: json_fetch_key,
119
119
+
parent_host: parent_host.to_string(),
120
120
+
};
121
121
+
return (jar, RenderHtml("prompt-known", engine, known)).into_response();
122
122
+
} else {
123
123
+
LOGIN_HTML.into_response()
124
124
+
};
125
125
+
(jar, Html(m)).into_response()
56
126
}
57
127
58
58
-
async fn prompt(jar: SignedCookieJar) -> impl IntoResponse {
59
59
-
let m = if let Some(did) = jar.get("did") {
60
60
-
format!("oh i know you: {did}")
61
61
-
} else {
62
62
-
LOGIN_HTML.into()
128
128
+
#[derive(Debug, Deserialize)]
129
129
+
#[serde(rename_all = "kebab-case")]
130
130
+
struct UserInfoParams {
131
131
+
fetch_key: String,
132
132
+
}
133
133
+
async fn user_info(
134
134
+
State(AppState { resolving, .. }): State<AppState>,
135
135
+
Query(params): Query<UserInfoParams>,
136
136
+
) -> impl IntoResponse {
137
137
+
// let fetch_key: [char; 16] = params.fetch_key.chars().collect::<Vec<_>>().try_into().unwrap();
138
138
+
let Some(handle) = resolving.take(¶ms.fetch_key) else {
139
139
+
return "oops, task does not exist or is gone".into_response();
63
140
};
64
64
-
(jar, Html(m))
141
141
+
let s = handle.await.unwrap();
142
142
+
format!("sup: {s}").into_response()
65
143
}
66
144
67
145
#[derive(Debug, Deserialize)]
···
74
152
jar: SignedCookieJar,
75
153
) -> (SignedCookieJar, Redirect) {
76
154
// if any existing session was active, clear it first
77
77
-
let jar = jar.remove("did");
155
155
+
let jar = jar.remove(DID_COOKIE_KEY);
78
156
79
157
let auth_url = authorize(&state.client, ¶ms.handle).await;
80
158
(jar, Redirect::to(&auth_url))
···
89
167
panic!("failed to do client callback");
90
168
};
91
169
let did = oauth_session.did().await.expect("a did to be present");
92
92
-
let cookie = Cookie::build(("did", did.to_string()))
170
170
+
let cookie = Cookie::build((DID_COOKIE_KEY, did.to_string()))
93
171
.http_only(true)
94
172
.secure(true)
95
173
.same_site(SameSite::None)
+96
who-am-i/templates/prompt-known.hbs
···
1
1
+
<!doctype html>
2
2
+
3
3
+
<style>
4
4
+
body {
5
5
+
color: #434;
6
6
+
font-family: 'Iowan Old Style', 'Palatino Linotype', 'URW Palladio L', P052, serif;
7
7
+
margin: 0;
8
8
+
min-height: 100vh;
9
9
+
padding: 0;
10
10
+
}
11
11
+
.wrap {
12
12
+
border: 2px solid #221828;
13
13
+
border-radius: 0.5rem;
14
14
+
box-sizing: border-box;
15
15
+
overflow: hidden;
16
16
+
display: flex;
17
17
+
flex-direction: column;
18
18
+
height: 100vh;
19
19
+
}
20
20
+
header {
21
21
+
background: #221828;
22
22
+
display: flex;
23
23
+
justify-content: space-between;
24
24
+
padding: 0 0.25rem;
25
25
+
color: #c9b;
26
26
+
display: flex;
27
27
+
gap: 0.5rem;
28
28
+
align-items: baseline;
29
29
+
}
30
30
+
header > * {
31
31
+
flex-basis: 33%;
32
32
+
}
33
33
+
header > .title {
34
34
+
text-align: center;
35
35
+
}
36
36
+
header > a.micro {
37
37
+
text-decoration: none;
38
38
+
font-size: 0.8rem;
39
39
+
text-align: right;
40
40
+
opacity: 0.5;
41
41
+
}
42
42
+
header > a.micro:hover {
43
43
+
opacity: 1;
44
44
+
}
45
45
+
main {
46
46
+
padding: 0.25rem 0.5rem;
47
47
+
background: #ccc;
48
48
+
flex-grow: 1;
49
49
+
}
50
50
+
p {
51
51
+
margin: 0.5rem 0;
52
52
+
}
53
53
+
</style>
54
54
+
55
55
+
<div class="wrap">
56
56
+
<header>
57
57
+
<div class="empty"></div>
58
58
+
<code class="title" style="font-family: monospace;"
59
59
+
>who-am-i</code>
60
60
+
<a href="https://microcosm.blue" target="_blank" class="micro"
61
61
+
><span style="color: #f396a9">m</span
62
62
+
><span style="color: #f49c5c">i</span
63
63
+
><span style="color: #c7b04c">c</span
64
64
+
><span style="color: #92be4c">r</span
65
65
+
><span style="color: #4ec688">o</span
66
66
+
><span style="color: #51c2b6">c</span
67
67
+
><span style="color: #54bed7">o</span
68
68
+
><span style="color: #8fb1f1">s</span
69
69
+
><span style="color: #ce9df1">m</span
70
70
+
></a>
71
71
+
</header>
72
72
+
73
73
+
<main>
74
74
+
<p>Share your identity with {{ parent_host }}?</p>
75
75
+
<div id="user-info">Loading…</div>
76
76
+
</main>
77
77
+
</div>
78
78
+
79
79
+
80
80
+
<script>
81
81
+
const infoEl = document.getElementById('user-info');
82
82
+
var DID = {{{json did}}};
83
83
+
let user_info = new URL('/user-info', window.location);
84
84
+
user_info.searchParams.set('fetch-key', {{{json fetch_key}}});
85
85
+
fetch(user_info).then(
86
86
+
info => {
87
87
+
infoEl.textContent = 'yay';
88
88
+
console.log(info);
89
89
+
},
90
90
+
err => {
91
91
+
infoEl.textContent = 'ohno';
92
92
+
console.error(err);
93
93
+
},
94
94
+
);
95
95
+
96
96
+
</script>