Repository: sebadob/rauthy (language: rust)

Issue:
UserResponse.created_at described as string in openapi spec, but api return number
In v0.24.1 (and v0.25.0 I think too) it seems like the components.schemas.UserResponse.properties.created_at is of type "string" in spec, but when querying the `/user/email/{email}` endpoint, created_at is returned as a number.


Discussion / hints:
Thanks!

Yes that obviously is wrong in the documentation. The `str` there is a leftover from when I returned String-formatted `Date`s. It may be possible that such values exist on other response types as well, I need to double check that.

Basically, whenever you expect any Timestamp, Expiry, or anything like that, Rauthy will always return the Unix Timestamp in seconds.

Fix patch:
diff --git a/src/api_types/src/api_keys.rs b/src/api_types/src/api_keys.rs
index a08f3df9c..31dc66836 100644
--- a/src/api_types/src/api_keys.rs
+++ b/src/api_types/src/api_keys.rs
@@ -40,7 +40,7 @@ pub struct ApiKeyRequest {
     pub name: String,
     // TODO max validation for inner i64 is broken in the macro in v0.18.1
     // #[validate(range(min = 1719784800, max = 4070905200))]
-    /// Unix timestamp
+    /// Unix timestamp in seconds
     #[validate(range(min = 1719784800))]
     pub exp: Option<i64>,
     pub access: Vec<ApiKeyAccess>,
@@ -54,9 +54,9 @@ pub struct ApiKeysResponse {
 #[derive(Debug, Serialize, ToSchema)]
 pub struct ApiKeyResponse {
     pub name: String,
-    /// unix timestamp
+    /// Unix timestamp in seconds
     pub created: i64,
-    /// unix timestamp
+    /// Unix timestamp in seconds
     pub expires: Option<i64>,
     pub access: Vec<ApiKeyAccess>,
 }
diff --git a/src/api_types/src/blacklist.rs b/src/api_types/src/blacklist.rs
index 2276cb664..4ef345e0c 100644
--- a/src/api_types/src/blacklist.rs
+++ b/src/api_types/src/blacklist.rs
@@ -10,7 +10,7 @@ pub struct IpBlacklistRequest {
     pub ip: Ipv4Addr,
     // TODO max validation for inner i64 is broken in the macro in v0.18.1
     // #[validate(range(min = 1719784800, max = 4070905200))]
-    /// Unix timestamp
+    /// Unix timestamp in seconds
     #[validate(range(min = 1719784800))]
     pub exp: i64,
 }
@@ -23,5 +23,6 @@ pub struct BlacklistResponse {
 #[derive(Debug, Clone, Serialize, ToSchema)]
 pub struct BlacklistedIp {
     pub ip: String,
+    /// Unix timestamp in seconds
     pub exp: i64,
 }
diff --git a/src/api_types/src/clients.rs b/src/api_types/src/clients.rs
index 6c70efb85..81a46ed2e 100644
--- a/src/api_types/src/clients.rs
+++ b/src/api_types/src/clients.rs
@@ -285,6 +285,7 @@ pub struct DynamicClientResponse {
     pub client_secret: Option<String>,
     // TODO can we "trust" in a client doing a PUT on Self before en expiry to
     // implement proper forced secret rotation from time to time? -> not mentioned in RFC
+    /// Unix timestamp in seconds
     pub client_secret_expires_at: i64,
 
     pub redirect_uris: Vec<String>,
diff --git a/src/api_types/src/sessions.rs b/src/api_types/src/sessions.rs
index 5efebcf16..2198721a7 100644
--- a/src/api_types/src/sessions.rs
+++ b/src/api_types/src/sessions.rs
@@ -17,7 +17,9 @@ pub struct SessionResponse<'a> {
     pub user_id: Option<&'a str>,
     pub is_mfa: bool,
     pub state: SessionState,
+    /// Unix timestamp in seconds
     pub exp: i64,
+    /// Unix timestamp in seconds
     pub last_seen: i64,
     pub remote_ip: Option<&'a str>,
 }
diff --git a/src/api_types/src/users.rs b/src/api_types/src/users.rs
index b3c7f0270..1a939b837 100644
--- a/src/api_types/src/users.rs
+++ b/src/api_types/src/users.rs
@@ -57,7 +57,7 @@ pub struct NewUserRequest {
     pub roles: Vec<String>,
     // TODO max validation for inner i64 is broken in the macro in v0.18.1
     // #[validate(range(min = 1719784800, max = 4070905200))]
-    /// Unix timestamp
+    /// Unix timestamp in seconds
     #[validate(range(min = 1719784800))]
     pub user_expires: Option<i64>,
 }
@@ -135,7 +135,7 @@ pub struct UpdateUserRequest {
     pub email_verified: bool,
     // TODO max validation for inner i64 is broken in the macro in v0.18.1
     // #[validate(range(min = 1719784800, max = 4070905200))]
-    /// Unix timestamp
+    /// Unix timestamp in seconds
     #[validate(range(min = 1719784800))]
     pub user_expires: Option<i64>,
     #[validate(nested)]
@@ -261,8 +261,11 @@ pub struct DeviceResponse {
     pub client_id: String,
     #[serde(skip_serializing_if = "Option::is_none")]
     pub user_id: Option<String>,
+    /// Unix timestamp in seconds
     pub created: i64,
+    /// Unix timestamp in seconds
     pub access_exp: i64,
+    /// Unix timestamp in seconds
     #[serde(skip_serializing_if = "Option::is_none")]
     pub refresh_exp: Option<i64>,
     pub peer_ip: String,
@@ -272,9 +275,9 @@ pub struct DeviceResponse {
 #[derive(Debug, Serialize, Deserialize, ToSchema)]
 pub struct PasskeyResponse {
     pub name: String,
-    /// format: `NaiveDateTime`
+    /// Unix timestamp in seconds
     pub registered: i64,
-    /// format: `NaiveDateTime`
+    /// Unix timestamp in seconds
     pub last_used: i64,
     pub user_verified: Option<bool>,
 }
@@ -367,20 +370,19 @@ pub struct UserResponse {
     pub groups: Option<Vec<String>>,
     pub enabled: bool,
     pub email_verified: bool,
-    /// format: `NaiveDateTime`
+    /// Unix timestamp in seconds
     #[serde(skip_serializing_if = "Option::is_none")]
     pub password_expires: Option<i64>,
-    /// format: `NaiveDateTime`
-    #[schema(value_type = str)]
+    /// Unix timestamp in seconds
     pub created_at: i64,
-    /// format: `NaiveDateTime`
-    #[serde(skip_serializing_if = "Option::is_none")]
+    /// Unix timestamp in seconds
     pub last_login: Option<i64>,
-    /// format: `NaiveDateTime`
+    /// Unix timestamp in seconds
     #[serde(skip_serializing_if = "Option::is_none")]
     pub last_failed_login: Option<i64>,
     #[serde(skip_serializing_if = "Option::is_none")]
     pub failed_login_attempts: Option<i64>,
+    /// Unix timestamp in seconds
     #[serde(skip_serializing_if = "Option::is_none")]
     pub user_expires: Option<i64>,
     pub account_type: UserAccountTypeResponse,


Test patch: