Flask object has no attribute when calling a working function in a different route - flask

I have a flask app where I send a user a confirmation email on registration and then the user can also request that same email to be sent. I call the same function within my registration flow but it works for that, but fails when I call within the resend flow.
The error is:
File "/opt/render/project/src/app/auth/routes.py",in resend_confirmation
send_account_verify_email(user)
File "/opt/render/project/src/app/auth/email.py", line 12, in send_account_verify_email
token = user.get_token()
AttributeError: 'str' object has no attribute 'get_token'
This route works when calling send_account_verify_email(user)
#bp.route('/register', methods=['GET', 'POST'])
def register():
if current_user.is_authenticated:
return redirect(url_for('main.index'))
form = RegistrationForm()
if form.validate_on_submit():
user = User(username=form.username.data, email=form.email.data)
user.set_password(form.password.data)
db.session.add(user)
db.session.commit()
send_account_verify_email(user)
flash('Sent verification email to {email}'.format(email=user.email))
return redirect(url_for('auth.login'))
return render_template('auth/register.html', title='Register', form=form)
This route fails when calling send_account_verify_email(user)
#bp.route("/resend")
#login_required
def resend_confirmation():
if current_user.is_confirmed:
flash('Your account has already been confirmed')
return redirect(url_for('main.index'))
user=current_user.username
if user:
send_account_verify_email(user)
flash('A new confirmation email has been sent')
return redirect(url_for('auth.inactive'))
I believe it's down to this specific bit of code user=current_user.username which results in a different output. How can I correctly assign my value to user before I pass to my email function?

Related

Flask-login causing login form to not validate

The login form of my flask app is failing to validate (form.valididate = false) when the app has automatically logged users out:
app.permanent_session_lifetime = timedelta(minutes=5)
form.errors returns an empty dictionary {}.
Any ideas what is going on?
Login route below. When the user has been automatically logged out the code beneath 'if form.validate_on_submit...' is completely bypassed.
#view.route('/Login', methods=['GET', 'POST'])
def login():
if current_user.is_authenticated and current_user.is_suspended != True:
return redirect(url_for('view.Index'))
form = LoginForm()
if form.validate_on_submit():
user = User.query.filter_by(username=form.username.data).first()
if user is None or not user.check_password(form.password.data):
flash('Invalid username or password')
return redirect(url_for('view.login'))
if user.is_suspended and user.check_password(form.password.data):
flash('Your login has been suspended. Please contact NZGBC.')
return redirect(url_for('view.login'))
user.session_token = user.generate_session_token()
db.session.commit()
login_user(user, remember=form.remember_me.data)
if form.remember_me.data is not True:
app.permanent_session_lifetime = timedelta(minutes=60)
else:
app.permanent_session_lifetime = timedelta(days=365)
next = request.args.get('next')
# is_safe_url should check if the url is safe for redirects.
# See http://flask.pocoo.org/snippets/62/ for an example.
if not is_safe_url(next):
return abort(400)
return redirect(next or url_for('view.Index'))
return render_template('login.html', highlight='11', title='Sign In', form=form)
According to your comment:
Obliously after using the CSRF token for submitting a form it will fail, since CSRF tokens are 1-time use.
Try doing this as a workaround in your login route:
try:
if form.validate_on_submit():
# ... process your form normally ...
except:
return redirect(url_for('view.login'))
# ... maybe display a message for the user to reenter his creds because he was logged out ...
This should work as a viable fix for your problem.

how to access a firebase token and use as a decorator? [duplicate]

This question already has answers here:
How to pass a variable between Flask pages?
(2 answers)
Closed last year.
I want some of my flask routes to be authenticated before accessing, meaning any routes with the decorator #check_token will have to have a valid token prior to accessing the route.
I have a login_or_create route where users can either login or create an account onto Firebase.
From the login logic, a token is generated, and I want to pass that token to the home route. I feel my code is almost there, but I'm not sure how to persist the token into the next route.
def check_token(f):
#wraps(f)
def wrap(*args,**kwargs):
if not request.headers.get('authorization'):
return {'message': 'No token provided'},400
try:
user = auth.verify_id_token(request.headers['authorization'])
request.user = user
except:
return {'message':'Invalid token provided.'},400
return f(*args, **kwargs)
return wrap
#app.route("/", methods=['GET', 'POST'])
def login_or_create():
if request.method == 'POST':
#Handle login form
if request.form['action'] == 'login':
data = request.form
email = data.get('user_email')
password = data.get('user_password')
try:
signin_user = pb.auth().sign_in_with_email_and_password(email, password)
token = signin_user['idToken']
return redirect(url_for('home'))
except:
return {'message':'There was an error logging in'}, 400
#app.route("/home")
#check_token
def home():
return render_template('home_page.html)'
I ended up saving the token in a session variable. I think there are drawbacks to this, but this has been my solution thus far.

Error when fetching the logged in user details

I am using Django 2.2.6.
Login Code
class loginController(View):
def post(self, request):
username = request.POST.get('username')
password = request.POST.get('password')
userobj = authenticate(username = username, password = password)
if(userobj != None):
login(request, userobj)
return redirect('myprofileapp:profile')
Once the user is authenticated, navigated to profile page. When this line executes if(request.user.is_authenticated):, I get an error.
class accountController(View):
def get(self, request):
if(request.user.is_authenticated):
return HttpResponse(request.user)
'User' object is not iterable
Edit 1
class accountController(View):
def get(self, request):
if(request.user.is_authenticated):
print(request.user.username)
return HttpResponse("ok")
else:
return HttpResponse("not ok")
nothing is printed. Just ok got printed in above code.
This is wrong:
return HttpResponse(request.user)
You can pass in a string to HttpResponse. You are passing request.user which is an object. Since the user name is a string you can do something like:
return HttpResponse( request.user.username)
Basically whatever string you pass to HttpResponse will be shown in in the browser. You pass "ok", ok will be printed, you pass "Pankaj", Pankaj gets printed. You pass request.user.username which is a string, that username gets shown in the browser as response.
Putting something in print prints to your django console, it does not print to your web browser.
You should not return HttpResponse(request.user), as it is not an iterable object. It is just a SimpleLazyObject of AuthenticationMiddleware.
instead of if(request.user.is_authenticated): try this if request.user.is_authenticated():

Flask-login redirecting back to login page

I am writing a simple flask app using flask-login and flask-mongoengine, everything was working fine until I updated all of the python plugins that I need for the project. Now flask won't log me in. When I login and the form is validated, it returns me to the login page, with the url: http://localhost:5000/auth/login?next=%2Findex ... I think the %2F might have something to do with the issue.
Here is my login view:
#bp.route('/login', methods=['GET', 'POST'])
def login():
if current_user.is_authenticated:
return redirect(url_for('main.index'))
form = LoginForm()
if form.validate_on_submit():
user = User.objects(username__exact=form.username.data).first()
if user is None or not user.check_password(form.password.data):
flash(_('Invalid username or password'))
return redirect(url_for('auth.login'))
login_user(user, remember=form.remember_me.data)
next_page = request.args.get('next')
if not next_page or url_parse(next_page).netloc != '':
next_page = url_for('main.index')
return redirect(next_page)
return render_template('auth/login.html', title=_('Sign In'), form=form)
I'm using WTForms and I can confirm that the form action=""
This is adapted from the flask megatutorial, but I'm using MongoDB.
Problem solved!
When I was converting the app from using SQL to MongoDB, I incorrectly made the load_user function like:
#login.user_loader
def load_user(username):
return User.objects(username__exact=username).first()
But in reality the load_user function needs to accept the id field as an interface, so the function should look like:
#login.user_loader
def load_user(id):
return User.objects.get(id=id)

Django Cookie with Login function

I'm trying to set my first cookie with Django when users are logged on my application.
When user is logged, the template is well-displayed but none cookie in my application which is named : Cookie
My function looks like :
def Login(request):
error = False
if request.method == "POST":
form = ConnexionForm(request.POST)
if form.is_valid():
username = form.cleaned_data["username"]
password = form.cleaned_data["password"]
user = authenticate(username=username, password=password)
if user:
login(request, user)
toto = GEDCookie(request)
return render(request, 'Home_Homepage.html', {'toto':toto})
else:
error = True
else:
form = ConnexionForm()
return render(request, 'Authentication_Homepage.html', locals())
#csrf_exempt
def GEDCookie(request):
SID = Logger.login("test", "10test")
response = HttpResponse("Cookie")
response.set_cookie('Cookie', SID, max_age=None)
return response
I missed something in my script ?
This isn't how you use cookies at all.
Inside your Login view, you're calling a separate view - GEDCookie that returns an HTTP response. But instead of returning that response directly to the user, which would set the cookie, you're for some reason trying to insert it in a template. That doesn't make sense.
If you want to set a cookie in your login view, you need to do so on the response that you return to the user.
Note also that after a successful login (or other post), you should always redirect, not display a template directly. So:
if user:
login(request, user)
response = redirect('home')
response.set_cookie('whatever')
return response
Finally, you almost certainly don't need a cookie here in any case. If you want to store data related to the current user, use the session.
As you can clearly see that you are not attaching your cookie to your real response, you are passing it as the context in render function which is an issue.
def Login(request):
error = False
if request.method == "POST":
form = ConnexionForm(request.POST)
if form.is_valid():
username = form.cleaned_data["username"]
password = form.cleaned_data["password"]
user = authenticate(username=username, password=password)
if user:
login(request, user)
SID = Logger.login("test", "10test")
response = render(request, 'Home_Homepage.html', {})
response.set_cookie('Cookie', SID, max_age=None)
return response
else:
error = True
else:
form = ConnexionForm()
return render(request, 'Authentication_Homepage.html', locals())
https://docs.djangoproject.com/en/3.0/ref/request-response/#django.http.HttpResponse.set_cookie Please refer this link for individual arguments of inbuilt function.
Create signal.py in app. where your user model is present or add in main project directory and Add below snippet in signal.py
from django.db.models.signals import pre_save, pre_delete, post_save, post_delete
from django.dispatch import receiver
from django.dispatch import Signal
from allauth.account.signals import user_logged_in # it signal for post login
from django.shortcuts import render
#receiver(user_logged_in) # Decorator of receiving signal while user going to logged in
def post_login(sender, user, request, response, **kwargs):
response.set_cookie('team', 'india') # This will set cookie
return response
In given snippet, default response will come in argument, so direct redirect to that response, if you want to change then render other template using render/redirect django.shortcuts methods like below,
response = render(request, 'base.html', {})