diff --git a/src/content/docs/developer-tools/sdks/backend/python-sdk.mdx b/src/content/docs/developer-tools/sdks/backend/python-sdk.mdx index 1c0ac5fc7..9f85ca9f3 100644 --- a/src/content/docs/developer-tools/sdks/backend/python-sdk.mdx +++ b/src/content/docs/developer-tools/sdks/backend/python-sdk.mdx @@ -1386,239 +1386,230 @@ You don't need to manually manage tokens or sessions - the SDK handles this auto ## Management API -The Kinde Python SDK provides a Management API client for interacting with Kinde's management endpoints. This allows you to programmatically manage users, organizations, and other resources. The Management API supports both sync and async patterns. +The Kinde Python SDK includes a separate Management API client for interacting with Kinde's management endpoints. This allows you to programmatically manage users, organizations, roles, permissions, feature flags, and other resources. -### Getting started +The Management API client is independent of the OAuth authentication clients (`OAuth`, `AsyncOAuth`, `SmartOAuth`). You initialize it directly with your management API credentials. -#### With OAuth client (Framework-based) +### Getting started ```python -from kinde_sdk.auth.oauth import OAuth -from flask import Flask -import asyncio +from kinde_sdk.management.management_client import ManagementClient -app = Flask(__name__) -oauth = OAuth(framework="flask", app=app) +management = ManagementClient( + domain="your-domain.kinde.com", + client_id="your-management-client-id", + client_secret="your-management-client-secret" +) +``` -# Get the management client -management = oauth.get_management() +You can also load credentials from a `.env` file. This requires the `python-dotenv` package: -# Use with asyncio in Flask -def list_users_sync(): - loop = asyncio.get_event_loop() - users = loop.run_until_complete(management.get_users()) - return users +```bash +pip install python-dotenv ``` -#### With AsyncOAuth client (Native async) +Call `load_dotenv()` before initializing `ManagementClient` so the environment variables are available: ```python -from kinde_sdk.auth.async_oauth import AsyncOAuth - -oauth = AsyncOAuth() +import os +from dotenv import load_dotenv +from kinde_sdk.management.management_client import ManagementClient -# Get the management client (native async) -management = await oauth.get_management() +load_dotenv() -# All methods are async -users = await management.get_users() +management = ManagementClient( + domain=os.getenv("KINDE_HOST", "").replace("https://", ""), + client_id=os.getenv("MGMT_API_CLIENT_ID"), + client_secret=os.getenv("MGMT_API_CLIENT_SECRET") +) ``` -#### With SmartOAuth client (Context-aware) +### Available endpoints + +The `ManagementClient` dynamically discovers all auto-generated API classes and exposes them as snake_case properties. Access endpoints through these API class properties: + +**User management:** ```python -from kinde_sdk.auth.smart_oauth import SmartOAuth +# List users +users = management.users_api.get_users(page_size=10) + +# Get a specific user +user = management.users_api.get_user_data(id="user_123") + +# Create a new user +new_user = management.users_api.create_user( + create_user_request={ + "profile": { + "given_name": "John", + "family_name": "Doe" + }, + "identities": [{ + "type": "email", + "details": {"email": "user@example.com"} + }] + } +) -oauth = SmartOAuth() +# Update a user +management.users_api.update_user( + id="user_123", + update_user_request={"given_name": "Johnny"} +) -# Works in async context -async def async_get_users(): - management = await oauth.get_management() - return await management.get_users() - -# Works in sync context (if supported) -def sync_get_users(): - management = oauth.get_management() - return management.get_users() +# Delete a user +management.users_api.delete_user(id="user_123") ``` -### Available endpoints +**Other common API classes:** -The Management API provides methods for common operations on resources. All examples use async patterns: +```python +management.organizations_api # Organization management +management.roles_api # Role management +management.permissions_api # Permission management +management.feature_flags_api # Feature flag management +management.applications_api # Application management +management.subscribers_api # Subscriber management +management.connections_api # Connection management +management.webhooks_api # Webhook management +``` -**User management:** +When new API classes are added to the generated module, they are automatically available on the client without any code changes. + +### Organization management ```python -# List users (async) -users = await management.get_users() +from kinde_sdk.management.management_client import ManagementClient -# Get a specific user (async) -user = await management.get_user(user_id="user_123") +management = ManagementClient( + domain="your-domain.kinde.com", + client_id="your-management-client-id", + client_secret="your-management-client-secret" +) + +# List organizations +orgs = management.organizations_api.get_organizations(page_size=10) + +# Get a specific organization +org = management.organizations_api.get_organization(code="org_1234") -# Create a new user (async) -new_user = await management.create_user( - email="user@example.com", - given_name="John", - family_name="Doe" +# Create a new organization +new_org = management.organizations_api.create_organization( + create_organization_request={"name": "My Organization"} ) -# Update a user (async) -updated_user = await management.update_user( - user_id="user_123", - given_name="Johnny" +# Update an organization +management.organizations_api.update_organization( + org_code="org_1234", + update_organization_request={"name": "Updated Name"} ) -# Delete a user (async) -await management.delete_user(user_id="user_123") +# Delete an organization +management.organizations_api.delete_organization(org_code="org_1234") ``` -### Organization management - -**Using Management API with FastAPI (OAuth client):** +**Using the Management API in a FastAPI application:** ```python from fastapi import FastAPI, HTTPException -from kinde_sdk.auth.oauth import OAuth +from kinde_sdk.management.management_client import ManagementClient app = FastAPI() -oauth = OAuth(framework="fastapi", app=app) +management = ManagementClient( + domain="your-domain.kinde.com", + client_id="your-management-client-id", + client_secret="your-management-client-secret" +) @app.get("/organizations") -async def list_organizations(): - management = oauth.get_management() - orgs = await management.get_organizations() +def list_organizations(): + orgs = management.organizations_api.get_organizations() return orgs -@app.get("/organizations/{org_id}") -async def get_organization(org_id: str): - management = oauth.get_management() - org = await management.get_organization(org_id=org_id) +@app.get("/organizations/{org_code}") +def get_organization(org_code: str): + org = management.organizations_api.get_organization(code=org_code) return org @app.post("/organizations") -async def create_organization(name: str): - management = oauth.get_management() - new_org = await management.create_organization(name=name) - return new_org - -@app.put("/organizations/{org_id}") -async def update_organization(org_id: str, name: str): - management = oauth.get_management() - updated_org = await management.update_organization( - org_id=org_id, - name=name +def create_organization(name: str): + new_org = management.organizations_api.create_organization( + create_organization_request={"name": name} ) - return updated_org + return new_org -@app.delete("/organizations/{org_id}") -async def delete_organization(org_id: str): - management = oauth.get_management() - await management.delete_organization(org_id=org_id) +@app.delete("/organizations/{org_code}") +def delete_organization(org_code: str): + management.organizations_api.delete_organization(org_code=org_code) return {"message": "Organization deleted"} ``` -**Using Management API with AsyncOAuth client:** +### Error handling + +The Management API methods raise exceptions for HTTP errors. Wrap calls in try/except blocks: ```python -from kinde_sdk.auth.async_oauth import AsyncOAuth +from kinde_sdk.management.management_client import ManagementClient -oauth = AsyncOAuth() +management = ManagementClient( + domain="your-domain.kinde.com", + client_id="your-management-client-id", + client_secret="your-management-client-secret" +) -async def manage_organizations(): - # Get management client - management = await oauth.get_management() - - # List organizations - orgs = await management.get_organizations() - - # Get a specific organization - org = await management.get_organization(org_id="org_123") - - # Create a new organization - new_org = await management.create_organization( - name="My Organization" - ) - - # Update an organization - updated_org = await management.update_organization( - org_id="org_123", - name="Updated Name" - ) - - # Delete an organization - await management.delete_organization(org_id="org_123") - - return orgs +try: + user = management.users_api.get_user_data(id="user_123") +except Exception as e: + print(f"Error: {e}") ``` -### Error handling - -The Management API methods will raise exceptions for API errors. It's recommended to handle these appropriately: - -**Example with OAuth client (FastAPI):** +**In a FastAPI application:** ```python +import logging from fastapi import FastAPI, HTTPException -from kinde_sdk.auth.oauth import OAuth -from kinde_sdk.exceptions import KindeAPIException +from kinde_sdk.management.management_client import ManagementClient +from kinde_sdk.management.exceptions import ApiException + +logger = logging.getLogger(__name__) app = FastAPI() -oauth = OAuth(framework="fastapi", app=app) +management = ManagementClient( + domain="your-domain.kinde.com", + client_id="your-management-client-id", + client_secret="your-management-client-secret" +) @app.get("/users/{user_id}") -async def get_user(user_id: str): - management = oauth.get_management() - try: - user = await management.get_user(user_id=user_id) - return user - except KindeAPIException as e: - raise HTTPException(status_code=e.status_code, detail=str(e)) - except Exception as e: - raise HTTPException(status_code=500, detail=f"Internal error: {str(e)}") -``` - -**Example with AsyncOAuth client:** - -```python -from kinde_sdk.auth.async_oauth import AsyncOAuth -from kinde_sdk.exceptions import KindeAPIException - -oauth = AsyncOAuth() - -async def get_user_safely(user_id: str): - management = await oauth.get_management() +def get_user(user_id: str): try: - user = await management.get_user(user_id=user_id) - return user - except KindeAPIException as e: - print(f"API Error {e.status_code}: {e.message}") - return None + return management.users_api.get_user_data(id=user_id) + except ApiException as e: + logger.error("Kinde API error: %s %s", e.status, e.body) + raise HTTPException(status_code=e.status, detail="Failed to fetch user") except Exception as e: - print(f"Unexpected error: {str(e)}") - return None + logger.exception("Unexpected error fetching user %s", user_id) + raise HTTPException(status_code=500, detail="Internal server error") ``` ### Token management -The Management API client has its own token management system for API authentication, which is separate from the core SDK's user session token management. The Management API client automatically handles: +The `ManagementClient` has its own token management system that is separate from the OAuth authentication clients. It uses the OAuth2 client credentials flow and automatically handles: -- **accessing Kinde Management API endpoints**: Obtains tokens for accessing Kinde's management endpoints -- **Token refresh**: Automatically refreshes management API tokens when they expire -- **Token storage**: Securely stores management API tokens -- **Thread safety**: Ensures thread-safe token handling for concurrent requests +- **Token acquisition**: Obtains tokens using a client credentials grant on the first API call +- **Token caching**: Tokens are cached and reused to avoid unnecessary requests +- **Automatic re-acquisition**: Requests new tokens via a client credentials grant when existing ones near expiry (60 seconds before expiration) +- **Shared tokens**: Multiple `ManagementClient` instances with the same domain and client ID share the same token -You don't need to manually manage Management API tokens - the client handles this for you. This is different from the core SDK's user session token management, which handles user authentication tokens automatically. +You don't need to manually manage tokens -- the client handles this for you. Note that credentials are validated on the first API call, not during client initialization. If your credentials are incorrect, the error will surface when you make your first request. ### Best practices -1. **Always use async/await when calling Management API methods**: The Management API is async-native for better performance -2. **Handle API errors appropriately**: Use try/except blocks and handle `KindeAPIException` specifically +1. **Use the API class properties**: Always use `management.users_api`, `management.organizations_api`, etc. The top-level convenience methods (e.g. `management.get_users()`) are deprecated +2. **Handle API errors appropriately**: Use try/except blocks for all Management API calls 3. **Cache results when appropriate**: Reduce API calls by caching user data, organizations, and permissions -4. **Use appropriate error handling for production**: Implement logging, monitoring, and graceful error recovery -5. **Keep your client credentials secure**: Use environment variables, never commit secrets to version control -6. **Use connection pooling**: For high-traffic applications, configure HTTP connection pooling -7. **Implement retry logic**: Add retry logic with exponential backoff for transient failures -8. **Monitor token expiration**: Handle token refresh gracefully to avoid authentication failures +4. **Keep your client credentials secure**: Use environment variables, never commit secrets to version control +5. **Implement retry logic**: Add retry logic with exponential backoff for transient failures For more information about the Management API endpoints and capabilities, see the [Kinde Management API documentation](https://docs.kinde.com/kinde-apis/management/).