Giter Club home page Giter Club logo

dash-auth's Introduction

Dash Authorization and Login

Docs: https://dash.plotly.com/authentication

License: MIT

Tests: CircleCI

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:

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:

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.

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

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

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

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

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:

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.

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:

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!",
)

dash-auth's People

Contributors

alexcjohnson avatar amarvin avatar charleyferrari avatar chriddyp avatar cldougl avatar cpsievert avatar davecap avatar gvwilson avatar jbampton avatar marc-andre-rivet avatar michaelbabyn avatar michaels0m avatar renaudln avatar scjody avatar shammamah-zz avatar t4rk1n avatar wwerst avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

dash-auth's Issues

Plotly POST operations turn to 403 after a minute with auth activated

When using the basic HTTP Auth with following code:

VALID_USERNAME_PASSWORD_PAIRS = [('example', 'hunter2')]
auth = dash_auth.BasicAuth(app, VALID_USERNAME_PASSWORD_PAIRS)

I get 403 responses when clicking on the application's elements that have callbacks attached, after a while. The problem disappears completely if I comment-out the above lines of code.

What is strange is that the problem manifests itself only after clicking a certain number of times on elements in the dash app that have callbacks, such that the server stdout looks like this:

127.0.0.1 - - [31/Jul/2018 21:52:10] "POST /_dash-update-component HTTP/1.1" 200 -
127.0.0.1 - - [31/Jul/2018 21:52:10] "POST /_dash-update-component HTTP/1.1" 200 -
127.0.0.1 - - [31/Jul/2018 21:52:13] "POST /_dash-update-component HTTP/1.1" 200 -
127.0.0.1 - - [31/Jul/2018 21:52:19] "POST /_dash-update-component HTTP/1.1" 200 -
127.0.0.1 - - [31/Jul/2018 21:52:22] "POST /_dash-update-component HTTP/1.1" 403 -
127.0.0.1 - - [31/Jul/2018 21:52:25] "POST /_dash-update-component HTTP/1.1" 403 -

The above log is produced by repeatedly clicking on the same element.

Once a call to /_dash-update-component turns to 403 it remains like that forever, until the user reloads the whole web page.

basic_auth username attribute

adding:

self._username = username

to BasicAuth.is_authorized
allows dash app to use the username for special content/features

No module named 'plotly_auth'

I am using Anaconda with a python 3.4 virtual environment.
When I try to import the module dash_auth, I get the following error:

Traceback (most recent call last):
  File "inputs.py", line 6, in <module>
    import dash_auth as dauth
  File "C:\Users\pierre\AppData\Local\Continuum\Anaconda2\envs\py34env\lib\site
-packages\dash_auth\__init__.py", line 1, in <module>
    from plotly_auth import PlotlyAuth
ImportError: No module named 'plotly_auth'

Although Python tells me that there no module named 'plotly_auth', when I look in the dash_auth folder I can see the plotly_auth file.
I don't have this issue when I import dash_auth in a Python 2.7 environment.

BasicAuth - 'TypeError: โ€˜typeโ€™ object is not subscriptable '.

I have incorporated an authentication functionality into my code based off of the official Dash-Auth docs. This app is to be hosted on heroku. Couple of things happen (Code is below) :

app = dash.Dash('app',server=server)
app = dash.Dash('auth')
auth = dash_auth.BasicAuth(
    app,
    (('abcde','1234',),)
)

Locally, the authentication works flawlessly, except that once you login into the app it saves the login info as cookies. Hence, if you would refresh the page or probably paste the link to a new window it wont ask for the login info again unless and until you clear you cookies in the browser.

Once the app is pushed to the heroku master it successfully deploys it, unfortunalely the app does not open due to an application error. On checking the heroku logs the error shown is below. This error is not shown if hosted locally.

'TypeError: โ€˜typeโ€™ object is not subscriptable '.

As per the post I have removed dash.ly so im sure the error has nothing to do with it. As per this post around the 8th comment someone raises the issue of the login but the reply is not definitive.

Questions about Basic Auth Implementation

I am using dash-auth (Basic Auth) in my dash app. I want to improve the login experience for the user and want to provide change password etc functionality.

For this, I wanted to convert the login dialog into a simple HTML login form with a link to change password. I tried to reverse engineer and explore the code in dash-auth module but didn't get much luck. Here are my primary questions

  1. Where is the JS code which receives the username and password from login dialog and submit to server ?
  2. Where is the code which sets the Authorization header before every request to the server?
  3. At client side where are we storing the value set in Authorization header ?
  4. Where is the code which renders the dialog for username and password.

Please let me know if anything else I would need in order to implement above use case or if there is a better existing implementation which I can reuse.

Thanks!

TypeError: a bytes-like object is required, not 'str'

Hi there,

I ran into some error while trying to reproduce the "Basic Auth Example" as detailed here. I installed dash, dash-auth and plotly, and copied and pasted the auth example into a script called test.py. The error that I ran into is as follows:

> python test.py
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 159-397-137
 * Running on http://127.0.0.1:8050/ (Press CTRL+C to quit)
127.0.0.1 - - [18/Aug/2017 10:29:49] "GET / HTTP/1.1" 401 -
127.0.0.1 - - [18/Aug/2017 10:29:55] "GET / HTTP/1.1" 500 -
Traceback (most recent call last):
  File "C:\ProgramData\Anaconda3\lib\site-packages\flask\app.py", line 1997, in __call__
    return self.wsgi_app(environ, start_response)
  File "C:\ProgramData\Anaconda3\lib\site-packages\flask\app.py", line 1985, in wsgi_app
    response = self.handle_exception(e)
  File "C:\ProgramData\Anaconda3\lib\site-packages\flask\app.py", line 1540, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "C:\ProgramData\Anaconda3\lib\site-packages\flask\_compat.py", line 33, in reraise
    raise value
  File "C:\ProgramData\Anaconda3\lib\site-packages\flask\app.py", line 1982, in wsgi_app
    response = self.full_dispatch_request()
  File "C:\ProgramData\Anaconda3\lib\site-packages\flask\app.py", line 1614, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "C:\ProgramData\Anaconda3\lib\site-packages\flask\app.py", line 1517, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "C:\ProgramData\Anaconda3\lib\site-packages\flask\_compat.py", line 33, in reraise
    raise value
  File "C:\ProgramData\Anaconda3\lib\site-packages\flask\app.py", line 1612, in full_dispatch_request
    rv = self.dispatch_request()
  File "C:\ProgramData\Anaconda3\lib\site-packages\flask\app.py", line 1598, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "C:\ProgramData\Anaconda3\lib\site-packages\dash_auth\auth.py", line 19, in wrap_index
    if self.is_authorized():
  File "C:\ProgramData\Anaconda3\lib\site-packages\dash_auth\basic_auth.py", line 16, in is_authorized
    header.split('Basic ')[1]).split(':')
TypeError: a bytes-like object is required, not 'str'
127.0.0.1 - - [18/Aug/2017 10:29:55] "GET /?__debugger__=yes&cmd=resource&f=style.css HTTP/1.1" 200 -
127.0.0.1 - - [18/Aug/2017 10:29:55] "GET /?__debugger__=yes&cmd=resource&f=jquery.js HTTP/1.1" 200 -
127.0.0.1 - - [18/Aug/2017 10:29:55] "GET /?__debugger__=yes&cmd=resource&f=debugger.js HTTP/1.1" 200 -
127.0.0.1 - - [18/Aug/2017 10:29:56] "GET /?__debugger__=yes&cmd=resource&f=ubuntu.ttf HTTP/1.1" 200 -
127.0.0.1 - - [18/Aug/2017 10:29:56] "GET /?__debugger__=yes&cmd=resource&f=console.png HTTP/1.1" 200 -

For reference, I'm running this with Python 3 on Windows 7 Enterprise, with the following versions of dash and plotly:

dash.__version__
Out[7]: '0.17.7'

plotly.__version__
Out[8]: '2.0.12'

I'd love some help in figuring out what's going on!

Thanks,
Anthony

logout from basic auth

Is there any way to logout from basic auth in these examples? The only way I can possibly think is if you did a redirect with a 401 status code. I'm just looking for a simple way to circumvent this problem for now.

I've looked at the docs on https://dash.plot.ly/authentication and understand that one of the limitations of dash_auth.BasicAuth is that users cannot logout of applications, but there must be a way to use flask routing to return a 401 unauthorized.

If anyone has figured out a way around the problem, I'd love to hear some suggestions!

dash==0.21.2 raise KeyError: 'index'

follow the example code, following error raised

$ python app.py
Traceback (most recent call last):
  File "app.py", line 49, in <module>
    APP_URL
  File "/home/tony/pi/lib/python3.6/site-packages/dash_auth/plotly_auth.py", line 17, in __init__
    Auth.__init__(self, app)
  File "/home/tony/pi/lib/python3.6/site-packages/dash_auth/auth.py", line 12, in __init__
    self._overwrite_index()
  File "/home/tony/pi/lib/python3.6/site-packages/dash_auth/auth.py", line 16, in _overwrite_index
    original_index = self.app.server.view_functions['index']
KeyError: 'index'

Encryption with https

Hi, maybe a dumb question but: if I use this on a Dash app where i've enabled ssl on the Flask server, does this mean the login details passed around in this package are always encrypted end to end?

Thanks,
Z

Basic auth prompt does not appear on sub pages, only on main page, browser displays blank page

I have a multi-page dash app using Dash Pages that I have added basic authentication to as described here.

The issue I have is that the prompt for authentication only appears when I visit the main/home page. If I am not already logged in and visit one of the sub pages by entering its URL directly, no authentication prompt appears and the browser just displays a blank page.

I would expect the authentication prompt to also appear when visiting one of the sub pages directly.

State of integration tests

The integrations tests currently fails with errors more often than not. The cause of the errors is mostly related to opening/closing the authentication dialog. The errors happen in the _login_flow method at the last line where it wait for the `'input[name="allow"]' element, sometime it's the other elements find that fails.

def _login_flow(self, username, pw):
try:
el = self.wait_for_element_by_css_selector(
'#dash-auth--login__container')
except Exception as e:
print(self.wait_for_element_by_css_selector('body').html)
raise e
self.wait_for_element_by_css_selector(
'#dash-auth--login__button').click()
switch_windows(self.driver)
self.wait_for_element_by_css_selector(
'#js-auth-modal-signin-username'
).send_keys(username)
self.wait_for_element_by_css_selector(
'#js-auth-modal-signin-password'
).send_keys(pw)
self.wait_for_element_by_css_selector(
'#js-auth-modal-signin-submit').click()
time.sleep(3)
# wait for oauth screen
self.wait_for_element_by_css_selector('input[name="allow"]').click()

When running the tests locally one by one, the tests passes. When running all the tests locally they passes most of the time but sometime the hangup/slowdown in the _login_flow can be seen, it takes a while for the elements to show.

Tried:

  • Removed tox
  • Use threading instead of multi-process
  • Use selenium WebDriverWait api
  • Improved the js bundle/login index for easier element access.
  • Use flaky to retry failed tests.
  • Increased the circleci container performance tier in light of circle support suggestion that selenium is running out of memory, did not help.
  • Various sleep time modification.
  • Use same selenium browser instance for each test instead opening/closing a new one.
  • Tried changing _login_flow find elements method by selectors, xpath, id.

Things to try:

  • put back tox.
  • put back multi-process.
  • refactor _login_flow

Something I am trying in the long run:

  • refactor the tests to use the subprocess pytest fixture https://github.com/T4rk1n/pytest-dash/pull/8
    • Running as an external process should in theory free up selenium performance.
    • Still trying to make it work under circleci, worked on linux/windows but circle was hanging.
    • Refactor the tests to use pytests fixtures instead of unittest.TestCase.

cc @plotly/dash @scjody

Loging to another website using dash-auth

I am trying to create a dash app where I want to log in to another website. I have an API for the website. I can write a query with username and password for the website login. How can I use dash-auth to ask user to enter the username and password?

Multi-app wraps all routes.

If you have a multiple dash apps that share the same flask server, dash-auth will wraps all routes of all apps, even if you use dash-auth on only one.

Specify hashing to use encrypted database

My understanding is that the current implementation reads in login/pw pairs where it will do a direct comparison of the pw in plain text with whatever is entered. However, I would like to connect to a db which stored hashed passwords and then hash the user input and compare.

How hard would it be to implement functionality to also define an arbitrary python function which the user can define which will be used for this hashing and comparison?

Support for IE 10+ with fetch polyfill

I noticed a TODO in the code for this and was wondering if there have been plans to integrate a fetch polyfill. We have some end-users who may encounter the OAuth2 flow through IE. Does the rest of the Plotly ecosystem support IE well?

BasicAuth for only page in a Dash App

Hi,

I've managed to include BasicAuth for my Dash app and it works fine. I have 4 pages in my app and i want to use include authentication for only one of those pages and not the entire app. Is that possible ?

Thanks !

AWS deployment - Login username/password details not accepted

Hi . I have been trying to deploy the dash basic login.

Whilst it works fine locally, once deployed to AWS ( using Beanstalk), the login window keep appearing after entering the right login details.
The login details are never validated.

I have successfully deployed the same app without the loging part.
I also tried to deploy the dash_login example with no luck ( again it runs fine locally, and the same problem occurs).

The logs are below. Any help much appreciated.
I have managed to deploy a basic Flask login module but I have yet to learn how to mask urls, etc.
I would rather use the dash_auth module if possible.

Thanks
PD: After reading in the community forum I found that the problem was also reported when deploying in digital ocean


/var/log/httpd/error_log

[Sun Sep 02 01:53:54.118596 2018] [suexec:notice] [pid 3088] AH01232: suEXEC mechanism enabled (wrapper: /usr/sbin/suexec)
[Sun Sep 02 01:53:54.133927 2018] [http2:warn] [pid 3088] AH10034: The mpm module (prefork.c) is not supported by mod_http2. The mpm determines how things are processed in your server. HTTP/2 has more demands in this regard and the currently selected mpm will just not do. This is an advisory warning. Your server will continue to work, but the HTTP/2 protocol will be inactive.
[Sun Sep 02 01:53:54.133937 2018] [http2:warn] [pid 3088] AH02951: mod_ssl does not seem to be enabled
[Sun Sep 02 01:53:54.134327 2018] [lbmethod_heartbeat:notice] [pid 3088] AH02282: No slotmem from mod_heartmonitor
[Sun Sep 02 01:53:54.134369 2018] [:warn] [pid 3088] mod_wsgi: Compiled for Python/3.6.2.
[Sun Sep 02 01:53:54.134372 2018] [:warn] [pid 3088] mod_wsgi: Runtime using Python/3.6.5.
[Sun Sep 02 01:53:54.136211 2018] [mpm_prefork:notice] [pid 3088] AH00163: Apache/2.4.33 (Amazon) mod_wsgi/3.5 Python/3.6.5 configured -- resuming normal operations
[Sun Sep 02 01:53:54.136226 2018] [core:notice] [pid 3088] AH00094: Command line: '/usr/sbin/httpd -D FOREGROUND'
[Sun Sep 02 01:55:22.308807 2018] [mpm_prefork:notice] [pid 3088] AH00169: caught SIGTERM, shutting down
[Sun Sep 02 01:55:23.394411 2018] [suexec:notice] [pid 3534] AH01232: suEXEC mechanism enabled (wrapper: /usr/sbin/suexec)
[Sun Sep 02 01:55:23.409282 2018] [so:warn] [pid 3534] AH01574: module wsgi_module is already loaded, skipping
[Sun Sep 02 01:55:23.411264 2018] [http2:warn] [pid 3534] AH10034: The mpm module (prefork.c) is not supported by mod_http2. The mpm determines how things are processed in your server. HTTP/2 has more demands in this regard and the currently selected mpm will just not do. This is an advisory warning. Your server will continue to work, but the HTTP/2 protocol will be inactive.
[Sun Sep 02 01:55:23.411274 2018] [http2:warn] [pid 3534] AH02951: mod_ssl does not seem to be enabled
[Sun Sep 02 01:55:23.411814 2018] [lbmethod_heartbeat:notice] [pid 3534] AH02282: No slotmem from mod_heartmonitor
[Sun Sep 02 01:55:23.411875 2018] [:warn] [pid 3534] mod_wsgi: Compiled for Python/3.6.2.
[Sun Sep 02 01:55:23.411879 2018] [:warn] [pid 3534] mod_wsgi: Runtime using Python/3.6.5.
[Sun Sep 02 01:55:23.414057 2018] [mpm_prefork:notice] [pid 3534] AH00163: Apache/2.4.33 (Amazon) mod_wsgi/3.5 Python/3.6.5 configured -- resuming normal operations
[Sun Sep 02 01:55:23.414071 2018] [core:notice] [pid 3534] AH00094: Command line: '/usr/sbin/httpd -D FOREGROUND'
[Sun Sep 02 01:55:27.036415 2018] [:error] [pid 3539] /opt/python/run/venv/local/lib/python3.6/site-packages/plotly/tools.py:102: UserWarning:
[Sun Sep 02 01:55:27.036444 2018] [:error] [pid 3539]
[Sun Sep 02 01:55:27.036448 2018] [:error] [pid 3539] Looks like you don't have 'read-write' permission to your 'home' ('') directory or to our '/.plotly' directory. That means plotly's python api can't setup local configuration files. No problem though! You'll just have to sign-in using 'plotly.plotly.sign_in()'. For help with that: 'help(plotly.plotly.sign_in)'.
[Sun Sep 02 01:55:27.036451 2018] [:error] [pid 3539] Questions? Visit https://support.plot.ly
[Sun Sep 02 01:55:27.036454 2018] [:error] [pid 3539]


/opt/python/log/supervisord.log

2018-09-02 01:53:52,915 CRIT Supervisor running as root (no user in config file)
2018-09-02 01:53:52,957 INFO RPC interface 'supervisor' initialized
2018-09-02 01:53:52,957 CRIT Server 'unix_http_server' running without any HTTP authentication checking
2018-09-02 01:53:52,957 INFO supervisord started with pid 3002
2018-09-02 01:53:53,963 INFO spawned: 'httpd' with pid 3088
2018-09-02 01:53:55,118 INFO success: httpd entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
2018-09-02 01:55:22,321 INFO stopped: httpd (exit status 0)
2018-09-02 01:55:23,326 INFO spawned: 'httpd' with pid 3534
2018-09-02 01:55:24,393 INFO success: httpd entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)

unable to authenticate with user and password from tutorial

I set up my dash-auth exactly how the sample code shows in the dash.plot.ly authentication page. However, when entering in the 'hello' and 'world' username and password, it simply won't log in to the demo graph page. Any ideas?

My dash versions are exactly as shown in the tutorial:
dash - 1.0.2
dash-auth = 1.2.0

This is the code from the tutorial and I am running python index.py

`import dash
import dash_auth
import dash_core_components as dcc
import dash_html_components as html
import plotly

Keep this out of source code repository - save in a file or a database

VALID_USERNAME_PASSWORD_PAIRS = {
'hello': 'world'
}

external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

app = dash.Dash(name, external_stylesheets=external_stylesheets)
auth = dash_auth.BasicAuth(
app,
VALID_USERNAME_PASSWORD_PAIRS
)

app.layout = html.Div([
html.H1('Welcome to the app'),
html.H3('You are successfully authorized'),
dcc.Dropdown(
id='dropdown',
options=[{'label': i, 'value': i} for i in ['A', 'B']],
value='A'
),
dcc.Graph(id='graph')
], className='container')

@app.callback(
dash.dependencies.Output('graph', 'figure'),
[dash.dependencies.Input('dropdown', 'value')])
def update_graph(dropdown_value):
return {
'layout': {
'title': 'Graph of {}'.format(dropdown_value),
'margin': {
'l': 20,
'b': 20,
'r': 10,
't': 60
}
},
'data': [{'x': [1, 2, 3], 'y': [4, 1, 2]}]
}

if name == 'main':
app.run_server(debug=True)`

Browser Compatibility Problem with Chrome on Windows System.

I used the following version of the libraries. It works for OS Chrome/Safari browser and Windows IE browser, but won't work for the Windows Chrome browser. It just shows the error message saying that "Login required" and didn't have the pop up window to enter the username and password. I'm wondering if there's a compatibility problem with Window Chrome browser. Thanks for the help!

attrs==20.1.0
beautifulsoup4==4.9.1
Brotli==1.0.7
certifi==2020.6.20
cffi==1.14.2
chardet==3.0.4
click==7.1.2
cryptography==3.0
dash==1.14.0
dash-auth==1.3.2
dash-bootstrap-components==0.10.3
dash-core-components==1.10.2
dash-enterprise-auth==0.0.4
dash-html-components==1.0.3
dash-renderer==1.6.0
dash-table==4.9.0
Flask==1.1.2
Flask-Compress==1.5.0
Flask-SeaSurf==0.2.2
future==0.17.1
idna==2.10
itsdangerous==1.1.0
Jinja2==2.11.2
json5==0.9.1
jsonschema==3.2.0
MarkupSafe==1.1.1
mspider==0.2.5
numpy==1.19.1
pandas==1.1.0
Pillow==7.1.2
plotly==4.1.1
pycparser==2.20
pyOpenSSL==19.1.0
pyrsistent==0.16.0
python-dateutil==2.8.0
pytz==2020.1
requests==2.24.0
retrying==1.3.3
scidownl==0.2.7
six==1.15.0
soupsieve==2.0.1
termcolor==1.1.0
tqdm==4.46.0
ua-parser==0.10.0
urllib3==1.25.10
Werkzeug==1.0.1
wget==3.2

Is it possible to do <username>:<password> in URL for dash-auth basic http authentication?

I came across this in the test code which leads me to believe it is not, but wondering if there is any update on when dash routes might support this or if there is another way around it (besides reloading the URL again).

Currently behavior is page returns "Loading..." to the browser and I see this from the server (before reloading):

$ python3 test_dash_auth.py
http://hello:wor:ld@barolo:47125
Dash is running on http://barolo:47125/

 * Serving Flask app "test_dash_auth" (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://barolo:47125/ (Press CTRL+C to quit)
192.168.0.186 - - [30/Sep/2020 21:15:35] "GET / HTTP/1.1" 401 -
192.168.0.186 - - [30/Sep/2020 21:15:35] "GET / HTTP/1.1" 200 -
192.168.0.186 - - [30/Sep/2020 21:15:35] "GET /assets/bWLwgP.css?m=1601518562.6601 HTTP/1.1" 403 -
192.168.0.186 - - [30/Sep/2020 21:15:35] "GET /_dash-component-suites/dash_renderer/[email protected]_8_2m1601187996.8.7.min.js HTTP/1.1" 403 -
192.168.0.186 - - [30/Sep/2020 21:15:35] "GET /_dash-component-suites/dash_renderer/[email protected]_8_2m1601187996.13.0.min.js HTTP/1.1" 403 -
192.168.0.186 - - [30/Sep/2020 21:15:35] "GET /_dash-component-suites/dash_renderer/[email protected]_8_2m1601187996.13.0.min.js HTTP/1.1" 403 -
192.168.0.186 - - [30/Sep/2020 21:15:35] "GET /_dash-component-suites/dash_renderer/[email protected]_8_2m1601187996.7.2.min.js HTTP/1.1" 403 -
192.168.0.186 - - [30/Sep/2020 21:15:35] "GET /_dash-component-suites/dash_html_components/dash_html_components.v1_1_1m1599668561.min.js HTTP/1.1" 403 -
192.168.0.186 - - [30/Sep/2020 21:15:35] "GET /_dash-component-suites/dash_core_components/dash_core_components.v1_12_1m1600343520.min.js HTTP/1.1" 403 -
192.168.0.186 - - [30/Sep/2020 21:15:35] "GET /_dash-component-suites/dash_core_components/dash_core_components-shared.v1_12_1m1600343520.js HTTP/1.1" 403 -
192.168.0.186 - - [30/Sep/2020 21:15:35] "GET /_dash-component-suites/dash_renderer/dash_renderer.v1_8_2m1601187996.min.js HTTP/1.1" 403 -
192.168.0.186 - - [30/Sep/2020 21:15:35] "GET /_dash-component-suites/dash_renderer/[email protected]_8_2m1601187996.13.0.min.js HTTP/1.1" 403 -
192.168.0.186 - - [30/Sep/2020 21:15:35] "GET /_dash-component-suites/dash_renderer/[email protected]_8_2m1601187996.13.0.min.js HTTP/1.1" 403 -
192.168.0.186 - - [30/Sep/2020 21:15:35] "GET /_dash-component-suites/dash_renderer/[email protected]_8_2m1601187996.7.2.min.js HTTP/1.1" 403 -
192.168.0.186 - - [30/Sep/2020 21:15:35] "GET /_dash-component-suites/dash_html_components/dash_html_components.v1_1_1m1599668561.min.js HTTP/1.1" 403 -
192.168.0.186 - - [30/Sep/2020 21:15:35] "GET /_dash-component-suites/dash_core_components/dash_core_components.v1_12_1m1600343520.min.js HTTP/1.1" 403 -
192.168.0.186 - - [30/Sep/2020 21:15:35] "GET /_dash-component-suites/dash_core_components/dash_core_components-shared.v1_12_1m1600343520.js HTTP/1.1" 403 -
192.168.0.186 - - [30/Sep/2020 21:15:35] "GET /_dash-component-suites/dash_renderer/dash_renderer.v1_8_2m1601187996.min.js HTTP/1.1" 403 -
192.168.0.186 - - [30/Sep/2020 21:15:35] "GET /assets/favicon.ico?m=1601263521.8334005 HTTP/1.1" 403 -

Basic Auth fails if password contains ":"

I get the following error in dash_auth/basic_auth.py if I use a password that contains a colon:
line 19, in is_authorized username, password = username_password_utf8.split(':') ValueError: too many values to unpack (expected 2)

A solution could be to keep the user/pass pair as a dictionary or convert to a dictionary and covert back.

Upgrade to Dash 2.0

It seems like this imports dash_html_components, which is deprecated. So using this library with dash 2.0 creates a warning.

Updating the imports to dash 2.0 style would be nice.

Change passwords

Test user passwords weren't rolled for dash-test-viewer (and maybe others??) after #80 which removed them from being hardcoded. Guessing Plotly team would be interested in this. I think you should roll this test user password.

Use sharekey

  • Front end request to do the auth is made here:
    body: JSON.stringify({access_token})
    }).then(res => {
    this.setState({loginRequest: {status: res.status}});
    return res.json().then(json => {
    this.setState({loginRequest: {
    status: res.status,
    content: json
    }});
    }).then(() => {
    return fetch(`${requests_pathname_prefix}${IS_AUTHORIZED_PATHNAME}`, {
    method: 'GET',
    credentials: 'same-origin',
    headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'X-CSRFToken': cookie.parse(document.cookie)._csrf_token
    }
    }).then(res => {
    this.setState({
    authorizationRequest: {
    status: res.status
    }
    });
    })
    });
    }).catch(err => {
  • Which makes the API call to the auth backend here:
    def login_api(self):
    oauth_token = flask.request.get_json()['access_token']
    res = api_requests.get(
    '/v2/users/current',
    headers={'Authorization': 'Bearer {}'.format(oauth_token)},
    )
    try:
    res.raise_for_status()
    except Exception as e:
    print(res.content)
    raise e
    response = flask.Response(
    json.dumps(res.json()),
    mimetype='application/json',
    status=res.status_code
    )
    self.set_cookie(
    response=response,
    name='plotly_oauth_token',
    value=oauth_token,
    max_age=None
    )
    return response
  • The frontend is built with yarn: yarn run build inside js:
    "build": "./node_modules/.bin/webpack"
  • Integration tests are here: https://github.com/plotly/dash-auth/blob/174c547c8c83c6195660c6e4a2b7548b8bc718c7/tests/test_plotly_auth_integration.py

[Feature Request] More documentaion for new dash-auth 2.2.0 package

Dear,

I would like to request more documentation for the new version of Dash-Auth (version 2.2.0). The explanation of defining public routes and public callbacks is not clear in the current documentation. Although I have reviewed the documentation, it seems insufficient.

For example, I don't have public routes, but I do have some callbacks to open modals or new pages from a sidebar. I am encountering an error related to the line if inp["property"] == "pathname", and when I replace it with if isinstance(inp, dict) and inp.get("property") == "pathname", the issue goes away. However, I understand that modifying the library in this way is not the correct approach.

It would be immensely helpful to have additional examples for debugging purposes.

Thank you.

Support for additional OAuth providers

Please allow support for, or document how to add, third-party OAuth providers. E.g. logging in with:

  • GitHub
  • Google

While it is understandable that Plot.ly needs to build a business model around Dash, it would be appreciated that it not preclude federated authentication for Dash apps.

Plotly OAuth auth.logout() Issue

There seems to be an issue with the implementation of the Plotly OAuth auth.logout() function.

The issue shows up when running:

import dash
import dash_auth
import dash_html_components as html
import os

os.environ['PLOTLY_USERNAME'] = '<your_username>'
os.environ['PLOTLY_API_KEY'] = '<your_api_key>'

APP_NAME = 'app_name'
APP_URL = 'http://127.0.0.1:8050/'
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
server = app.server

auth = dash_auth.PlotlyAuth(app, APP_NAME, 'private', APP_URL)

app.layout = html.Div([
    html.Div('TEST', id='div'),

    auth.create_logout_button(
            label='Sign out',
            redirect_to='https://google.com')
])

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

Here is the debug:

Traceback (most recent call last):
  File "/anaconda3/envs/nanobox_env/lib/python3.7/site-packages/flask/app.py", line 2309, in __call__
    return self.wsgi_app(environ, start_response)
  File "/anaconda3/envs/nanobox_env/lib/python3.7/site-packages/flask/app.py", line 2295, in wsgi_app
    response = self.handle_exception(e)
  File "/anaconda3/envs/nanobox_env/lib/python3.7/site-packages/flask/app.py", line 1741, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/anaconda3/envs/nanobox_env/lib/python3.7/site-packages/flask/_compat.py", line 35, in reraise
    raise value
  File "/anaconda3/envs/nanobox_env/lib/python3.7/site-packages/flask/app.py", line 2292, in wsgi_app
    response = self.full_dispatch_request()
  File "/anaconda3/envs/nanobox_env/lib/python3.7/site-packages/flask/app.py", line 1815, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/anaconda3/envs/nanobox_env/lib/python3.7/site-packages/flask/app.py", line 1718, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/anaconda3/envs/nanobox_env/lib/python3.7/site-packages/flask/_compat.py", line 35, in reraise
    raise value
  File "/anaconda3/envs/nanobox_env/lib/python3.7/site-packages/flask/app.py", line 1813, in full_dispatch_request
    rv = self.dispatch_request()
  File "/anaconda3/envs/nanobox_env/lib/python3.7/site-packages/flask/app.py", line 1799, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/anaconda3/envs/nanobox_env/lib/python3.7/site-packages/dash_auth/oauth.py", line 187, in wrap
    response = f(*args, **kwargs)
  File "/anaconda3/envs/nanobox_env/lib/python3.7/site-packages/dash/dash.py", line 962, in dispatch
    return self.callback_map[target_id]['callback'](*args)
  File "/anaconda3/envs/nanobox_env/lib/python3.7/site-packages/dash/dash.py", line 902, in add_context
    output_value = func(*args, **kwargs)
  File "/anaconda3/envs/nanobox_env/lib/python3.7/site-packages/dash_auth/plotly_auth.py", line 224, in _on_log_out
    self.logout()
  File "/anaconda3/envs/nanobox_env/lib/python3.7/site-packages/dash_auth/plotly_auth.py", line 188, in logout
    invalidation_resp.raise_for_status()
  File "/anaconda3/envs/nanobox_env/lib/python3.7/site-packages/requests/models.py", line 940, in raise_for_status
    raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 502 Server Error: Bad Gateway for url: https://plot.ly/Auth/o/revoke_token/

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.