Authentication + Encrypted Passwords

Does Dash-Auth support the use of encrypted passwords with any of the authentication methods? For obvious reasons, I’m very hesitant to store user passwords in any sort of plaintext format.

I was also wondering the same. It would be very nice if we could use werkzeug.security to check hashed passwords.

I’ve taken a look at the dash-auth repository (https://github.com/plotly/dash-auth) and there is an open pull request for adding hashes to the passwords (https://github.com/plotly/dash-auth/pull/22), so we may get at least some added security soon.

Note that with basic auth, these shouldn’t be “user passwords” as users can’t create accounts. So, there isn’t any risk of a user re-using their gmail password in their dash app, as it is up to the dash developer / admin to create the username/password pair and share it directly with their users. What you can do instead, which will have the same security as hashing, is generate long, random passwords that are only used for your particular app. For example, create a password with

''.join(
    random.SystemRandom().choice(
        string.ascii_uppercase + string.digits) for _ in range(N))

Some more discussion here: Hash Option for BasicAuth Class by jn7dt · Pull Request #22 · plotly/dash-auth · GitHub

1 Like
from dash_auth.auth import Auth
import flask, base64, hashlib
from types import MethodType
class EncryptedAuth(Auth):
    def __init__(self, app, username_password_list):
        Auth.__init__(self, app)
        self._users = username_password_list \
            if isinstance(username_password_list, dict) \
            else {k: v for k, v in username_password_list}

    def is_authorized(self):
        header = flask.request.headers.get('Authorization', None)
        if not header:
            return False
        username_password = base64.b64decode(header.split('Basic ')[1])
        username_password_utf8 = username_password.decode('utf-8')
        username, password = username_password_utf8.split(':')
        return self._users.get(username) == hashlib.new('sha224', password.encode()).hexdigest()

    def login_request(self):
        return flask.Response(
            'Login Required',
            headers={'WWW-Authenticate': 'Basic realm="User Visible Realm"'},
            status=401)

    def auth_wrapper(self, f):
        def wrap(*args, **kwargs):
            if not self.is_authorized():
                return flask.Response(status=403)

            response = f(*args, **kwargs)
            return response
        return wrap

    def index_auth_wrapper(self, original_index):
        def wrap(*args, **kwargs):
            if self.is_authorized():
                return original_index(*args, **kwargs)
            else:
                return self.login_request()
        return wrap

For those who are looking for workaround, this works for me.
User the class EncryptedAuth the same way you use BasicAuth.