about summary refs log tree commit diff
diff options
context:
space:
mode:
authorVika <vika@fireburn.ru>2022-07-19 05:27:39 +0300
committerVika <vika@fireburn.ru>2022-07-19 05:32:18 +0300
commit12c96ceaeaabe0aa7d0d2c70497886d9a5610f10 (patch)
tree397b108e44ca16eb402c3725e46a73a8dc1053a3
parent1efb89a75af31d7580fdb06e3b4535bcde08e966 (diff)
kittybox-indieauth: convert Error into axum::response::Response
This requires the `axum` feature to be enabled, to prevent unwanted
dependencies (e.g. in client apps or when using a different framework,
since the library doesn't concern itself with I/O)
-rw-r--r--kittybox-rs/Cargo.lock2
-rw-r--r--kittybox-rs/Cargo.toml1
-rw-r--r--kittybox-rs/indieauth/Cargo.toml13
-rw-r--r--kittybox-rs/indieauth/src/lib.rs14
-rw-r--r--kittybox-rs/src/indieauth/mod.rs44
5 files changed, 52 insertions, 22 deletions
diff --git a/kittybox-rs/Cargo.lock b/kittybox-rs/Cargo.lock
index b44fa6f..716b10d 100644
--- a/kittybox-rs/Cargo.lock
+++ b/kittybox-rs/Cargo.lock
@@ -1544,7 +1544,9 @@ dependencies = [
 name = "kittybox-indieauth"
 version = "0.1.0"
 dependencies = [
+ "axum-core",
  "data-encoding",
+ "http",
  "rand 0.8.5",
  "serde",
  "serde_json",
diff --git a/kittybox-rs/Cargo.toml b/kittybox-rs/Cargo.toml
index 1d1d9fd..556a85b 100644
--- a/kittybox-rs/Cargo.toml
+++ b/kittybox-rs/Cargo.toml
@@ -38,6 +38,7 @@ path = "./templates"
 [dependencies.kittybox-indieauth]
 version = "0.1.0"
 path = "./indieauth"
+features = ["axum"]
 
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
diff --git a/kittybox-rs/indieauth/Cargo.toml b/kittybox-rs/indieauth/Cargo.toml
index 80511f9..a81fd51 100644
--- a/kittybox-rs/indieauth/Cargo.toml
+++ b/kittybox-rs/indieauth/Cargo.toml
@@ -3,6 +3,10 @@ name = "kittybox-indieauth"
 version = "0.1.0"
 edition = "2021"
 
+[features]
+default = []
+axum = ["axum-core", "serde_json", "http"]
+
 [dev-dependencies]
 serde_json = "^1.0.64"       # A JSON serialization file format
 serde_urlencoded = "^0.7.0"  # `x-www-form-urlencoded` meets Serde
@@ -16,3 +20,12 @@ features = ["serde"]
 [dependencies.serde]         # A generic serialization/deserialization framework
 version = "^1.0.125"
 features = ["derive"]
+[dependencies.axum-core]
+version = "^0.2.6"
+optional = true
+[dependencies.serde_json]
+version = "^1.0.64"
+optional = true
+[dependencies.http]
+version = "^0.2.7"
+optional = true
\ No newline at end of file
diff --git a/kittybox-rs/indieauth/src/lib.rs b/kittybox-rs/indieauth/src/lib.rs
index b461fea..cb99146 100644
--- a/kittybox-rs/indieauth/src/lib.rs
+++ b/kittybox-rs/indieauth/src/lib.rs
@@ -144,6 +144,7 @@ pub enum GrantResponse {
         #[serde(skip_serializing_if = "Option::is_none")]
         profile: Option<Profile>,
         access_token: String,
+        // TODO replace with std::time::Duration
         #[serde(skip_serializing_if = "Option::is_none")]
         expires_in: Option<u64>,
         #[serde(skip_serializing_if = "Option::is_none")]
@@ -176,6 +177,7 @@ pub struct TokenData {
     pub me: Url,
     pub client_id: Url,
     pub scope: Scopes,
+    // TODO replace these two with std::time::SystemTime
     #[serde(skip_serializing_if = "Option::is_none")]
     pub exp: Option<u64>,
     #[serde(skip_serializing_if = "Option::is_none")]
@@ -359,6 +361,18 @@ impl std::fmt::Display for self::Error {
     }
 }
 
+#[cfg(feature = "axum")]
+impl axum_core::response::IntoResponse for self::Error {
+    fn into_response(self) -> axum_core::response::Response {
+        use http::StatusCode;
+
+        (StatusCode::BAD_REQUEST,
+         [("Content-Type", "application/json")],
+         serde_json::to_vec(&self).unwrap())
+            .into_response()
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
diff --git a/kittybox-rs/src/indieauth/mod.rs b/kittybox-rs/src/indieauth/mod.rs
index a059211..2c15e72 100644
--- a/kittybox-rs/src/indieauth/mod.rs
+++ b/kittybox-rs/src/indieauth/mod.rs
@@ -110,37 +110,37 @@ async fn authorization_endpoint_post<A: AuthBackend>(
             GrantRequest::AuthorizationCode { code, client_id, redirect_uri, code_verifier } => {
                 let request: AuthorizationRequest = match backend.get_code(&code).await {
                     Ok(Some(request)) => request,
-                    Ok(None) => return Json(Error {
+                    Ok(None) => return Error {
                         kind: ErrorKind::InvalidGrant,
                         msg: Some("The provided authorization code is invalid.".to_string()),
                         error_uri: None
-                    }).into_response(),
+                    }.into_response(),
                     Err(err) => {
                         tracing::error!("Error retrieving auth request: {}", err);
                         return StatusCode::INTERNAL_SERVER_ERROR.into_response();
                     }
                 };
                 if client_id != request.client_id {
-                    return Json(Error {
+                    return Error {
                         kind: ErrorKind::InvalidGrant,
                         msg: Some("This authorization code isn't yours.".to_string()),
                         error_uri: None
-                    }).into_response()
+                    }.into_response()
                 }
                 if redirect_uri != request.redirect_uri {
-                    return Json(Error {
+                    return Error {
                         kind: ErrorKind::InvalidGrant,
                         msg: Some("This redirect_uri doesn't match the one the code has been sent to.".to_string()),
                         error_uri: None
-                    }).into_response()
+                    }.into_response()
                 }
                 if !request.code_challenge.verify(code_verifier) {
-                    return Json(Error {
+                    return Error {
                         kind: ErrorKind::InvalidGrant,
                         msg: Some("The PKCE challenge failed.".to_string()),
                         // are RFCs considered human-readable? 😝
                         error_uri: "https://datatracker.ietf.org/doc/html/rfc7636#section-4.6".parse().ok()
-                    }).into_response()
+                    }.into_response()
                 }
                 let profile = if request.scope
                     .map(|s| s.has(&Scope::Profile))
@@ -154,11 +154,11 @@ async fn authorization_endpoint_post<A: AuthBackend>(
 
                 Json(GrantResponse::ProfileUrl { me, profile }).into_response()
             },
-            _ => Json(Error {
+            _ => Error {
                 kind: ErrorKind::InvalidGrant,
                 msg: Some("The provided grant_type is unusable on this endpoint.".to_string()),
                 error_uri: "https://indieauth.spec.indieweb.org/#redeeming-the-authorization-code".parse().ok()
-            }).into_response()
+            }.into_response()
         }
     }
 }
@@ -209,11 +209,11 @@ async fn token_endpoint_post<A: AuthBackend>(
             // TODO load the information corresponding to the code
             let request: AuthorizationRequest = match backend.get_code(&code).await {
                 Ok(Some(request)) => request,
-                Ok(None) => return Json(Error {
+                Ok(None) => return Error {
                     kind: ErrorKind::InvalidGrant,
                     msg: Some("The provided authorization code is invalid.".to_string()),
                     error_uri: None
-                }).into_response(),
+                }.into_response(),
                 Err(err) => {
                     tracing::error!("Error retrieving auth request: {}", err);
                     return StatusCode::INTERNAL_SERVER_ERROR.into_response();
@@ -223,11 +223,11 @@ async fn token_endpoint_post<A: AuthBackend>(
             let me: url::Url = format!("https://{}/", host).parse().unwrap();
 
             let scope = if let Some(scope) = request.scope { scope } else {
-                return Json(Error {
+                return Error {
                     kind: ErrorKind::InvalidScope,
                     msg: Some("Tokens cannot be issued if no scopes are requested.".to_string()),
                     error_uri: "https://indieauth.spec.indieweb.org/#access-token-response".parse().ok()
-                }).into_response();
+                }.into_response();
             };
             if client_id != request.client_id {
                 return Error {
@@ -244,11 +244,11 @@ async fn token_endpoint_post<A: AuthBackend>(
                 }.into_response()
             }
             if !request.code_challenge.verify(code_verifier) {
-                return Json(Error {
+                return Error {
                     kind: ErrorKind::InvalidGrant,
                     msg: Some("The PKCE challenge failed.".to_string()),
                     error_uri: "https://datatracker.ietf.org/doc/html/rfc7636#section-4.6".parse().ok()
-                }).into_response();
+                }.into_response();
             }
 
             let profile = if scope.has(&Scope::Profile) {
@@ -288,11 +288,11 @@ async fn token_endpoint_post<A: AuthBackend>(
         GrantRequest::RefreshToken { refresh_token, client_id, scope } => {
             let data = match backend.get_refresh_token(&refresh_token).await {
                 Ok(Some(token)) => token,
-                Ok(None) => return Json(Error {
+                Ok(None) => return Error {
                     kind: ErrorKind::InvalidGrant,
                     msg: Some("This refresh token is not valid.".to_string()),
                     error_uri: None
-                }).into_response(),
+                }.into_response(),
                 Err(err) => {
                     tracing::error!("Error retrieving refresh token: {}", err);
                     return StatusCode::INTERNAL_SERVER_ERROR.into_response()
@@ -300,20 +300,20 @@ async fn token_endpoint_post<A: AuthBackend>(
             };
 
             if data.client_id != client_id {
-                return Json(Error {
+                return Error {
                     kind: ErrorKind::InvalidGrant,
                     msg: Some("This refresh token is not yours.".to_string()),
                     error_uri: None
-                }).into_response();
+                }.into_response();
             }
 
             let scope = if let Some(scope) = scope {
                 if !data.scope.has_all(scope.as_ref()) {
-                    return Json(Error {
+                    return Error {
                         kind: ErrorKind::InvalidScope,
                         msg: Some("You can't request additional scopes through the refresh token grant.".to_string()),
                         error_uri: None
-                    }).into_response();
+                    }.into_response();
                 }
 
                 scope