1- from  flask  import  Blueprint , render_template , redirect , url_for , flash , request 
1+ from  flask  import  Blueprint , render_template , redirect , url_for , request ,  flash , jsonify ,  session 
22from  flask_login  import  login_user , logout_user , login_required , current_user 
3- from  werkzeug .security  import  generate_password_hash 
43from  app .models  import  db , User 
54import  pyotp 
65import  qrcode 
76import  io 
87import  base64 
8+ from  datetime  import  datetime 
99
1010auth_bp  =  Blueprint ('auth' , __name__ )
1111
@@ -14,19 +14,30 @@ def login():
1414    if  request .method  ==  'POST' :
1515        username  =  request .form .get ('username' )
1616        password  =  request .form .get ('password' )
17-         totp_token  =  request .form .get ('totp_token ' , '' )
17+         totp_code  =  request .form .get ('totp_code ' , '' )
1818
1919        user  =  User .query .filter_by (username = username ).first ()
2020
2121        if  user  and  user .check_password (password ):
22-             if  user .totp_enabled  and  not  user .verify_totp (totp_token ):
23-                 flash ('Invalid 2FA token' , 'error' )
24-                 return  redirect (url_for ('auth.login' ))
22+             # Check TOTP if enabled 
23+             if  user .totp_enabled :
24+                 if  not  totp_code :
25+                     flash ('2FA code required' , 'error' )
26+                     return  render_template ('login.html' , require_totp = True , username = username )
2527
26-             login_user (user )
27-             return  redirect (url_for ('main.dashboard' ))
28+                 if  not  user .verify_totp (totp_code ):
29+                     flash ('Invalid 2FA code' , 'error' )
30+                     return  render_template ('login.html' , require_totp = True , username = username )
31+ 
32+             # Update last login 
33+             user .last_login  =  datetime .utcnow ()
34+             db .session .commit ()
2835
29-         flash ('Invalid credentials' , 'error' )
36+             login_user (user )
37+             next_page  =  request .args .get ('next' )
38+             return  redirect (next_page ) if  next_page  else  redirect (url_for ('index' ))
39+         else :
40+             flash ('Invalid username or password' , 'error' )
3041
3142    return  render_template ('login.html' )
3243
@@ -36,30 +47,98 @@ def logout():
3647    logout_user ()
3748    return  redirect (url_for ('auth.login' ))
3849
50+ @auth_bp .route ('/profile' ) 
51+ @login_required  
52+ def  profile ():
53+     return  render_template ('profile.html' , user = current_user )
54+ 
3955@auth_bp .route ('/setup-2fa' , methods = ['GET' , 'POST' ]) 
4056@login_required  
4157def  setup_2fa ():
42-     if  request .method  ==  'POST' :
43-         if  'enable'  in  request .form :
44-             secret  =  current_user .generate_totp_secret ()
45-             current_user .totp_enabled  =  True 
46-             db .session .commit ()
58+     """Setup 2FA for user""" 
59+     try :
60+         print (f"Setup 2FA called for user: { current_user .username }  " )
4761
48-             # Generate QR code 
49-             qr  =  qrcode .QRCode (version = 1 , box_size = 10 , border = 5 )
50-             qr .add_data (current_user .get_totp_uri ())
51-             qr .make (fit = True )
52-             img  =  qr .make_image (fill_color = "black" , back_color = "white" )
62+         if  request .method  ==  'POST' :
63+             # Verify the TOTP code 
64+             token  =  request .form .get ('token' )
65+             print (f"Verifying token: { token }  " )
5366
54-             buf   =   io . BytesIO () 
55-             img . save ( buf ,  format = 'PNG ' )
56-             qr_code   =   base64 . b64encode ( buf . getvalue ()). decode ( )
67+             if   not   token : 
68+                  flash ( 'Please enter the verification code' ,  'error ' )
69+                  return   redirect ( url_for ( 'auth.setup_2fa' ) )
5770
58-             return  {'qr_code' : qr_code , 'secret' : secret }
71+             # Verify the token 
72+             if  current_user .verify_totp (token ):
73+                 current_user .totp_enabled  =  True 
74+                 db .session .commit ()
75+                 flash ('2FA has been enabled successfully!' , 'success' )
76+                 print (f"2FA enabled for user: { current_user .username }  " )
77+                 return  redirect (url_for ('auth.profile' ))
78+             else :
79+                 flash ('Invalid verification code. Please try again.' , 'error' )
80+                 print (f"Invalid token for user: { current_user .username }  " )
5981
60-         elif  'disable'  in  request .form :
61-             current_user .totp_enabled  =  False 
62-             current_user .totp_secret  =  None 
82+         # Generate new secret if needed 
83+         if  not  current_user .totp_secret :
84+             print ("Generating new TOTP secret" )
85+             current_user .generate_totp_secret ()
6386            db .session .commit ()
6487
65-     return  redirect (url_for ('main.dashboard' ))
88+         # Generate QR code 
89+         print ("Generating QR code" )
90+         qr_code  =  generate_qr_code (current_user )
91+ 
92+         return  render_template ('setup_2fa.html' , qr_code = qr_code , user = current_user )
93+ 
94+     except  Exception  as  e :
95+         print (f"ERROR in setup_2fa: { str (e )}  " )
96+         import  traceback 
97+         traceback .print_exc ()
98+         flash ('An error occurred while setting up 2FA' , 'error' )
99+         return  redirect (url_for ('auth.profile' ))
100+ 
101+ @auth_bp .route ('/disable-2fa' , methods = ['POST' ]) 
102+ @login_required  
103+ def  disable_2fa ():
104+     """Disable 2FA for user""" 
105+     try :
106+         print (f"Disabling 2FA for user: { current_user .username }  " )
107+ 
108+         current_user .totp_enabled  =  False 
109+         current_user .totp_secret  =  None 
110+         db .session .commit ()
111+ 
112+         flash ('2FA has been disabled' , 'success' )
113+         return  redirect (url_for ('auth.profile' ))
114+ 
115+     except  Exception  as  e :
116+         print (f"ERROR in disable_2fa: { str (e )}  " )
117+         flash ('An error occurred while disabling 2FA' , 'error' )
118+         return  redirect (url_for ('auth.profile' ))
119+ 
120+ def  generate_qr_code (user ):
121+     """Generate QR code for TOTP setup""" 
122+     try :
123+         # Get the provisioning URI 
124+         uri  =  user .get_totp_uri ()
125+         print (f"TOTP URI: { uri }  " )
126+ 
127+         # Generate QR code 
128+         qr  =  qrcode .QRCode (version = 1 , box_size = 10 , border = 5 )
129+         qr .add_data (uri )
130+         qr .make (fit = True )
131+ 
132+         img  =  qr .make_image (fill_color = "black" , back_color = "white" )
133+ 
134+         # Convert to base64 
135+         buf  =  io .BytesIO ()
136+         img .save (buf , format = 'PNG' )
137+         buf .seek (0 )
138+ 
139+         qr_code  =  base64 .b64encode (buf .getvalue ()).decode ()
140+         return  f"data:image/png;base64,{ qr_code }  " 
141+ 
142+     except  Exception  as  e :
143+         print (f"ERROR generating QR code: { str (e )}  " )
144+         raise 
0 commit comments