Skip to content

Commit d07af26

Browse files
committed
Revoke session tokens on logout.
1 parent d6b1058 commit d07af26

File tree

4 files changed

+43
-7
lines changed

4 files changed

+43
-7
lines changed

migrations/2022-02-21-211645_create_sessions/up.sql

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,7 @@ CREATE TABLE persistent_sessions
33
id SERIAL
44
CONSTRAINT persistent_sessions_pk
55
PRIMARY KEY,
6-
user_id INTEGER NOT NULL
7-
CONSTRAINT persistent_sessions_users_id_fk
8-
REFERENCES users
9-
ON UPDATE CASCADE ON DELETE CASCADE,
6+
user_id INTEGER NOT NULL,
107
hashed_token bytea NOT NULL,
118
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
129
last_used_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
@@ -17,5 +14,5 @@ CREATE TABLE persistent_sessions
1714

1815
COMMENT ON TABLE persistent_sessions IS 'This table contains the hashed tokens for all of the cookie-based persistent sessions';
1916

20-
CREATE INDEX persistent_sessions_user_id_index
21-
ON persistent_sessions (user_id);
17+
CREATE INDEX persistent_sessions_token_index
18+
ON persistent_sessions (hashed_token);

src/controllers/user/session.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,24 @@ fn save_user_to_database(
168168

169169
/// Handles the `DELETE /api/private/session` route.
170170
pub fn logout(req: &mut dyn RequestExt) -> EndpointResult {
171+
// TODO(adsnaider): Remove this.
171172
req.session_mut().remove(&"user_id".to_string());
173+
if let Some(session_token) = req
174+
.cookies()
175+
.get(SESSION_COOKIE_NAME)
176+
.map(|cookie| cookie.value().to_string())
177+
{
178+
req.cookies_mut().remove(Cookie::named(SESSION_COOKIE_NAME));
179+
180+
if let Ok(conn) = req.db_conn() {
181+
match PersistentSession::revoke_from_token(&conn, &session_token) {
182+
Ok(0) => {}
183+
Ok(1) => {}
184+
Ok(_count) => {}
185+
Err(_e) => {}
186+
}
187+
}
188+
}
172189
Ok(req.json(&true))
173190
}
174191

src/models/persistent_session.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ use chrono::NaiveDateTime;
22
use diesel::prelude::*;
33
use ipnetwork::IpNetwork;
44
use std::net::IpAddr;
5+
use thiserror::Error;
56

67
use crate::schema::persistent_sessions;
78
use crate::util::token::SecureToken;
9+
use crate::util::token::SecureTokenKind;
810

911
#[derive(Clone, Debug, PartialEq, Eq, Identifiable, Queryable)]
1012
#[table_name = "persistent_sessions"]
@@ -19,6 +21,14 @@ pub struct PersistentSession {
1921
pub last_user_agent: String,
2022
}
2123

24+
#[derive(Error, Debug, PartialEq)]
25+
pub enum SessionError {
26+
#[error("token prefix doesn't match session tokens")]
27+
InvalidSessionToken,
28+
#[error("database manipulation error")]
29+
DieselError(#[from] diesel::result::Error),
30+
}
31+
2232
impl PersistentSession {
2333
pub fn create<'a, 'b>(
2434
user_id: i32,
@@ -33,6 +43,19 @@ impl PersistentSession {
3343
last_user_agent,
3444
}
3545
}
46+
47+
pub fn revoke_from_token(conn: &PgConnection, token: &str) -> Result<usize, SessionError> {
48+
let hashed_token = SecureToken::parse(SecureTokenKind::Session, token)
49+
.ok_or(SessionError::InvalidSessionToken)?;
50+
let sessions = persistent_sessions::table
51+
.filter(persistent_sessions::hashed_token.eq(hashed_token))
52+
.filter(persistent_sessions::revoked.eq(false));
53+
54+
diesel::update(sessions)
55+
.set(persistent_sessions::revoked.eq(true))
56+
.execute(conn)
57+
.map_err(SessionError::DieselError)
58+
}
3659
}
3760

3861
#[derive(Clone, Debug, PartialEq, Eq, Insertable)]

src/schema.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1082,7 +1082,6 @@ joinable!(dependencies -> versions (version_id));
10821082
joinable!(emails -> users (user_id));
10831083
joinable!(follows -> crates (crate_id));
10841084
joinable!(follows -> users (user_id));
1085-
joinable!(persistent_sessions -> users (user_id));
10861085
joinable!(publish_limit_buckets -> users (user_id));
10871086
joinable!(publish_rate_overrides -> users (user_id));
10881087
joinable!(readme_renderings -> versions (version_id));

0 commit comments

Comments
 (0)