Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
Add authorization permissions support
Introduce the authorization module with CRUD operations for permissions
including create, list (paginated), get, update (PATCH), and delete.
Register the module on both sync and async clients.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
  • Loading branch information
csrbarber and claude committed Feb 17, 2026
commit 87f4b680a7015647d272b3e51a3597055fad01dc
5 changes: 5 additions & 0 deletions src/workos/_base_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from workos._client_configuration import ClientConfiguration
from workos.api_keys import ApiKeysModule
from workos.audit_logs import AuditLogsModule
from workos.authorization import AuthorizationModule
from workos.directory_sync import DirectorySyncModule
from workos.events import EventsModule
from workos.fga import FGAModule
Expand Down Expand Up @@ -74,6 +75,10 @@ def __init__(
@abstractmethod
def api_keys(self) -> ApiKeysModule: ...

@property
@abstractmethod
def authorization(self) -> AuthorizationModule: ...

@property
@abstractmethod
def audit_logs(self) -> AuditLogsModule: ...
Expand Down
7 changes: 7 additions & 0 deletions src/workos/async_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from workos._base_client import BaseClient
from workos.api_keys import AsyncApiKeys
from workos.audit_logs import AsyncAuditLogs
from workos.authorization import AsyncAuthorization
from workos.directory_sync import AsyncDirectorySync
from workos.events import AsyncEvents
from workos.fga import FGAModule
Expand Down Expand Up @@ -55,6 +56,12 @@ def api_keys(self) -> AsyncApiKeys:
self._api_keys = AsyncApiKeys(self._http_client)
return self._api_keys

@property
def authorization(self) -> AsyncAuthorization:
if not getattr(self, "_authorization", None):
self._authorization = AsyncAuthorization(self._http_client)
return self._authorization

@property
def sso(self) -> AsyncSSO:
if not getattr(self, "_sso", None):
Expand Down
239 changes: 239 additions & 0 deletions src/workos/authorization.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
from typing import Any, Dict, Optional, Protocol

from workos.types.authorization.permission import Permission
from workos.types.list_resource import (
ListArgs,
ListMetadata,
ListPage,
WorkOSListResource,
)
from workos.typing.sync_or_async import SyncOrAsync
from workos.utils.http_client import AsyncHTTPClient, SyncHTTPClient
from workos.utils.pagination_order import PaginationOrder
from workos.utils.request_helper import (
DEFAULT_LIST_RESPONSE_LIMIT,
REQUEST_METHOD_DELETE,
REQUEST_METHOD_GET,
REQUEST_METHOD_PATCH,
REQUEST_METHOD_POST,
)

AUTHORIZATION_PERMISSIONS_PATH = "authorization/permissions"


class PermissionListFilters(ListArgs, total=False):
pass


PermissionsListResource = WorkOSListResource[
Permission, PermissionListFilters, ListMetadata
]


class AuthorizationModule(Protocol):
"""Offers methods through the WorkOS Authorization service."""

def create_permission(
self,
*,
slug: str,
name: str,
description: Optional[str] = None,
) -> SyncOrAsync[Permission]: ...

def list_permissions(
self,
*,
limit: int = DEFAULT_LIST_RESPONSE_LIMIT,
before: Optional[str] = None,
after: Optional[str] = None,
order: PaginationOrder = "desc",
) -> SyncOrAsync[PermissionsListResource]: ...

def get_permission(self, slug: str) -> SyncOrAsync[Permission]: ...

def update_permission(
self,
slug: str,
*,
name: Optional[str] = None,
description: Optional[str] = None,
) -> SyncOrAsync[Permission]: ...

def delete_permission(self, slug: str) -> SyncOrAsync[None]: ...


class Authorization(AuthorizationModule):
_http_client: SyncHTTPClient

def __init__(self, http_client: SyncHTTPClient):
self._http_client = http_client

def create_permission(
self,
*,
slug: str,
name: str,
description: Optional[str] = None,
) -> Permission:
json: Dict[str, Any] = {"slug": slug, "name": name}
if description is not None:
json["description"] = description

response = self._http_client.request(
AUTHORIZATION_PERMISSIONS_PATH,
method=REQUEST_METHOD_POST,
json=json,
)

return Permission.model_validate(response)

def list_permissions(
self,
*,
limit: int = DEFAULT_LIST_RESPONSE_LIMIT,
before: Optional[str] = None,
after: Optional[str] = None,
order: PaginationOrder = "desc",
) -> PermissionsListResource:
list_params: PermissionListFilters = {
"limit": limit,
"before": before,
"after": after,
"order": order,
}

response = self._http_client.request(
AUTHORIZATION_PERMISSIONS_PATH,
method=REQUEST_METHOD_GET,
params=list_params,
)

return WorkOSListResource[Permission, PermissionListFilters, ListMetadata](
list_method=self.list_permissions,
list_args=list_params,
**ListPage[Permission](**response).model_dump(),
)

def get_permission(self, slug: str) -> Permission:
response = self._http_client.request(
f"{AUTHORIZATION_PERMISSIONS_PATH}/{slug}",
method=REQUEST_METHOD_GET,
)

return Permission.model_validate(response)

def update_permission(
self,
slug: str,
*,
name: Optional[str] = None,
description: Optional[str] = None,
) -> Permission:
json: Dict[str, Any] = {}
if name is not None:
json["name"] = name
if description is not None:
json["description"] = description

response = self._http_client.request(
f"{AUTHORIZATION_PERMISSIONS_PATH}/{slug}",
method=REQUEST_METHOD_PATCH,
json=json,
)

return Permission.model_validate(response)

def delete_permission(self, slug: str) -> None:
self._http_client.request(
f"{AUTHORIZATION_PERMISSIONS_PATH}/{slug}",
method=REQUEST_METHOD_DELETE,
)


class AsyncAuthorization(AuthorizationModule):
_http_client: AsyncHTTPClient

def __init__(self, http_client: AsyncHTTPClient):
self._http_client = http_client

async def create_permission(
self,
*,
slug: str,
name: str,
description: Optional[str] = None,
) -> Permission:
json: Dict[str, Any] = {"slug": slug, "name": name}
if description is not None:
json["description"] = description

response = await self._http_client.request(
AUTHORIZATION_PERMISSIONS_PATH,
method=REQUEST_METHOD_POST,
json=json,
)

return Permission.model_validate(response)

async def list_permissions(
self,
*,
limit: int = DEFAULT_LIST_RESPONSE_LIMIT,
before: Optional[str] = None,
after: Optional[str] = None,
order: PaginationOrder = "desc",
) -> PermissionsListResource:
list_params: PermissionListFilters = {
"limit": limit,
"before": before,
"after": after,
"order": order,
}

response = await self._http_client.request(
AUTHORIZATION_PERMISSIONS_PATH,
method=REQUEST_METHOD_GET,
params=list_params,
)

return WorkOSListResource[Permission, PermissionListFilters, ListMetadata](
list_method=self.list_permissions,
list_args=list_params,
**ListPage[Permission](**response).model_dump(),
)

async def get_permission(self, slug: str) -> Permission:
response = await self._http_client.request(
f"{AUTHORIZATION_PERMISSIONS_PATH}/{slug}",
method=REQUEST_METHOD_GET,
)

return Permission.model_validate(response)

async def update_permission(
self,
slug: str,
*,
name: Optional[str] = None,
description: Optional[str] = None,
) -> Permission:
json: Dict[str, Any] = {}
if name is not None:
json["name"] = name
if description is not None:
json["description"] = description

response = await self._http_client.request(
f"{AUTHORIZATION_PERMISSIONS_PATH}/{slug}",
method=REQUEST_METHOD_PATCH,
json=json,
)

return Permission.model_validate(response)

async def delete_permission(self, slug: str) -> None:
await self._http_client.request(
f"{AUTHORIZATION_PERMISSIONS_PATH}/{slug}",
method=REQUEST_METHOD_DELETE,
)
7 changes: 7 additions & 0 deletions src/workos/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from workos._base_client import BaseClient
from workos.api_keys import ApiKeys
from workos.audit_logs import AuditLogs
from workos.authorization import Authorization
from workos.directory_sync import DirectorySync
from workos.fga import FGA
from workos.organizations import Organizations
Expand Down Expand Up @@ -55,6 +56,12 @@ def api_keys(self) -> ApiKeys:
self._api_keys = ApiKeys(self._http_client)
return self._api_keys

@property
def authorization(self) -> Authorization:
if not getattr(self, "_authorization", None):
self._authorization = Authorization(self._http_client)
return self._authorization

@property
def sso(self) -> SSO:
if not getattr(self, "_sso", None):
Expand Down
3 changes: 3 additions & 0 deletions src/workos/types/authorization/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from workos.types.authorization.permission import (
Permission as Permission,
)
14 changes: 14 additions & 0 deletions src/workos/types/authorization/permission.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from typing import Literal, Optional

from workos.types.workos_model import WorkOSModel


class Permission(WorkOSModel):
object: Literal["permission"]
id: str
slug: str
name: str
description: Optional[str] = None
system: bool
created_at: str
updated_at: str
2 changes: 2 additions & 0 deletions src/workos/types/list_resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from typing_extensions import Required, TypedDict
from workos.types.api_keys import ApiKey
from workos.types.audit_logs import AuditLogAction, AuditLogSchema
from workos.types.authorization.permission import Permission
from workos.types.directory_sync import (
Directory,
DirectoryGroup,
Expand Down Expand Up @@ -57,6 +58,7 @@
Invitation,
Organization,
OrganizationMembership,
Permission,
AuthorizationResource,
AuthorizationResourceType,
User,
Expand Down
1 change: 1 addition & 0 deletions src/workos/utils/request_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
RESPONSE_TYPE_CODE = "code"
REQUEST_METHOD_DELETE = "delete"
REQUEST_METHOD_GET = "get"
REQUEST_METHOD_PATCH = "patch"
REQUEST_METHOD_POST = "post"
REQUEST_METHOD_PUT = "put"

Expand Down
Loading