Rework browser sessions

This commit is contained in:
Simon Bihel
2022-02-21 11:29:37 +00:00
parent 950a493dc4
commit 66b2c51339
10 changed files with 173 additions and 617 deletions

View File

@@ -1,6 +1,5 @@
use anyhow::{anyhow, Result};
use async_trait::async_trait;
// use cached::{stores::TimedCache, Cached};
use chrono::{DateTime, Duration, Utc};
use matchit::Node;
use std::collections::HashMap;
@@ -109,7 +108,6 @@ impl DBClient for CFClient {
.map_err(|e| anyhow!("Failed to serialize client entry: {}", e))?,
)
.map_err(|e| anyhow!("Failed to build KV put: {}", e))?
// TODO put some sort of expiration for dynamic registration
.execute()
.await
.map_err(|e| anyhow!("Failed to put KV: {}", e))?;
@@ -202,4 +200,32 @@ impl DBClient for CFClient {
code => Err(anyhow!("Error fetching from Durable Object: {}", code)),
}
}
async fn set_session(&self, id: String, entry: SessionEntry) -> Result<()> {
self.ctx
.kv(KV_NAMESPACE)
.map_err(|e| anyhow!("Failed to get KV store: {}", e))?
.put(
&format!("{}/{}", KV_SESSION_PREFIX, id),
serde_json::to_string(&entry)
.map_err(|e| anyhow!("Failed to serialize client entry: {}", e))?,
)
.map_err(|e| anyhow!("Failed to build KV put: {}", e))?
.expiration_ttl(SESSION_LIFETIME)
.execute()
.await
.map_err(|e| anyhow!("Failed to put KV: {}", e))?;
Ok(())
}
async fn get_session(&self, id: String) -> Result<Option<SessionEntry>> {
Ok(self
.ctx
.kv(KV_NAMESPACE)
.map_err(|e| anyhow!("Failed to get KV store: {}", e))?
.get(&format!("{}/{}", KV_SESSION_PREFIX, id))
.json()
.await
.map_err(|e| anyhow!("Failed to get KV: {}", e))?)
}
}

View File

@@ -15,7 +15,10 @@ mod cf;
pub use cf::CFClient;
const KV_CLIENT_PREFIX: &str = "clients";
const KV_SESSION_PREFIX: &str = "sessions";
pub const ENTRY_LIFETIME: usize = 30;
pub const SESSION_LIFETIME: u64 = 300; // 5min
pub const SESSION_COOKIE_NAME: &str = "session";
#[derive(Clone, Serialize, Deserialize)]
pub struct CodeEntry {
@@ -33,6 +36,14 @@ pub struct ClientEntry {
pub access_token: Option<RegistrationAccessToken>,
}
#[derive(Clone, Serialize, Deserialize)]
pub struct SessionEntry {
pub siwe_nonce: String,
pub oidc_nonce: Option<Nonce>,
pub secret: String,
pub signin_count: u64,
}
// Using a trait to easily pass async functions with async_trait
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
@@ -42,4 +53,6 @@ pub trait DBClient {
async fn delete_client(&self, client_id: String) -> Result<()>;
async fn set_code(&self, code: String, code_entry: CodeEntry) -> Result<()>;
async fn get_code(&self, code: String) -> Result<Option<CodeEntry>>;
async fn set_session(&self, id: String, entry: SessionEntry) -> Result<()>;
async fn get_session(&self, id: String) -> Result<Option<SessionEntry>>;
}

View File

@@ -98,4 +98,40 @@ impl DBClient for RedisClient {
.map_err(|e| anyhow!("Failed to deserialize code: {}", e))?;
Ok(Some(code_entry))
}
async fn set_session(&self, id: String, entry: SessionEntry) -> Result<()> {
let mut conn = self
.pool
.get()
.await
.map_err(|e| anyhow!("Failed to get connection to database: {}", e))?;
conn.set_ex(
format!("{}/{}", KV_SESSION_PREFIX, id),
serde_json::to_string(&entry)
.map_err(|e| anyhow!("Failed to serialize session entry: {}", e))?,
SESSION_LIFETIME.try_into().unwrap(),
)
.await
.map_err(|e| anyhow!("Failed to set kv: {}", e))?;
Ok(())
}
async fn get_session(&self, id: String) -> Result<Option<SessionEntry>> {
let mut conn = self
.pool
.get()
.await
.map_err(|e| anyhow!("Failed to get connection to database: {}", e))?;
let entry: Option<String> = conn
.get(format!("{}/{}", KV_SESSION_PREFIX, id))
.await
.map_err(|e| anyhow!("Failed to get kv: {}", e))?;
if let Some(e) = entry {
Ok(serde_json::from_str(&e)
.map_err(|e| anyhow!("Failed to deserialize session entry: {}", e))?)
} else {
Ok(None)
}
}
}