Implement SaaSKit Python
Add login, callback, an encrypted sk_session cookie, logout, and refresh in FastAPI. Then stop.
Guardrails
- MUST keep
SCALEKIT_REDIRECT_URI, the dashboard Redirect URI, and the process host:port the same string. - MUST call
auth.install(app).app.include_router(auth.router)alone skips the 302 handler. - MUST set
cookie_secure=Falseon local HTTP. MUST set itTruein production. - MUST treat
returnToas a relative path only (/..., not//…). The adapter already sanitizes it.
Gotchas
setup-saaskitalready wrote env and registered the redirect. Start there.- Read
SCALEKIT_ENVIRONMENT_URL,SCALEKIT_CLIENT_ID,SCALEKIT_CLIENT_SECRET, andSCALEKIT_REDIRECT_URI. NeverSCALEKIT_ENV_URL. Do not prependhttps://. - Also need
COOKIE_ENCRYPTION_SECRET(openssl rand -base64 32). Keep it identical on every server. - Default path is FastAPI
ScalekitAuth. Do not hand-rollScalekitClientroutes. Do not copy Express cookies frommanage-saaskit-sessions. - Adapter defaults: GET
/login,/callback,/logout. Cookie issk_session(HttpOnly, SameSite=lax). CSRF cookie issk_oauth_state. - If
SCALEKIT_REDIRECT_URIalready ends in/auth/callback, passcallback_path="/auth/callback"or change both the env and the dashboard to/callback. Keep that URI's host and port. Run the app on that port. Do not keep port 3000 in env and run FastAPI on 5001. userfromrequires_authis access-token claims.subis always present.requires_authrefreshes. Do not add/auth/refresh. Do not return aResponsefrom a protected endpoint — that drops the refreshed cookie.- Register the Initiate Login URL and the Post Logout Redirect URI too.
Step 1 — Pick the path
- FastAPI → stay here.
- Django → open references/django.md. Stop reading this file.
- Flask → open references/flask.md. Stop reading this file.
- Node or Express → name
implement-saaskit. Stop. - Next.js App Router → name
implement-saaskit-nextjs. Stop.
If env is missing, collect the four Scalekit values from app.scalekit.com → Developers → Settings → API Credentials. Register SCALEKIT_REDIRECT_URI under Authentication → Redirect URLs → Allowed callback URLs. Use one origin for that URI, the dashboard, and the process. Do not mix setup's localhost:3000 with adapter samples on localhost:5001. Also register that origin's /login as Initiate Login URL and / as Post Logout Redirect URI. Generate COOKIE_ENCRYPTION_SECRET. Do not invent credential values.
Done when: this skill is the right path, the four Scalekit env names exist, and COOKIE_ENCRYPTION_SECRET exists.
Step 2 — Install and init
Install "scalekit-sdk-python[fastapi]" only when the repo has no Scalekit FastAPI extra yet. Do not add Django or Flask packages.
# app.py
import os
from fastapi import Depends, FastAPI
from scalekit.frameworks.fastapi import ScalekitAuth
app = FastAPI()
auth = ScalekitAuth(
env_url=os.environ["SCALEKIT_ENVIRONMENT_URL"],
client_id=os.environ["SCALEKIT_CLIENT_ID"],
client_secret=os.environ["SCALEKIT_CLIENT_SECRET"],
redirect_uri=os.environ["SCALEKIT_REDIRECT_URI"],
cookie_encryption_secret=os.environ["COOKIE_ENCRYPTION_SECRET"],
cookie_secure=False, # set True behind HTTPS
)
auth.install(app)
cookie_secure defaults to True in the SDK. Browsers drop a Secure cookie on plain http://localhost.
Done when: the extra is installed, ScalekitAuth reads those env vars, and auth.install(app) has run.
Step 3 — Login, callback, session
auth.install registers GET /login, GET /callback, and GET /logout. Do not write those routes by hand.
/loginsetssk_oauth_state, requestsopenid profile email offline_access, and redirects to Scalekit./callbackcomparessk_oauth_statetostatebeforeauthenticate_with_code. Mismatch → 302/login. Success writes encryptedsk_sessionand deletes the state cookie.returnTois a relative path only.
Link to /login. Do not send the browser to Scalekit yourself.
Done when: /login redirects to Scalekit, /callback writes sk_session, and the browser leaves /callback.
Step 4 — Logout
GET /logout. Link to /logout. full_logout defaults to True: get_logout_url with id_token_hint and post_logout_redirect_uri, then delete sk_session.
Register that same origin as a Post Logout Redirect URI.
Done when: sk_session is gone and the browser hits the logout URL.
Step 5 — Protect routes
requires_auth is the caller for refresh. It checks sk_session, refreshes when expires_at is near, and 302s to /login?returnTo=… when the session is missing. Never a JSON 401.
@app.get("/account")
async def account(user: dict = Depends(auth.requires_auth)):
return {"sub": user["sub"]}
Return a plain value, not a Response. Verify with GET /account.
Done when: a missing cookie on GET /account 302s to /login, and an expired-but-refreshable session stays signed in.
Step 6 — Stop
Do not write Express cookie-parser middleware or Next.js App Router auth.
Done when: login, callback, sk_session, logout, and refresh are in FastAPI, and this skill has stopped.
Reach for
setup-saaskitif env or the redirect URI is missingimplement-saaskitfor Node or Expressimplement-saaskit-nextjsfor Next.js App Router- references/django.md for Django
- references/flask.md for Flask
Live lookups
- Docs index: https://docs.scalekit.com/llms.txt
- FastAPI: https://docs.scalekit.com/saaskit/sdks/fastapi/
- Auth flow: https://docs.scalekit.com/authenticate/fsa/quickstart/
- MCP: https://mcp.scalekit.com