dash-auth


Namedash-auth JSON
Version 2.3.0 PyPI version JSON
download
home_pagehttps://plotly.com/dash
SummaryDash Authorization Package.
upload_time2024-03-19 01:37:41
maintainer
docs_urlNone
authorChristopher Parmer
requires_python>=3.8
licenseMIT
keywords
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            ## Dash Authorization and Login

Docs: [https://dash.plotly.com/authentication](https://dash.plotly.com/authentication)

License: MIT

Tests: [![CircleCI](https://circleci.com/gh/plotly/dash-auth.svg?style=svg)](https://circleci.com/gh/plotly/dash-auth)

For local testing, create a virtualenv, install the dev requirements, and run individual
tests or test classes:

```
python -m venv venv
. venv/bin/activate
pip install -r dev-requirements.txt
python -k ba001
```

Note that Python 3.8 or greater is required.

## Usage

### Basic Authentication

To add basic authentication, add the following to your Dash app:

```python
from dash import Dash
from dash_auth import BasicAuth

app = Dash(__name__)
USER_PWD = {
    "username": "password",
    "user2": "useSomethingMoreSecurePlease",
}
BasicAuth(app, USER_PWD)
```

One can also use an authorization python function instead of a dictionary/list of usernames and passwords:

```python
from dash import Dash
from dash_auth import BasicAuth

def authorization_function(username, password):
    if (username == "hello") and (password == "world"):
        return True
    else:
        return False


app = Dash(__name__)
BasicAuth(app, auth_func = authorization_function)
```

### Public routes

You can whitelist routes from authentication with the `add_public_routes` utility function,
or by passing a `public_routes` argument to the Auth constructor.
The public routes should follow [Flask's route syntax](https://flask.palletsprojects.com/en/2.3.x/quickstart/#routing).

```python
from dash import Dash
from dash_auth import BasicAuth, add_public_routes

app = Dash(__name__)
USER_PWD = {
    "username": "password",
    "user2": "useSomethingMoreSecurePlease",
}
BasicAuth(app, USER_PWD, public_routes=["/"])

add_public_routes(app, public_routes=["/user/<user_id>/public"])
```

NOTE: If you are using server-side callbacks on your public routes, you should also use dash_auth's new `public_callback` rather than the default Dash callback.
Below is an example of a public route and callbacks on a multi-page Dash app using Dash's pages API:

*app.py*
```python
from dash import Dash, html, dcc, page_container
from dash_auth import BasicAuth

app = Dash(__name__, use_pages=True, suppress_callback_exceptions=True)
USER_PWD = {
    "username": "password",
    "user2": "useSomethingMoreSecurePlease",
}
BasicAuth(app, USER_PWD, public_routes=["/", "/user/<user_id>/public"])

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

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

---
*pages/home.py*
```python
from dash import Input, Output, html, register_page
from dash_auth import public_callback

register_page(__name__, "/")

layout = [
    html.H1("Home Page"),
    html.Button("Click me", id="home-button"),
    html.Div(id="home-contents"),
]

# Note the use of public callback here rather than the default Dash callback
@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 "You clicked the button {} times".format(n_clicks)
```

---
*pages/public_user.py*
```python
from dash import html, dcc, register_page

register_page(__name__, path_template="/user/<user_id>/public")

def layout(user_id: str):
    return [
        html.H1(f"User {user_id} (public)"),
        dcc.Link("Authenticated user content", href=f"/user/{user_id}/private"),
    ]
```

---
*pages/private_user.py*
```python
from dash import html, register_page

register_page(__name__, path_template="/user/<user_id>/private")

def layout(user_id: str):
    return [
        html.H1(f"User {user_id} (authenticated only)"),
        html.Div("Members-only information"),
    ]
```

### OIDC Authentication

To add authentication with OpenID Connect, you will first need to set up an OpenID Connect provider (IDP).
This typically requires creating
* An application in your IDP
* Defining the redirect URI for your application, for testing locally you can use http://localhost:8050/oidc/callback
* A client ID and secret for the application

Once you have set up your IDP, you can add it to your Dash app as follows:

```python
from dash import Dash
from dash_auth import OIDCAuth

app = Dash(__name__)

auth = OIDCAuth(app, secret_key="aStaticSecretKey!")
auth.register_provider(
    "idp",
    token_endpoint_auth_method="client_secret_post",
    # Replace the below values with your own
    # NOTE: Do not hardcode your client secret!
    client_id="<my-client-id>",
    client_secret="<my-client-secret>",
    server_metadata_url="<my-idp-.well-known-configuration>",
)
```

Once this is done, connecting to your app will automatically redirect to the IDP login page.

#### Multiple OIDC Providers

For multiple OIDC providers, you can use `register_provider` to add new ones after the OIDCAuth has been instantiated.

```python
from dash import Dash, html
from dash_auth import OIDCAuth
from flask import request, redirect, url_for

app = Dash(__name__)

app.layout = html.Div([
    html.Div("Hello world!"),
    html.A("Logout", href="/oidc/logout"),
])

auth = OIDCAuth(
    app,
    secret_key="aStaticSecretKey!",
    # Set the route at which the user will select the IDP they wish to login with
    idp_selection_route="/login",
)
auth.register_provider(
    "IDP 1",
    token_endpoint_auth_method="client_secret_post",
    client_id="<my-client-id>",
    client_secret="<my-client-secret>",
    server_metadata_url="<my-idp-.well-known-configuration>",
)
auth.register_provider(
    "IDP 2",
    token_endpoint_auth_method="client_secret_post",
    client_id="<my-client-id2>",
    client_secret="<my-client-secret2>",
    server_metadata_url="<my-idp2-.well-known-configuration>",
)

@app.server.route("/login", methods=["GET", "POST"])
def login_handler():
    if request.method == "POST":
        idp = request.form.get("idp")
    else:
        idp = request.args.get("idp")

    if idp is not None:
        return redirect(url_for("oidc_login", idp=idp))

    return """<div>
        <form>
            <div>How do you wish to sign in:</div>
            <select name="idp">
                <option value="IDP 1">IDP 1</option>
                <option value="IDP 2">IDP 2</option>
            </select>
            <input type="submit" value="Login">
        </form>
    </div>"""


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

### User-group-based permissions

`dash_auth` provides a convenient way to secure parts of your app based on user groups.

The following utilities are defined:
* `list_groups`: Returns the groups of the current user, or None if the user is not authenticated.
* `check_groups`: Checks the current user groups against the provided list of groups.
  Available group checks are `one_of`, `all_of` and `none_of`.
  The function returns None if the user is not authenticated.
* `protected`: A function decorator that modifies the output if the user is unauthenticated
  or missing group permission.
* `protected_callback`: A callback that only runs if the user is authenticated
  and with the right group permissions.

NOTE: user info is stored in the session so make sure you define a secret_key on the Flask server
to use this feature.

If you wish to use this feature with BasicAuth, you will need to define the groups for individual
basicauth users:

```python
from dash_auth import BasicAuth

app = Dash(__name__)
USER_PWD = {
    "username": "password",
    "user2": "useSomethingMoreSecurePlease",
}
BasicAuth(
    app,
    USER_PWD,
    user_groups={"user1": ["group1", "group2"], "user2": ["group2"]},
    secret_key="Test!",
)

# You can also use a function to get user groups
def check_user(username, password):
    if username == "user1" and password == "password":
        return True
    if username == "user2" and password == "useSomethingMoreSecurePlease":
        return True
    return False

def get_user_groups(user):
    if user == "user1":
        return ["group1", "group2"]
    elif user == "user2":
        return ["group2"]
    return []

BasicAuth(
    app,
    auth_func=check_user,
    user_groups=get_user_groups,
    secret_key="Test!",
)
```

            

Raw data

            {
    "_id": null,
    "home_page": "https://plotly.com/dash",
    "name": "dash-auth",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": "",
    "keywords": "",
    "author": "Christopher Parmer",
    "author_email": "chris@plotly.com",
    "download_url": "https://files.pythonhosted.org/packages/8b/8a/27c2bfb5f9fa97acedce529f859fab4ad2bae805d3a7f2b87c251ff8a6ee/dash_auth-2.3.0.tar.gz",
    "platform": null,
    "description": "## Dash Authorization and Login\n\nDocs: [https://dash.plotly.com/authentication](https://dash.plotly.com/authentication)\n\nLicense: MIT\n\nTests: [![CircleCI](https://circleci.com/gh/plotly/dash-auth.svg?style=svg)](https://circleci.com/gh/plotly/dash-auth)\n\nFor local testing, create a virtualenv, install the dev requirements, and run individual\ntests or test classes:\n\n```\npython -m venv venv\n. venv/bin/activate\npip install -r dev-requirements.txt\npython -k ba001\n```\n\nNote that Python 3.8 or greater is required.\n\n## Usage\n\n### Basic Authentication\n\nTo add basic authentication, add the following to your Dash app:\n\n```python\nfrom dash import Dash\nfrom dash_auth import BasicAuth\n\napp = Dash(__name__)\nUSER_PWD = {\n    \"username\": \"password\",\n    \"user2\": \"useSomethingMoreSecurePlease\",\n}\nBasicAuth(app, USER_PWD)\n```\n\nOne can also use an authorization python function instead of a dictionary/list of usernames and passwords:\n\n```python\nfrom dash import Dash\nfrom dash_auth import BasicAuth\n\ndef authorization_function(username, password):\n    if (username == \"hello\") and (password == \"world\"):\n        return True\n    else:\n        return False\n\n\napp = Dash(__name__)\nBasicAuth(app, auth_func = authorization_function)\n```\n\n### Public routes\n\nYou can whitelist routes from authentication with the `add_public_routes` utility function,\nor by passing a `public_routes` argument to the Auth constructor.\nThe public routes should follow [Flask's route syntax](https://flask.palletsprojects.com/en/2.3.x/quickstart/#routing).\n\n```python\nfrom dash import Dash\nfrom dash_auth import BasicAuth, add_public_routes\n\napp = Dash(__name__)\nUSER_PWD = {\n    \"username\": \"password\",\n    \"user2\": \"useSomethingMoreSecurePlease\",\n}\nBasicAuth(app, USER_PWD, public_routes=[\"/\"])\n\nadd_public_routes(app, public_routes=[\"/user/<user_id>/public\"])\n```\n\nNOTE: If you are using server-side callbacks on your public routes, you should also use dash_auth's new `public_callback` rather than the default Dash callback.\nBelow is an example of a public route and callbacks on a multi-page Dash app using Dash's pages API:\n\n*app.py*\n```python\nfrom dash import Dash, html, dcc, page_container\nfrom dash_auth import BasicAuth\n\napp = Dash(__name__, use_pages=True, suppress_callback_exceptions=True)\nUSER_PWD = {\n    \"username\": \"password\",\n    \"user2\": \"useSomethingMoreSecurePlease\",\n}\nBasicAuth(app, USER_PWD, public_routes=[\"/\", \"/user/<user_id>/public\"])\n\napp.layout = html.Div(\n    [\n        html.Div(\n            [\n                dcc.Link(\"Home\", href=\"/\"),\n                dcc.Link(\"John Doe\", href=\"/user/john_doe/public\"),\n            ],\n            style={\"display\": \"flex\", \"gap\": \"1rem\", \"background\": \"lightgray\", \"padding\": \"0.5rem 1rem\"},\n        ),\n        page_container,\n    ],\n    style={\"display\": \"flex\", \"flexDirection\": \"column\"},\n)\n\nif __name__ == \"__main__\":\n    app.run_server(debug=True)\n```\n\n---\n*pages/home.py*\n```python\nfrom dash import Input, Output, html, register_page\nfrom dash_auth import public_callback\n\nregister_page(__name__, \"/\")\n\nlayout = [\n    html.H1(\"Home Page\"),\n    html.Button(\"Click me\", id=\"home-button\"),\n    html.Div(id=\"home-contents\"),\n]\n\n# Note the use of public callback here rather than the default Dash callback\n@public_callback(\n    Output(\"home-contents\", \"children\"),\n    Input(\"home-button\", \"n_clicks\"),\n)\ndef home(n_clicks):\n    if not n_clicks:\n        return \"You haven't clicked the button.\"\n    return \"You clicked the button {} times\".format(n_clicks)\n```\n\n---\n*pages/public_user.py*\n```python\nfrom dash import html, dcc, register_page\n\nregister_page(__name__, path_template=\"/user/<user_id>/public\")\n\ndef layout(user_id: str):\n    return [\n        html.H1(f\"User {user_id} (public)\"),\n        dcc.Link(\"Authenticated user content\", href=f\"/user/{user_id}/private\"),\n    ]\n```\n\n---\n*pages/private_user.py*\n```python\nfrom dash import html, register_page\n\nregister_page(__name__, path_template=\"/user/<user_id>/private\")\n\ndef layout(user_id: str):\n    return [\n        html.H1(f\"User {user_id} (authenticated only)\"),\n        html.Div(\"Members-only information\"),\n    ]\n```\n\n### OIDC Authentication\n\nTo add authentication with OpenID Connect, you will first need to set up an OpenID Connect provider (IDP).\nThis typically requires creating\n* An application in your IDP\n* Defining the redirect URI for your application, for testing locally you can use http://localhost:8050/oidc/callback\n* A client ID and secret for the application\n\nOnce you have set up your IDP, you can add it to your Dash app as follows:\n\n```python\nfrom dash import Dash\nfrom dash_auth import OIDCAuth\n\napp = Dash(__name__)\n\nauth = OIDCAuth(app, secret_key=\"aStaticSecretKey!\")\nauth.register_provider(\n    \"idp\",\n    token_endpoint_auth_method=\"client_secret_post\",\n    # Replace the below values with your own\n    # NOTE: Do not hardcode your client secret!\n    client_id=\"<my-client-id>\",\n    client_secret=\"<my-client-secret>\",\n    server_metadata_url=\"<my-idp-.well-known-configuration>\",\n)\n```\n\nOnce this is done, connecting to your app will automatically redirect to the IDP login page.\n\n#### Multiple OIDC Providers\n\nFor multiple OIDC providers, you can use `register_provider` to add new ones after the OIDCAuth has been instantiated.\n\n```python\nfrom dash import Dash, html\nfrom dash_auth import OIDCAuth\nfrom flask import request, redirect, url_for\n\napp = Dash(__name__)\n\napp.layout = html.Div([\n    html.Div(\"Hello world!\"),\n    html.A(\"Logout\", href=\"/oidc/logout\"),\n])\n\nauth = OIDCAuth(\n    app,\n    secret_key=\"aStaticSecretKey!\",\n    # Set the route at which the user will select the IDP they wish to login with\n    idp_selection_route=\"/login\",\n)\nauth.register_provider(\n    \"IDP 1\",\n    token_endpoint_auth_method=\"client_secret_post\",\n    client_id=\"<my-client-id>\",\n    client_secret=\"<my-client-secret>\",\n    server_metadata_url=\"<my-idp-.well-known-configuration>\",\n)\nauth.register_provider(\n    \"IDP 2\",\n    token_endpoint_auth_method=\"client_secret_post\",\n    client_id=\"<my-client-id2>\",\n    client_secret=\"<my-client-secret2>\",\n    server_metadata_url=\"<my-idp2-.well-known-configuration>\",\n)\n\n@app.server.route(\"/login\", methods=[\"GET\", \"POST\"])\ndef login_handler():\n    if request.method == \"POST\":\n        idp = request.form.get(\"idp\")\n    else:\n        idp = request.args.get(\"idp\")\n\n    if idp is not None:\n        return redirect(url_for(\"oidc_login\", idp=idp))\n\n    return \"\"\"<div>\n        <form>\n            <div>How do you wish to sign in:</div>\n            <select name=\"idp\">\n                <option value=\"IDP 1\">IDP 1</option>\n                <option value=\"IDP 2\">IDP 2</option>\n            </select>\n            <input type=\"submit\" value=\"Login\">\n        </form>\n    </div>\"\"\"\n\n\nif __name__ == \"__main__\":\n    app.run_server(debug=True)\n```\n\n### User-group-based permissions\n\n`dash_auth` provides a convenient way to secure parts of your app based on user groups.\n\nThe following utilities are defined:\n* `list_groups`: Returns the groups of the current user, or None if the user is not authenticated.\n* `check_groups`: Checks the current user groups against the provided list of groups.\n  Available group checks are `one_of`, `all_of` and `none_of`.\n  The function returns None if the user is not authenticated.\n* `protected`: A function decorator that modifies the output if the user is unauthenticated\n  or missing group permission.\n* `protected_callback`: A callback that only runs if the user is authenticated\n  and with the right group permissions.\n\nNOTE: user info is stored in the session so make sure you define a secret_key on the Flask server\nto use this feature.\n\nIf you wish to use this feature with BasicAuth, you will need to define the groups for individual\nbasicauth users:\n\n```python\nfrom dash_auth import BasicAuth\n\napp = Dash(__name__)\nUSER_PWD = {\n    \"username\": \"password\",\n    \"user2\": \"useSomethingMoreSecurePlease\",\n}\nBasicAuth(\n    app,\n    USER_PWD,\n    user_groups={\"user1\": [\"group1\", \"group2\"], \"user2\": [\"group2\"]},\n    secret_key=\"Test!\",\n)\n\n# You can also use a function to get user groups\ndef check_user(username, password):\n    if username == \"user1\" and password == \"password\":\n        return True\n    if username == \"user2\" and password == \"useSomethingMoreSecurePlease\":\n        return True\n    return False\n\ndef get_user_groups(user):\n    if user == \"user1\":\n        return [\"group1\", \"group2\"]\n    elif user == \"user2\":\n        return [\"group2\"]\n    return []\n\nBasicAuth(\n    app,\n    auth_func=check_user,\n    user_groups=get_user_groups,\n    secret_key=\"Test!\",\n)\n```\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Dash Authorization Package.",
    "version": "2.3.0",
    "project_urls": {
        "Homepage": "https://plotly.com/dash"
    },
    "split_keywords": [],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "1942d636affcd8074f7f48156a99d8854e5be951d8601dcb4178231c92c988d1",
                "md5": "0f246d08c51fe10999df7dd79953b396",
                "sha256": "2fa72d4ee128b4f9cf0c958157a986ffcd0b93d43b54d64e3cef5976ab7e0abe"
            },
            "downloads": -1,
            "filename": "dash_auth-2.3.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "0f246d08c51fe10999df7dd79953b396",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 14428,
            "upload_time": "2024-03-19T01:37:37",
            "upload_time_iso_8601": "2024-03-19T01:37:37.878906Z",
            "url": "https://files.pythonhosted.org/packages/19/42/d636affcd8074f7f48156a99d8854e5be951d8601dcb4178231c92c988d1/dash_auth-2.3.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "8b8a27c2bfb5f9fa97acedce529f859fab4ad2bae805d3a7f2b87c251ff8a6ee",
                "md5": "a549c2880434b9539e7d42c0823d5cda",
                "sha256": "72df43248c15e121f8d5d710981e880deb2df546b564f2951a10ca50d7d92f6d"
            },
            "downloads": -1,
            "filename": "dash_auth-2.3.0.tar.gz",
            "has_sig": false,
            "md5_digest": "a549c2880434b9539e7d42c0823d5cda",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 17727,
            "upload_time": "2024-03-19T01:37:41",
            "upload_time_iso_8601": "2024-03-19T01:37:41.275627Z",
            "url": "https://files.pythonhosted.org/packages/8b/8a/27c2bfb5f9fa97acedce529f859fab4ad2bae805d3a7f2b87c251ff8a6ee/dash_auth-2.3.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-03-19 01:37:41",
    "github": false,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "lcname": "dash-auth"
}
        
Elapsed time: 0.90140s