commit ecd11e20284f89142f972d1bc56fa5baec352b16
parent 72e86b9d67844790a1eede1a0cd5e03b6d6d302b
Author: Silas Brack <silasbrack@gmail.com>
Date: Sat, 25 Apr 2026 15:03:47 +0200
fix: use client.user().sub for vote user_id
- TrailBase client provides the user ID via client.user()
- Remove manual JWT parsing and base64 dependency
- Pass user_id from client directly to CreateVote
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Diffstat:
4 files changed, 4 insertions(+), 22 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
@@ -1911,7 +1911,6 @@ dependencies = [
"askama",
"axum",
"axum-extra",
- "base64",
"chrono",
"serde",
"serde_json",
diff --git a/Cargo.toml b/Cargo.toml
@@ -7,7 +7,6 @@ edition = "2021"
axum = "0.8"
axum-extra = { version = "0.10", features = ["cookie"] }
askama = "0.12"
-base64 = "0.22"
chrono = { version = "0.4", features = ["serde"] }
tokio = { version = "1", features = ["macros", "rt-multi-thread", "net"] }
serde = { version = "1", features = ["derive"] }
diff --git a/src/trailbase.rs b/src/trailbase.rs
@@ -31,25 +31,6 @@ pub struct StoredTokens {
pub refresh_token: Option<String>,
}
-/// Extract user ID from JWT auth token
-pub fn extract_user_id_from_token(tokens: &StoredTokens) -> Option<String> {
- use base64::{Engine, engine::general_purpose::URL_SAFE_NO_PAD};
-
- // JWT format: header.payload.signature
- let parts: Vec<&str> = tokens.auth_token.split('.').collect();
- if parts.len() != 3 {
- return None;
- }
-
- // Decode the payload (second part)
- let payload = URL_SAFE_NO_PAD.decode(parts[1]).ok()?;
- let payload_str = String::from_utf8(payload).ok()?;
-
- // Parse as JSON and extract "sub" claim
- let json: serde_json::Value = serde_json::from_str(&payload_str).ok()?;
- json.get("sub").and_then(|v| v.as_str()).map(|s| s.to_string())
-}
-
/// Create a new unauthenticated client
pub fn new_client(base_url: &str) -> Result<Client, ClientError> {
Ok(Client::new(base_url, None)?)
@@ -291,12 +272,15 @@ pub async fn calculate_comment_path(
/// Create a vote
#[derive(Debug, Serialize)]
struct CreateVote {
+ user_id: String,
target_type: i64,
target_id: i64,
}
pub async fn vote(client: &Client, target_type: i64, target_id: i64) -> Result<(), ClientError> {
+ let user = client.user().ok_or_else(|| ClientError::Auth("Not authenticated".to_string()))?;
let vote_data = CreateVote {
+ user_id: user.sub,
target_type,
target_id,
};
diff --git a/traildepot/config.textproto b/traildepot/config.textproto
@@ -40,7 +40,7 @@ record_apis: [{
table_name: "vote"
acl_world: []
acl_authenticated: [READ, CREATE, DELETE]
- autofill_missing_user_id_column: "user_id"
+ create_access_rule: "_REQ_.user_id = _USER_.id"
delete_access_rule: "_ROW_.user_id = _USER_.id"
}, {
name: "user_profile"