about summary refs log tree commit diff
path: root/kittybox-rs/src/indieauth/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'kittybox-rs/src/indieauth/mod.rs')
-rw-r--r--kittybox-rs/src/indieauth/mod.rs72
1 files changed, 72 insertions, 0 deletions
diff --git a/kittybox-rs/src/indieauth/mod.rs b/kittybox-rs/src/indieauth/mod.rs
index 36f207c..6dc9ec6 100644
--- a/kittybox-rs/src/indieauth/mod.rs
+++ b/kittybox-rs/src/indieauth/mod.rs
@@ -1,3 +1,5 @@
+use std::marker::PhantomData;
+
 use tracing::error;
 use serde::Deserialize;
 use axum::{
@@ -27,6 +29,76 @@ const REFRESH_TOKEN_VALIDITY: u64 = ACCESS_TOKEN_VALIDITY / 7 * 60; // 60 days
 /// Internal scope for accessing the token introspection endpoint.
 const KITTYBOX_TOKEN_STATUS: &str = "kittybox:token_status";
 
+pub(crate) struct User<A: AuthBackend>(pub(crate) TokenData, pub(crate) PhantomData<A>);
+impl<A: AuthBackend> std::fmt::Debug for User<A> {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_tuple("User").field(&self.0).finish()
+    }
+}
+impl<A: AuthBackend> std::ops::Deref for User<A> {
+    type Target = TokenData;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+pub enum IndieAuthResourceError {
+    InvalidRequest,
+    Unauthorized,
+    InvalidToken
+}
+impl axum::response::IntoResponse for IndieAuthResourceError {
+    fn into_response(self) -> axum::response::Response {
+        use IndieAuthResourceError::*;
+
+        match self {
+            Unauthorized => (
+                StatusCode::UNAUTHORIZED,
+                [("WWW-Authenticate", "Bearer")]
+            ).into_response(),
+            InvalidRequest => (
+                StatusCode::BAD_REQUEST,
+                Json(&serde_json::json!({"error": "invalid_request"}))
+            ).into_response(),
+            InvalidToken => (
+                StatusCode::UNAUTHORIZED,
+                [("WWW-Authenticate", "Bearer, error=\"invalid_token\"")],
+                Json(&serde_json::json!({"error": "unauthorized"}))
+            ).into_response()
+        }
+    }
+}
+
+#[async_trait::async_trait]
+impl <B: Send, A: AuthBackend> axum::extract::FromRequest<B> for User<A> {
+    type Rejection = IndieAuthResourceError;
+
+    async fn from_request(req: &mut axum::extract::RequestParts<B>) -> Result<Self, Self::Rejection> {
+        let TypedHeader(Authorization(token)) =
+            TypedHeader::<Authorization<Bearer>>::from_request(req)
+            .await
+            .map_err(|_| IndieAuthResourceError::Unauthorized)?;
+
+        let axum::Extension(auth) = axum::Extension::<A>::from_request(req)
+            .await
+            .unwrap();
+
+        let Host(host) = Host::from_request(req)
+            .await
+            .map_err(|_| IndieAuthResourceError::InvalidRequest)?;
+        
+        auth.get_token(
+            &format!("https://{host}/").parse().unwrap(),
+            token.token()
+        )
+            .await
+            .unwrap()
+            .ok_or(IndieAuthResourceError::InvalidToken)
+            .map(|t| User(t, PhantomData))
+    }
+}
+
 pub async fn metadata(
     Host(host): Host
 ) -> Metadata {