diff --git a/.release-please-manifest.json b/.release-please-manifest.json index f7014c3..a713055 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.11.0" + ".": "0.12.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index d9f05c9..76c2c81 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 106 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/keycard/keycard-api-98bb06a2aca1b7ea066960a14eb4879aa4967bc4ed9686b66e60438824ce8415.yml -openapi_spec_hash: b7bdac3288a2e626465c0baf6df49c17 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/keycard/keycard-api-52cd0ce5e22a084f1d1b6479ea85a80066c68c88d70ec7b23daeceba6264797b.yml +openapi_spec_hash: 438cb543611b1ecd037414d52791a4a0 config_hash: b0ee2dc67cc490e45f8fe0acabd76206 diff --git a/CHANGELOG.md b/CHANGELOG.md index b54ceb4..3aa1ef0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 0.12.0 (2026-05-21) + +Full Changelog: [v0.11.0...v0.12.0](https://github.com/keycardai/keycard-python/compare/v0.11.0...v0.12.0) + +### Features + +* add scopes to provider openid protocol schemas (ACC-354) ([350683d](https://github.com/keycardai/keycard-python/commit/350683d62f6d36e181f69effb96cd4a6354853db)) + ## 0.11.0 (2026-05-15) Full Changelog: [v0.10.0...v0.11.0](https://github.com/keycardai/keycard-python/compare/v0.10.0...v0.11.0) diff --git a/pyproject.toml b/pyproject.toml index bfa48fe..531e706 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "keycardai_api" -version = "0.11.0" +version = "0.12.0" description = "The official Python library for the keycard-api API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/keycardai_api/_version.py b/src/keycardai_api/_version.py index fb2e193..68072d8 100644 --- a/src/keycardai_api/_version.py +++ b/src/keycardai_api/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "keycardai_api" -__version__ = "0.11.0" # x-release-please-version +__version__ = "0.12.0" # x-release-please-version diff --git a/src/keycardai_api/resources/zones/delegated_grants.py b/src/keycardai_api/resources/zones/delegated_grants.py index 9287fa8..4533bc5 100644 --- a/src/keycardai_api/resources/zones/delegated_grants.py +++ b/src/keycardai_api/resources/zones/delegated_grants.py @@ -129,6 +129,7 @@ def list( expand: Union[Literal["total_count"], List[Literal["total_count"]]] | Omit = omit, limit: int | Omit = omit, resource_id: str | Omit = omit, + sort: str | Omit = omit, status: Literal["active", "expired", "revoked"] | Omit = omit, user_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -141,7 +142,9 @@ def list( """Returns a list of delegated grants in the specified zone. Can be filtered by - user, resource, or status. + user, resource, or status. Use cursor pagination via `after`/`before`. Sort: + comma-separated field list; prefix with `-` for descending. Use + `expand[]=total_count` to include the matching row count. Args: after: Cursor for forward pagination @@ -152,6 +155,8 @@ def list( resource_id: Filter by resource ID + sort: Comma-separated sort fields. Prefix with - for descending. Allowed: created_at + user_id: Filter by user ID extra_headers: Send extra headers @@ -179,6 +184,7 @@ def list( "expand": expand, "limit": limit, "resource_id": resource_id, + "sort": sort, "status": status, "user_id": user_id, }, @@ -333,6 +339,7 @@ async def list( expand: Union[Literal["total_count"], List[Literal["total_count"]]] | Omit = omit, limit: int | Omit = omit, resource_id: str | Omit = omit, + sort: str | Omit = omit, status: Literal["active", "expired", "revoked"] | Omit = omit, user_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -345,7 +352,9 @@ async def list( """Returns a list of delegated grants in the specified zone. Can be filtered by - user, resource, or status. + user, resource, or status. Use cursor pagination via `after`/`before`. Sort: + comma-separated field list; prefix with `-` for descending. Use + `expand[]=total_count` to include the matching row count. Args: after: Cursor for forward pagination @@ -356,6 +365,8 @@ async def list( resource_id: Filter by resource ID + sort: Comma-separated sort fields. Prefix with - for descending. Allowed: created_at + user_id: Filter by user ID extra_headers: Send extra headers @@ -383,6 +394,7 @@ async def list( "expand": expand, "limit": limit, "resource_id": resource_id, + "sort": sort, "status": status, "user_id": user_id, }, diff --git a/src/keycardai_api/resources/zones/users.py b/src/keycardai_api/resources/zones/users.py index f79e92f..b542c59 100644 --- a/src/keycardai_api/resources/zones/users.py +++ b/src/keycardai_api/resources/zones/users.py @@ -87,7 +87,11 @@ def list( *, after: str | Omit = omit, before: str | Omit = omit, - expand: Union[Literal["total_count"], List[Literal["total_count"]]] | Omit = omit, + expand: Union[ + Literal["total_count", "session_count", "grant_count"], + List[Literal["total_count", "session_count", "grant_count"]], + ] + | Omit = omit, filter_email: Union[str, SequenceNotStr[str]] | Omit = omit, filter_id: Union[str, SequenceNotStr[str]] | Omit = omit, limit: int | Omit = omit, @@ -115,14 +119,15 @@ def list( Use cursor pagination via `after`/`before`. Sort: comma-separated field list; prefix with `-` for descending. Use `expand[]=total_count` to include the - matching row count. Filter by exact email via `filter[email]`; search via - `query[email]` / `query[subject]` / `query[]` (substring match, OR'd across - repeated values). `query[]` matches against email and federation credential - subject. Pass `filter[id]` (repeatable, max 100) to restrict results to a known - set of users — mutually exclusive with `after`/`before` (returns 400 if - combined). When `filter[id]` is set, `limit` is ignored and the response - contains every requested user that exists in the zone, in a single page. IDs not - in the zone are silently omitted. + matching row count, `expand[]=session_count` to include per-user session counts, + and `expand[]=grant_count` to include per-user delegated-grant counts. Filter by + exact email via `filter[email]`; search via `query[email]` / `query[subject]` / + `query[]` (substring match, OR'd across repeated values). `query[]` matches + against email and federation credential subject. Pass `filter[id]` (repeatable, + max 100) to restrict results to a known set of users — mutually exclusive with + `after`/`before` (returns 400 if combined). When `filter[id]` is set, `limit` is + ignored and the response contains every requested user that exists in the zone, + in a single page. IDs not in the zone are silently omitted. Args: after: Cursor for forward pagination @@ -244,7 +249,11 @@ async def list( *, after: str | Omit = omit, before: str | Omit = omit, - expand: Union[Literal["total_count"], List[Literal["total_count"]]] | Omit = omit, + expand: Union[ + Literal["total_count", "session_count", "grant_count"], + List[Literal["total_count", "session_count", "grant_count"]], + ] + | Omit = omit, filter_email: Union[str, SequenceNotStr[str]] | Omit = omit, filter_id: Union[str, SequenceNotStr[str]] | Omit = omit, limit: int | Omit = omit, @@ -272,14 +281,15 @@ async def list( Use cursor pagination via `after`/`before`. Sort: comma-separated field list; prefix with `-` for descending. Use `expand[]=total_count` to include the - matching row count. Filter by exact email via `filter[email]`; search via - `query[email]` / `query[subject]` / `query[]` (substring match, OR'd across - repeated values). `query[]` matches against email and federation credential - subject. Pass `filter[id]` (repeatable, max 100) to restrict results to a known - set of users — mutually exclusive with `after`/`before` (returns 400 if - combined). When `filter[id]` is set, `limit` is ignored and the response - contains every requested user that exists in the zone, in a single page. IDs not - in the zone are silently omitted. + matching row count, `expand[]=session_count` to include per-user session counts, + and `expand[]=grant_count` to include per-user delegated-grant counts. Filter by + exact email via `filter[email]`; search via `query[email]` / `query[subject]` / + `query[]` (substring match, OR'd across repeated values). `query[]` matches + against email and federation credential subject. Pass `filter[id]` (repeatable, + max 100) to restrict results to a known set of users — mutually exclusive with + `after`/`before` (returns 400 if combined). When `filter[id]` is set, `limit` is + ignored and the response contains every requested user that exists in the zone, + in a single page. IDs not in the zone are silently omitted. Args: after: Cursor for forward pagination diff --git a/src/keycardai_api/types/zones/delegated_grant_list_params.py b/src/keycardai_api/types/zones/delegated_grant_list_params.py index 986b7a2..4cd92c4 100644 --- a/src/keycardai_api/types/zones/delegated_grant_list_params.py +++ b/src/keycardai_api/types/zones/delegated_grant_list_params.py @@ -27,6 +27,9 @@ class DelegatedGrantListParams(TypedDict, total=False): resource_id: str """Filter by resource ID""" + sort: str + """Comma-separated sort fields. Prefix with - for descending. Allowed: created_at""" + status: Literal["active", "expired", "revoked"] user_id: str diff --git a/src/keycardai_api/types/zones/provider.py b/src/keycardai_api/types/zones/provider.py index f7d1c0a..64f535a 100644 --- a/src/keycardai_api/types/zones/provider.py +++ b/src/keycardai_api/types/zones/provider.py @@ -65,6 +65,12 @@ class ProtocolsOauth2(BaseModel): class ProtocolsOpenid(BaseModel): """OpenID Connect protocol configuration""" + scopes: Optional[List[str]] = None + """Additional OIDC scopes to request from this provider during authentication (e.g. + + "groups"). Merged with the default scopes (openid, profile, email). + """ + user_identifier_claim: Optional[str] = None """ Name of a top-level string claim in this provider's ID Token to use as the user diff --git a/src/keycardai_api/types/zones/provider_create_params.py b/src/keycardai_api/types/zones/provider_create_params.py index 64ba971..f5c21ab 100644 --- a/src/keycardai_api/types/zones/provider_create_params.py +++ b/src/keycardai_api/types/zones/provider_create_params.py @@ -102,6 +102,12 @@ class ProtocolsOauth2(TypedDict, total=False): class ProtocolsOpenid(TypedDict, total=False): """OpenID Connect protocol configuration for provider creation""" + scopes: SequenceNotStr[str] + """Additional OIDC scopes to request from this provider during authentication (e.g. + + "groups"). Merged with the default scopes (openid, profile, email). + """ + user_identifier_claim: str """ Name of a top-level string claim in this provider's ID Token to use as the user diff --git a/src/keycardai_api/types/zones/provider_update_params.py b/src/keycardai_api/types/zones/provider_update_params.py index ee710c1..99ea899 100644 --- a/src/keycardai_api/types/zones/provider_update_params.py +++ b/src/keycardai_api/types/zones/provider_update_params.py @@ -107,6 +107,13 @@ class ProtocolsOauth2(TypedDict, total=False): class ProtocolsOpenid(TypedDict, total=False): """OpenID Connect protocol configuration. Set to null to remove all OpenID config.""" + scopes: Optional[SequenceNotStr[str]] + """Additional OIDC scopes to request from this provider during authentication (e.g. + + "groups"). Merged with the default scopes (openid, profile, email). Set to null + to clear. + """ + user_identifier_claim: Optional[str] """ Name of a top-level string claim in this provider's ID Token to use as the user diff --git a/src/keycardai_api/types/zones/user.py b/src/keycardai_api/types/zones/user.py index fb3cf04..49a592b 100644 --- a/src/keycardai_api/types/zones/user.py +++ b/src/keycardai_api/types/zones/user.py @@ -42,6 +42,12 @@ class User(BaseModel): authenticated_at: Optional[str] = None """Date when the user was last authenticated""" + grant_count: Optional[int] = None + """Delegated-grant count for this user. + + Populated only when `expand[]=grant_count` is set on the listing endpoint. + """ + issuer: Optional[str] = None """Issuer identifier of the identity provider""" @@ -52,5 +58,11 @@ class User(BaseModel): user is not deleted. """ + session_count: Optional[int] = None + """Session count for this user. + + Populated only when `expand[]=session_count` is set on the listing endpoint. + """ + subject: Optional[str] = None """Subject identifier from the identity provider""" diff --git a/src/keycardai_api/types/zones/user_list_params.py b/src/keycardai_api/types/zones/user_list_params.py index 5ef469f..c7e615d 100644 --- a/src/keycardai_api/types/zones/user_list_params.py +++ b/src/keycardai_api/types/zones/user_list_params.py @@ -18,7 +18,13 @@ class UserListParams(TypedDict, total=False): before: str """Cursor for backward pagination""" - expand: Annotated[Union[Literal["total_count"], List[Literal["total_count"]]], PropertyInfo(alias="expand[]")] + expand: Annotated[ + Union[ + Literal["total_count", "session_count", "grant_count"], + List[Literal["total_count", "session_count", "grant_count"]], + ], + PropertyInfo(alias="expand[]"), + ] filter_email: Annotated[Union[str, SequenceNotStr[str]], PropertyInfo(alias="filter[email]")] """Filter by exact email address""" diff --git a/tests/api_resources/zones/test_delegated_grants.py b/tests/api_resources/zones/test_delegated_grants.py index 705dc34..750d088 100644 --- a/tests/api_resources/zones/test_delegated_grants.py +++ b/tests/api_resources/zones/test_delegated_grants.py @@ -148,6 +148,7 @@ def test_method_list_with_all_params(self, client: KeycardAPI) -> None: expand="total_count", limit=1, resource_id="resource_id", + sort="-created_at, -created_at,\r\r \t \n\r-created_at,\n\n \t-created_at,\n \r \r created_at,\n\t\t\n\t \ncreated_at,\n \n\r\r -created_at, \t\n\n -created_at", status="active", user_id="user_id", ) @@ -373,6 +374,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncKeycardAPI) expand="total_count", limit=1, resource_id="resource_id", + sort="-created_at, -created_at,\r\r \t \n\r-created_at,\n\n \t-created_at,\n \r \r created_at,\n\t\t\n\t \ncreated_at,\n \n\r\r -created_at, \t\n\n -created_at", status="active", user_id="user_id", ) diff --git a/tests/api_resources/zones/test_providers.py b/tests/api_resources/zones/test_providers.py index 222ed03..8b7efbe 100644 --- a/tests/api_resources/zones/test_providers.py +++ b/tests/api_resources/zones/test_providers.py @@ -58,6 +58,7 @@ def test_method_create_with_all_params(self, client: KeycardAPI) -> None: "token_response_access_token_pointer": "token_response_access_token_pointer", }, "openid": { + "scopes": ["string"], "user_identifier_claim": "user_identifier_claim", "userinfo_endpoint": "https://example.com", }, @@ -195,6 +196,7 @@ def test_method_update_with_all_params(self, client: KeycardAPI) -> None: "token_response_access_token_pointer": "token_response_access_token_pointer", }, "openid": { + "scopes": ["string"], "user_identifier_claim": "user_identifier_claim", "userinfo_endpoint": "https://example.com", }, @@ -399,6 +401,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncKeycardAPI "token_response_access_token_pointer": "token_response_access_token_pointer", }, "openid": { + "scopes": ["string"], "user_identifier_claim": "user_identifier_claim", "userinfo_endpoint": "https://example.com", }, @@ -536,6 +539,7 @@ async def test_method_update_with_all_params(self, async_client: AsyncKeycardAPI "token_response_access_token_pointer": "token_response_access_token_pointer", }, "openid": { + "scopes": ["string"], "user_identifier_claim": "user_identifier_claim", "userinfo_endpoint": "https://example.com", },