summary refs log tree commit diff
path: root/src/components
diff options
context:
space:
mode:
authorVika <vika@fireburn.ru>2024-11-15 07:17:20 +0300
committerVika <vika@fireburn.ru>2024-11-15 07:17:20 +0300
commitef6b0f6ee3b769356f7bc852eb240e26f2d895cf (patch)
tree59561fa18c119117b003856a0e644e141eef8342 /src/components
parent64b9bf0ec12d33a9bb717e6a8cb8f0ca4d8bf991 (diff)
downloadbowl-ef6b0f6ee3b769356f7bc852eb240e26f2d895cf.tar.zst
Actually use refresh tokens
This code is untested. I guess I'll need to revisit this in about a
week, when my access token expires. Then I'll be able to see if it
correctly refreshes.
Diffstat (limited to 'src/components')
-rw-r--r--src/components/signin.rs154
1 files changed, 65 insertions, 89 deletions
diff --git a/src/components/signin.rs b/src/components/signin.rs
index 156686e..53c3670 100644
--- a/src/components/signin.rs
+++ b/src/components/signin.rs
@@ -37,7 +37,7 @@ pub struct SignIn {
 }
 
 #[derive(Debug, thiserror::Error)]
-enum Error {
+pub enum Error {
     #[error("glib error: {0}")]
     Glib(#[from] glib::Error),
     #[error("indieauth error: {0}")]
@@ -67,6 +67,66 @@ pub enum Input {
     Callback(Result<AuthorizationResponse, Error>),
 }
 
+pub async fn get_metadata(http: soup::Session, url: glib::Uri) -> Result<(Metadata, glib::Uri), Error> {
+    // Fire off a speculative request at the well-known URI. This could
+    // improve UX by parallelizing how we query the user's website.
+    //
+    // Note that exposing the metadata at the .well-known URI is
+    // RECOMMENDED though optional according to IndieAuth specification
+    // § 4.1.1, so we could use that if it's there to speed up the
+    // process.
+    let metadata = relm4::spawn_local(
+        SignIn::well_known_metadata(http.clone(), url.clone())
+    );
+    let msg = soup::Message::from_uri("GET", &url);
+    let body = http.send_future(&msg, glib::Priority::DEFAULT).await?;
+
+    let mf2 = microformats::from_reader(
+        std::io::BufReader::new(body.into_read()),
+        url.to_string().parse().unwrap()
+    )?;
+
+    let rels = mf2.rels.by_rels();
+    let metadata_url = if let Some(url) = rels
+        .get("indieauth-metadata")
+        .map(Vec::as_slice)
+        .and_then(<[_]>::first)
+    {
+        glib::Uri::parse(url.as_str(), glib::UriFlags::NONE).unwrap()
+    } else {
+        // I intentionally refuse to support older IndieAuth versions.
+        // The new versions are superior by providing more features that
+        // were previously proprietary extensions, and are more clearer in
+        // general.
+        return Err(Error::MetadataNotFound)
+    };
+
+    let micropub_uri = if let Some(url) = rels
+        .get("micropub")
+        .map(Vec::as_slice)
+        .and_then(<[_]>::first)
+    {
+        glib::Uri::parse(url.as_str(), glib::UriFlags::NONE).unwrap()
+    } else {
+        return Err(Error::MicropubLinkNotFound)
+    };
+
+    if let Ok(Some(metadata)) = metadata.await {
+        Ok((metadata, micropub_uri))
+    } else {
+        let msg = soup::Message::from_uri("GET", &metadata_url);
+        msg.request_headers().unwrap().append("Accept", "application/json");
+        match http.send_and_read_future(&msg, glib::Priority::DEFAULT).await {
+            Ok(body) if msg.status() == soup::Status::Ok => {
+                let metadata = serde_json::from_slice(&body)?;
+                Ok((metadata, micropub_uri))
+            },
+            Ok(_) => Err(Error::MetadataEndpointFailed(msg.status())),
+            Err(err) => Err(err.into()),
+        }
+    }
+}
+
 fn callback_handler(sender: AsyncComponentSender<SignIn>) -> impl Fn(&soup::Server, &soup::ServerMessage, &str, std::collections::HashMap<&str, &str>) {
     move |server, msg, _, _| {
         let server = ObjectExt::downgrade(server);
@@ -125,7 +185,7 @@ fn callback_handler(sender: AsyncComponentSender<SignIn>) -> impl Fn(&soup::Serv
 }
 
 impl SignIn {
-    fn scopes() -> kittybox_indieauth::Scopes {
+    pub fn scopes() -> kittybox_indieauth::Scopes {
         kittybox_indieauth::Scopes::new(vec![
             kittybox_indieauth::Scope::Profile,
             kittybox_indieauth::Scope::Create,
@@ -314,93 +374,9 @@ impl AsyncComponent for SignIn {
                     },
                 };
 
-                let (metadata, micropub_uri) = {
-                    // Fire off a speculative request at the well-known URI. This could
-                    // improve UX by parallelizing how we query the user's website.
-                    //
-                    // Note that exposing the metadata at the .well-known URI is
-                    // RECOMMENDED though optional according to IndieAuth specification
-                    // § 4.1.1, so we could use that if it's there to speed up the
-                    // process.
-                    let metadata = relm4::spawn_local(
-                        Self::well_known_metadata(self.http.clone(), url.clone())
-                    );
-                    let msg = soup::Message::from_uri("GET", &url);
-                    let body = match self.http.send_future(&msg, glib::Priority::DEFAULT).await {
-                        Ok(body) => body,
-                        Err(err) => {
-                            return self.bail_out(
-                                widgets, sender,
-                                err.into()
-                            );
-                        },
-                    };
-
-                    let mf2 = match microformats::from_reader(
-                        std::io::BufReader::new(body.into_read()),
-                        url.to_string().parse().unwrap()
-                    ) {
-                        Ok(mf2) => mf2,
-                        Err(err) => {
-                            return self.bail_out(
-                                widgets, sender,
-                                err.into()
-                            );
-                        }
-                    };
-
-                    let rels = mf2.rels.by_rels();
-                    let metadata_url = if let Some(url) = rels
-                        .get("indieauth-metadata")
-                        .map(Vec::as_slice)
-                        .and_then(<[_]>::first)
-                    {
-                        glib::Uri::parse(url.as_str(), glib::UriFlags::NONE).unwrap()
-                    } else {
-                        // I intentionally refuse to support older IndieAuth versions.
-                        // The new versions are superior by providing more features that
-                        // were previously proprietary extensions, and are more clearer in
-                        // general.
-                        return self.bail_out(
-                            widgets, sender,
-                            Error::MetadataNotFound
-                        );
-                    };
-
-                    let micropub_uri = if let Some(url) = rels
-                        .get("micropub")
-                        .map(Vec::as_slice)
-                        .and_then(<[_]>::first)
-                    {
-                        glib::Uri::parse(url.as_str(), glib::UriFlags::NONE).unwrap()
-                    } else {
-                        return self.bail_out(
-                            widgets, sender,
-                            Error::MicropubLinkNotFound
-                        );
-                    };
-
-                    if let Ok(Some(metadata)) = metadata.await {
-                        (metadata, micropub_uri)
-                    } else {
-                        let msg = soup::Message::from_uri("GET", &metadata_url);
-                        msg.request_headers().unwrap().append("Accept", "application/json");
-                        match self.http.send_and_read_future(&msg, glib::Priority::DEFAULT).await {
-                            Ok(body) if msg.status() == soup::Status::Ok => {
-                                match serde_json::from_slice(&body) {
-                                    Ok(metadata) => (metadata, micropub_uri),
-                                    Err(err) => return self.bail_out(widgets, sender, err.into()),
-                                }
-                            },
-                            Ok(_) => {
-                                return self.bail_out(
-                                    widgets, sender,
-                                    Error::MetadataEndpointFailed(msg.status())
-                                )
-                            },
-                            Err(err) => return self.bail_out(widgets, sender, err.into()),
-                        }
-                    }
+                let (metadata, micropub_uri) = match get_metadata(self.http.clone(), url.clone()).await {
+                    Ok((metadata, micropub_uri)) => (metadata, micropub_uri),
+                    Err(err) => return self.bail_out(widgets, sender, err)
                 };
 
                 let auth_request = AuthorizationRequest {