Simple Authentication for Streamlit Apps

Hi,

Some of you may know I’m into authentication/identity and have implemented a Streamlit component for Auth0 identity integration with Streamlit apps. That’s quite an involved solution and can be intimidating for some. On the other end of the scale is a simple database holding usernames + passwords. @madflier provided a neat solution recently which inspired me to delve into his approach in some detail and apply things I’d learned building enterprise apps to my own version of it. Thanks @madflier! :wave:t4: :v:t4: :muscle:t4:

I recently had the opportunity to work on this idea in a Streamlit-internal hackathon. I’m pleased to share outputs of that effort with the Streamlit community today!

See my GitHub repo.

What you’ll get:

  • Session state support so logins survive Streamlit’s top-down reruns which occur in it’s normal execution.
  • Support for logout, authenticated check, and a requires_auth function decorator to protect areas of your own apps, e.g. secure pages in a multi-page Streamlit application.
  • Built-in authentication/login status header widget that will sit nicely in most Streamlit apps.
  • Support for SQLite DB and Airtable cloud DB providers.
  • Passwords are stored hashed (MD5) & encrypted (AES256 CBC Extended) in the database, not as plain text.
  • Configuration has been externalized for things like database names and locations, cloud service account secrets, api keys, etc.

P.S. I’m preparing another implementation and tutorials of my Auth0 identity component (for Auth0), so watch this space.

Happy safe/secure Streamliting!

Arvindra

auth-simple-demo

14 Likes

Awesome!

I’ll let you edit your Authentication component links in the Tracker Streamlit Components - Community Tracker :slight_smile:

2 Likes

It’s more of a library, does that qualify? :see_no_evil:

Streamlit components at their most reductionist are just normal Python libraries :slight_smile:

2 Likes

Done!

1 Like

here i got an error:

(.venv) PS D:\streamlit\auth-simple-for-streamlit> streamlit run .\admin.py
2021-08-30 16:13:41.464 >>> Environment loading status <<<
2021-08-30 16:13:41.465 --  Application base directory: D:\streamlit\auth-simple-for-streamlit
2021-08-30 16:13:41.465 --  Dotenv file: D:\streamlit\auth-simple-for-streamlit\.env



  You can now view your Streamlit app in your browser.

  Local URL: http://localhost:8501
  Network URL: http://10.10.0.173:8501

2021-08-30 16:13:41.827 Traceback (most recent call last):
  File "d:\streamlit\auth-simple-for-streamlit\.venv\lib\site-packages\streamlit\script_runner.py", line 350, in _run_script
    exec(code, module.__dict__)
  File "D:\streamlit\auth-simple-for-streamlit\admin.py", line 69, in <module>
    import authlib.auth as auth
  File "D:\streamlit\auth-simple-for-streamlit\.\authlib\__init__.py", line 3, in <module>
    from .common.crypto import aes256cbcExtended
ModuleNotFoundError: No module named 'authlib.common.crypto'

even i pip install authlib , there still got the same error, am i miss something?
and i has installed the requirements

@lt8cn

Did you clone the GitHub repo? You should see full source tree below the root. This package isn’t available via PyPi (pip install).

Please also add pycryptodome to requirements.txt (I’ve updated this in GitHub with pinned versions that I use). I suggest you re-pull the GitHub repo.

Hi, I’m getting the same error as @lt8cn, but I’m using a Mac and I downloaded the repo after your reply to him–it seems something still isn’t quite right:

Installing collected packages: streamlit, python-dotenv, pycryptodome, pyairtable
Attempting uninstall: streamlit
Found existing installation: streamlit 0.85.1
Uninstalling streamlit-0.85.1:
Successfully uninstalled streamlit-0.85.1
Successfully installed pyairtable-1.0.0rc3 pycryptodome-3.10.1 python-dotenv-0.19.0 streamlit-0.86.0
(py38) qt02537@MacBook-Air ~/local/repos/learn/21Q3/GH/auth-simple-for-streamlit (master) [0|0]
20:07:43$ streamlit run --server.port 8080 app.py
2021-08-30 20:08:20.853 >>> Remote debugging in NOT active <<<
2021-08-30 20:08:20.862 >>> Environment loading status <<<
2021-08-30 20:08:20.862 – Application base directory: /Users/qt02537/local/repos/learn/21Q3/GH/auth-simple-for-streamlit
2021-08-30 20:08:20.862 – Dotenv file:

2021-08-30 20:08:20.871 Traceback (most recent call last):
File “/Users/qt02537/opt/miniconda3/envs/py38/lib/python3.8/site-packages/streamlit/script_runner.py”, line 350, in _run_script
exec(code, module.dict)
File “/Users/qt02537/local/repos/learn/21Q3/GH/auth-simple-for-streamlit/app.py”, line 10, in
from authlib.auth import auth, authenticated, requires_auth
File “/Users/qt02537/local/repos/learn/21Q3/GH/auth-simple-for-streamlit/authlib/init.py”, line 3, in
from .common.crypto import aes256cbcExtended
ModuleNotFoundError: No module named ‘authlib.common.crypto’

You can now view your Streamlit app in your browser.

Local URL: http://localhost:8080
Network URL: http://10.26.18.10:8080

@chilidogdaddyo (@lt8cn )

Apologies, the crypto class file was missing. I’ve added this so please do another pull.

Thanks for trying the solution. Do let me know if works now?

Cheers,
Arvindra

thank you so much.
there still some error
when i run streamlit run admin.py got error in the browser:

Super user mode (store = SQLITE)

Trapped exception

'dict_keys' object is not subscriptable

terminal displayed:

_sqlite_provider(allow_db_create=True, if_table_exists=ignore)
2021-08-31 09:39:44.237 >>> Creating table `USERS` in database `auth` <<<

when i run app.py the terminal displayed error:

  You can now view your Streamlit app in your browser.

  Local URL: http://localhost:8501
  Network URL: http://10.10.0.173:8501

2021-08-31 09:48:28.989 Traceback (most recent call last):
  File "c:\users\david\appdata\local\programs\python\python39\lib\site-packages\streamlit\script_runner.py", line 349, in _run_script
    exec(code, module.__dict__)
  File "D:\streamlit\auth-simple-for-streamlit\app.py", line 13, in <module>
    user = auth(sidebar=True, show_msgs=True)
  File "D:\streamlit\auth-simple-for-streamlit\authlib\auth.py", line 126, in auth
    with st.expander('Authentication', expanded=True):
AttributeError: module 'streamlit' has no attribute 'expander'

2021-08-31 09:48:29.446 >>> Remote debugging in NOT active <<<
2021-08-31 09:48:29.446 >>> Environment loading status <<<
2021-08-31 09:48:29.446 --  Application base directory: D:\streamlit\auth-simple-for-streamlit
2021-08-31 09:48:29.446 --  Dotenv file: D:\streamlit\auth-simple-for-streamlit\.env


2021-08-31 09:48:29.455 Traceback (most recent call last):
  File "c:\users\david\appdata\local\programs\python\python39\lib\site-packages\streamlit\script_runner.py", line 349, in _run_script
    exec(code, module.__dict__)
  File "D:\streamlit\auth-simple-for-streamlit\app.py", line 13, in <module>
    user = auth(sidebar=True, show_msgs=True)
  File "D:\streamlit\auth-simple-for-streamlit\authlib\auth.py", line 126, in auth
    with st.expander('Authentication', expanded=True):
AttributeError: module 'streamlit' has no attribute 'expander'

@lt8cn - a few of things to check.

  1. Was auth.db created in ./db when you ran the admin app? (Or, whatever DB name you configured in your .env file.)

The first few console messages at the beginning should be something like this:

_sqlite_provider(allow_db_create=True, if_table_exists=ignore)
2021-08-31 03:07:29.683 >>> Creating table USERS in database auth <<<
2021-08-31 03:07:29.700 Query: SELECT username, password, su FROM USERS
2021-08-31 03:07:50.797 >>> Remote debugging in NOT active <<<
2021-08-31 03:07:50.798 >>> Environment loading status <<<
2021-08-31 03:07:50.800 -- Application base directory: C:\Dev\streamlit\auth-simple-for-streamlit
2021-08-31 03:07:50.802 -- Dotenv file: C:\Dev\streamlit\auth-simple-for-streamlit\.env

  1. Did you then create users in the DB via the admin app?

  2. What version of Streamlit are you using, as the example app.py is reporting AttributeError: module 'streamlit' has no attribute 'expander'st.expander missing is weird unless you have an old version.

I have also added a minimal auth.db file to GitHub which will help you get started with a DB. Note, the admin and test users have data generated using the default encryption secrets. Once you’re familiar with the workings of the system, change the default encryption secrets and recreate the auth DB from scratch.

Thanks for testing!

Arvindra

Hi, I get the following when I run admin.py

(py38) qt02537@MacBook-Air ~/local/repos/learn/21Q3/GH/auth-simple-for-streamlit (master) [0|0]
07:50:33$ streamlit run admin.py
2021-08-31 07:51:28.900 >>> Remote debugging in NOT active <<<
2021-08-31 07:51:28.903 >>> Environment loading status <<<
2021-08-31 07:51:28.904 --  Application base directory: /Users/qt02537/local/repos/learn/21Q3/GH/auth-simple-for-streamlit
2021-08-31 07:51:28.904 --  Dotenv file:



  You can now view your Streamlit app in your browser.

  Local URL: http://localhost:8501
  Network URL: http://10.3.18.7:8501

2021-08-31 07:51:37.229 >>> Remote debugging in NOT active <<<
2021-08-31 07:51:37.230 >>> Environment loading status <<<
2021-08-31 07:51:37.230 --  Application base directory: /Users/qt02537/local/repos/learn/21Q3/GH/auth-simple-for-streamlit
2021-08-31 07:51:37.230 --  Dotenv file:


_sqlite_provider(allow_db_create=True, if_table_exists=ignore)

note that a database was created:

(py38) qt02537@MacBook-Air ~/local/repos/learn/21Q3/GH/auth-simple-for-streamlit (master) [0|0]
07:54:08$ ll db
total 24
drwxr-xr-x   4 qt02537  staff    128 Aug 31 07:36 .
drwxr-xr-x  20 qt02537  staff    640 Aug 30 20:08 ..
-rw-r--r--   1 qt02537  staff  12288 Aug 31 07:36 auth.db
-rw-r--r--   1 qt02537  staff      0 Aug 30 20:05 fake_for_github_repo.txt

in the browser I see the following exception:

Thanks

@chilidogdaddyo - Do you have time for a quick conf call so we can go over this?

You just need a .env file from env.sample: cp env.sample .env,than all will be all good。

i already test the prj , clone from github and make a .env file in the prj dir,than all error will be fixed.
mac or linux
cp env.sample .env
windows
copy env.sample .env
thank you so much for share the awesome prj !

                                                              David

@lt8cn - David, thanks for confirming. Yes, the .env file is very imp. as stated in the Readme.md.

app.py will print out the path to the .env file being used (should be based on env.sample). For example you will see the trace:

2021-08-31 03:26:29.411 >>> Environment loading status <<<
2021-08-31 03:26:29.411 --  Application base directory: C:\Dev\streamlit\auth-simple-for-streamlit
2021-08-31 03:26:29.412 --  Dotenv file: C:\Dev\streamlit\auth-simple-for-streamlit\.env

If you get the Airtable provider working for yourself, please let me know?

Best regards,
Arvindra

copying the sample to .env fixed it, thanks.

Great. Based on your experiences, I added a TL;DR to the top of the readme file for those who want to get started with bare minimum of instructions.

Hope you make use of it.

Arvindra

1 Like

Hi Arvindra,

I just wanted to say that this was incredibly helpful for me.

I’ve got it successfully working locally for my project (& paired it with st.secrets to keep my database token hidden).

I had a question though. I’ve never deployed a streamlit app before but would you have any tips for the best way to do so with this solution? Are there any pitfalls with, say, heroku? Would I leave the admin.py off the server, for example?

Cheers
Rahul

2 Likes

@rahul-psych
That’s great to hear and encouragement to do more. Thanks.

Taking the .env file approach vs. st.secrets and secrets.toml is a matter of taste. Definitely would prefer the latter for deployments to Streamlit Sharing. Regarding deployments to Heroku, assuming the server is secure, I see no problem in leaving admin.py there. But note you’d have to run it from the Heroku management console in headless mode, on a different port than the main app. The passwords are encrypted and as long as you have a strong password for the admin super user, you can safely manage the database from the main app. If you don’t want that management feature exposed in the main app, then comment out the code in auth.py that invokes super user mode when a SU logs in. It’s a lot easier to use Airtable because you can connect to it by running admin.py locally, avoiding having to run it in headless mode in Heroku, each time you want to edit the user DB. Airtable is free for 1000 records, so should be sufficient for most scenarios.

Thanks again for the feedback.

Best wishes,
Arvindra