Skip to content

get token from callback_context #41

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Nov 11, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 40 additions & 8 deletions dash_enterprise_auth/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

Methods to integrate dash apps with the authentication from Dash Enterprise.
"""

import datetime as _dt
import os as _os
import platform as _platform
Expand All @@ -19,6 +20,7 @@


import dash as _dash

if hasattr(_dash, "dcc"):
_dcc = _dash.dcc
else:
Expand All @@ -35,6 +37,7 @@
f" Platform={_platform.system()}/{_platform.release()})"
)

_undefined = object()

class UaPyJWKClient(_jwt.PyJWKClient):
def fetch_data(self) -> Any:
Expand All @@ -53,6 +56,7 @@ def _wrap(*args, **kwargs):
f" context to run. Make sure to run `{func.__name__}` from a callback."
)
return func(*args, **kwargs)

return _wrap


Expand All @@ -69,9 +73,7 @@ def create_logout_button(label="Logout", style=None):
"""
logout_url = _os.getenv("DASH_LOGOUT_URL")
if not logout_url:
raise RuntimeError(
"DASH_LOGOUT_URL was not set in the environment."
)
raise RuntimeError("DASH_LOGOUT_URL was not set in the environment.")

if not _os.getenv("DASH_JWKS_URL"):
return _dcc.LogoutButton(
Expand All @@ -89,28 +91,59 @@ def create_logout_button(label="Logout", style=None):
label,
href=logout_url,
className="dash-logout-btn",
style={"textDecoration": "none"}
style={"textDecoration": "none"},
),
className="dash-logout-frame",
style=btn_style
style=btn_style,
)


def _raise_context_error():
try:
is_jupyter_kernel = (
get_ipython().__class__.__name__ == "ZMQInteractiveShell" # type: ignore
)
except NameError:
is_jupyter_kernel = False

raise RuntimeError(
"dash-enterprise-auth functions should be called in a flask request context or dash callback and will not run in a notebook cell.\n"
"This codeblock will still run correctly in your App Studio preview or deployed Dash app."
if is_jupyter_kernel
else "Could not find user token from the context.\n"
"Make sure you are running inside a flask request or a dash callback."
)


def _get_decoded_token(name):
token = _flask.request.cookies.get(name)
token = _undefined
if _flask.has_request_context():
token = _flask.request.cookies.get(name)
if token is _undefined and hasattr(_dash.callback_context, "cookies"):
token = _dash.callback_context.cookies.get(name)
if token is _undefined:
_raise_context_error()
if token is None:
return token
return _b64.b64decode(token)


@_need_request_context
def get_user_data():
jwks_url = _os.getenv("DASH_JWKS_URL")
info_url = _os.getenv("DASH_USER_INFO_URL")
if not jwks_url:
if not _flask.has_request_context():
# Old DE4 should always be in a request context.
_raise_context_error()
return _json.loads(_flask.request.headers.get("Plotly-User-Data", "{}"))
try:
jwks_client = UaPyJWKClient(jwks_url)

token = _get_decoded_token("kcIdToken")

if not token:
return {}

signing_key = jwks_client.get_signing_key_from_jwt(token)

info = _jwt.decode(
Expand Down Expand Up @@ -142,7 +175,6 @@ def get_user_data():
return {}


@_need_request_context
def get_username():
"""
Get the current user.
Expand Down