-
Notifications
You must be signed in to change notification settings - Fork 23
Open
Labels
bugSomething isn't workingSomething isn't working
Description
Prerequisites
- I have searched the repository’s issues and Kinde community to ensure my issue isn’t a duplicate
- I have checked the latest version of the library to replicate my issue
- I have read the contributing guidelines
- I agree to the terms within the code of conduct
Describe the issue
KindeApiClient is not json serializable, which makes it impossible to store one in the flask session object, and based on the examples and docs is how this entire workflow is supposed to function
Library URL
https://github.com/kinde-oss/kinde-python-sdk
Library version
1.2.7
Operating system(s)
macOS
Operating system version(s)
does not matter
Further environment details
No response
Reproducible test case URL
"""Kinde Auth Flask Extension."""
from functools import wraps
from typing import Any, Callable, Optional, TypeVar, cast
from flask import Flask, Response, redirect, request, session, url_for
from kinde_sdk import Configuration # type: ignore
from kinde_sdk.kinde_api_client import GrantType, KindeApiClient # type: ignore
from werkzeug.wrappers.response import Response as WerkzeugResponse
F = TypeVar("F", bound=Callable[..., Any])
class KindeFlaskExtension:
"""Flask extension for Kinde authentication."""
def __init__(
self,
app: Flask,
index_url: str,
login_url: str,
logout_url: str,
kinde_host: str,
kinde_client_id: str,
kinde_client_secret: str,
disable_auth: bool = False,
):
"""Initialize Kinde Flask Extension.
Args:
app: Flask application instance.
index_url: URL to redirect after successful login.
login_url: URL to redirect for login.
logout_url: URL to redirect for logout.
kinde_host: Kinde host URL.
kinde_client_id: Kinde client ID.
kinde_client_secret: Kinde client secret.
disable_auth: Disable authentication for testing purposes.
"""
self.app = app
self.index_url = index_url
self.login_url = login_url
self.logout_url = logout_url
self.kinde_host = kinde_host
self.kinde_client_id = kinde_client_id
self.kinde_client_secret = kinde_client_secret
self.user_clients: dict[str, KindeApiClient] = {}
self._register_routes(app)
self.app.extensions = getattr(app, "extensions", {})
self.app.extensions["kinde"] = self
self.disable_auth = disable_auth
def login_required(self, f: F) -> F:
"""Require authentication for a route."""
if self.disable_auth:
return f
@wraps(f)
def decorated_function(*args: Any, **kwargs: Any) -> Any:
if kinde_client := session.get("kinde_client", None):
if kinde_client is not None and kinde_client.is_authenticated():
return f(*args, **kwargs)
return redirect(self.login_url)
return cast(F, decorated_function)
def _register_routes(self, app: Flask) -> None:
@app.route("/kinde_callback")
def callback() -> Response | WerkzeugResponse:
redirect_url = session.pop("redirect_url", self.index_url)
if self.disable_auth:
return redirect(redirect_url)
try:
kinde_client = session["kinde_client"]
kinde_client.fetch_token(authorization_response=str(request.url))
user = self.get_authorized_data(kinde_client)
user_id = user["id"]
session["user_id"] = user_id
return redirect(redirect_url)
except Exception as e:
return Response(
f"Error during authentication: {str(e)}",
status=400,
)
@app.route(self.login_url)
def login() -> Response | WerkzeugResponse:
redirect_url = session.pop("redirect_url", self.index_url)
if self.disable_auth:
return redirect(redirect_url)
kinde_client = KindeApiClient(
configuration=Configuration(host=self.kinde_host),
domain=self.kinde_host,
client_id=self.kinde_client_id,
client_secret=self.kinde_client_secret,
grant_type=GrantType.AUTHORIZATION_CODE,
callback_url=url_for(
"callback",
_external=True,
),
)
session["kinde_client"] = kinde_client
return redirect(kinde_client.get_login_url())
@app.route(self.logout_url)
def logout() -> Response | WerkzeugResponse:
self.user_clients[session["user_id"]] = None
session.pop("user_id", None)
return app.redirect(self.kinde_client.logout(redirect_to=self.index_url))
def get_authorized_data(self, kinde_client: KindeApiClient) -> dict[str, str]:
"""Get user data from Kinde API."""
user: dict[str, str] = kinde_client.get_user_details()
return userAdditional information
I pasted the example Im working with, but I cannot stick a client (which is specific to a user) inside of the flask session object.
Metadata
Metadata
Assignees
Labels
bugSomething isn't workingSomething isn't working