Skip to content
This repository was archived by the owner on May 13, 2026. It is now read-only.

Commit 3330416

Browse files
feat(user-management): add list and revoke session (#186)
1 parent 3c82040 commit 3330416

3 files changed

Lines changed: 284 additions & 0 deletions

File tree

src/user_management/operations.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,12 @@ mod get_user_identities;
3131
mod list_auth_factors;
3232
mod list_invitations;
3333
mod list_organization_memberships;
34+
mod list_sessions;
3435
mod list_users;
3536
mod reactivate_organization_membership;
3637
mod reset_password;
3738
mod revoke_invitation;
39+
mod revoke_session;
3840
mod send_invitation;
3941
mod update_organization_membership;
4042
mod update_user;
@@ -72,10 +74,12 @@ pub use get_user_identities::*;
7274
pub use list_auth_factors::*;
7375
pub use list_invitations::*;
7476
pub use list_organization_memberships::*;
77+
pub use list_sessions::*;
7578
pub use list_users::*;
7679
pub use reactivate_organization_membership::*;
7780
pub use reset_password::*;
7881
pub use revoke_invitation::*;
82+
pub use revoke_session::*;
7983
pub use send_invitation::*;
8084
pub use update_organization_membership::*;
8185
pub use update_user::*;
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
use async_trait::async_trait;
2+
use serde::Serialize;
3+
use thiserror::Error;
4+
5+
use crate::user_management::{Session, UserId, UserManagement};
6+
use crate::{PaginatedList, PaginationParams, ResponseExt, WorkOsError, WorkOsResult};
7+
8+
/// The parameters for the [`ListSessions`] function.
9+
#[derive(Debug, Serialize)]
10+
pub struct ListSessionsParams<'a> {
11+
/// The ID of the user.
12+
#[serde(skip_serializing)]
13+
pub user_id: &'a UserId,
14+
15+
/// The pagination parameters to use when listing sessions.
16+
#[serde(flatten)]
17+
pub pagination: PaginationParams<'a>,
18+
}
19+
20+
/// An error returned from [`ListSessions`].
21+
#[derive(Debug, Error)]
22+
pub enum ListSessionsError {}
23+
24+
impl From<ListSessionsError> for WorkOsError<ListSessionsError> {
25+
fn from(err: ListSessionsError) -> Self {
26+
Self::Operation(err)
27+
}
28+
}
29+
30+
/// [WorkOS Docs: List sessions](https://workos.com/docs/reference/user-management/session/list)
31+
#[async_trait]
32+
pub trait ListSessions {
33+
/// Get a list of all active sessions for a specific user.
34+
///
35+
/// [WorkOS Docs: List sessions](https://workos.com/docs/reference/user-management/session/list)
36+
///
37+
/// # Examples
38+
///
39+
/// ```
40+
/// # use workos::WorkOsResult;
41+
/// # use workos::user_management::*;
42+
/// use workos::{ApiKey, WorkOs};
43+
/// use workos::organizations::OrganizationId;
44+
///
45+
/// # async fn run() -> WorkOsResult<(), ListSessionsError> {
46+
/// let workos = WorkOs::new(&ApiKey::from("sk_example_123456789"));
47+
///
48+
/// let sessions = workos
49+
/// .user_management()
50+
/// .list_sessions(&ListSessionsParams {
51+
/// user_id: &UserId::from("user_01E4ZCR3C56J083X43JQXF3JK5"),
52+
/// pagination: Default::default(),
53+
/// })
54+
/// .await?;
55+
/// # Ok(())
56+
/// # }
57+
/// ```
58+
async fn list_sessions(
59+
&self,
60+
params: &ListSessionsParams,
61+
) -> WorkOsResult<PaginatedList<Session>, ListSessionsError>;
62+
}
63+
64+
#[async_trait]
65+
impl ListSessions for UserManagement<'_> {
66+
async fn list_sessions(
67+
&self,
68+
params: &ListSessionsParams,
69+
) -> WorkOsResult<PaginatedList<Session>, ListSessionsError> {
70+
let url = self.workos.base_url().join(&format!(
71+
"/user_management/users/{}/sessions",
72+
params.user_id
73+
))?;
74+
75+
let sessions = self
76+
.workos
77+
.client()
78+
.get(url)
79+
.query(&params)
80+
.bearer_auth(self.workos.key())
81+
.send()
82+
.await?
83+
.handle_unauthorized_or_generic_error()
84+
.await?
85+
.json::<PaginatedList<Session>>()
86+
.await?;
87+
88+
Ok(sessions)
89+
}
90+
}
91+
92+
#[cfg(test)]
93+
mod test {
94+
use mockito::Matcher;
95+
use serde_json::json;
96+
use tokio;
97+
98+
use crate::{ApiKey, WorkOs, user_management::SessionId};
99+
100+
use super::*;
101+
102+
#[tokio::test]
103+
async fn it_calls_the_get_list_sessions_endpoint() {
104+
let mut server = mockito::Server::new_async().await;
105+
106+
let workos = WorkOs::builder(&ApiKey::from("sk_example_123456789"))
107+
.base_url(&server.url())
108+
.unwrap()
109+
.build();
110+
111+
server
112+
.mock("GET", "/user_management/users/user_01E4ZCR3C56J083X43JQXF3JK5/sessions")
113+
.match_query(Matcher::UrlEncoded("order".to_string(), "desc".to_string()))
114+
.match_header("Authorization", "Bearer sk_example_123456789")
115+
.with_status(200)
116+
.with_body(
117+
json!({
118+
"data": [
119+
{
120+
"object": "session",
121+
"id": "session_01E4ZCR3C56J083X43JQXF3JK5",
122+
"user_id": "user_01E4ZCR3C56J083X43JQXF3JK5",
123+
"organization_id": "org_01E4ZCR3C56J083X43JQXF3JK5",
124+
"status": "active",
125+
"auth_method": "password",
126+
"ip_address": "192.168.1.1",
127+
"user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36",
128+
"expires_at": "2025-07-23T15:00:00.000Z",
129+
"ended_at": null,
130+
"created_at": "2025-07-23T14:00:00.000Z",
131+
"updated_at": "2025-07-23T14:00:00.000Z"
132+
}
133+
],
134+
"list_metadata": {
135+
"before": "session_01E4ZCR3C56J083X43JQXF3JK5",
136+
"after": "session_01EJBGJT2PC6638TN5Y380M40Z"
137+
}
138+
})
139+
.to_string(),
140+
)
141+
.create_async()
142+
.await;
143+
144+
let paginated_list = workos
145+
.user_management()
146+
.list_sessions(&ListSessionsParams {
147+
user_id: &UserId::from("user_01E4ZCR3C56J083X43JQXF3JK5"),
148+
pagination: Default::default(),
149+
})
150+
.await
151+
.unwrap();
152+
153+
assert_eq!(
154+
paginated_list
155+
.data
156+
.into_iter()
157+
.next()
158+
.map(|session| session.id),
159+
Some(SessionId::from("session_01E4ZCR3C56J083X43JQXF3JK5"))
160+
)
161+
}
162+
}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
use async_trait::async_trait;
2+
use serde::Serialize;
3+
use thiserror::Error;
4+
5+
use crate::user_management::{SessionId, UserManagement};
6+
use crate::{ResponseExt, WorkOsError, WorkOsResult};
7+
8+
/// The parameters for [`RevokeSession`].
9+
#[derive(Debug, Serialize)]
10+
pub struct RevokeSessionParams<'a> {
11+
/// The ID of the session.
12+
pub session_id: &'a SessionId,
13+
}
14+
15+
/// An error returned from [`RevokeSession`].
16+
#[derive(Debug, Error)]
17+
pub enum RevokeSessionError {}
18+
19+
impl From<RevokeSessionError> for WorkOsError<RevokeSessionError> {
20+
fn from(err: RevokeSessionError) -> Self {
21+
Self::Operation(err)
22+
}
23+
}
24+
25+
/// [WorkOS Docs: Revoke session](https://workos.com/docs/reference/user-management/session/revoke)
26+
#[async_trait]
27+
pub trait RevokeSession {
28+
/// Revoke a session.
29+
///
30+
/// [WorkOS Docs: Revoke session](https://workos.com/docs/reference/user-management/session/revoke)
31+
///
32+
/// # Examples
33+
///
34+
/// ```
35+
/// # use workos::WorkOsResult;
36+
/// # use workos::user_management::*;
37+
/// use workos::{ApiKey, Metadata, WorkOs};
38+
///
39+
/// # async fn run() -> WorkOsResult<(), RevokeSessionError> {
40+
/// let workos = WorkOs::new(&ApiKey::from("sk_example_123456789"));
41+
///
42+
/// workos
43+
/// .user_management()
44+
/// .revoke_session(&RevokeSessionParams {
45+
/// session_id: &SessionId::from("session_01E4ZCR3C56J083X43JQXF3JK5"),
46+
/// })
47+
/// .await?;
48+
/// # Ok(())
49+
/// # }
50+
/// ```
51+
async fn revoke_session(
52+
&self,
53+
params: &RevokeSessionParams<'_>,
54+
) -> WorkOsResult<(), RevokeSessionError>;
55+
}
56+
57+
#[async_trait]
58+
impl RevokeSession for UserManagement<'_> {
59+
async fn revoke_session(
60+
&self,
61+
params: &RevokeSessionParams<'_>,
62+
) -> WorkOsResult<(), RevokeSessionError> {
63+
let url = self
64+
.workos
65+
.base_url()
66+
.join("/user_management/sessions/revoke")?;
67+
68+
self.workos
69+
.client()
70+
.post(url)
71+
.bearer_auth(self.workos.key())
72+
.json(&params)
73+
.send()
74+
.await?
75+
.handle_unauthorized_or_generic_error()
76+
.await?;
77+
78+
Ok(())
79+
}
80+
}
81+
82+
#[cfg(test)]
83+
mod test {
84+
use matches::assert_matches;
85+
86+
use tokio;
87+
88+
use crate::{ApiKey, WorkOs};
89+
90+
use super::*;
91+
92+
#[tokio::test]
93+
async fn it_calls_the_revoke_session_endpoint() {
94+
let mut server = mockito::Server::new_async().await;
95+
96+
let workos = WorkOs::builder(&ApiKey::from("sk_example_123456789"))
97+
.base_url(&server.url())
98+
.unwrap()
99+
.build();
100+
101+
server
102+
.mock("POST", "/user_management/sessions/revoke")
103+
.match_header("Authorization", "Bearer sk_example_123456789")
104+
.match_body(r#"{"session_id":"session_01E4ZCR3C56J083X43JQXF3JK5"}"#)
105+
.with_status(202)
106+
.create_async()
107+
.await;
108+
109+
let result = workos
110+
.user_management()
111+
.revoke_session(&RevokeSessionParams {
112+
session_id: &SessionId::from("session_01E4ZCR3C56J083X43JQXF3JK5"),
113+
})
114+
.await;
115+
116+
assert_matches!(result, Ok(()));
117+
}
118+
}

0 commit comments

Comments
 (0)