From 6583518fa32ce463b8414636df26648745a7c957 Mon Sep 17 00:00:00 2001 From: saif-at-scalekit Date: Mon, 30 Mar 2026 21:20:27 +0530 Subject: [PATCH 1/2] docs: Update README with agent-first positioning and improved structure - Refactor README structure for better readability - Add agent-first features section ahead of human auth - Update messaging to emphasize 'auth stack for agents' positioning - Add scalekit-logo.svg asset for consistent branding - Improve documentation links and quick start section - Align features description with current capabilities --- README.md | 242 ++++++++++++++++++++------------------------ images/scalekit.jpg | Bin 0 -> 29724 bytes scalekit-logo.svg | 13 +++ 3 files changed, 120 insertions(+), 135 deletions(-) create mode 100644 images/scalekit.jpg create mode 100644 scalekit-logo.svg diff --git a/README.md b/README.md index 90b5443..da83277 100644 --- a/README.md +++ b/README.md @@ -1,164 +1,136 @@ -

- - - - - -
-

+
-# Official Python SDK + + + Scalekit + + -[![PyPI version](https://img.shields.io/pypi/v/scalekit-sdk-python.svg?style=flat-square)](https://pypi.org/project/scalekit-sdk-python/) -[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) -[![Python versions](https://img.shields.io/pypi/pyversions/scalekit-sdk-python.svg?style=flat-square)](https://pypi.org/project/scalekit-sdk-python/) - -Scalekit is the **auth stack for AI apps** - from human authentication to agent authorization. Build secure AI products faster with authentication for humans (SSO, passwordless, full-stack auth) and agents (MCP/APIs, delegated actions), all unified on one platform. This Python SDK enables both traditional B2B authentication and cutting-edge agentic workflows. - -## ๐Ÿค– Agent-First Features +

Official Python SDK for Scalekit โ€” the auth stack for agents.
+authentication, authorization, and tool-calling for human-in-the-loop and autonomous agent flows.

-- **๐Ÿ” Agent Identity**: Agents as first-class actors with human ownership and org context -- **๐ŸŽฏ MCP-Native OAuth 2.1**: Purpose-built for Model Context Protocol with DCR/PKCE support -- **โฐ Ephemeral Credentials**: Time-bound, task-based authorization (minutes, not days) -- **๐Ÿ”’ Token Vault**: Per-user, per-tool token storage with rotation and progressive consent -- **๐Ÿ‘ฅ Human-in-the-Loop**: Step-up authentication when risk crosses thresholds -- **๐Ÿ“Š Immutable Audit**: Track which user initiated, which agent acted, what resource was accessed - -## ๐Ÿ‘จโ€๐Ÿ’ผ Human Authentication +[![PyPI version](https://img.shields.io/pypi/v/scalekit-sdk-python.svg)](https://pypi.org/project/scalekit-sdk-python/) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) +[![Python versions](https://img.shields.io/pypi/pyversions/scalekit-sdk-python.svg)](https://pypi.org/project/scalekit-sdk-python) -- **๐Ÿ” Enterprise SSO**: Support for SAML and OIDC protocols -- **๐Ÿ‘ฅ SCIM Provisioning**: Automated user provisioning and deprovisioning -- **๐Ÿš€ Passwordless Authentication**: Magic links, OTP, and modern auth flows -- **๐Ÿข Multi-tenant Architecture**: Organization-level authentication policies -- **๐Ÿ“ฑ Social Logins**: Support for popular social identity providers -- **๐Ÿ›ก๏ธ Full-Stack Auth**: Complete IdP-of-record solution for B2B SaaS -- **๐Ÿ Pythonic API**: Clean, intuitive interface following Python conventions +**[๐Ÿ“– Documentation](https://docs.scalekit.com)** ยท **[๐Ÿ› Report an Issue](https://github.com/scalekit-inc/scalekit-sdk-python/issues)** ยท **[๐Ÿ’ฌ Join our Slack](https://join.slack.com/t/scalekit-community/shared_invite/zt-3gsxwr4hc-0tvhwT2b_qgVSIZQBQCWRw)** -
-๐Ÿ“š Documentation โ€ข ๐Ÿš€ SSO Quickstart โ€ข ๐Ÿ’ป API Reference
-
- -## Pre-requisites - -1. [Sign up](https://scalekit.com) for a Scalekit account. -2. Get your ```env_url```, ```client_id``` and ```client_secret``` from the Scalekit dashboard. - -## Installation - -Install Scalekit SDK using your preferred package manager. +--- + +this is the official Python SDK for [Scalekit](https://scalekit.com), โ€” the auth stack for agents. Build secure AI products faster with authentication for humans (SSO, passwordless, full-stack auth) and agents (Mcp/APIs, delegated actions), all unified on one platform. +This Python SDK enables both traditional B2B authentication and cutting-edge agentic workflows. +#### Agent-First Features +- **Agent Identity** โ€” Agents as first-class actors with human ownership and org context +- **Mcp-Native OAuth 2.1** โ€” Purpose-built for Model Context Protocol with Dcr/pkce support +- **Ephemeral Credentials** โ€” Time-bound, task-based authorization (minutes, not days) +- **Token Vault** โ€” per-user, per-tool token storage with rotation and progressive consent +- **Human-in-the-Loop** โ€” step-up authentication when risk crosses thresholds +- **Immutable Audit** โ€” track which user initiated, which agent acted, what resource was accessed +#### Human Authentication +- **Enterprise SSO** โ€” support for SAML and OIDC protocols +- **SCIM Provisioning** โ€” automated user provisioning and deprovisioning +- **passwordless Authentication** โ€” magic links, OTP, and modern auth flows +- **Multi-tenant Architecture** โ€” organization-level authentication policies +- **Social Logins** โ€” support for popular social identity providers +- **Full-Stack Auth** โ€” complete IdP-of-record solution for B2B SaaS +- **Pythonic API** โ€” idiomatic Python with clean, intuitive interfaces +--- +### Getting started +#### Prerequisites +- **Python** โ‰ฅ 3.8 +- [Scalekit account](https://scalekit.com) with `env_url`, `client_id`, and `client_secret` +#### Installation ```sh pip install scalekit-sdk-python - +# or +poetry add scalekit-sdk-python ``` - -## Usage - -```py - -from scalekit import ScalekitClient - -sc = ScalekitClient( - env_url, - client_id, - client_secret +#### Usage +```python +from scalekit import Scalekit + +scalekit_client = Scalekit( + client_id="your-client-id", + client_secret="your-client-secret", + environment_url="https://your-env.scalekit.com" ) -# Use the sc object to interact with the Scalekit API -auth_url = sc.get_authorization_url( - "https://acme-corp.com/redirect-uri", - state="state", - connection_id="con_123456789" +# use scalekit_client to interact with the Scalekit API +auth_url = scalekit_client.get_authorization_url( + "https://acme-corp.com/redirect-uri", + state="state", + connection_id="con_123456789" ) - ``` - -##### Minimum Requirements - -To use the Scalekit Python SDK, you must have the following: - -| Component | Version | -| --------- | ------- | -| Python | 3.8+ | - -> **Tip:** Although Python 3.8 meets the minimum requirement, using a more recent version (such as Python 3.9 or later) is advisable. - - -## Examples - SSO with FastAPI - -Below is a simple code sample that showcases how to implement Single Sign-on using Scalekit SDK - -```py +--- +### Example โ€” SSO with FastAPI +```python from fastapi import FastAPI, Request, Response -from scalekit import ScalekitClient -import uvicorn +from scalekit import Scalekit app = FastAPI() - -sc = ScalekitClient( - env_url, - client_id, - client_secret +scalekit_client = Scalekit( + env_url="https://your-env.scalekit.com", + client_id="your-client-id", + client_secret="your-client-secret" ) - redirect_uri = "http://localhost:8000/auth/callback" @app.get("/auth/login") async def auth_login(request: Request): - auth_url = sc.get_authorization_url( - redirect_uri, - state="state", - connection_id="con_123456789" - ) - return Response(status_code=302, headers={"Location": auth_url}) - + auth_url = scalekit_client.get_authorization_url( + redirect_uri, + state="state", + connection_id="con_123456789" + ) + return Response(status_code=302, headers={"Location": auth_url}) @app.get("/auth/callback") async def auth_callback(request: Request): - code = request.query_params.get("code") - token = sc.authenticate_with_code( - code, - redirect_uri - ) - response = JSONResponse(content=token) - response.set_cookie("access_token", token["access_token"]) - - return response - + code = request.query_params.get("code") + token = scalekit_client.authenticate_with_code(code, redirect_uri) + response = Response(content=token["access_token"]) + response.set_cookie("access_token", token["access_token"]) + return response if __name__ == "__main__": - uvicorn.run(app, port=8080) - + import uvicorn + uvicorn.run(app, port=8080) ``` - -## ๐Ÿ“ฑ Example Apps - -Explore fully functional sample applications built with popular Python frameworks and the Scalekit SDK: - | Framework | Repository | Description | |-----------|------------|-------------| | **FastAPI** | [scalekit-fastapi-example](https://github.com/scalekit-developers/scalekit-fastapi-example) | Modern async Python API framework | - -## ๐Ÿ”— Helpful Links - -### ๐Ÿ“– Quickstart Guides -- [**SSO Integration**](https://docs.scalekit.com/sso/quickstart/) - Implement enterprise Single Sign-on -- [**Full Stack Auth**](https://docs.scalekit.com/fsa/quickstart/) - Complete authentication solution -- [**Passwordless Auth**](https://docs.scalekit.com/passwordless/quickstart/) - Modern authentication flows -- [**Social Logins**](https://docs.scalekit.com/social-logins/quickstart/) - Popular social identity providers -- [**Machine-to-Machine**](https://docs.scalekit.com/m2m/quickstart/) - API authentication - -### ๐Ÿ“š Documentation & Reference -- [**API Reference**](https://docs.scalekit.com/apis) - Complete API documentation -- [**Developer Kit**](https://docs.scalekit.com/dev-kit/) - Tools and utilities -- [**API Authentication Guide**](https://docs.scalekit.com/guides/authenticate-scalekit-api/) - Secure API access - -### ๐Ÿ› ๏ธ Additional Resources -- [**Setup Guide**](https://docs.scalekit.com/guides/setup-scalekit/) - Initial platform configuration -- [**Code Examples**](https://docs.scalekit.com/directory/code-examples/) - Ready-to-use code snippets -- [**Admin Portal Guide**](https://docs.scalekit.com/directory/guides/admin-portal/) - Administrative interface -- [**Launch Checklist**](https://docs.scalekit.com/directory/guides/launch-checklist/) - Pre-production checklist - -## License - -This project is licensed under the **MIT license**. -See the [LICENSE](LICENSE) file for more information. +| **Django** | [scalekit-django-auth-example](https://github.com/scalekit-inc/scalekit-django-auth-example) | Django web framework integration | +| **Flask** | [scalekit-flask-auth-example](https://github.com/scalekit-inc/scalekit-flask-auth-example) | Flask microframework integration | +--- +### Helpful links +#### Quickstart Guides +- [SSO Integration](https://docs.scalekit.com/sso/quickstart/) โ€” implement enterprise Single Sign-on +- [Full Stack Auth](https://docs.scalekit.com/fsa/quickstart/) โ€” complete authentication solution +- [Passwordless Auth](https://docs.scalekit.com/passwordless/quickstart/) โ€” modern authentication flows +- [Social Logins](https://docs.scalekit.com/social-logins/quickstart/) โ€” popular social identity providers +- [Machine-to-Machine](https://docs.scalekit.com/m2m/quickstart/) โ€” API authentication +#### Documentation & Reference +- [API Reference](https://docs.scalekit.com/apis) โ€” complete API documentation +- [Developer Kit](https://docs.scalekit.com/dev-kit/) โ€” tools and utilities +- [API authentication guide](https://docs.scalekit.com/guides/authenticate-scalekit-api/) โ€” secure API access +#### Additional resources +- [setup Guide](https://docs.scalekit.com/guides/setup-scalekit/) โ€” initial platform configuration +- [Code examples](https://docs.scalekit.com/directory/code-examples/) โ€” ready-to-use code snippets +- [Admin Portal Guide](https://docs.scalekit.com/directory/guides/admin-portal/) โ€” administrative interface +- [Launch Checklist](https://docs.scalekit.com/directory/guides/launch-checklist/) โ€” pre-production checklist +--- +### Contributing + +Contributions are welcome! Coming soon: contribution guidelines. + +For now: +1. Fork this repository +2. Create a branch โ€” `git checkout -b fix/my-improvement` +3. Make your changes +4. Run tests โ€” `pytest` +5. Open a Pull Request + +--- +### License +This project is licensed under the **MIT license**. See the [LICENSE](LICENSE) file for more information. diff --git a/images/scalekit.jpg b/images/scalekit.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d9a9541848d61abb80103555d90fe2b2d981369e GIT binary patch literal 29724 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 + + + + + + + + + + + + From 8ff860d3333893ffb11326df366dc6629671a613 Mon Sep 17 00:00:00 2001 From: saif-at-scalekit Date: Wed, 1 Apr 2026 11:13:14 +0530 Subject: [PATCH 2/2] fix: correct SDK class name, capitalization, cookie security, and markdown formatting --- README.md | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index da83277..8d4cfa6 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ this is the official Python SDK for [Scalekit](https://scalekit.com), โ€” the au This Python SDK enables both traditional B2B authentication and cutting-edge agentic workflows. #### Agent-First Features - **Agent Identity** โ€” Agents as first-class actors with human ownership and org context -- **Mcp-Native OAuth 2.1** โ€” Purpose-built for Model Context Protocol with Dcr/pkce support +- **MCP-Native OAuth 2.1** โ€” Purpose-built for Model Context Protocol with DCR/PKCE support - **Ephemeral Credentials** โ€” Time-bound, task-based authorization (minutes, not days) - **Token Vault** โ€” per-user, per-tool token storage with rotation and progressive consent - **Human-in-the-Loop** โ€” step-up authentication when risk crosses thresholds @@ -31,7 +31,7 @@ This Python SDK enables both traditional B2B authentication and cutting-edge age #### Human Authentication - **Enterprise SSO** โ€” support for SAML and OIDC protocols - **SCIM Provisioning** โ€” automated user provisioning and deprovisioning -- **passwordless Authentication** โ€” magic links, OTP, and modern auth flows +- **Passwordless Authentication** โ€” magic links, OTP, and modern auth flows - **Multi-tenant Architecture** โ€” organization-level authentication policies - **Social Logins** โ€” support for popular social identity providers - **Full-Stack Auth** โ€” complete IdP-of-record solution for B2B SaaS @@ -49,12 +49,12 @@ poetry add scalekit-sdk-python ``` #### Usage ```python -from scalekit import Scalekit +from scalekit import ScalekitClient -scalekit_client = Scalekit( +scalekit_client = ScalekitClient( client_id="your-client-id", client_secret="your-client-secret", - environment_url="https://your-env.scalekit.com" + env_url="https://your-env.scalekit.com" ) # use scalekit_client to interact with the Scalekit API @@ -68,10 +68,10 @@ auth_url = scalekit_client.get_authorization_url( ### Example โ€” SSO with FastAPI ```python from fastapi import FastAPI, Request, Response -from scalekit import Scalekit +from scalekit import ScalekitClient app = FastAPI() -scalekit_client = Scalekit( +scalekit_client = ScalekitClient( env_url="https://your-env.scalekit.com", client_id="your-client-id", client_secret="your-client-secret" @@ -90,18 +90,27 @@ async def auth_login(request: Request): async def auth_callback(request: Request): code = request.query_params.get("code") token = scalekit_client.authenticate_with_code(code, redirect_uri) - response = Response(content=token["access_token"]) - response.set_cookie("access_token", token["access_token"]) + response = Response(content="Authenticated successfully") + response.set_cookie( + "access_token", + token["access_token"], + httponly=True, + secure=True, + samesite="Lax" + ) return response if __name__ == "__main__": import uvicorn uvicorn.run(app, port=8080) ``` +``` + | Framework | Repository | Description | |-----------|------------|-------------| | **FastAPI** | [scalekit-fastapi-example](https://github.com/scalekit-developers/scalekit-fastapi-example) | Modern async Python API framework | | **Django** | [scalekit-django-auth-example](https://github.com/scalekit-inc/scalekit-django-auth-example) | Django web framework integration | | **Flask** | [scalekit-flask-auth-example](https://github.com/scalekit-inc/scalekit-flask-auth-example) | Flask microframework integration | + --- ### Helpful links #### Quickstart Guides @@ -115,7 +124,7 @@ if __name__ == "__main__": - [Developer Kit](https://docs.scalekit.com/dev-kit/) โ€” tools and utilities - [API authentication guide](https://docs.scalekit.com/guides/authenticate-scalekit-api/) โ€” secure API access #### Additional resources -- [setup Guide](https://docs.scalekit.com/guides/setup-scalekit/) โ€” initial platform configuration +- [Setup Guide](https://docs.scalekit.com/guides/setup-scalekit/) โ€” initial platform configuration - [Code examples](https://docs.scalekit.com/directory/code-examples/) โ€” ready-to-use code snippets - [Admin Portal Guide](https://docs.scalekit.com/directory/guides/admin-portal/) โ€” administrative interface - [Launch Checklist](https://docs.scalekit.com/directory/guides/launch-checklist/) โ€” pre-production checklist