diff --git a/pkg/usermanagement/client.go b/pkg/usermanagement/client.go index 102268bc..97d9d113 100644 --- a/pkg/usermanagement/client.go +++ b/pkg/usermanagement/client.go @@ -588,6 +588,49 @@ 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 string `json:"expires_at"` + // The time at which the Session ended, if applicable. + EndedAt *string `json:"ended_at"` + // The time at which the Session was created. + CreatedAt string `json:"created_at"` + // The time at which the Session was last updated. + UpdatedAt string `json:"updated_at"` +} + +type ListSessionsOpts struct { + Limit int `url:"limit"` + Before string `url:"before,omitempty"` + After string `url:"after,omitempty"` + Order Order `url:"order,omitempty"` +} + +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 +2377,48 @@ func (c *Client) RevokeSession(ctx context.Context, opts RevokeSessionOpts) erro return nil } + +func (c *Client) ListSessions(ctx context.Context, userID string, opts ListSessionsOpts) (ListSessionsResponse, error) { + req, err := http.NewRequest( + http.MethodGet, + fmt.Sprintf("%s/user_management/users/%s/sessions", c.Endpoint, userID), + 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 4d144cf0..65f2a312 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: "2021-07-25T19:07:33.155Z", + CreatedAt: "2021-06-25T19:07:33.155Z", + UpdatedAt: "2021-06-25T19:07:33.155Z", + }, + }, + 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..fef20807 100644 --- a/pkg/usermanagement/usermanagement.go +++ b/pkg/usermanagement/usermanagement.go @@ -351,3 +351,11 @@ 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, + userID string, + opts ListSessionsOpts, +) (ListSessionsResponse, error) { + return DefaultClient.ListSessions(ctx, userID, opts) +} diff --git a/pkg/usermanagement/usermanagement_test.go b/pkg/usermanagement/usermanagement_test.go index 7607cc6d..c5e3739b 100644 --- a/pkg/usermanagement/usermanagement_test.go +++ b/pkg/usermanagement/usermanagement_test.go @@ -1090,3 +1090,39 @@ func TestUsersRevokeSession(t *testing.T) { require.NoError(t, err) } + +func TestUserManagementListSessions(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(listSessionsTestHandler)) + + defer server.Close() + userID := "user_01E4ZCR3C5A4QZ2Z2JQXGKZJ9E" + DefaultClient = mockClient(server) + + SetAPIKey("test") + expectedResponse := ListSessionsResponse{ + Object: "list", + Data: []Session{ + { + Object: "session", + ID: "session_01E4ZCR3C56J083X43JQXF3JK5", + UserID: userID, + OrganizationID: "org_01E4ZCR3C56J083X43JQXF3JK5", + Status: "active", + AuthMethod: "password", + IPAddress: "192.168.1.1", + UserAgent: "Mozilla/5.0", + ExpiresAt: "2021-07-25T19:07:33.155Z", + CreatedAt: "2021-06-25T19:07:33.155Z", + UpdatedAt: "2021-06-25T19:07:33.155Z", + }, + }, + ListMetadata: common.ListMetadata{ + After: "", + }, + } + + sessionsRes, err := ListSessions(context.Background(), userID, ListSessionsOpts{}) + + require.NoError(t, err) + require.Equal(t, expectedResponse, sessionsRes) +}