tangled
alpha
login
or
join now
hotsocket.fyi
/
microcosm-rs
forked from
microcosm.blue/microcosm-rs
0
fork
atom
Constellation, Spacedust, Slingshot, UFOs: atproto crates and services for microcosm
0
fork
atom
overview
issues
pulls
pipelines
dev and prod with all the oauth joy
bad-example.com
8 months ago
c99e3c33
8304c4de
+254
-24
5 changed files
expand all
collapse all
unified
split
Cargo.lock
who-am-i
Cargo.toml
src
main.rs
oauth.rs
server.rs
+113
Cargo.lock
···
1162
checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb"
1163
dependencies = [
1164
"const-oid",
0
1165
"zeroize",
1166
]
1167
···
1344
"elliptic-curve",
1345
"rfc6979",
1346
"signature",
0
1347
]
1348
1349
[[package]]
···
1364
"ff",
1365
"generic-array",
1366
"group",
0
0
1367
"rand_core 0.6.4",
1368
"sec1",
1369
"subtle",
···
2442
"jose-b64",
2443
"jose-jwa",
2444
"p256",
0
0
2445
"serde",
2446
"zeroize",
2447
]
···
2485
version = "1.5.0"
2486
source = "registry+https://github.com/rust-lang/crates.io-index"
2487
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
0
0
0
2488
2489
[[package]]
2490
name = "lazycell"
···
2982
]
2983
2984
[[package]]
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
2985
name = "num-conv"
2986
version = "0.1.0"
2987
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3007
]
3008
3009
[[package]]
0
0
0
0
0
0
0
0
0
0
0
3010
name = "num-modular"
3011
version = "0.6.1"
3012
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3028
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
3029
dependencies = [
3030
"autocfg",
0
3031
]
3032
3033
[[package]]
···
3142
]
3143
3144
[[package]]
0
0
0
0
0
0
0
0
0
0
3145
name = "parking"
3146
version = "2.2.1"
3147
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3202
dependencies = [
3203
"base64 0.22.1",
3204
"serde",
0
0
0
0
0
0
0
0
0
3205
]
3206
3207
[[package]]
···
3267
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
3268
3269
[[package]]
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
3270
name = "pkg-config"
3271
version = "0.3.32"
3272
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3666
]
3667
3668
[[package]]
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
3669
name = "rustc-demangle"
3670
version = "0.1.24"
3671
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3873
"base16ct",
3874
"der",
3875
"generic-array",
0
3876
"subtle",
3877
"zeroize",
3878
]
···
4266
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
4267
dependencies = [
4268
"lock_api",
0
0
0
0
0
0
0
0
0
0
4269
]
4270
4271
[[package]]
···
5143
"clap",
5144
"ctrlc",
5145
"dashmap",
0
5146
"handlebars",
5147
"hickory-resolver",
0
5148
"jsonwebtoken",
5149
"metrics",
5150
"metrics-exporter-prometheus 0.17.2",
0
0
5151
"rand 0.9.1",
5152
"reqwest",
5153
"serde",
···
1162
checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb"
1163
dependencies = [
1164
"const-oid",
1165
+
"pem-rfc7468",
1166
"zeroize",
1167
]
1168
···
1345
"elliptic-curve",
1346
"rfc6979",
1347
"signature",
1348
+
"spki",
1349
]
1350
1351
[[package]]
···
1366
"ff",
1367
"generic-array",
1368
"group",
1369
+
"pem-rfc7468",
1370
+
"pkcs8",
1371
"rand_core 0.6.4",
1372
"sec1",
1373
"subtle",
···
2446
"jose-b64",
2447
"jose-jwa",
2448
"p256",
2449
+
"p384",
2450
+
"rsa",
2451
"serde",
2452
"zeroize",
2453
]
···
2491
version = "1.5.0"
2492
source = "registry+https://github.com/rust-lang/crates.io-index"
2493
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
2494
+
dependencies = [
2495
+
"spin",
2496
+
]
2497
2498
[[package]]
2499
name = "lazycell"
···
2991
]
2992
2993
[[package]]
2994
+
name = "num-bigint-dig"
2995
+
version = "0.8.4"
2996
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2997
+
checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151"
2998
+
dependencies = [
2999
+
"byteorder",
3000
+
"lazy_static",
3001
+
"libm",
3002
+
"num-integer",
3003
+
"num-iter",
3004
+
"num-traits",
3005
+
"rand 0.8.5",
3006
+
"smallvec",
3007
+
"zeroize",
3008
+
]
3009
+
3010
+
[[package]]
3011
name = "num-conv"
3012
version = "0.1.0"
3013
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3033
]
3034
3035
[[package]]
3036
+
name = "num-iter"
3037
+
version = "0.1.45"
3038
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3039
+
checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf"
3040
+
dependencies = [
3041
+
"autocfg",
3042
+
"num-integer",
3043
+
"num-traits",
3044
+
]
3045
+
3046
+
[[package]]
3047
name = "num-modular"
3048
version = "0.6.1"
3049
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3065
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
3066
dependencies = [
3067
"autocfg",
3068
+
"libm",
3069
]
3070
3071
[[package]]
···
3180
]
3181
3182
[[package]]
3183
+
name = "p384"
3184
+
version = "0.13.1"
3185
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3186
+
checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6"
3187
+
dependencies = [
3188
+
"elliptic-curve",
3189
+
"primeorder",
3190
+
]
3191
+
3192
+
[[package]]
3193
name = "parking"
3194
version = "2.2.1"
3195
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3250
dependencies = [
3251
"base64 0.22.1",
3252
"serde",
3253
+
]
3254
+
3255
+
[[package]]
3256
+
name = "pem-rfc7468"
3257
+
version = "0.7.0"
3258
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3259
+
checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412"
3260
+
dependencies = [
3261
+
"base64ct",
3262
]
3263
3264
[[package]]
···
3324
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
3325
3326
[[package]]
3327
+
name = "pkcs1"
3328
+
version = "0.7.5"
3329
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3330
+
checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f"
3331
+
dependencies = [
3332
+
"der",
3333
+
"pkcs8",
3334
+
"spki",
3335
+
]
3336
+
3337
+
[[package]]
3338
+
name = "pkcs8"
3339
+
version = "0.10.2"
3340
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3341
+
checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
3342
+
dependencies = [
3343
+
"der",
3344
+
"spki",
3345
+
]
3346
+
3347
+
[[package]]
3348
name = "pkg-config"
3349
version = "0.3.32"
3350
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3744
]
3745
3746
[[package]]
3747
+
name = "rsa"
3748
+
version = "0.9.8"
3749
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3750
+
checksum = "78928ac1ed176a5ca1d17e578a1825f3d81ca54cf41053a592584b020cfd691b"
3751
+
dependencies = [
3752
+
"const-oid",
3753
+
"digest",
3754
+
"num-bigint-dig",
3755
+
"num-integer",
3756
+
"num-traits",
3757
+
"pkcs1",
3758
+
"pkcs8",
3759
+
"rand_core 0.6.4",
3760
+
"signature",
3761
+
"spki",
3762
+
"subtle",
3763
+
"zeroize",
3764
+
]
3765
+
3766
+
[[package]]
3767
name = "rustc-demangle"
3768
version = "0.1.24"
3769
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3971
"base16ct",
3972
"der",
3973
"generic-array",
3974
+
"pkcs8",
3975
"subtle",
3976
"zeroize",
3977
]
···
4365
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
4366
dependencies = [
4367
"lock_api",
4368
+
]
4369
+
4370
+
[[package]]
4371
+
name = "spki"
4372
+
version = "0.7.3"
4373
+
source = "registry+https://github.com/rust-lang/crates.io-index"
4374
+
checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
4375
+
dependencies = [
4376
+
"base64ct",
4377
+
"der",
4378
]
4379
4380
[[package]]
···
5252
"clap",
5253
"ctrlc",
5254
"dashmap",
5255
+
"elliptic-curve",
5256
"handlebars",
5257
"hickory-resolver",
5258
+
"jose-jwk",
5259
"jsonwebtoken",
5260
"metrics",
5261
"metrics-exporter-prometheus 0.17.2",
5262
+
"p256",
5263
+
"pkcs8",
5264
"rand 0.9.1",
5265
"reqwest",
5266
"serde",
+4
who-am-i/Cargo.toml
···
14
clap = { version = "4.5.40", features = ["derive", "env"] }
15
ctrlc = "3.4.7"
16
dashmap = "6.1.0"
0
17
handlebars = { version = "6.3.2", features = ["dir_source"] }
18
hickory-resolver = "0.25.2"
0
19
jsonwebtoken = "9.3.1"
20
metrics = "0.24.2"
0
0
21
rand = "0.9.1"
22
reqwest = { version = "0.12.22", features = ["native-tls-vendored"] }
23
serde = { version = "1.0.219", features = ["derive"] }
···
14
clap = { version = "4.5.40", features = ["derive", "env"] }
15
ctrlc = "3.4.7"
16
dashmap = "6.1.0"
17
+
elliptic-curve = "0.13.8"
18
handlebars = { version = "6.3.2", features = ["dir_source"] }
19
hickory-resolver = "0.25.2"
20
+
jose-jwk = "0.1.2"
21
jsonwebtoken = "9.3.1"
22
metrics = "0.24.2"
23
+
p256 = "0.13.2"
24
+
pkcs8 = "0.10.2"
25
rand = "0.9.1"
26
reqwest = { version = "0.12.22", features = ["native-tls-vendored"] }
27
serde = { version = "1.0.219", features = ["derive"] }
+38
-1
who-am-i/src/main.rs
···
15
/// eg: `cat /dev/urandom | head -c 64 | base64`
16
#[arg(long, env)]
17
app_secret: String,
0
0
0
0
0
0
0
0
18
/// path to jwt private key (PEM pk8 format)
19
///
20
/// generate with:
···
34
/// wrap the jwk in an array, then in an object under "keys":
35
///
36
/// { "keys": [<JWK obj>] }
0
0
37
#[arg(long)]
38
jwks: PathBuf,
0
0
0
0
0
0
0
0
39
/// Enable dev mode
40
///
41
-
/// enables automatic template reloading
42
#[arg(long, action)]
43
dev: bool,
44
/// Hosts who are allowed to one-click auth
···
57
58
let args = Args::parse();
59
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
60
if args.allowed_hosts.is_empty() {
61
panic!("at least one --allowed-host host must be set");
62
}
···
75
serve(
76
shutdown,
77
args.app_secret,
0
78
tokens,
0
0
79
args.allowed_hosts,
80
args.dev,
81
)
···
15
/// eg: `cat /dev/urandom | head -c 64 | base64`
16
#[arg(long, env)]
17
app_secret: String,
18
+
/// path to at-oauth private key (PEM pk8 format)
19
+
///
20
+
/// generate with:
21
+
///
22
+
/// openssl ecparam -genkey -noout -name prime256v1 \
23
+
/// | openssl pkcs8 -topk8 -nocrypt -out <PATH-TO-PRIV-KEY>.pem
24
+
#[arg(long, env)]
25
+
oauth_private_key: Option<PathBuf>,
26
/// path to jwt private key (PEM pk8 format)
27
///
28
/// generate with:
···
42
/// wrap the jwk in an array, then in an object under "keys":
43
///
44
/// { "keys": [<JWK obj>] }
45
+
///
46
+
/// TODO: remove this, serve automatically
47
#[arg(long)]
48
jwks: PathBuf,
49
+
/// this server's client-reachable base url, for oauth redirect + jwt check
50
+
///
51
+
/// required unless running in localhost mode with --dev
52
+
#[arg(long, env)]
53
+
base_url: Option<String>,
54
+
/// host:port to bind to on startup
55
+
#[arg(long, env, default_value = "127.0.0.1:9997")]
56
+
bind: String,
57
/// Enable dev mode
58
///
59
+
/// enables automatic template reloading, uses localhost oauth config, etc
60
#[arg(long, action)]
61
dev: bool,
62
/// Hosts who are allowed to one-click auth
···
75
76
let args = Args::parse();
77
78
+
// let bind = args.bind.to_socket_addrs().expect("--bind must be ToSocketAddrs");
79
+
80
+
let base = args.base_url.unwrap_or_else(|| {
81
+
if args.dev {
82
+
format!("http://{}", args.bind)
83
+
} else {
84
+
panic!("not in --dev mode so --base-url is required")
85
+
}
86
+
});
87
+
88
+
if !args.dev && args.oauth_private_key.is_none() {
89
+
panic!("--at-oauth-key is required except in --dev");
90
+
} else if args.dev && args.oauth_private_key.is_some() {
91
+
eprintln!("warn: --at-oauth-key is ignored in dev (localhost config)");
92
+
}
93
+
94
if args.allowed_hosts.is_empty() {
95
panic!("at least one --allowed-host host must be set");
96
}
···
109
serve(
110
shutdown,
111
args.app_secret,
112
+
args.oauth_private_key,
113
tokens,
114
+
base,
115
+
args.bind,
116
args.allowed_hosts,
117
args.dev,
118
)
+77
-21
who-am-i/src/oauth.rs
···
0
0
0
0
0
0
0
1
use atrium_api::{agent::SessionManager, types::string::Did};
2
use atrium_common::resolver::Resolver;
3
use atrium_identity::{
···
5
handle::{AtprotoHandleResolver, AtprotoHandleResolverConfig, DnsTxtResolver},
6
};
7
use atrium_oauth::{
8
-
AtprotoLocalhostClientMetadata, AuthorizeOptions, CallbackParams, DefaultHttpClient,
9
-
KnownScope, OAuthClient, OAuthClientConfig, OAuthResolverConfig, Scope,
0
10
store::{session::MemorySessionStore, state::MemoryStateStore},
11
};
0
12
use hickory_resolver::{ResolveError, TokioResolver};
0
0
13
use serde::Deserialize;
14
use std::sync::Arc;
15
use thiserror::Error;
···
83
}
84
85
impl OAuth {
86
-
pub fn new() -> Result<Self, AuthSetupError> {
87
let http_client = Arc::new(DefaultHttpClient::default());
88
let did_resolver = || {
89
CommonDidResolver::new(CommonDidResolverConfig {
···
93
};
94
let dns_txt_resolver =
95
HickoryDnsTxtResolver::new().map_err(AuthSetupError::HickoryResolverError)?;
96
-
let client_config = OAuthClientConfig {
97
-
client_metadata: AtprotoLocalhostClientMetadata {
98
-
redirect_uris: Some(vec![String::from("http://127.0.0.1:9997/authorized")]),
99
-
scopes: Some(READONLY_SCOPE.to_vec()),
100
-
},
101
-
keys: None,
102
-
resolver: OAuthResolverConfig {
103
-
did_resolver: did_resolver(),
104
-
handle_resolver: AtprotoHandleResolver::new(AtprotoHandleResolverConfig {
105
-
dns_txt_resolver,
106
-
http_client: Arc::clone(&http_client),
107
-
}),
108
-
authorization_server_metadata: Default::default(),
109
-
protected_resource_metadata: Default::default(),
110
-
},
111
-
state_store: MemoryStateStore::default(),
112
-
session_store: MemorySessionStore::default(),
113
};
114
115
-
let client = OAuthClient::new(client_config).map_err(AuthSetupError::AtriumClientError)?;
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
116
117
Ok(Self {
118
client: Arc::new(client),
119
did_resolver: Arc::new(did_resolver()),
120
})
0
0
0
0
0
0
0
0
121
}
122
123
pub async fn begin(&self, handle: &str) -> Result<String, atrium_oauth::Error> {
···
1
+
use jose_jwk::Class;
2
+
use jose_jwk::Jwk;
3
+
use jose_jwk::Key;
4
+
use jose_jwk::Parameters;
5
+
use std::fs;
6
+
use std::path::PathBuf;
7
+
// use p256::SecretKey;
8
use atrium_api::{agent::SessionManager, types::string::Did};
9
use atrium_common::resolver::Resolver;
10
use atrium_identity::{
···
12
handle::{AtprotoHandleResolver, AtprotoHandleResolverConfig, DnsTxtResolver},
13
};
14
use atrium_oauth::{
15
+
AtprotoClientMetadata, AtprotoLocalhostClientMetadata, AuthMethod, AuthorizeOptions,
16
+
CallbackParams, DefaultHttpClient, GrantType, KnownScope, OAuthClient, OAuthClientConfig,
17
+
OAuthClientMetadata, OAuthResolverConfig, Scope,
18
store::{session::MemorySessionStore, state::MemoryStateStore},
19
};
20
+
use elliptic_curve::SecretKey;
21
use hickory_resolver::{ResolveError, TokioResolver};
22
+
use jose_jwk::JwkSet;
23
+
use pkcs8::DecodePrivateKey;
24
use serde::Deserialize;
25
use std::sync::Arc;
26
use thiserror::Error;
···
94
}
95
96
impl OAuth {
97
+
pub fn new(oauth_private_key: Option<PathBuf>, base: String) -> Result<Self, AuthSetupError> {
98
let http_client = Arc::new(DefaultHttpClient::default());
99
let did_resolver = || {
100
CommonDidResolver::new(CommonDidResolverConfig {
···
104
};
105
let dns_txt_resolver =
106
HickoryDnsTxtResolver::new().map_err(AuthSetupError::HickoryResolverError)?;
107
+
108
+
let resolver = OAuthResolverConfig {
109
+
did_resolver: did_resolver(),
110
+
handle_resolver: AtprotoHandleResolver::new(AtprotoHandleResolverConfig {
111
+
dns_txt_resolver,
112
+
http_client: Arc::clone(&http_client),
113
+
}),
114
+
authorization_server_metadata: Default::default(),
115
+
protected_resource_metadata: Default::default(),
0
0
0
0
0
0
0
0
116
};
117
118
+
let state_store = MemoryStateStore::default();
119
+
let session_store = MemorySessionStore::default();
120
+
121
+
let client = if let Some(path) = oauth_private_key {
122
+
let key_contents: Vec<u8> = fs::read(path).unwrap();
123
+
let key_string = String::from_utf8(key_contents).unwrap();
124
+
let key = SecretKey::<p256::NistP256>::from_pkcs8_pem(&key_string)
125
+
.map(|secret_key| Jwk {
126
+
key: Key::from(&secret_key.into()),
127
+
prm: Parameters {
128
+
kid: Some("at-oauth-00".to_string()),
129
+
cls: Some(Class::Signing),
130
+
..Default::default()
131
+
},
132
+
})
133
+
.expect("to get private key");
134
+
OAuthClient::new(OAuthClientConfig {
135
+
client_metadata: AtprotoClientMetadata {
136
+
client_id: format!("{base}/client-metadata.json"),
137
+
client_uri: Some(base.clone()),
138
+
redirect_uris: vec![format!("{base}/authorized")],
139
+
token_endpoint_auth_method: AuthMethod::PrivateKeyJwt,
140
+
grant_types: vec![GrantType::AuthorizationCode, GrantType::RefreshToken],
141
+
scopes: READONLY_SCOPE.to_vec(),
142
+
jwks_uri: Some(format!("{base}/.well-known/at-jwks.json")),
143
+
token_endpoint_auth_signing_alg: Some(String::from("ES256")),
144
+
},
145
+
keys: Some(vec![key]),
146
+
resolver,
147
+
state_store,
148
+
session_store,
149
+
})
150
+
.map_err(AuthSetupError::AtriumClientError)?
151
+
} else {
152
+
OAuthClient::new(OAuthClientConfig {
153
+
client_metadata: AtprotoLocalhostClientMetadata {
154
+
redirect_uris: Some(vec![String::from("http://127.0.0.1:9997/authorized")]),
155
+
scopes: Some(READONLY_SCOPE.to_vec()),
156
+
},
157
+
keys: None,
158
+
resolver,
159
+
state_store,
160
+
session_store,
161
+
})
162
+
.map_err(AuthSetupError::AtriumClientError)?
163
+
};
164
165
Ok(Self {
166
client: Arc::new(client),
167
did_resolver: Arc::new(did_resolver()),
168
})
169
+
}
170
+
171
+
pub fn client_metadata(&self) -> OAuthClientMetadata {
172
+
self.client.client_metadata.clone()
173
+
}
174
+
175
+
pub fn jwks(&self) -> JwkSet {
176
+
self.client.jwks()
177
}
178
179
pub async fn begin(&self, handle: &str) -> Result<String, atrium_oauth::Error> {
+22
-2
who-am-i/src/server.rs
···
1
use atrium_api::types::string::Did;
0
2
use axum::{
3
Router,
4
extract::{FromRef, Json as ExtractJson, Query, State},
···
12
use axum_extra::extract::cookie::{Cookie, Key, SameSite, SignedCookieJar};
13
use axum_template::{RenderHtml, engine::Engine};
14
use handlebars::{Handlebars, handlebars_helper};
0
0
15
16
use serde::Deserialize;
17
use serde_json::{Value, json};
···
52
}
53
}
54
0
55
pub async fn serve(
56
shutdown: CancellationToken,
57
app_secret: String,
0
58
tokens: Tokens,
0
0
59
allowed_hosts: Vec<String>,
60
dev: bool,
61
) {
···
70
// clients have to pick up their identity-resolving tasks within this period
71
let task_pickup_expiration = Duration::from_secs(15);
72
73
-
let oauth = OAuth::new().unwrap();
74
75
let state = AppState {
76
engine: Engine::new(hbs),
···
88
.route("/style.css", get(css))
89
.route("/prompt", get(prompt))
90
.route("/user-info", post(user_info))
0
91
.route("/auth", get(start_oauth))
92
.route("/authorized", get(complete_oauth))
93
.route("/disconnect", post(disconnect))
0
94
.route("/.well-known/jwks.json", get(jwks))
95
.with_state(state);
96
97
-
let listener = TcpListener::bind("0.0.0.0:9997")
0
98
.await
99
.expect("listener binding to work");
100
···
297
Json(json!({ "handle": handle })).into_response()
298
}
299
}
0
0
0
0
0
0
0
0
0
0
300
}
301
302
#[derive(Debug, Deserialize)]
···
1
use atrium_api::types::string::Did;
2
+
use atrium_oauth::OAuthClientMetadata;
3
use axum::{
4
Router,
5
extract::{FromRef, Json as ExtractJson, Query, State},
···
13
use axum_extra::extract::cookie::{Cookie, Key, SameSite, SignedCookieJar};
14
use axum_template::{RenderHtml, engine::Engine};
15
use handlebars::{Handlebars, handlebars_helper};
16
+
use jose_jwk::JwkSet;
17
+
use std::path::PathBuf;
18
19
use serde::Deserialize;
20
use serde_json::{Value, json};
···
55
}
56
}
57
58
+
#[allow(clippy::too_many_arguments)]
59
pub async fn serve(
60
shutdown: CancellationToken,
61
app_secret: String,
62
+
oauth_private_key: Option<PathBuf>,
63
tokens: Tokens,
64
+
base: String,
65
+
bind: String,
66
allowed_hosts: Vec<String>,
67
dev: bool,
68
) {
···
77
// clients have to pick up their identity-resolving tasks within this period
78
let task_pickup_expiration = Duration::from_secs(15);
79
80
+
let oauth = OAuth::new(oauth_private_key, base).unwrap();
81
82
let state = AppState {
83
engine: Engine::new(hbs),
···
95
.route("/style.css", get(css))
96
.route("/prompt", get(prompt))
97
.route("/user-info", post(user_info))
98
+
.route("/client-metadata.json", get(client_metadata))
99
.route("/auth", get(start_oauth))
100
.route("/authorized", get(complete_oauth))
101
.route("/disconnect", post(disconnect))
102
+
.route("/.well-known/at-jwks.json", get(at_jwks)) // todo combine jwks eps (key id is enough?)
103
.route("/.well-known/jwks.json", get(jwks))
104
.with_state(state);
105
106
+
eprintln!("starting server at http://{bind}");
107
+
let listener = TcpListener::bind(bind)
108
.await
109
.expect("listener binding to work");
110
···
307
Json(json!({ "handle": handle })).into_response()
308
}
309
}
310
+
}
311
+
312
+
async fn client_metadata(
313
+
State(AppState { oauth, .. }): State<AppState>,
314
+
) -> Json<OAuthClientMetadata> {
315
+
Json(oauth.client_metadata())
316
+
}
317
+
318
+
async fn at_jwks(State(AppState { oauth, .. }): State<AppState>) -> Json<JwkSet> {
319
+
Json(oauth.jwks())
320
}
321
322
#[derive(Debug, Deserialize)]