feat: more controls on setting token duration (#5421)

* allow setting max session length

* more options for expiration

* sqlx

* option to invalidate all old sessions on new session

* sqlx update script on mac

* order

* add audit log

---------

Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
This commit is contained in:
Alexander Petric
2025-03-04 20:08:42 +01:00
committed by GitHub
parent b705e4e415
commit 13253b2d6e
4 changed files with 59 additions and 11 deletions
@@ -0,0 +1,14 @@
{
"db_name": "PostgreSQL",
"query": "DELETE FROM token WHERE email = $1 AND label = 'session'",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Text"
]
},
"nullable": []
},
"hash": "7db681e86f8332c636d0f29b389860292b704788827edfebc467a92486f9e14f"
}
@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO token\n (token, email, label, expiration, super_admin)\n VALUES ($1, $2, $3, now() + ($4 || ' hours')::interval, $5)",
"query": "INSERT INTO token\n (token, email, label, expiration, super_admin)\n VALUES ($1, $2, $3, now() + ($4 || ' seconds')::interval, $5)",
"describe": {
"columns": [],
"parameters": {
@@ -14,5 +14,5 @@
},
"nullable": []
},
"hash": "b05c5f62ef4aa21d33369130cced0e9d7d128727eb58a9be7ae69cbb16bcbb27"
"hash": "8aebd7f7fd1374f1c3d5389e953ebf080df3f76ac3e6e6373a89c8d46388125d"
}
+35 -4
View File
@@ -55,7 +55,6 @@ use windmill_common::{
utils::{not_found_if_none, rd_string, require_admin, Pagination, StripPath},
};
use windmill_git_sync::handle_deployment_metadata;
pub const TTL_TOKEN_DB_H: u32 = 72;
const COOKIE_PATH: &str = "/";
@@ -1696,6 +1695,11 @@ async fn refresh_token(
Ok("token refreshed".to_string())
}
lazy_static::lazy_static! {
static ref MAX_SESSION_VALIDITY_SECONDS: i64 = std::env::var("MAX_SESSION_VALIDITY_SECONDS").ok().unwrap_or_else(|| String::new()).parse::<i64>().unwrap_or(3 * 24 * 60 * 60);
static ref INVALIDATE_OLD_SESSIONS: bool = std::env::var("INVALIDATE_OLD_SESSIONS").ok().unwrap_or_else(|| String::new()).parse::<bool>().unwrap_or(false);
}
pub async fn create_session_token<'c>(
email: &str,
super_admin: bool,
@@ -1703,18 +1707,45 @@ pub async fn create_session_token<'c>(
cookies: Cookies,
) -> Result<String> {
let token = rd_string(32);
if *INVALIDATE_OLD_SESSIONS {
sqlx::query!(
"DELETE FROM token WHERE email = $1 AND label = 'session'",
email
)
.execute(&mut **tx)
.await?;
audit_log(
&mut **tx,
&AuditAuthor {
email: email.to_string(),
username: email.to_string(),
username_override: None,
},
"users.token.invalidate_old_sessions",
ActionKind::Delete,
&"global",
None,
None,
)
.instrument(tracing::info_span!("token", email))
.await?;
}
sqlx::query!(
"INSERT INTO token
(token, email, label, expiration, super_admin)
VALUES ($1, $2, $3, now() + ($4 || ' hours')::interval, $5)",
VALUES ($1, $2, $3, now() + ($4 || ' seconds')::interval, $5)",
token,
email,
"session",
TTL_TOKEN_DB_H.to_string(),
&MAX_SESSION_VALIDITY_SECONDS.to_string(),
super_admin
)
.execute(&mut **tx)
.await?;
let mut cookie = Cookie::new(COOKIE_NAME, token.clone());
cookie.set_secure(IS_SECURE.read().await.clone());
cookie.set_same_site(Some(tower_cookies::cookie::SameSite::Lax));
@@ -1725,7 +1756,7 @@ pub async fn create_session_token<'c>(
}
let mut expire: OffsetDateTime = time::OffsetDateTime::now_utc();
expire += time::Duration::days(3);
expire += time::Duration::seconds(*MAX_SESSION_VALIDITY_SECONDS);
cookie.set_expires(expire);
cookies.add(cookie);
Ok(token)
@@ -72,8 +72,7 @@
newToken = undefined
let date: Date | undefined
if (newTokenExpiration) {
date = new Date()
date.setDate(date.getDate() + newTokenExpiration)
date = new Date(new Date().getTime() + newTokenExpiration * 1000)
}
newToken = await UserService.createToken({
requestBody: {
@@ -299,9 +298,13 @@
</label>
<select bind:value={newTokenExpiration}>
<option value={undefined}>No expiration</option>
<option value={7}>7d</option>
<option value={30}>30d</option>
<option value={90}>90d</option>
<option value={15*60}>15m</option>
<option value={30*60}>30m</option>
<option value={1*60*60}>1h</option>
<option value={1*24*60*60}>1d</option>
<option value={7*24*60*60}>7d</option>
<option value={30*24*60*60}>30d</option>
<option value={90*24*60*60}>90d</option>
</select>
</div>
<div class="flex items-end">