scalekit-sdk-python 2.16.0__tar.gz → 2.17.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (276) hide show
  1. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/PKG-INFO +91 -2
  2. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/README.md +82 -0
  3. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/_version.py +1 -1
  4. scalekit_sdk_python-2.17.0/scalekit/frameworks/django.py +369 -0
  5. scalekit_sdk_python-2.17.0/scalekit/frameworks/fastapi.py +386 -0
  6. scalekit_sdk_python-2.17.0/scalekit/frameworks/flask.py +368 -0
  7. scalekit_sdk_python-2.17.0/scalekit/middleware/csrf_state.py +57 -0
  8. scalekit_sdk_python-2.17.0/scalekit/middleware/protocol.py +56 -0
  9. scalekit_sdk_python-2.17.0/scalekit/middleware/session_crypto.py +126 -0
  10. scalekit_sdk_python-2.17.0/scalekit/middleware/session_manager.py +242 -0
  11. scalekit_sdk_python-2.17.0/scalekit/v1/webhooks/__init__.py +0 -0
  12. scalekit_sdk_python-2.17.0/scalekit/v1/workspaces/__init__.py +0 -0
  13. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit_sdk_python.egg-info/PKG-INFO +91 -2
  14. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit_sdk_python.egg-info/SOURCES.txt +15 -0
  15. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit_sdk_python.egg-info/requires.txt +10 -1
  16. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/setup.py +9 -1
  17. scalekit_sdk_python-2.17.0/tests/test_middleware_csrf_state.py +47 -0
  18. scalekit_sdk_python-2.17.0/tests/test_middleware_django.py +428 -0
  19. scalekit_sdk_python-2.17.0/tests/test_middleware_fastapi.py +434 -0
  20. scalekit_sdk_python-2.17.0/tests/test_middleware_flask.py +557 -0
  21. scalekit_sdk_python-2.17.0/tests/test_middleware_session_crypto.py +86 -0
  22. scalekit_sdk_python-2.17.0/tests/test_middleware_session_manager.py +303 -0
  23. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/LICENSE +0 -0
  24. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/buf/__init__.py +0 -0
  25. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/buf/validate/__init__.py +0 -0
  26. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/buf/validate/validate_pb2.py +0 -0
  27. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/buf/validate/validate_pb2.pyi +0 -0
  28. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/buf/validate/validate_pb2_grpc.py +0 -0
  29. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/pyproject.toml +0 -0
  30. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/__init__.py +0 -0
  31. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/actions/__init__.py +0 -0
  32. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/actions/actions.py +0 -0
  33. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/actions/frameworks/__init__.py +0 -0
  34. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/actions/frameworks/google_adk.py +0 -0
  35. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/actions/frameworks/langchain.py +0 -0
  36. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/actions/frameworks/types/__init__.py +0 -0
  37. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/actions/frameworks/types/google_adk_tool.py +0 -0
  38. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/actions/frameworks/util.py +0 -0
  39. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/actions/models/__init__.py +0 -0
  40. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/actions/models/custom_provider.py +0 -0
  41. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/actions/models/mcp_config.py +0 -0
  42. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/actions/models/mcp_connection_auth_state.py +0 -0
  43. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/actions/models/mcp_instance.py +0 -0
  44. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/actions/models/requests/__init__.py +0 -0
  45. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/actions/models/requests/create_connected_account_request.py +0 -0
  46. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/actions/models/requests/create_custom_provider_request.py +0 -0
  47. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/actions/models/requests/delete_custom_provider_request.py +0 -0
  48. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/actions/models/requests/list_providers_request.py +0 -0
  49. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/actions/models/requests/mcp_request.py +0 -0
  50. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/actions/models/requests/tool_request.py +0 -0
  51. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/actions/models/requests/update_connected_account_request.py +0 -0
  52. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/actions/models/requests/update_custom_provider_request.py +0 -0
  53. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/actions/models/responses/__init__.py +0 -0
  54. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/actions/models/responses/create_connected_account_response.py +0 -0
  55. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/actions/models/responses/create_custom_provider_response.py +0 -0
  56. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/actions/models/responses/create_mcp_config_response.py +0 -0
  57. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/actions/models/responses/create_mcp_response.py +0 -0
  58. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/actions/models/responses/create_mcp_session_token_response.py +0 -0
  59. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/actions/models/responses/delete_connected_account_response.py +0 -0
  60. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/actions/models/responses/delete_custom_provider_response.py +0 -0
  61. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/actions/models/responses/delete_mcp_config_response.py +0 -0
  62. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/actions/models/responses/delete_mcp_instance_response.py +0 -0
  63. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/actions/models/responses/ensure_mcp_instance_response.py +0 -0
  64. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/actions/models/responses/execute_tool_response.py +0 -0
  65. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/actions/models/responses/get_connected_account_auth_response.py +0 -0
  66. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/actions/models/responses/get_mcp_instance_auth_state_response.py +0 -0
  67. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/actions/models/responses/get_mcp_instance_response.py +0 -0
  68. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/actions/models/responses/get_mcp_response.py +0 -0
  69. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/actions/models/responses/list_connected_accounts_response.py +0 -0
  70. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/actions/models/responses/list_mcp_configs_response.py +0 -0
  71. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/actions/models/responses/list_mcp_connected_accounts_response.py +0 -0
  72. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/actions/models/responses/list_mcp_instances_response.py +0 -0
  73. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/actions/models/responses/list_providers_response.py +0 -0
  74. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/actions/models/responses/magic_link_response.py +0 -0
  75. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/actions/models/responses/update_connected_account_response.py +0 -0
  76. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/actions/models/responses/update_custom_provider_response.py +0 -0
  77. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/actions/models/responses/update_mcp_config_response.py +0 -0
  78. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/actions/models/responses/update_mcp_instance_response.py +0 -0
  79. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/actions/models/responses/verify_connected_account_user_response.py +0 -0
  80. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/actions/models/tool_input_output.py +0 -0
  81. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/actions/models/tool_mapping.py +0 -0
  82. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/actions/modifier.py +0 -0
  83. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/actions/types.py +0 -0
  84. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/auth.py +0 -0
  85. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/client.py +0 -0
  86. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/common/__init__.py +0 -0
  87. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/common/exceptions.py +0 -0
  88. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/common/scalekit.py +0 -0
  89. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/common/user.py +0 -0
  90. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/connected_accounts.py +0 -0
  91. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/connection.py +0 -0
  92. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/constants/__init__.py +0 -0
  93. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/constants/user.py +0 -0
  94. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/core.py +0 -0
  95. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/directory.py +0 -0
  96. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/domain.py +0 -0
  97. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/events.py +0 -0
  98. {scalekit_sdk_python-2.16.0/scalekit/utils → scalekit_sdk_python-2.17.0/scalekit/frameworks}/__init__.py +0 -0
  99. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/m2m_client.py +0 -0
  100. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/mcp.py +0 -0
  101. {scalekit_sdk_python-2.16.0/scalekit/v1 → scalekit_sdk_python-2.17.0/scalekit/middleware}/__init__.py +0 -0
  102. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/organization.py +0 -0
  103. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/passwordless.py +0 -0
  104. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/permissions.py +0 -0
  105. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/providers.py +0 -0
  106. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/role.py +0 -0
  107. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/sessions.py +0 -0
  108. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/token.py +0 -0
  109. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/tools.py +0 -0
  110. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/users.py +0 -0
  111. {scalekit_sdk_python-2.16.0/scalekit/v1/agentkit_logs → scalekit_sdk_python-2.17.0/scalekit/utils}/__init__.py +0 -0
  112. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/utils/directory.py +0 -0
  113. {scalekit_sdk_python-2.16.0/scalekit/v1/auditlogs → scalekit_sdk_python-2.17.0/scalekit/v1}/__init__.py +0 -0
  114. {scalekit_sdk_python-2.16.0/scalekit/v1/auth → scalekit_sdk_python-2.17.0/scalekit/v1/agentkit_logs}/__init__.py +0 -0
  115. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/agentkit_logs/agentkit_analytics_pb2.py +0 -0
  116. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/agentkit_logs/agentkit_analytics_pb2.pyi +0 -0
  117. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/agentkit_logs/agentkit_analytics_pb2_grpc.py +0 -0
  118. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/agentkit_logs/agentkit_logs_pb2.py +0 -0
  119. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/agentkit_logs/agentkit_logs_pb2.pyi +0 -0
  120. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/agentkit_logs/agentkit_logs_pb2_grpc.py +0 -0
  121. {scalekit_sdk_python-2.16.0/scalekit/v1/clients → scalekit_sdk_python-2.17.0/scalekit/v1/auditlogs}/__init__.py +0 -0
  122. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/auditlogs/auditlogs_pb2.py +0 -0
  123. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/auditlogs/auditlogs_pb2.pyi +0 -0
  124. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/auditlogs/auditlogs_pb2_grpc.py +0 -0
  125. {scalekit_sdk_python-2.16.0/scalekit/v1/commons → scalekit_sdk_python-2.17.0/scalekit/v1/auth}/__init__.py +0 -0
  126. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/auth/auth_pb2.py +0 -0
  127. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/auth/auth_pb2.pyi +0 -0
  128. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/auth/auth_pb2_grpc.py +0 -0
  129. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/auth/passwordless_pb2.py +0 -0
  130. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/auth/passwordless_pb2.pyi +0 -0
  131. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/auth/passwordless_pb2_grpc.py +0 -0
  132. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/auth/totp_pb2.py +0 -0
  133. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/auth/totp_pb2.pyi +0 -0
  134. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/auth/totp_pb2_grpc.py +0 -0
  135. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/auth/webauthn_pb2.py +0 -0
  136. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/auth/webauthn_pb2.pyi +0 -0
  137. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/auth/webauthn_pb2_grpc.py +0 -0
  138. {scalekit_sdk_python-2.16.0/scalekit/v1/connected_accounts → scalekit_sdk_python-2.17.0/scalekit/v1/clients}/__init__.py +0 -0
  139. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/clients/clients_pb2.py +0 -0
  140. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/clients/clients_pb2.pyi +0 -0
  141. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/clients/clients_pb2_grpc.py +0 -0
  142. {scalekit_sdk_python-2.16.0/scalekit/v1/connections → scalekit_sdk_python-2.17.0/scalekit/v1/commons}/__init__.py +0 -0
  143. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/commons/commons_pb2.py +0 -0
  144. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/commons/commons_pb2.pyi +0 -0
  145. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/commons/commons_pb2_grpc.py +0 -0
  146. {scalekit_sdk_python-2.16.0/scalekit/v1/directories → scalekit_sdk_python-2.17.0/scalekit/v1/connected_accounts}/__init__.py +0 -0
  147. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/connected_accounts/connected_accounts_pb2.py +0 -0
  148. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/connected_accounts/connected_accounts_pb2.pyi +0 -0
  149. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/connected_accounts/connected_accounts_pb2_grpc.py +0 -0
  150. {scalekit_sdk_python-2.16.0/scalekit/v1/domains → scalekit_sdk_python-2.17.0/scalekit/v1/connections}/__init__.py +0 -0
  151. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/connections/connections_pb2.py +0 -0
  152. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/connections/connections_pb2.pyi +0 -0
  153. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/connections/connections_pb2_grpc.py +0 -0
  154. {scalekit_sdk_python-2.16.0/scalekit/v1/emails → scalekit_sdk_python-2.17.0/scalekit/v1/directories}/__init__.py +0 -0
  155. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/directories/directories_pb2.py +0 -0
  156. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/directories/directories_pb2.pyi +0 -0
  157. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/directories/directories_pb2_grpc.py +0 -0
  158. {scalekit_sdk_python-2.16.0/scalekit/v1/environments → scalekit_sdk_python-2.17.0/scalekit/v1/domains}/__init__.py +0 -0
  159. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/domains/domains_pb2.py +0 -0
  160. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/domains/domains_pb2.pyi +0 -0
  161. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/domains/domains_pb2_grpc.py +0 -0
  162. {scalekit_sdk_python-2.16.0/scalekit/v1/errdetails → scalekit_sdk_python-2.17.0/scalekit/v1/emails}/__init__.py +0 -0
  163. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/emails/emails_pb2.py +0 -0
  164. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/emails/emails_pb2.pyi +0 -0
  165. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/emails/emails_pb2_grpc.py +0 -0
  166. {scalekit_sdk_python-2.16.0/scalekit/v1/events → scalekit_sdk_python-2.17.0/scalekit/v1/environments}/__init__.py +0 -0
  167. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/environments/environments_pb2.py +0 -0
  168. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/environments/environments_pb2.pyi +0 -0
  169. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/environments/environments_pb2_grpc.py +0 -0
  170. {scalekit_sdk_python-2.16.0/scalekit/v1/gateway → scalekit_sdk_python-2.17.0/scalekit/v1/errdetails}/__init__.py +0 -0
  171. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/errdetails/errdetails_pb2.py +0 -0
  172. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/errdetails/errdetails_pb2.pyi +0 -0
  173. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/errdetails/errdetails_pb2_grpc.py +0 -0
  174. {scalekit_sdk_python-2.16.0/scalekit/v1/interceptors → scalekit_sdk_python-2.17.0/scalekit/v1/events}/__init__.py +0 -0
  175. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/events/events_pb2.py +0 -0
  176. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/events/events_pb2.pyi +0 -0
  177. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/events/events_pb2_grpc.py +0 -0
  178. {scalekit_sdk_python-2.16.0/scalekit/v1/keys → scalekit_sdk_python-2.17.0/scalekit/v1/gateway}/__init__.py +0 -0
  179. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/gateway/gateway_pb2.py +0 -0
  180. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/gateway/gateway_pb2.pyi +0 -0
  181. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/gateway/gateway_pb2_grpc.py +0 -0
  182. {scalekit_sdk_python-2.16.0/scalekit/v1/mcp → scalekit_sdk_python-2.17.0/scalekit/v1/interceptors}/__init__.py +0 -0
  183. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/interceptors/interceptors_pb2.py +0 -0
  184. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/interceptors/interceptors_pb2.pyi +0 -0
  185. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/interceptors/interceptors_pb2_grpc.py +0 -0
  186. {scalekit_sdk_python-2.16.0/scalekit/v1/members → scalekit_sdk_python-2.17.0/scalekit/v1/keys}/__init__.py +0 -0
  187. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/keys/keys_pb2.py +0 -0
  188. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/keys/keys_pb2.pyi +0 -0
  189. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/keys/keys_pb2_grpc.py +0 -0
  190. {scalekit_sdk_python-2.16.0/scalekit/v1/migrations → scalekit_sdk_python-2.17.0/scalekit/v1/mcp}/__init__.py +0 -0
  191. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/mcp/mcp_pb2.py +0 -0
  192. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/mcp/mcp_pb2.pyi +0 -0
  193. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/mcp/mcp_pb2_grpc.py +0 -0
  194. {scalekit_sdk_python-2.16.0/scalekit/v1/options → scalekit_sdk_python-2.17.0/scalekit/v1/members}/__init__.py +0 -0
  195. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/members/members_pb2.py +0 -0
  196. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/members/members_pb2.pyi +0 -0
  197. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/members/members_pb2_grpc.py +0 -0
  198. {scalekit_sdk_python-2.16.0/scalekit/v1/organizations → scalekit_sdk_python-2.17.0/scalekit/v1/migrations}/__init__.py +0 -0
  199. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/migrations/migrations_pb2.py +0 -0
  200. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/migrations/migrations_pb2.pyi +0 -0
  201. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/migrations/migrations_pb2_grpc.py +0 -0
  202. {scalekit_sdk_python-2.16.0/scalekit/v1/providers → scalekit_sdk_python-2.17.0/scalekit/v1/options}/__init__.py +0 -0
  203. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/options/options_pb2.py +0 -0
  204. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/options/options_pb2.pyi +0 -0
  205. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/options/options_pb2_grpc.py +0 -0
  206. {scalekit_sdk_python-2.16.0/scalekit/v1/roles → scalekit_sdk_python-2.17.0/scalekit/v1/organizations}/__init__.py +0 -0
  207. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/organizations/organizations_pb2.py +0 -0
  208. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/organizations/organizations_pb2.pyi +0 -0
  209. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/organizations/organizations_pb2_grpc.py +0 -0
  210. {scalekit_sdk_python-2.16.0/scalekit/v1/secrets → scalekit_sdk_python-2.17.0/scalekit/v1/providers}/__init__.py +0 -0
  211. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/providers/providers_pb2.py +0 -0
  212. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/providers/providers_pb2.pyi +0 -0
  213. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/providers/providers_pb2_grpc.py +0 -0
  214. {scalekit_sdk_python-2.16.0/scalekit/v1/sessions → scalekit_sdk_python-2.17.0/scalekit/v1/roles}/__init__.py +0 -0
  215. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/roles/roles_pb2.py +0 -0
  216. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/roles/roles_pb2.pyi +0 -0
  217. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/roles/roles_pb2_grpc.py +0 -0
  218. {scalekit_sdk_python-2.16.0/scalekit/v1/tokens → scalekit_sdk_python-2.17.0/scalekit/v1/secrets}/__init__.py +0 -0
  219. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/secrets/secrets_pb2.py +0 -0
  220. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/secrets/secrets_pb2.pyi +0 -0
  221. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/secrets/secrets_pb2_grpc.py +0 -0
  222. {scalekit_sdk_python-2.16.0/scalekit/v1/tools → scalekit_sdk_python-2.17.0/scalekit/v1/sessions}/__init__.py +0 -0
  223. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/sessions/sessions_pb2.py +0 -0
  224. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/sessions/sessions_pb2.pyi +0 -0
  225. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/sessions/sessions_pb2_grpc.py +0 -0
  226. {scalekit_sdk_python-2.16.0/scalekit/v1/user_attributes → scalekit_sdk_python-2.17.0/scalekit/v1/tokens}/__init__.py +0 -0
  227. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/tokens/tokens_pb2.py +0 -0
  228. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/tokens/tokens_pb2.pyi +0 -0
  229. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/tokens/tokens_pb2_grpc.py +0 -0
  230. {scalekit_sdk_python-2.16.0/scalekit/v1/users → scalekit_sdk_python-2.17.0/scalekit/v1/tools}/__init__.py +0 -0
  231. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/tools/tools_pb2.py +0 -0
  232. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/tools/tools_pb2.pyi +0 -0
  233. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/tools/tools_pb2_grpc.py +0 -0
  234. {scalekit_sdk_python-2.16.0/scalekit/v1/webhooks → scalekit_sdk_python-2.17.0/scalekit/v1/user_attributes}/__init__.py +0 -0
  235. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/user_attributes/user_attributes_pb2.py +0 -0
  236. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/user_attributes/user_attributes_pb2.pyi +0 -0
  237. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/user_attributes/user_attributes_pb2_grpc.py +0 -0
  238. {scalekit_sdk_python-2.16.0/scalekit/v1/workspaces → scalekit_sdk_python-2.17.0/scalekit/v1/users}/__init__.py +0 -0
  239. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/users/users_pb2.py +0 -0
  240. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/users/users_pb2.pyi +0 -0
  241. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/users/users_pb2_grpc.py +0 -0
  242. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/webhooks/webhooks_pb2.py +0 -0
  243. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/webhooks/webhooks_pb2.pyi +0 -0
  244. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/webhooks/webhooks_pb2_grpc.py +0 -0
  245. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/workspaces/workspaces_pb2.py +0 -0
  246. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/workspaces/workspaces_pb2.pyi +0 -0
  247. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/v1/workspaces/workspaces_pb2_grpc.py +0 -0
  248. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit/webauthn.py +0 -0
  249. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit_sdk_python.egg-info/dependency_links.txt +0 -0
  250. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/scalekit_sdk_python.egg-info/top_level.txt +0 -0
  251. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/setup.cfg +0 -0
  252. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/tests/test_actions.py +0 -0
  253. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/tests/test_connected_accounts.py +0 -0
  254. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/tests/test_connection.py +0 -0
  255. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/tests/test_directory.py +0 -0
  256. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/tests/test_domain.py +0 -0
  257. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/tests/test_events.py +0 -0
  258. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/tests/test_login.py +0 -0
  259. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/tests/test_m2m_client.py +0 -0
  260. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/tests/test_mcp.py +0 -0
  261. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/tests/test_org_roles.py +0 -0
  262. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/tests/test_organization.py +0 -0
  263. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/tests/test_organization_session_policy.py +0 -0
  264. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/tests/test_organization_slug_logo.py +0 -0
  265. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/tests/test_passwordless.py +0 -0
  266. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/tests/test_permissions.py +0 -0
  267. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/tests/test_providers.py +0 -0
  268. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/tests/test_role_defaults_dependent.py +0 -0
  269. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/tests/test_roles.py +0 -0
  270. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/tests/test_sessions.py +0 -0
  271. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/tests/test_sk819_retry_behavior.py +0 -0
  272. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/tests/test_tokens.py +0 -0
  273. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/tests/test_tools.py +0 -0
  274. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/tests/test_user_roles_permissions.py +0 -0
  275. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/tests/test_users.py +0 -0
  276. {scalekit_sdk_python-2.16.0 → scalekit_sdk_python-2.17.0}/tests/test_webauthn_credentials.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scalekit-sdk-python
3
- Version: 2.16.0
3
+ Version: 2.17.0
4
4
  Summary: Scalekit official Python SDK
5
5
  Home-page: https://github.com/scalekit-inc/scalekit-sdk-python
6
6
  Author: Team Scalekit
@@ -17,7 +17,7 @@ Requires-Dist: google>=3.0
17
17
  Requires-Dist: requests>=2.34.0
18
18
  Requires-Dist: PyJWT>=2.13.0
19
19
  Requires-Dist: cffi>=1.15.1
20
- Requires-Dist: cryptography<49,>=46.0.6
20
+ Requires-Dist: cryptography>=50.0.0
21
21
  Requires-Dist: setuptools<83.0,>=82.0.1
22
22
  Requires-Dist: grpcio-status<2.0,>=1.81.0
23
23
  Requires-Dist: protoc-gen-openapiv2>=0.0.1
@@ -27,6 +27,12 @@ Requires-Dist: python-dotenv<2.0,>=1.2.2
27
27
  Requires-Dist: Faker<41.0,>=33.0.0
28
28
  Requires-Dist: pydantic>=2.13.4
29
29
  Requires-Dist: mcp>=1.27.2
30
+ Provides-Extra: flask
31
+ Requires-Dist: flask>=2.0; extra == "flask"
32
+ Provides-Extra: fastapi
33
+ Requires-Dist: fastapi>=0.100; extra == "fastapi"
34
+ Provides-Extra: django
35
+ Requires-Dist: django>=4.2; extra == "django"
30
36
  Dynamic: author
31
37
  Dynamic: author-email
32
38
  Dynamic: classifier
@@ -35,6 +41,7 @@ Dynamic: description-content-type
35
41
  Dynamic: home-page
36
42
  Dynamic: license
37
43
  Dynamic: license-file
44
+ Dynamic: provides-extra
38
45
  Dynamic: requires-dist
39
46
  Dynamic: summary
40
47
 
@@ -178,6 +185,88 @@ Explore fully functional sample applications built with popular Python framework
178
185
  |-----------|------------|-------------|
179
186
  | **FastAPI** | [scalekit-fastapi-example](https://github.com/scalekit-developers/scalekit-fastapi-example) | Modern async Python API framework |
180
187
 
188
+ ### Full Stack Auth — encrypted-session middleware for Flask, FastAPI, and Django
189
+
190
+ The example above is for **Modular SSO**: Scalekit brokers the OAuth exchange with your customer's own IdP via a `connection_id`, and your app owns its own session however it likes.
191
+
192
+ If instead Scalekit hosts your login UI and you want it to also manage the session lifecycle for you (**Full Stack Auth**), `scalekit-sdk-python` ships optional Flask, FastAPI, and Django extras that handle the encrypted session cookie, transparent token refresh, CSRF-safe login/callback, and full logout for you — no hand-rolled cookies, no manual refresh timing.
193
+
194
+ Register these under **Dashboard → Authentication → Redirects** before testing:
195
+ - **Redirect URI** — your `redirect_uri` (the `/callback` path). Scalekit rejects the exchange if this doesn't match exactly.
196
+ - **Post Logout Redirect URI** — where users land after full logout. A relative path gets auto-absolutized against the request host, but the resulting absolute URL must still be registered.
197
+ - **Initiate Login URL** — your `/login` path. Scalekit redirects here (not `/callback`) for a bookmarked login page, an IdP portal tile, or an invite/magic link — the login view already handles this correctly, including the `idp_initiated_login` case, with no extra code required.
198
+
199
+ pip install "scalekit-sdk-python[flask]" # or "scalekit-sdk-python[fastapi]" or "scalekit-sdk-python[django]"
200
+
201
+ ```python
202
+ # Flask
203
+ import os
204
+ from flask import Flask
205
+ from scalekit.frameworks.flask import ScalekitAuth
206
+
207
+ app = Flask(__name__)
208
+ auth = ScalekitAuth(
209
+ app,
210
+ env_url=os.environ["SCALEKIT_ENV_URL"],
211
+ client_id=os.environ["SCALEKIT_CLIENT_ID"],
212
+ client_secret=os.environ["SCALEKIT_CLIENT_SECRET"],
213
+ redirect_uri="https://myapp.com/callback",
214
+ cookie_encryption_secret=os.environ["COOKIE_ENCRYPTION_SECRET"], # openssl rand -base64 32
215
+ ) # registers /login, /callback, /logout
216
+
217
+ @app.route("/account")
218
+ @auth.requires_auth
219
+ def account():
220
+ return {"email": auth.current_user["email"]}
221
+ ```
222
+
223
+ ```python
224
+ # FastAPI -- protect routes with Depends(), FastAPI's idiomatic mechanism
225
+ import os
226
+ from fastapi import Depends, FastAPI
227
+ from scalekit.frameworks.fastapi import ScalekitAuth
228
+
229
+ app = FastAPI()
230
+ auth = ScalekitAuth(
231
+ env_url=os.environ["SCALEKIT_ENV_URL"],
232
+ client_id=os.environ["SCALEKIT_CLIENT_ID"],
233
+ client_secret=os.environ["SCALEKIT_CLIENT_SECRET"],
234
+ redirect_uri="https://myapp.com/callback",
235
+ cookie_encryption_secret=os.environ["COOKIE_ENCRYPTION_SECRET"],
236
+ )
237
+ auth.install(app) # registers /login, /callback, /logout
238
+
239
+ @app.get("/account")
240
+ async def account(user: dict = Depends(auth.requires_auth)):
241
+ return {"email": user["email"]}
242
+ ```
243
+
244
+ ```python
245
+ # Django -- settings.py
246
+ import os
247
+
248
+ MIDDLEWARE = [..., "scalekit.frameworks.django.ScalekitAuthMiddleware"]
249
+ SCALEKIT_ENV_URL = os.environ["SCALEKIT_ENV_URL"]
250
+ SCALEKIT_CLIENT_ID = os.environ["SCALEKIT_CLIENT_ID"]
251
+ SCALEKIT_CLIENT_SECRET = os.environ["SCALEKIT_CLIENT_SECRET"]
252
+ SCALEKIT_REDIRECT_URI = "https://myapp.com/callback"
253
+ SCALEKIT_COOKIE_ENCRYPTION_SECRET = os.environ["COOKIE_ENCRYPTION_SECRET"]
254
+
255
+ # urls.py
256
+ from django.urls import include, path
257
+ urlpatterns = [path("", include("scalekit.frameworks.django")), ...] # /login, /callback, /logout
258
+
259
+ # views.py
260
+ from django.http import JsonResponse
261
+ from scalekit.frameworks.django import login_required
262
+
263
+ @login_required
264
+ def account(request):
265
+ return JsonResponse(request.scalekit_user)
266
+ ```
267
+
268
+ See [`examples/flask`](./examples/flask), [`examples/fastapi`](./examples/fastapi), and [`examples/django`](./examples/django) for complete, runnable versions. For a fuller production-oriented sample app, see the framework repos above.
269
+
181
270
  ## 🔗 Helpful Links
182
271
 
183
272
  ### 📖 Quickstart Guides
@@ -138,6 +138,88 @@ Explore fully functional sample applications built with popular Python framework
138
138
  |-----------|------------|-------------|
139
139
  | **FastAPI** | [scalekit-fastapi-example](https://github.com/scalekit-developers/scalekit-fastapi-example) | Modern async Python API framework |
140
140
 
141
+ ### Full Stack Auth — encrypted-session middleware for Flask, FastAPI, and Django
142
+
143
+ The example above is for **Modular SSO**: Scalekit brokers the OAuth exchange with your customer's own IdP via a `connection_id`, and your app owns its own session however it likes.
144
+
145
+ If instead Scalekit hosts your login UI and you want it to also manage the session lifecycle for you (**Full Stack Auth**), `scalekit-sdk-python` ships optional Flask, FastAPI, and Django extras that handle the encrypted session cookie, transparent token refresh, CSRF-safe login/callback, and full logout for you — no hand-rolled cookies, no manual refresh timing.
146
+
147
+ Register these under **Dashboard → Authentication → Redirects** before testing:
148
+ - **Redirect URI** — your `redirect_uri` (the `/callback` path). Scalekit rejects the exchange if this doesn't match exactly.
149
+ - **Post Logout Redirect URI** — where users land after full logout. A relative path gets auto-absolutized against the request host, but the resulting absolute URL must still be registered.
150
+ - **Initiate Login URL** — your `/login` path. Scalekit redirects here (not `/callback`) for a bookmarked login page, an IdP portal tile, or an invite/magic link — the login view already handles this correctly, including the `idp_initiated_login` case, with no extra code required.
151
+
152
+ pip install "scalekit-sdk-python[flask]" # or "scalekit-sdk-python[fastapi]" or "scalekit-sdk-python[django]"
153
+
154
+ ```python
155
+ # Flask
156
+ import os
157
+ from flask import Flask
158
+ from scalekit.frameworks.flask import ScalekitAuth
159
+
160
+ app = Flask(__name__)
161
+ auth = ScalekitAuth(
162
+ app,
163
+ env_url=os.environ["SCALEKIT_ENV_URL"],
164
+ client_id=os.environ["SCALEKIT_CLIENT_ID"],
165
+ client_secret=os.environ["SCALEKIT_CLIENT_SECRET"],
166
+ redirect_uri="https://myapp.com/callback",
167
+ cookie_encryption_secret=os.environ["COOKIE_ENCRYPTION_SECRET"], # openssl rand -base64 32
168
+ ) # registers /login, /callback, /logout
169
+
170
+ @app.route("/account")
171
+ @auth.requires_auth
172
+ def account():
173
+ return {"email": auth.current_user["email"]}
174
+ ```
175
+
176
+ ```python
177
+ # FastAPI -- protect routes with Depends(), FastAPI's idiomatic mechanism
178
+ import os
179
+ from fastapi import Depends, FastAPI
180
+ from scalekit.frameworks.fastapi import ScalekitAuth
181
+
182
+ app = FastAPI()
183
+ auth = ScalekitAuth(
184
+ env_url=os.environ["SCALEKIT_ENV_URL"],
185
+ client_id=os.environ["SCALEKIT_CLIENT_ID"],
186
+ client_secret=os.environ["SCALEKIT_CLIENT_SECRET"],
187
+ redirect_uri="https://myapp.com/callback",
188
+ cookie_encryption_secret=os.environ["COOKIE_ENCRYPTION_SECRET"],
189
+ )
190
+ auth.install(app) # registers /login, /callback, /logout
191
+
192
+ @app.get("/account")
193
+ async def account(user: dict = Depends(auth.requires_auth)):
194
+ return {"email": user["email"]}
195
+ ```
196
+
197
+ ```python
198
+ # Django -- settings.py
199
+ import os
200
+
201
+ MIDDLEWARE = [..., "scalekit.frameworks.django.ScalekitAuthMiddleware"]
202
+ SCALEKIT_ENV_URL = os.environ["SCALEKIT_ENV_URL"]
203
+ SCALEKIT_CLIENT_ID = os.environ["SCALEKIT_CLIENT_ID"]
204
+ SCALEKIT_CLIENT_SECRET = os.environ["SCALEKIT_CLIENT_SECRET"]
205
+ SCALEKIT_REDIRECT_URI = "https://myapp.com/callback"
206
+ SCALEKIT_COOKIE_ENCRYPTION_SECRET = os.environ["COOKIE_ENCRYPTION_SECRET"]
207
+
208
+ # urls.py
209
+ from django.urls import include, path
210
+ urlpatterns = [path("", include("scalekit.frameworks.django")), ...] # /login, /callback, /logout
211
+
212
+ # views.py
213
+ from django.http import JsonResponse
214
+ from scalekit.frameworks.django import login_required
215
+
216
+ @login_required
217
+ def account(request):
218
+ return JsonResponse(request.scalekit_user)
219
+ ```
220
+
221
+ See [`examples/flask`](./examples/flask), [`examples/fastapi`](./examples/fastapi), and [`examples/django`](./examples/django) for complete, runnable versions. For a fuller production-oriented sample app, see the framework repos above.
222
+
141
223
  ## 🔗 Helpful Links
142
224
 
143
225
  ### 📖 Quickstart Guides
@@ -1,3 +1,3 @@
1
1
  # Single source of truth for the SDK version.
2
2
  # Import this in setup.py and scalekit/core.py — never hardcode the version elsewhere.
3
- __version__ = "2.16.0"
3
+ __version__ = "2.17.0"
@@ -0,0 +1,369 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ import time
5
+ from functools import lru_cache, wraps
6
+ from typing import Any, Callable, Optional
7
+ from urllib.parse import quote
8
+
9
+ try:
10
+ from django.conf import settings as django_settings
11
+ from django.http import HttpRequest, HttpResponse, HttpResponseRedirect
12
+ from django.urls import path
13
+ except ImportError as exc: # pragma: no cover -- exercised only without the extra installed
14
+ raise ImportError(
15
+ "Django integration requires the 'django' extra: "
16
+ "pip install scalekit-sdk-python[django]"
17
+ ) from exc
18
+
19
+ from scalekit.client import ScalekitClient
20
+ from scalekit.common.scalekit import (
21
+ AuthorizationUrlOptions,
22
+ CodeAuthenticationOptions,
23
+ LogoutUrlOptions,
24
+ )
25
+ from scalekit.middleware.csrf_state import (
26
+ RETURN_TO_COOKIE_NAME,
27
+ STATE_COOKIE_MAX_AGE,
28
+ STATE_COOKIE_NAME,
29
+ generate_state,
30
+ sanitize_return_to,
31
+ verify_state,
32
+ )
33
+ from scalekit.middleware.protocol import RequestAdapter, ResponseAdapter
34
+ from scalekit.middleware.session_manager import DEFAULT_COOKIE_NAME, SessionRefreshManager
35
+
36
+ logger = logging.getLogger("scalekit.frameworks.django")
37
+
38
+
39
+ class _DjangoRequestAdapter:
40
+ """RequestAdapter implementation backed by a Django HttpRequest."""
41
+
42
+ def __init__(self, request: HttpRequest):
43
+ self._request = request
44
+
45
+ def get_cookie(self, name: str) -> Optional[str]:
46
+ return self._request.COOKIES.get(name)
47
+
48
+ def get_request_url(self) -> str:
49
+ return self._request.build_absolute_uri()
50
+
51
+
52
+ class _DjangoResponseAdapter:
53
+ """ResponseAdapter implementation that mutates a Django HttpResponse in place."""
54
+
55
+ def __init__(self, response: HttpResponse, *, secure: bool = True):
56
+ self.response = response
57
+ # Default for callers that don't pass `secure` explicitly (e.g.
58
+ # SessionRefreshManager.apply()-style callers that only know the
59
+ # ResponseAdapter interface, not SCALEKIT_COOKIE_SECURE) -- every
60
+ # call site below now constructs this with secure=config.cookie_secure.
61
+ self._default_secure = secure
62
+
63
+ def set_cookie(
64
+ self,
65
+ name: str,
66
+ value: str,
67
+ *,
68
+ max_age: Optional[int] = None,
69
+ secure: Optional[bool] = None,
70
+ http_only: bool = True,
71
+ same_site: str = "Lax",
72
+ domain: Optional[str] = None,
73
+ path: str = "/",
74
+ ) -> None:
75
+ self.response.set_cookie(
76
+ name,
77
+ value,
78
+ max_age=max_age,
79
+ secure=self._default_secure if secure is None else secure,
80
+ httponly=http_only,
81
+ samesite=same_site,
82
+ domain=domain,
83
+ path=path,
84
+ )
85
+
86
+ def delete_cookie(
87
+ self, name: str, *, path: str = "/", domain: Optional[str] = None
88
+ ) -> None:
89
+ self.response.delete_cookie(name, path=path, domain=domain)
90
+
91
+
92
+ def _setting(name: str, default: Any = None, required: bool = False) -> Any:
93
+ value = getattr(django_settings, name, default)
94
+ if required and not value:
95
+ raise ValueError(
96
+ f"Django setting {name} is required to use scalekit.frameworks.django"
97
+ )
98
+ return value
99
+
100
+
101
+ class ScalekitAuthConfig:
102
+ """
103
+ Holds the shared ScalekitClient + SessionRefreshManager, built once from
104
+ Django settings (all read with an SCALEKIT_ prefix, matching Django
105
+ convention for third-party app config):
106
+
107
+ SCALEKIT_CLIENT_ID, SCALEKIT_CLIENT_SECRET, SCALEKIT_ENV_URL,
108
+ SCALEKIT_REDIRECT_URI, SCALEKIT_COOKIE_ENCRYPTION_SECRET (required --
109
+ see scalekit.middleware.session_crypto for why there is intentionally
110
+ no default), SCALEKIT_COOKIE_NAME (optional), SCALEKIT_LOGIN_PATH
111
+ (optional, default "/login"), SCALEKIT_POST_LOGIN_REDIRECT (optional,
112
+ default "/"), SCALEKIT_POST_LOGOUT_REDIRECT_URI (optional, falls back
113
+ to SCALEKIT_POST_LOGIN_REDIRECT), SCALEKIT_FULL_LOGOUT (optional
114
+ bool, default True).
115
+
116
+ SCALEKIT_CLIENT (an already-constructed ScalekitClient) can be set
117
+ directly, primarily for tests -- normal usage constructs one from the
118
+ ID/secret/env_url settings above.
119
+ """
120
+
121
+ def __init__(self):
122
+ client = _setting("SCALEKIT_CLIENT", None)
123
+ if client is None:
124
+ client = ScalekitClient(
125
+ env_url=_setting("SCALEKIT_ENV_URL", required=True),
126
+ client_id=_setting("SCALEKIT_CLIENT_ID", required=True),
127
+ client_secret=_setting("SCALEKIT_CLIENT_SECRET", required=True),
128
+ )
129
+ self.client = client
130
+ self.redirect_uri = _setting("SCALEKIT_REDIRECT_URI", required=True)
131
+ self.login_path = _setting("SCALEKIT_LOGIN_PATH", "/login")
132
+ self.post_login_redirect = _setting("SCALEKIT_POST_LOGIN_REDIRECT", "/")
133
+ self.post_logout_redirect_uri = (
134
+ _setting("SCALEKIT_POST_LOGOUT_REDIRECT_URI") or self.post_login_redirect
135
+ )
136
+ self.full_logout = _setting("SCALEKIT_FULL_LOGOUT", True)
137
+ # Right default for production. Chrome (and some other browsers) silently
138
+ # drop a Secure cookie set over plain http://localhost, which looks like a
139
+ # session that never sticks -- set SCALEKIT_COOKIE_SECURE=False for local
140
+ # HTTP dev.
141
+ self.cookie_secure = _setting("SCALEKIT_COOKIE_SECURE", True)
142
+
143
+ # Raises immediately if the secret is missing.
144
+ self.manager = SessionRefreshManager(
145
+ self.client,
146
+ _setting("SCALEKIT_COOKIE_ENCRYPTION_SECRET", required=True),
147
+ cookie_name=_setting("SCALEKIT_COOKIE_NAME", DEFAULT_COOKIE_NAME),
148
+ )
149
+
150
+
151
+ @lru_cache(maxsize=1)
152
+ def get_config() -> ScalekitAuthConfig:
153
+ return ScalekitAuthConfig()
154
+
155
+
156
+ class ScalekitAuthMiddleware:
157
+ """
158
+ Add to MIDDLEWARE in settings.py. Populates `request.scalekit_user` (None
159
+ if not authenticated) and transparently refreshes the session cookie on
160
+ every request. Mirrors Django's own convention (AuthenticationMiddleware
161
+ populates `request.user`, `login_required` enforces it) -- this
162
+ middleware does NOT itself enforce authentication; use `login_required`
163
+ on individual views for that.
164
+ """
165
+
166
+ def __init__(self, get_response: Callable):
167
+ self.get_response = get_response
168
+
169
+ def __call__(self, request: HttpRequest):
170
+ config = get_config()
171
+ result = config.manager.check(_DjangoRequestAdapter(request))
172
+ request.scalekit_user = result.user if result.authenticated else None
173
+ request.scalekit_session_result = result
174
+
175
+ response = self.get_response(request)
176
+
177
+ if result.new_cookie_value:
178
+ _DjangoResponseAdapter(response, secure=config.cookie_secure).set_cookie(
179
+ config.manager.cookie_name, result.new_cookie_value
180
+ )
181
+ elif result.should_clear_cookie:
182
+ _DjangoResponseAdapter(response).delete_cookie(config.manager.cookie_name)
183
+
184
+ return response
185
+
186
+
187
+ def get_session(request: HttpRequest) -> Optional[dict]:
188
+ """
189
+ Read-only session lookup -- for use outside a `login_required`-gated view
190
+ (e.g. a homepage that wants to show a logged-in/logged-out state without
191
+ gating the whole route). Never refreshes or writes a new cookie.
192
+
193
+ Returns `{"user": ..., "expires_at": ...}`, or `None` if there's no valid
194
+ session. `user` is the access-token claims (not tokens themselves) --
195
+ this function never returns `access_token`, `refresh_token`, or
196
+ `id_token`.
197
+
198
+ Note: ScalekitAuthMiddleware already sets `request.scalekit_user` on
199
+ every request (`None` if unauthenticated), so most views can just read
200
+ that directly. Use `get_session()` instead when you also need
201
+ `expires_at`.
202
+ """
203
+ config = get_config()
204
+ cookie_value = request.COOKIES.get(config.manager.cookie_name)
205
+ if not cookie_value:
206
+ return None
207
+ payload = config.manager.read_session(cookie_value)
208
+ if payload is None:
209
+ return None
210
+ return {"user": payload.get("user"), "expires_at": payload.get("expires_at")}
211
+
212
+
213
+ def login_required(view_func: Callable) -> Callable:
214
+ """
215
+ View decorator: redirects to SCALEKIT_LOGIN_PATH with a real 302 if
216
+ `request.scalekit_user` isn't set. Requires ScalekitAuthMiddleware to be
217
+ installed. Same "real redirect, never a JSON 401" guarantee as the
218
+ Flask/FastAPI integrations -- a JSON 401 is exactly what a background
219
+ fetch/XHR would silently swallow, which is the failure mode this whole
220
+ design exists to avoid.
221
+ """
222
+
223
+ @wraps(view_func)
224
+ def wrapped(request: HttpRequest, *args, **kwargs):
225
+ if getattr(request, "scalekit_user", None) is None:
226
+ # Preserve the page the caller was trying to reach so callback_view
227
+ # can send them back there instead of the fixed post_login_redirect.
228
+ current_path = request.get_full_path()
229
+ location = f"{get_config().login_path}?returnTo={quote(current_path, safe='')}"
230
+ return HttpResponseRedirect(location)
231
+ return view_func(request, *args, **kwargs)
232
+
233
+ return wrapped
234
+
235
+
236
+ def login_view(request: HttpRequest) -> HttpResponse:
237
+ config = get_config()
238
+ options = AuthorizationUrlOptions()
239
+ # offline_access is required to get a refresh_token back at all -- see
240
+ # scalekit.frameworks.flask for the full explanation.
241
+ options.scopes = ["openid", "profile", "email", "offline_access"]
242
+
243
+ # This view doubles as the dashboard-registered "Initiate Login URL" --
244
+ # see scalekit.frameworks.flask for the full reasoning.
245
+ idp_initiated_login = request.GET.get("idp_initiated_login")
246
+ if idp_initiated_login:
247
+ try:
248
+ claims = config.client.get_idp_initiated_login_claims(idp_initiated_login)
249
+ options.connection_id = claims.get("connection_id")
250
+ options.organization_id = claims.get("organization_id")
251
+ options.login_hint = claims.get("login_hint")
252
+ except Exception:
253
+ logger.exception(
254
+ "idp_initiated_login claim validation failed; falling back to normal login"
255
+ )
256
+
257
+ # Bind this authorization request to the browser that started it, so
258
+ # callback_view can reject a forged callback carrying an attacker's own
259
+ # authorization code (CSRF) -- see scalekit.frameworks.flask.
260
+ state = generate_state()
261
+ options.state = state
262
+ url = config.client.get_authorization_url(config.redirect_uri, options)
263
+ response = HttpResponseRedirect(url)
264
+ adapter = _DjangoResponseAdapter(response, secure=config.cookie_secure)
265
+ adapter.set_cookie(STATE_COOKIE_NAME, state, max_age=STATE_COOKIE_MAX_AGE)
266
+
267
+ # Preserve the page the caller was trying to reach (set by
268
+ # login_required's redirect) so callback_view can send them back there
269
+ # instead of the fixed post_login_redirect -- re-validated here since
270
+ # query strings are always attacker-influenceable.
271
+ return_to = sanitize_return_to(request.GET.get("returnTo"))
272
+ if return_to:
273
+ adapter.set_cookie(RETURN_TO_COOKIE_NAME, return_to, max_age=STATE_COOKIE_MAX_AGE)
274
+ return response
275
+
276
+
277
+ def callback_view(request: HttpRequest) -> HttpResponse:
278
+ config = get_config()
279
+
280
+ def _redirect_to_login():
281
+ resp = HttpResponseRedirect(config.login_path)
282
+ adapter = _DjangoResponseAdapter(resp, secure=config.cookie_secure)
283
+ adapter.delete_cookie(STATE_COOKIE_NAME)
284
+ adapter.delete_cookie(RETURN_TO_COOKIE_NAME)
285
+ return resp
286
+
287
+ # The provider redirects here with `error` (no `code`) if the user
288
+ # cancels consent or the request is otherwise rejected -- never reflect
289
+ # error/error_description into the response, it's attacker-influenced.
290
+ error = request.GET.get("error")
291
+ code = request.GET.get("code")
292
+ if error or not code:
293
+ return _redirect_to_login()
294
+
295
+ stored_state = request.COOKIES.get(STATE_COOKIE_NAME)
296
+ returned_state = request.GET.get("state")
297
+ if not verify_state(stored_state, returned_state):
298
+ # Missing or mismatched state -- this callback did not originate
299
+ # from a /login this browser actually made. Refuse the exchange.
300
+ return _redirect_to_login()
301
+
302
+ try:
303
+ result = config.client.authenticate_with_code(
304
+ code, config.redirect_uri, CodeAuthenticationOptions()
305
+ )
306
+ access_token = result["access_token"]
307
+ # Access-token claims (not id_token claims) are the source of truth for
308
+ # `user` -- see scalekit.frameworks.flask for the full reasoning.
309
+ claims = config.client.validate_access_token_and_get_claims(access_token)
310
+ payload = {
311
+ "user": claims,
312
+ "access_token": access_token,
313
+ "refresh_token": result.get("refresh_token"),
314
+ "id_token": result.get("id_token"),
315
+ "expires_at": claims.get("exp", time.time() + (result.get("expires_in") or 300)),
316
+ }
317
+ # create_session_cookie raises if the encrypted payload exceeds the
318
+ # browser cookie size limit -- must stay inside this try, not just
319
+ # the network calls above, or this view raises with no wrapper.
320
+ cookie_value = config.manager.create_session_cookie(payload)
321
+ except Exception:
322
+ logger.exception("login callback failed; redirecting to login")
323
+ return _redirect_to_login()
324
+
325
+ return_to = sanitize_return_to(request.COOKIES.get(RETURN_TO_COOKIE_NAME))
326
+ response = HttpResponseRedirect(return_to or config.post_login_redirect)
327
+ adapter = _DjangoResponseAdapter(response, secure=config.cookie_secure)
328
+ adapter.set_cookie(config.manager.cookie_name, cookie_value)
329
+ adapter.delete_cookie(STATE_COOKIE_NAME)
330
+ adapter.delete_cookie(RETURN_TO_COOKIE_NAME)
331
+ return response
332
+
333
+
334
+ def logout_view(request: HttpRequest) -> HttpResponse:
335
+ config = get_config()
336
+ cookie_value = request.COOKIES.get(config.manager.cookie_name)
337
+ id_token = None
338
+ if cookie_value:
339
+ payload = config.manager.read_session(cookie_value)
340
+ if payload:
341
+ id_token = payload.get("id_token")
342
+
343
+ redirect_url = config.post_logout_redirect_uri
344
+ if config.full_logout and id_token:
345
+ # Scalekit requires an absolute, dashboard-registered post-logout
346
+ # redirect URI -- absolutize a relative default against the current
347
+ # request, same as the Flask/FastAPI adapters.
348
+ absolute_redirect_uri = config.post_logout_redirect_uri
349
+ if not absolute_redirect_uri.startswith(("http://", "https://")):
350
+ absolute_redirect_uri = request.build_absolute_uri(absolute_redirect_uri)
351
+ options = LogoutUrlOptions()
352
+ options.id_token_hint = id_token
353
+ options.post_logout_redirect_uri = absolute_redirect_uri
354
+ redirect_url = config.client.get_logout_url(options)
355
+
356
+ response = HttpResponseRedirect(redirect_url)
357
+ _DjangoResponseAdapter(response).delete_cookie(config.manager.cookie_name)
358
+ return response
359
+
360
+
361
+ # No trailing slashes -- matches the Flask/FastAPI adapters' paths
362
+ # ("/login", "/callback", "/logout") so a redirect_uri registered in the
363
+ # Scalekit dashboard for one framework works unchanged for the others.
364
+ # Include via: path("", include("scalekit.frameworks.django"))
365
+ urlpatterns = [
366
+ path("login", login_view, name="scalekit_login"),
367
+ path("callback", callback_view, name="scalekit_callback"),
368
+ path("logout", logout_view, name="scalekit_logout"),
369
+ ]