+243
-33
Cargo.lock
+243
-33
Cargo.lock
···
192
192
"nom",
193
193
"num-traits",
194
194
"rusticata-macros",
195
-
"thiserror 2.0.12",
195
+
"thiserror 2.0.16",
196
196
"time",
197
197
]
198
198
···
371
371
]
372
372
373
373
[[package]]
374
+
name = "atrium-crypto"
375
+
version = "0.1.2"
376
+
source = "registry+https://github.com/rust-lang/crates.io-index"
377
+
checksum = "73a3da430c71dd9006d61072c20771f264e5c498420a49c32305ceab8bd71955"
378
+
dependencies = [
379
+
"ecdsa",
380
+
"k256",
381
+
"multibase",
382
+
"p256",
383
+
"thiserror 1.0.69",
384
+
]
385
+
386
+
[[package]]
374
387
name = "atrium-identity"
375
388
version = "0.1.5"
376
389
source = "registry+https://github.com/rust-lang/crates.io-index"
···
628
641
"axum",
629
642
"handlebars",
630
643
"serde",
631
-
"thiserror 2.0.12",
644
+
"thiserror 2.0.16",
632
645
]
633
646
634
647
[[package]]
···
774
787
]
775
788
776
789
[[package]]
790
+
name = "bitcoin-io"
791
+
version = "0.1.3"
792
+
source = "registry+https://github.com/rust-lang/crates.io-index"
793
+
checksum = "0b47c4ab7a93edb0c7198c5535ed9b52b63095f4e9b45279c6736cec4b856baf"
794
+
795
+
[[package]]
796
+
name = "bitcoin_hashes"
797
+
version = "0.14.0"
798
+
source = "registry+https://github.com/rust-lang/crates.io-index"
799
+
checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16"
800
+
dependencies = [
801
+
"bitcoin-io",
802
+
"hex-conservative",
803
+
]
804
+
805
+
[[package]]
777
806
name = "bitflags"
778
807
version = "2.9.0"
779
808
source = "registry+https://github.com/rust-lang/crates.io-index"
···
907
936
]
908
937
909
938
[[package]]
939
+
name = "ciborium"
940
+
version = "0.2.2"
941
+
source = "registry+https://github.com/rust-lang/crates.io-index"
942
+
checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e"
943
+
dependencies = [
944
+
"ciborium-io",
945
+
"ciborium-ll",
946
+
"serde",
947
+
]
948
+
949
+
[[package]]
950
+
name = "ciborium-io"
951
+
version = "0.2.2"
952
+
source = "registry+https://github.com/rust-lang/crates.io-index"
953
+
checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757"
954
+
955
+
[[package]]
956
+
name = "ciborium-ll"
957
+
version = "0.2.2"
958
+
source = "registry+https://github.com/rust-lang/crates.io-index"
959
+
checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9"
960
+
dependencies = [
961
+
"ciborium-io",
962
+
"half",
963
+
]
964
+
965
+
[[package]]
910
966
name = "cid"
911
967
version = "0.11.1"
912
968
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1172
1228
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
1173
1229
1174
1230
[[package]]
1231
+
name = "crunchy"
1232
+
version = "0.2.4"
1233
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1234
+
checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
1235
+
1236
+
[[package]]
1175
1237
name = "crypto-bigint"
1176
1238
version = "0.5.5"
1177
1239
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1514
1576
"slog-bunyan",
1515
1577
"slog-json",
1516
1578
"slog-term",
1517
-
"thiserror 2.0.12",
1579
+
"thiserror 2.0.16",
1518
1580
"tokio",
1519
1581
"tokio-rustls 0.25.0",
1520
1582
"toml",
···
1684
1746
]
1685
1747
1686
1748
[[package]]
1749
+
name = "fallible-iterator"
1750
+
version = "0.3.0"
1751
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1752
+
checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
1753
+
1754
+
[[package]]
1755
+
name = "fallible-streaming-iterator"
1756
+
version = "0.1.9"
1757
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1758
+
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
1759
+
1760
+
[[package]]
1687
1761
name = "fastrand"
1688
1762
version = "2.3.0"
1689
1763
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1814
1888
"mixtrics",
1815
1889
"pin-project",
1816
1890
"serde",
1817
-
"thiserror 2.0.12",
1891
+
"thiserror 2.0.16",
1818
1892
"tokio",
1819
1893
"tracing",
1820
1894
]
···
1834
1908
"parking_lot",
1835
1909
"pin-project",
1836
1910
"serde",
1837
-
"thiserror 2.0.12",
1911
+
"thiserror 2.0.16",
1838
1912
"tokio",
1839
1913
"twox-hash",
1840
1914
]
···
1867
1941
"parking_lot",
1868
1942
"pin-project",
1869
1943
"serde",
1870
-
"thiserror 2.0.12",
1944
+
"thiserror 2.0.16",
1871
1945
"tokio",
1872
1946
"tracing",
1873
1947
]
···
1899
1973
"pin-project",
1900
1974
"rand 0.9.1",
1901
1975
"serde",
1902
-
"thiserror 2.0.12",
1976
+
"thiserror 2.0.16",
1903
1977
"tokio",
1904
1978
"tracing",
1905
1979
"twox-hash",
···
2121
2195
]
2122
2196
2123
2197
[[package]]
2198
+
name = "half"
2199
+
version = "2.6.0"
2200
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2201
+
checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9"
2202
+
dependencies = [
2203
+
"cfg-if",
2204
+
"crunchy",
2205
+
]
2206
+
2207
+
[[package]]
2124
2208
name = "handlebars"
2125
2209
version = "6.3.2"
2126
2210
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2133
2217
"pest_derive",
2134
2218
"serde",
2135
2219
"serde_json",
2136
-
"thiserror 2.0.12",
2220
+
"thiserror 2.0.16",
2137
2221
"walkdir",
2138
2222
]
2139
2223
···
2170
2254
]
2171
2255
2172
2256
[[package]]
2257
+
name = "hashlink"
2258
+
version = "0.10.0"
2259
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2260
+
checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1"
2261
+
dependencies = [
2262
+
"hashbrown 0.15.2",
2263
+
]
2264
+
2265
+
[[package]]
2173
2266
name = "headers"
2174
2267
version = "0.4.0"
2175
2268
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2223
2316
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
2224
2317
2225
2318
[[package]]
2319
+
name = "hex-conservative"
2320
+
version = "0.2.1"
2321
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2322
+
checksum = "5313b072ce3c597065a808dbf612c4c8e8590bdbf8b579508bf7a762c5eae6cd"
2323
+
dependencies = [
2324
+
"arrayvec",
2325
+
]
2326
+
2327
+
[[package]]
2226
2328
name = "hickory-proto"
2227
2329
version = "0.25.2"
2228
2330
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2240
2342
"once_cell",
2241
2343
"rand 0.9.1",
2242
2344
"ring",
2243
-
"thiserror 2.0.12",
2345
+
"thiserror 2.0.16",
2244
2346
"tinyvec",
2245
2347
"tokio",
2246
2348
"tracing",
···
2263
2365
"rand 0.9.1",
2264
2366
"resolv-conf",
2265
2367
"smallvec",
2266
-
"thiserror 2.0.12",
2368
+
"thiserror 2.0.16",
2267
2369
"tokio",
2268
2370
"tracing",
2269
2371
]
···
2757
2859
"metrics",
2758
2860
"serde",
2759
2861
"serde_json",
2760
-
"thiserror 2.0.12",
2862
+
"thiserror 2.0.16",
2761
2863
"tokio",
2762
2864
"tokio-tungstenite 0.26.2",
2763
2865
"url",
···
2860
2962
]
2861
2963
2862
2964
[[package]]
2965
+
name = "jwt-compact"
2966
+
version = "0.9.0-beta.1"
2967
+
source = "git+https://github.com/fatfingers23/jwt-compact.git#aed088b8ff5ad44ef2785c453f6a4b7916728b1c"
2968
+
dependencies = [
2969
+
"anyhow",
2970
+
"base64ct",
2971
+
"chrono",
2972
+
"ciborium",
2973
+
"hmac",
2974
+
"lazy_static",
2975
+
"rand_core 0.6.4",
2976
+
"secp256k1",
2977
+
"serde",
2978
+
"serde_json",
2979
+
"sha2",
2980
+
"smallvec",
2981
+
"subtle",
2982
+
"zeroize",
2983
+
]
2984
+
2985
+
[[package]]
2986
+
name = "k256"
2987
+
version = "0.13.4"
2988
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2989
+
checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b"
2990
+
dependencies = [
2991
+
"cfg-if",
2992
+
"ecdsa",
2993
+
"elliptic-curve",
2994
+
"sha2",
2995
+
]
2996
+
2997
+
[[package]]
2863
2998
name = "langtag"
2864
2999
version = "0.3.4"
2865
3000
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2952
3087
]
2953
3088
2954
3089
[[package]]
3090
+
name = "libsqlite3-sys"
3091
+
version = "0.35.0"
3092
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3093
+
checksum = "133c182a6a2c87864fe97778797e46c7e999672690dc9fa3ee8e241aa4a9c13f"
3094
+
dependencies = [
3095
+
"pkg-config",
3096
+
"vcpkg",
3097
+
]
3098
+
3099
+
[[package]]
2955
3100
name = "libz-sys"
2956
3101
version = "1.1.22"
2957
3102
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2969
3114
"anyhow",
2970
3115
"fluent-uri",
2971
3116
"nom",
2972
-
"thiserror 2.0.12",
3117
+
"thiserror 2.0.16",
2973
3118
"tinyjson",
2974
3119
]
2975
3120
···
3235
3380
"metrics",
3236
3381
"metrics-util 0.20.0",
3237
3382
"quanta",
3238
-
"thiserror 2.0.12",
3383
+
"thiserror 2.0.16",
3239
3384
"tokio",
3240
3385
"tracing",
3241
3386
]
···
3784
3929
checksum = "1db05f56d34358a8b1066f67cbb203ee3e7ed2ba674a6263a1d5ec6db2204323"
3785
3930
dependencies = [
3786
3931
"memchr",
3787
-
"thiserror 2.0.12",
3932
+
"thiserror 2.0.16",
3788
3933
"ucd-trie",
3789
3934
]
3790
3935
···
3881
4026
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
3882
4027
3883
4028
[[package]]
4029
+
name = "pocket"
4030
+
version = "0.1.0"
4031
+
dependencies = [
4032
+
"atrium-crypto",
4033
+
"clap",
4034
+
"jwt-compact",
4035
+
"log",
4036
+
"poem",
4037
+
"poem-openapi",
4038
+
"reqwest",
4039
+
"rusqlite",
4040
+
"serde",
4041
+
"serde_json",
4042
+
"thiserror 2.0.16",
4043
+
"tokio",
4044
+
"tracing-subscriber",
4045
+
]
4046
+
4047
+
[[package]]
3884
4048
name = "poem"
3885
4049
version = "3.1.12"
3886
4050
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3918
4082
"smallvec",
3919
4083
"sync_wrapper",
3920
4084
"tempfile",
3921
-
"thiserror 2.0.12",
4085
+
"thiserror 2.0.16",
3922
4086
"tokio",
3923
4087
"tokio-rustls 0.26.2",
3924
4088
"tokio-stream",
···
3962
4126
"serde_json",
3963
4127
"serde_urlencoded",
3964
4128
"serde_yaml",
3965
-
"thiserror 2.0.12",
4129
+
"thiserror 2.0.16",
3966
4130
"tokio",
3967
4131
]
3968
4132
···
3981
4145
"quote",
3982
4146
"regex",
3983
4147
"syn 2.0.103",
3984
-
"thiserror 2.0.12",
4148
+
"thiserror 2.0.16",
3985
4149
]
3986
4150
3987
4151
[[package]]
···
4130
4294
"rustc-hash 2.1.1",
4131
4295
"rustls 0.23.31",
4132
4296
"socket2 0.5.9",
4133
-
"thiserror 2.0.12",
4297
+
"thiserror 2.0.16",
4134
4298
"tokio",
4135
4299
"tracing",
4136
4300
"web-time",
···
4151
4315
"rustls 0.23.31",
4152
4316
"rustls-pki-types",
4153
4317
"slab",
4154
-
"thiserror 2.0.12",
4318
+
"thiserror 2.0.16",
4155
4319
"tinyvec",
4156
4320
"tracing",
4157
4321
"web-time",
···
4505
4669
]
4506
4670
4507
4671
[[package]]
4672
+
name = "rusqlite"
4673
+
version = "0.37.0"
4674
+
source = "registry+https://github.com/rust-lang/crates.io-index"
4675
+
checksum = "165ca6e57b20e1351573e3729b958bc62f0e48025386970b6e4d29e7a7e71f3f"
4676
+
dependencies = [
4677
+
"bitflags",
4678
+
"fallible-iterator",
4679
+
"fallible-streaming-iterator",
4680
+
"hashlink",
4681
+
"libsqlite3-sys",
4682
+
"smallvec",
4683
+
]
4684
+
4685
+
[[package]]
4508
4686
name = "rustc-demangle"
4509
4687
version = "0.1.24"
4510
4688
source = "registry+https://github.com/rust-lang/crates.io-index"
···
4733
4911
]
4734
4912
4735
4913
[[package]]
4914
+
name = "secp256k1"
4915
+
version = "0.30.0"
4916
+
source = "registry+https://github.com/rust-lang/crates.io-index"
4917
+
checksum = "b50c5943d326858130af85e049f2661ba3c78b26589b8ab98e65e80ae44a1252"
4918
+
dependencies = [
4919
+
"bitcoin_hashes",
4920
+
"rand 0.8.5",
4921
+
"secp256k1-sys",
4922
+
]
4923
+
4924
+
[[package]]
4925
+
name = "secp256k1-sys"
4926
+
version = "0.10.1"
4927
+
source = "registry+https://github.com/rust-lang/crates.io-index"
4928
+
checksum = "d4387882333d3aa8cb20530a17c69a3752e97837832f34f6dccc760e715001d9"
4929
+
dependencies = [
4930
+
"cc",
4931
+
]
4932
+
4933
+
[[package]]
4736
4934
name = "security-framework"
4737
4935
version = "2.11.1"
4738
4936
source = "registry+https://github.com/rust-lang/crates.io-index"
···
4865
5063
"percent-encoding",
4866
5064
"ryu",
4867
5065
"serde",
4868
-
"thiserror 2.0.12",
5066
+
"thiserror 2.0.16",
4869
5067
]
4870
5068
4871
5069
[[package]]
···
5008
5206
dependencies = [
5009
5207
"num-bigint",
5010
5208
"num-traits",
5011
-
"thiserror 2.0.12",
5209
+
"thiserror 2.0.16",
5012
5210
"time",
5013
5211
]
5014
5212
···
5050
5248
"rustls 0.23.31",
5051
5249
"serde",
5052
5250
"serde_json",
5053
-
"thiserror 2.0.12",
5251
+
"thiserror 2.0.16",
5054
5252
"time",
5055
5253
"tokio",
5056
5254
"tokio-util",
···
5161
5359
"serde",
5162
5360
"serde_json",
5163
5361
"serde_qs",
5164
-
"thiserror 2.0.12",
5362
+
"thiserror 2.0.16",
5165
5363
"tinyjson",
5166
5364
"tokio",
5167
5365
"tokio-tungstenite 0.27.0",
···
5327
5525
5328
5526
[[package]]
5329
5527
name = "thiserror"
5330
-
version = "2.0.12"
5528
+
version = "2.0.16"
5331
5529
source = "registry+https://github.com/rust-lang/crates.io-index"
5332
-
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
5530
+
checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0"
5333
5531
dependencies = [
5334
-
"thiserror-impl 2.0.12",
5532
+
"thiserror-impl 2.0.16",
5335
5533
]
5336
5534
5337
5535
[[package]]
···
5347
5545
5348
5546
[[package]]
5349
5547
name = "thiserror-impl"
5350
-
version = "2.0.12"
5548
+
version = "2.0.16"
5351
5549
source = "registry+https://github.com/rust-lang/crates.io-index"
5352
-
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
5550
+
checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960"
5353
5551
dependencies = [
5354
5552
"proc-macro2",
5355
5553
"quote",
···
5742
5940
"native-tls",
5743
5941
"rand 0.9.1",
5744
5942
"sha1",
5745
-
"thiserror 2.0.12",
5943
+
"thiserror 2.0.16",
5746
5944
"url",
5747
5945
"utf-8",
5748
5946
]
···
5760
5958
"log",
5761
5959
"rand 0.9.1",
5762
5960
"sha1",
5763
-
"thiserror 2.0.12",
5961
+
"thiserror 2.0.16",
5764
5962
"utf-8",
5765
5963
]
5766
5964
···
5813
6011
"serde_qs",
5814
6012
"sha2",
5815
6013
"tempfile",
5816
-
"thiserror 2.0.12",
6014
+
"thiserror 2.0.16",
5817
6015
"tikv-jemallocator",
5818
6016
"tokio",
5819
6017
"tokio-util",
···
6147
6345
"reqwest",
6148
6346
"serde",
6149
6347
"serde_json",
6150
-
"thiserror 2.0.12",
6348
+
"thiserror 2.0.16",
6151
6349
"tokio",
6152
6350
"tokio-util",
6153
6351
"url",
···
6540
6738
"nom",
6541
6739
"oid-registry",
6542
6740
"rusticata-macros",
6543
-
"thiserror 2.0.12",
6741
+
"thiserror 2.0.16",
6544
6742
"time",
6545
6743
]
6546
6744
···
6651
6849
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
6652
6850
dependencies = [
6653
6851
"serde",
6852
+
"zeroize_derive",
6853
+
]
6854
+
6855
+
[[package]]
6856
+
name = "zeroize_derive"
6857
+
version = "1.4.2"
6858
+
source = "registry+https://github.com/rust-lang/crates.io-index"
6859
+
checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
6860
+
dependencies = [
6861
+
"proc-macro2",
6862
+
"quote",
6863
+
"syn 2.0.103",
6654
6864
]
6655
6865
6656
6866
[[package]]
+19
pocket/Cargo.toml
+19
pocket/Cargo.toml
···
1
+
[package]
2
+
name = "pocket"
3
+
version = "0.1.0"
4
+
edition = "2024"
5
+
6
+
[dependencies]
7
+
atrium-crypto = "0.1.2"
8
+
clap = { version = "4.5.41", features = ["derive"] }
9
+
jwt-compact = { git = "https://github.com/fatfingers23/jwt-compact.git", features = ["es256k"] }
10
+
log = "0.4.27"
11
+
poem = { version = "3.1.12", features = ["acme", "static-files"] }
12
+
poem-openapi = { version = "5.1.16", features = ["scalar"] }
13
+
reqwest = { version = "0.12.22", features = ["json"] }
14
+
rusqlite = "0.37.0"
15
+
serde = { version = "1.0.219", features = ["derive"] }
16
+
serde_json = { version = "1.0.141" }
17
+
thiserror = "2.0.16"
18
+
tokio = { version = "1.47.0", features = ["full"] }
19
+
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
+5
pocket/src/lib.rs
+5
pocket/src/lib.rs
+9
pocket/src/main.rs
+9
pocket/src/main.rs
+208
pocket/src/server.rs
+208
pocket/src/server.rs
···
1
+
use poem::{
2
+
endpoint::make_sync,
3
+
Endpoint,
4
+
Route,
5
+
Server,
6
+
EndpointExt,
7
+
http::{Method, HeaderMap},
8
+
middleware::{CatchPanic, Cors, Tracing},
9
+
listener::TcpListener,
10
+
};
11
+
use poem_openapi::{
12
+
ContactObject,
13
+
ExternalDocumentObject,
14
+
OpenApi,
15
+
OpenApiService,
16
+
Tags,
17
+
Object,
18
+
ApiResponse,
19
+
types::Example,
20
+
auth::Bearer,
21
+
payload::Json,
22
+
SecurityScheme,
23
+
};
24
+
use crate::verify;
25
+
use serde::Serialize;
26
+
use serde_json::{Value, json};
27
+
28
+
29
+
#[derive(Debug, SecurityScheme)]
30
+
#[oai(ty = "bearer")]
31
+
struct BlahAuth(Bearer);
32
+
33
+
34
+
#[derive(Tags)]
35
+
enum ApiTags {
36
+
/// Bluesky-compatible APIs.
37
+
#[oai(rename = "app.bsky.* queries")]
38
+
AppBsky,
39
+
}
40
+
41
+
#[derive(Object)]
42
+
#[oai(example = true)]
43
+
struct XrpcErrorResponseObject {
44
+
/// Should correspond an error `name` in the lexicon errors array
45
+
error: String,
46
+
/// Human-readable description and possibly additonal context
47
+
message: String,
48
+
}
49
+
impl Example for XrpcErrorResponseObject {
50
+
fn example() -> Self {
51
+
Self {
52
+
error: "PreferencesNotFound".to_string(),
53
+
message: "No preferences were found for this user".to_string(),
54
+
}
55
+
}
56
+
}
57
+
type XrpcError = Json<XrpcErrorResponseObject>;
58
+
fn xrpc_error(error: impl AsRef<str>, message: impl AsRef<str>) -> XrpcError {
59
+
Json(XrpcErrorResponseObject {
60
+
error: error.as_ref().to_string(),
61
+
message: message.as_ref().to_string(),
62
+
})
63
+
}
64
+
65
+
#[derive(Object)]
66
+
#[oai(example = true)]
67
+
struct GetBskyPrefsResponseObject {
68
+
/// at-uri for this record
69
+
preferences: Value,
70
+
}
71
+
impl Example for GetBskyPrefsResponseObject {
72
+
fn example() -> Self {
73
+
Self {
74
+
preferences: json!({
75
+
"hello": "world",
76
+
}),
77
+
}
78
+
}
79
+
}
80
+
81
+
#[derive(ApiResponse)]
82
+
enum GetBskyPrefsResponse {
83
+
/// Record found
84
+
#[oai(status = 200)]
85
+
Ok(Json<GetBskyPrefsResponseObject>),
86
+
/// Bad request or no preferences to return
87
+
#[oai(status = 400)]
88
+
BadRequest(XrpcError),
89
+
// /// Server errors
90
+
// #[oai(status = 500)]
91
+
// ServerError(XrpcError),
92
+
}
93
+
94
+
struct Xrpc {
95
+
domain: String,
96
+
}
97
+
98
+
#[OpenApi]
99
+
impl Xrpc {
100
+
/// app.bsky.actor.getPreferences
101
+
///
102
+
/// get stored bluesky prefs
103
+
#[oai(
104
+
path = "/app.bsky.actor.getPreferences",
105
+
method = "get",
106
+
tag = "ApiTags::AppBsky"
107
+
)]
108
+
async fn app_bsky_get_prefs(
109
+
&self,
110
+
BlahAuth(auth): BlahAuth,
111
+
m: &HeaderMap,
112
+
) -> GetBskyPrefsResponse {
113
+
log::warn!("hm: {m:?}");
114
+
match verify(
115
+
&format!("did:web:{}#bsky_appview", self.domain),
116
+
"app.bsky.actor.getPreferences",
117
+
&auth.token,
118
+
).await {
119
+
Ok(did) => log::info!("wooo! {did}"),
120
+
Err(err) => return GetBskyPrefsResponse::BadRequest(xrpc_error("booo", err)),
121
+
};
122
+
log::warn!("got bearer: {:?}", auth.token);
123
+
GetBskyPrefsResponse::Ok(Json(GetBskyPrefsResponseObject::example()))
124
+
}
125
+
126
+
/// app.bsky.actor.putPreferences
127
+
///
128
+
/// store bluesky prefs
129
+
#[oai(
130
+
path = "/app.bsky.actor.putPreferences",
131
+
method = "post",
132
+
tag = "ApiTags::AppBsky"
133
+
)]
134
+
async fn app_bsky_put_prefs(
135
+
&self,
136
+
Json(prefs): Json<Value>,
137
+
) -> () {
138
+
log::warn!("received prefs: {prefs:?}");
139
+
()
140
+
}
141
+
}
142
+
143
+
#[derive(Debug, Clone, Serialize)]
144
+
#[serde(rename_all = "camelCase")]
145
+
struct AppViewService {
146
+
id: String,
147
+
r#type: String,
148
+
service_endpoint: String,
149
+
}
150
+
#[derive(Debug, Clone, Serialize)]
151
+
struct AppViewDoc {
152
+
id: String,
153
+
service: [AppViewService; 1],
154
+
}
155
+
/// Serve a did document for did:web for this to be an xrpc appview
156
+
fn get_did_doc(domain: &str) -> impl Endpoint + use<> {
157
+
let doc = poem::web::Json(AppViewDoc {
158
+
id: format!("did:web:{domain}"),
159
+
service: [AppViewService {
160
+
id: "#bsky_appview".to_string(),
161
+
r#type: "PocketBlueskyPreferences".to_string(),
162
+
service_endpoint: format!("https://{domain}"),
163
+
}],
164
+
});
165
+
make_sync(move |_| doc.clone())
166
+
}
167
+
168
+
pub async fn serve(
169
+
domain: &str,
170
+
) -> () {
171
+
let api_service = OpenApiService::new(
172
+
Xrpc { domain: domain.to_string() },
173
+
"Pocket",
174
+
env!("CARGO_PKG_VERSION"),
175
+
)
176
+
.server(domain)
177
+
.url_prefix("/xrpc")
178
+
.contact(
179
+
ContactObject::new()
180
+
.name("@microcosm.blue")
181
+
.url("https://bsky.app/profile/microcosm.blue"),
182
+
)
183
+
// .description(include_str!("../api-description.md"))
184
+
.external_document(ExternalDocumentObject::new(
185
+
"https://microcosm.blue/pocket",
186
+
));
187
+
188
+
let app = Route::new()
189
+
.at("/.well-known/did.json", get_did_doc(&domain))
190
+
.nest("/xrpc/", api_service)
191
+
// .at("/", StaticFileEndpoint::new("./static/index.html"))
192
+
// .nest("/openapi", api_service.spec_endpoint())
193
+
.with(
194
+
Cors::new()
195
+
.allow_method(Method::GET)
196
+
.allow_method(Method::POST)
197
+
)
198
+
.with(CatchPanic::new())
199
+
.with(Tracing);
200
+
201
+
let listener = TcpListener::bind("127.0.0.1:3000");
202
+
Server::new(listener)
203
+
.name("pocket")
204
+
.run(app)
205
+
.await
206
+
.unwrap();
207
+
208
+
}
+72
pocket/src/token.rs
+72
pocket/src/token.rs
···
1
+
use jwt_compact::{Claims, UntrustedToken};
2
+
use atrium_crypto::did::parse_multikey;
3
+
use atrium_crypto::verify::Verifier;
4
+
use std::collections::HashMap;
5
+
use serde::Deserialize;
6
+
7
+
#[derive(Debug, Deserialize)]
8
+
struct MiniDoc {
9
+
signing_key: String,
10
+
}
11
+
12
+
pub async fn verify(
13
+
expected_aud: &str,
14
+
expected_lxm: &str,
15
+
token: &str,
16
+
) -> Result<String, &'static str> {
17
+
let untrusted = UntrustedToken::new(token).unwrap();
18
+
19
+
let claims: Claims<HashMap<String, String>> = untrusted.deserialize_claims_unchecked().unwrap();
20
+
21
+
let Some(did) = claims.custom.get("iss") else {
22
+
return Err("jwt must include the user's did in `iss`");
23
+
};
24
+
25
+
if !did.starts_with("did:") {
26
+
return Err("iss should be a did");
27
+
}
28
+
if did.contains("#") {
29
+
return Err("iss should be a user did without a service identifier");
30
+
}
31
+
32
+
println!("Claims: {claims:#?}");
33
+
println!("did: {did:#?}");
34
+
35
+
let endpoint = "https://slingshot.microcosm.blue/xrpc/com.bad-example.identity.resolveMiniDoc";
36
+
let doc: MiniDoc = reqwest::get(format!("{endpoint}?identifier={did}"))
37
+
.await
38
+
.unwrap()
39
+
.error_for_status()
40
+
.unwrap()
41
+
.json()
42
+
.await
43
+
.unwrap();
44
+
45
+
log::info!("got minidoc response: {doc:?}");
46
+
47
+
let (alg, public_key) = parse_multikey(&doc.signing_key).unwrap();
48
+
log::info!("parsed key: {public_key:?}");
49
+
50
+
Verifier::default().verify(
51
+
alg,
52
+
&public_key,
53
+
&untrusted.signed_data,
54
+
untrusted.signature_bytes(),
55
+
).unwrap();
56
+
// if this passes, then our claims were trustworthy after all(??)
57
+
58
+
let Some(aud) = claims.custom.get("aud") else {
59
+
return Err("missing aud");
60
+
};
61
+
if aud != expected_aud {
62
+
return Err("wrong aud");
63
+
}
64
+
let Some(lxm) = claims.custom.get("lxm") else {
65
+
return Err("missing lxm");
66
+
};
67
+
if lxm != expected_lxm {
68
+
return Err("wrong lxm");
69
+
}
70
+
71
+
Ok(did.to_string())
72
+
}