1use serde::{Deserialize, Serialize};
8use ulid::Ulid;
9
10use crate::UrlBuilder;
11pub use crate::traits::*;
12
13#[derive(Deserialize, Serialize, Clone, Debug)]
14#[serde(rename_all = "snake_case", tag = "kind")]
15pub enum PostAuthAction {
16 ContinueAuthorizationGrant {
17 id: Ulid,
18 },
19 ContinueDeviceCodeGrant {
20 id: Ulid,
21 },
22 ContinueCompatSsoLogin {
23 id: Ulid,
24 },
25 ChangePassword,
26 LinkUpstream {
27 id: Ulid,
28 },
29 ManageAccount {
30 #[serde(flatten)]
31 action: Option<AccountAction>,
32 },
33}
34
35impl PostAuthAction {
36 #[must_use]
37 pub const fn continue_grant(id: Ulid) -> Self {
38 PostAuthAction::ContinueAuthorizationGrant { id }
39 }
40
41 #[must_use]
42 pub const fn continue_device_code_grant(id: Ulid) -> Self {
43 PostAuthAction::ContinueDeviceCodeGrant { id }
44 }
45
46 #[must_use]
47 pub const fn continue_compat_sso_login(id: Ulid) -> Self {
48 PostAuthAction::ContinueCompatSsoLogin { id }
49 }
50
51 #[must_use]
52 pub const fn link_upstream(id: Ulid) -> Self {
53 PostAuthAction::LinkUpstream { id }
54 }
55
56 #[must_use]
57 pub const fn manage_account(action: Option<AccountAction>) -> Self {
58 PostAuthAction::ManageAccount { action }
59 }
60
61 pub fn go_next(&self, url_builder: &UrlBuilder) -> axum::response::Redirect {
62 match self {
63 Self::ContinueAuthorizationGrant { id } => {
64 url_builder.redirect(&ContinueAuthorizationGrant(*id))
65 }
66 Self::ContinueDeviceCodeGrant { id } => {
67 url_builder.redirect(&DeviceCodeConsent::new(*id))
68 }
69 Self::ContinueCompatSsoLogin { id } => {
70 url_builder.redirect(&CompatLoginSsoComplete::new(*id, None))
71 }
72 Self::ChangePassword => url_builder.redirect(&AccountPasswordChange),
73 Self::LinkUpstream { id } => url_builder.redirect(&UpstreamOAuth2Link::new(*id)),
74 Self::ManageAccount { action } => url_builder.redirect(&Account {
75 action: action.clone(),
76 }),
77 }
78 }
79}
80
81#[derive(Default, Debug, Clone)]
83pub struct OidcConfiguration;
84
85impl SimpleRoute for OidcConfiguration {
86 const PATH: &'static str = "/.well-known/openid-configuration";
87}
88
89#[derive(Default, Debug, Clone)]
91pub struct Webfinger;
92
93impl SimpleRoute for Webfinger {
94 const PATH: &'static str = "/.well-known/webfinger";
95}
96
97pub struct ChangePasswordDiscovery;
99
100impl SimpleRoute for ChangePasswordDiscovery {
101 const PATH: &'static str = "/.well-known/change-password";
102}
103
104#[derive(Default, Debug, Clone)]
106pub struct OAuth2Keys;
107
108impl SimpleRoute for OAuth2Keys {
109 const PATH: &'static str = "/oauth2/keys.json";
110}
111
112#[derive(Default, Debug, Clone)]
114pub struct OidcUserinfo;
115
116impl SimpleRoute for OidcUserinfo {
117 const PATH: &'static str = "/oauth2/userinfo";
118}
119
120#[derive(Default, Debug, Clone)]
122pub struct OAuth2Introspection;
123
124impl SimpleRoute for OAuth2Introspection {
125 const PATH: &'static str = "/oauth2/introspect";
126}
127
128#[derive(Default, Debug, Clone)]
130pub struct OAuth2Revocation;
131
132impl SimpleRoute for OAuth2Revocation {
133 const PATH: &'static str = "/oauth2/revoke";
134}
135
136#[derive(Default, Debug, Clone)]
138pub struct OAuth2TokenEndpoint;
139
140impl SimpleRoute for OAuth2TokenEndpoint {
141 const PATH: &'static str = "/oauth2/token";
142}
143
144#[derive(Default, Debug, Clone)]
146pub struct OAuth2RegistrationEndpoint;
147
148impl SimpleRoute for OAuth2RegistrationEndpoint {
149 const PATH: &'static str = "/oauth2/registration";
150}
151
152#[derive(Default, Debug, Clone)]
154pub struct OAuth2AuthorizationEndpoint;
155
156impl SimpleRoute for OAuth2AuthorizationEndpoint {
157 const PATH: &'static str = "/authorize";
158}
159
160#[derive(Default, Debug, Clone)]
162pub struct Index;
163
164impl SimpleRoute for Index {
165 const PATH: &'static str = "/";
166}
167
168#[derive(Default, Debug, Clone)]
170pub struct Healthcheck;
171
172impl SimpleRoute for Healthcheck {
173 const PATH: &'static str = "/health";
174}
175
176#[derive(Default, Debug, Clone)]
178pub struct Login {
179 post_auth_action: Option<PostAuthAction>,
180}
181
182impl Route for Login {
183 type Query = PostAuthAction;
184
185 fn route() -> &'static str {
186 "/login"
187 }
188
189 fn query(&self) -> Option<&Self::Query> {
190 self.post_auth_action.as_ref()
191 }
192}
193
194impl Login {
195 #[must_use]
196 pub const fn and_then(action: PostAuthAction) -> Self {
197 Self {
198 post_auth_action: Some(action),
199 }
200 }
201
202 #[must_use]
203 pub const fn and_continue_grant(id: Ulid) -> Self {
204 Self {
205 post_auth_action: Some(PostAuthAction::continue_grant(id)),
206 }
207 }
208
209 #[must_use]
210 pub const fn and_continue_device_code_grant(id: Ulid) -> Self {
211 Self {
212 post_auth_action: Some(PostAuthAction::continue_device_code_grant(id)),
213 }
214 }
215
216 #[must_use]
217 pub const fn and_continue_compat_sso_login(id: Ulid) -> Self {
218 Self {
219 post_auth_action: Some(PostAuthAction::continue_compat_sso_login(id)),
220 }
221 }
222
223 #[must_use]
224 pub const fn and_link_upstream(id: Ulid) -> Self {
225 Self {
226 post_auth_action: Some(PostAuthAction::link_upstream(id)),
227 }
228 }
229
230 #[must_use]
232 pub fn post_auth_action(&self) -> Option<&PostAuthAction> {
233 self.post_auth_action.as_ref()
234 }
235
236 pub fn go_next(&self, url_builder: &UrlBuilder) -> axum::response::Redirect {
237 match &self.post_auth_action {
238 Some(action) => action.go_next(url_builder),
239 None => url_builder.redirect(&Index),
240 }
241 }
242}
243
244impl From<Option<PostAuthAction>> for Login {
245 fn from(post_auth_action: Option<PostAuthAction>) -> Self {
246 Self { post_auth_action }
247 }
248}
249
250#[derive(Default, Debug, Clone)]
252pub struct Logout;
253
254impl SimpleRoute for Logout {
255 const PATH: &'static str = "/logout";
256}
257
258#[derive(Default, Debug, Clone)]
260pub struct Reauth {
261 post_auth_action: Option<PostAuthAction>,
262}
263
264impl Reauth {
265 #[must_use]
266 pub fn and_then(action: PostAuthAction) -> Self {
267 Self {
268 post_auth_action: Some(action),
269 }
270 }
271
272 #[must_use]
273 pub fn and_continue_grant(data: Ulid) -> Self {
274 Self {
275 post_auth_action: Some(PostAuthAction::continue_grant(data)),
276 }
277 }
278
279 #[must_use]
280 pub fn and_continue_device_code_grant(data: Ulid) -> Self {
281 Self {
282 post_auth_action: Some(PostAuthAction::continue_device_code_grant(data)),
283 }
284 }
285
286 #[must_use]
288 pub fn post_auth_action(&self) -> Option<&PostAuthAction> {
289 self.post_auth_action.as_ref()
290 }
291
292 pub fn go_next(&self, url_builder: &UrlBuilder) -> axum::response::Redirect {
293 match &self.post_auth_action {
294 Some(action) => action.go_next(url_builder),
295 None => url_builder.redirect(&Index),
296 }
297 }
298}
299
300impl Route for Reauth {
301 type Query = PostAuthAction;
302
303 fn route() -> &'static str {
304 "/reauth"
305 }
306
307 fn query(&self) -> Option<&Self::Query> {
308 self.post_auth_action.as_ref()
309 }
310}
311
312impl From<Option<PostAuthAction>> for Reauth {
313 fn from(post_auth_action: Option<PostAuthAction>) -> Self {
314 Self { post_auth_action }
315 }
316}
317
318#[derive(Default, Debug, Clone)]
320pub struct Register {
321 post_auth_action: Option<PostAuthAction>,
322}
323
324impl Register {
325 #[must_use]
326 pub fn and_then(action: PostAuthAction) -> Self {
327 Self {
328 post_auth_action: Some(action),
329 }
330 }
331
332 #[must_use]
333 pub fn and_continue_grant(data: Ulid) -> Self {
334 Self {
335 post_auth_action: Some(PostAuthAction::continue_grant(data)),
336 }
337 }
338
339 #[must_use]
340 pub fn and_continue_compat_sso_login(data: Ulid) -> Self {
341 Self {
342 post_auth_action: Some(PostAuthAction::continue_compat_sso_login(data)),
343 }
344 }
345
346 #[must_use]
348 pub fn post_auth_action(&self) -> Option<&PostAuthAction> {
349 self.post_auth_action.as_ref()
350 }
351
352 pub fn go_next(&self, url_builder: &UrlBuilder) -> axum::response::Redirect {
353 match &self.post_auth_action {
354 Some(action) => action.go_next(url_builder),
355 None => url_builder.redirect(&Index),
356 }
357 }
358}
359
360impl Route for Register {
361 type Query = PostAuthAction;
362
363 fn route() -> &'static str {
364 "/register"
365 }
366
367 fn query(&self) -> Option<&Self::Query> {
368 self.post_auth_action.as_ref()
369 }
370}
371
372impl From<Option<PostAuthAction>> for Register {
373 fn from(post_auth_action: Option<PostAuthAction>) -> Self {
374 Self { post_auth_action }
375 }
376}
377
378#[derive(Default, Debug, Clone, Serialize, Deserialize)]
380pub struct PasswordRegister {
381 username: Option<String>,
382
383 #[serde(flatten)]
384 post_auth_action: Option<PostAuthAction>,
385}
386
387impl PasswordRegister {
388 #[must_use]
389 pub fn and_then(mut self, action: PostAuthAction) -> Self {
390 self.post_auth_action = Some(action);
391 self
392 }
393
394 #[must_use]
395 pub fn and_continue_grant(mut self, data: Ulid) -> Self {
396 self.post_auth_action = Some(PostAuthAction::continue_grant(data));
397 self
398 }
399
400 #[must_use]
401 pub fn and_continue_compat_sso_login(mut self, data: Ulid) -> Self {
402 self.post_auth_action = Some(PostAuthAction::continue_compat_sso_login(data));
403 self
404 }
405
406 #[must_use]
408 pub fn post_auth_action(&self) -> Option<&PostAuthAction> {
409 self.post_auth_action.as_ref()
410 }
411
412 #[must_use]
414 pub fn username(&self) -> Option<&str> {
415 self.username.as_deref()
416 }
417
418 pub fn go_next(&self, url_builder: &UrlBuilder) -> axum::response::Redirect {
419 match &self.post_auth_action {
420 Some(action) => action.go_next(url_builder),
421 None => url_builder.redirect(&Index),
422 }
423 }
424}
425
426impl Route for PasswordRegister {
427 type Query = Self;
428
429 fn route() -> &'static str {
430 "/register/password"
431 }
432
433 fn query(&self) -> Option<&Self::Query> {
434 Some(self)
435 }
436}
437
438impl From<Option<PostAuthAction>> for PasswordRegister {
439 fn from(post_auth_action: Option<PostAuthAction>) -> Self {
440 Self {
441 username: None,
442 post_auth_action,
443 }
444 }
445}
446
447#[derive(Debug, Clone)]
449pub struct RegisterDisplayName {
450 id: Ulid,
451}
452
453impl RegisterDisplayName {
454 #[must_use]
455 pub fn new(id: Ulid) -> Self {
456 Self { id }
457 }
458}
459
460impl Route for RegisterDisplayName {
461 type Query = ();
462 fn route() -> &'static str {
463 "/register/steps/{id}/display-name"
464 }
465
466 fn path(&self) -> std::borrow::Cow<'static, str> {
467 format!("/register/steps/{}/display-name", self.id).into()
468 }
469}
470
471#[derive(Debug, Clone)]
473pub struct RegisterVerifyEmail {
474 id: Ulid,
475}
476
477impl RegisterVerifyEmail {
478 #[must_use]
479 pub fn new(id: Ulid) -> Self {
480 Self { id }
481 }
482}
483
484impl Route for RegisterVerifyEmail {
485 type Query = ();
486 fn route() -> &'static str {
487 "/register/steps/{id}/verify-email"
488 }
489
490 fn path(&self) -> std::borrow::Cow<'static, str> {
491 format!("/register/steps/{}/verify-email", self.id).into()
492 }
493}
494
495#[derive(Debug, Clone)]
497pub struct RegisterFinish {
498 id: Ulid,
499}
500
501impl RegisterFinish {
502 #[must_use]
503 pub const fn new(id: Ulid) -> Self {
504 Self { id }
505 }
506}
507
508impl Route for RegisterFinish {
509 type Query = ();
510 fn route() -> &'static str {
511 "/register/steps/{id}/finish"
512 }
513
514 fn path(&self) -> std::borrow::Cow<'static, str> {
515 format!("/register/steps/{}/finish", self.id).into()
516 }
517}
518
519#[derive(Debug, Clone, Serialize, Deserialize)]
521#[serde(tag = "action")]
522pub enum AccountAction {
523 #[serde(rename = "org.matrix.profile")]
524 OrgMatrixProfile,
525 #[serde(rename = "profile")]
526 Profile,
527
528 #[serde(rename = "org.matrix.sessions_list")]
529 OrgMatrixSessionsList,
530 #[serde(rename = "sessions_list")]
531 SessionsList,
532
533 #[serde(rename = "org.matrix.session_view")]
534 OrgMatrixSessionView { device_id: String },
535 #[serde(rename = "session_view")]
536 SessionView { device_id: String },
537
538 #[serde(rename = "org.matrix.session_end")]
539 OrgMatrixSessionEnd { device_id: String },
540 #[serde(rename = "session_end")]
541 SessionEnd { device_id: String },
542
543 #[serde(rename = "org.matrix.cross_signing_reset")]
544 OrgMatrixCrossSigningReset,
545}
546
547#[derive(Default, Debug, Clone)]
549pub struct Account {
550 action: Option<AccountAction>,
551}
552
553impl Route for Account {
554 type Query = AccountAction;
555
556 fn route() -> &'static str {
557 "/account/"
558 }
559
560 fn query(&self) -> Option<&Self::Query> {
561 self.action.as_ref()
562 }
563}
564
565#[derive(Default, Debug, Clone)]
567pub struct AccountWildcard;
568
569impl SimpleRoute for AccountWildcard {
570 const PATH: &'static str = "/account/{*rest}";
571}
572
573#[derive(Default, Debug, Clone)]
578pub struct AccountPasswordChange;
579
580impl SimpleRoute for AccountPasswordChange {
581 const PATH: &'static str = "/account/password/change";
582}
583
584#[derive(Debug, Clone)]
586pub struct ContinueAuthorizationGrant(pub Ulid);
587
588impl Route for ContinueAuthorizationGrant {
589 type Query = ();
590 fn route() -> &'static str {
591 "/authorize/{grant_id}"
592 }
593
594 fn path(&self) -> std::borrow::Cow<'static, str> {
595 format!("/authorize/{}", self.0).into()
596 }
597}
598
599#[derive(Debug, Clone)]
601pub struct Consent(pub Ulid);
602
603impl Route for Consent {
604 type Query = ();
605 fn route() -> &'static str {
606 "/consent/{grant_id}"
607 }
608
609 fn path(&self) -> std::borrow::Cow<'static, str> {
610 format!("/consent/{}", self.0).into()
611 }
612}
613
614pub struct CompatLogin;
616
617impl SimpleRoute for CompatLogin {
618 const PATH: &'static str = "/_matrix/client/{version}/login";
619}
620
621pub struct CompatLogout;
623
624impl SimpleRoute for CompatLogout {
625 const PATH: &'static str = "/_matrix/client/{version}/logout";
626}
627
628pub struct CompatRefresh;
630
631impl SimpleRoute for CompatRefresh {
632 const PATH: &'static str = "/_matrix/client/{version}/refresh";
633}
634
635pub struct CompatLoginSsoRedirect;
637
638impl SimpleRoute for CompatLoginSsoRedirect {
639 const PATH: &'static str = "/_matrix/client/{version}/login/sso/redirect";
640}
641
642pub struct CompatLoginSsoRedirectSlash;
647
648impl SimpleRoute for CompatLoginSsoRedirectSlash {
649 const PATH: &'static str = "/_matrix/client/{version}/login/sso/redirect/";
650}
651
652pub struct CompatLoginSsoRedirectIdp;
654
655impl SimpleRoute for CompatLoginSsoRedirectIdp {
656 const PATH: &'static str = "/_matrix/client/{version}/login/sso/redirect/{idp}";
657}
658
659#[derive(Debug, Serialize, Deserialize, Clone, Copy)]
660#[serde(rename_all = "lowercase")]
661pub enum CompatLoginSsoAction {
662 Login,
663 Register,
664}
665
666#[derive(Debug, Serialize, Deserialize, Clone, Copy)]
667pub struct CompatLoginSsoActionParams {
668 #[serde(rename = "org.matrix.msc3824.action")]
669 action: CompatLoginSsoAction,
670}
671
672pub struct CompatLoginSsoComplete {
674 id: Ulid,
675 query: Option<CompatLoginSsoActionParams>,
676}
677
678impl CompatLoginSsoComplete {
679 #[must_use]
680 pub fn new(id: Ulid, action: Option<CompatLoginSsoAction>) -> Self {
681 Self {
682 id,
683 query: action.map(|action| CompatLoginSsoActionParams { action }),
684 }
685 }
686}
687
688impl Route for CompatLoginSsoComplete {
689 type Query = CompatLoginSsoActionParams;
690
691 fn query(&self) -> Option<&Self::Query> {
692 self.query.as_ref()
693 }
694
695 fn route() -> &'static str {
696 "/complete-compat-sso/{grant_id}"
697 }
698
699 fn path(&self) -> std::borrow::Cow<'static, str> {
700 format!("/complete-compat-sso/{}", self.id).into()
701 }
702}
703
704pub struct UpstreamOAuth2Authorize {
706 id: Ulid,
707 post_auth_action: Option<PostAuthAction>,
708}
709
710impl UpstreamOAuth2Authorize {
711 #[must_use]
712 pub const fn new(id: Ulid) -> Self {
713 Self {
714 id,
715 post_auth_action: None,
716 }
717 }
718
719 #[must_use]
720 pub fn and_then(mut self, action: PostAuthAction) -> Self {
721 self.post_auth_action = Some(action);
722 self
723 }
724}
725
726impl Route for UpstreamOAuth2Authorize {
727 type Query = PostAuthAction;
728 fn route() -> &'static str {
729 "/upstream/authorize/{provider_id}"
730 }
731
732 fn path(&self) -> std::borrow::Cow<'static, str> {
733 format!("/upstream/authorize/{}", self.id).into()
734 }
735
736 fn query(&self) -> Option<&Self::Query> {
737 self.post_auth_action.as_ref()
738 }
739}
740
741pub struct UpstreamOAuth2Callback {
743 id: Ulid,
744}
745
746impl UpstreamOAuth2Callback {
747 #[must_use]
748 pub const fn new(id: Ulid) -> Self {
749 Self { id }
750 }
751}
752
753impl Route for UpstreamOAuth2Callback {
754 type Query = ();
755 fn route() -> &'static str {
756 "/upstream/callback/{provider_id}"
757 }
758
759 fn path(&self) -> std::borrow::Cow<'static, str> {
760 format!("/upstream/callback/{}", self.id).into()
761 }
762}
763
764pub struct UpstreamOAuth2Link {
766 id: Ulid,
767}
768
769impl UpstreamOAuth2Link {
770 #[must_use]
771 pub const fn new(id: Ulid) -> Self {
772 Self { id }
773 }
774}
775
776impl Route for UpstreamOAuth2Link {
777 type Query = ();
778 fn route() -> &'static str {
779 "/upstream/link/{link_id}"
780 }
781
782 fn path(&self) -> std::borrow::Cow<'static, str> {
783 format!("/upstream/link/{}", self.id).into()
784 }
785}
786
787#[derive(Default, Serialize, Deserialize, Debug, Clone)]
789pub struct DeviceCodeLink {
790 code: Option<String>,
791}
792
793impl DeviceCodeLink {
794 #[must_use]
795 pub fn with_code(code: String) -> Self {
796 Self { code: Some(code) }
797 }
798}
799
800impl Route for DeviceCodeLink {
801 type Query = DeviceCodeLink;
802 fn route() -> &'static str {
803 "/link"
804 }
805
806 fn query(&self) -> Option<&Self::Query> {
807 Some(self)
808 }
809}
810
811#[derive(Default, Serialize, Deserialize, Debug, Clone)]
813pub struct DeviceCodeConsent {
814 id: Ulid,
815}
816
817impl Route for DeviceCodeConsent {
818 type Query = ();
819 fn route() -> &'static str {
820 "/device/{device_code_id}"
821 }
822
823 fn path(&self) -> std::borrow::Cow<'static, str> {
824 format!("/device/{}", self.id).into()
825 }
826}
827
828impl DeviceCodeConsent {
829 #[must_use]
830 pub fn new(id: Ulid) -> Self {
831 Self { id }
832 }
833}
834
835#[derive(Default, Serialize, Deserialize, Debug, Clone)]
837pub struct OAuth2DeviceAuthorizationEndpoint;
838
839impl SimpleRoute for OAuth2DeviceAuthorizationEndpoint {
840 const PATH: &'static str = "/oauth2/device";
841}
842
843#[derive(Default, Serialize, Deserialize, Debug, Clone)]
845pub struct AccountRecoveryStart;
846
847impl SimpleRoute for AccountRecoveryStart {
848 const PATH: &'static str = "/recover";
849}
850
851#[derive(Default, Serialize, Deserialize, Debug, Clone)]
853pub struct AccountRecoveryProgress {
854 session_id: Ulid,
855}
856
857impl AccountRecoveryProgress {
858 #[must_use]
859 pub fn new(session_id: Ulid) -> Self {
860 Self { session_id }
861 }
862}
863
864impl Route for AccountRecoveryProgress {
865 type Query = ();
866 fn route() -> &'static str {
867 "/recover/progress/{session_id}"
868 }
869
870 fn path(&self) -> std::borrow::Cow<'static, str> {
871 format!("/recover/progress/{}", self.session_id).into()
872 }
873}
874
875#[derive(Default, Serialize, Deserialize, Debug, Clone)]
878pub struct AccountRecoveryFinish {
879 ticket: String,
880}
881
882impl AccountRecoveryFinish {
883 #[must_use]
884 pub fn new(ticket: String) -> Self {
885 Self { ticket }
886 }
887}
888
889impl Route for AccountRecoveryFinish {
890 type Query = AccountRecoveryFinish;
891
892 fn route() -> &'static str {
893 "/account/password/recovery"
894 }
895
896 fn query(&self) -> Option<&Self::Query> {
897 Some(self)
898 }
899}
900
901pub struct StaticAsset {
903 path: String,
904}
905
906impl StaticAsset {
907 #[must_use]
908 pub fn new(path: String) -> Self {
909 Self { path }
910 }
911}
912
913impl Route for StaticAsset {
914 type Query = ();
915 fn route() -> &'static str {
916 "/assets/"
917 }
918
919 fn path(&self) -> std::borrow::Cow<'static, str> {
920 format!("/assets/{}", self.path).into()
921 }
922}
923
924pub struct GraphQL;
926
927impl SimpleRoute for GraphQL {
928 const PATH: &'static str = "/graphql";
929}
930
931pub struct GraphQLPlayground;
933
934impl SimpleRoute for GraphQLPlayground {
935 const PATH: &'static str = "/graphql/playground";
936}
937
938pub struct ApiSpec;
940
941impl SimpleRoute for ApiSpec {
942 const PATH: &'static str = "/api/spec.json";
943}
944
945pub struct ApiDoc;
947
948impl SimpleRoute for ApiDoc {
949 const PATH: &'static str = "/api/doc/";
950}
951
952pub struct ApiDocCallback;
954
955impl SimpleRoute for ApiDocCallback {
956 const PATH: &'static str = "/api/doc/oauth2-callback";
957}