Loading...
Loading...
Use when adding login, logout, and user profile to a Flask web application using session-based authentication - integrates auth0-server-python for server-rendered apps with login/callback/profile/logout flows.
npx skill4agent add auth0/agent-skills auth0-flaskauth0-server-pythonauth0-quickstartauth0-fastapi-apiauth0-reactauth0-vueauth0-angularauth0-nextjsauth0-expressauth0-fastifypip install auth0-server-python "flask[async]" python-dotenvflask[async]flask[async]asgirefasync defrequirements.txtflask[async]>=2.0.0.envAUTH0_DOMAIN=your-tenant.us.auth0.com
AUTH0_CLIENT_ID=your_client_id
AUTH0_CLIENT_SECRET=your_client_secret
AUTH0_SECRET=your_generated_app_secret
AUTH0_REDIRECT_URI=http://localhost:5000/callbackAUTH0_DOMAINhttps://AUTH0_CLIENT_IDAUTH0_CLIENT_SECRETAUTH0_SECRETopenssl rand -hex 64http://localhost:5000/callbackhttp://localhost:5000auth.pyServerClientsessionimport os
from flask import session as flask_session
from auth0_server_python.auth_server.server_client import ServerClient
from auth0_server_python.auth_types import StateData, TransactionData
from auth0_server_python.store import StateStore, TransactionStore
from dotenv import load_dotenv
load_dotenv() # Uses .env by default; pass load_dotenv(".env.local") if credentials are in .env.local
class FlaskSessionStateStore(StateStore):
"""State store that uses Flask's session for persistence."""
def __init__(self, secret: str):
super().__init__({"secret": secret})
async def set(self, identifier, state, remove_if_expires=False, options=None):
data = state.dict() if hasattr(state, "dict") else state
flask_session[identifier] = self.encrypt(identifier, data)
async def get(self, identifier, options=None):
data = flask_session.get(identifier)
if data is None:
return None
decrypted = self.decrypt(identifier, data)
# Ensure to not return a dict, as the underlying SDK expects a StateData instance, not a dict
return StateData(**decrypted) if isinstance(decrypted, dict) else decrypted
async def delete(self, identifier, options=None):
flask_session.pop(identifier, None)
async def delete_by_logout_token(self, claims, options=None):
pass
class FlaskSessionTransactionStore(TransactionStore):
"""Transaction store that uses Flask's session for persistence."""
def __init__(self, secret: str):
super().__init__({"secret": secret})
async def set(self, identifier, state, remove_if_expires=False, options=None):
data = state.dict() if hasattr(state, "dict") else state
flask_session[identifier] = self.encrypt(identifier, data)
async def get(self, identifier, options=None):
data = flask_session.get(identifier)
if data is None:
return None
decrypted = self.decrypt(identifier, data)
# Ensure to not return a dict, as the underlying SDK expects a TransactionData instance, not a dict
return TransactionData(**decrypted) if isinstance(decrypted, dict) else decrypted
async def delete(self, identifier, options=None):
flask_session.pop(identifier, None)
secret = os.getenv("AUTH0_SECRET")
auth0 = ServerClient(
domain=os.getenv("AUTH0_DOMAIN"),
client_id=os.getenv("AUTH0_CLIENT_ID"),
client_secret=os.getenv("AUTH0_CLIENT_SECRET"),
secret=secret,
redirect_uri=os.getenv("AUTH0_REDIRECT_URI"),
state_store=FlaskSessionStateStore(secret=secret),
transaction_store=FlaskSessionTransactionStore(secret=secret),
authorization_params={"scope": "openid profile email"},
)ServerClientstore_optionsbefore_requeststore_optionsflask.sessionstore_optionsflask.sessionstore_options{"request": request, "response": response}app.pyimport os
from flask import Flask, redirect, request
from auth import auth0
from dotenv import load_dotenv
load_dotenv()
app = Flask(__name__)
app.secret_key = os.getenv("AUTH0_SECRET")
app.config.update(
SESSION_COOKIE_SECURE=False, # Set to True in production (requires HTTPS)
SESSION_COOKIE_HTTPONLY=True,
SESSION_COOKIE_SAMESITE="Lax",
)app.secret_keySESSION_COOKIE_SECURE=TrueFalse@app.route("/")
async def home():
user = await auth0.get_user()
if user:
return f"Hello, {user['name']}! <a href='/profile'>Profile</a> | <a href='/logout'>Logout</a>"
return "Welcome! <a href='/login'>Login</a>"@app.route("/login")
async def login():
authorization_url = await auth0.start_interactive_login()
return redirect(authorization_url)start_interactive_login()redirect()ServerClient@app.route("/callback")
async def callback():
try:
await auth0.complete_interactive_login(str(request.url))
return redirect("/")
except Exception as e:
return f"Authentication error: {str(e)}", 400str(request.url)@app.route("/profile")
async def profile():
user = await auth0.get_user()
if user is None:
return redirect("/login")
return (
f"<h1>{user['name']}</h1>"
f"<p>Email: {user['email']}</p>"
f"<img src='{user['picture']}' alt='{user['name']}' width='100' />"
f"<p><a href='/logout'>Logout</a></p>"
)get_user()None@app.route("/logout")
async def logout():
url = await auth0.logout()
return redirect(url)logout()flask runhttp://localhost:5000/loginpip install flask-session redisapp.pyimport os
from flask import Flask, redirect, request
from flask_session import Session
from auth import auth0
from dotenv import load_dotenv
load_dotenv()
app = Flask(__name__)
app.secret_key = os.getenv("AUTH0_SECRET")
app.config.update(
SESSION_TYPE="redis",
SESSION_PERMANENT=True,
SESSION_KEY_PREFIX="auth0:",
SESSION_COOKIE_SECURE=False,
SESSION_COOKIE_HTTPONLY=True,
SESSION_COOKIE_SAMESITE="Lax",
)
Session(app)FlaskSessionStateStoreFlaskSessionTransactionStoreauth.pyflask.sessionflask.session| Mistake | Fix |
|---|---|
Hardcoding | Always read from environment variables — never embed credentials in code |
Using | Not needed; |
Using | Not needed; the SDK manages sessions and authentication |
Manually parsing JWTs with | The SDK handles token validation internally |
Installing | Must use |
| Using synchronous route handlers | All routes calling SDK methods must be |
Forgetting | Required for Flask session management — without it, sessions silently fail |
Using | That package is for FastAPI APIs — use |
Passing | |
| Not configuring callback URL in Auth0 Dashboard | Must add |
Returning | It returns a URL string, not a response — must wrap in |
Not handling errors in | |
Calling SDK methods without | All SDK methods are async — forgetting |
Passing options positionally to | Use |
| Expecting backchannel logout to work | Not supported with cookie-based sessions — |
Deploying with | Must set to |
| Method | Signature | Purpose |
|---|---|---|
| | Returns authorization URL string — wrap in |
| | Processes the callback URL, exchanges code for tokens |
| | Returns current session user dict or |
| | Returns the access token for calling external APIs |
| | Returns Auth0 logout URL string |
auth0-expressauth0-fastifyauth0 = ServerClient(
domain=os.getenv("AUTH0_DOMAIN"), # required
client_id=os.getenv("AUTH0_CLIENT_ID"), # required
client_secret=os.getenv("AUTH0_CLIENT_SECRET"), # required
secret=os.getenv("AUTH0_SECRET"), # required (encryption secret)
redirect_uri=os.getenv("AUTH0_REDIRECT_URI"), # required
state_store=FlaskSessionStateStore(secret=secret), # required
transaction_store=FlaskSessionTransactionStore(secret=secret), # required
authorization_params={"scope": "openid profile email"}, # recommended
)user = await auth0.get_user()
if user is None:
return redirect("/login")AUTH0_DOMAINtenant.us.auth0.comAUTH0_CLIENT_IDAUTH0_CLIENT_SECRETAUTH0_SECRETAUTH0_REDIRECT_URIhttp://localhost:5000/callback