A better Rust ATProto crate
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

oauth docs

Orual c7cafbcb af37cbe9

+1518 -601
+640 -523
Cargo.lock
··· 103 103 104 104 [[package]] 105 105 name = "anstream" 106 - version = "0.6.21" 106 + version = "1.0.0" 107 107 source = "registry+https://github.com/rust-lang/crates.io-index" 108 - checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" 108 + checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" 109 109 dependencies = [ 110 110 "anstyle", 111 111 "anstyle-parse", ··· 118 118 119 119 [[package]] 120 120 name = "anstyle" 121 - version = "1.0.13" 121 + version = "1.0.14" 122 122 source = "registry+https://github.com/rust-lang/crates.io-index" 123 - checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" 123 + checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" 124 124 125 125 [[package]] 126 126 name = "anstyle-parse" 127 - version = "0.2.7" 127 + version = "1.0.0" 128 128 source = "registry+https://github.com/rust-lang/crates.io-index" 129 - checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" 129 + checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" 130 130 dependencies = [ 131 131 "utf8parse", 132 132 ] ··· 153 153 154 154 [[package]] 155 155 name = "anyhow" 156 - version = "1.0.100" 156 + version = "1.0.102" 157 157 source = "registry+https://github.com/rust-lang/crates.io-index" 158 - checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" 158 + checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" 159 159 160 160 [[package]] 161 161 name = "arbitrary" ··· 171 171 dependencies = [ 172 172 "proc-macro2", 173 173 "quote", 174 - "syn 2.0.112", 174 + "syn", 175 175 ] 176 176 177 177 [[package]] ··· 203 203 204 204 [[package]] 205 205 name = "async-compression" 206 - version = "0.4.36" 206 + version = "0.4.41" 207 207 source = "registry+https://github.com/rust-lang/crates.io-index" 208 - checksum = "98ec5f6c2f8bc326c994cb9e241cc257ddaba9afa8555a43cffbb5dd86efaa37" 208 + checksum = "d0f9ee0f6e02ffd7ad5816e9464499fba7b3effd01123b515c41d1697c43dad1" 209 209 dependencies = [ 210 210 "compression-codecs", 211 211 "compression-core", 212 - "futures-core", 213 212 "pin-project-lite", 214 213 "tokio", 215 214 ] ··· 222 221 dependencies = [ 223 222 "proc-macro2", 224 223 "quote", 225 - "syn 2.0.112", 224 + "syn", 226 225 ] 227 226 228 227 [[package]] ··· 261 260 "num-traits", 262 261 "pastey", 263 262 "rayon", 264 - "thiserror 2.0.17", 263 + "thiserror 2.0.18", 265 264 "v_frame", 266 265 "y4m", 267 266 ] ··· 282 281 283 282 [[package]] 284 283 name = "avif-serialize" 285 - version = "0.8.6" 284 + version = "0.8.8" 286 285 source = "registry+https://github.com/rust-lang/crates.io-index" 287 - checksum = "47c8fbc0f831f4519fe8b810b6a7a91410ec83031b8233f730a0480029f6a23f" 286 + checksum = "375082f007bd67184fb9c0374614b29f9aaa604ec301635f72338bb65386a53d" 288 287 dependencies = [ 289 288 "arrayvec 0.7.6", 290 289 ] ··· 349 348 dependencies = [ 350 349 "proc-macro2", 351 350 "quote", 352 - "syn 2.0.112", 351 + "syn", 353 352 ] 354 353 355 354 [[package]] 356 355 name = "axum-test" 357 - version = "18.5.0" 356 + version = "18.7.0" 358 357 source = "registry+https://github.com/rust-lang/crates.io-index" 359 - checksum = "cf48df8b4be768081e11b7bb6d50e7dd96a3616b0b728f9e8d49bfbd8116f3c6" 358 + checksum = "0ce2a8627e8d8851f894696b39f2b67807d6375c177361d376173ace306a21e2" 360 359 dependencies = [ 361 360 "anyhow", 362 361 "axum", ··· 441 440 442 441 [[package]] 443 442 name = "base64ct" 444 - version = "1.8.1" 443 + version = "1.8.3" 445 444 source = "registry+https://github.com/rust-lang/crates.io-index" 446 - checksum = "0e050f626429857a27ddccb31e0aca21356bfa709c04041aefddac081a8f068a" 445 + checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" 447 446 448 447 [[package]] 449 448 name = "bit-set" ··· 468 467 469 468 [[package]] 470 469 name = "bitflags" 471 - version = "2.10.0" 470 + version = "2.11.0" 472 471 source = "registry+https://github.com/rust-lang/crates.io-index" 473 - checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" 472 + checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" 474 473 475 474 [[package]] 476 475 name = "bitstream-io" ··· 492 491 493 492 [[package]] 494 493 name = "bon" 495 - version = "3.8.1" 494 + version = "3.9.1" 496 495 source = "registry+https://github.com/rust-lang/crates.io-index" 497 - checksum = "ebeb9aaf9329dff6ceb65c689ca3db33dbf15f324909c60e4e5eef5701ce31b1" 496 + checksum = "f47dbe92550676ee653353c310dfb9cf6ba17ee70396e1f7cf0a2020ad49b2fe" 498 497 dependencies = [ 499 498 "bon-macros", 500 499 "rustversion", ··· 502 501 503 502 [[package]] 504 503 name = "bon-macros" 505 - version = "3.8.1" 504 + version = "3.9.1" 506 505 source = "registry+https://github.com/rust-lang/crates.io-index" 507 - checksum = "77e9d642a7e3a318e37c2c9427b5a6a48aa1ad55dcd986f3034ab2239045a645" 506 + checksum = "519bd3116aeeb42d5372c29d982d16d0170d3d4a5ed85fc7dd91642ffff3c67c" 508 507 dependencies = [ 509 508 "darling", 510 509 "ident_case", ··· 512 511 "proc-macro2", 513 512 "quote", 514 513 "rustversion", 515 - "syn 2.0.112", 514 + "syn", 516 515 ] 517 516 518 517 [[package]] ··· 523 522 524 523 [[package]] 525 524 name = "borsh" 526 - version = "1.6.0" 525 + version = "1.6.1" 527 526 source = "registry+https://github.com/rust-lang/crates.io-index" 528 - checksum = "d1da5ab77c1437701eeff7c88d968729e7766172279eab0676857b3d63af7a6f" 527 + checksum = "cfd1e3f8955a5d7de9fab72fc8373fade9fb8a703968cb200ae3dc6cf08e185a" 529 528 dependencies = [ 529 + "bytes", 530 530 "cfg_aliases", 531 531 ] 532 532 ··· 579 579 580 580 [[package]] 581 581 name = "bumpalo" 582 - version = "3.19.1" 582 + version = "3.20.2" 583 583 source = "registry+https://github.com/rust-lang/crates.io-index" 584 - checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" 584 + checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" 585 585 586 586 [[package]] 587 587 name = "bytemuck" 588 - version = "1.24.0" 588 + version = "1.25.0" 589 589 source = "registry+https://github.com/rust-lang/crates.io-index" 590 - checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" 590 + checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" 591 591 592 592 [[package]] 593 593 name = "byteorder" ··· 603 603 604 604 [[package]] 605 605 name = "bytes" 606 - version = "1.11.0" 606 + version = "1.11.1" 607 607 source = "registry+https://github.com/rust-lang/crates.io-index" 608 - checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" 608 + checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" 609 609 dependencies = [ 610 610 "serde", 611 611 ] ··· 633 633 634 634 [[package]] 635 635 name = "cc" 636 - version = "1.2.51" 636 + version = "1.2.57" 637 637 source = "registry+https://github.com/rust-lang/crates.io-index" 638 - checksum = "7a0aeaff4ff1a90589618835a598e545176939b97874f7abc7851caa0618f203" 638 + checksum = "7a0dd1ca384932ff3641c8718a02769f1698e7563dc6974ffd03346116310423" 639 639 dependencies = [ 640 640 "find-msvc-tools", 641 641 "jobserver", ··· 644 644 ] 645 645 646 646 [[package]] 647 - name = "cesu8" 648 - version = "1.1.0" 649 - source = "registry+https://github.com/rust-lang/crates.io-index" 650 - checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" 651 - 652 - [[package]] 653 647 name = "cfg-if" 654 648 version = "1.0.4" 655 649 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 663 657 664 658 [[package]] 665 659 name = "chrono" 666 - version = "0.4.42" 660 + version = "0.4.44" 667 661 source = "registry+https://github.com/rust-lang/crates.io-index" 668 - checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" 662 + checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" 669 663 dependencies = [ 670 664 "iana-time-zone", 671 665 "js-sys", ··· 724 718 725 719 [[package]] 726 720 name = "clap" 727 - version = "4.5.53" 721 + version = "4.6.0" 728 722 source = "registry+https://github.com/rust-lang/crates.io-index" 729 - checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8" 723 + checksum = "b193af5b67834b676abd72466a96c1024e6a6ad978a1f484bd90b85c94041351" 730 724 dependencies = [ 731 725 "clap_builder", 732 726 "clap_derive", ··· 734 728 735 729 [[package]] 736 730 name = "clap_builder" 737 - version = "4.5.53" 731 + version = "4.6.0" 738 732 source = "registry+https://github.com/rust-lang/crates.io-index" 739 - checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00" 733 + checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" 740 734 dependencies = [ 741 735 "anstream", 742 736 "anstyle", ··· 746 740 747 741 [[package]] 748 742 name = "clap_complete" 749 - version = "4.5.64" 743 + version = "4.6.0" 750 744 source = "registry+https://github.com/rust-lang/crates.io-index" 751 - checksum = "4c0da80818b2d95eca9aa614a30783e42f62bf5fdfee24e68cfb960b071ba8d1" 745 + checksum = "19c9f1dde76b736e3681f28cec9d5a61299cbaae0fce80a68e43724ad56031eb" 752 746 dependencies = [ 753 747 "clap", 754 748 ] 755 749 756 750 [[package]] 757 751 name = "clap_derive" 758 - version = "4.5.49" 752 + version = "4.6.0" 759 753 source = "registry+https://github.com/rust-lang/crates.io-index" 760 - checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" 754 + checksum = "1110bd8a634a1ab8cb04345d8d878267d57c3cf1b38d91b71af6686408bbca6a" 761 755 dependencies = [ 762 756 "heck 0.5.0", 763 757 "proc-macro2", 764 758 "quote", 765 - "syn 2.0.112", 759 + "syn", 766 760 ] 767 761 768 762 [[package]] 769 763 name = "clap_lex" 770 - version = "0.7.6" 764 + version = "1.1.0" 771 765 source = "registry+https://github.com/rust-lang/crates.io-index" 772 - checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" 766 + checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" 773 767 774 768 [[package]] 775 769 name = "clap_mangen" 776 - version = "0.2.31" 770 + version = "0.2.33" 777 771 source = "registry+https://github.com/rust-lang/crates.io-index" 778 - checksum = "439ea63a92086df93893164221ad4f24142086d535b3a0957b9b9bea2dc86301" 772 + checksum = "7e30ffc187e2e3aeafcd1c6e2aa416e29739454c0ccaa419226d5ecd181f2d78" 779 773 dependencies = [ 780 774 "clap", 781 775 "roff", ··· 787 781 source = "registry+https://github.com/rust-lang/crates.io-index" 788 782 checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" 789 783 dependencies = [ 790 - "thiserror 2.0.17", 784 + "thiserror 2.0.18", 791 785 ] 792 786 793 787 [[package]] ··· 798 792 799 793 [[package]] 800 794 name = "colorchoice" 801 - version = "1.0.4" 795 + version = "1.0.5" 802 796 source = "registry+https://github.com/rust-lang/crates.io-index" 803 - checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" 797 + checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" 804 798 805 799 [[package]] 806 800 name = "combine" ··· 814 808 815 809 [[package]] 816 810 name = "compression-codecs" 817 - version = "0.4.35" 811 + version = "0.4.37" 818 812 source = "registry+https://github.com/rust-lang/crates.io-index" 819 - checksum = "b0f7ac3e5b97fdce45e8922fb05cae2c37f7bbd63d30dd94821dacfd8f3f2bf2" 813 + checksum = "eb7b51a7d9c967fc26773061ba86150f19c50c0d65c887cb1fbe295fd16619b7" 820 814 dependencies = [ 821 815 "compression-core", 822 816 "flate2", ··· 1040 1034 dependencies = [ 1041 1035 "proc-macro2", 1042 1036 "quote", 1043 - "syn 2.0.112", 1037 + "syn", 1044 1038 ] 1045 1039 1046 1040 [[package]] 1047 1041 name = "darling" 1048 - version = "0.21.3" 1042 + version = "0.23.0" 1049 1043 source = "registry+https://github.com/rust-lang/crates.io-index" 1050 - checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" 1044 + checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" 1051 1045 dependencies = [ 1052 1046 "darling_core", 1053 1047 "darling_macro", ··· 1055 1049 1056 1050 [[package]] 1057 1051 name = "darling_core" 1058 - version = "0.21.3" 1052 + version = "0.23.0" 1059 1053 source = "registry+https://github.com/rust-lang/crates.io-index" 1060 - checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" 1054 + checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" 1061 1055 dependencies = [ 1062 - "fnv", 1063 1056 "ident_case", 1064 1057 "proc-macro2", 1065 1058 "quote", 1066 1059 "strsim", 1067 - "syn 2.0.112", 1060 + "syn", 1068 1061 ] 1069 1062 1070 1063 [[package]] 1071 1064 name = "darling_macro" 1072 - version = "0.21.3" 1065 + version = "0.23.0" 1073 1066 source = "registry+https://github.com/rust-lang/crates.io-index" 1074 - checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" 1067 + checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" 1075 1068 dependencies = [ 1076 1069 "darling_core", 1077 1070 "quote", 1078 - "syn 2.0.112", 1071 + "syn", 1079 1072 ] 1080 1073 1081 1074 [[package]] ··· 1094 1087 1095 1088 [[package]] 1096 1089 name = "data-encoding" 1097 - version = "2.9.0" 1090 + version = "2.10.0" 1098 1091 source = "registry+https://github.com/rust-lang/crates.io-index" 1099 - checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" 1092 + checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" 1100 1093 1101 1094 [[package]] 1102 1095 name = "data-encoding-macro" 1103 - version = "0.1.18" 1096 + version = "0.1.19" 1104 1097 source = "registry+https://github.com/rust-lang/crates.io-index" 1105 - checksum = "47ce6c96ea0102f01122a185683611bd5ac8d99e62bc59dd12e6bda344ee673d" 1098 + checksum = "8142a83c17aa9461d637e649271eae18bf2edd00e91f2e105df36c3c16355bdb" 1106 1099 dependencies = [ 1107 1100 "data-encoding", 1108 1101 "data-encoding-macro-internal", ··· 1110 1103 1111 1104 [[package]] 1112 1105 name = "data-encoding-macro-internal" 1113 - version = "0.1.16" 1106 + version = "0.1.17" 1114 1107 source = "registry+https://github.com/rust-lang/crates.io-index" 1115 - checksum = "8d162beedaa69905488a8da94f5ac3edb4dd4788b732fadb7bd120b2625c1976" 1108 + checksum = "7ab67060fc6b8ef687992d439ca0fa36e7ed17e9a0b16b25b601e8757df720de" 1116 1109 dependencies = [ 1117 1110 "data-encoding", 1118 - "syn 2.0.112", 1111 + "syn", 1119 1112 ] 1120 1113 1121 1114 [[package]] ··· 1141 1134 1142 1135 [[package]] 1143 1136 name = "deranged" 1144 - version = "0.5.5" 1137 + version = "0.5.8" 1145 1138 source = "registry+https://github.com/rust-lang/crates.io-index" 1146 - checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" 1139 + checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" 1147 1140 dependencies = [ 1148 1141 "powerfmt", 1149 1142 ] ··· 1165 1158 dependencies = [ 1166 1159 "proc-macro2", 1167 1160 "quote", 1168 - "syn 2.0.112", 1161 + "syn", 1169 1162 "unicode-xid", 1170 1163 ] 1171 1164 ··· 1201 1194 dependencies = [ 1202 1195 "proc-macro2", 1203 1196 "quote", 1204 - "syn 2.0.112", 1197 + "syn", 1205 1198 ] 1206 1199 1207 1200 [[package]] ··· 1334 1327 "heck 0.5.0", 1335 1328 "proc-macro2", 1336 1329 "quote", 1337 - "syn 2.0.112", 1330 + "syn", 1338 1331 ] 1339 1332 1340 1333 [[package]] ··· 1354 1347 dependencies = [ 1355 1348 "proc-macro2", 1356 1349 "quote", 1357 - "syn 2.0.112", 1350 + "syn", 1358 1351 ] 1359 1352 1360 1353 [[package]] ··· 1365 1358 1366 1359 [[package]] 1367 1360 name = "erased-serde" 1368 - version = "0.4.9" 1361 + version = "0.4.10" 1369 1362 source = "registry+https://github.com/rust-lang/crates.io-index" 1370 - checksum = "89e8918065695684b2b0702da20382d5ae6065cf3327bc2d6436bd49a71ce9f3" 1363 + checksum = "d2add8a07dd6a8d93ff627029c51de145e12686fbc36ecb298ac22e74cf02dec" 1371 1364 dependencies = [ 1372 1365 "serde", 1373 1366 "serde_core", ··· 1386 1379 1387 1380 [[package]] 1388 1381 name = "expect-json" 1389 - version = "1.8.1" 1382 + version = "1.10.1" 1390 1383 source = "registry+https://github.com/rust-lang/crates.io-index" 1391 - checksum = "aaf3355a7ef83e52c9383ab0c7719acd1da54be5fed7c6572d87ddc4d8589753" 1384 + checksum = "869f97f4abe8e78fc812a94ad6b721d72c4fb5532877c79610f2c238d7ccf6c4" 1392 1385 dependencies = [ 1393 1386 "chrono", 1394 1387 "email_address", ··· 1397 1390 "regex", 1398 1391 "serde", 1399 1392 "serde_json", 1400 - "thiserror 2.0.17", 1393 + "thiserror 2.0.18", 1401 1394 "typetag", 1402 1395 "uuid", 1403 1396 ] 1404 1397 1405 1398 [[package]] 1406 1399 name = "expect-json-macros" 1407 - version = "1.8.1" 1400 + version = "1.10.1" 1408 1401 source = "registry+https://github.com/rust-lang/crates.io-index" 1409 - checksum = "24ff9262e5b5f9760f60c57ada4fffd25201ae9fefd426f29f097dcc573d86e6" 1402 + checksum = "6e6fdf550180a6c29a28cb9aac262dc0064c25735641d2317f670075e9a469d9" 1410 1403 dependencies = [ 1411 1404 "proc-macro2", 1412 1405 "quote", 1413 - "syn 2.0.112", 1406 + "syn", 1414 1407 ] 1415 1408 1416 1409 [[package]] ··· 1451 1444 dependencies = [ 1452 1445 "proc-macro2", 1453 1446 "quote", 1454 - "syn 2.0.112", 1447 + "syn", 1455 1448 ] 1456 1449 1457 1450 [[package]] ··· 1481 1474 1482 1475 [[package]] 1483 1476 name = "filetime" 1484 - version = "0.2.26" 1477 + version = "0.2.27" 1485 1478 source = "registry+https://github.com/rust-lang/crates.io-index" 1486 - checksum = "bc0505cd1b6fa6580283f6bdf70a73fcf4aba1184038c90902b92b3dd0df63ed" 1479 + checksum = "f98844151eee8917efc50bd9e8318cb963ae8b297431495d3f758616ea5c57db" 1487 1480 dependencies = [ 1488 1481 "cfg-if", 1489 1482 "libc", 1490 1483 "libredox", 1491 - "windows-sys 0.60.2", 1492 1484 ] 1493 1485 1494 1486 [[package]] 1495 1487 name = "find-msvc-tools" 1496 - version = "0.1.6" 1488 + version = "0.1.9" 1497 1489 source = "registry+https://github.com/rust-lang/crates.io-index" 1498 - checksum = "645cbb3a84e60b7531617d5ae4e57f7e27308f6445f5abf653209ea76dec8dff" 1490 + checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" 1499 1491 1500 1492 [[package]] 1501 1493 name = "flate2" 1502 - version = "1.1.5" 1494 + version = "1.1.9" 1503 1495 source = "registry+https://github.com/rust-lang/crates.io-index" 1504 - checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb" 1496 + checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" 1505 1497 dependencies = [ 1506 1498 "crc32fast", 1507 1499 "miniz_oxide 0.8.9", ··· 1551 1543 1552 1544 [[package]] 1553 1545 name = "futures" 1554 - version = "0.3.31" 1546 + version = "0.3.32" 1555 1547 source = "registry+https://github.com/rust-lang/crates.io-index" 1556 - checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" 1548 + checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" 1557 1549 dependencies = [ 1558 1550 "futures-channel", 1559 1551 "futures-core", ··· 1566 1558 1567 1559 [[package]] 1568 1560 name = "futures-buffered" 1569 - version = "0.2.12" 1561 + version = "0.2.13" 1570 1562 source = "registry+https://github.com/rust-lang/crates.io-index" 1571 - checksum = "a8e0e1f38ec07ba4abbde21eed377082f17ccb988be9d988a5adbf4bafc118fd" 1563 + checksum = "4421cb78ee172b6b06080093479d3c50f058e7c81b7d577bbb8d118d551d4cd5" 1572 1564 dependencies = [ 1573 1565 "cordyceps", 1574 1566 "diatomic-waker", ··· 1579 1571 1580 1572 [[package]] 1581 1573 name = "futures-channel" 1582 - version = "0.3.31" 1574 + version = "0.3.32" 1583 1575 source = "registry+https://github.com/rust-lang/crates.io-index" 1584 - checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" 1576 + checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" 1585 1577 dependencies = [ 1586 1578 "futures-core", 1587 1579 "futures-sink", ··· 1589 1581 1590 1582 [[package]] 1591 1583 name = "futures-core" 1592 - version = "0.3.31" 1584 + version = "0.3.32" 1593 1585 source = "registry+https://github.com/rust-lang/crates.io-index" 1594 - checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" 1586 + checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" 1595 1587 1596 1588 [[package]] 1597 1589 name = "futures-executor" 1598 - version = "0.3.31" 1590 + version = "0.3.32" 1599 1591 source = "registry+https://github.com/rust-lang/crates.io-index" 1600 - checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" 1592 + checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" 1601 1593 dependencies = [ 1602 1594 "futures-core", 1603 1595 "futures-task", ··· 1606 1598 1607 1599 [[package]] 1608 1600 name = "futures-io" 1609 - version = "0.3.31" 1601 + version = "0.3.32" 1610 1602 source = "registry+https://github.com/rust-lang/crates.io-index" 1611 - checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" 1603 + checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" 1612 1604 1613 1605 [[package]] 1614 1606 name = "futures-lite" ··· 1625 1617 1626 1618 [[package]] 1627 1619 name = "futures-macro" 1628 - version = "0.3.31" 1620 + version = "0.3.32" 1629 1621 source = "registry+https://github.com/rust-lang/crates.io-index" 1630 - checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" 1622 + checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" 1631 1623 dependencies = [ 1632 1624 "proc-macro2", 1633 1625 "quote", 1634 - "syn 2.0.112", 1626 + "syn", 1635 1627 ] 1636 1628 1637 1629 [[package]] 1638 1630 name = "futures-sink" 1639 - version = "0.3.31" 1631 + version = "0.3.32" 1640 1632 source = "registry+https://github.com/rust-lang/crates.io-index" 1641 - checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" 1633 + checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" 1642 1634 1643 1635 [[package]] 1644 1636 name = "futures-task" 1645 - version = "0.3.31" 1637 + version = "0.3.32" 1646 1638 source = "registry+https://github.com/rust-lang/crates.io-index" 1647 - checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" 1639 + checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" 1648 1640 1649 1641 [[package]] 1650 1642 name = "futures-util" 1651 - version = "0.3.31" 1643 + version = "0.3.32" 1652 1644 source = "registry+https://github.com/rust-lang/crates.io-index" 1653 - checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" 1645 + checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" 1654 1646 dependencies = [ 1655 1647 "futures-channel", 1656 1648 "futures-core", ··· 1660 1652 "futures-task", 1661 1653 "memchr", 1662 1654 "pin-project-lite", 1663 - "pin-utils", 1664 1655 "slab", 1665 1656 ] 1666 1657 ··· 1692 1683 1693 1684 [[package]] 1694 1685 name = "getrandom" 1695 - version = "0.2.16" 1686 + version = "0.2.17" 1696 1687 source = "registry+https://github.com/rust-lang/crates.io-index" 1697 - checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" 1688 + checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" 1698 1689 dependencies = [ 1699 1690 "cfg-if", 1700 1691 "js-sys", ··· 1712 1703 "cfg-if", 1713 1704 "js-sys", 1714 1705 "libc", 1715 - "r-efi", 1706 + "r-efi 5.3.0", 1716 1707 "wasip2", 1717 1708 "wasm-bindgen", 1718 1709 ] 1719 1710 1720 1711 [[package]] 1712 + name = "getrandom" 1713 + version = "0.4.2" 1714 + source = "registry+https://github.com/rust-lang/crates.io-index" 1715 + checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" 1716 + dependencies = [ 1717 + "cfg-if", 1718 + "libc", 1719 + "r-efi 6.0.0", 1720 + "wasip2", 1721 + "wasip3", 1722 + ] 1723 + 1724 + [[package]] 1721 1725 name = "gif" 1722 1726 version = "0.14.1" 1723 1727 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1789 1793 1790 1794 [[package]] 1791 1795 name = "h2" 1792 - version = "0.4.12" 1796 + version = "0.4.13" 1793 1797 source = "registry+https://github.com/rust-lang/crates.io-index" 1794 - checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" 1798 + checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" 1795 1799 dependencies = [ 1796 1800 "atomic-waker", 1797 1801 "bytes", ··· 1971 1975 "markup5ever", 1972 1976 "proc-macro2", 1973 1977 "quote", 1974 - "syn 2.0.112", 1978 + "syn", 1975 1979 ] 1976 1980 1977 1981 [[package]] ··· 2061 2065 2062 2066 [[package]] 2063 2067 name = "hyper-util" 2064 - version = "0.1.19" 2068 + version = "0.1.20" 2065 2069 source = "registry+https://github.com/rust-lang/crates.io-index" 2066 - checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" 2070 + checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" 2067 2071 dependencies = [ 2068 2072 "base64 0.22.1", 2069 2073 "bytes", 2070 2074 "futures-channel", 2071 - "futures-core", 2072 2075 "futures-util", 2073 2076 "http", 2074 2077 "http-body", ··· 2077 2080 "libc", 2078 2081 "percent-encoding", 2079 2082 "pin-project-lite", 2080 - "socket2 0.6.1", 2083 + "socket2 0.6.3", 2081 2084 "system-configuration", 2082 2085 "tokio", 2083 2086 "tower-service", ··· 2087 2090 2088 2091 [[package]] 2089 2092 name = "iana-time-zone" 2090 - version = "0.1.64" 2093 + version = "0.1.65" 2091 2094 source = "registry+https://github.com/rust-lang/crates.io-index" 2092 - checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" 2095 + checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" 2093 2096 dependencies = [ 2094 2097 "android_system_properties", 2095 2098 "core-foundation-sys", ··· 2191 2194 ] 2192 2195 2193 2196 [[package]] 2197 + name = "id-arena" 2198 + version = "2.3.0" 2199 + source = "registry+https://github.com/rust-lang/crates.io-index" 2200 + checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" 2201 + 2202 + [[package]] 2194 2203 name = "ident_case" 2195 2204 version = "1.0.1" 2196 2205 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2219 2228 2220 2229 [[package]] 2221 2230 name = "image" 2222 - version = "0.25.9" 2231 + version = "0.25.10" 2223 2232 source = "registry+https://github.com/rust-lang/crates.io-index" 2224 - checksum = "e6506c6c10786659413faa717ceebcb8f70731c0a60cbae39795fdf114519c1a" 2233 + checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104" 2225 2234 dependencies = [ 2226 2235 "bytemuck", 2227 2236 "byteorder-lite", ··· 2236 2245 "ravif", 2237 2246 "rayon", 2238 2247 "rgb", 2239 - "tiff 0.10.3", 2240 - "zune-core 0.5.0", 2241 - "zune-jpeg 0.5.8", 2248 + "tiff 0.11.3", 2249 + "zune-core", 2250 + "zune-jpeg", 2242 2251 ] 2243 2252 2244 2253 [[package]] ··· 2259 2268 2260 2269 [[package]] 2261 2270 name = "indexmap" 2262 - version = "2.12.1" 2271 + version = "2.13.0" 2263 2272 source = "registry+https://github.com/rust-lang/crates.io-index" 2264 - checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" 2273 + checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" 2265 2274 dependencies = [ 2266 2275 "equivalent", 2267 2276 "hashbrown 0.16.1", 2277 + "serde", 2278 + "serde_core", 2268 2279 ] 2269 2280 2270 2281 [[package]] ··· 2275 2286 dependencies = [ 2276 2287 "proc-macro2", 2277 2288 "quote", 2278 - "syn 2.0.112", 2289 + "syn", 2279 2290 ] 2280 2291 2281 2292 [[package]] 2282 2293 name = "inventory" 2283 - version = "0.3.21" 2294 + version = "0.3.22" 2284 2295 source = "registry+https://github.com/rust-lang/crates.io-index" 2285 - checksum = "bc61209c082fbeb19919bee74b176221b27223e27b65d781eb91af24eb1fb46e" 2296 + checksum = "009ae045c87e7082cb72dab0ccd01ae075dd00141ddc108f43a0ea150a9e7227" 2286 2297 dependencies = [ 2287 2298 "rustversion", 2288 2299 ] ··· 2301 2312 2302 2313 [[package]] 2303 2314 name = "ipld-core" 2304 - version = "0.4.2" 2315 + version = "0.4.3" 2305 2316 source = "registry+https://github.com/rust-lang/crates.io-index" 2306 - checksum = "104718b1cc124d92a6d01ca9c9258a7df311405debb3408c445a36452f9bf8db" 2317 + checksum = "090f624976d72f0b0bb71b86d58dc16c15e069193067cb3a3a09d655246cbbda" 2307 2318 dependencies = [ 2308 2319 "cid", 2309 2320 "serde", ··· 2312 2323 2313 2324 [[package]] 2314 2325 name = "ipnet" 2315 - version = "2.11.0" 2326 + version = "2.12.0" 2316 2327 source = "registry+https://github.com/rust-lang/crates.io-index" 2317 - checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" 2328 + checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" 2318 2329 2319 2330 [[package]] 2320 2331 name = "iri-string" ··· 2365 2376 2366 2377 [[package]] 2367 2378 name = "itoa" 2368 - version = "1.0.17" 2379 + version = "1.0.18" 2369 2380 source = "registry+https://github.com/rust-lang/crates.io-index" 2370 - checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" 2381 + checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" 2371 2382 2372 2383 [[package]] 2373 2384 name = "jacquard" ··· 2375 2386 dependencies = [ 2376 2387 "bytes", 2377 2388 "clap", 2378 - "getrandom 0.2.16", 2389 + "getrandom 0.2.17", 2379 2390 "gloo-storage", 2380 2391 "http", 2381 2392 "image", ··· 2394 2405 "serde_html_form", 2395 2406 "serde_json", 2396 2407 "smol_str", 2397 - "thiserror 2.0.17", 2408 + "thiserror 2.0.18", 2398 2409 "tiff 0.6.1", 2399 2410 "tokio", 2400 2411 "tracing 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)", ··· 2412 2423 "jacquard-lexicon", 2413 2424 "miette", 2414 2425 "serde", 2415 - "thiserror 2.0.17", 2426 + "thiserror 2.0.18", 2416 2427 ] 2417 2428 2418 2429 [[package]] ··· 2437 2448 "serde", 2438 2449 "serde_html_form", 2439 2450 "serde_json", 2440 - "thiserror 2.0.17", 2451 + "thiserror 2.0.18", 2441 2452 "tokio", 2442 2453 "tower", 2443 2454 "tower-http", 2444 2455 "tracing 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)", 2445 - "tracing-subscriber 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", 2456 + "tracing-subscriber 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)", 2446 2457 ] 2447 2458 2448 2459 [[package]] ··· 2460 2471 "fluent-uri", 2461 2472 "futures", 2462 2473 "futures-lite", 2463 - "getrandom 0.2.16", 2474 + "getrandom 0.2.17", 2464 2475 "getrandom 0.3.4", 2465 2476 "hashbrown 0.15.5", 2466 2477 "http", ··· 2490 2501 "signature", 2491 2502 "smol_str", 2492 2503 "spin 0.10.0", 2493 - "thiserror 2.0.17", 2504 + "thiserror 2.0.18", 2494 2505 "tokio", 2495 2506 "tokio-tungstenite-wasm", 2496 2507 "tokio-util", ··· 2512 2523 "quote", 2513 2524 "serde", 2514 2525 "serde_json", 2515 - "syn 2.0.112", 2526 + "syn", 2516 2527 "unicode-segmentation", 2517 2528 ] 2518 2529 ··· 2534 2545 "serde", 2535 2546 "serde_html_form", 2536 2547 "serde_json", 2537 - "thiserror 2.0.17", 2548 + "thiserror 2.0.18", 2538 2549 "tokio", 2539 2550 "tracing 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)", 2540 2551 "trait-variant", ··· 2559 2570 "reqwest", 2560 2571 "serde", 2561 2572 "serde_json", 2562 - "syn 2.0.112", 2573 + "syn", 2563 2574 "tempfile", 2564 - "thiserror 2.0.17", 2575 + "thiserror 2.0.18", 2565 2576 "tokio", 2566 2577 "toml 0.8.23", 2567 2578 "walkdir", ··· 2590 2601 "serde_repr", 2591 2602 "serde_with", 2592 2603 "sha2", 2593 - "syn 2.0.112", 2604 + "syn", 2594 2605 "tempfile", 2595 - "thiserror 2.0.17", 2606 + "thiserror 2.0.18", 2596 2607 "unicode-segmentation", 2597 2608 ] 2598 2609 ··· 2620 2631 "serde_json", 2621 2632 "sha2", 2622 2633 "smol_str", 2623 - "thiserror 2.0.17", 2634 + "thiserror 2.0.18", 2624 2635 "tokio", 2625 2636 "tracing 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)", 2626 2637 "trait-variant", ··· 2654 2665 "sha2", 2655 2666 "smol_str", 2656 2667 "tempfile", 2657 - "thiserror 2.0.17", 2668 + "thiserror 2.0.18", 2658 2669 "tokio", 2659 2670 "trait-variant", 2660 2671 ] 2661 2672 2662 2673 [[package]] 2663 2674 name = "jni" 2664 - version = "0.21.1" 2675 + version = "0.22.4" 2665 2676 source = "registry+https://github.com/rust-lang/crates.io-index" 2666 - checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" 2677 + checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" 2667 2678 dependencies = [ 2668 - "cesu8", 2669 2679 "cfg-if", 2670 2680 "combine", 2681 + "jni-macros", 2671 2682 "jni-sys", 2672 2683 "log", 2673 - "thiserror 1.0.69", 2684 + "simd_cesu8", 2685 + "thiserror 2.0.18", 2674 2686 "walkdir", 2675 - "windows-sys 0.45.0", 2687 + "windows-link", 2688 + ] 2689 + 2690 + [[package]] 2691 + name = "jni-macros" 2692 + version = "0.22.4" 2693 + source = "registry+https://github.com/rust-lang/crates.io-index" 2694 + checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" 2695 + dependencies = [ 2696 + "proc-macro2", 2697 + "quote", 2698 + "rustc_version", 2699 + "simd_cesu8", 2700 + "syn", 2676 2701 ] 2677 2702 2678 2703 [[package]] 2679 2704 name = "jni-sys" 2680 - version = "0.3.0" 2705 + version = "0.4.1" 2706 + source = "registry+https://github.com/rust-lang/crates.io-index" 2707 + checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" 2708 + dependencies = [ 2709 + "jni-sys-macros", 2710 + ] 2711 + 2712 + [[package]] 2713 + name = "jni-sys-macros" 2714 + version = "0.4.1" 2681 2715 source = "registry+https://github.com/rust-lang/crates.io-index" 2682 - checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" 2716 + checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" 2717 + dependencies = [ 2718 + "quote", 2719 + "syn", 2720 + ] 2683 2721 2684 2722 [[package]] 2685 2723 name = "jobserver" ··· 2735 2773 2736 2774 [[package]] 2737 2775 name = "js-sys" 2738 - version = "0.3.83" 2776 + version = "0.3.91" 2739 2777 source = "registry+https://github.com/rust-lang/crates.io-index" 2740 - checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" 2778 + checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" 2741 2779 dependencies = [ 2742 2780 "once_cell", 2743 2781 "wasm-bindgen", ··· 2793 2831 "tokio", 2794 2832 "tracing 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)", 2795 2833 "tracing 0.1.44 (git+https://github.com/tokio-rs/tracing)", 2796 - "tracing-subscriber 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", 2797 - "tracing-subscriber 0.3.22 (git+https://github.com/tokio-rs/tracing)", 2834 + "tracing-subscriber 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)", 2835 + "tracing-subscriber 0.3.23 (git+https://github.com/tokio-rs/tracing)", 2798 2836 ] 2799 2837 2800 2838 [[package]] ··· 2807 2845 ] 2808 2846 2809 2847 [[package]] 2848 + name = "leb128fmt" 2849 + version = "0.1.0" 2850 + source = "registry+https://github.com/rust-lang/crates.io-index" 2851 + checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" 2852 + 2853 + [[package]] 2810 2854 name = "lebe" 2811 2855 version = "0.5.3" 2812 2856 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2814 2858 2815 2859 [[package]] 2816 2860 name = "libc" 2817 - version = "0.2.178" 2861 + version = "0.2.183" 2818 2862 source = "registry+https://github.com/rust-lang/crates.io-index" 2819 - checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" 2863 + checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" 2820 2864 2821 2865 [[package]] 2822 2866 name = "libfuzzer-sys" 2823 - version = "0.4.10" 2867 + version = "0.4.12" 2824 2868 source = "registry+https://github.com/rust-lang/crates.io-index" 2825 - checksum = "5037190e1f70cbeef565bd267599242926f724d3b8a9f510fd7e0b540cfa4404" 2869 + checksum = "f12a681b7dd8ce12bff52488013ba614b869148d54dd79836ab85aafdd53f08d" 2826 2870 dependencies = [ 2827 2871 "arbitrary", 2828 2872 "cc", ··· 2830 2874 2831 2875 [[package]] 2832 2876 name = "libm" 2833 - version = "0.2.15" 2877 + version = "0.2.16" 2834 2878 source = "registry+https://github.com/rust-lang/crates.io-index" 2835 - checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" 2879 + checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" 2836 2880 2837 2881 [[package]] 2838 2882 name = "libredox" 2839 - version = "0.1.12" 2883 + version = "0.1.14" 2840 2884 source = "registry+https://github.com/rust-lang/crates.io-index" 2841 - checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" 2885 + checksum = "1744e39d1d6a9948f4f388969627434e31128196de472883b39f148769bfe30a" 2842 2886 dependencies = [ 2843 2887 "bitflags", 2844 2888 "libc", 2845 - "redox_syscall 0.7.0", 2889 + "plain", 2890 + "redox_syscall 0.7.3", 2846 2891 ] 2847 2892 2848 2893 [[package]] ··· 2859 2904 2860 2905 [[package]] 2861 2906 name = "linux-raw-sys" 2862 - version = "0.11.0" 2907 + version = "0.12.1" 2863 2908 source = "registry+https://github.com/rust-lang/crates.io-index" 2864 - checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" 2909 + checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" 2865 2910 2866 2911 [[package]] 2867 2912 name = "litemap" ··· 2894 2939 "generator", 2895 2940 "scoped-tls", 2896 2941 "tracing 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)", 2897 - "tracing-subscriber 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", 2942 + "tracing-subscriber 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)", 2898 2943 ] 2899 2944 2900 2945 [[package]] ··· 2995 3040 2996 3041 [[package]] 2997 3042 name = "match-lookup" 2998 - version = "0.1.1" 3043 + version = "0.1.2" 2999 3044 source = "registry+https://github.com/rust-lang/crates.io-index" 3000 - checksum = "1265724d8cb29dbbc2b0f06fffb8bf1a8c0cf73a78eede9ba73a4a66c52a981e" 3045 + checksum = "757aee279b8bdbb9f9e676796fd459e4207a1f986e87886700abf589f5abf771" 3001 3046 dependencies = [ 3002 3047 "proc-macro2", 3003 3048 "quote", 3004 - "syn 1.0.109", 3049 + "syn", 3005 3050 ] 3006 3051 3007 3052 [[package]] ··· 3031 3076 3032 3077 [[package]] 3033 3078 name = "memchr" 3034 - version = "2.7.6" 3079 + version = "2.8.0" 3035 3080 source = "registry+https://github.com/rust-lang/crates.io-index" 3036 - checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" 3081 + checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" 3037 3082 3038 3083 [[package]] 3039 3084 name = "miette" ··· 3062 3107 dependencies = [ 3063 3108 "proc-macro2", 3064 3109 "quote", 3065 - "syn 2.0.112", 3110 + "syn", 3066 3111 ] 3067 3112 3068 3113 [[package]] ··· 3089 3134 "crossbeam-channel", 3090 3135 "crossbeam-utils", 3091 3136 "dashmap", 3092 - "getrandom 0.2.16", 3137 + "getrandom 0.2.17", 3093 3138 "once_cell", 3094 3139 "smallvec", 3095 3140 "tagptr", ··· 3142 3187 3143 3188 [[package]] 3144 3189 name = "moxcms" 3145 - version = "0.7.11" 3190 + version = "0.8.1" 3146 3191 source = "registry+https://github.com/rust-lang/crates.io-index" 3147 - checksum = "ac9557c559cd6fc9867e122e20d2cbefc9ca29d80d027a8e39310920ed2f0a97" 3192 + checksum = "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b" 3148 3193 dependencies = [ 3149 3194 "num-traits", 3150 3195 "pxfm", ··· 3311 3356 3312 3357 [[package]] 3313 3358 name = "num-conv" 3314 - version = "0.1.0" 3359 + version = "0.2.0" 3315 3360 source = "registry+https://github.com/rust-lang/crates.io-index" 3316 - checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" 3361 + checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" 3317 3362 3318 3363 [[package]] 3319 3364 name = "num-derive" ··· 3323 3368 dependencies = [ 3324 3369 "proc-macro2", 3325 3370 "quote", 3326 - "syn 2.0.112", 3371 + "syn", 3327 3372 ] 3328 3373 3329 3374 [[package]] ··· 3388 3433 3389 3434 [[package]] 3390 3435 name = "objc2" 3391 - version = "0.6.3" 3436 + version = "0.6.4" 3392 3437 source = "registry+https://github.com/rust-lang/crates.io-index" 3393 - checksum = "b7c2599ce0ec54857b29ce62166b0ed9b4f6f1a70ccc9a71165b6154caca8c05" 3438 + checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" 3394 3439 dependencies = [ 3395 3440 "objc2-encode", 3396 3441 ] ··· 3422 3467 3423 3468 [[package]] 3424 3469 name = "once_cell" 3425 - version = "1.21.3" 3470 + version = "1.21.4" 3426 3471 source = "registry+https://github.com/rust-lang/crates.io-index" 3427 - checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 3472 + checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" 3428 3473 3429 3474 [[package]] 3430 3475 name = "once_cell_polyfill" ··· 3440 3485 3441 3486 [[package]] 3442 3487 name = "openssl-probe" 3443 - version = "0.2.0" 3488 + version = "0.2.1" 3444 3489 source = "registry+https://github.com/rust-lang/crates.io-index" 3445 - checksum = "9f50d9b3dabb09ecd771ad0aa242ca6894994c130308ca3d7684634df8037391" 3490 + checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" 3446 3491 3447 3492 [[package]] 3448 3493 name = "ouroboros" ··· 3465 3510 "proc-macro2", 3466 3511 "proc-macro2-diagnostics", 3467 3512 "quote", 3468 - "syn 2.0.112", 3513 + "syn", 3469 3514 ] 3470 3515 3471 3516 [[package]] 3472 3517 name = "owo-colors" 3473 - version = "4.2.3" 3518 + version = "4.3.0" 3474 3519 source = "registry+https://github.com/rust-lang/crates.io-index" 3475 - checksum = "9c6901729fa79e91a0913333229e9ca5dc725089d1c363b2f4b4760709dc4a52" 3520 + checksum = "d211803b9b6b570f68772237e415a029d5a50c65d382910b879fb19d3271f94d" 3476 3521 3477 3522 [[package]] 3478 3523 name = "oxilangtag" ··· 3601 3646 "phf_shared", 3602 3647 "proc-macro2", 3603 3648 "quote", 3604 - "syn 2.0.112", 3649 + "syn", 3605 3650 ] 3606 3651 3607 3652 [[package]] ··· 3615 3660 3616 3661 [[package]] 3617 3662 name = "pin-project" 3618 - version = "1.1.10" 3663 + version = "1.1.11" 3619 3664 source = "registry+https://github.com/rust-lang/crates.io-index" 3620 - checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" 3665 + checksum = "f1749c7ed4bcaf4c3d0a3efc28538844fb29bcdd7d2b67b2be7e20ba861ff517" 3621 3666 dependencies = [ 3622 3667 "pin-project-internal", 3623 3668 ] 3624 3669 3625 3670 [[package]] 3626 3671 name = "pin-project-internal" 3627 - version = "1.1.10" 3672 + version = "1.1.11" 3628 3673 source = "registry+https://github.com/rust-lang/crates.io-index" 3629 - checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" 3674 + checksum = "d9b20ed30f105399776b9c883e68e536ef602a16ae6f596d2c473591d6ad64c6" 3630 3675 dependencies = [ 3631 3676 "proc-macro2", 3632 3677 "quote", 3633 - "syn 2.0.112", 3678 + "syn", 3634 3679 ] 3635 3680 3636 3681 [[package]] 3637 3682 name = "pin-project-lite" 3638 - version = "0.2.16" 3683 + version = "0.2.17" 3639 3684 source = "registry+https://github.com/rust-lang/crates.io-index" 3640 - checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" 3685 + checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" 3641 3686 3642 3687 [[package]] 3643 3688 name = "pin-utils" ··· 3673 3718 checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" 3674 3719 3675 3720 [[package]] 3721 + name = "plain" 3722 + version = "0.2.3" 3723 + source = "registry+https://github.com/rust-lang/crates.io-index" 3724 + checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" 3725 + 3726 + [[package]] 3676 3727 name = "png" 3677 - version = "0.18.0" 3728 + version = "0.18.1" 3678 3729 source = "registry+https://github.com/rust-lang/crates.io-index" 3679 - checksum = "97baced388464909d42d89643fe4361939af9b7ce7a31ee32a168f832a70f2a0" 3730 + checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" 3680 3731 dependencies = [ 3681 3732 "bitflags", 3682 3733 "crc32fast", ··· 3687 3738 3688 3739 [[package]] 3689 3740 name = "portable-atomic" 3690 - version = "1.13.0" 3741 + version = "1.13.1" 3691 3742 source = "registry+https://github.com/rust-lang/crates.io-index" 3692 - checksum = "f89776e4d69bb58bc6993e99ffa1d11f228b839984854c7daeb5d37f87cbe950" 3743 + checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" 3693 3744 3694 3745 [[package]] 3695 3746 name = "postcard" ··· 3751 3802 checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" 3752 3803 dependencies = [ 3753 3804 "proc-macro2", 3754 - "syn 2.0.112", 3805 + "syn", 3755 3806 ] 3756 3807 3757 3808 [[package]] ··· 3765 3816 3766 3817 [[package]] 3767 3818 name = "proc-macro2" 3768 - version = "1.0.104" 3819 + version = "1.0.106" 3769 3820 source = "registry+https://github.com/rust-lang/crates.io-index" 3770 - checksum = "9695f8df41bb4f3d222c95a67532365f569318332d03d5f3f67f37b20e6ebdf0" 3821 + checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" 3771 3822 dependencies = [ 3772 3823 "unicode-ident", 3773 3824 ] ··· 3780 3831 dependencies = [ 3781 3832 "proc-macro2", 3782 3833 "quote", 3783 - "syn 2.0.112", 3834 + "syn", 3784 3835 "version_check", 3785 3836 "yansi", 3786 3837 ] ··· 3801 3852 checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b" 3802 3853 dependencies = [ 3803 3854 "quote", 3804 - "syn 2.0.112", 3855 + "syn", 3805 3856 ] 3806 3857 3807 3858 [[package]] 3808 3859 name = "proptest" 3809 - version = "1.9.0" 3860 + version = "1.10.0" 3810 3861 source = "registry+https://github.com/rust-lang/crates.io-index" 3811 - checksum = "bee689443a2bd0a16ab0348b52ee43e3b2d1b1f931c8aa5c9f8de4c86fbe8c40" 3862 + checksum = "37566cb3fdacef14c0737f9546df7cfeadbfbc9fef10991038bf5015d0c80532" 3812 3863 dependencies = [ 3813 3864 "bit-set", 3814 3865 "bit-vec", ··· 3825 3876 3826 3877 [[package]] 3827 3878 name = "pxfm" 3828 - version = "0.1.27" 3879 + version = "0.1.28" 3829 3880 source = "registry+https://github.com/rust-lang/crates.io-index" 3830 - checksum = "7186d3822593aa4393561d186d1393b3923e9d6163d3fbfd6e825e3e6cf3e6a8" 3831 - dependencies = [ 3832 - "num-traits", 3833 - ] 3881 + checksum = "b5a041e753da8b807c9255f28de81879c78c876392ff2469cde94799b2896b9d" 3834 3882 3835 3883 [[package]] 3836 3884 name = "qoi" ··· 3866 3914 "quinn-udp", 3867 3915 "rustc-hash", 3868 3916 "rustls", 3869 - "socket2 0.6.1", 3870 - "thiserror 2.0.17", 3917 + "socket2 0.6.3", 3918 + "thiserror 2.0.18", 3871 3919 "tokio", 3872 3920 "tracing 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)", 3873 3921 "web-time", ··· 3875 3923 3876 3924 [[package]] 3877 3925 name = "quinn-proto" 3878 - version = "0.11.13" 3926 + version = "0.11.14" 3879 3927 source = "registry+https://github.com/rust-lang/crates.io-index" 3880 - checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" 3928 + checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" 3881 3929 dependencies = [ 3882 3930 "bytes", 3883 3931 "getrandom 0.3.4", ··· 3888 3936 "rustls", 3889 3937 "rustls-pki-types", 3890 3938 "slab", 3891 - "thiserror 2.0.17", 3939 + "thiserror 2.0.18", 3892 3940 "tinyvec", 3893 3941 "tracing 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)", 3894 3942 "web-time", ··· 3903 3951 "cfg_aliases", 3904 3952 "libc", 3905 3953 "once_cell", 3906 - "socket2 0.6.1", 3954 + "socket2 0.6.3", 3907 3955 "tracing 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)", 3908 3956 "windows-sys 0.60.2", 3909 3957 ] 3910 3958 3911 3959 [[package]] 3912 3960 name = "quote" 3913 - version = "1.0.42" 3961 + version = "1.0.45" 3914 3962 source = "registry+https://github.com/rust-lang/crates.io-index" 3915 - checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" 3963 + checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" 3916 3964 dependencies = [ 3917 3965 "proc-macro2", 3918 3966 ] ··· 3924 3972 checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" 3925 3973 3926 3974 [[package]] 3975 + name = "r-efi" 3976 + version = "6.0.0" 3977 + source = "registry+https://github.com/rust-lang/crates.io-index" 3978 + checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" 3979 + 3980 + [[package]] 3927 3981 name = "rand" 3928 3982 version = "0.8.5" 3929 3983 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3941 3995 checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" 3942 3996 dependencies = [ 3943 3997 "rand_chacha 0.9.0", 3944 - "rand_core 0.9.3", 3998 + "rand_core 0.9.5", 3945 3999 ] 3946 4000 3947 4001 [[package]] ··· 3961 4015 checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" 3962 4016 dependencies = [ 3963 4017 "ppv-lite86", 3964 - "rand_core 0.9.3", 4018 + "rand_core 0.9.5", 3965 4019 ] 3966 4020 3967 4021 [[package]] ··· 3970 4024 source = "registry+https://github.com/rust-lang/crates.io-index" 3971 4025 checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 3972 4026 dependencies = [ 3973 - "getrandom 0.2.16", 4027 + "getrandom 0.2.17", 3974 4028 ] 3975 4029 3976 4030 [[package]] 3977 4031 name = "rand_core" 3978 - version = "0.9.3" 4032 + version = "0.9.5" 3979 4033 source = "registry+https://github.com/rust-lang/crates.io-index" 3980 - checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" 4034 + checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" 3981 4035 dependencies = [ 3982 4036 "getrandom 0.3.4", 3983 4037 ] ··· 3988 4042 source = "registry+https://github.com/rust-lang/crates.io-index" 3989 4043 checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" 3990 4044 dependencies = [ 3991 - "rand_core 0.9.3", 4045 + "rand_core 0.9.5", 3992 4046 ] 3993 4047 3994 4048 [[package]] ··· 4021 4075 "rand 0.9.2", 4022 4076 "rand_chacha 0.9.0", 4023 4077 "simd_helpers", 4024 - "thiserror 2.0.17", 4078 + "thiserror 2.0.18", 4025 4079 "v_frame", 4026 4080 "wasm-bindgen", 4027 4081 ] 4028 4082 4029 4083 [[package]] 4030 4084 name = "ravif" 4031 - version = "0.12.0" 4085 + version = "0.13.0" 4032 4086 source = "registry+https://github.com/rust-lang/crates.io-index" 4033 - checksum = "ef69c1990ceef18a116855938e74793a5f7496ee907562bd0857b6ac734ab285" 4087 + checksum = "e52310197d971b0f5be7fe6b57530dcd27beb35c1b013f29d66c1ad73fbbcc45" 4034 4088 dependencies = [ 4035 4089 "avif-serialize", 4036 4090 "imgref", ··· 4072 4126 4073 4127 [[package]] 4074 4128 name = "redox_syscall" 4075 - version = "0.7.0" 4129 + version = "0.7.3" 4076 4130 source = "registry+https://github.com/rust-lang/crates.io-index" 4077 - checksum = "49f3fe0889e69e2ae9e41f4d6c4c0181701d00e4697b356fb1f74173a5e0ee27" 4131 + checksum = "6ce70a74e890531977d37e532c34d45e9055d2409ed08ddba14529471ed0be16" 4078 4132 dependencies = [ 4079 4133 "bitflags", 4080 4134 ] ··· 4096 4150 dependencies = [ 4097 4151 "proc-macro2", 4098 4152 "quote", 4099 - "syn 2.0.112", 4153 + "syn", 4100 4154 ] 4101 4155 4102 4156 [[package]] 4103 4157 name = "regex" 4104 - version = "1.12.2" 4158 + version = "1.12.3" 4105 4159 source = "registry+https://github.com/rust-lang/crates.io-index" 4106 - checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" 4160 + checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" 4107 4161 dependencies = [ 4108 4162 "aho-corasick", 4109 4163 "memchr", ··· 4113 4167 4114 4168 [[package]] 4115 4169 name = "regex-automata" 4116 - version = "0.4.13" 4170 + version = "0.4.14" 4117 4171 source = "registry+https://github.com/rust-lang/crates.io-index" 4118 - checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" 4172 + checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" 4119 4173 dependencies = [ 4120 4174 "aho-corasick", 4121 4175 "memchr", ··· 4124 4178 4125 4179 [[package]] 4126 4180 name = "regex-lite" 4127 - version = "0.1.8" 4181 + version = "0.1.9" 4128 4182 source = "registry+https://github.com/rust-lang/crates.io-index" 4129 - checksum = "8d942b98df5e658f56f20d592c7f868833fe38115e65c33003d8cd224b0155da" 4183 + checksum = "cab834c73d247e67f4fae452806d17d3c7501756d98c8808d7c9c7aa7d18f973" 4130 4184 4131 4185 [[package]] 4132 4186 name = "regex-syntax" 4133 - version = "0.8.8" 4187 + version = "0.8.10" 4134 4188 source = "registry+https://github.com/rust-lang/crates.io-index" 4135 - checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" 4189 + checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" 4136 4190 4137 4191 [[package]] 4138 4192 name = "reqwest" ··· 4180 4234 4181 4235 [[package]] 4182 4236 name = "reserve-port" 4183 - version = "2.3.0" 4237 + version = "2.4.0" 4184 4238 source = "registry+https://github.com/rust-lang/crates.io-index" 4185 - checksum = "21918d6644020c6f6ef1993242989bf6d4952d2e025617744f184c02df51c356" 4239 + checksum = "94070964579245eb2f76e62a7668fe87bd9969ed6c41256f3bf614e3323dd3cc" 4186 4240 dependencies = [ 4187 - "thiserror 2.0.17", 4241 + "thiserror 2.0.18", 4188 4242 ] 4189 4243 4190 4244 [[package]] ··· 4205 4259 4206 4260 [[package]] 4207 4261 name = "rgb" 4208 - version = "0.8.52" 4262 + version = "0.8.53" 4209 4263 source = "registry+https://github.com/rust-lang/crates.io-index" 4210 - checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce" 4264 + checksum = "47b34b781b31e5d73e9fbc8689c70551fd1ade9a19e3e28cfec8580a79290cc4" 4211 4265 dependencies = [ 4212 4266 "bytemuck", 4213 4267 ] ··· 4220 4274 dependencies = [ 4221 4275 "cc", 4222 4276 "cfg-if", 4223 - "getrandom 0.2.16", 4277 + "getrandom 0.2.17", 4224 4278 "libc", 4225 4279 "untrusted", 4226 4280 "windows-sys 0.52.0", ··· 4228 4282 4229 4283 [[package]] 4230 4284 name = "roff" 4231 - version = "0.2.2" 4285 + version = "1.1.0" 4232 4286 source = "registry+https://github.com/rust-lang/crates.io-index" 4233 - checksum = "88f8660c1ff60292143c98d08fc6e2f654d722db50410e3f3797d40baaf9d8f3" 4287 + checksum = "dbf2048e0e979efb2ca7b91c4f1a8d77c91853e9b987c94c555668a8994915ad" 4234 4288 4235 4289 [[package]] 4236 4290 name = "rouille" ··· 4258 4312 4259 4313 [[package]] 4260 4314 name = "rsa" 4261 - version = "0.9.9" 4315 + version = "0.9.10" 4262 4316 source = "registry+https://github.com/rust-lang/crates.io-index" 4263 - checksum = "40a0376c50d0358279d9d643e4bf7b7be212f1f4ff1da9070a7b54d22ef75c88" 4317 + checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d" 4264 4318 dependencies = [ 4265 4319 "const-oid", 4266 4320 "digest", ··· 4288 4342 "http", 4289 4343 "mime", 4290 4344 "rand 0.9.2", 4291 - "thiserror 2.0.17", 4345 + "thiserror 2.0.18", 4292 4346 ] 4293 4347 4294 4348 [[package]] 4295 4349 name = "rustc-demangle" 4296 - version = "0.1.26" 4350 + version = "0.1.27" 4297 4351 source = "registry+https://github.com/rust-lang/crates.io-index" 4298 - checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" 4352 + checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" 4299 4353 4300 4354 [[package]] 4301 4355 name = "rustc-hash" ··· 4327 4381 4328 4382 [[package]] 4329 4383 name = "rustix" 4330 - version = "1.1.3" 4384 + version = "1.1.4" 4331 4385 source = "registry+https://github.com/rust-lang/crates.io-index" 4332 - checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" 4386 + checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" 4333 4387 dependencies = [ 4334 4388 "bitflags", 4335 4389 "errno", 4336 4390 "libc", 4337 - "linux-raw-sys 0.11.0", 4391 + "linux-raw-sys 0.12.1", 4338 4392 "windows-sys 0.61.2", 4339 4393 ] 4340 4394 4341 4395 [[package]] 4342 4396 name = "rustls" 4343 - version = "0.23.35" 4397 + version = "0.23.37" 4344 4398 source = "registry+https://github.com/rust-lang/crates.io-index" 4345 - checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" 4399 + checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" 4346 4400 dependencies = [ 4347 4401 "once_cell", 4348 4402 "ring", ··· 4366 4420 4367 4421 [[package]] 4368 4422 name = "rustls-pki-types" 4369 - version = "1.13.2" 4423 + version = "1.14.0" 4370 4424 source = "registry+https://github.com/rust-lang/crates.io-index" 4371 - checksum = "21e6f2ab2928ca4291b86736a8bd920a277a399bba1589409d72154ff87c1282" 4425 + checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" 4372 4426 dependencies = [ 4373 4427 "web-time", 4374 4428 "zeroize", ··· 4376 4430 4377 4431 [[package]] 4378 4432 name = "rustls-webpki" 4379 - version = "0.103.8" 4433 + version = "0.103.9" 4380 4434 source = "registry+https://github.com/rust-lang/crates.io-index" 4381 - checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" 4435 + checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" 4382 4436 dependencies = [ 4383 4437 "ring", 4384 4438 "rustls-pki-types", ··· 4405 4459 4406 4460 [[package]] 4407 4461 name = "ryu" 4408 - version = "1.0.22" 4462 + version = "1.0.23" 4409 4463 source = "registry+https://github.com/rust-lang/crates.io-index" 4410 - checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" 4464 + checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" 4411 4465 4412 4466 [[package]] 4413 4467 name = "safemem" ··· 4426 4480 4427 4481 [[package]] 4428 4482 name = "schannel" 4429 - version = "0.1.28" 4483 + version = "0.1.29" 4430 4484 source = "registry+https://github.com/rust-lang/crates.io-index" 4431 - checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" 4485 + checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" 4432 4486 dependencies = [ 4433 4487 "windows-sys 0.61.2", 4434 4488 ] ··· 4461 4515 4462 4516 [[package]] 4463 4517 name = "security-framework" 4464 - version = "3.5.1" 4518 + version = "3.7.0" 4465 4519 source = "registry+https://github.com/rust-lang/crates.io-index" 4466 - checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" 4520 + checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" 4467 4521 dependencies = [ 4468 4522 "bitflags", 4469 4523 "core-foundation 0.10.1", ··· 4474 4528 4475 4529 [[package]] 4476 4530 name = "security-framework-sys" 4477 - version = "2.15.0" 4531 + version = "2.17.0" 4478 4532 source = "registry+https://github.com/rust-lang/crates.io-index" 4479 - checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" 4533 + checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" 4480 4534 dependencies = [ 4481 4535 "core-foundation-sys", 4482 4536 "libc", ··· 4531 4585 dependencies = [ 4532 4586 "proc-macro2", 4533 4587 "quote", 4534 - "syn 2.0.112", 4588 + "syn", 4535 4589 ] 4536 4590 4537 4591 [[package]] ··· 4571 4625 4572 4626 [[package]] 4573 4627 name = "serde_json" 4574 - version = "1.0.148" 4628 + version = "1.0.149" 4575 4629 source = "registry+https://github.com/rust-lang/crates.io-index" 4576 - checksum = "3084b546a1dd6289475996f182a22aba973866ea8e8b02c51d9f46b1336a22da" 4630 + checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" 4577 4631 dependencies = [ 4578 4632 "indexmap", 4579 4633 "itoa", ··· 4602 4656 dependencies = [ 4603 4657 "proc-macro2", 4604 4658 "quote", 4605 - "syn 2.0.112", 4659 + "syn", 4606 4660 ] 4607 4661 4608 4662 [[package]] ··· 4637 4691 4638 4692 [[package]] 4639 4693 name = "serde_with" 4640 - version = "3.16.1" 4694 + version = "3.18.0" 4641 4695 source = "registry+https://github.com/rust-lang/crates.io-index" 4642 - checksum = "4fa237f2807440d238e0364a218270b98f767a00d3dada77b1c53ae88940e2e7" 4696 + checksum = "dd5414fad8e6907dbdd5bc441a50ae8d6e26151a03b1de04d89a5576de61d01f" 4643 4697 dependencies = [ 4644 4698 "base64 0.22.1", 4645 4699 "chrono", ··· 4652 4706 4653 4707 [[package]] 4654 4708 name = "serde_with_macros" 4655 - version = "3.16.1" 4709 + version = "3.18.0" 4656 4710 source = "registry+https://github.com/rust-lang/crates.io-index" 4657 - checksum = "52a8e3ca0ca629121f70ab50f95249e5a6f925cc0f6ffe8256c45b728875706c" 4711 + checksum = "d3db8978e608f1fe7357e211969fd9abdcae80bac1ba7a3369bb7eb6b404eb65" 4658 4712 dependencies = [ 4659 4713 "darling", 4660 4714 "proc-macro2", 4661 4715 "quote", 4662 - "syn 2.0.112", 4716 + "syn", 4663 4717 ] 4664 4718 4665 4719 [[package]] ··· 4732 4786 checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" 4733 4787 4734 4788 [[package]] 4789 + name = "simd_cesu8" 4790 + version = "1.1.1" 4791 + source = "registry+https://github.com/rust-lang/crates.io-index" 4792 + checksum = "94f90157bb87cddf702797c5dadfa0be7d266cdf49e22da2fcaa32eff75b2c33" 4793 + dependencies = [ 4794 + "rustc_version", 4795 + "simdutf8", 4796 + ] 4797 + 4798 + [[package]] 4735 4799 name = "simd_helpers" 4736 4800 version = "0.1.0" 4737 4801 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4741 4805 ] 4742 4806 4743 4807 [[package]] 4808 + name = "simdutf8" 4809 + version = "0.1.5" 4810 + source = "registry+https://github.com/rust-lang/crates.io-index" 4811 + checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" 4812 + 4813 + [[package]] 4744 4814 name = "siphasher" 4745 - version = "1.0.1" 4815 + version = "1.0.2" 4746 4816 source = "registry+https://github.com/rust-lang/crates.io-index" 4747 - checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" 4817 + checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" 4748 4818 4749 4819 [[package]] 4750 4820 name = "slab" 4751 - version = "0.4.11" 4821 + version = "0.4.12" 4752 4822 source = "registry+https://github.com/rust-lang/crates.io-index" 4753 - checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" 4823 + checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" 4754 4824 4755 4825 [[package]] 4756 4826 name = "smallvec" ··· 4760 4830 4761 4831 [[package]] 4762 4832 name = "smol_str" 4763 - version = "0.3.4" 4833 + version = "0.3.6" 4764 4834 source = "registry+https://github.com/rust-lang/crates.io-index" 4765 - checksum = "3498b0a27f93ef1402f20eefacfaa1691272ac4eca1cdc8c596cb0a245d6cbf5" 4835 + checksum = "4aaa7368fcf4852a4c2dd92df0cace6a71f2091ca0a23391ce7f3a31833f1523" 4766 4836 dependencies = [ 4767 4837 "borsh", 4768 4838 "serde_core", ··· 4780 4850 4781 4851 [[package]] 4782 4852 name = "socket2" 4783 - version = "0.6.1" 4853 + version = "0.6.3" 4784 4854 source = "registry+https://github.com/rust-lang/crates.io-index" 4785 - checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" 4855 + checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" 4786 4856 dependencies = [ 4787 4857 "libc", 4788 - "windows-sys 0.60.2", 4858 + "windows-sys 0.61.2", 4789 4859 ] 4790 4860 4791 4861 [[package]] ··· 4885 4955 4886 4956 [[package]] 4887 4957 name = "syn" 4888 - version = "1.0.109" 4958 + version = "2.0.117" 4889 4959 source = "registry+https://github.com/rust-lang/crates.io-index" 4890 - checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 4891 - dependencies = [ 4892 - "proc-macro2", 4893 - "quote", 4894 - "unicode-ident", 4895 - ] 4896 - 4897 - [[package]] 4898 - name = "syn" 4899 - version = "2.0.112" 4900 - source = "registry+https://github.com/rust-lang/crates.io-index" 4901 - checksum = "21f182278bf2d2bcb3c88b1b08a37df029d71ce3d3ae26168e3c653b213b99d4" 4960 + checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" 4902 4961 dependencies = [ 4903 4962 "proc-macro2", 4904 4963 "quote", ··· 4922 4981 dependencies = [ 4923 4982 "proc-macro2", 4924 4983 "quote", 4925 - "syn 2.0.112", 4984 + "syn", 4926 4985 ] 4927 4986 4928 4987 [[package]] 4929 4988 name = "system-configuration" 4930 - version = "0.6.1" 4989 + version = "0.7.0" 4931 4990 source = "registry+https://github.com/rust-lang/crates.io-index" 4932 - checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" 4991 + checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" 4933 4992 dependencies = [ 4934 4993 "bitflags", 4935 4994 "core-foundation 0.9.4", ··· 4960 5019 4961 5020 [[package]] 4962 5021 name = "tempfile" 4963 - version = "3.24.0" 5022 + version = "3.27.0" 4964 5023 source = "registry+https://github.com/rust-lang/crates.io-index" 4965 - checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" 5024 + checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" 4966 5025 dependencies = [ 4967 5026 "fastrand", 4968 - "getrandom 0.3.4", 5027 + "getrandom 0.4.2", 4969 5028 "once_cell", 4970 - "rustix 1.1.3", 5029 + "rustix 1.1.4", 4971 5030 "windows-sys 0.61.2", 4972 5031 ] 4973 5032 ··· 4997 5056 source = "registry+https://github.com/rust-lang/crates.io-index" 4998 5057 checksum = "60b8cb979cb11c32ce1603f8137b22262a9d131aaa5c37b5678025f22b8becd0" 4999 5058 dependencies = [ 5000 - "rustix 1.1.3", 5059 + "rustix 1.1.4", 5001 5060 "windows-sys 0.60.2", 5002 5061 ] 5003 5062 ··· 5022 5081 5023 5082 [[package]] 5024 5083 name = "thiserror" 5025 - version = "2.0.17" 5084 + version = "2.0.18" 5026 5085 source = "registry+https://github.com/rust-lang/crates.io-index" 5027 - checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" 5086 + checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" 5028 5087 dependencies = [ 5029 - "thiserror-impl 2.0.17", 5088 + "thiserror-impl 2.0.18", 5030 5089 ] 5031 5090 5032 5091 [[package]] ··· 5037 5096 dependencies = [ 5038 5097 "proc-macro2", 5039 5098 "quote", 5040 - "syn 2.0.112", 5099 + "syn", 5041 5100 ] 5042 5101 5043 5102 [[package]] 5044 5103 name = "thiserror-impl" 5045 - version = "2.0.17" 5104 + version = "2.0.18" 5046 5105 source = "registry+https://github.com/rust-lang/crates.io-index" 5047 - checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" 5106 + checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" 5048 5107 dependencies = [ 5049 5108 "proc-macro2", 5050 5109 "quote", 5051 - "syn 2.0.112", 5110 + "syn", 5052 5111 ] 5053 5112 5054 5113 [[package]] ··· 5082 5141 5083 5142 [[package]] 5084 5143 name = "tiff" 5085 - version = "0.10.3" 5144 + version = "0.11.3" 5086 5145 source = "registry+https://github.com/rust-lang/crates.io-index" 5087 - checksum = "af9605de7fee8d9551863fd692cce7637f548dbd9db9180fcc07ccc6d26c336f" 5146 + checksum = "b63feaf3343d35b6ca4d50483f94843803b0f51634937cc2ec519fc32232bc52" 5088 5147 dependencies = [ 5089 5148 "fax", 5090 5149 "flate2", 5091 5150 "half", 5092 5151 "quick-error 2.0.1", 5093 5152 "weezl", 5094 - "zune-jpeg 0.4.21", 5153 + "zune-jpeg", 5095 5154 ] 5096 5155 5097 5156 [[package]] 5098 5157 name = "time" 5099 - version = "0.3.44" 5158 + version = "0.3.47" 5100 5159 source = "registry+https://github.com/rust-lang/crates.io-index" 5101 - checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" 5160 + checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" 5102 5161 dependencies = [ 5103 5162 "deranged", 5104 5163 "itoa", ··· 5106 5165 "num-conv", 5107 5166 "num_threads", 5108 5167 "powerfmt", 5109 - "serde", 5168 + "serde_core", 5110 5169 "time-core", 5111 5170 "time-macros", 5112 5171 ] 5113 5172 5114 5173 [[package]] 5115 5174 name = "time-core" 5116 - version = "0.1.6" 5175 + version = "0.1.8" 5117 5176 source = "registry+https://github.com/rust-lang/crates.io-index" 5118 - checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" 5177 + checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" 5119 5178 5120 5179 [[package]] 5121 5180 name = "time-macros" 5122 - version = "0.2.24" 5181 + version = "0.2.27" 5123 5182 source = "registry+https://github.com/rust-lang/crates.io-index" 5124 - checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" 5183 + checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" 5125 5184 dependencies = [ 5126 5185 "num-conv", 5127 5186 "time-core", ··· 5151 5210 5152 5211 [[package]] 5153 5212 name = "tinyvec" 5154 - version = "1.10.0" 5213 + version = "1.11.0" 5155 5214 source = "registry+https://github.com/rust-lang/crates.io-index" 5156 - checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" 5215 + checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" 5157 5216 dependencies = [ 5158 5217 "tinyvec_macros", 5159 5218 ] ··· 5166 5225 5167 5226 [[package]] 5168 5227 name = "tokio" 5169 - version = "1.48.0" 5228 + version = "1.50.0" 5170 5229 source = "registry+https://github.com/rust-lang/crates.io-index" 5171 - checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" 5230 + checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d" 5172 5231 dependencies = [ 5173 5232 "bytes", 5174 5233 "libc", ··· 5176 5235 "parking_lot", 5177 5236 "pin-project-lite", 5178 5237 "signal-hook-registry", 5179 - "socket2 0.6.1", 5238 + "socket2 0.6.3", 5180 5239 "tokio-macros", 5181 5240 "windows-sys 0.61.2", 5182 5241 ] 5183 5242 5184 5243 [[package]] 5185 5244 name = "tokio-macros" 5186 - version = "2.6.0" 5245 + version = "2.6.1" 5187 5246 source = "registry+https://github.com/rust-lang/crates.io-index" 5188 - checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" 5247 + checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c" 5189 5248 dependencies = [ 5190 5249 "proc-macro2", 5191 5250 "quote", 5192 - "syn 2.0.112", 5251 + "syn", 5193 5252 ] 5194 5253 5195 5254 [[package]] ··· 5239 5298 5240 5299 [[package]] 5241 5300 name = "tokio-util" 5242 - version = "0.7.17" 5301 + version = "0.7.18" 5243 5302 source = "registry+https://github.com/rust-lang/crates.io-index" 5244 - checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594" 5303 + checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" 5245 5304 dependencies = [ 5246 5305 "bytes", 5247 5306 "futures-core", ··· 5265 5324 5266 5325 [[package]] 5267 5326 name = "toml" 5268 - version = "0.9.10+spec-1.1.0" 5327 + version = "1.0.7+spec-1.1.0" 5269 5328 source = "registry+https://github.com/rust-lang/crates.io-index" 5270 - checksum = "0825052159284a1a8b4d6c0c86cbc801f2da5afd2b225fa548c72f2e74002f48" 5329 + checksum = "dd28d57d8a6f6e458bc0b8784f8fdcc4b99a437936056fa122cb234f18656a96" 5271 5330 dependencies = [ 5272 5331 "indexmap", 5273 5332 "serde_core", 5274 5333 "serde_spanned 1.0.4", 5275 - "toml_datetime 0.7.5+spec-1.1.0", 5334 + "toml_datetime 1.0.1+spec-1.1.0", 5276 5335 "toml_parser", 5277 5336 "toml_writer", 5278 - "winnow 0.7.14", 5337 + "winnow 1.0.0", 5279 5338 ] 5280 5339 5281 5340 [[package]] ··· 5289 5348 5290 5349 [[package]] 5291 5350 name = "toml_datetime" 5292 - version = "0.7.5+spec-1.1.0" 5351 + version = "1.0.1+spec-1.1.0" 5293 5352 source = "registry+https://github.com/rust-lang/crates.io-index" 5294 - checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" 5353 + checksum = "9b320e741db58cac564e26c607d3cc1fdc4a88fd36c879568c07856ed83ff3e9" 5295 5354 dependencies = [ 5296 5355 "serde_core", 5297 5356 ] ··· 5307 5366 "serde_spanned 0.6.9", 5308 5367 "toml_datetime 0.6.11", 5309 5368 "toml_write", 5310 - "winnow 0.7.14", 5369 + "winnow 0.7.15", 5311 5370 ] 5312 5371 5313 5372 [[package]] 5314 5373 name = "toml_parser" 5315 - version = "1.0.6+spec-1.1.0" 5374 + version = "1.0.10+spec-1.1.0" 5316 5375 source = "registry+https://github.com/rust-lang/crates.io-index" 5317 - checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" 5376 + checksum = "7df25b4befd31c4816df190124375d5a20c6b6921e2cad937316de3fccd63420" 5318 5377 dependencies = [ 5319 - "winnow 0.7.14", 5378 + "winnow 1.0.0", 5320 5379 ] 5321 5380 5322 5381 [[package]] ··· 5327 5386 5328 5387 [[package]] 5329 5388 name = "toml_writer" 5330 - version = "1.0.6+spec-1.1.0" 5389 + version = "1.0.7+spec-1.1.0" 5331 5390 source = "registry+https://github.com/rust-lang/crates.io-index" 5332 - checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" 5391 + checksum = "f17aaa1c6e3dc22b1da4b6bba97d066e354c7945cac2f7852d4e4e7ca7a6b56d" 5333 5392 5334 5393 [[package]] 5335 5394 name = "tower" 5336 - version = "0.5.2" 5395 + version = "0.5.3" 5337 5396 source = "registry+https://github.com/rust-lang/crates.io-index" 5338 - checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" 5397 + checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" 5339 5398 dependencies = [ 5340 5399 "futures-core", 5341 5400 "futures-util", ··· 5398 5457 [[package]] 5399 5458 name = "tracing" 5400 5459 version = "0.1.44" 5401 - source = "git+https://github.com/tokio-rs/tracing#efc690fa6bd1d9c3a57528b9bc8ac80504a7a6ed" 5460 + source = "git+https://github.com/tokio-rs/tracing#54ede4d5d85a536aed5485c5213011d9ec961935" 5402 5461 dependencies = [ 5403 5462 "pin-project-lite", 5404 5463 "tracing-attributes 0.1.31 (git+https://github.com/tokio-rs/tracing)", ··· 5413 5472 dependencies = [ 5414 5473 "proc-macro2", 5415 5474 "quote", 5416 - "syn 2.0.112", 5475 + "syn", 5417 5476 ] 5418 5477 5419 5478 [[package]] 5420 5479 name = "tracing-attributes" 5421 5480 version = "0.1.31" 5422 - source = "git+https://github.com/tokio-rs/tracing#efc690fa6bd1d9c3a57528b9bc8ac80504a7a6ed" 5481 + source = "git+https://github.com/tokio-rs/tracing#54ede4d5d85a536aed5485c5213011d9ec961935" 5423 5482 dependencies = [ 5424 5483 "proc-macro2", 5425 5484 "quote", 5426 - "syn 2.0.112", 5485 + "syn", 5427 5486 ] 5428 5487 5429 5488 [[package]] ··· 5439 5498 [[package]] 5440 5499 name = "tracing-core" 5441 5500 version = "0.1.36" 5442 - source = "git+https://github.com/tokio-rs/tracing#efc690fa6bd1d9c3a57528b9bc8ac80504a7a6ed" 5501 + source = "git+https://github.com/tokio-rs/tracing#54ede4d5d85a536aed5485c5213011d9ec961935" 5443 5502 dependencies = [ 5444 5503 "valuable", 5445 5504 ] ··· 5458 5517 [[package]] 5459 5518 name = "tracing-log" 5460 5519 version = "0.2.0" 5461 - source = "git+https://github.com/tokio-rs/tracing#efc690fa6bd1d9c3a57528b9bc8ac80504a7a6ed" 5520 + source = "git+https://github.com/tokio-rs/tracing#54ede4d5d85a536aed5485c5213011d9ec961935" 5462 5521 dependencies = [ 5463 5522 "log", 5464 5523 "once_cell", ··· 5467 5526 5468 5527 [[package]] 5469 5528 name = "tracing-subscriber" 5470 - version = "0.3.22" 5529 + version = "0.3.23" 5471 5530 source = "registry+https://github.com/rust-lang/crates.io-index" 5472 - checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" 5531 + checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" 5473 5532 dependencies = [ 5474 5533 "matchers", 5475 5534 "nu-ansi-term", ··· 5486 5545 5487 5546 [[package]] 5488 5547 name = "tracing-subscriber" 5489 - version = "0.3.22" 5490 - source = "git+https://github.com/tokio-rs/tracing#efc690fa6bd1d9c3a57528b9bc8ac80504a7a6ed" 5548 + version = "0.3.23" 5549 + source = "git+https://github.com/tokio-rs/tracing#54ede4d5d85a536aed5485c5213011d9ec961935" 5491 5550 dependencies = [ 5492 5551 "matchers", 5493 5552 "nu-ansi-term", ··· 5509 5568 dependencies = [ 5510 5569 "proc-macro2", 5511 5570 "quote", 5512 - "syn 2.0.112", 5571 + "syn", 5513 5572 ] 5514 5573 5515 5574 [[package]] ··· 5526 5585 5527 5586 [[package]] 5528 5587 name = "trybuild" 5529 - version = "1.0.114" 5588 + version = "1.0.116" 5530 5589 source = "registry+https://github.com/rust-lang/crates.io-index" 5531 - checksum = "3e17e807bff86d2a06b52bca4276746584a78375055b6e45843925ce2802b335" 5590 + checksum = "47c635f0191bd3a2941013e5062667100969f8c4e9cd787c14f977265d73616e" 5532 5591 dependencies = [ 5533 5592 "glob", 5534 5593 "serde", ··· 5536 5595 "serde_json", 5537 5596 "target-triple", 5538 5597 "termcolor", 5539 - "toml 0.9.10+spec-1.1.0", 5598 + "toml 1.0.7+spec-1.1.0", 5540 5599 ] 5541 5600 5542 5601 [[package]] ··· 5601 5660 dependencies = [ 5602 5661 "proc-macro2", 5603 5662 "quote", 5604 - "syn 2.0.112", 5663 + "syn", 5605 5664 ] 5606 5665 5607 5666 [[package]] ··· 5612 5671 5613 5672 [[package]] 5614 5673 name = "unicase" 5615 - version = "2.8.1" 5674 + version = "2.9.0" 5616 5675 source = "registry+https://github.com/rust-lang/crates.io-index" 5617 - checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" 5676 + checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" 5618 5677 5619 5678 [[package]] 5620 5679 name = "unicode-ident" 5621 - version = "1.0.22" 5680 + version = "1.0.24" 5622 5681 source = "registry+https://github.com/rust-lang/crates.io-index" 5623 - checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" 5682 + checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" 5624 5683 5625 5684 [[package]] 5626 5685 name = "unicode-linebreak" ··· 5672 5731 5673 5732 [[package]] 5674 5733 name = "url" 5675 - version = "2.5.7" 5734 + version = "2.5.8" 5676 5735 source = "registry+https://github.com/rust-lang/crates.io-index" 5677 - checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" 5736 + checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" 5678 5737 dependencies = [ 5679 5738 "form_urlencoded", 5680 5739 "idna", ··· 5702 5761 5703 5762 [[package]] 5704 5763 name = "uuid" 5705 - version = "1.19.0" 5764 + version = "1.22.0" 5706 5765 source = "registry+https://github.com/rust-lang/crates.io-index" 5707 - checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" 5766 + checksum = "a68d3c8f01c0cfa54a75291d83601161799e4a89a39e0929f4b0354d88757a37" 5708 5767 dependencies = [ 5709 5768 "js-sys", 5710 5769 "wasm-bindgen", ··· 5785 5844 5786 5845 [[package]] 5787 5846 name = "wasip2" 5788 - version = "1.0.1+wasi-0.2.4" 5847 + version = "1.0.2+wasi-0.2.9" 5789 5848 source = "registry+https://github.com/rust-lang/crates.io-index" 5790 - checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" 5849 + checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" 5850 + dependencies = [ 5851 + "wit-bindgen", 5852 + ] 5853 + 5854 + [[package]] 5855 + name = "wasip3" 5856 + version = "0.4.0+wasi-0.3.0-rc-2026-01-06" 5857 + source = "registry+https://github.com/rust-lang/crates.io-index" 5858 + checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" 5791 5859 dependencies = [ 5792 5860 "wit-bindgen", 5793 5861 ] 5794 5862 5795 5863 [[package]] 5796 5864 name = "wasm-bindgen" 5797 - version = "0.2.106" 5865 + version = "0.2.114" 5798 5866 source = "registry+https://github.com/rust-lang/crates.io-index" 5799 - checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" 5867 + checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" 5800 5868 dependencies = [ 5801 5869 "cfg-if", 5802 5870 "once_cell", ··· 5807 5875 5808 5876 [[package]] 5809 5877 name = "wasm-bindgen-futures" 5810 - version = "0.4.56" 5878 + version = "0.4.64" 5811 5879 source = "registry+https://github.com/rust-lang/crates.io-index" 5812 - checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c" 5880 + checksum = "e9c5522b3a28661442748e09d40924dfb9ca614b21c00d3fd135720e48b67db8" 5813 5881 dependencies = [ 5814 5882 "cfg-if", 5883 + "futures-util", 5815 5884 "js-sys", 5816 5885 "once_cell", 5817 5886 "wasm-bindgen", ··· 5820 5889 5821 5890 [[package]] 5822 5891 name = "wasm-bindgen-macro" 5823 - version = "0.2.106" 5892 + version = "0.2.114" 5824 5893 source = "registry+https://github.com/rust-lang/crates.io-index" 5825 - checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" 5894 + checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" 5826 5895 dependencies = [ 5827 5896 "quote", 5828 5897 "wasm-bindgen-macro-support", ··· 5830 5899 5831 5900 [[package]] 5832 5901 name = "wasm-bindgen-macro-support" 5833 - version = "0.2.106" 5902 + version = "0.2.114" 5834 5903 source = "registry+https://github.com/rust-lang/crates.io-index" 5835 - checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" 5904 + checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" 5836 5905 dependencies = [ 5837 5906 "bumpalo", 5838 5907 "proc-macro2", 5839 5908 "quote", 5840 - "syn 2.0.112", 5909 + "syn", 5841 5910 "wasm-bindgen-shared", 5842 5911 ] 5843 5912 5844 5913 [[package]] 5845 5914 name = "wasm-bindgen-shared" 5846 - version = "0.2.106" 5915 + version = "0.2.114" 5847 5916 source = "registry+https://github.com/rust-lang/crates.io-index" 5848 - checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" 5917 + checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" 5849 5918 dependencies = [ 5850 5919 "unicode-ident", 5851 5920 ] 5852 5921 5853 5922 [[package]] 5854 5923 name = "wasm-bindgen-test" 5855 - version = "0.3.56" 5924 + version = "0.3.64" 5856 5925 source = "registry+https://github.com/rust-lang/crates.io-index" 5857 - checksum = "25e90e66d265d3a1efc0e72a54809ab90b9c0c515915c67cdf658689d2c22c6c" 5926 + checksum = "6311c867385cc7d5602463b31825d454d0837a3aba7cdb5e56d5201792a3f7fe" 5858 5927 dependencies = [ 5859 5928 "async-trait", 5860 5929 "cast", ··· 5869 5938 "wasm-bindgen", 5870 5939 "wasm-bindgen-futures", 5871 5940 "wasm-bindgen-test-macro", 5941 + "wasm-bindgen-test-shared", 5872 5942 ] 5873 5943 5874 5944 [[package]] 5875 5945 name = "wasm-bindgen-test-macro" 5876 - version = "0.3.56" 5946 + version = "0.3.64" 5877 5947 source = "registry+https://github.com/rust-lang/crates.io-index" 5878 - checksum = "7150335716dce6028bead2b848e72f47b45e7b9422f64cccdc23bedca89affc1" 5948 + checksum = "67008cdde4769831958536b0f11b3bdd0380bde882be17fff9c2f34bb4549abd" 5879 5949 dependencies = [ 5880 5950 "proc-macro2", 5881 5951 "quote", 5882 - "syn 2.0.112", 5952 + "syn", 5953 + ] 5954 + 5955 + [[package]] 5956 + name = "wasm-bindgen-test-shared" 5957 + version = "0.2.114" 5958 + source = "registry+https://github.com/rust-lang/crates.io-index" 5959 + checksum = "cfe29135b180b72b04c74aa97b2b4a2ef275161eff9a6c7955ea9eaedc7e1d4e" 5960 + 5961 + [[package]] 5962 + name = "wasm-encoder" 5963 + version = "0.244.0" 5964 + source = "registry+https://github.com/rust-lang/crates.io-index" 5965 + checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" 5966 + dependencies = [ 5967 + "leb128fmt", 5968 + "wasmparser", 5969 + ] 5970 + 5971 + [[package]] 5972 + name = "wasm-metadata" 5973 + version = "0.244.0" 5974 + source = "registry+https://github.com/rust-lang/crates.io-index" 5975 + checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" 5976 + dependencies = [ 5977 + "anyhow", 5978 + "indexmap", 5979 + "wasm-encoder", 5980 + "wasmparser", 5883 5981 ] 5884 5982 5885 5983 [[package]] ··· 5896 5994 ] 5897 5995 5898 5996 [[package]] 5997 + name = "wasmparser" 5998 + version = "0.244.0" 5999 + source = "registry+https://github.com/rust-lang/crates.io-index" 6000 + checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" 6001 + dependencies = [ 6002 + "bitflags", 6003 + "hashbrown 0.15.5", 6004 + "indexmap", 6005 + "semver", 6006 + ] 6007 + 6008 + [[package]] 5899 6009 name = "web-sys" 5900 - version = "0.3.83" 6010 + version = "0.3.91" 5901 6011 source = "registry+https://github.com/rust-lang/crates.io-index" 5902 - checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" 6012 + checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9" 5903 6013 dependencies = [ 5904 6014 "js-sys", 5905 6015 "wasm-bindgen", ··· 5917 6027 5918 6028 [[package]] 5919 6029 name = "webbrowser" 5920 - version = "1.0.6" 6030 + version = "1.2.0" 5921 6031 source = "registry+https://github.com/rust-lang/crates.io-index" 5922 - checksum = "00f1243ef785213e3a32fa0396093424a3a6ea566f9948497e5a2309261a4c97" 6032 + checksum = "fe985f41e291eecef5e5c0770a18d28390addb03331c043964d9e916453d6f16" 5923 6033 dependencies = [ 5924 6034 "core-foundation 0.10.1", 5925 6035 "jni", ··· 5945 6055 5946 6056 [[package]] 5947 6057 name = "webpki-roots" 5948 - version = "1.0.4" 6058 + version = "1.0.6" 5949 6059 source = "registry+https://github.com/rust-lang/crates.io-index" 5950 - checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e" 6060 + checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" 5951 6061 dependencies = [ 5952 6062 "rustls-pki-types", 5953 6063 ] ··· 6016 6126 dependencies = [ 6017 6127 "proc-macro2", 6018 6128 "quote", 6019 - "syn 2.0.112", 6129 + "syn", 6020 6130 ] 6021 6131 6022 6132 [[package]] ··· 6027 6137 dependencies = [ 6028 6138 "proc-macro2", 6029 6139 "quote", 6030 - "syn 2.0.112", 6140 + "syn", 6031 6141 ] 6032 6142 6033 6143 [[package]] ··· 6067 6177 6068 6178 [[package]] 6069 6179 name = "windows-sys" 6070 - version = "0.45.0" 6071 - source = "registry+https://github.com/rust-lang/crates.io-index" 6072 - checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" 6073 - dependencies = [ 6074 - "windows-targets 0.42.2", 6075 - ] 6076 - 6077 - [[package]] 6078 - name = "windows-sys" 6079 6180 version = "0.48.0" 6080 6181 source = "registry+https://github.com/rust-lang/crates.io-index" 6081 6182 checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" ··· 6121 6222 6122 6223 [[package]] 6123 6224 name = "windows-targets" 6124 - version = "0.42.2" 6125 - source = "registry+https://github.com/rust-lang/crates.io-index" 6126 - checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" 6127 - dependencies = [ 6128 - "windows_aarch64_gnullvm 0.42.2", 6129 - "windows_aarch64_msvc 0.42.2", 6130 - "windows_i686_gnu 0.42.2", 6131 - "windows_i686_msvc 0.42.2", 6132 - "windows_x86_64_gnu 0.42.2", 6133 - "windows_x86_64_gnullvm 0.42.2", 6134 - "windows_x86_64_msvc 0.42.2", 6135 - ] 6136 - 6137 - [[package]] 6138 - name = "windows-targets" 6139 6225 version = "0.48.5" 6140 6226 source = "registry+https://github.com/rust-lang/crates.io-index" 6141 6227 checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" ··· 6184 6270 6185 6271 [[package]] 6186 6272 name = "windows_aarch64_gnullvm" 6187 - version = "0.42.2" 6188 - source = "registry+https://github.com/rust-lang/crates.io-index" 6189 - checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" 6190 - 6191 - [[package]] 6192 - name = "windows_aarch64_gnullvm" 6193 6273 version = "0.48.5" 6194 6274 source = "registry+https://github.com/rust-lang/crates.io-index" 6195 6275 checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" ··· 6208 6288 6209 6289 [[package]] 6210 6290 name = "windows_aarch64_msvc" 6211 - version = "0.42.2" 6212 - source = "registry+https://github.com/rust-lang/crates.io-index" 6213 - checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" 6214 - 6215 - [[package]] 6216 - name = "windows_aarch64_msvc" 6217 6291 version = "0.48.5" 6218 6292 source = "registry+https://github.com/rust-lang/crates.io-index" 6219 6293 checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" ··· 6229 6303 version = "0.53.1" 6230 6304 source = "registry+https://github.com/rust-lang/crates.io-index" 6231 6305 checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" 6232 - 6233 - [[package]] 6234 - name = "windows_i686_gnu" 6235 - version = "0.42.2" 6236 - source = "registry+https://github.com/rust-lang/crates.io-index" 6237 - checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" 6238 6306 6239 6307 [[package]] 6240 6308 name = "windows_i686_gnu" ··· 6268 6336 6269 6337 [[package]] 6270 6338 name = "windows_i686_msvc" 6271 - version = "0.42.2" 6272 - source = "registry+https://github.com/rust-lang/crates.io-index" 6273 - checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" 6274 - 6275 - [[package]] 6276 - name = "windows_i686_msvc" 6277 6339 version = "0.48.5" 6278 6340 source = "registry+https://github.com/rust-lang/crates.io-index" 6279 6341 checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" ··· 6292 6354 6293 6355 [[package]] 6294 6356 name = "windows_x86_64_gnu" 6295 - version = "0.42.2" 6296 - source = "registry+https://github.com/rust-lang/crates.io-index" 6297 - checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" 6298 - 6299 - [[package]] 6300 - name = "windows_x86_64_gnu" 6301 6357 version = "0.48.5" 6302 6358 source = "registry+https://github.com/rust-lang/crates.io-index" 6303 6359 checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" ··· 6316 6372 6317 6373 [[package]] 6318 6374 name = "windows_x86_64_gnullvm" 6319 - version = "0.42.2" 6320 - source = "registry+https://github.com/rust-lang/crates.io-index" 6321 - checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" 6322 - 6323 - [[package]] 6324 - name = "windows_x86_64_gnullvm" 6325 6375 version = "0.48.5" 6326 6376 source = "registry+https://github.com/rust-lang/crates.io-index" 6327 6377 checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" ··· 6340 6390 6341 6391 [[package]] 6342 6392 name = "windows_x86_64_msvc" 6343 - version = "0.42.2" 6344 - source = "registry+https://github.com/rust-lang/crates.io-index" 6345 - checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" 6346 - 6347 - [[package]] 6348 - name = "windows_x86_64_msvc" 6349 6393 version = "0.48.5" 6350 6394 source = "registry+https://github.com/rust-lang/crates.io-index" 6351 6395 checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" ··· 6373 6417 6374 6418 [[package]] 6375 6419 name = "winnow" 6376 - version = "0.7.14" 6420 + version = "0.7.15" 6377 6421 source = "registry+https://github.com/rust-lang/crates.io-index" 6378 - checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" 6422 + checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" 6379 6423 dependencies = [ 6380 6424 "memchr", 6381 6425 ] 6382 6426 6383 6427 [[package]] 6428 + name = "winnow" 6429 + version = "1.0.0" 6430 + source = "registry+https://github.com/rust-lang/crates.io-index" 6431 + checksum = "a90e88e4667264a994d34e6d1ab2d26d398dcdca8b7f52bec8668957517fc7d8" 6432 + 6433 + [[package]] 6384 6434 name = "winreg" 6385 6435 version = "0.50.0" 6386 6436 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 6392 6442 6393 6443 [[package]] 6394 6444 name = "wit-bindgen" 6395 - version = "0.46.0" 6445 + version = "0.51.0" 6446 + source = "registry+https://github.com/rust-lang/crates.io-index" 6447 + checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" 6448 + dependencies = [ 6449 + "wit-bindgen-rust-macro", 6450 + ] 6451 + 6452 + [[package]] 6453 + name = "wit-bindgen-core" 6454 + version = "0.51.0" 6455 + source = "registry+https://github.com/rust-lang/crates.io-index" 6456 + checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" 6457 + dependencies = [ 6458 + "anyhow", 6459 + "heck 0.5.0", 6460 + "wit-parser", 6461 + ] 6462 + 6463 + [[package]] 6464 + name = "wit-bindgen-rust" 6465 + version = "0.51.0" 6466 + source = "registry+https://github.com/rust-lang/crates.io-index" 6467 + checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" 6468 + dependencies = [ 6469 + "anyhow", 6470 + "heck 0.5.0", 6471 + "indexmap", 6472 + "prettyplease", 6473 + "syn", 6474 + "wasm-metadata", 6475 + "wit-bindgen-core", 6476 + "wit-component", 6477 + ] 6478 + 6479 + [[package]] 6480 + name = "wit-bindgen-rust-macro" 6481 + version = "0.51.0" 6482 + source = "registry+https://github.com/rust-lang/crates.io-index" 6483 + checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" 6484 + dependencies = [ 6485 + "anyhow", 6486 + "prettyplease", 6487 + "proc-macro2", 6488 + "quote", 6489 + "syn", 6490 + "wit-bindgen-core", 6491 + "wit-bindgen-rust", 6492 + ] 6493 + 6494 + [[package]] 6495 + name = "wit-component" 6496 + version = "0.244.0" 6497 + source = "registry+https://github.com/rust-lang/crates.io-index" 6498 + checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" 6499 + dependencies = [ 6500 + "anyhow", 6501 + "bitflags", 6502 + "indexmap", 6503 + "log", 6504 + "serde", 6505 + "serde_derive", 6506 + "serde_json", 6507 + "wasm-encoder", 6508 + "wasm-metadata", 6509 + "wasmparser", 6510 + "wit-parser", 6511 + ] 6512 + 6513 + [[package]] 6514 + name = "wit-parser" 6515 + version = "0.244.0" 6396 6516 source = "registry+https://github.com/rust-lang/crates.io-index" 6397 - checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" 6517 + checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" 6518 + dependencies = [ 6519 + "anyhow", 6520 + "id-arena", 6521 + "indexmap", 6522 + "log", 6523 + "semver", 6524 + "serde", 6525 + "serde_derive", 6526 + "serde_json", 6527 + "unicode-xid", 6528 + "wasmparser", 6529 + ] 6398 6530 6399 6531 [[package]] 6400 6532 name = "writeable" ··· 6444 6576 dependencies = [ 6445 6577 "proc-macro2", 6446 6578 "quote", 6447 - "syn 2.0.112", 6579 + "syn", 6448 6580 "synstructure", 6449 6581 ] 6450 6582 6451 6583 [[package]] 6452 6584 name = "zerocopy" 6453 - version = "0.8.31" 6585 + version = "0.8.47" 6454 6586 source = "registry+https://github.com/rust-lang/crates.io-index" 6455 - checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" 6587 + checksum = "efbb2a062be311f2ba113ce66f697a4dc589f85e78a4aea276200804cea0ed87" 6456 6588 dependencies = [ 6457 6589 "zerocopy-derive", 6458 6590 ] 6459 6591 6460 6592 [[package]] 6461 6593 name = "zerocopy-derive" 6462 - version = "0.8.31" 6594 + version = "0.8.47" 6463 6595 source = "registry+https://github.com/rust-lang/crates.io-index" 6464 - checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" 6596 + checksum = "0e8bc7269b54418e7aeeef514aa68f8690b8c0489a06b0136e5f57c4c5ccab89" 6465 6597 dependencies = [ 6466 6598 "proc-macro2", 6467 6599 "quote", 6468 - "syn 2.0.112", 6600 + "syn", 6469 6601 ] 6470 6602 6471 6603 [[package]] ··· 6485 6617 dependencies = [ 6486 6618 "proc-macro2", 6487 6619 "quote", 6488 - "syn 2.0.112", 6620 + "syn", 6489 6621 "synstructure", 6490 6622 ] 6491 6623 ··· 6528 6660 dependencies = [ 6529 6661 "proc-macro2", 6530 6662 "quote", 6531 - "syn 2.0.112", 6663 + "syn", 6532 6664 ] 6533 6665 6534 6666 [[package]] 6535 6667 name = "zmij" 6536 - version = "1.0.6" 6668 + version = "1.0.21" 6537 6669 source = "registry+https://github.com/rust-lang/crates.io-index" 6538 - checksum = "aac060176f7020d62c3bcc1cdbcec619d54f48b07ad1963a3f80ce7a0c17755f" 6670 + checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" 6539 6671 6540 6672 [[package]] 6541 6673 name = "zstd" ··· 6567 6699 6568 6700 [[package]] 6569 6701 name = "zune-core" 6570 - version = "0.4.12" 6571 - source = "registry+https://github.com/rust-lang/crates.io-index" 6572 - checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" 6573 - 6574 - [[package]] 6575 - name = "zune-core" 6576 - version = "0.5.0" 6702 + version = "0.5.1" 6577 6703 source = "registry+https://github.com/rust-lang/crates.io-index" 6578 - checksum = "111f7d9820f05fd715df3144e254d6fc02ee4088b0644c0ffd0efc9e6d9d2773" 6704 + checksum = "cb8a0807f7c01457d0379ba880ba6322660448ddebc890ce29bb64da71fb40f9" 6579 6705 6580 6706 [[package]] 6581 6707 name = "zune-inflate" ··· 6588 6714 6589 6715 [[package]] 6590 6716 name = "zune-jpeg" 6591 - version = "0.4.21" 6592 - source = "registry+https://github.com/rust-lang/crates.io-index" 6593 - checksum = "29ce2c8a9384ad323cf564b67da86e21d3cfdff87908bc1223ed5c99bc792713" 6594 - dependencies = [ 6595 - "zune-core 0.4.12", 6596 - ] 6597 - 6598 - [[package]] 6599 - name = "zune-jpeg" 6600 - version = "0.5.8" 6717 + version = "0.5.13" 6601 6718 source = "registry+https://github.com/rust-lang/crates.io-index" 6602 - checksum = "e35aee689668bf9bd6f6f3a6c60bb29ba1244b3b43adfd50edd554a371da37d5" 6719 + checksum = "ec5f41c76397b7da451efd19915684f727d7e1d516384ca6bd0ec43ec94de23c" 6603 6720 dependencies = [ 6604 - "zune-core 0.5.0", 6721 + "zune-core", 6605 6722 ]
+1 -1
crates/jacquard-common/src/lib.rs
··· 190 190 //! It's also not too bad to write, once you're aware of the pattern and why it works. If you run 191 191 //! into a lifetime/borrowing inference issue in jacquard, please contact the crate author. She'd 192 192 //! be happy to debug, and if it's using a method from one of the jacquard crates and seems like 193 - //! it *should* just work, that is a bug in jacquard, and you should [file an issue](https://tangled.org/@nonbinary.computer/jacquard/). 193 + //! it *should* just work, that is a bug in jacquard, and you should [file an issue](https://tangled.org/nonbinary.computer/jacquard/). 194 194 195 195 #![no_std] 196 196 #![warn(missing_docs)]
+69 -11
crates/jacquard-identity/src/lexicon_resolver.rs
··· 67 67 } 68 68 69 69 impl LexiconResolutionError { 70 + /// Create a new error with the given kind and optional source. 70 71 pub fn new( 71 72 kind: LexiconResolutionErrorKind, 72 73 source: Option<Box<dyn std::error::Error + Send + Sync>>, ··· 78 79 } 79 80 } 80 81 82 + /// Return the error kind. 81 83 pub fn kind(&self) -> &LexiconResolutionErrorKind { 82 84 &self.kind 83 85 } ··· 93 95 self.context.as_deref() 94 96 } 95 97 98 + /// Create an error for a failed DNS TXT lookup while resolving a lexicon authority. 96 99 pub fn dns_lookup_failed( 97 100 authority: impl Into<SmolStr>, 98 101 source: impl std::error::Error + Send + Sync + 'static, ··· 105 108 ) 106 109 } 107 110 111 + /// Create an error for when DNS records exist but contain no `did=...` entry. 108 112 pub fn no_did_found(authority: impl Into<SmolStr>) -> Self { 109 113 Self::new( 110 114 LexiconResolutionErrorKind::NoDIDFound { ··· 114 118 ) 115 119 } 116 120 121 + /// Create an error for a syntactically invalid DID found in DNS for the given authority. 117 122 pub fn invalid_did(authority: impl Into<SmolStr>, value: impl Into<SmolStr>) -> Self { 118 123 Self::new( 119 124 LexiconResolutionErrorKind::InvalidDID { ··· 124 129 ) 125 130 } 126 131 132 + /// Create an error for when DNS is not available (feature disabled or WASM target). 127 133 pub fn dns_not_configured() -> Self { 128 134 Self::new(LexiconResolutionErrorKind::DnsNotConfigured, None) 129 135 } 130 136 137 + /// Create an error for a failure to fetch the lexicon record for an NSID. 131 138 pub fn fetch_failed( 132 139 nsid: impl Into<SmolStr>, 133 140 source: impl std::error::Error + Send + Sync + 'static, ··· 138 145 ) 139 146 } 140 147 148 + /// Create an error for a failure to parse a fetched lexicon schema document. 141 149 pub fn parse_failed( 142 150 nsid: impl Into<SmolStr>, 143 151 source: impl std::error::Error + Send + Sync + 'static, ··· 148 156 ) 149 157 } 150 158 159 + /// Create a generic resolution failure error with a descriptive message. 151 160 pub fn resolution_failed(nsid: impl Into<SmolStr>, message: impl Into<SmolStr>) -> Self { 152 161 Self::new( 153 162 LexiconResolutionErrorKind::ResolutionFailed { ··· 158 167 ) 159 168 } 160 169 170 + /// Create an error for a non-success HTTP status received while fetching a lexicon. 161 171 pub fn http_error(nsid: impl Into<SmolStr>, status: u16) -> Self { 162 172 Self::new( 163 173 LexiconResolutionErrorKind::HttpError { ··· 168 178 ) 169 179 } 170 180 181 + /// Create an error for a required field missing from the XRPC response. 171 182 pub fn missing_response_field(nsid: impl Into<SmolStr>, field: &'static str) -> Self { 172 183 Self::new( 173 184 LexiconResolutionErrorKind::MissingResponseField { ··· 178 189 ) 179 190 } 180 191 192 + /// Create an error for an invalid lexicon collection NSID. 181 193 pub fn invalid_collection() -> Self { 182 194 Self::new(LexiconResolutionErrorKind::InvalidCollection, None) 183 195 } 184 196 197 + /// Create an error for a lexicon record response that is missing its CID. 185 198 pub fn missing_cid(nsid: impl Into<SmolStr>) -> Self { 186 199 Self::new( 187 200 LexiconResolutionErrorKind::MissingCID { nsid: nsid.into() }, ··· 200 213 #[derive(Debug, thiserror::Error, miette::Diagnostic)] 201 214 #[non_exhaustive] 202 215 pub enum LexiconResolutionErrorKind { 216 + /// DNS TXT lookup for the lexicon authority failed. 203 217 #[error("DNS lookup failed for authority {authority}")] 204 218 #[diagnostic(code(jacquard::lexicon::dns_lookup_failed))] 205 - DnsLookupFailed { authority: SmolStr }, 219 + DnsLookupFailed { 220 + /// The NSID authority segment that was being looked up. 221 + authority: SmolStr, 222 + }, 206 223 224 + /// DNS records were reachable but contained no `did=...` entry. 207 225 #[error("no DID found in DNS for authority {authority}")] 208 226 #[diagnostic( 209 227 code(jacquard::lexicon::no_did_found), 210 228 help("ensure _lexicon.{{reversed-authority}} TXT record exists with did=...") 211 229 )] 212 - NoDIDFound { authority: SmolStr }, 230 + NoDIDFound { 231 + /// The NSID authority segment that was being looked up. 232 + authority: SmolStr, 233 + }, 213 234 235 + /// DNS returned a `did=...` entry but its value is not a valid DID. 214 236 #[error("invalid DID in DNS for authority {authority}: {value}")] 215 237 #[diagnostic(code(jacquard::lexicon::invalid_did))] 216 - InvalidDID { authority: SmolStr, value: SmolStr }, 238 + InvalidDID { 239 + /// The NSID authority segment. 240 + authority: SmolStr, 241 + /// The raw invalid DID string found in DNS. 242 + value: SmolStr, 243 + }, 217 244 245 + /// DNS is not available on this build (the `dns` feature is disabled or target is WASM). 218 246 #[error("DNS not configured (dns feature disabled or WASM target)")] 219 247 #[diagnostic( 220 248 code(jacquard::lexicon::dns_not_configured), ··· 222 250 )] 223 251 DnsNotConfigured, 224 252 253 + /// XRPC or HTTP request to fetch the lexicon record failed. 225 254 #[error("failed to fetch lexicon record for {nsid}")] 226 255 #[diagnostic(code(jacquard::lexicon::fetch_failed))] 227 - FetchFailed { nsid: SmolStr }, 256 + FetchFailed { 257 + /// The NSID of the lexicon that could not be fetched. 258 + nsid: SmolStr, 259 + }, 228 260 261 + /// The fetched lexicon record could not be deserialized as a `LexiconDoc`. 229 262 #[error("failed to parse lexicon schema for {nsid}")] 230 263 #[diagnostic(code(jacquard::lexicon::parse_failed))] 231 - ParseFailed { nsid: SmolStr }, 264 + ParseFailed { 265 + /// The NSID of the lexicon that could not be parsed. 266 + nsid: SmolStr, 267 + }, 232 268 269 + /// Generic resolution failure with a descriptive message. 233 270 #[error("failed to parse lexicon schema for {nsid}")] 234 271 #[diagnostic(code(jacquard::lexicon::resolution_failed))] 235 - ResolutionFailed { nsid: SmolStr, message: SmolStr }, 272 + ResolutionFailed { 273 + /// The NSID of the lexicon being resolved. 274 + nsid: SmolStr, 275 + /// Human-readable description of what went wrong. 276 + message: SmolStr, 277 + }, 236 278 237 - /// HTTP non-success status from lexicon fetch 279 + /// HTTP non-success status from lexicon fetch. 238 280 #[error("HTTP {status} fetching lexicon {nsid}")] 239 281 #[diagnostic(code(jacquard::lexicon::http_error))] 240 - HttpError { nsid: SmolStr, status: u16 }, 282 + HttpError { 283 + /// The NSID of the lexicon being fetched. 284 + nsid: SmolStr, 285 + /// The HTTP status code received. 286 + status: u16, 287 + }, 241 288 242 - /// Required field missing in XRPC response 289 + /// Required field missing in XRPC response. 243 290 #[error("missing '{field}' field in response for {nsid}")] 244 291 #[diagnostic( 245 292 code(jacquard::lexicon::missing_response_field), 246 293 help("the XRPC response is missing a required field") 247 294 )] 248 - MissingResponseField { nsid: SmolStr, field: &'static str }, 295 + MissingResponseField { 296 + /// The NSID of the lexicon being fetched. 297 + nsid: SmolStr, 298 + /// Name of the missing field. 299 + field: &'static str, 300 + }, 249 301 302 + /// The lexicon collection NSID was not valid. 250 303 #[error("invalid collection NSID")] 251 304 #[diagnostic(code(jacquard::lexicon::invalid_collection))] 252 305 InvalidCollection, 253 306 307 + /// The `getRecord` response did not include a CID for the lexicon record. 254 308 #[error("record missing CID for {nsid}")] 255 309 #[diagnostic(code(jacquard::lexicon::missing_cid))] 256 - MissingCID { nsid: SmolStr }, 310 + MissingCID { 311 + /// The NSID of the lexicon whose record was missing a CID. 312 + nsid: SmolStr, 313 + }, 257 314 315 + /// Identity resolution failed while locating the PDS that hosts the lexicon. 258 316 #[error(transparent)] 259 317 #[diagnostic(code(jacquard::lexicon::identity_resolution_failed))] 260 318 IdentityResolution(#[from] crate::resolver::IdentityError),
+10 -6
crates/jacquard-identity/src/lib.rs
··· 65 65 //! 66 66 //! Both support `.parse()` for borrowing and validation. 67 67 68 - // use crate::CowStr; // not currently needed directly here 69 - 68 + #![warn(missing_docs)] 70 69 #![cfg_attr(target_arch = "wasm32", allow(unused))] 71 70 pub mod lexicon_resolver; 72 71 pub mod resolver; ··· 284 283 #[cfg(feature = "cache")] 285 284 #[derive(Clone)] 286 285 pub struct ResolverCaches { 286 + /// Cache mapping handles to their resolved DIDs. 287 287 pub handle_to_did: cache_impl::Cache<Handle<'static>, Did<'static>>, 288 + /// Cache mapping DIDs to their full DID documents. 288 289 pub did_to_doc: cache_impl::Cache<Did<'static>, Arc<DidDocResponse>>, 290 + /// Cache mapping authority strings (e.g., PDS hosts) to DIDs. 289 291 pub authority_to_did: cache_impl::Cache<SmolStr, Did<'static>>, 292 + /// Cache mapping NSIDs to their resolved lexicon schemas. 290 293 pub nsid_to_schema: cache_impl::Cache<Nsid<'static>, Arc<ResolvedLexiconSchema<'static>>>, 291 294 } 292 295 293 296 #[cfg(feature = "cache")] 294 297 impl ResolverCaches { 298 + /// Creates a new set of resolver caches from the given configuration. 295 299 pub fn new(config: &CacheConfig) -> Self { 296 300 Self { 297 301 handle_to_did: cache_impl::new_cache( ··· 1182 1186 /// Resolver specialized for unauthenticated/public flows using reqwest and stateless XRPC 1183 1187 pub type PublicResolver = JacquardResolver; 1184 1188 1185 - impl Default for PublicResolver { 1189 + impl Default for JacquardResolver { 1186 1190 /// Build a resolver with: 1187 1191 /// - reqwest HTTP client 1188 1192 /// - Public fallbacks enabled for handle resolution ··· 1190 1194 /// 1191 1195 /// Example 1192 1196 /// ```ignore 1193 - /// use jacquard::identity::resolver::PublicResolver; 1194 - /// let resolver = PublicResolver::default(); 1197 + /// use jacquard::identity::resolver::JacquardResolver; 1198 + /// let resolver = JacquardResolver::default(); 1195 1199 /// ``` 1196 1200 fn default() -> Self { 1197 1201 let http = reqwest::Client::new(); ··· 1207 1211 1208 1212 /// Build a resolver configured to use Slingshot (`https://slingshot.microcosm.blue`) for PLC and 1209 1213 /// mini-doc fallbacks, unauthenticated by default. 1210 - pub fn slingshot_resolver_default() -> PublicResolver { 1214 + pub fn slingshot_resolver_default() -> JacquardResolver { 1211 1215 let http = reqwest::Client::new(); 1212 1216 let mut opts = ResolverOptions::default(); 1213 1217 opts.plc_source = PlcSource::slingshot_default();
+2
crates/jacquard-identity/src/resolver.rs
··· 661 661 help("document id differs from requested DID; do not trust this document") 662 662 )] 663 663 DocIdMismatch { 664 + /// The DID that was requested and expected to appear as the document `id`. 664 665 expected: Did<'static>, 666 + /// The DID document we *actually* got 665 667 doc: DidDocument<'static>, 666 668 }, 667 669 }
+66 -5
crates/jacquard-oauth/src/atproto.rs
··· 7 7 use smol_str::{SmolStr, ToSmolStr}; 8 8 use thiserror::Error; 9 9 10 + /// Errors that can occur when building AT Protocol OAuth client metadata. 10 11 #[derive(Error, Debug)] 11 12 #[non_exhaustive] 12 13 pub enum Error { 14 + /// The `client_id` is not a valid URL. 13 15 #[error("`client_id` must be a valid URL")] 14 16 InvalidClientId, 17 + /// The `grant_types` list does not include `authorization_code`, which is required by atproto. 15 18 #[error("`grant_types` must include `authorization_code`")] 16 19 InvalidGrantTypes, 20 + /// The `scope` list does not include `atproto`, which is required for all atproto clients. 17 21 #[error("`scope` must not include `atproto`")] 18 22 InvalidScope, 23 + /// No redirect URIs were provided; at least one is required. 19 24 #[error("`redirect_uris` must not be empty")] 20 25 EmptyRedirectUris, 26 + /// The `private_key_jwt` auth method was requested but no JWK keys were provided. 21 27 #[error("`private_key_jwt` auth method requires `jwks` keys")] 22 28 EmptyJwks, 29 + /// Signing algorithm mismatch: `private_key_jwt` requires `token_endpoint_auth_signing_alg`, 30 + /// and non-`private_key_jwt` methods must not provide it. 23 31 #[error( 24 32 "`private_key_jwt` auth method requires `token_endpoint_auth_signing_alg`, otherwise must not be provided" 25 33 )] 26 34 AuthSigningAlg, 35 + /// HTML form serialization of the loopback `client_id` query string failed. 27 36 #[error(transparent)] 28 37 SerdeHtmlForm(#[from] serde_html_form::ser::Error), 38 + /// A localhost-specific validation error occurred. 29 39 #[error(transparent)] 30 40 LocalhostClient(#[from] LocalhostClientError), 31 41 } 32 42 43 + /// Errors specific to validating a loopback (localhost) OAuth client's redirect URIs. 44 + /// 45 + /// The AT Protocol spec has specific requirements for loopback clients: redirect URIs must 46 + /// use the `http` scheme and must point to actual loopback addresses (not the hostname `localhost`). 33 47 #[derive(Error, Debug)] 34 48 #[non_exhaustive] 35 49 pub enum LocalhostClientError { 50 + /// The redirect URI could not be parsed. 36 51 #[error("invalid redirect_uri: {0}")] 37 52 Invalid(#[from] jacquard_common::deps::fluent_uri::ParseError), 53 + /// Loopback redirect URIs must use `http:`, not `https:` or any other scheme. 38 54 #[error("loopback client_id must use `http:` redirect_uri")] 39 55 NotHttpScheme, 56 + /// The hostname `localhost` is not allowed; use a numeric loopback address instead. 40 57 #[error("loopback client_id must not use `localhost` as redirect_uri hostname")] 41 58 Localhost, 59 + /// The redirect URI host is not a loopback address (127.x.x.x or ::1). 42 60 #[error("loopback client_id must not use loopback addresses as redirect_uri")] 43 61 NotLoopbackHost, 44 62 } 45 63 64 + /// Convenience result type for AT Protocol client metadata operations. 46 65 pub type Result<T> = core::result::Result<T, Error>; 47 66 67 + /// The token endpoint authentication method for an OAuth client. 68 + /// 69 + /// AT Protocol clients either authenticate with no client secret (public/loopback clients) 70 + /// or with a private key JWT signed by a key from the client's JWK set. 48 71 #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] 49 72 #[serde(rename_all = "snake_case")] 50 73 pub enum AuthMethod { 74 + /// No client authentication; used for public and loopback clients. 51 75 None, 52 - // https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication 76 + /// Authenticate using a JWT signed with a private key from the client's JWK set. 77 + /// <https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication> 53 78 PrivateKeyJwt, 54 79 } 55 80 ··· 62 87 } 63 88 } 64 89 90 + /// OAuth 2.0 grant types supported by AT Protocol clients. 65 91 #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] 66 92 #[serde(rename_all = "snake_case")] 67 93 pub enum GrantType { 94 + /// Standard authorization code grant, required by atproto. 68 95 AuthorizationCode, 96 + /// Refresh token grant, used to obtain new access tokens without re-authorization. 69 97 RefreshToken, 70 98 } 71 99 ··· 78 106 } 79 107 } 80 108 109 + /// AT Protocol-specific OAuth client metadata, used to describe a client before converting to 110 + /// the generic [`OAuthClientMetadata`] format for server registration. 111 + /// 112 + /// This type provides a validated, atproto-aware view of client registration data, with 113 + /// typed fields for URIs and scopes rather than raw strings. Use [`atproto_client_metadata`] 114 + /// to convert this into the wire format expected by OAuth servers. 81 115 #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] 82 116 pub struct AtprotoClientMetadata<'m> { 117 + /// The unique identifier for this client, typically the URL of its metadata document. 83 118 pub client_id: Uri<String>, 119 + /// The URI of the client's homepage or information page. 84 120 pub client_uri: Option<Uri<String>>, 121 + /// The list of allowed redirect URIs for the authorization code flow. 85 122 pub redirect_uris: Vec<Uri<String>>, 123 + /// The grant types this client will use. 86 124 pub grant_types: Vec<GrantType>, 125 + /// The OAuth scopes this client requests; must include `atproto`. 87 126 #[serde(borrow)] 88 127 pub scopes: Vec<Scope<'m>>, 128 + /// URI pointing to the client's JWK Set; mutually exclusive with inline `jwks`. 89 129 pub jwks_uri: Option<Uri<String>>, 130 + /// Human-readable display name for the client. 90 131 pub client_name: Option<SmolStr>, 132 + /// URI of the client's logo image. 91 133 pub logo_uri: Option<Uri<String>>, 134 + /// URI of the client's terms of service document. 92 135 pub tos_uri: Option<Uri<String>>, 136 + /// URI of the client's privacy policy document. 93 137 pub privacy_policy_uri: Option<Uri<String>>, 94 138 } 95 139 ··· 112 156 } 113 157 114 158 impl<'m> AtprotoClientMetadata<'m> { 159 + /// Attach optional production branding fields to the metadata. 160 + /// 161 + /// Chainable builder method for setting display name, logo, and policy URLs after 162 + /// constructing the base metadata. 115 163 pub fn with_prod_info( 116 164 mut self, 117 165 client_name: &str, ··· 126 174 self 127 175 } 128 176 177 + /// Create a default loopback client metadata with the `atproto` and `transition:generic` scopes. 178 + /// 179 + /// This is a convenience constructor for local development and CLI tools. The resulting 180 + /// metadata uses `http://localhost` as the `client_id` with both IPv4 and IPv6 loopback 181 + /// redirect URIs. 129 182 pub fn default_localhost() -> Self { 130 183 Self::new_localhost( 131 184 None, ··· 133 186 ) 134 187 } 135 188 189 + /// Create loopback client metadata with optional custom redirect URIs and scopes. 190 + /// 191 + /// Encodes non-default redirect URIs and scopes into the `client_id` query string as 192 + /// required by the AT Protocol loopback client specification. When `redirect_uris` or 193 + /// `scopes` are `None`, sensible defaults (IPv4 + IPv6 loopback addresses, `atproto` scope) 194 + /// are used. 136 195 pub fn new_localhost( 137 196 redirect_uris: Option<Vec<Uri<String>>>, 138 197 scopes: Option<Vec<Scope<'static>>>, ··· 181 240 } 182 241 } 183 242 243 + /// Convert [`AtprotoClientMetadata`] into the [`OAuthClientMetadata`] wire format. 244 + /// 245 + /// Validates all atproto-specific constraints (required scopes, grant types, redirect URIs), 246 + /// selects the appropriate `token_endpoint_auth_method` based on whether a keyset is provided, 247 + /// and serializes scopes and grant types into their string representations. Returns an error 248 + /// if any required field is missing or invalid. 184 249 pub fn atproto_client_metadata<'m>( 185 250 metadata: AtprotoClientMetadata<'m>, 186 251 keyset: &Option<Keyset>, 187 252 ) -> Result<OAuthClientMetadata<'static>> { 188 - // For non-loopback clients, require a keyset/JWKs. 189 253 let is_loopback = metadata.client_id.scheme().as_str() == "http" 190 254 && metadata.client_id.authority().map(|a| a.host()) == Some("localhost"); 191 255 let application_type = if is_loopback { ··· 193 257 } else { 194 258 Some(CowStr::new_static("web")) 195 259 }; 196 - // if !is_loopback && keyset.is_none() { 197 - // return Err(Error::EmptyJwks); 198 - // } 199 260 if metadata.redirect_uris.is_empty() { 200 261 return Err(Error::EmptyRedirectUris); 201 262 }
+15
crates/jacquard-oauth/src/authstore.rs
··· 11 11 12 12 use crate::session::{AuthRequestData, ClientSessionData}; 13 13 14 + /// Persistent storage backend for OAuth client sessions and in-flight authorization requests. 15 + /// 16 + /// Implementors are responsible for durably storing two categories of data: 17 + /// - Active client sessions (access tokens, refresh tokens, nonces) keyed by DID + session ID. 18 + /// - Pending authorization request state, keyed by the OAuth `state` parameter, which must 19 + /// survive the round-trip to the authorization server and be cleaned up after use. 14 20 #[cfg_attr(not(target_arch = "wasm32"), trait_variant::make(Send))] 15 21 pub trait ClientAuthStore { 22 + /// Retrieve an active session for the given DID and session identifier, if one exists. 16 23 fn get_session( 17 24 &self, 18 25 did: &Did<'_>, 19 26 session_id: &str, 20 27 ) -> impl Future<Output = Result<Option<ClientSessionData<'_>>, SessionStoreError>>; 21 28 29 + /// Insert or update a session, replacing any existing entry for the same DID and session ID. 22 30 fn upsert_session( 23 31 &self, 24 32 session: ClientSessionData<'_>, 25 33 ) -> impl Future<Output = Result<(), SessionStoreError>>; 26 34 35 + /// Delete the session for the given DID and session identifier. 27 36 fn delete_session( 28 37 &self, 29 38 did: &Did<'_>, 30 39 session_id: &str, 31 40 ) -> impl Future<Output = Result<(), SessionStoreError>>; 32 41 42 + /// Retrieve the authorization request data associated with the given OAuth `state` value. 33 43 fn get_auth_req_info( 34 44 &self, 35 45 state: &str, 36 46 ) -> impl Future<Output = Result<Option<AuthRequestData<'_>>, SessionStoreError>>; 37 47 48 + /// Persist authorization request data so it can be retrieved after the OAuth redirect. 38 49 fn save_auth_req_info( 39 50 &self, 40 51 auth_req_info: &AuthRequestData<'_>, 41 52 ) -> impl Future<Output = Result<(), SessionStoreError>>; 42 53 54 + /// Remove authorization request data after the callback has been handled. 43 55 fn delete_auth_req_info( 44 56 &self, 45 57 state: &str, 46 58 ) -> impl Future<Output = Result<(), SessionStoreError>>; 47 59 } 48 60 61 + /// An in-memory implementation of [`ClientAuthStore`], suitable for testing and single-process 62 + /// deployments where session persistence across restarts is not required. 49 63 pub struct MemoryAuthStore { 50 64 sessions: DashMap<SmolStr, ClientSessionData<'static>>, 51 65 auth_reqs: DashMap<SmolStr, AuthRequestData<'static>>, 52 66 } 53 67 54 68 impl MemoryAuthStore { 69 + /// Create a new, empty in-memory auth store. 55 70 pub fn new() -> Self { 56 71 Self { 57 72 sessions: DashMap::new(),
+88
crates/jacquard-oauth/src/client.rs
··· 34 34 use std::{future::Future, sync::Arc}; 35 35 use tokio::sync::RwLock; 36 36 37 + /// The top-level OAuth client responsible for driving the authorization flow. 37 38 pub struct OAuthClient<T, S> 38 39 where 39 40 T: OAuthResolver, 40 41 S: ClientAuthStore, 41 42 { 43 + /// Shared session registry that mediates access to the backing auth store. 42 44 pub registry: Arc<SessionRegistry<T, S>>, 45 + /// Default call options applied to every outgoing XRPC request. 43 46 pub options: RwLock<CallOptions<'static>>, 47 + /// Override for the XRPC base URI; falls back to the public Bluesky AppView when `None`. 44 48 pub endpoint: RwLock<Option<Uri<String>>>, 49 + /// Underlying HTTP/identity/OAuth resolver used for all network operations. 45 50 pub client: Arc<T>, 46 51 } 47 52 48 53 impl<S: ClientAuthStore> OAuthClient<JacquardResolver, S> { 54 + /// Create an `OAuthClient` using the default [`JacquardResolver`] for identity and metadata resolution. 49 55 pub fn new(store: S, client_data: ClientData<'static>) -> Self { 50 56 let client = JacquardResolver::default(); 51 57 Self::new_from_resolver(store, client, client_data) ··· 103 109 T: OAuthResolver, 104 110 S: ClientAuthStore, 105 111 { 112 + /// Create an OAuth client from an explicit resolver instance, taking ownership of both. 106 113 pub fn new_from_resolver(store: S, client: T, client_data: ClientData<'static>) -> Self { 107 114 // #[cfg(feature = "tracing")] 108 115 // tracing::info!( ··· 122 129 } 123 130 } 124 131 132 + /// Create an OAuth client from already-`Arc`-wrapped store and resolver. 125 133 pub fn new_with_shared( 126 134 store: Arc<S>, 127 135 client: Arc<T>, ··· 146 154 S: ClientAuthStore + Send + Sync + 'static, 147 155 T: OAuthResolver + DpopExt + Send + Sync + 'static, 148 156 { 157 + /// Return the public JWK set for this client's keyset, or an empty set if no keyset is configured. 149 158 pub fn jwks(&self) -> JwkSet { 150 159 self.registry 151 160 .client_data ··· 154 163 .map(|keyset| keyset.public_jwks()) 155 164 .unwrap_or_default() 156 165 } 166 + /// Begin an OAuth authorization flow and return the URL to which the user should be redirected. 167 + /// 168 + /// This resolves OAuth metadata for the given `input` (a handle, DID, or PDS/entryway URL), 169 + /// performs a Pushed Authorization Request (PAR) to the authorization server, persists the 170 + /// resulting state for later callback verification, and returns a fully-constructed 171 + /// authorization endpoint URL. 172 + /// 173 + /// The caller is responsible for redirecting the user's browser to the returned URL. 157 174 #[cfg_attr(feature = "tracing", tracing::instrument(level = "debug", skip(self, input), fields(input = input.as_ref())))] 158 175 pub async fn start_auth( 159 176 &self, ··· 205 222 .unwrap()) 206 223 } 207 224 225 + /// Complete the OAuth authorization flow after the authorization server redirects back to the client. 226 + /// 227 + /// Validates the `state` and optional `iss` parameters, exchanges the authorization code for 228 + /// tokens via the token endpoint, verifies the `sub` claim against the expected issuer, and 229 + /// persists the resulting session. On success returns an [`OAuthSession`] ready for API calls. 208 230 #[cfg_attr(feature = "tracing", tracing::instrument(level = "info", skip_all, fields(state = params.state.as_ref().map(|s| s.as_ref()))))] 209 231 pub async fn callback(&self, params: CallbackParams<'_>) -> Result<OAuthSession<T, S>> { 210 232 let Some(state_key) = params.state else { ··· 294 316 )) 295 317 } 296 318 319 + /// Restore a previously created session from the backing store, refreshing tokens if needed. 297 320 pub async fn restore(&self, did: &Did<'_>, session_id: &str) -> Result<OAuthSession<T, S>> { 298 321 self.create_session(self.registry.get(did, session_id, true).await?) 299 322 .await 300 323 } 301 324 325 + /// Revoke a session by deleting it from the backing store. 326 + /// 327 + /// Note: this removes the session from local storage but does **not** call the authorization 328 + /// server's revocation endpoint. To also invalidate the token server-side, prefer 329 + /// [`OAuthSession::logout`], which calls `revoke` on the token before deleting the session. 302 330 pub async fn revoke(&self, did: &Did<'_>, session_id: &str) -> Result<()> { 303 331 Ok(self.registry.del(did, session_id).await?) 304 332 } ··· 398 426 } 399 427 } 400 428 429 + /// An active OAuth session for a specific account, used to make authenticated API requests. 430 + /// 431 + /// `OAuthSession` holds the DPoP-bound token set for one account and handles transparent 432 + /// token refresh on `401 invalid_token` responses. The optional `W` type parameter allows 433 + /// attaching a WebSocket client (defaults to `()` when WebSocket support is not needed). 434 + /// 435 + /// Obtain an `OAuthSession` from [`OAuthClient::callback`] or [`OAuthClient::restore`]. 401 436 pub struct OAuthSession<T, S, W = ()> 402 437 where 403 438 T: OAuthResolver, 404 439 S: ClientAuthStore, 405 440 { 441 + /// Shared registry used to persist and retrieve session data across refresh operations. 406 442 pub registry: Arc<SessionRegistry<T, S>>, 443 + /// Underlying HTTP/identity/OAuth resolver shared with the parent `OAuthClient`. 407 444 pub client: Arc<T>, 445 + /// Optional WebSocket client; `()` when WebSocket support is not required. 408 446 pub ws_client: W, 447 + /// Mutable session data including DPoP key, nonces, and token set. 409 448 pub data: RwLock<ClientSessionData<'static>>, 449 + /// Default call options applied to every outgoing XRPC request from this session. 410 450 pub options: RwLock<CallOptions<'static>>, 411 451 } 412 452 ··· 415 455 T: OAuthResolver, 416 456 S: ClientAuthStore, 417 457 { 458 + /// Create a new session without a WebSocket client. 459 + /// 460 + /// This is the standard constructor used by [`OAuthClient::callback`] and 461 + /// [`OAuthClient::restore`]. For WebSocket support use [`OAuthSession::new_with_ws`]. 418 462 pub fn new( 419 463 registry: Arc<SessionRegistry<T, S>>, 420 464 client: Arc<T>, ··· 435 479 T: OAuthResolver, 436 480 S: ClientAuthStore, 437 481 { 482 + /// Create a new session with an attached WebSocket client. 483 + /// 484 + /// Use this variant when the session needs to support WebSocket subscriptions in addition 485 + /// to standard XRPC calls. The `ws_client` is exposed via [`OAuthSession::ws_client`] and 486 + /// is used by the `WebSocketClient` impl when the `websocket` feature is enabled. 438 487 pub fn new_with_ws( 439 488 registry: Arc<SessionRegistry<T, S>>, 440 489 client: Arc<T>, ··· 450 499 } 451 500 } 452 501 502 + /// Consume this session and return a new one with the given call options pre-applied. 503 + /// 504 + /// Useful for setting request-level defaults (e.g., `atproto-proxy` or custom headers) once 505 + /// at construction time rather than passing them to every individual XRPC call. 453 506 pub fn with_options(self, options: CallOptions<'_>) -> Self { 454 507 Self { 455 508 registry: self.registry, ··· 465 518 &self.ws_client 466 519 } 467 520 521 + /// Replace the default call options for this session without consuming it. 468 522 pub async fn set_options(&self, options: CallOptions<'_>) { 469 523 *self.options.write().await = options.into_static(); 470 524 } 471 525 526 + /// Return the DID and session ID for this session. 527 + /// 528 + /// The session ID is the random `state` token generated during the PAR flow and can 529 + /// be used together with the DID to restore the session via [`OAuthClient::restore`]. 472 530 pub async fn session_info(&self) -> (Did<'_>, CowStr<'_>) { 473 531 let data = self.data.read().await; 474 532 (data.account_did.clone(), data.session_id.clone()) 475 533 } 476 534 535 + /// Return the resource server (PDS) base URI for this session. 477 536 pub async fn endpoint(&self) -> Uri<String> { 478 537 self.data.read().await.host_url.clone() 479 538 } 480 539 540 + /// Return the current DPoP-bound access token for this session. 541 + /// 542 + /// The token may be stale if it has expired; use [`OAuthSession::refresh`] or 543 + /// rely on the automatic refresh performed by `send_with_opts` to obtain a fresh one. 481 544 pub async fn access_token(&self) -> AuthorizationToken<'_> { 482 545 AuthorizationToken::Dpop(self.data.read().await.token_set.access_token.clone()) 483 546 } 484 547 548 + /// Return the current refresh token for this session, if one is present. 549 + /// 550 + /// Not all authorization servers issue refresh tokens. When `None` is returned, 551 + /// the session cannot be silently renewed and the user must re-authenticate. 485 552 pub async fn refresh_token(&self) -> Option<AuthorizationToken<'_>> { 486 553 self.data 487 554 .read() ··· 492 559 .map(|t| AuthorizationToken::Dpop(t.clone())) 493 560 } 494 561 562 + /// Derive an unauthenticated [`OAuthClient`] that shares the same registry and resolver. 563 + /// 564 + /// Useful when you need to initiate a new authorization flow from within an existing 565 + /// session context (e.g., to add a second account) without constructing a fresh client. 495 566 pub fn to_client(&self) -> OAuthClient<T, S> { 496 567 OAuthClient::from_session(self) 497 568 } ··· 501 572 S: ClientAuthStore + Send + Sync + 'static, 502 573 T: OAuthResolver + DpopExt + Send + Sync + 'static, 503 574 { 575 + /// Revoke the access token at the authorization server and delete the session from the store. 576 + /// 577 + /// Revocation is best-effort: if the server does not advertise a revocation endpoint, or if 578 + /// the revocation call fails, the session is still deleted locally. This prevents a dangling 579 + /// session record from blocking future logins for the same account. 504 580 pub async fn logout(&self) -> Result<()> { 505 581 use crate::request::{OAuthMetadata, revoke}; 506 582 let mut data = self.data.write().await; ··· 525 601 T: OAuthResolver, 526 602 S: ClientAuthStore, 527 603 { 604 + /// Construct an `OAuthClient` that shares the registry and resolver of an existing session. 605 + /// 606 + /// Equivalent to [`OAuthSession::to_client`]; provided on `OAuthClient` for symmetry so 607 + /// callers can obtain an unauthenticated client without holding a session reference. 528 608 pub fn from_session<W>(session: &OAuthSession<T, S, W>) -> Self { 529 609 Self { 530 610 registry: session.registry.clone(), ··· 539 619 S: ClientAuthStore + Send + Sync + 'static, 540 620 T: OAuthResolver + DpopExt + Send + Sync + 'static, 541 621 { 622 + /// Explicitly refresh the access token using the stored refresh token. 623 + /// 624 + /// On success the new token set is written back into both the in-memory session data and 625 + /// the backing store. The returned `AuthorizationToken` is the new access token, which 626 + /// callers can immediately use to retry a failed request. 627 + /// 628 + /// The actual token exchange is serialized per `(DID, session_id)` pair via a `Mutex` inside 629 + /// the registry, so concurrent refresh attempts will not result in duplicate token exchanges. 542 630 #[cfg_attr(feature = "tracing", tracing::instrument(level = "debug", skip_all))] 543 631 pub async fn refresh(&self) -> Result<AuthorizationToken<'_>> { 544 632 // Read identifiers without holding the lock across await
+39
crates/jacquard-oauth/src/dpop.rs
··· 23 23 session::DpopDataSource, 24 24 }; 25 25 26 + /// The `typ` header value required in all DPoP proof JWTs, per RFC 9449. 26 27 pub const JWT_HEADER_TYP_DPOP: &str = "dpop+jwt"; 27 28 28 29 #[derive(serde::Deserialize)] ··· 332 333 333 334 type Result<T> = core::result::Result<T, DpopError>; 334 335 336 + /// An HTTP client capable of making DPoP-protected requests to both auth servers and resource servers. 337 + /// 338 + /// Implementors must be able to attach a DPoP proof header, handle nonce challenges, and 339 + /// retry transparently on `use_dpop_nonce` errors. 335 340 #[cfg_attr(not(target_arch = "wasm32"), trait_variant::make(Send))] 336 341 pub trait DpopClient: HttpClient { 342 + /// Send a DPoP-protected request to an authorization server (token endpoint, PAR, etc.). 337 343 fn dpop_server( 338 344 &self, 339 345 request: Request<Vec<u8>>, 340 346 ) -> impl Future<Output = Result<Response<Vec<u8>>>>; 347 + /// Send a DPoP-protected request to a resource server (PDS, AppView, etc.). 341 348 fn dpop_client( 342 349 &self, 343 350 request: Request<Vec<u8>>, 344 351 ) -> impl Future<Output = Result<Response<Vec<u8>>>>; 352 + /// Send a DPoP-protected request, inferring the target type from the request context. 345 353 fn wrap_request( 346 354 &self, 347 355 request: Request<Vec<u8>>, 348 356 ) -> impl Future<Output = Result<Response<Vec<u8>>>>; 349 357 } 350 358 359 + /// Extension trait for any [`HttpClient`] that adds builder methods for constructing 360 + /// DPoP-protected request calls without requiring a full [`DpopClient`] implementation. 351 361 pub trait DpopExt: HttpClient { 362 + /// Begin building a DPoP-protected request targeting an authorization server. 352 363 fn dpop_server_call<'r, D>(&'r self, data_source: &'r mut D) -> DpopCall<'r, Self, D> 353 364 where 354 365 Self: Sized, ··· 357 368 DpopCall::server(self, data_source) 358 369 } 359 370 371 + /// Begin building a DPoP-protected request targeting a resource server. 360 372 fn dpop_call<'r, N>(&'r self, data_source: &'r mut N) -> DpopCall<'r, Self, N> 361 373 where 362 374 Self: Sized, ··· 366 378 } 367 379 } 368 380 381 + /// A builder for a single DPoP-protected HTTP request, holding references to the underlying 382 + /// client and the session data source that supplies nonces and the DPoP signing key. 369 383 pub struct DpopCall<'r, C: HttpClient, D: DpopDataSource> { 384 + /// The HTTP client that will send the request. 370 385 pub client: &'r C, 386 + /// Whether the request targets an authorization server rather than a resource server. 387 + /// 388 + /// This controls which nonce slot is read from and written to, and how `use_dpop_nonce` 389 + /// errors are detected in the response. 371 390 pub is_to_auth_server: bool, 391 + /// The session data source providing the DPoP key and current nonces. 372 392 pub data_source: &'r mut D, 373 393 } 374 394 375 395 impl<'r, C: HttpClient, N: DpopDataSource> DpopCall<'r, C, N> { 396 + /// Create a call builder targeting an authorization server. 376 397 pub fn server(client: &'r C, data_source: &'r mut N) -> Self { 377 398 Self { 378 399 client, ··· 381 402 } 382 403 } 383 404 405 + /// Create a call builder targeting a resource server. 384 406 pub fn client(client: &'r C, data_source: &'r mut N) -> Self { 385 407 Self { 386 408 client, ··· 389 411 } 390 412 } 391 413 414 + /// Send the request with a DPoP proof, retrying once if the server provides a new nonce. 392 415 pub async fn send(self, request: Request<Vec<u8>>) -> Result<Response<Vec<u8>>> { 393 416 wrap_request_with_dpop( 394 417 self.client, ··· 399 422 .await 400 423 } 401 424 425 + /// Sends the request with DPoP proof and returns a streaming response. 402 426 #[cfg(feature = "streaming")] 403 427 pub async fn send_streaming( 404 428 self, ··· 416 440 .await 417 441 } 418 442 443 + /// Sends the request with DPoP proof using bidirectional streaming. 419 444 #[cfg(feature = "streaming")] 420 445 pub async fn send_bidirectional( 421 446 self, ··· 470 495 } 471 496 } 472 497 498 + /// Attach a DPoP proof to `request`, send it, and transparently retry once if the server 499 + /// responds with a `use_dpop_nonce` error and a fresh nonce. 500 + /// 501 + /// The nonce is read from and written back to `data_source` based on `is_to_auth_server`, 502 + /// keeping the two nonce slots (auth server vs. resource server) independent. 473 503 pub async fn wrap_request_with_dpop<T, N>( 474 504 client: &T, 475 505 data_source: &mut N, ··· 531 561 Ok(response) 532 562 } 533 563 564 + /// Wraps an HTTP request with a DPoP proof and returns a streaming response. 565 + /// 566 + /// Like [`wrap_request_with_dpop`], but returns a [`StreamingResponse`](jacquard_common::xrpc::StreamingResponse) 567 + /// instead of buffering the body. Nonce retry is limited to status/header inspection 568 + /// since the body stream cannot be rewound. 534 569 #[cfg(feature = "streaming")] 535 570 pub async fn wrap_request_with_dpop_streaming<T, N>( 536 571 client: &T, ··· 600 635 Ok(StreamingResponse::new(parts, body)) 601 636 } 602 637 638 + /// Wraps an HTTP request with a DPoP proof using bidirectional streaming. 639 + /// 640 + /// Similar to [`wrap_request_with_dpop_streaming`] but accepts a [`ByteStream`](jacquard_common::stream::ByteStream) 641 + /// request body for upload streaming scenarios. 603 642 #[cfg(feature = "streaming")] 604 643 pub async fn wrap_request_with_dpop_bidirectional<T, N>( 605 644 client: &T,
+27 -1
crates/jacquard-oauth/src/error.rs
··· 8 8 #[derive(Debug, thiserror::Error, Diagnostic)] 9 9 #[non_exhaustive] 10 10 pub enum OAuthError { 11 + /// An error occurred during identity or metadata resolution. 11 12 #[error(transparent)] 12 13 #[diagnostic(code(jacquard_oauth::resolver))] 13 14 Resolver(#[from] ResolverError), 14 15 16 + /// An error occurred while making an OAuth HTTP request. 15 17 #[error(transparent)] 16 18 #[diagnostic(code(jacquard_oauth::request))] 17 19 Request(#[from] RequestError), 18 20 21 + /// An error occurred reading or writing session state. 19 22 #[error(transparent)] 20 23 #[diagnostic(code(jacquard_oauth::storage))] 21 24 Storage(#[from] SessionStoreError), 22 25 26 + /// An error occurred during DPoP proof generation or validation. 23 27 #[error(transparent)] 24 28 #[diagnostic(code(jacquard_oauth::dpop))] 25 29 Dpop(#[from] crate::dpop::DpopError), 26 30 31 + /// An error occurred with the client's key set. 27 32 #[error(transparent)] 28 33 #[diagnostic(code(jacquard_oauth::keyset))] 29 34 Keyset(#[from] crate::keyset::Error), 30 35 36 + /// An ATProto-specific OAuth error (e.g. scope validation, client ID). 31 37 #[error(transparent)] 32 38 #[diagnostic(code(jacquard_oauth::atproto))] 33 39 Atproto(#[from] crate::atproto::Error), 34 40 41 + /// An error occurred managing or refreshing an OAuth session. 35 42 #[error(transparent)] 36 43 #[diagnostic(code(jacquard_oauth::session))] 37 44 Session(#[from] crate::session::Error), 38 45 46 + /// A JSON serialization or deserialization error. 39 47 #[error(transparent)] 40 48 #[diagnostic(code(jacquard_oauth::serde_json))] 41 49 SerdeJson(#[from] serde_json::Error), 42 50 51 + /// A URI parse error. 43 52 #[error(transparent)] 44 53 #[diagnostic(code(jacquard_oauth::url))] 45 54 Url(#[from] jacquard_common::deps::fluent_uri::ParseError), 46 55 56 + /// A form (URL-encoded) serialization error. 47 57 #[error(transparent)] 48 58 #[diagnostic(code(jacquard_oauth::form))] 49 59 Form(#[from] serde_html_form::ser::Error), 50 60 61 + /// An error validating an authorization callback. 51 62 #[error(transparent)] 52 63 #[diagnostic(code(jacquard_oauth::callback))] 53 64 Callback(#[from] CallbackError), ··· 57 68 #[derive(Debug, thiserror::Error, Diagnostic)] 58 69 #[non_exhaustive] 59 70 pub enum CallbackError { 71 + /// The `state` parameter was absent from the authorization callback. 72 + /// 73 + /// State is required to prevent CSRF attacks per RFC 6749 §10.12. 60 74 #[error("missing state parameter in callback")] 61 75 #[diagnostic(code(jacquard_oauth::callback::missing_state))] 62 76 MissingState, 77 + /// The `iss` (issuer) parameter was absent from the authorization callback. 78 + /// 79 + /// RFC 9207 requires `iss` to be present so that clients can reject 80 + /// mix-up attacks from malicious authorization servers. 63 81 #[error("missing `iss` parameter")] 64 82 #[diagnostic(code(jacquard_oauth::callback::missing_iss))] 65 83 MissingIssuer, 84 + /// The issuer in the callback did not match the expected authorization server. 66 85 #[error("issuer mismatch: expected {expected}, got {got}")] 67 86 #[diagnostic(code(jacquard_oauth::callback::issuer_mismatch))] 68 - IssuerMismatch { expected: String, got: String }, 87 + IssuerMismatch { 88 + /// The issuer that was expected. 89 + expected: String, 90 + /// The issuer that was actually present in the callback. 91 + got: String, 92 + }, 93 + /// The authorization request timed out before a callback was received. 69 94 #[error("timeout")] 70 95 #[diagnostic(code(jacquard_oauth::callback::timeout))] 71 96 Timeout, 72 97 } 73 98 99 + /// Convenience alias for `Result<T, OAuthError>`. 74 100 pub type Result<T> = core::result::Result<T, OAuthError>;
+7
crates/jacquard-oauth/src/jose.rs
··· 1 + /// JWS (JSON Web Signature) header types. 1 2 pub mod jws; 3 + /// JWT (JSON Web Token) claims types. 2 4 pub mod jwt; 5 + /// Signed JWT creation using ES256 keys. 3 6 pub mod signing; 4 7 5 8 use serde::{Deserialize, Serialize}; 6 9 10 + /// A JOSE header, covering the supported JWS formats. 11 + /// 12 + /// Serialized as an untagged enum so the wire format matches the relevant JOSE spec directly. 7 13 #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] 8 14 #[serde(untagged)] 9 15 pub enum Header<'a> { 16 + /// A JWS compact-serialization header. 10 17 #[serde(borrow)] 11 18 Jws(jws::Header<'a>), 12 19 }
+13 -1
crates/jacquard-oauth/src/jose/jws.rs
··· 3 3 use jose_jwk::Jwk; 4 4 use serde::{Deserialize, Serialize}; 5 5 6 + /// A JWS compact-serialization header, wrapping the registered header fields. 6 7 #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] 7 8 pub struct Header<'a> { 9 + /// The registered header parameters defined by the JWS specification. 8 10 #[serde(flatten)] 9 11 #[serde(borrow)] 10 12 pub registered: RegisteredHeader<'a>, ··· 16 18 } 17 19 } 18 20 21 + /// Registered JWS header parameters as defined in RFC 7515 §4.1. 19 22 #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] 20 23 21 24 pub struct RegisteredHeader<'a> { 25 + /// The cryptographic algorithm used to sign the JWS (e.g., `ES256`). 22 26 pub alg: Algorithm, 27 + /// JWK Set URL: a URI pointing to a resource containing the public key(s) used to sign the JWS. 23 28 #[serde(borrow)] 24 29 #[serde(skip_serializing_if = "Option::is_none")] 25 30 pub jku: Option<CowStr<'a>>, 31 + /// JSON Web Key: the public key used to verify the JWS, embedded directly in the header. 26 32 #[serde(skip_serializing_if = "Option::is_none")] 27 33 pub jwk: Option<Jwk>, 34 + /// Key ID: a hint indicating which key was used to sign the JWS. 28 35 #[serde(skip_serializing_if = "Option::is_none")] 29 36 pub kid: Option<CowStr<'a>>, 37 + /// X.509 URL: a URI pointing to a resource for the X.509 certificate used to sign the JWS. 30 38 #[serde(skip_serializing_if = "Option::is_none")] 31 39 pub x5u: Option<CowStr<'a>>, 40 + /// X.509 certificate chain: the certificate (and chain) corresponding to the key used to sign the JWS. 32 41 #[serde(skip_serializing_if = "Option::is_none")] 33 42 pub x5c: Option<CowStr<'a>>, 43 + /// X.509 certificate SHA-1 thumbprint: base64url-encoded SHA-1 digest of the DER-encoded certificate. 34 44 #[serde(skip_serializing_if = "Option::is_none")] 35 45 pub x5t: Option<CowStr<'a>>, 46 + /// X.509 certificate SHA-256 thumbprint: base64url-encoded SHA-256 digest of the DER-encoded certificate. 36 47 #[serde(skip_serializing_if = "Option::is_none")] 37 48 #[serde(rename = "x5t#S256")] 38 49 pub x5ts256: Option<CowStr<'a>>, 39 - 50 + /// Type: declares the media type of the complete JWS, used by applications to disambiguate among JOSe objects. 40 51 #[serde(skip_serializing_if = "Option::is_none")] 41 52 pub typ: Option<CowStr<'a>>, 53 + /// Content type: declares the media type of the secured content (the payload). 42 54 #[serde(skip_serializing_if = "Option::is_none")] 43 55 pub cty: Option<CowStr<'a>>, 44 56 }
+22
crates/jacquard-oauth/src/jose/jwt.rs
··· 1 1 use jacquard_common::{CowStr, IntoStatic}; 2 2 use serde::{Deserialize, Serialize}; 3 3 4 + /// Full JWT claims payload, combining registered and public (DPoP-specific) claims. 4 5 #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)] 5 6 pub struct Claims<'a> { 7 + /// Standard registered JWT claims (iss, sub, aud, exp, etc.). 6 8 #[serde(flatten)] 7 9 pub registered: RegisteredClaims<'a>, 10 + /// Public claims used in DPoP proofs (htm, htu, ath, nonce). 8 11 #[serde(flatten)] 9 12 #[serde(borrow)] 10 13 pub public: PublicClaims<'a>, 11 14 } 12 15 16 + /// Standard registered JWT claims as defined in RFC 7519 §4.1. 13 17 #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)] 14 18 15 19 pub struct RegisteredClaims<'a> { 20 + /// Issuer: identifies the principal that issued the JWT. 16 21 #[serde(borrow)] 17 22 #[serde(skip_serializing_if = "Option::is_none")] 18 23 pub iss: Option<CowStr<'a>>, 24 + /// Subject: identifies the principal that is the subject of the JWT. 19 25 #[serde(skip_serializing_if = "Option::is_none")] 20 26 pub sub: Option<CowStr<'a>>, 27 + /// Audience: recipients that the JWT is intended for. 21 28 #[serde(skip_serializing_if = "Option::is_none")] 22 29 pub aud: Option<RegisteredClaimsAud<'a>>, 30 + /// Expiration time (Unix timestamp): the JWT must not be accepted on or after this time. 23 31 #[serde(skip_serializing_if = "Option::is_none")] 24 32 pub exp: Option<i64>, 33 + /// Not before (Unix timestamp): the JWT must not be accepted before this time. 25 34 #[serde(skip_serializing_if = "Option::is_none")] 26 35 pub nbf: Option<i64>, 36 + /// Issued at (Unix timestamp): identifies when the JWT was created. 27 37 #[serde(skip_serializing_if = "Option::is_none")] 28 38 pub iat: Option<i64>, 39 + /// JWT ID: unique identifier for the token, used to prevent replay attacks. 29 40 #[serde(skip_serializing_if = "Option::is_none")] 30 41 pub jti: Option<CowStr<'a>>, 31 42 } 32 43 44 + /// Public claims used in DPoP proof JWTs (RFC 9449). 45 + /// 46 + /// These claims bind the DPoP proof to a specific HTTP request, preventing 47 + /// the proof from being replayed against a different endpoint or method. 33 48 #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)] 34 49 35 50 pub struct PublicClaims<'a> { 51 + /// HTTP method of the request the DPoP proof is bound to (e.g., `"POST"`). 36 52 #[serde(borrow)] 37 53 #[serde(skip_serializing_if = "Option::is_none")] 38 54 pub htm: Option<CowStr<'a>>, 55 + /// HTTP target URI of the request the DPoP proof is bound to. 39 56 #[serde(skip_serializing_if = "Option::is_none")] 40 57 pub htu: Option<CowStr<'a>>, 58 + /// Access token hash: base64url-encoded SHA-256 of the access token, binding the proof to a specific token. 41 59 #[serde(skip_serializing_if = "Option::is_none")] 42 60 pub ath: Option<CowStr<'a>>, 61 + /// Server-provided nonce, included to prevent replay attacks when required by the authorization server. 43 62 #[serde(skip_serializing_if = "Option::is_none")] 44 63 pub nonce: Option<CowStr<'a>>, 45 64 } ··· 53 72 } 54 73 } 55 74 75 + /// The `aud` (audience) claim, which may be a single string or a list of strings per RFC 7519. 56 76 #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] 57 77 #[serde(untagged)] 58 78 pub enum RegisteredClaimsAud<'a> { 79 + /// A single audience identifier. 59 80 #[serde(borrow)] 60 81 Single(CowStr<'a>), 82 + /// Multiple audience identifiers. 61 83 Multiple(Vec<CowStr<'a>>), 62 84 } 63 85
+1
crates/jacquard-oauth/src/jose/signing.rs
··· 5 5 6 6 use super::{Header, jwt::Claims}; 7 7 8 + /// Creates a compact-serialized signed JWT using an ES256 (P-256 ECDSA) key. 8 9 pub fn create_signed_jwt( 9 10 key: SigningKey, 10 11 header: Header,
+17
crates/jacquard-oauth/src/keyset.rs
··· 8 8 use std::collections::HashSet; 9 9 use thiserror::Error; 10 10 11 + /// Errors that can occur when constructing or using a [`Keyset`]. 11 12 #[derive(Error, Debug)] 12 13 #[non_exhaustive] 13 14 pub enum Error { 15 + /// Two keys in the set share the same `kid`, which would make key selection ambiguous. 14 16 #[error("duplicate kid: {0}")] 15 17 DuplicateKid(String), 18 + /// A keyset with no keys cannot sign anything. 16 19 #[error("keys must not be empty")] 17 20 EmptyKeys, 21 + /// Each key must carry a `kid` so it can be referenced in JWS headers. 18 22 #[error("key at index {0} must have a `kid`")] 19 23 EmptyKid(usize), 24 + /// No key in the set matches any of the requested signing algorithms. 20 25 #[error("no signing key found for algorithms: {0:?}")] 21 26 NotFound(Vec<CowStr<'static>>), 27 + /// Only secret (private) keys may be used for signing; a public key was provided. 22 28 #[error("key for signing must be a secret key")] 23 29 PublicKey, 30 + /// An error from the underlying JWK cryptographic operation. 24 31 #[error("crypto error: {0:?}")] 25 32 JwkCrypto(crypto::Error), 33 + /// JSON serialization of a JWT header or claims payload failed. 26 34 #[error(transparent)] 27 35 SerdeJson(#[from] serde_json::Error), 28 36 } 29 37 38 + /// Convenience result type for keyset operations. 30 39 pub type Result<T> = core::result::Result<T, Error>; 31 40 41 + /// A validated collection of JWK secret keys used for signing DPoP proofs and client assertions. 42 + /// 43 + /// Key selection follows a preference order defined in [`PREFERRED_SIGNING_ALGORITHMS`](Self::PREFERRED_SIGNING_ALGORITHMS), 44 + /// though currently only P-256 (ES256) keys are supported. 32 45 #[derive(Clone, Debug, Default, PartialEq, Eq)] 33 46 pub struct Keyset(Vec<Jwk>); 34 47 ··· 36 49 const PREFERRED_SIGNING_ALGORITHMS: [&'static str; 9] = [ 37 50 "EdDSA", "ES256K", "ES256", "PS256", "PS384", "PS512", "HS256", "HS384", "HS512", 38 51 ]; 52 + /// Returns a [`JwkSet`] containing the public halves of all keys in this keyset. 39 53 pub fn public_jwks(&self) -> JwkSet { 40 54 let mut keys = Vec::with_capacity(self.0.len()); 41 55 for mut key in self.0.clone() { ··· 49 63 } 50 64 JwkSet { keys } 51 65 } 66 + /// Signs a JWT with the best available key that matches one of the requested algorithms. 67 + /// 68 + /// Returns [`Error::NotFound`] if no key in the keyset supports any of the given algorithms. 52 69 pub fn create_jwt(&self, algs: &[CowStr], claims: Claims) -> Result<CowStr<'static>> { 53 70 let Some(jwk) = self.find_key(algs, Class::Signing) else { 54 71 return Err(Error::NotFound(algs.to_vec().into_static()));
+15
crates/jacquard-oauth/src/lib.rs
··· 47 47 //! See [`atproto`] module for AT Protocol-specific metadata helpers. 48 48 49 49 #![warn(missing_docs)] 50 + /// AT Protocol-specific OAuth client metadata helpers and builder types. 50 51 pub mod atproto; 52 + /// Storage trait and in-memory implementation for OAuth client auth state. 51 53 pub mod authstore; 54 + /// High-level OAuth client for driving the full authorization code flow. 52 55 pub mod client; 56 + /// DPoP (Demonstrating Proof-of-Possession) key generation and request signing. 53 57 pub mod dpop; 58 + /// Top-level OAuth error types for the authorization flow. 54 59 pub mod error; 60 + /// JOSE primitives: JWS headers, JWT claims, and signing utilities. 55 61 pub mod jose; 62 + /// JWK keyset management for signing keys used in DPoP and client auth. 56 63 pub mod keyset; 64 + /// Low-level OAuth request helpers: PAR, token exchange, and refresh. 57 65 pub mod request; 66 + /// OAuth server metadata resolution: authorization server and protected resource discovery. 58 67 pub mod resolver; 68 + /// 59 69 pub mod scopes; 70 + /// OAuth session types, token storage, and DPoP session state. 60 71 pub mod session; 72 + /// OAuth protocol types: client metadata, token sets, and server metadata. 61 73 pub mod types; 74 + /// Miscellaneous cryptographic utilities: key generation, PKCE, and hashing helpers. 62 75 pub mod utils; 63 76 77 + /// Fallback signing algorithm used when no preferred algorithm is negotiated with the server. 64 78 pub const FALLBACK_ALG: &str = "ES256"; 65 79 80 + /// Loopback server helpers for the local redirect-based OAuth flow. 66 81 #[cfg(feature = "loopback")] 67 82 pub mod loopback;
+13
crates/jacquard-oauth/src/loopback.rs
··· 59 59 use std::net::SocketAddr; 60 60 use tokio::sync::mpsc; 61 61 62 + /// Port selection strategy for the loopback OAuth callback server. 62 63 #[derive(Clone, Debug)] 63 64 pub enum LoopbackPort { 65 + /// Bind to a specific port number. 64 66 Fixed(u16), 67 + /// Let the OS assign an available port. 65 68 Ephemeral, 66 69 } 67 70 71 + /// Configuration for the loopback OAuth callback server. 68 72 #[derive(Clone, Debug)] 69 73 pub struct LoopbackConfig { 74 + /// The host address to bind to (e.g., `"127.0.0.1"`). 70 75 pub host: String, 76 + /// Port selection strategy. 71 77 pub port: LoopbackPort, 78 + /// Whether to attempt opening the authorization URL in the user's browser. 72 79 pub open_browser: bool, 80 + /// How long to wait for the callback before timing out, in milliseconds. 73 81 pub timeout_ms: u64, 74 82 } 75 83 ··· 84 92 } 85 93 } 86 94 95 + /// Attempts to open the given URL in the user's default browser. 96 + /// 97 + /// Returns `true` if the browser was opened successfully, `false` otherwise. 87 98 #[cfg(feature = "browser-open")] 88 99 pub fn try_open_in_browser(url: &str) -> bool { 89 100 webbrowser::open(url).is_ok() 90 101 } 102 + /// Stub for when the `browser-open` feature is disabled. Always returns `false`. 91 103 #[cfg(not(feature = "browser-open"))] 92 104 pub fn try_open_in_browser(_url: &str) -> bool { 93 105 false ··· 114 126 ) 115 127 } 116 128 129 + /// Handle to a running loopback callback server, used to await the OAuth redirect. 117 130 pub struct CallbackHandle { 118 131 #[allow(dead_code)] 119 132 server_handle: std::thread::JoinHandle<()>,
+69 -2
crates/jacquard-oauth/src/request.rs
··· 41 41 42 42 use smol_str::SmolStr; 43 43 44 + /// Convenience alias for a heap-allocated, thread-safe, `'static` error value. 44 45 pub type BoxError = Box<dyn std::error::Error + Send + Sync + 'static>; 45 46 46 47 /// OAuth request error for token operations and auth flows ··· 130 131 code(jacquard_oauth::request::http_status_body), 131 132 help("server returned error JSON; inspect fields like `error`, `error_description`") 132 133 )] 133 - HttpStatusWithBody { status: StatusCode, body: Value }, 134 + HttpStatusWithBody { 135 + /// HTTP status code returned by the server. 136 + status: StatusCode, 137 + /// Parsed JSON body containing OAuth error fields such as `error` and `error_description`. 138 + body: Value, 139 + }, 134 140 135 141 /// Identity resolution error 136 142 #[error("identity error")] ··· 413 419 } 414 420 } 415 421 422 + /// Convenience `Result` type for OAuth request operations, defaulting to [`RequestError`]. 416 423 pub type Result<T> = core::result::Result<T, RequestError>; 417 424 425 + /// Represents the different OAuth token-endpoint request types sent by this crate. 418 426 #[allow(dead_code)] 419 427 pub enum OAuthRequest<'a> { 428 + /// Standard authorization-code token exchange. 420 429 Token(TokenRequestParameters<'a>), 430 + /// Refresh-token grant to obtain a fresh access token. 421 431 Refresh(RefreshRequestParameters<'a>), 432 + /// Token revocation request (RFC 7009). 422 433 Revocation(RevocationRequestParameters<'a>), 434 + /// Token introspection request (RFC 7662). 423 435 Introspection, 436 + /// Pushed authorization request (RFC 9126) for pre-registering auth parameters. 424 437 PushedAuthorizationRequest(ParParameters<'a>), 425 438 } 426 439 427 440 impl OAuthRequest<'_> { 441 + /// Return a human-readable name for this request variant, used in error messages. 428 442 pub fn name(&self) -> CowStr<'static> { 429 443 CowStr::new_static(match self { 430 444 Self::Token(_) => "token", ··· 434 448 Self::PushedAuthorizationRequest(_) => "pushed_authorization_request", 435 449 }) 436 450 } 451 + /// Returns the HTTP status code that a successful response to this request should carry. 437 452 pub fn expected_status(&self) -> StatusCode { 438 453 match self { 439 454 Self::Token(_) | Self::Refresh(_) => StatusCode::OK, ··· 445 460 } 446 461 } 447 462 463 + /// The serialized body of an OAuth token-endpoint request. 448 464 #[derive(Debug, Serialize)] 449 465 pub struct RequestPayload<'a, T> 450 466 where 451 467 T: Serialize, 452 468 { 469 + /// The OAuth `client_id` advertised in the client metadata document. 453 470 client_id: CowStr<'a>, 471 + /// The assertion type URI; set to `urn:ietf:params:oauth:client-assertion-type:jwt-bearer` 472 + /// when using `private_key_jwt` client authentication. 454 473 #[serde(skip_serializing_if = "Option::is_none")] 455 474 client_assertion_type: Option<CowStr<'a>>, 475 + /// A JWT signed with the client's private key, proving client identity to the server. 456 476 #[serde(skip_serializing_if = "Option::is_none")] 457 477 client_assertion: Option<CowStr<'a>>, 478 + /// The grant-specific parameters (token request, refresh, PAR, etc.) flattened into the body. 458 479 #[serde(flatten)] 459 480 parameters: T, 460 481 } 461 482 483 + /// Bundled OAuth metadata needed to perform token-endpoint operations. 484 + /// 485 + /// Aggregates the server's authorization server metadata, the client's own registered metadata, 486 + /// and the optional signing keyset into a single value that is passed to helper functions such 487 + /// as [`par`], [`exchange_code`], [`refresh`], and [`revoke`]. 462 488 #[derive(Debug, Clone)] 463 489 pub struct OAuthMetadata { 490 + /// Metadata fetched from the authorization server's `/.well-known/oauth-authorization-server` document. 464 491 pub server_metadata: OAuthAuthorizationServerMetadata<'static>, 492 + /// This client's registered metadata, derived from [`crate::atproto::AtprotoClientMetadata`]. 465 493 pub client_metadata: OAuthClientMetadata<'static>, 494 + /// Optional signing keyset; required for `private_key_jwt` client authentication. 466 495 pub keyset: Option<Keyset>, 467 496 } 468 497 469 498 impl OAuthMetadata { 499 + /// Fetch server metadata and assemble an `OAuthMetadata` from an active session context. 500 + /// 501 + /// Contacts the authorization server recorded in `session_data` to retrieve its current 502 + /// metadata, then combines it with the client configuration. This is the preferred way to 503 + /// build an `OAuthMetadata` during token refresh or revocation. 470 504 pub async fn new<'r, T: HttpClient + OAuthResolver + Send + Sync>( 471 505 client: &T, 472 506 ClientData { keyset, config }: &ClientData<'r>, ··· 484 518 } 485 519 } 486 520 521 + /// Perform a Pushed Authorization Request (PAR) and return the resulting state for the auth flow. 522 + /// 523 + /// Generates a PKCE code challenge, a fresh DPoP key, and a random `state` token, then POSTs 524 + /// them to the authorization server's PAR endpoint. The returned [`AuthRequestData`] must be 525 + /// persisted (e.g., in the auth store) so it can be retrieved and verified during 526 + /// [`crate::client::OAuthClient::callback`]. 487 527 #[cfg_attr(feature = "tracing", tracing::instrument(level = "debug", skip_all, fields(login_hint = login_hint.as_ref().map(|h| h.as_ref()))))] 488 528 pub async fn par<'r, T: OAuthResolver + DpopExt + Send + Sync + 'static>( 489 529 client: &T, ··· 562 602 } 563 603 } 564 604 605 + /// Exchange a refresh token for a fresh token set and update the session data in place. 565 606 #[cfg_attr(feature = "tracing", tracing::instrument(level = "debug", skip_all, fields(did = %session_data.account_did)))] 566 607 pub async fn refresh<'r, T>( 567 608 client: &T, ··· 621 662 Ok(session_data) 622 663 } 623 664 665 + /// Exchange an authorization code for a token set and return a fully-verified [`TokenSet`]. 666 + /// 667 + /// Per the AT Protocol OAuth spec, the `sub` claim in the token response **must** be verified 668 + /// against the expected authorization server issuer before the token can be trusted. This 669 + /// function performs that verification as part of the exchange, so callers receive a token 670 + /// set that is safe to persist. 624 671 #[cfg_attr(feature = "tracing", tracing::instrument(level = "debug", skip_all))] 625 672 pub async fn exchange_code<'r, T, D>( 626 673 client: &T, ··· 680 727 }) 681 728 } 682 729 730 + /// Send a token revocation request (RFC 7009) to the authorization server. 731 + /// 732 + /// This function is called by [`crate::client::OAuthSession::logout`] when a revocation endpoint is advertised 733 + /// by the server. The caller is responsible for deleting the session from local storage regardless 734 + /// of whether revocation succeeds. 683 735 #[cfg_attr(feature = "tracing", tracing::instrument(level = "debug", skip_all))] 684 736 pub async fn revoke<'r, T, D>( 685 737 client: &T, ··· 703 755 Ok(()) 704 756 } 705 757 758 + /// Low-level function for sending an OAuth token-endpoint request and deserializing the response. 759 + /// 760 + /// Selects the correct server endpoint for `request`, builds the form-encoded body with 761 + /// client authentication, performs the DPoP-wrapped HTTP POST, and deserializes the response 762 + /// body into `O`. The type parameter `O` is inferred from the call site; use `()` for requests 763 + /// where the response body is empty (e.g., revocation). 706 764 pub async fn oauth_request<'de: 'r, 'r, O, T, D>( 707 765 client: &T, 708 766 data_source: &'r mut D, ··· 784 842 })?) 785 843 } 786 844 845 + /// Client identity fields appended to every token-endpoint request body. 846 + /// 847 + /// Encapsulates the result of choosing a client authentication method (`none` vs. 848 + /// `private_key_jwt`). The `build_auth` helper selects the appropriate variant based 849 + /// on server capabilities and client configuration. 787 850 #[derive(Debug, Clone, Default)] 788 851 pub struct ClientAuth<'a> { 852 + /// The OAuth `client_id` for this client. 789 853 client_id: CowStr<'a>, 790 - assertion_type: Option<CowStr<'a>>, // either none or `CLIENT_ASSERTION_TYPE_JWT_BEARER` 854 + /// Either absent (for `none` auth) or `urn:ietf:params:oauth:client-assertion-type:jwt-bearer`. 855 + assertion_type: Option<CowStr<'a>>, 856 + /// A signed JWT proving client identity; present only for `private_key_jwt` auth. 791 857 assertion: Option<CowStr<'a>>, 792 858 } 793 859 794 860 impl<'s> ClientAuth<'s> { 861 + /// Construct a `ClientAuth` with only a `client_id` and no assertion (the `none` method). 795 862 pub fn new_id(client_id: CowStr<'s>) -> Self { 796 863 Self { 797 864 client_id,
+53 -4
crates/jacquard-oauth/src/resolver.rs
··· 14 14 use jacquard_identity::resolver::{IdentityError, IdentityResolver}; 15 15 use smol_str::SmolStr; 16 16 17 + /// Convenience alias for a heap-allocated, thread-safe, `'static` error value. 17 18 pub type BoxError = Box<dyn std::error::Error + Send + Sync + 'static>; 18 19 19 20 /// OAuth resolver error for identity and metadata resolution ··· 613 614 Ok(as_metadata) 614 615 } 615 616 617 + /// Resolver trait for the AT Protocol OAuth flow. 618 + /// 619 + /// `OAuthResolver` extends [`IdentityResolver`] and [`HttpClient`] with the methods needed to 620 + /// drive the full OAuth flow: resolving an AT identifier (handle or DID) to the authorization 621 + /// server that protects its PDS, fetching server metadata, and verifying that a token's `sub` 622 + /// claim is authorized by the expected issuer. 623 + /// 624 + /// A default implementation based on [`jacquard_identity::JacquardResolver`] is provided. 625 + /// Custom implementations are possible for testing or for environments that require 626 + /// non-standard identity resolution (e.g., federated or offline setups). 616 627 #[cfg_attr(not(target_arch = "wasm32"), trait_variant::make(Send))] 617 628 pub trait OAuthResolver: IdentityResolver + HttpClient { 629 + /// Verify that the authorization server in `server_metadata` is the correct issuer for `sub`. 618 630 #[cfg(not(target_arch = "wasm32"))] 619 631 fn verify_issuer( 620 632 &self, ··· 627 639 verify_issuer_impl(self, server_metadata, sub) 628 640 } 629 641 642 + /// Verify that the authorization server in `server_metadata` is the correct issuer for `sub`. 630 643 #[cfg(target_arch = "wasm32")] 631 644 fn verify_issuer( 632 645 &self, ··· 636 649 verify_issuer_impl(self, server_metadata, sub) 637 650 } 638 651 652 + /// Resolve `input` (a handle, DID, PDS URL, or entryway URL) to OAuth metadata. 653 + /// 654 + /// When `input` starts with `https://`, it is treated as a service URL and resolved 655 + /// directly via [`OAuthResolver::resolve_from_service`]. Otherwise it is treated as an 656 + /// AT identifier and resolved via [`OAuthResolver::resolve_from_identity`]. Returns the 657 + /// authorization server metadata and, when `input` was an identity, the resolved DID document. 639 658 #[cfg(not(target_arch = "wasm32"))] 640 659 fn resolve_oauth( 641 660 &self, ··· 652 671 resolve_oauth_impl(self, input) 653 672 } 654 673 674 + /// Resolve `input` (a handle, DID, PDS URL, or entryway URL) to OAuth metadata. 675 + /// 676 + /// When `input` starts with `https://`, it is treated as a service URL and resolved 677 + /// directly via [`OAuthResolver::resolve_from_service`]. Otherwise it is treated as an 678 + /// AT identifier and resolved via [`OAuthResolver::resolve_from_identity`]. Returns the 679 + /// authorization server metadata and, when `input` was an identity, the resolved DID document. 655 680 #[cfg(target_arch = "wasm32")] 656 681 fn resolve_oauth( 657 682 &self, ··· 665 690 resolve_oauth_impl(self, input) 666 691 } 667 692 693 + /// Resolve a service URL (PDS or entryway) to its authorization server metadata. 694 + /// 695 + /// First attempts to fetch the PDS's protected resource metadata; if that fails, falls back 696 + /// to treating the URL as an entryway and fetching authorization server metadata directly. 668 697 #[cfg(not(target_arch = "wasm32"))] 669 698 fn resolve_from_service( 670 699 &self, ··· 676 705 resolve_from_service_impl(self, input) 677 706 } 678 707 708 + /// Resolve a service URL to its authorization server metadata. 709 + /// 710 + /// First attempts to fetch the PDS's protected resource metadata; if that fails, falls back 711 + /// to treating the URL as an entryway and fetching authorization server metadata directly. 679 712 #[cfg(target_arch = "wasm32")] 680 713 fn resolve_from_service( 681 714 &self, ··· 684 717 resolve_from_service_impl(self, input) 685 718 } 686 719 720 + /// Resolve an AT identifier (handle or DID) to its authorization server metadata and DID document. 687 721 #[cfg(not(target_arch = "wasm32"))] 688 722 fn resolve_from_identity( 689 723 &self, ··· 700 734 resolve_from_identity_impl(self, input) 701 735 } 702 736 737 + /// Resolve an AT identifier to its authorization server metadata and DID document. 703 738 #[cfg(target_arch = "wasm32")] 704 739 fn resolve_from_identity( 705 740 &self, ··· 713 748 resolve_from_identity_impl(self, input) 714 749 } 715 750 751 + /// Fetch and validate the authorization server metadata for the given issuer URL. 752 + /// 753 + /// Retrieves the `/.well-known/oauth-authorization-server` document and confirms that 754 + /// the `issuer` field in the response matches the requested URL, as required by RFC 8414 §3.3. 716 755 #[cfg(not(target_arch = "wasm32"))] 717 756 fn get_authorization_server_metadata( 718 757 &self, ··· 724 763 get_authorization_server_metadata_impl(self, issuer) 725 764 } 726 765 766 + /// Fetch and validate the authorization server metadata for the given issuer URL. 767 + /// 768 + /// Retrieves the `/.well-known/oauth-authorization-server` document and confirms that 769 + /// the `issuer` field in the response matches the requested URL, as required by RFC 8414 §3.3. 727 770 #[cfg(target_arch = "wasm32")] 728 771 fn get_authorization_server_metadata( 729 772 &self, ··· 732 775 get_authorization_server_metadata_impl(self, issuer) 733 776 } 734 777 778 + /// Resolve a PDS base URL to its authorization server metadata. 735 779 #[cfg(not(target_arch = "wasm32"))] 736 780 fn get_resource_server_metadata( 737 781 &self, ··· 743 787 get_resource_server_metadata_impl(self, pds) 744 788 } 745 789 790 + /// Resolve a PDS base URL to its authorization server metadata. 746 791 #[cfg(target_arch = "wasm32")] 747 792 fn get_resource_server_metadata( 748 793 &self, ··· 752 797 } 753 798 } 754 799 800 + /// Fetch and validate the `/.well-known/oauth-authorization-server` document for `server`. 801 + /// 802 + /// Per RFC 8414 §3.3 the `issuer` field in the response must equal the `server` URL exactly; 803 + /// this prevents a compromised server from claiming to be a different issuer. 755 804 pub async fn resolve_authorization_server<T: HttpClient + ?Sized>( 756 805 client: &T, 757 806 server: &CowStr<'_>, ··· 772 821 if res.status() == StatusCode::OK { 773 822 let metadata = serde_json::from_slice::<OAuthAuthorizationServerMetadata>(res.body())?; 774 823 // https://datatracker.ietf.org/doc/html/rfc8414#section-3.3 775 - // Accept semantically equivalent issuer (normalize to the requested URL form) 776 824 if metadata.issuer == server.as_str() { 777 - // if equivalent, keep the canonical form 778 825 Ok(metadata.into_static()) 779 826 } else { 780 827 Err(ResolverError::authorization_server_metadata( ··· 786 833 } 787 834 } 788 835 836 + /// Fetch the `/.well-known/oauth-protected-resource` document for `server`. 837 + /// 838 + /// The `resource` field in the response must equal the requested `server` URL, ensuring 839 + /// that the metadata belongs to the PDS we queried and not a different resource. 789 840 pub async fn resolve_protected_resource_info<T: HttpClient + ?Sized>( 790 841 client: &T, 791 842 server: &CowStr<'_>, ··· 806 857 if res.status() == StatusCode::OK { 807 858 let metadata = serde_json::from_slice::<OAuthProtectedResourceMetadata>(res.body())?; 808 859 // https://datatracker.ietf.org/doc/html/rfc8414#section-3.3 809 - // Accept semantically equivalent resource URL (normalize to the requested URL form) 810 860 if metadata.resource == server.as_str() { 811 - // if equivalent, keep the canonical form 812 861 Ok(metadata.into_static()) 813 862 } else { 814 863 Err(ResolverError::authorization_server_metadata(
+3 -1
crates/jacquard-oauth/src/scopes.rs
··· 1 1 //! AT Protocol OAuth scopes 2 - //! Derived from <https://tangled.org/@smokesignal.events/atproto-identity-rs/raw/main/crates/atproto-oauth/src/scopes.rs> 2 + //! 3 + //! Derived from <https://tangled.org/smokesignal.events/atproto-identity-rs/raw/main/crates/atproto-oauth/src/scopes.rs> 3 4 //! 4 5 //! This module provides comprehensive support for AT Protocol OAuth scopes, 5 6 //! including parsing, serialization, normalization, and permission checking. ··· 1035 1036 InvalidAction(String), 1036 1037 /// Invalid MIME type 1037 1038 InvalidMimeType(String), 1039 + /// An AT Protocol string type (DID, NSID, etc.) failed validation during scope parsing. 1038 1040 ParseError(#[from] AtStrError), 1039 1041 } 1040 1042
+108 -19
crates/jacquard-oauth/src/session.rs
··· 26 26 use smol_str::{SmolStr, format_smolstr}; 27 27 use tokio::sync::Mutex; 28 28 29 + /// Provides DPoP key material and per-server nonces to the DPoP proof-building machinery. 30 + /// 31 + /// This trait abstracts over two different holders of DPoP state: [`DpopReqData`] (used 32 + /// during the initial authorization request, where only an authserver nonce is tracked) and 33 + /// [`DpopClientData`] (used in active sessions, where both authserver and host nonces are 34 + /// maintained). Implementors must store nonces durably so that the next request to the same 35 + /// server includes the most recently observed nonce. 29 36 pub trait DpopDataSource { 37 + /// Return the private JWK used to sign DPoP proofs. 30 38 fn key(&self) -> &Key; 39 + /// Return the most recently observed nonce from the authorization server, if any. 31 40 fn authserver_nonce(&self) -> Option<CowStr<'_>>; 41 + /// Persist a new nonce received from the authorization server. 32 42 fn set_authserver_nonce(&mut self, nonce: CowStr<'_>); 43 + /// Return the most recently observed nonce from the resource server (PDS), if any. 33 44 fn host_nonce(&self) -> Option<CowStr<'_>>; 45 + /// Persist a new nonce received from the resource server (PDS). 34 46 fn set_host_nonce(&mut self, nonce: CowStr<'_>); 35 47 } 36 48 37 49 /// Persisted information about an OAuth session. Used to resume an active session. 38 50 #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] 39 51 pub struct ClientSessionData<'s> { 40 - // Account DID for this session. Assuming only one active session per account, this can be used as "primary key" for storing and retrieving this information. 52 + /// DID of the authenticated account; serves as the primary key for session storage 53 + /// because only one active session per account is assumed. 41 54 #[serde(borrow)] 42 55 pub account_did: Did<'s>, 43 56 44 - // Identifier to distinguish this particular session for the account. Server backends generally support multiple sessions for the same account. This package will re-use the random 'state' token from the auth flow as the session ID. 57 + /// Opaque identifier that distinguishes this session from other sessions for the same account. 58 + /// 59 + /// Reuses the random `state` token generated during the PAR flow. 45 60 pub session_id: CowStr<'s>, 46 61 47 - // Base URL of the "resource server" (eg, PDS). Should include scheme, hostname, port; no path or auth info. 62 + /// Base URL of the resource server (PDS): scheme, host, and port only 48 63 pub host_url: Uri<String>, 49 64 50 - // Base URL of the "auth server" (eg, PDS or entryway). Should include scheme, hostname, port; no path or auth info. 65 + /// Base URL of the authorization server (PDS or entryway): scheme, host, and port only 51 66 pub authserver_url: CowStr<'s>, 52 67 53 - // Full token endpoint 68 + /// Full URL of the authorization server's token endpoint. 54 69 pub authserver_token_endpoint: CowStr<'s>, 55 70 56 - // Full revocation endpoint, if it exists 71 + /// Full URL of the authorization server's revocation endpoint, if advertised. 57 72 #[serde(skip_serializing_if = "std::option::Option::is_none")] 58 73 pub authserver_revocation_endpoint: Option<CowStr<'s>>, 59 74 60 - // The set of scopes approved for this session (returned in the initial token request) 75 + /// The set of OAuth scopes approved for this session, as returned in the initial token response. 61 76 pub scopes: Vec<Scope<'s>>, 62 77 78 + /// DPoP key and nonce state for ongoing requests in this session. 63 79 #[serde(flatten)] 64 80 pub dpop_data: DpopClientData<'s>, 65 81 82 + /// Current token set (access token, refresh token, expiry, etc.). 66 83 #[serde(flatten)] 67 84 pub token_set: TokenSet<'s>, 68 85 } ··· 88 105 } 89 106 90 107 impl ClientSessionData<'_> { 108 + /// Update this session's token set and, if the new token set includes scopes, replace the scope list. 109 + /// 110 + /// Called after a successful token refresh so that any scope changes returned by the server 111 + /// are reflected in the persisted session without requiring a full re-authentication. 91 112 pub fn update_with_tokens(&mut self, token_set: TokenSet<'_>) { 92 113 if let Some(Ok(scopes)) = token_set 93 114 .scope ··· 100 121 } 101 122 } 102 123 124 + /// DPoP state for an active OAuth session, persisted alongside the token set. 125 + /// 126 + /// Both nonces must be written back to the store after each request so that the next 127 + /// request to the same server includes the correct replay-protection nonce. 103 128 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] 104 129 pub struct DpopClientData<'s> { 130 + /// The private JWK bound to this session; used to sign all DPoP proofs. 105 131 pub dpop_key: Key, 106 - // Current auth server DPoP nonce 132 + /// Most recently observed DPoP nonce from the authorization server. 107 133 #[serde(borrow)] 108 134 pub dpop_authserver_nonce: CowStr<'s>, 109 - // Current host ("resource server", eg PDS) DPoP nonce 135 + /// Most recently observed DPoP nonce from the resource server (PDS). 110 136 pub dpop_host_nonce: CowStr<'s>, 111 137 } 112 138 ··· 143 169 } 144 170 } 145 171 172 + /// Transient state created during the PAR flow and consumed by the callback handler. 173 + /// 174 + /// This struct is persisted to the auth store between [`crate::request::par`] and 175 + /// [`crate::client::OAuthClient::callback`] so that the callback can verify the 176 + /// `state`, reconstruct the token exchange, and create a full [`ClientSessionData`]. 146 177 #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] 147 178 pub struct AuthRequestData<'s> { 148 - // The random identifier generated by the client for the auth request flow. Can be used as "primary key" for storing and retrieving this information. 179 + /// Random identifier generated for this authorization request; used as the primary key 180 + /// for storing and looking up this record during the callback. 149 181 #[serde(borrow)] 150 182 pub state: CowStr<'s>, 151 183 152 - // URL of the auth server (eg, PDS or entryway) 184 + /// Base URL of the authorization server that was selected for this flow. 153 185 pub authserver_url: CowStr<'s>, 154 186 155 - // If the flow started with an account identifier (DID or handle), it should be persisted, to verify against the initial token response. 187 + /// If the flow was initiated with a DID or handle, the resolved DID is stored here 188 + /// so it can be compared against the `sub` in the token response. 156 189 #[serde(skip_serializing_if = "std::option::Option::is_none")] 157 190 pub account_did: Option<Did<'s>>, 158 191 159 - // OAuth scope strings 192 + /// OAuth scopes requested for this authorization. 160 193 pub scopes: Vec<Scope<'s>>, 161 194 162 - // unique token in URI format, which will be used by the client in the auth flow redirect 195 + /// The PAR `request_uri` returned by the authorization server; included in the redirect URL. 163 196 pub request_uri: CowStr<'s>, 164 197 165 - // Full token endpoint URL 198 + /// Full URL of the authorization server's token endpoint. 166 199 pub authserver_token_endpoint: CowStr<'s>, 167 200 168 - // Full revocation endpoint, if it exists 201 + /// Full URL of the authorization server's revocation endpoint, if advertised. 169 202 #[serde(skip_serializing_if = "std::option::Option::is_none")] 170 203 pub authserver_revocation_endpoint: Option<CowStr<'s>>, 171 204 172 - // The secret token/nonce which a code challenge was generated from 205 + /// The PKCE code verifier whose SHA-256 hash was sent as the code challenge; required 206 + /// at the token exchange step to prove the initiator of the auth request. 173 207 pub pkce_verifier: CowStr<'s>, 174 208 209 + /// DPoP key and any authserver nonce observed during the PAR request. 175 210 #[serde(flatten)] 176 211 pub dpop_data: DpopReqData<'s>, 177 212 } ··· 195 230 } 196 231 } 197 232 233 + /// DPoP state for an in-progress authorization request (PAR through code exchange). 234 + /// 235 + /// Unlike [`DpopClientData`], this struct only tracks the authserver nonce—no resource-server 236 + /// nonce is needed until a full session is established. 198 237 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] 199 238 pub struct DpopReqData<'s> { 200 - // The secret cryptographic key generated by the client for this specific OAuth session 239 + /// The private JWK generated fresh for this authorization request and session. 201 240 pub dpop_key: Key, 202 - // Server-provided DPoP nonce from auth request (PAR) 241 + /// DPoP nonce received from the authorization server during the PAR exchange, if any. 203 242 #[serde(borrow)] 204 243 pub dpop_authserver_nonce: Option<CowStr<'s>>, 205 244 } ··· 233 272 fn set_host_nonce(&mut self, _nonce: CowStr<'_>) {} 234 273 } 235 274 275 + /// Static configuration for an OAuth client: the signing keyset and registered client metadata. 276 + /// 277 + /// `ClientData` is constructed once at startup and shared (via `Arc`) across all sessions 278 + /// managed by the same [`crate::client::OAuthClient`]. 236 279 #[derive(Clone, Debug)] 237 280 pub struct ClientData<'s> { 281 + /// Optional private key set used for `private_key_jwt` client authentication. 282 + /// When `None`, the `none` authentication method is used instead. 238 283 pub keyset: Option<Keyset>, 284 + /// AT Protocol-specific client registration metadata (redirect URIs, scopes, etc.). 239 285 pub config: AtprotoClientMetadata<'s>, 240 286 } 241 287 ··· 250 296 } 251 297 252 298 impl<'s> ClientData<'s> { 299 + /// Create `ClientData` with an optional signing keyset and the given client metadata. 253 300 pub fn new(keyset: Option<Keyset>, config: AtprotoClientMetadata<'s>) -> Self { 254 301 Self { keyset, config } 255 302 } 256 303 304 + /// Create `ClientData` without a signing keyset, relying on the `none` auth method. 305 + /// 306 + /// Suitable for public clients (e.g., single-page applications or native apps) that 307 + /// cannot securely store a private key. 257 308 pub fn new_public(config: AtprotoClientMetadata<'s>) -> Self { 258 309 Self { 259 310 keyset: None, ··· 262 313 } 263 314 } 264 315 316 + /// A bundle of client configuration and an active session, used for operations that need both. 317 + /// 318 + /// `ClientSession` is a convenience type that pairs a [`ClientData`] with a 319 + /// [`ClientSessionData`] so that methods like `metadata` can access both without requiring 320 + /// callers to pass them separately. 265 321 pub struct ClientSession<'s> { 322 + /// Optional signing keyset, forwarded from [`ClientData`]. 266 323 pub keyset: Option<Keyset>, 324 + /// Client registration metadata, forwarded from [`ClientData`]. 267 325 pub config: AtprotoClientMetadata<'s>, 326 + /// The session state for the authenticated account. 268 327 pub session_data: ClientSessionData<'s>, 269 328 } 270 329 271 330 impl<'s> ClientSession<'s> { 331 + /// Construct a `ClientSession` from a [`ClientData`] and an active session. 272 332 pub fn new( 273 333 ClientData { keyset, config }: ClientData<'s>, 274 334 session_data: ClientSessionData<'s>, ··· 280 340 } 281 341 } 282 342 343 + /// Fetch and assemble an [`OAuthMetadata`] for the authorization server of this session. 283 344 pub async fn metadata<T: HttpClient + OAuthResolver + Send + Sync>( 284 345 &self, 285 346 client: &T, ··· 297 358 } 298 359 } 299 360 361 + /// Errors that can occur during OAuth session management. 300 362 #[derive(thiserror::Error, Debug, miette::Diagnostic)] 301 363 #[non_exhaustive] 302 364 pub enum Error { 365 + /// A token-endpoint or metadata operation failed. 303 366 #[error(transparent)] 304 367 #[diagnostic(code(jacquard_oauth::session::request))] 305 368 ServerAgent(#[from] crate::request::RequestError), 369 + /// The backing session store returned an error. 306 370 #[error(transparent)] 307 371 #[diagnostic(code(jacquard_oauth::session::storage))] 308 372 Store(#[from] SessionStoreError), 373 + /// The requested session does not exist in the store. 309 374 #[error("session does not exist")] 310 375 #[diagnostic(code(jacquard_oauth::session::not_found))] 311 376 SessionNotFound, 377 + /// Token refresh failed with a permanent error (e.g., `invalid_grant`); the session 378 + /// has already been removed from the store and the user must re-authenticate. 312 379 #[error("session refresh failed permanently")] 313 380 #[diagnostic( 314 381 code(jacquard_oauth::session::refresh_failed), ··· 330 397 } 331 398 } 332 399 400 + /// Central coordinator for OAuth session storage and token refresh. 401 + /// 402 + /// `SessionRegistry` wraps the [`ClientAuthStore`] and provides serialized token refresh: 403 + /// concurrent refresh attempts for the same `(DID, session_id)` pair are coalesced behind 404 + /// a per-key `Mutex` stored in `pending`, so only one refresh request is issued to the 405 + /// authorization server even when many concurrent requests detect an expired token. 333 406 pub struct SessionRegistry<T, S> 334 407 where 335 408 T: OAuthResolver, 336 409 S: ClientAuthStore, 337 410 { 411 + /// Backing store for persisting session data across process restarts. 338 412 pub store: Arc<S>, 413 + /// Shared resolver used to fetch authorization server metadata during refresh. 339 414 pub client: Arc<T>, 415 + /// Static client configuration (keyset and registration metadata). 340 416 pub client_data: ClientData<'static>, 417 + /// Per-`(DID, session_id)` mutex that serializes concurrent refresh attempts. 341 418 pending: DashMap<SmolStr, Arc<Mutex<()>>>, 342 419 } 343 420 ··· 346 423 S: ClientAuthStore, 347 424 T: OAuthResolver, 348 425 { 426 + /// Create a new registry, taking ownership of the store. 349 427 pub fn new(store: S, client: Arc<T>, client_data: ClientData<'static>) -> Self { 350 428 let store = Arc::new(store); 351 429 Self { ··· 356 434 } 357 435 } 358 436 437 + /// Create a new registry from an already-`Arc`-wrapped store. 438 + /// 439 + /// Use this variant when the store needs to be accessed from outside the registry, 440 + /// for example to expose session listing or administration functionality. 359 441 pub fn new_shared(store: Arc<S>, client: Arc<T>, client_data: ClientData<'static>) -> Self { 360 442 Self { 361 443 store, ··· 419 501 Err(e) => Err(Error::ServerAgent(e)), 420 502 } 421 503 } 504 + /// Retrieve a session from the store, optionally refreshing it first. 505 + /// 506 + /// When `refresh` is `true`, proactively 507 + /// renews the token if it is within 60 seconds of expiry. When `false`, returns the session 508 + /// data as-is without contacting the authorization server. 422 509 pub async fn get( 423 510 &self, 424 511 did: &Did<'_>, ··· 435 522 .ok_or(Error::SessionNotFound) 436 523 } 437 524 } 525 + /// Persist an updated session to the backing store. 438 526 pub async fn set(&self, value: ClientSessionData<'_>) -> Result<(), Error> { 439 527 self.store.upsert_session(value).await?; 440 528 Ok(()) 441 529 } 530 + /// Delete a session from the backing store. 442 531 pub async fn del(&self, did: &Did<'_>, session_id: &str) -> Result<(), Error> { 443 532 self.store.delete_session(did, session_id).await?; 444 533 Ok(())
+22
crates/jacquard-oauth/src/types.rs
··· 16 16 use jacquard_common::deps::fluent_uri::Uri; 17 17 use serde::Deserialize; 18 18 19 + /// The `prompt` parameter for an OAuth authorization request. 20 + /// 21 + /// Controls whether the authorization server prompts the user for 22 + /// re-authentication or re-consent, as defined in OpenID Connect Core §3.1.2.1. 19 23 #[derive(Debug, Deserialize, Clone, Copy)] 20 24 pub enum AuthorizeOptionPrompt { 25 + /// Prompt the user to re-authenticate. 21 26 Login, 27 + /// Do not display any authentication or consent UI; fail if interaction is required. 22 28 None, 29 + /// Prompt the user for explicit consent before issuing tokens. 23 30 Consent, 31 + /// Prompt the user to select an account when multiple sessions are active. 24 32 SelectAccount, 25 33 } 26 34 ··· 35 43 } 36 44 } 37 45 46 + /// Options for initiating an OAuth authorization request. 38 47 #[derive(Debug)] 39 48 pub struct AuthorizeOptions<'s> { 49 + /// Override the redirect URI registered in the client metadata. 40 50 pub redirect_uri: Option<Uri<String>>, 51 + /// Scopes to request. Defaults to an empty list (server-defined defaults apply). 41 52 pub scopes: Vec<Scope<'s>>, 53 + /// Optional prompt hint for the authorization server's UI. 42 54 pub prompt: Option<AuthorizeOptionPrompt>, 55 + /// Opaque client-provided state value, echoed back in the callback for CSRF protection. 43 56 pub state: Option<CowStr<'s>>, 44 57 } 45 58 ··· 55 68 } 56 69 57 70 impl<'s> AuthorizeOptions<'s> { 71 + /// Set the `prompt` parameter sent to the authorization server. 58 72 pub fn with_prompt(mut self, prompt: AuthorizeOptionPrompt) -> Self { 59 73 self.prompt = Some(prompt); 60 74 self 61 75 } 62 76 77 + /// Set a CSRF-protection `state` value to be echoed in the callback. 63 78 pub fn with_state(mut self, state: CowStr<'s>) -> Self { 64 79 self.state = Some(state); 65 80 self 66 81 } 67 82 83 + /// Override the redirect URI for this specific authorization request. 68 84 pub fn with_redirect_uri(mut self, redirect_uri: Uri<String>) -> Self { 69 85 self.redirect_uri = Some(redirect_uri); 70 86 self 71 87 } 72 88 89 + /// Set the OAuth scopes to request. 73 90 pub fn with_scopes(mut self, scopes: Vec<Scope<'s>>) -> Self { 74 91 self.scopes = scopes; 75 92 self 76 93 } 77 94 } 78 95 96 + /// Query parameters delivered to the OAuth redirect URI after user authorization. 79 97 #[derive(Debug, Deserialize)] 80 98 pub struct CallbackParams<'s> { 99 + /// The authorization code issued by the authorization server. 81 100 #[serde(borrow)] 82 101 pub code: CowStr<'s>, 102 + /// The `state` value originally sent in the authorization request, used to 103 + /// verify the response belongs to this session. 83 104 pub state: Option<CowStr<'s>>, 105 + /// The `iss` (issuer) parameter, required by RFC 9207 to prevent mix-up attacks. 84 106 pub iss: Option<CowStr<'s>>, 85 107 } 86 108
+29 -3
crates/jacquard-oauth/src/types/client_metadata.rs
··· 3 3 use serde::{Deserialize, Serialize}; 4 4 use smol_str::SmolStr; 5 5 6 + /// OAuth 2.1 client metadata, used in the ATProto client ID metadata document. 7 + /// 8 + /// In ATProto's OAuth profile, clients are identified by a URL that serves this 9 + /// metadata document. Fields follow RFC 7591 (Dynamic Client Registration), 10 + /// RFC 9449 (DPoP), and OpenID Connect Registration. 11 + /// 12 + /// <https://atproto.com/specs/oauth> 6 13 #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] 7 14 pub struct OAuthClientMetadata<'c> { 15 + /// The client identifier, typically a URL pointing to this metadata document. 8 16 pub client_id: CowStr<'c>, 17 + /// URL of the client's home page, used for display purposes. 9 18 #[serde(skip_serializing_if = "Option::is_none")] 10 19 pub client_uri: Option<CowStr<'c>>, 20 + /// List of redirect URIs the authorization server may send callbacks to. 11 21 pub redirect_uris: Vec<CowStr<'c>>, 22 + /// Space-separated list of scopes the client is allowed to request. 12 23 #[serde(skip_serializing_if = "Option::is_none")] 13 24 #[serde(borrow)] 14 25 pub scope: Option<CowStr<'c>>, 26 + /// Application type (`web` or `native`), used to enforce redirect URI constraints. 15 27 #[serde(skip_serializing_if = "Option::is_none")] 16 28 pub application_type: Option<CowStr<'c>>, 29 + /// OAuth 2.0 grant types the client will use. 17 30 #[serde(skip_serializing_if = "Option::is_none")] 18 31 pub grant_types: Option<Vec<CowStr<'c>>>, 32 + /// Authentication method the client uses at the token endpoint. 19 33 #[serde(skip_serializing_if = "Option::is_none")] 20 34 pub token_endpoint_auth_method: Option<CowStr<'c>>, 35 + /// Response types the client will use in authorization requests. 21 36 pub response_types: Vec<CowStr<'c>>, 22 - // https://datatracker.ietf.org/doc/html/rfc9449#section-5.2 37 + /// If `true`, the client requires DPoP-bound access tokens (RFC 9449 §5.2). 38 + /// 39 + /// <https://datatracker.ietf.org/doc/html/rfc9449#section-5.2> 23 40 #[serde(skip_serializing_if = "Option::is_none")] 24 41 pub dpop_bound_access_tokens: Option<bool>, 25 - // https://datatracker.ietf.org/doc/html/rfc7591#section-2 42 + /// URL of the client's JWK Set document for verifying signed requests (RFC 7591 §2). 43 + /// 44 + /// <https://datatracker.ietf.org/doc/html/rfc7591#section-2> 26 45 #[serde(skip_serializing_if = "Option::is_none")] 27 46 pub jwks_uri: Option<CowStr<'c>>, 47 + /// Inline JWK Set for verifying signed requests, alternative to `jwks_uri`. 28 48 #[serde(skip_serializing_if = "Option::is_none")] 29 49 pub jwks: Option<JwkSet>, 30 - // https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata 50 + /// JWS algorithm the client uses to sign token endpoint authentication assertions. 51 + /// 52 + /// <https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata> 31 53 #[serde(skip_serializing_if = "Option::is_none")] 32 54 pub token_endpoint_auth_signing_alg: Option<CowStr<'c>>, 55 + /// Human-readable name of the client, shown to users during authorization. 33 56 #[serde(skip_serializing_if = "Option::is_none")] 34 57 pub client_name: Option<SmolStr>, 58 + /// URL of the client's logo image. 35 59 #[serde(skip_serializing_if = "Option::is_none")] 36 60 pub logo_uri: Option<CowStr<'c>>, 61 + /// URL of the client's terms of service. 37 62 #[serde(skip_serializing_if = "Option::is_none")] 38 63 pub tos_uri: Option<CowStr<'c>>, 64 + /// URL of the client's privacy policy. 39 65 #[serde(skip_serializing_if = "Option::is_none")] 40 66 pub privacy_policy_uri: Option<CowStr<'c>>, 41 67 }
+65 -10
crates/jacquard-oauth/src/types/metadata.rs
··· 1 1 use jacquard_common::{CowStr, IntoStatic, types::string::Language}; 2 2 use serde::{Deserialize, Serialize}; 3 3 4 + /// Authorization server metadata, as returned from the 5 + /// `.well-known/oauth-authorization-server` discovery document. 6 + /// 7 + /// Defined by [RFC 8414](https://datatracker.ietf.org/doc/html/rfc8414#section-2) 8 + /// with extensions from OpenID Connect Discovery, RFC 9126 (PAR), RFC 9207, 9 + /// RFC 9449 (DPoP), and the ATProto client ID metadata document draft. 4 10 #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default)] 5 11 pub struct OAuthAuthorizationServerMetadata<'s> { 6 - // https://datatracker.ietf.org/doc/html/rfc8414#section-2 12 + /// The issuer identifier URL of the authorization server. 13 + /// 14 + /// <https://datatracker.ietf.org/doc/html/rfc8414#section-2> 7 15 #[serde(borrow)] 8 16 pub issuer: CowStr<'s>, 17 + /// The URL of the authorization endpoint. 9 18 pub authorization_endpoint: CowStr<'s>, // optional? 10 - pub token_endpoint: CowStr<'s>, // optional? 19 + /// The URL of the token endpoint. 20 + pub token_endpoint: CowStr<'s>, // optional? 21 + /// URL of the authorization server's JWK Set document. 11 22 pub jwks_uri: Option<CowStr<'s>>, 23 + /// URL of the dynamic client registration endpoint, if supported. 12 24 pub registration_endpoint: Option<CowStr<'s>>, 25 + /// List of OAuth 2.0 scope values the server supports. 13 26 pub scopes_supported: Vec<CowStr<'s>>, 27 + /// List of OAuth 2.0 response type values the server supports. 14 28 pub response_types_supported: Vec<CowStr<'s>>, 29 + /// List of OAuth 2.0 response mode values the server supports. 15 30 pub response_modes_supported: Option<Vec<CowStr<'s>>>, 31 + /// List of OAuth 2.0 grant type values the server supports. 16 32 pub grant_types_supported: Option<Vec<CowStr<'s>>>, 33 + /// List of client authentication methods supported at the token endpoint. 17 34 pub token_endpoint_auth_methods_supported: Option<Vec<CowStr<'s>>>, 35 + /// List of JWS signing algorithms supported for token endpoint auth. 18 36 pub token_endpoint_auth_signing_alg_values_supported: Option<Vec<CowStr<'s>>>, 37 + /// URL of a page with human-readable information about the server. 19 38 pub service_documentation: Option<CowStr<'s>>, 39 + /// BCP 47 language tags for UI locales the server supports. 20 40 pub ui_locales_supported: Option<Vec<Language>>, 41 + /// URL of the authorization server's privacy policy. 21 42 pub op_policy_uri: Option<CowStr<'s>>, 43 + /// URL of the authorization server's terms of service. 22 44 pub op_tos_uri: Option<CowStr<'s>>, 45 + /// URL of the token revocation endpoint (RFC 7009). 23 46 pub revocation_endpoint: Option<CowStr<'s>>, 47 + /// List of client authentication methods supported at the revocation endpoint. 24 48 pub revocation_endpoint_auth_methods_supported: Option<Vec<CowStr<'s>>>, 49 + /// List of JWS signing algorithms supported for revocation endpoint auth. 25 50 pub revocation_endpoint_auth_signing_alg_values_supported: Option<Vec<CowStr<'s>>>, 51 + /// URL of the token introspection endpoint (RFC 7662). 26 52 pub introspection_endpoint: Option<CowStr<'s>>, 53 + /// List of client authentication methods supported at the introspection endpoint. 27 54 pub introspection_endpoint_auth_methods_supported: Option<Vec<CowStr<'s>>>, 55 + /// List of JWS signing algorithms supported for introspection endpoint auth. 28 56 pub introspection_endpoint_auth_signing_alg_values_supported: Option<Vec<CowStr<'s>>>, 57 + /// PKCE code challenge methods supported by the server. 29 58 pub code_challenge_methods_supported: Option<Vec<CowStr<'s>>>, 30 59 31 - // https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata 60 + /// Subject identifier types supported (`public` or `pairwise`). 61 + /// 62 + /// <https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata> 32 63 pub subject_types_supported: Option<Vec<CowStr<'s>>>, 64 + /// If `true`, clients must pre-register `request_uri` values. 33 65 pub require_request_uri_registration: Option<bool>, 34 66 35 - // https://datatracker.ietf.org/doc/html/rfc9126#section-5 67 + /// URL of the Pushed Authorization Request (PAR) endpoint (RFC 9126). 68 + /// 69 + /// <https://datatracker.ietf.org/doc/html/rfc9126#section-5> 36 70 pub pushed_authorization_request_endpoint: Option<CowStr<'s>>, 71 + /// If `true`, all authorization requests must use PAR. 37 72 pub require_pushed_authorization_requests: Option<bool>, 38 73 39 - // https://datatracker.ietf.org/doc/html/rfc9207#section-3 74 + /// If `true`, the server includes `iss` in authorization responses to prevent mix-up attacks. 75 + /// 76 + /// <https://datatracker.ietf.org/doc/html/rfc9207#section-3> 40 77 pub authorization_response_iss_parameter_supported: Option<bool>, 41 78 42 - // https://datatracker.ietf.org/doc/html/rfc9449#section-5.1 79 + /// DPoP JWS signing algorithms supported by this server (RFC 9449). 80 + /// 81 + /// <https://datatracker.ietf.org/doc/html/rfc9449#section-5.1> 43 82 pub dpop_signing_alg_values_supported: Option<Vec<CowStr<'s>>>, 44 83 45 - // https://drafts.aaronpk.com/draft-parecki-oauth-client-id-metadata-document/draft-parecki-oauth-client-id-metadata-document.html#section-5 84 + /// If `true`, the server supports the ATProto client ID metadata document extension. 85 + /// 86 + /// <https://drafts.aaronpk.com/draft-parecki-oauth-client-id-metadata-document/draft-parecki-oauth-client-id-metadata-document.html#section-5> 46 87 pub client_id_metadata_document_supported: Option<bool>, 47 88 48 - // https://datatracker.ietf.org/doc/html/draft-ietf-oauth-resource-metadata-08#name-authorization-server-metada 89 + /// Protected resources associated with this authorization server. 90 + /// 91 + /// <https://datatracker.ietf.org/doc/html/draft-ietf-oauth-resource-metadata-08#name-authorization-server-metada> 49 92 pub protected_resources: Option<Vec<CowStr<'s>>>, 50 93 } 51 94 52 - // https://datatracker.ietf.org/doc/draft-ietf-oauth-resource-metadata/ 53 - // https://datatracker.ietf.org/doc/html/draft-ietf-oauth-resource-metadata-08#section-2 95 + /// Protected resource metadata, returned from `.well-known/oauth-protected-resource`. 96 + /// 97 + /// Allows clients to discover which authorization servers protect a given resource 98 + /// and what scopes and bearer methods are accepted. Defined by 99 + /// [draft-ietf-oauth-resource-metadata](https://datatracker.ietf.org/doc/draft-ietf-oauth-resource-metadata/). 54 100 #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default)] 55 101 pub struct OAuthProtectedResourceMetadata<'s> { 102 + /// The URL of the protected resource itself. 56 103 #[serde(borrow)] 57 104 pub resource: CowStr<'s>, 105 + /// URLs of authorization servers that can issue tokens for this resource. 58 106 pub authorization_servers: Option<Vec<CowStr<'s>>>, 107 + /// URL of the resource server's JWK Set document. 59 108 pub jwks_uri: Option<CowStr<'s>>, 109 + /// List of OAuth 2.0 scope values the resource server supports. 60 110 pub scopes_supported: Vec<CowStr<'s>>, 111 + /// Bearer token presentation methods supported (`header`, `body`, `query`). 61 112 pub bearer_methods_supported: Option<Vec<CowStr<'s>>>, 113 + /// JWS signing algorithms supported for resource-bound tokens. 62 114 pub resource_signing_alg_values_supported: Option<Vec<CowStr<'s>>>, 115 + /// URL of a page with human-readable information about the resource. 63 116 pub resource_documentation: Option<CowStr<'s>>, 117 + /// URL of the resource server's privacy policy. 64 118 pub resource_policy_uri: Option<CowStr<'s>>, 119 + /// URL of the resource server's terms of service. 65 120 pub resource_tos_uri: Option<CowStr<'s>>, 66 121 } 67 122
+68 -10
crates/jacquard-oauth/src/types/request.rs
··· 1 1 use jacquard_common::{CowStr, IntoStatic}; 2 2 use serde::{Deserialize, Serialize}; 3 3 4 + /// The `response_type` parameter for an OAuth 2.0 authorization request. 5 + /// 6 + /// Determines what the authorization server returns in the redirect response. 4 7 #[derive(Serialize, Deserialize, Debug)] 5 8 #[serde(rename_all = "snake_case")] 6 9 pub enum AuthorizationResponseType { 10 + /// Authorization code flow — server returns a short-lived code for token exchange. 7 11 Code, 12 + /// Implicit flow — server returns an access token directly (not recommended for new clients). 8 13 Token, 9 - // OIDC (https://openid.net/specs/oauth-v2-multiple-response-types-1_0.html) 14 + /// OpenID Connect ID token response (see the 15 + /// [multiple response types spec](https://openid.net/specs/oauth-v2-multiple-response-types-1_0.html)). 10 16 IdToken, 11 17 } 12 18 19 + /// The `response_mode` parameter controlling how the authorization response is returned. 20 + /// 21 + /// Defaults to `query` for `code` response type and `fragment` for `token`. 13 22 #[derive(Serialize, Deserialize, Debug)] 14 23 #[serde(rename_all = "snake_case")] 15 24 pub enum AuthorizationResponseMode { 25 + /// Parameters are appended as query string components to the redirect URI. 16 26 Query, 27 + /// Parameters are appended as URI fragment components to the redirect URI. 17 28 Fragment, 18 - // https://openid.net/specs/oauth-v2-form-post-response-mode-1_0.html#FormPostResponseMode 29 + /// Parameters are encoded in an HTML form POSTed to the redirect URI. 30 + /// 31 + /// <https://openid.net/specs/oauth-v2-form-post-response-mode-1_0.html#FormPostResponseMode> 19 32 FormPost, 20 33 } 21 34 35 + /// PKCE code challenge method, as defined in RFC 7636. 36 + /// 37 + /// `S256` is strongly preferred; `Plain` should only be used when the client 38 + /// cannot perform SHA-256. 22 39 #[derive(Serialize, Deserialize, Debug)] 23 40 pub enum AuthorizationCodeChallengeMethod { 41 + /// SHA-256 hash of the code verifier, base64url-encoded (recommended). 24 42 S256, 43 + /// Raw code verifier used as the challenge (not recommended). 25 44 #[serde(rename = "plain")] 26 45 Plain, 27 46 } 28 47 48 + /// Parameters for a Pushed Authorization Request (PAR), as defined in RFC 9126. 49 + /// 50 + /// PAR allows clients to push their authorization parameters directly to the 51 + /// authorization server before redirecting the user, improving security by keeping 52 + /// parameters out of the browser URL. 29 53 #[derive(Serialize, Deserialize, Debug)] 30 54 pub struct ParParameters<'a> { 31 - // https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.1 55 + /// The response type to request (e.g. `code`). 56 + /// 57 + /// <https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.1> 32 58 pub response_type: AuthorizationResponseType, 59 + /// The redirect URI where the authorization response will be sent. 33 60 #[serde(borrow)] 34 61 pub redirect_uri: CowStr<'a>, 62 + /// An opaque CSRF state value to be echoed back in the callback. 35 63 pub state: CowStr<'a>, 64 + /// Space-separated list of requested scopes. 36 65 pub scope: Option<CowStr<'a>>, 37 - // https://openid.net/specs/oauth-v2-multiple-response-types-1_0.html#ResponseModes 66 + /// How the authorization response parameters are delivered to the client. 67 + /// 68 + /// <https://openid.net/specs/oauth-v2-multiple-response-types-1_0.html#ResponseModes> 38 69 pub response_mode: Option<AuthorizationResponseMode>, 39 - // https://datatracker.ietf.org/doc/html/rfc7636#section-4.3 70 + /// The PKCE code challenge derived from the code verifier. 71 + /// 72 + /// <https://datatracker.ietf.org/doc/html/rfc7636#section-4.3> 40 73 pub code_challenge: CowStr<'a>, 74 + /// The method used to derive the code challenge. 41 75 pub code_challenge_method: AuthorizationCodeChallengeMethod, 42 - // https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest 76 + /// Hint to pre-fill the login form with a handle or email. 77 + /// 78 + /// <https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest> 43 79 pub login_hint: Option<CowStr<'a>>, 80 + /// Prompt hint controlling authorization server UI behavior. 44 81 pub prompt: Option<CowStr<'a>>, 45 82 } 46 83 84 + /// The `grant_type` parameter for a token endpoint request. 47 85 #[derive(Serialize, Deserialize)] 48 86 #[serde(rename_all = "snake_case")] 49 87 pub enum TokenGrantType { 88 + /// Exchange an authorization code for tokens. 50 89 AuthorizationCode, 90 + /// Use a refresh token to obtain a new access token. 51 91 RefreshToken, 52 92 } 53 93 94 + /// Parameters for exchanging an authorization code for tokens (RFC 6749 §4.1.3). 54 95 #[derive(Serialize, Deserialize)] 55 96 pub struct TokenRequestParameters<'a> { 56 - // https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.3 97 + /// Must be `authorization_code` for the authorization code grant. 98 + /// 99 + /// <https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.3> 57 100 pub grant_type: TokenGrantType, 101 + /// The authorization code received from the authorization server. 58 102 #[serde(borrow)] 59 103 pub code: CowStr<'a>, 104 + /// The redirect URI used in the original authorization request. 60 105 pub redirect_uri: CowStr<'a>, 61 - // https://datatracker.ietf.org/doc/html/rfc7636#section-4.5 106 + /// The PKCE code verifier that was used to generate the code challenge (RFC 7636 §4.5). 107 + /// 108 + /// <https://datatracker.ietf.org/doc/html/rfc7636#section-4.5> 62 109 pub code_verifier: CowStr<'a>, 63 110 } 64 111 112 + /// Parameters for refreshing an access token using a refresh token (RFC 6749 §6). 65 113 #[derive(Serialize, Deserialize)] 66 114 pub struct RefreshRequestParameters<'a> { 67 - // https://datatracker.ietf.org/doc/html/rfc6749#section-6 115 + /// Must be `refresh_token` for the refresh grant. 116 + /// 117 + /// <https://datatracker.ietf.org/doc/html/rfc6749#section-6> 68 118 pub grant_type: TokenGrantType, 119 + /// The refresh token previously issued to the client. 69 120 #[serde(borrow)] 70 121 pub refresh_token: CowStr<'a>, 122 + /// Optional scope to request; must not exceed the originally granted scope. 71 123 pub scope: Option<CowStr<'a>>, 72 124 } 73 125 74 - // https://datatracker.ietf.org/doc/html/rfc7009#section-2.1 126 + /// Parameters for a token revocation request (RFC 7009 §2.1). 127 + /// 128 + /// Sent to the revocation endpoint to invalidate an access or refresh token, 129 + /// for example on logout. 130 + /// 131 + /// <https://datatracker.ietf.org/doc/html/rfc7009#section-2.1> 75 132 #[derive(Serialize, Deserialize)] 76 133 pub struct RevocationRequestParameters<'a> { 134 + /// The token to be revoked. 77 135 #[serde(borrow)] 78 136 pub token: CowStr<'a>, 79 137 // ?
+18 -1
crates/jacquard-oauth/src/types/response.rs
··· 1 1 use serde::{Deserialize, Serialize}; 2 2 use smol_str::SmolStr; 3 3 4 + /// The response from a Pushed Authorization Request (PAR) endpoint. 5 + /// 6 + /// The returned `request_uri` is used in place of inline authorization parameters 7 + /// when redirecting the user to the authorization server. 4 8 #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] 5 9 pub struct OAuthParResponse { 10 + /// A short-lived URI representing the pushed authorization request. 6 11 pub request_uri: SmolStr, 12 + /// Number of seconds until the `request_uri` expires. 7 13 pub expires_in: Option<u32>, 8 14 } 9 15 16 + /// The token type returned by the authorization server, indicating how to present the token. 10 17 #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] 11 18 pub enum OAuthTokenType { 19 + /// Demonstration of Proof of Possession (DPoP) token (RFC 9449). Requires a DPoP proof header. 12 20 DPoP, 21 + /// Standard Bearer token (RFC 6750). Sent as `Authorization: Bearer <token>`. 13 22 Bearer, 14 23 } 15 24 16 25 impl OAuthTokenType { 26 + /// Returns the string representation used in HTTP `Authorization` headers. 17 27 pub fn as_str(&self) -> &'static str { 18 28 match self { 19 29 OAuthTokenType::DPoP => "DPoP", ··· 22 32 } 23 33 } 24 34 25 - // https://datatracker.ietf.org/doc/html/rfc6749#section-5.1 35 + /// A successful token response from the authorization server (RFC 6749 §5.1). 36 + /// <https://datatracker.ietf.org/doc/html/rfc6749#section-5.1> 26 37 #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] 27 38 pub struct OAuthTokenResponse { 39 + /// The issued access token. 28 40 pub access_token: SmolStr, 41 + /// The type of token, indicating the presentation scheme to use. 29 42 pub token_type: OAuthTokenType, 43 + /// Lifetime of the access token in seconds from the time of issuance. 30 44 pub expires_in: Option<i64>, 45 + /// A refresh token that can be used to obtain new access tokens. 31 46 pub refresh_token: Option<SmolStr>, 47 + /// The scopes actually granted, if different from those requested. 32 48 pub scope: Option<SmolStr>, 33 49 // ATPROTO extension: add the sub claim to the token response to allow 34 50 // clients to resolve the PDS url (audience) using the did resolution 35 51 // mechanism. 52 + /// The subject (DID) the token was issued for; ATProto extension for PDS discovery. 36 53 pub sub: Option<SmolStr>, 37 54 }
+13
crates/jacquard-oauth/src/types/token.rs
··· 3 3 use jacquard_common::{CowStr, IntoStatic}; 4 4 use serde::{Deserialize, Serialize}; 5 5 6 + /// A complete set of OAuth tokens and associated claims for an authenticated session. 7 + /// 8 + /// Combines the token response with resolved identity claims to give the client 9 + /// everything it needs to make authorized requests. This is stored in the session 10 + /// and refreshed transparently by `OAuthSession`. 6 11 #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] 7 12 pub struct TokenSet<'s> { 13 + /// The issuer URL of the authorization server that issued these tokens. 8 14 #[serde(borrow)] 9 15 pub iss: CowStr<'s>, 16 + /// The subject DID identifying the authenticated user. 10 17 pub sub: Did<'s>, 18 + /// The audience (resource server URL or DID) the tokens are intended for. 11 19 pub aud: CowStr<'s>, 20 + /// The scopes granted by the authorization server. 12 21 pub scope: Option<CowStr<'s>>, 13 22 23 + /// A refresh token that can be exchanged for new access tokens. 14 24 pub refresh_token: Option<CowStr<'s>>, 25 + /// The current access token to include in API requests. 15 26 pub access_token: CowStr<'s>, 27 + /// Whether the access token must be presented as a DPoP or Bearer token. 16 28 pub token_type: OAuthTokenType, 17 29 30 + /// The point in time at which the access token expires. 18 31 pub expires_at: Option<Datetime>, 19 32 } 20 33
+22 -1
crates/jacquard-oauth/src/utils.rs
··· 9 9 10 10 use crate::{FALLBACK_ALG, types::OAuthAuthorizationServerMetadata}; 11 11 12 + /// Generate a fresh JWK secret key using the first algorithm from `allowed_algos` that is 13 + /// supported, returning `None` if none are supported. 14 + /// 15 + /// Currently only `ES256` (P-256 ECDSA) is implemented; other algorithm identifiers are skipped. 12 16 pub fn generate_key(allowed_algos: &[CowStr]) -> Option<Key> { 13 17 for alg in allowed_algos { 14 18 #[allow(clippy::single_match)] ··· 26 30 None 27 31 } 28 32 33 + /// Generate a cryptographically random 16-byte nonce encoded as base64url (no padding). 29 34 pub fn generate_nonce() -> CowStr<'static> { 30 35 URL_SAFE_NO_PAD 31 36 .encode(get_random_values::<_, 16>(&mut ThreadRng::default())) 32 37 .into() 33 38 } 34 39 40 + /// Generate a cryptographically random 43-byte PKCE code verifier encoded as base64url (no padding). 35 41 pub fn generate_verifier() -> CowStr<'static> { 36 42 URL_SAFE_NO_PAD 37 43 .encode(get_random_values::<_, 43>(&mut ThreadRng::default())) 38 44 .into() 39 45 } 40 46 47 + /// Fill a `LEN`-byte array with cryptographically random bytes from `rng`. 41 48 pub fn get_random_values<R, const LEN: usize>(rng: &mut R) -> [u8; LEN] 42 49 where 43 50 R: RngCore + CryptoRng, ··· 47 54 bytes 48 55 } 49 56 50 - // 256K > ES (256 > 384 > 512) > PS (256 > 384 > 512) > RS (256 > 384 > 512) > other (in original order) 57 + /// Compare two algorithm identifier strings by preference order for DPoP key generation. 58 + /// 59 + /// The ordering is: ES256K > ES (256 > 384 > 512) > PS (256 > 384 > 512) > RS (256 > 384 > 512) > other. 60 + /// Algorithms within the same family are ordered by key length, preferring shorter (faster) keys first. 51 61 pub fn compare_algos(a: &CowStr, b: &CowStr) -> Ordering { 52 62 if a.as_ref() == "ES256K" { 53 63 return Ordering::Less; ··· 73 83 Ordering::Equal 74 84 } 75 85 86 + /// Generate a PKCE challenge/verifier pair. 87 + /// 88 + /// Returns `(challenge, verifier)` where `challenge` is the base64url-encoded SHA-256 hash 89 + /// of the verifier, per [RFC 7636 §4.1](https://datatracker.ietf.org/doc/html/rfc7636#section-4.1). 90 + /// The verifier must be kept secret and sent at the token endpoint; the challenge is sent at 91 + /// the authorization endpoint. 76 92 pub fn generate_pkce() -> (CowStr<'static>, CowStr<'static>) { 77 93 // https://datatracker.ietf.org/doc/html/rfc7636#section-4.1 78 94 let verifier = generate_verifier(); ··· 84 100 ) 85 101 } 86 102 103 + /// Generate a DPoP signing key compatible with the algorithms advertised by the authorization server. 104 + /// 105 + /// Reads `dpop_signing_alg_values_supported` from the server metadata, sorts by preference 106 + /// using [`compare_algos`], and attempts to generate a key for the most preferred supported 107 + /// algorithm. Falls back to [`crate::FALLBACK_ALG`] if the server does not advertise any algorithms. 87 108 pub fn generate_dpop_key(metadata: &OAuthAuthorizationServerMetadata) -> Option<Key> { 88 109 let mut algs = metadata 89 110 .dpop_signing_alg_values_supported
+3 -2
crates/jacquard/src/lib.rs
··· 224 224 pub mod client; 225 225 226 226 #[cfg(feature = "streaming")] 227 - /// Experimental streaming endpoints 227 + /// Streaming endpoints 228 228 pub mod streaming; 229 229 230 230 #[cfg(feature = "api_bluesky")] ··· 247 247 248 248 /// Prelude with the extension traits you're likely to want and some other stuff 249 249 pub mod prelude { 250 + pub use crate::client::Agent; 250 251 pub use crate::client::AgentSession; 251 252 #[cfg(feature = "api")] 252 253 pub use crate::client::AgentSessionExt; ··· 254 255 pub use crate::common::http_client::HttpClient; 255 256 pub use crate::common::xrpc::XrpcClient; 256 257 pub use crate::common::xrpc::XrpcExt; 257 - pub use crate::identity::PublicResolver; 258 + pub use crate::identity::JacquardResolver; 258 259 pub use crate::identity::resolver::IdentityResolver; 259 260 pub use crate::oauth::dpop::DpopExt; 260 261 pub use crate::oauth::resolver::OAuthResolver;