Creating a Google OAuth login for Dash

I am creating a package to handle Google OAuth but I am not sure how secure it is. I am not sure if this is the best forum for this question, so please let me know if I am overstepping and I will submit a question on StackOverflow.

I was looking at the new updates to dash-auth but it did not really fit my needs - i.e., I need a customized solution due to how my app functions. I have set everything up in Google Cloud for the authentication and it is set as internal; however, not every employee will have access to my Dash App. I have an Azure Cosmos DB that stores user permissions. So essentially, I am using Flask to handle the authentication and when the user logs in, I check the Cosmos DB to make sure the user should have access and, if they do have access, check the resources/pages they should have access to.

I am just not sure how secure my app is. In Azure, I have set up my lists of allow/deny rules to only allow access from certain IPs but I am not sure I am storing each users auth token securely. Currently, I am using flask.session to store the user’s access token (as well as other information - e.g., username, profile picture, etc.)

I made sure to set a secret key

app.server.config['SECRET_KEY'] = os.environ.get('FLASK_SECRET_KEY')
app.server.config['SESSION_COOKIE_SECURE'] = True
app.server.config['PERMANENT_SESSION_LIFETIME'] = datetime.timedelta(minutes=60)

For the actual login callback I am doing

if google_resp.status_code == 200:
    user_data = google_resp.json()

    # check db to see if they have access
    # dd is a custom function I created that uses azure.cosmos.cosmos_client
    cosmos = dd.CosmosDB(host=os.environ.get('COSMOS_HOST'),
                         master_key=os.environ.get('COSMOS_MASTER_KEY'),
                         db_id='DashboardUserAccess', container_id='users')
    resp = cosmos.get_user(email=user_data['email'].lower())

    if resp:
        r = flask.redirect(flask.session['REDIRECT_URL'])
        flask.session['AUTH_USERNAME'] = user_data['name']
        flask.session['AUTH_PICTURE'] = user_data['picture']
        flask.session['AUTH_EMAIL'] = user_data['email']
        flask.session['AUTH_TOKEN'] = token['access_token']
        # update db with login time
        now = datetime.datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')
        cosmos.upsert_user(email=resp['id'], first=resp['first'],
                           last=resp['last'], access=resp['access'], login=now)
        return r
    else:
        return flask.Response(status=403)

Do I need to encrypt token['access_token'] when doing flask.session['AUTH_TOKEN'] = token['access_token'] or is setting the following secure enough:

app.server.config['SECRET_KEY'] = os.environ.get('FLASK_SECRET_KEY')
app.server.config['SESSION_COOKIE_SECURE'] = True

Hello @PyGuy,

Did you check out my PR for dash-auth? The nice thing about dash-auth is that you can lock down callbacks, which you’d have to come up with the mechanism by hand in your method.

I’d also recommend making sure that your cookie can’t be read without some sort of encryption. Your cookie is secure, https, but you should make it same site lax and also http only.

1 Like

@jinnyzor thank you for the response! I will take a look at your PR for dash-auth. Also, can you direct me to some resources on how to make sure my cookie can’t be read without some sort of encryption? Is it as simple as doing something like

app.server.config['SESSION_COOKIE_SAMESITE'] = 'Lax'
app.server.config['SESSION_COOKIE_HTTPONLY'] = True

...

from cryptography.fernet import Fernet

key = Fernet.generate_key()
fernet = Fernet(key)

flask.session['AUTH_TOKEN'] = fernet.encrypt(token['access_token'].encode()).decode()

Not quite, you’ll need to encrypt and decrypt it in order to get any session data from it.

1 Like

Here is something where you can check what your cookie is putting out:

https://www.kirsle.net/wizards/flask-session.cgi

1 Like

@jinnyzor this is super helpful thank you!

1 Like

@jinnyzor is there a way to redirect the user after the session expires? Currently, I have flask.session.permanent = True so the session expiration gets updated with every request but I cannot seem to figure out a good way to redirect the user to the login screen or throw a popup message once the session expires.

Should I just set flask.session.permanent = False, end the session after a fixed time and use dcc.Interval with a clientside_callback to redirect the user?

You can handle this before_request and if a dash update component, pass a redirect to the login via a component command, otherwise just redirect to the login screen. :slight_smile:

1 Like

Ahhh that makes sense. Thank you, you’re awesome!

1 Like

Hey Jinzor,

Is this in the main dash-auth package or do you have an mre for it please?

Thanks heaps!

This is not in the main dash-Auth package, it is in a PR, you can check it out and then build it in order to take the updated package.

Or you can copy the altered files into dash-auth. There are a couple of examples in the PR of how you can use it.

1 Like