# This file is auto-generated by oagen. Do not edit.

from __future__ import annotations

from typing import TYPE_CHECKING, Any, Literal

if TYPE_CHECKING:
    from .._client import AsyncWorkOSClient, WorkOSClient

from workos.common.models.pagination_order import PaginationOrder

from .._pagination import AsyncPage, SyncPage
from .._types import RequestOptions, enum_value
from .models import (
    Connection,
    ConnectionsConnectionType,
    Profile,
    SSOLogoutAuthorizeResponse,
    SSOProvider,
    SSOTokenResponse,
)


class SSO:
    """SSO API resources."""

    def __init__(self, client: WorkOSClient) -> None:
        self._client = client

    def list_connections(
        self,
        *,
        limit: int | None = None,
        before: str | None = None,
        after: str | None = None,
        order: PaginationOrder | str | None = "desc",
        connection_type: ConnectionsConnectionType | str | None = None,
        domain: str | None = None,
        organization_id: str | None = None,
        search: str | None = None,
        request_options: RequestOptions | None = None,
    ) -> SyncPage[Connection]:
        """List Connections

        Get a list of all of your existing connections matching the criteria specified.

        Args:
            limit: Upper limit on the number of objects to return, between `1` and `100`. Defaults to `10`.
            before: An object ID that defines your place in the list. When the ID is not present, you are at the end of the list.
            after: An object ID that defines your place in the list. When the ID is not present, you are at the end of the list.
            order: Order the results by the creation time. Defaults to `desc`.
            connection_type: Filter Connections by their type.
            domain: Filter Connections by their associated domain.
            organization_id: Filter Connections by their associated organization.
            search: Searchable text to match against Connection names.
            request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override.

        Returns:
            SyncPage[Connection]

        Raises:
            AuthorizationError: If the request is forbidden (403).
            UnprocessableEntityError: If the request data is unprocessable (422).
            AuthenticationError: If the API key is invalid (401).
            RateLimitExceededError: If rate limited (429).
            ServerError: If the server returns a 5xx error.
        """
        params = {
            k: v
            for k, v in {
                "limit": limit,
                "before": before,
                "after": after,
                "order": enum_value(order) if order is not None else None,
                "connection_type": enum_value(connection_type)
                if connection_type is not None
                else None,
                "domain": domain,
                "organization_id": organization_id,
                "search": search,
            }.items()
            if v is not None
        }
        return self._client.request_page(
            method="get",
            path=("connections",),
            model=Connection,
            params=params,
            request_options=request_options,
        )

    def get_connection(
        self,
        id: str,
        *,
        request_options: RequestOptions | None = None,
    ) -> Connection:
        """Get a Connection

        Get the details of an existing connection.

        Args:
            id: Unique identifier for the Connection.
            request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override.

        Returns:
            Connection

        Raises:
            AuthorizationError: If the request is forbidden (403).
            NotFoundError: If the resource is not found (404).
            AuthenticationError: If the API key is invalid (401).
            RateLimitExceededError: If rate limited (429).
            ServerError: If the server returns a 5xx error.
        """
        return self._client.request(
            method="get",
            path=("connections", str(id)),
            model=Connection,
            request_options=request_options,
        )

    def delete_connection(
        self,
        id: str,
        *,
        request_options: RequestOptions | None = None,
    ) -> None:
        """Delete a Connection

        Permanently deletes an existing connection. It cannot be undone.

        Args:
            id: Unique identifier for the Connection.
            request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override.

        Raises:
            AuthorizationError: If the request is forbidden (403).
            NotFoundError: If the resource is not found (404).
            AuthenticationError: If the API key is invalid (401).
            RateLimitExceededError: If rate limited (429).
            ServerError: If the server returns a 5xx error.
        """
        self._client.request(
            method="delete",
            path=("connections", str(id)),
            request_options=request_options,
        )

    def get_authorization_url(
        self,
        *,
        provider_scopes: list[str] | None = None,
        provider_query_params: dict[str, str] | None = None,
        client_id: str | None = None,
        domain: str | None = None,
        provider: SSOProvider | str | None = None,
        redirect_uri: str,
        state: str | None = None,
        connection: str | None = None,
        organization: str | None = None,
        domain_hint: str | None = None,
        login_hint: str | None = None,
        nonce: str | None = None,
        prompt: Literal["login"] | None = None,
        request_options: RequestOptions | None = None,
    ) -> str:
        """Initiate SSO

        Initiates the single sign-on flow.

        Args:
            provider_scopes: Additional scopes to request from the identity provider. Applicable when using OAuth or OpenID Connect connections.
            provider_query_params: Key/value pairs of query parameters to pass to the OAuth provider. Only applicable when using OAuth connections.
            client_id: The unique identifier of the WorkOS environment client. Defaults to the client's configured client_id.
            domain: (deprecated) Deprecated. Use `connection` or `organization` instead. Used to initiate SSO for a connection by domain. The domain must be associated with a connection in your WorkOS environment.
            provider: Used to initiate OAuth authentication with various providers.
            redirect_uri: Where to redirect the user after they complete the authentication process. You must use one of the redirect URIs configured via the [Redirects](https://dashboard.workos.com/redirects) page on the dashboard.
            state: An optional parameter that can be used to encode arbitrary information to help restore application state between redirects. If included, the redirect URI received from WorkOS will contain the exact `state` that was passed.
            connection: Used to initiate SSO for a connection. The value should be a WorkOS connection ID.
                You can persist the WorkOS connection ID with application user or team identifiers. WorkOS will use the connection indicated by the connection parameter to direct the user to the corresponding IdP for authentication.
            organization: Used to initiate SSO for an organization. The value should be a WorkOS organization ID.
                You can persist the WorkOS organization ID with application user or team identifiers. WorkOS will use the organization ID to determine the appropriate connection and the IdP to direct the user to for authentication.
            domain_hint: Can be used to pre-fill the domain field when initiating authentication with Microsoft OAuth or with a Google SAML connection type.
            login_hint: Can be used to pre-fill the username/email address field of the IdP sign-in page for the user, if you know their username ahead of time. Currently supported for OAuth, OpenID Connect, Okta, Entra ID, and custom SAML connections.
            nonce: A random string generated by the client that is used to mitigate replay attacks.
            prompt: If set to `login`, forces re-authentication at the identity provider. For supported SAML providers this sets `ForceAuthn="true"` in the SAML request; providers that don't support it are unaffected.
            request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override.

        Returns:
            str

        Raises:
            AuthenticationError: If the API key is invalid (401).
            RateLimitExceededError: If rate limited (429).
            ServerError: If the server returns a 5xx error.
        """
        params = {
            k: v
            for k, v in {
                "provider_scopes": ",".join(str(v) for v in provider_scopes)
                if provider_scopes is not None
                else None,
                "provider_query_params": provider_query_params,
                "client_id": client_id,
                "domain": domain,
                "provider": enum_value(provider) if provider is not None else None,
                "redirect_uri": redirect_uri,
                "state": state,
                "connection": connection,
                "organization": organization,
                "domain_hint": domain_hint,
                "login_hint": login_hint,
                "nonce": nonce,
                "prompt": prompt,
            }.items()
            if v is not None
        }
        params["response_type"] = "code"
        if "client_id" not in params and self._client.client_id is not None:
            params["client_id"] = self._client.client_id
        return self._client.build_url(("sso", "authorize"), params)

    def get_logout_url(
        self,
        *,
        token: str,
        request_options: RequestOptions | None = None,
    ) -> str:
        """Logout Redirect

        Logout allows to sign out a user from your application by triggering the identity provider sign out flow. This `GET` endpoint should be a redirection, since the identity provider user will be identified in the browser session.

        Before redirecting to this endpoint, you need to generate a short-lived logout token using the [Logout Authorize](https://workos.com/docs/reference/sso/logout/authorize) endpoint.

        Args:
            token: The logout token returned from the [Logout Authorize](https://workos.com/docs/reference/sso/logout/authorize) endpoint.
            request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override.

        Returns:
            str

        Raises:
            NotFoundError: If the resource is not found (404).
            AuthenticationError: If the API key is invalid (401).
            RateLimitExceededError: If rate limited (429).
            ServerError: If the server returns a 5xx error.
        """
        params = {
            k: v
            for k, v in {
                "token": token,
            }.items()
            if v is not None
        }
        return self._client.build_url(("sso", "logout"), params)

    def authorize_logout(
        self,
        *,
        profile_id: str,
        request_options: RequestOptions | None = None,
    ) -> SSOLogoutAuthorizeResponse:
        """Logout Authorize

        You should call this endpoint from your server to generate a logout token which is required for the [Logout Redirect](https://workos.com/docs/reference/sso/logout) endpoint.

        Args:
            profile_id: The unique ID of the profile to log out.
            request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override.

        Returns:
            SSOLogoutAuthorizeResponse

        Raises:
            BadRequestError: If the request is malformed (400).
            NotFoundError: If the resource is not found (404).
            AuthenticationError: If the API key is invalid (401).
            RateLimitExceededError: If rate limited (429).
            ServerError: If the server returns a 5xx error.
        """
        body: dict[str, Any] = {
            "profile_id": profile_id,
        }
        return self._client.request(
            method="post",
            path=("sso", "logout", "authorize"),
            body=body,
            model=SSOLogoutAuthorizeResponse,
            request_options=request_options,
        )

    def get_profile(
        self,
        *,
        access_token: str,
        request_options: RequestOptions | None = None,
    ) -> Profile:
        """Get a User Profile

        Exchange an access token for a user's [Profile](https://workos.com/docs/reference/sso/profile). Because this profile is returned in the [Get a Profile and Token endpoint](https://workos.com/docs/reference/sso/profile/get-profile-and-token) your application usually does not need to call this endpoint. It is available for any authentication flows that require an additional endpoint to retrieve a user's profile.

        Args:
            access_token: The bearer token for authentication.
            request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override.

        Returns:
            Profile

        Raises:
            AuthenticationError: If the API key is invalid (401).
            NotFoundError: If the resource is not found (404).
            RateLimitExceededError: If rate limited (429).
            ServerError: If the server returns a 5xx error.
        """
        request_options = request_options or {}
        request_options = {
            **request_options,
            "extra_headers": {
                **(request_options.get("extra_headers") or {}),
                "Authorization": f"Bearer {access_token}",
            },
        }
        return self._client.request(
            method="get",
            path=("sso", "profile"),
            model=Profile,
            request_options=request_options,
        )

    def get_profile_and_token(
        self,
        *,
        code: str,
        request_options: RequestOptions | None = None,
    ) -> SSOTokenResponse:
        """Get a Profile and Token

        Get an access token along with the user [Profile](https://workos.com/docs/reference/sso/profile) using the code passed to your [Redirect URI](https://workos.com/docs/reference/sso/get-authorization-url/redirect-uri).

        Args:
            code: The authorization code received from the authorization callback.
            request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override.

        Returns:
            SSOTokenResponse

        Raises:
            BadRequestError: If the request is malformed (400).
            NotFoundError: If the resource is not found (404).
            UnprocessableEntityError: If the request data is unprocessable (422).
            AuthenticationError: If the API key is invalid (401).
            RateLimitExceededError: If rate limited (429).
            ServerError: If the server returns a 5xx error.
        """
        body: dict[str, Any] = {
            "code": code,
        }
        body["grant_type"] = "authorization_code"
        if self._client.client_id is not None:
            body["client_id"] = self._client.client_id
        if self._client._api_key is not None:
            body["client_secret"] = self._client._api_key
        return self._client.request(
            method="post",
            path=("sso", "token"),
            body=body,
            model=SSOTokenResponse,
            request_options=request_options,
        )

    # @oagen-ignore-start

    def get_authorization_url_with_pkce(
        self,
        *,
        redirect_uri: str,
        client_id: str | None = None,
        connection: str | None = None,
        organization: str | None = None,
        provider: SSOProvider | str | None = None,
        domain_hint: str | None = None,
        login_hint: str | None = None,
        provider_scopes: list[str] | None = None,
        provider_query_params: dict[str, str] | None = None,
    ) -> dict[str, str]:
        """Generate an SSO authorization URL with auto-generated PKCE parameters."""
        from ..pkce import PKCE

        pkce = PKCE()
        pair = pkce.generate()
        state = pkce.generate_code_verifier(43)
        resolved_client_id = client_id or self._client._require_client_id()

        params = {
            k: v
            for k, v in {
                "client_id": resolved_client_id,
                "redirect_uri": redirect_uri,
                "response_type": "code",
                "code_challenge": pair.code_challenge,
                "code_challenge_method": "S256",
                "state": state,
                "connection": connection,
                "organization": organization,
                "provider": enum_value(provider) if provider is not None else None,
                "domain_hint": domain_hint,
                "login_hint": login_hint,
                "provider_scopes": provider_scopes,
                "provider_query_params": provider_query_params,
            }.items()
            if v is not None
        }
        url = self._client.build_url(("sso", "authorize"), params)
        return {"url": url, "state": state, "code_verifier": pair.code_verifier}

    def get_profile_and_token_pkce(
        self,
        *,
        code: str,
        code_verifier: str,
        client_id: str | None = None,
        request_options: RequestOptions | None = None,
    ) -> SSOTokenResponse:
        """Exchange an authorization code using a PKCE code_verifier for SSO."""
        resolved_client_id = client_id or self._client._require_client_id()
        body: dict[str, Any] = {
            "client_id": resolved_client_id,
            "code": code,
            "grant_type": "authorization_code",
            "code_verifier": code_verifier,
        }
        if self._client._api_key:
            body["client_secret"] = self._client._api_key

        return self._client.request(
            method="post",
            path=("sso", "token"),
            body=body,
            model=SSOTokenResponse,
            request_options=request_options,
        )

    # @oagen-ignore-end


class AsyncSSO:
    """SSO API resources (async)."""

    def __init__(self, client: AsyncWorkOSClient) -> None:
        self._client = client

    async def list_connections(
        self,
        *,
        limit: int | None = None,
        before: str | None = None,
        after: str | None = None,
        order: PaginationOrder | str | None = "desc",
        connection_type: ConnectionsConnectionType | str | None = None,
        domain: str | None = None,
        organization_id: str | None = None,
        search: str | None = None,
        request_options: RequestOptions | None = None,
    ) -> AsyncPage[Connection]:
        """List Connections

        Get a list of all of your existing connections matching the criteria specified.

        Args:
            limit: Upper limit on the number of objects to return, between `1` and `100`. Defaults to `10`.
            before: An object ID that defines your place in the list. When the ID is not present, you are at the end of the list.
            after: An object ID that defines your place in the list. When the ID is not present, you are at the end of the list.
            order: Order the results by the creation time. Defaults to `desc`.
            connection_type: Filter Connections by their type.
            domain: Filter Connections by their associated domain.
            organization_id: Filter Connections by their associated organization.
            search: Searchable text to match against Connection names.
            request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override.

        Returns:
            AsyncPage[Connection]

        Raises:
            AuthorizationError: If the request is forbidden (403).
            UnprocessableEntityError: If the request data is unprocessable (422).
            AuthenticationError: If the API key is invalid (401).
            RateLimitExceededError: If rate limited (429).
            ServerError: If the server returns a 5xx error.
        """
        params = {
            k: v
            for k, v in {
                "limit": limit,
                "before": before,
                "after": after,
                "order": enum_value(order) if order is not None else None,
                "connection_type": enum_value(connection_type)
                if connection_type is not None
                else None,
                "domain": domain,
                "organization_id": organization_id,
                "search": search,
            }.items()
            if v is not None
        }
        return await self._client.request_page(
            method="get",
            path=("connections",),
            model=Connection,
            params=params,
            request_options=request_options,
        )

    async def get_connection(
        self,
        id: str,
        *,
        request_options: RequestOptions | None = None,
    ) -> Connection:
        """Get a Connection

        Get the details of an existing connection.

        Args:
            id: Unique identifier for the Connection.
            request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override.

        Returns:
            Connection

        Raises:
            AuthorizationError: If the request is forbidden (403).
            NotFoundError: If the resource is not found (404).
            AuthenticationError: If the API key is invalid (401).
            RateLimitExceededError: If rate limited (429).
            ServerError: If the server returns a 5xx error.
        """
        return await self._client.request(
            method="get",
            path=("connections", str(id)),
            model=Connection,
            request_options=request_options,
        )

    async def delete_connection(
        self,
        id: str,
        *,
        request_options: RequestOptions | None = None,
    ) -> None:
        """Delete a Connection

        Permanently deletes an existing connection. It cannot be undone.

        Args:
            id: Unique identifier for the Connection.
            request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override.

        Raises:
            AuthorizationError: If the request is forbidden (403).
            NotFoundError: If the resource is not found (404).
            AuthenticationError: If the API key is invalid (401).
            RateLimitExceededError: If rate limited (429).
            ServerError: If the server returns a 5xx error.
        """
        await self._client.request(
            method="delete",
            path=("connections", str(id)),
            request_options=request_options,
        )

    def get_authorization_url(
        self,
        *,
        provider_scopes: list[str] | None = None,
        provider_query_params: dict[str, str] | None = None,
        client_id: str | None = None,
        domain: str | None = None,
        provider: SSOProvider | str | None = None,
        redirect_uri: str,
        state: str | None = None,
        connection: str | None = None,
        organization: str | None = None,
        domain_hint: str | None = None,
        login_hint: str | None = None,
        nonce: str | None = None,
        prompt: Literal["login"] | None = None,
        request_options: RequestOptions | None = None,
    ) -> str:
        """Initiate SSO

        Initiates the single sign-on flow.

        Args:
            provider_scopes: Additional scopes to request from the identity provider. Applicable when using OAuth or OpenID Connect connections.
            provider_query_params: Key/value pairs of query parameters to pass to the OAuth provider. Only applicable when using OAuth connections.
            client_id: The unique identifier of the WorkOS environment client. Defaults to the client's configured client_id.
            domain: (deprecated) Deprecated. Use `connection` or `organization` instead. Used to initiate SSO for a connection by domain. The domain must be associated with a connection in your WorkOS environment.
            provider: Used to initiate OAuth authentication with various providers.
            redirect_uri: Where to redirect the user after they complete the authentication process. You must use one of the redirect URIs configured via the [Redirects](https://dashboard.workos.com/redirects) page on the dashboard.
            state: An optional parameter that can be used to encode arbitrary information to help restore application state between redirects. If included, the redirect URI received from WorkOS will contain the exact `state` that was passed.
            connection: Used to initiate SSO for a connection. The value should be a WorkOS connection ID.
                You can persist the WorkOS connection ID with application user or team identifiers. WorkOS will use the connection indicated by the connection parameter to direct the user to the corresponding IdP for authentication.
            organization: Used to initiate SSO for an organization. The value should be a WorkOS organization ID.
                You can persist the WorkOS organization ID with application user or team identifiers. WorkOS will use the organization ID to determine the appropriate connection and the IdP to direct the user to for authentication.
            domain_hint: Can be used to pre-fill the domain field when initiating authentication with Microsoft OAuth or with a Google SAML connection type.
            login_hint: Can be used to pre-fill the username/email address field of the IdP sign-in page for the user, if you know their username ahead of time. Currently supported for OAuth, OpenID Connect, Okta, Entra ID, and custom SAML connections.
            nonce: A random string generated by the client that is used to mitigate replay attacks.
            prompt: If set to `login`, forces re-authentication at the identity provider. For supported SAML providers this sets `ForceAuthn="true"` in the SAML request; providers that don't support it are unaffected.
            request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override.

        Returns:
            str

        Raises:
            AuthenticationError: If the API key is invalid (401).
            RateLimitExceededError: If rate limited (429).
            ServerError: If the server returns a 5xx error.
        """
        params = {
            k: v
            for k, v in {
                "provider_scopes": ",".join(str(v) for v in provider_scopes)
                if provider_scopes is not None
                else None,
                "provider_query_params": provider_query_params,
                "client_id": client_id,
                "domain": domain,
                "provider": enum_value(provider) if provider is not None else None,
                "redirect_uri": redirect_uri,
                "state": state,
                "connection": connection,
                "organization": organization,
                "domain_hint": domain_hint,
                "login_hint": login_hint,
                "nonce": nonce,
                "prompt": prompt,
            }.items()
            if v is not None
        }
        params["response_type"] = "code"
        if "client_id" not in params and self._client.client_id is not None:
            params["client_id"] = self._client.client_id
        return self._client.build_url(("sso", "authorize"), params)

    def get_logout_url(
        self,
        *,
        token: str,
        request_options: RequestOptions | None = None,
    ) -> str:
        """Logout Redirect

        Logout allows to sign out a user from your application by triggering the identity provider sign out flow. This `GET` endpoint should be a redirection, since the identity provider user will be identified in the browser session.

        Before redirecting to this endpoint, you need to generate a short-lived logout token using the [Logout Authorize](https://workos.com/docs/reference/sso/logout/authorize) endpoint.

        Args:
            token: The logout token returned from the [Logout Authorize](https://workos.com/docs/reference/sso/logout/authorize) endpoint.
            request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override.

        Returns:
            str

        Raises:
            NotFoundError: If the resource is not found (404).
            AuthenticationError: If the API key is invalid (401).
            RateLimitExceededError: If rate limited (429).
            ServerError: If the server returns a 5xx error.
        """
        params = {
            k: v
            for k, v in {
                "token": token,
            }.items()
            if v is not None
        }
        return self._client.build_url(("sso", "logout"), params)

    async def authorize_logout(
        self,
        *,
        profile_id: str,
        request_options: RequestOptions | None = None,
    ) -> SSOLogoutAuthorizeResponse:
        """Logout Authorize

        You should call this endpoint from your server to generate a logout token which is required for the [Logout Redirect](https://workos.com/docs/reference/sso/logout) endpoint.

        Args:
            profile_id: The unique ID of the profile to log out.
            request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override.

        Returns:
            SSOLogoutAuthorizeResponse

        Raises:
            BadRequestError: If the request is malformed (400).
            NotFoundError: If the resource is not found (404).
            AuthenticationError: If the API key is invalid (401).
            RateLimitExceededError: If rate limited (429).
            ServerError: If the server returns a 5xx error.
        """
        body: dict[str, Any] = {
            "profile_id": profile_id,
        }
        return await self._client.request(
            method="post",
            path=("sso", "logout", "authorize"),
            body=body,
            model=SSOLogoutAuthorizeResponse,
            request_options=request_options,
        )

    async def get_profile(
        self,
        *,
        access_token: str,
        request_options: RequestOptions | None = None,
    ) -> Profile:
        """Get a User Profile

        Exchange an access token for a user's [Profile](https://workos.com/docs/reference/sso/profile). Because this profile is returned in the [Get a Profile and Token endpoint](https://workos.com/docs/reference/sso/profile/get-profile-and-token) your application usually does not need to call this endpoint. It is available for any authentication flows that require an additional endpoint to retrieve a user's profile.

        Args:
            access_token: The bearer token for authentication.
            request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override.

        Returns:
            Profile

        Raises:
            AuthenticationError: If the API key is invalid (401).
            NotFoundError: If the resource is not found (404).
            RateLimitExceededError: If rate limited (429).
            ServerError: If the server returns a 5xx error.
        """
        request_options = request_options or {}
        request_options = {
            **request_options,
            "extra_headers": {
                **(request_options.get("extra_headers") or {}),
                "Authorization": f"Bearer {access_token}",
            },
        }
        return await self._client.request(
            method="get",
            path=("sso", "profile"),
            model=Profile,
            request_options=request_options,
        )

    async def get_profile_and_token(
        self,
        *,
        code: str,
        request_options: RequestOptions | None = None,
    ) -> SSOTokenResponse:
        """Get a Profile and Token

        Get an access token along with the user [Profile](https://workos.com/docs/reference/sso/profile) using the code passed to your [Redirect URI](https://workos.com/docs/reference/sso/get-authorization-url/redirect-uri).

        Args:
            code: The authorization code received from the authorization callback.
            request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override.

        Returns:
            SSOTokenResponse

        Raises:
            BadRequestError: If the request is malformed (400).
            NotFoundError: If the resource is not found (404).
            UnprocessableEntityError: If the request data is unprocessable (422).
            AuthenticationError: If the API key is invalid (401).
            RateLimitExceededError: If rate limited (429).
            ServerError: If the server returns a 5xx error.
        """
        body: dict[str, Any] = {
            "code": code,
        }
        body["grant_type"] = "authorization_code"
        if self._client.client_id is not None:
            body["client_id"] = self._client.client_id
        if self._client._api_key is not None:
            body["client_secret"] = self._client._api_key
        return await self._client.request(
            method="post",
            path=("sso", "token"),
            body=body,
            model=SSOTokenResponse,
            request_options=request_options,
        )

    # @oagen-ignore-start

    async def get_authorization_url_with_pkce(
        self,
        *,
        redirect_uri: str,
        client_id: str | None = None,
        connection: str | None = None,
        organization: str | None = None,
        provider: SSOProvider | str | None = None,
        domain_hint: str | None = None,
        login_hint: str | None = None,
        provider_scopes: list[str] | None = None,
        provider_query_params: dict[str, str] | None = None,
    ) -> dict[str, str]:
        """Generate an SSO authorization URL with auto-generated PKCE parameters."""
        from ..pkce import PKCE

        pkce = PKCE()
        pair = pkce.generate()
        state = pkce.generate_code_verifier(43)
        resolved_client_id = client_id or self._client._require_client_id()

        params = {
            k: v
            for k, v in {
                "client_id": resolved_client_id,
                "redirect_uri": redirect_uri,
                "response_type": "code",
                "code_challenge": pair.code_challenge,
                "code_challenge_method": "S256",
                "state": state,
                "connection": connection,
                "organization": organization,
                "provider": enum_value(provider) if provider is not None else None,
                "domain_hint": domain_hint,
                "login_hint": login_hint,
                "provider_scopes": provider_scopes,
                "provider_query_params": provider_query_params,
            }.items()
            if v is not None
        }
        url = self._client.build_url(("sso", "authorize"), params)
        return {"url": url, "state": state, "code_verifier": pair.code_verifier}

    async def get_profile_and_token_pkce(
        self,
        *,
        code: str,
        code_verifier: str,
        client_id: str | None = None,
        request_options: RequestOptions | None = None,
    ) -> SSOTokenResponse:
        """Exchange an authorization code using a PKCE code_verifier for SSO."""
        resolved_client_id = client_id or self._client._require_client_id()
        body: dict[str, Any] = {
            "client_id": resolved_client_id,
            "code": code,
            "grant_type": "authorization_code",
            "code_verifier": code_verifier,
        }
        if self._client._api_key:
            body["client_secret"] = self._client._api_key

        return await self._client.request(
            method="post",
            path=("sso", "token"),
            body=body,
            model=SSOTokenResponse,
            request_options=request_options,
        )

    # @oagen-ignore-end
