about summary refs log tree commit diff
path: root/kittybox-rs/indieauth/src
diff options
context:
space:
mode:
Diffstat (limited to 'kittybox-rs/indieauth/src')
-rw-r--r--kittybox-rs/indieauth/src/lib.rs116
-rw-r--r--kittybox-rs/indieauth/src/scopes.rs3
2 files changed, 97 insertions, 22 deletions
diff --git a/kittybox-rs/indieauth/src/lib.rs b/kittybox-rs/indieauth/src/lib.rs
index eca1102..b461fea 100644
--- a/kittybox-rs/indieauth/src/lib.rs
+++ b/kittybox-rs/indieauth/src/lib.rs
@@ -156,6 +156,9 @@ pub enum GrantResponse {
     }
 }
 
+/// Describes requests that the authorization endpoint might want to handle.
+///
+/// This type mostly exists for ease-of-use with serde.
 #[derive(Debug, Clone, Serialize, Deserialize)]
 #[serde(untagged)]
 pub enum RequestMaybeAuthorizationEndpoint {
@@ -257,37 +260,108 @@ pub struct TokenRevocationRequest {
     pub token: String
 }
 
-// TODO rework in accordance with https://datatracker.ietf.org/doc/html/rfc6749#section-5.2
-// turns out I got some values wrong
+/// Types of errors that a resource server (IndieAuth consumer) can
+/// throw when authentication goes wrong.
 #[derive(Debug, Clone, Copy, Serialize, Deserialize)]
 #[serde(rename_all = "snake_case")]
-#[serde(tag = "error")]
-pub enum IndieAuthError {
+pub enum ResourceErrorKind {
     InvalidRequest,
     InvalidToken,
     InsufficientScope,
 }
 
+#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
+#[serde(rename_all = "snake_case")]
+pub enum ErrorKind {
+    /// The request is missing a required parameter, includes an
+    /// unsupported parameter value (other than grant type), repeats a
+    /// parameter, includes multiple credentials, utilizes more than
+    /// one mechanism for authenticating the client, or is otherwise
+    /// malformed.
+    InvalidRequest,
+    /// Client authentication failed (e.g., unknown client, no client
+    /// authentication included, or unsupported authentication
+    /// method).  The authorization server MAY return an HTTP 401
+    /// (Unauthorized) status code to indicate which HTTP
+    /// authentication schemes are supported.  If the client attempted
+    /// to authenticate via the "Authorization" request header field,
+    /// the authorization server MUST respond with an HTTP 401
+    /// (Unauthorized) status code and include the "WWW-Authenticate"
+    /// response header field matching the authentication scheme used
+    /// by the client.
+    InvalidClient,
+    /// The provided authorization grant (e.g., authorization
+    /// code, resource owner credentials) or refresh token is
+    /// invalid, expired, revoked, does not match the redirection
+    /// URI used in the authorization request, or was issued to
+    /// another client.
+    InvalidGrant,
+    /// The authenticated client is not authorized to use this
+    /// authorization grant type.
+    UnauthorizedClient,
+    /// The authorization grant type is not supported by the
+    /// authorization server.
+    UnsupportedGrantType,
+    /// The requested scope is invalid, unknown, malformed, or
+    /// exceeds the scope granted by the resource owner.
+    InvalidScope
+}
+// TODO consider relying on serde_variant for these conversions
+impl AsRef<str> for ErrorKind {
+    fn as_ref(&self) -> &str {
+        match self {
+            ErrorKind::InvalidRequest => "invalid_request",
+            ErrorKind::InvalidClient => "invalid_client",
+            ErrorKind::InvalidGrant => "invalid_grant",
+            ErrorKind::UnauthorizedClient => "unauthorized_client",
+            ErrorKind::UnsupportedGrantType => "unsupported_grant_type",
+            ErrorKind::InvalidScope => "invalid_scope",
+        }
+    }
+}
+impl std::fmt::Display for ErrorKind {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "{}", self.as_ref())
+    }
+}
+
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct Error {
+    #[serde(rename = "error")]
+    pub kind: ErrorKind,
+    #[serde(rename = "error_description")]
+    pub msg: Option<String>,
+    pub error_uri: Option<url::Url>
+}
+
+impl From<ErrorKind> for Error {
+    fn from(kind: ErrorKind) -> Error {
+        Error {
+            kind, msg: None, error_uri: None
+        }
+    }
+}
+
+impl std::error::Error for self::Error {}
+
+impl std::fmt::Display for self::Error {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "IndieAuth error ({})", self.kind)?;
+        if let Some(msg) = self.msg.as_deref() {
+            write!(f, ": {}", msg)?;
+        }
+        if let Some(error_uri) = &self.error_uri {
+            write!(f, " (see `{}` for more info)", error_uri)?;
+        }
+
+        Ok(())
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
-    use serde_json::json;
-
-    #[test]
-    fn test_serialize_indieauth_error() {
-        assert_eq!(
-            serde_json::to_value(IndieAuthError::InvalidRequest).unwrap(),
-            json!({"error": "invalid_request"})
-        );
-        assert_eq!(
-            serde_json::to_value(IndieAuthError::InvalidToken).unwrap(),
-            json!({"error": "invalid_token"})
-        );
-        assert_eq!(
-            serde_json::to_value(IndieAuthError::InsufficientScope).unwrap(),
-            json!({"error": "insufficient_scope"})
-        );
-    }
 
     #[test]
     fn test_serialize_deserialize_grant_request() {
diff --git a/kittybox-rs/indieauth/src/scopes.rs b/kittybox-rs/indieauth/src/scopes.rs
index e803dca..18ebfbd 100644
--- a/kittybox-rs/indieauth/src/scopes.rs
+++ b/kittybox-rs/indieauth/src/scopes.rs
@@ -42,7 +42,8 @@ impl Scope {
         Scope::Custom(scope.to_string())
     }
 }
-// TODO consider relying on serde for these conversions
+
+// TODO consider relying on serde_variant for these conversions
 impl AsRef<str> for Scope {
     fn as_ref(&self) -> &str {
         use Scope::*;