From f2b30597feedb3cd21aed2c083f81f0ba1d8daba Mon Sep 17 00:00:00 2001 From: Petar Drakulic Date: Sun, 26 Oct 2025 21:06:06 +0100 Subject: [PATCH 1/5] add list session method --- pkg/usermanagement/client.go | 89 +++++++++++++++++++++++ pkg/usermanagement/client_test.go | 46 ++++++++++++ pkg/usermanagement/usermanagement.go | 7 ++ pkg/usermanagement/usermanagement_test.go | 38 ++++++++++ 4 files changed, 180 insertions(+) diff --git a/pkg/usermanagement/client.go b/pkg/usermanagement/client.go index 62e3cf55..40817027 100644 --- a/pkg/usermanagement/client.go +++ b/pkg/usermanagement/client.go @@ -588,6 +588,50 @@ type ListIdentitiesOpts struct { ID string `json:"id"` } +type Session struct { + // The Object will always be "session". + Object string `json:"object"` + // The unique identifier for the Session. + ID string `json:"id"` + // The unique identifier for the User. + UserID string `json:"user_id"` + // The unique identifier for the Organization. + OrganizationID string `json:"organization_id"` + // The status of the Session. + Status string `json:"status"` + // The authentication method used to create the Session. + AuthMethod string `json:"auth_method"` + // The IP address from which the Session was created. + IPAddress string `json:"ip_address"` + // The user agent from which the Session was created. + UserAgent string `json:"user_agent"` + // The time at which the Session expires. + ExpiresAt time.Time `json:"expires_at"` + // The time at which the Session ended, if applicable. + EndedAt *time.Time `json:"ended_at"` + // The time at which the Session was created. + CreatedAt time.Time `json:"created_at"` + // The time at which the Session was last updated. + UpdatedAt time.Time `json:"updated_at"` +} + +type ListSessionsOpts struct { + UserID string + Limit int + Before string + After string + Order Order +} + +type ListSessionsResponse struct { + // The Object will always be "list". + Object string `json:"object"` + // List of Sessions + Data []Session `json:"data"` + // Cursor to paginate through the list of Users + ListMetadata common.ListMetadata `json:"list_metadata"` +} + func NewClient(apiKey string) *Client { return &Client{ APIKey: apiKey, @@ -2334,3 +2378,48 @@ func (c *Client) RevokeSession(ctx context.Context, opts RevokeSessionOpts) erro return nil } + +func (c *Client) ListSessions(ctx context.Context, opts ListSessionsOpts) (ListSessionsResponse, error) { + req, err := http.NewRequest( + http.MethodGet, + fmt.Sprintf("%s/user_management/sessions", c.Endpoint), + nil, + ) + if err != nil { + return ListSessionsResponse{}, err + } + req = req.WithContext(ctx) + req.Header.Set("User-Agent", "workos-go/"+workos.Version) + req.Header.Set("Authorization", "Bearer "+c.APIKey) + req.Header.Set("Content-Type", "application/json") + + if opts.Limit == 0 { + opts.Limit = ResponseLimit + } + + if opts.Order == "" { + opts.Order = Desc + } + + queryValues, err := query.Values(opts) + if err != nil { + return ListSessionsResponse{}, err + } + + req.URL.RawQuery = queryValues.Encode() + res, err := c.HTTPClient.Do(req) + if err != nil { + return ListSessionsResponse{}, err + } + defer res.Body.Close() + + if err = workos_errors.TryGetHTTPError(res); err != nil { + return ListSessionsResponse{}, err + } + + var body ListSessionsResponse + dec := json.NewDecoder(res.Body) + err = dec.Decode(&body) + + return body, err +} diff --git a/pkg/usermanagement/client_test.go b/pkg/usermanagement/client_test.go index e579b7c2..aa2558e3 100644 --- a/pkg/usermanagement/client_test.go +++ b/pkg/usermanagement/client_test.go @@ -3584,6 +3584,52 @@ func RevokeSessionTestHandler(w http.ResponseWriter, r *http.Request) { w.Write(body) } +func listSessionsTestHandler(w http.ResponseWriter, r *http.Request) { + auth := r.Header.Get("Authorization") + if auth != "Bearer test" { + http.Error(w, "bad auth", http.StatusUnauthorized) + return + } + + if userAgent := r.Header.Get("User-Agent"); !strings.Contains(userAgent, "workos-go/") { + w.WriteHeader(http.StatusBadRequest) + return + } + + body, err := json.Marshal(struct { + ListSessionsResponse + }{ + ListSessionsResponse: ListSessionsResponse{ + Object: "list", + Data: []Session{ + { + Object: "session", + ID: "session_01E4ZCR3C56J083X43JQXF3JK5", + UserID: "user_01E4ZCR3C5A4QZ2Z2JQXGKZJ9E", + OrganizationID: "org_01E4ZCR3C56J083X43JQXF3JK5", + Status: "active", + AuthMethod: "password", + IPAddress: "192.168.1.1", + UserAgent: "Mozilla/5.0", + ExpiresAt: time.Date(2021, 6, 25, 19, 7, 33, 155000000, time.UTC), + CreatedAt: time.Date(2021, 6, 25, 19, 7, 33, 155000000, time.UTC), + UpdatedAt: time.Date(2021, 6, 25, 19, 7, 33, 155000000, time.UTC), + }, + }, + ListMetadata: common.ListMetadata{ + After: "", + }, + }, + }) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } + + w.WriteHeader(http.StatusOK) + w.Write(body) +} + func TestListInvitations_UnmarshalSnakeCaseListMetadata(t *testing.T) { raw := []byte(`{ "data": [], diff --git a/pkg/usermanagement/usermanagement.go b/pkg/usermanagement/usermanagement.go index 8d079801..55ca200c 100644 --- a/pkg/usermanagement/usermanagement.go +++ b/pkg/usermanagement/usermanagement.go @@ -351,3 +351,10 @@ func GetLogoutURL(opts GetLogoutURLOpts) (*url.URL, error) { func RevokeSession(ctx context.Context, opts RevokeSessionOpts) error { return DefaultClient.RevokeSession(ctx, opts) } + +func ListSessions( + ctx context.Context, + opts ListSessionsOpts, +) (ListSessionsResponse, error) { + return DefaultClient.ListSessions(ctx, opts) +} diff --git a/pkg/usermanagement/usermanagement_test.go b/pkg/usermanagement/usermanagement_test.go index 041c7e88..5676fca0 100644 --- a/pkg/usermanagement/usermanagement_test.go +++ b/pkg/usermanagement/usermanagement_test.go @@ -1090,3 +1090,41 @@ func TestUsersRevokeSession(t *testing.T) { require.NoError(t, err) } + +func TestUserManagementListSessions(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(listSessionsTestHandler)) + + defer server.Close() + + DefaultClient = mockClient(server) + + SetAPIKey("test") + + expectedResponse := ListSessionsResponse{ + Object: "list", + Data: []Session{ + { + Object: "session", + ID: "session_01E4ZCR3C56J083X43JQXF3JK5", + UserID: "user_01E4ZCR3C5A4QZ2Z2JQXGKZJ9E", + OrganizationID: "org_01E4ZCR3C56J083X43JQXF3JK5", + Status: "active", + AuthMethod: "password", + IPAddress: "192.168.1.1", + UserAgent: "Mozilla/5.0", + }, + }, + ListMetadata: common.ListMetadata{ + After: "", + }, + } + + sessionsRes, err := ListSessions(context.Background(), ListSessionsOpts{}) + + require.NoError(t, err) + require.Equal(t, expectedResponse.Object, sessionsRes.Object) + require.Equal(t, len(expectedResponse.Data), len(sessionsRes.Data)) + require.Equal(t, expectedResponse.Data[0].ID, sessionsRes.Data[0].ID) + require.Equal(t, expectedResponse.Data[0].UserID, sessionsRes.Data[0].UserID) + require.Equal(t, expectedResponse.Data[0].Status, sessionsRes.Data[0].Status) +} From 68cfe89a1395d6db6f8c8e09be534b2d0949e2f2 Mon Sep 17 00:00:00 2001 From: petarTenderly <145981669+petarTenderly@users.noreply.github.com> Date: Mon, 27 Oct 2025 09:20:28 +0100 Subject: [PATCH 2/5] Update pkg/usermanagement/client.go Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- pkg/usermanagement/client.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/usermanagement/client.go b/pkg/usermanagement/client.go index 40817027..46ee8eea 100644 --- a/pkg/usermanagement/client.go +++ b/pkg/usermanagement/client.go @@ -616,11 +616,11 @@ type Session struct { } type ListSessionsOpts struct { - UserID string - Limit int - Before string - After string - Order Order + UserID string `url:"user_id,omitempty"` + Limit int `url:"limit"` + Before string `url:"before,omitempty"` + After string `url:"after,omitempty"` + Order Order `url:"order,omitempty"` } type ListSessionsResponse struct { From 12f0cfc36643caaaf01a141958f60556cfcae321 Mon Sep 17 00:00:00 2001 From: Petar Drakulic Date: Sun, 16 Nov 2025 16:22:28 +0100 Subject: [PATCH 3/5] change type of dates to string --- pkg/usermanagement/client.go | 8 ++++---- pkg/usermanagement/client_test.go | 6 +++--- pkg/usermanagement/usermanagement_test.go | 13 ++++++------- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/pkg/usermanagement/client.go b/pkg/usermanagement/client.go index 46ee8eea..7bc987d8 100644 --- a/pkg/usermanagement/client.go +++ b/pkg/usermanagement/client.go @@ -606,13 +606,13 @@ type Session struct { // The user agent from which the Session was created. UserAgent string `json:"user_agent"` // The time at which the Session expires. - ExpiresAt time.Time `json:"expires_at"` + ExpiresAt string `json:"expires_at"` // The time at which the Session ended, if applicable. - EndedAt *time.Time `json:"ended_at"` + EndedAt *string `json:"ended_at"` // The time at which the Session was created. - CreatedAt time.Time `json:"created_at"` + CreatedAt string `json:"created_at"` // The time at which the Session was last updated. - UpdatedAt time.Time `json:"updated_at"` + UpdatedAt string `json:"updated_at"` } type ListSessionsOpts struct { diff --git a/pkg/usermanagement/client_test.go b/pkg/usermanagement/client_test.go index aa2558e3..cbb48c66 100644 --- a/pkg/usermanagement/client_test.go +++ b/pkg/usermanagement/client_test.go @@ -3611,9 +3611,9 @@ func listSessionsTestHandler(w http.ResponseWriter, r *http.Request) { AuthMethod: "password", IPAddress: "192.168.1.1", UserAgent: "Mozilla/5.0", - ExpiresAt: time.Date(2021, 6, 25, 19, 7, 33, 155000000, time.UTC), - CreatedAt: time.Date(2021, 6, 25, 19, 7, 33, 155000000, time.UTC), - UpdatedAt: time.Date(2021, 6, 25, 19, 7, 33, 155000000, time.UTC), + ExpiresAt: "2021-07-25T19:07:33.155Z", + CreatedAt: "2021-06-25T19:07:33.155Z", + UpdatedAt: "2021-06-25T19:07:33.155Z", }, }, ListMetadata: common.ListMetadata{ diff --git a/pkg/usermanagement/usermanagement_test.go b/pkg/usermanagement/usermanagement_test.go index 5676fca0..bc6ff9d6 100644 --- a/pkg/usermanagement/usermanagement_test.go +++ b/pkg/usermanagement/usermanagement_test.go @@ -2,13 +2,12 @@ package usermanagement import ( "context" + "github.com/workos/workos-go/v5/pkg/common" + "github.com/workos/workos-go/v5/pkg/mfa" "net/http" "net/http/httptest" "testing" - "github.com/workos/workos-go/v5/pkg/common" - "github.com/workos/workos-go/v5/pkg/mfa" - "github.com/stretchr/testify/require" ) @@ -1099,7 +1098,6 @@ func TestUserManagementListSessions(t *testing.T) { DefaultClient = mockClient(server) SetAPIKey("test") - expectedResponse := ListSessionsResponse{ Object: "list", Data: []Session{ @@ -1112,6 +1110,9 @@ func TestUserManagementListSessions(t *testing.T) { AuthMethod: "password", IPAddress: "192.168.1.1", UserAgent: "Mozilla/5.0", + ExpiresAt: "2021-06-25T19:07:33.155Z", + CreatedAt: "2021-06-25T19:07:33.155Z", + UpdatedAt: "2021-06-25T19:07:33.155Z", }, }, ListMetadata: common.ListMetadata{ @@ -1124,7 +1125,5 @@ func TestUserManagementListSessions(t *testing.T) { require.NoError(t, err) require.Equal(t, expectedResponse.Object, sessionsRes.Object) require.Equal(t, len(expectedResponse.Data), len(sessionsRes.Data)) - require.Equal(t, expectedResponse.Data[0].ID, sessionsRes.Data[0].ID) - require.Equal(t, expectedResponse.Data[0].UserID, sessionsRes.Data[0].UserID) - require.Equal(t, expectedResponse.Data[0].Status, sessionsRes.Data[0].Status) + require.Equal(t, expectedResponse.Data[0], sessionsRes.Data[0]) } From e595a0bf50eaba02f0ab73fcc5ad0ecc86c26dbf Mon Sep 17 00:00:00 2001 From: Petar Drakulic Date: Sun, 16 Nov 2025 17:05:16 +0100 Subject: [PATCH 4/5] userID in param list --- pkg/usermanagement/client.go | 5 ++--- pkg/usermanagement/usermanagement.go | 3 ++- pkg/usermanagement/usermanagement_test.go | 12 +++++------- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/pkg/usermanagement/client.go b/pkg/usermanagement/client.go index 7bc987d8..c5c72cda 100644 --- a/pkg/usermanagement/client.go +++ b/pkg/usermanagement/client.go @@ -616,7 +616,6 @@ type Session struct { } type ListSessionsOpts struct { - UserID string `url:"user_id,omitempty"` Limit int `url:"limit"` Before string `url:"before,omitempty"` After string `url:"after,omitempty"` @@ -2379,10 +2378,10 @@ func (c *Client) RevokeSession(ctx context.Context, opts RevokeSessionOpts) erro return nil } -func (c *Client) ListSessions(ctx context.Context, opts ListSessionsOpts) (ListSessionsResponse, error) { +func (c *Client) ListSessions(ctx context.Context, userID string, opts ListSessionsOpts) (ListSessionsResponse, error) { req, err := http.NewRequest( http.MethodGet, - fmt.Sprintf("%s/user_management/sessions", c.Endpoint), + fmt.Sprintf("%s/user_management/users/%s/sessions", c.Endpoint, userID), nil, ) if err != nil { diff --git a/pkg/usermanagement/usermanagement.go b/pkg/usermanagement/usermanagement.go index 55ca200c..fef20807 100644 --- a/pkg/usermanagement/usermanagement.go +++ b/pkg/usermanagement/usermanagement.go @@ -354,7 +354,8 @@ func RevokeSession(ctx context.Context, opts RevokeSessionOpts) error { func ListSessions( ctx context.Context, + userID string, opts ListSessionsOpts, ) (ListSessionsResponse, error) { - return DefaultClient.ListSessions(ctx, opts) + return DefaultClient.ListSessions(ctx, userID, opts) } diff --git a/pkg/usermanagement/usermanagement_test.go b/pkg/usermanagement/usermanagement_test.go index bc6ff9d6..18ce194a 100644 --- a/pkg/usermanagement/usermanagement_test.go +++ b/pkg/usermanagement/usermanagement_test.go @@ -1094,7 +1094,7 @@ func TestUserManagementListSessions(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(listSessionsTestHandler)) defer server.Close() - + userID := "user_01E4ZCR3C5A4QZ2Z2JQXGKZJ9E" DefaultClient = mockClient(server) SetAPIKey("test") @@ -1104,13 +1104,13 @@ func TestUserManagementListSessions(t *testing.T) { { Object: "session", ID: "session_01E4ZCR3C56J083X43JQXF3JK5", - UserID: "user_01E4ZCR3C5A4QZ2Z2JQXGKZJ9E", + UserID: userID, OrganizationID: "org_01E4ZCR3C56J083X43JQXF3JK5", Status: "active", AuthMethod: "password", IPAddress: "192.168.1.1", UserAgent: "Mozilla/5.0", - ExpiresAt: "2021-06-25T19:07:33.155Z", + ExpiresAt: "2021-07-25T19:07:33.155Z", CreatedAt: "2021-06-25T19:07:33.155Z", UpdatedAt: "2021-06-25T19:07:33.155Z", }, @@ -1120,10 +1120,8 @@ func TestUserManagementListSessions(t *testing.T) { }, } - sessionsRes, err := ListSessions(context.Background(), ListSessionsOpts{}) + sessionsRes, err := ListSessions(context.Background(), userID, ListSessionsOpts{}) require.NoError(t, err) - require.Equal(t, expectedResponse.Object, sessionsRes.Object) - require.Equal(t, len(expectedResponse.Data), len(sessionsRes.Data)) - require.Equal(t, expectedResponse.Data[0], sessionsRes.Data[0]) + require.Equal(t, expectedResponse, sessionsRes) } From 9825514e6a82b79d2fc179c9f4631def3256b231 Mon Sep 17 00:00:00 2001 From: Petar Drakulic Date: Sun, 16 Nov 2025 17:08:06 +0100 Subject: [PATCH 5/5] fix --- pkg/usermanagement/usermanagement_test.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/pkg/usermanagement/usermanagement_test.go b/pkg/usermanagement/usermanagement_test.go index 9b8923d5..c5e3739b 100644 --- a/pkg/usermanagement/usermanagement_test.go +++ b/pkg/usermanagement/usermanagement_test.go @@ -2,8 +2,6 @@ package usermanagement import ( "context" - "github.com/workos/workos-go/v5/pkg/common" - "github.com/workos/workos-go/v5/pkg/mfa" "net/http" "net/http/httptest" "testing"