Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.

Each entry lists the date and the crate versions that were released.

## 2026-05-24 — mqdb-agent 0.8.4

### Removed

- Dead `jwt: String` field on `Session`, `NewSession`, and `SessionRef`. After 0.8.3 wired the JTI directly into the session, no code path read the stored JWT anymore: `handle_logout` uses `session.jti`, `destroy_others_by_canonical_id` returns JTIs, and `handle_ticket` mints fresh ticket JWTs from session claims rather than the stored one. The session-time JWT was never returned to the client either — callback, register, and login all set just the session-id cookie plus a user-info JSON body. Dropped the field and removed `mint_callback_jwt` entirely; its three callers now generate a JTI inline via `JtiRevocationStore::generate_jti()`. `handle_login` no longer needs to construct a `ProviderIdentity` or destructure `email_verified`. No behavior change; the affected types are crate-private.

## 2026-05-23 — mqdb-agent 0.8.3

### Fixed
Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/mqdb-agent/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "mqdb-agent"
version = "0.8.3"
version = "0.8.4"
edition.workspace = true
license = "Apache-2.0"
authors.workspace = true
Expand Down
60 changes: 6 additions & 54 deletions crates/mqdb-agent/src/http/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,11 +266,8 @@ pub async fn handle_callback(state: &ServerState, query: &str) -> HttpResponse {
persist_oauth_tokens(state, &link_key, &canonical_id, refresh_token, &identity).await;
}

let (jwt, jti) = mint_callback_jwt(state, &canonical_id, &identity);

let Some(session_id) = state.session_store.create(NewSession {
jwt,
jti,
jti: JtiRevocationStore::generate_jti(),
canonical_id,
provider: provider.to_string(),
provider_sub: identity.provider_sub.clone(),
Expand Down Expand Up @@ -618,33 +615,6 @@ async fn persist_oauth_tokens(
}
}

fn mint_callback_jwt(
state: &ServerState,
canonical_id: &str,
identity: &ProviderIdentity,
) -> (String, String) {
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map_or(0, |d| d.as_secs());

let jti = JtiRevocationStore::generate_jti();
let claims = json!({
"sub": canonical_id,
"iss": state.jwt_config.issuer,
"aud": state.jwt_config.audience,
"exp": now + state.jwt_config.expiry_secs,
"iat": now,
"jti": jti,
"email": identity.email,
"name": identity.name,
"picture": identity.picture,
"provider": identity.provider,
"provider_sub": identity.provider_sub,
});

(sign_jwt(&claims, &state.jwt_config), jti)
}

pub async fn handle_refresh(state: &ServerState, body: &[u8]) -> HttpResponse {
let cors = state.cors_origin.as_deref();

Expand Down Expand Up @@ -1741,10 +1711,8 @@ pub async fn handle_register(state: &ServerState, body: &[u8], client_ip: &str)
return json_response_with_credentials(500, &json!({"error": "registration failed"}), cors);
}

let (jwt, jti) = mint_callback_jwt(state, &canonical_id, &identity);
let Some(session_id) = state.session_store.create(NewSession {
jwt,
jti,
jti: JtiRevocationStore::generate_jti(),
canonical_id: canonical_id.clone(),
provider: "email".to_string(),
provider_sub,
Expand Down Expand Up @@ -1832,7 +1800,7 @@ pub async fn handle_login(state: &ServerState, body: &[u8], client_ip: &str) ->
return json_response_with_credentials(500, &json!({"error": "login failed"}), cors);
};

let (display_name, display_email, verified) = if let Some(mut ident) =
let (display_name, display_email) = if let Some(mut ident) =
read_entity(&state.mqtt_client, "_identities", canonical_id).await
{
if let Some(ref crypto) = state.identity_crypto {
Expand All @@ -1850,30 +1818,15 @@ pub async fn handle_login(state: &ServerState, body: &[u8], client_ip: &str) ->
.get("primary_email")
.and_then(|v| v.as_str())
.map(String::from);
let ev = ident
.get("email_verified")
.and_then(serde_json::Value::as_bool)
.unwrap_or(false);
(n, e, ev)
(n, e)
} else {
(None, Some(email.to_string()), false)
(None, Some(email.to_string()))
};

let picture = fetch_picture_from_links(state, canonical_id).await;

let identity = ProviderIdentity {
provider: "email",
provider_sub: canonical_id.to_string(),
email: display_email.clone(),
name: display_name.clone(),
picture: picture.clone(),
email_verified: verified,
};
let (jwt, jti) = mint_callback_jwt(state, canonical_id, &identity);

let Some(session_id) = state.session_store.create(NewSession {
jwt,
jti,
jti: JtiRevocationStore::generate_jti(),
canonical_id: canonical_id.to_string(),
provider: "email".to_string(),
provider_sub: canonical_id.to_string(),
Expand Down Expand Up @@ -2833,7 +2786,6 @@ pub async fn handle_dev_login(state: &ServerState, body: &[u8]) -> HttpResponse
}

let Some(session_id) = state.session_store.create(NewSession {
jwt: String::new(),
jti: String::new(),
canonical_id: canonical_id.clone(),
provider: "dev".to_string(),
Expand Down
10 changes: 0 additions & 10 deletions crates/mqdb-agent/src/http/session_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ const SESSION_ID_BYTES: usize = 32;
const SESSION_TTL_SECS: u64 = 86400;

pub struct Session {
pub jwt: String,
pub jti: String,
pub canonical_id: String,
pub provider: String,
Expand All @@ -24,7 +23,6 @@ pub struct Session {
}

pub struct NewSession {
pub jwt: String,
pub jti: String,
pub canonical_id: String,
pub provider: String,
Expand Down Expand Up @@ -61,7 +59,6 @@ impl SessionStore {

let session_id = hex_encode(&bytes);
let session = Session {
jwt: new.jwt,
jti: new.jti,
canonical_id: new.canonical_id,
provider: new.provider,
Expand Down Expand Up @@ -89,7 +86,6 @@ impl SessionStore {
}

Some(SessionRef {
jwt: session.jwt.clone(),
jti: session.jti.clone(),
canonical_id: session.canonical_id.clone(),
provider: session.provider.clone(),
Expand Down Expand Up @@ -170,7 +166,6 @@ impl SessionStore {
}

pub struct SessionRef {
pub jwt: String,
pub jti: String,
pub canonical_id: String,
pub provider: String,
Expand Down Expand Up @@ -283,7 +278,6 @@ mod tests {
let store = SessionStore::new();
let session_id = store
.create(NewSession {
jwt: "jwt123".into(),
jti: "jti-abc".into(),
canonical_id: "550e8400-e29b-41d4-a716-446655440000".into(),
provider: "google".into(),
Expand All @@ -297,7 +291,6 @@ mod tests {
assert_eq!(session_id.len(), 64);

let session = store.get(&session_id).expect("get should succeed");
assert_eq!(session.jwt, "jwt123");
assert_eq!(session.jti, "jti-abc");
assert_eq!(session.canonical_id, "550e8400-e29b-41d4-a716-446655440000");
assert_eq!(session.provider, "google");
Expand All @@ -310,7 +303,6 @@ mod tests {
let store = SessionStore::new();
let session_id = store
.create(NewSession {
jwt: "jwt".into(),
jti: "jti".into(),
canonical_id: "canonical-1".into(),
provider: "google".into(),
Expand All @@ -335,7 +327,6 @@ mod tests {
fn make_session(store: &SessionStore, canonical_id: &str, jti: &str) -> String {
store
.create(NewSession {
jwt: format!("jwt-for-{jti}"),
jti: jti.into(),
canonical_id: canonical_id.into(),
provider: "email".into(),
Expand Down Expand Up @@ -395,7 +386,6 @@ mod tests {
let store = SessionStore::new();
let dev_session = store
.create(NewSession {
jwt: String::new(),
jti: String::new(),
canonical_id: "user-a".into(),
provider: "dev".into(),
Expand Down
Loading