Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
2fd16b5
adds support for `ClerkAuth`
BSd3v Jun 5, 2025
1c39d79
fixing for lint
BSd3v Jun 5, 2025
f656ecc
adjustments for lint
BSd3v Jun 5, 2025
8cb6cec
adding test workflow as well as adding the hosted domain of the app a…
BSd3v Jun 5, 2025
be31cc6
fixing for lint
BSd3v Jun 5, 2025
2541ac9
adjustments for missing imports
BSd3v Jun 5, 2025
19dc9ea
bump to v3.9 python for test
BSd3v Jun 5, 2025
1a78d28
locking version for backend-api to 3.0.1
BSd3v Jun 5, 2025
1cccdf3
bump to 3.10
BSd3v Jun 5, 2025
f5a326c
adding missing imports
BSd3v Jun 5, 2025
e1aca30
adjusting for test variables
BSd3v Jun 5, 2025
2896292
fixing issue with auto allowed_parties
BSd3v Jun 5, 2025
6c98ef3
fixing issue with missing `allowed_parties` for test environment
BSd3v Jun 5, 2025
33ef9b2
`dotenv` -> `python-dotenv`
BSd3v Jun 5, 2025
a13739a
adding flow for logging out user if they are forced out of clerk remo…
BSd3v Jun 6, 2025
7f06d9f
fix for lint
BSd3v Jun 6, 2025
dfea8aa
adjustments for groups and cases where the user has a session but the…
BSd3v Jun 6, 2025
fba5335
fix for lint
BSd3v Jun 6, 2025
b98a373
fixing issue where logging out wouldnt clear the `clerk_logged_in` in…
BSd3v Jun 6, 2025
4a044bb
fix for lint
BSd3v Jun 6, 2025
dc84e65
fixing issue with redirect link for redirect process from navigation …
BSd3v Jun 6, 2025
4b11cf7
adjusting readme
BSd3v Jun 6, 2025
630d3da
adds method to pull the user_data to be used in Dash.
BSd3v Jun 9, 2025
b93bbd7
fixing for lint
BSd3v Jun 9, 2025
13f6f81
adding components for Clerk UserProfile
BSd3v Jun 11, 2025
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
11 changes: 10 additions & 1 deletion .github/workflows/python-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,12 @@ on:
jobs:
test:
runs-on: ubuntu-latest

environment:
name: test
strategy:
matrix:
python-version: ["3.8", "3.12"]
python-version: ["3.10", "3.12"]
steps:
- name: Checkout code
uses: actions/checkout@v3
Expand Down Expand Up @@ -54,6 +57,12 @@ jobs:
npm run lint

- name: Run tests
env:
CLERK_SECRET_KEY: ${{ secrets.CLERK_SECRET_KEY }}
CLERK_DOMAIN: ${{ secrets.CLERK_DOMAIN }}
CLERK_PUBLISHABLE_KEY: ${{ secrets.CLERK_PUBLISHABLE_KEY }}
CLERK_TEST_USER: ${{ secrets.CLERK_TEST_USER }}
CLERK_TEST_PASSWORD: ${{ secrets.CLERK_TEST_PASSWORD }}
run: |
source .venv/bin/activate
pytest --headless
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,8 @@ com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
R/
man/
src/jl/
*.jl
deps/
86 changes: 85 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -489,4 +489,88 @@ not protect the layouts of public routes.
Passing `auth_protect_layouts_kwargs` is the same are the additional `kwargs` passed to the function
By default, the app will check any non-public callback that has the `pathname` as an input,
when you pass `page_container` as the `id` of your container element for a page container,
it will only check the route if it is an output.
it will only check the route if it is an output.

## Example Usage with ClerkAuth

```python
from dash import Dash, html, dcc, page_container
from dash_auth_plus import ClerkAuth, public_callback
from dash import Input, Output, register_page

app = Dash(__name__, use_pages=True, pages_folder='', suppress_callback_exceptions=True)

# Initialize ClerkAuth with public routes
auth = ClerkAuth(
app,
secret_key="aStaticSecretKey!",
log_signins=True,
auth_protect_layouts=True,
page_container='_pages_content',
public_routes=['/', '/user/<user_id>/public'],
)

# Main layout with navigation
app.layout = html.Div(
[
html.Div(
[
dcc.Link("Home", href="/"),
dcc.Link("John Doe", href="/user/john_doe/public"),
dcc.Link('Logout', href='/logout', refresh=True),
],
style={"display": "flex", "gap": "1rem", "background": "lightgray", "padding": "0.5rem 1rem"},
),
page_container,
],
style={"display": "flex", "flexDirection": "column"},
)

# Home page (public)
home_layout = [
html.H1("Home Page"),
html.Button("Click me", id="home-button"),
html.Div(id="home-contents"),
]
register_page('home', "/", layout=home_layout)

@public_callback(
Output("home-contents", "children"),
Input("home-button", "n_clicks"),
)
def home(n_clicks):
if not n_clicks:
return "You haven't clicked the button."
return f"You clicked the button {n_clicks} times"

# Public user page
def user_layout(user_id: str, **kwargs):
return [
html.H1(f"User {user_id} (public)"),
dcc.Link("Authenticated user content", href=f"/user/{user_id}/private"),
]
register_page('user', path_template="/user/<user_id>/public", layout=user_layout)

# Private user page (protected)
def user_private(user_id: str, **kwargs):
return [
html.H1(f"User {user_id} (authenticated only)"),
html.Div("Members-only information"),
]
register_page('private', path_template="/user/{user_id}/private", layout=user_private)

if __name__ == "__main__":
app.run(debug=True)
```

Important things to note about ClerkAuth:
- if you are using your own logout method, you will need to have the `clerk_logged_in` local storage variable set to `false` to ensure the user is logged out.
- this can be done by a script similar to the following:
```html
<!-- Client-Side Logout State Reset -->
<script>
// Reset the client-side authentication flag on logout
localStorage.setItem('clerk_logged_in', 'false');
</script>
```
- The `Clerk` api is available in the browser, so you have access to all the api methods available in the Clerk documentation.
49 changes: 49 additions & 0 deletions dash_auth_plus/DashAuthComponents/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
from __future__ import print_function as _

import os as _os
import sys as _sys
import json

import dash as _dash

# noinspection PyUnresolvedReferences
from ._imports_ import * # noqa: F403,F401
from ._imports_ import __all__ # noqa: F401

if not hasattr(_dash, "__plotly_dash") and not hasattr(_dash, "development"):
print(
"Dash was not successfully imported. "
"Make sure you don't have a file "
'named \n"dash.py" in your current directory.',
file=_sys.stderr,
)
_sys.exit(1)

_basepath = _os.path.dirname(__file__)
_filepath = _os.path.abspath(_os.path.join(_basepath, "package-info.json"))
with open(_filepath) as f:
package = json.load(f)

package_name = package["name"].replace(" ", "_").replace("-", "_")
__version__ = package["version"]

_current_path = _os.path.dirname(_os.path.abspath(__file__))

_this_module = _sys.modules[__name__]

_unpkg = f"https://unpkg.com/dash-auth-plus@{__version__}/dash_auth_plus/"

_js_dist = [
{
"relative_package_path": "DashAuthComponents/dash_auth_plus.js",
"external_url": f"{_unpkg}dash_auth_plus.js",
"namespace": package_name,
},
]

_css_dist = []


for _component in __all__:
setattr(locals()[_component], "_js_dist", _js_dist)
setattr(locals()[_component], "_css_dist", _css_dist)
4 changes: 4 additions & 0 deletions dash_auth_plus/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
# oidc auth requires authlib, install with `pip install dash-auth-plus[oidc]`
try:
from .oidc_auth import OIDCAuth, get_oauth
from .clerk_auth import ClerkAuth
import DashAuthComponents
except ModuleNotFoundError:
pass
from .version import __version__, __plotly_dash_auth_version__
Expand All @@ -27,6 +29,8 @@
"public_callback",
"BasicAuth",
"OIDCAuth",
"ClerkAuth",
"DashAuthComponents",
"__version__",
"__plotly_dash_auth_version__",
]
1 change: 0 additions & 1 deletion dash_auth_plus/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ def _protect(self):

@server.before_request
def before_request_auth():

public_routes = get_public_routes(self.app)
public_callbacks = get_public_callbacks(self.app)

Expand Down
Loading