Jon Solow commited on
Commit
720467c
β€’
1 Parent(s): 7858dd3

Implement a token login system to replace passwords

Browse files
Files changed (2) hide show
  1. src/login.py +23 -43
  2. src/shared_page.py +8 -0
src/login.py CHANGED
@@ -39,67 +39,30 @@ class HFFriendlyGSheetsConnection(GSheetsConnection):
39
  conn = st.connection("gsheets", type=HFFriendlyGSheetsConnection)
40
 
41
 
42
- def get_user_id_from_email_if_exists(email: str) -> int:
43
- # returns -1 if id not found
44
- df_email = conn.read(
45
- worksheet="users",
46
- usecols=[0, 1],
47
- ttl=3600,
48
- )
49
- df_email = df_email[df_email.email.notna()]
50
- for row in df_email.itertuples():
51
- if row.email.lower() == email.lower():
52
- return row.id
53
- return -1
54
-
55
-
56
- def get_password_from_user_id(user_id: int) -> str | None:
57
- try:
58
- df_pass = conn.read(
59
- worksheet=f"user-{user_id}-password",
60
- usecols=[0],
61
- ttl=1,
62
- )
63
- return df_pass.password[0]
64
- except Exception:
65
- return None
66
-
67
-
68
  def check_password():
69
  """Returns `True` if the user had a correct password."""
70
 
71
  def login_form():
72
  """Form with widgets to collect user information"""
73
  with st.form("Credentials"):
74
- st.text_input("Email", key="email")
75
- st.text_input("Password", type="password", key="password")
76
  st.form_submit_button("Log in", on_click=password_entered)
77
 
78
  def password_entered():
79
  """Checks whether a password entered by the user is correct."""
80
- # check if email exists
81
- if user_id := get_user_id_from_email_if_exists(st.session_state["email"]):
82
- if password := get_password_from_user_id(user_id):
83
- if hmac.compare_digest(
84
- st.session_state["password"],
85
- password,
86
- ):
87
- st.session_state["password_correct"] = True
88
- st.session_state["logged_in_user"] = user_id
89
- del st.session_state["password"] # Don't store the email or password.
90
- del st.session_state["email"]
91
- del password
92
- return
93
  st.session_state["password_correct"] = False
94
 
95
- # Return True if the email + password is validated.
96
  if st.session_state.get("password_correct", False):
97
  return True
98
 
99
  # Show inputs for email + password.
100
  login_form()
101
  if st.session_state.get("password_correct", True) is False:
102
- st.error("πŸ˜• User not known or password incorrect")
103
  return False
104
 
105
 
@@ -120,3 +83,20 @@ def save_user_team(team_selections):
120
  worksheet=f"user-{user_id}-roster",
121
  data=pd.DataFrame(team_selections, index=[0]),
122
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  conn = st.connection("gsheets", type=HFFriendlyGSheetsConnection)
40
 
41
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  def check_password():
43
  """Returns `True` if the user had a correct password."""
44
 
45
  def login_form():
46
  """Form with widgets to collect user information"""
47
  with st.form("Credentials"):
48
+ st.text_input("Login token", type="password", key="password")
 
49
  st.form_submit_button("Log in", on_click=password_entered)
50
 
51
  def password_entered():
52
  """Checks whether a password entered by the user is correct."""
53
+ # check if token exists
54
+ if login_by_token(st.session_state["password"]):
55
+ return
 
 
 
 
 
 
 
 
 
 
56
  st.session_state["password_correct"] = False
57
 
58
+ # Return True if the token is validated.
59
  if st.session_state.get("password_correct", False):
60
  return True
61
 
62
  # Show inputs for email + password.
63
  login_form()
64
  if st.session_state.get("password_correct", True) is False:
65
+ st.error("πŸ˜• Token incorrect")
66
  return False
67
 
68
 
 
83
  worksheet=f"user-{user_id}-roster",
84
  data=pd.DataFrame(team_selections, index=[0]),
85
  )
86
+
87
+
88
+ def login_by_token(token: str):
89
+ # returns true if logged in successfully
90
+ df = conn.read(
91
+ worksheet="user-tokens",
92
+ usecols=[0, 1],
93
+ ttl=3600,
94
+ )
95
+ df = df[df.token.notna()]
96
+ for row in df.itertuples():
97
+ if hmac.compare_digest(row.token, token):
98
+ user_id = int(row.id)
99
+ st.session_state["password_correct"] = True
100
+ st.session_state["logged_in_user"] = user_id
101
+ return True
102
+ return False
src/shared_page.py CHANGED
@@ -2,6 +2,7 @@ import os
2
  import streamlit as st
3
 
4
  from queries.nflverse.github_data import load_assets_if_no_tables
 
5
 
6
 
7
  def get_local_style():
@@ -15,6 +16,13 @@ def local_css():
15
  return st.markdown(get_local_style(), unsafe_allow_html=True)
16
 
17
 
 
 
 
 
 
 
18
  def common_page_config():
19
  local_css()
20
  load_assets_if_no_tables()
 
 
2
  import streamlit as st
3
 
4
  from queries.nflverse.github_data import load_assets_if_no_tables
5
+ from login import login_by_token
6
 
7
 
8
  def get_local_style():
 
16
  return st.markdown(get_local_style(), unsafe_allow_html=True)
17
 
18
 
19
+ def login_token_arg_if_exists():
20
+ url_params = st.experimental_get_query_params()
21
+ if arg_token_list := url_params.get("token"):
22
+ login_by_token(arg_token_list[0])
23
+
24
+
25
  def common_page_config():
26
  local_css()
27
  load_assets_if_no_tables()
28
+ login_token_arg_if_exists()