I'm currently learning django and I've created a simple TODO project which has an otp authentication feature. While running the project in localserver otp was generated and stored in the model which is then matched by using if (t.otp == int(request.POST['otp'])): where t is a model object. This works perfectly fine. While I deployed the same in heroku server with DEBUG = True for testing purpose, it is throwing MultiValueDictKeyError in line if (t.otp == int(request.POST['otp'])):. Initially t.opt is set to the value generated and it is reinitialized to 0 once the user enters the correct OTP and registers. Kindly notice the comments in the code that explains the issue a bit more.
My views.py goes as (I've removed some elif part that are not required to reduce the number of lines),
def regsub(request):
if(request.method=="POST"):
uname = request.POST['username']
fname = request.POST['fname']
lname = request.POST['lname']
email = str(request.POST['email'])
pass1 = request.POST['pass1']
try: **#this tries to get the values pass1 and pass 2 whereas except gets the value otp**
if(request.POST['pass1']==request.POST['pass2']):
dictpass = {
'username':uname,
'fname':fname,
'lname':lname,
'email':email,
'password':pass1,
}
the_otp = r.randint(100000,999999)
try:
t=temp.objects.create(name=uname,otp=the_otp)
t.save()
except(IntegrityError):
return render(request,'register.html',{'flag':True,'msg':"Username already exits! Please try again."})
sub = "OTP for registration"
msg = "Some Message " + the_otp
from_email = settings.EMAIL_HOST_USER
to_email = [request.POST['email'],]
send_mail(sub,msg,from_email,to_email)
messages.info(request,"Password Verified! Please Enter the OTP sent to your email.")
return render(request,'register.html',dictpass)
except:
t = temp.objects.get(name=uname)
if (t.otp == int(request.POST['otp'])): **#the flow directly hits here after saving the otp in the model(send_mail part is not executed in try), also throws MultiValueDictKeyError**
user = User.objects.create_user(username=uname, first_name=fname, last_name=lname, email=email, password=pass1)
t.otp = 0
t.save()
user.save()
task.save()
sub = "Account creation successful"
msg = "Some Congrats message"
from_email = settings.EMAIL_HOST_USER
to_email = [request.POST['email'],]
send_mail(sub,msg,from_email,to_email)
messages.info(request,"Account has been created Successfully!")
return render(request,'login.html')
else:
return render(request,'register.html',{'flag':True})
The above code works good in localhost whereas fails in heroku. I have used heroku postgres in production.
Related
I am learning Celery+Reddis and trying to send email through Celery. I have created a task in which I have encapsulated my send email logic. I am calling the send_login_mail function and passing the keyword arguments using .delay().
#views.py
class SignUpOTP(APIView):
permission_classes = [AllowAny]
def post(self, request):
request_email = request.data.get("email",)
try:
user = User.objects.get(email__iexact = request_email)
return Response({"status": "User is already registered."}, status=status.HTTP_403_FORBIDDEN)
except:
if request_email:
send_login_mail.delay(email=request_email, subject="[OTP] New Login for Connect App")
return Response({'status':'OTP sent successfully.'},status = status.HTTP_200_OK)
else:
return Response({"status":"Please enter an email id"},status = status.HTTP_400_BAD_REQUEST)
#tasks.py
#shared_task(bind=True)
def send_login_mail(email=None, subject="[OTP] New Login for Connect App"):
print("Wowowofdsofsdojsdjdofdffdojodjodfdfjdoofdods\nsdfjdsfjdsofjdsojdsdsosdoosdjfosdsdfodf")
OTP.objects.filter(otp_email__iexact = email).delete()
otp = random.randint(100000,999999)
msg = EmailMessage(subject, f'<div style="font-family: Helvetica,Arial,sans-serif;min-width:1000px;overflow:auto;line-height:2"><div style="margin:50px auto;width:70%;padding:20px 0"><div style="border-bottom:1px solid #eee">Connect</div><p style="font-size:1.2em">Greetings,</p><p style="font-size:1.2em"> Thank you for creating an account on Connect. You can count on us for quality, service, and selection. Now, we would not like to hold you up, so use the following OTP to complete your Sign Up procedures and order away.<br><b style="text-align: center;display: block;">Note: OTP is only valid for 5 minutes.</b></p><h2 style="font-size: 1.9em;background: #FFD243;margin: 0 auto;width: max-content;padding: 0 15px;color: #fff;border-radius: 4px;">{otp}</h2><p style="font-size:1.2em;">Regards,<br/>Team Connect</p><hr style="border:none;border-top:1px solid #eee" /><div style="float:right;padding:8px 0;color:#aaa;font-size:1.2em;line-height:1;font-weight:500"><p>Connect</p><p>Boys Hostel, Near Girl Hostel AKGEC</p><p>Ghaziabad</p></div></div></div>', 'swaad.info.contact#gmail.com', (email,))
msg.content_subtype = "html"
msg.send()
time_created = int(time.time())
OTP.objects.create(otp=otp, otp_email = email, time_created = time_created)
return Response({"OTP has been successfully sent to your email."})
Am I using .delay() wrong? I have tried using .apply_async() but still couldn't make it work.p
send_login_mail.delay(request_email) or
send_login_mail.delay(request_email, "[OTP] New Login for Connect App")
might work without issue.
Also there is no point of keep None default argument email, make it a positional argument instead.
Celery might got two argument named email.
Refer the following link:
TypeError: got multiple values for argument
Soooooo, I've checked every answer I can on this site. But I'm not finding a solution that is working. I'm trying to log a user into a site I am developing on local host from a unity application. I'm not passing the boolean "remember" because it was giving me issues.
My unity code is:
IEnumerator Login(string username, string password, bool remember)
{
List<IMultipartFormSection> formData = new List<IMultipartFormSection>();
formData.Add(new MultipartFormDataSection("username=" + username + "&password=" + password));
using (UnityWebRequest www = UnityWebRequest.Post("http://127.0.0.1:5000/api/children/mobile_login/", formData))
{
yield return www.SendWebRequest();
var response = www.downloadHandler.text;
Debug.Log(response);
if (www.isNetworkError || www.isHttpError)
{
Debug.Log(www.error);
}
else
{
Debug.Log("Login complete!");
}
}
}
and my python code is:
#children.route('/mobile_login', methods=['POST'])
def mobile_login():
username = request.form.get('username')
password = request.form.get('password')
remember = request.form.get('remember')
user = User.objects(username=username).first()
if user and crypt.check_password_hash(user.password, password):
login_user(user)
user.save()
token = user.get_auth_token()
res = make_response(jsonify({"user": user}), 200)
if remember:
res.set_cookie('child-auth', value=token, path='/')
return res
else:
return jsonify({"error": "unable to login"}), 401
The code is returning "{"error": "unable to login"}" and HTTP/1.1 401 Unauthorized.
I know for a fact that the username and password are correct. And I know the log in function works because my colleague is able to login from the react app he is working on.
I tried running my local host as https but that didn't work so I changed it back to http.
Any suggestions?
I am trying to send mail to the user through flask-mail. This code shown below works fine on the localhost. But when I deployed my flask app to AWS Elastic Beanstalk and use the send_reset_email function, it throws me Internal Server Error. Where should I change my code? Any help will be appreciated.
My config code:
application = Flask(__name__)
application.config['SECRET_KEY'] = '-------'
application.config["MONGO_URI"] = "mongodb+srv://------"
mongo = PyMongo(application)
db = mongo.db
bcrypt = Bcrypt(application)
application.config['MAIL_SERVER'] = 'smtp.googlemail.com'
application.config['MAIL_PORT'] = 587
application.config['MAIL_USE_TLS'] = True
application.config['MAIL_USERNAME'] = '----'
application.config['MAIL_PASSWORD'] = '----'
mail = Mail(application)
My function file:
def get_reset_token(username, expires_sec=1800):
s = Serializer(application.config['SECRET_KEY'], expires_sec)
return s.dumps({'user': username}).decode('utf-8')
def verify_reset_token(token):
s = Serializer(application.config['SECRET_KEY'])
try:
username = s.loads(token)['user']
except:
return None
user = db.user.find_one({ 'username' : username })
return user
def send_reset_email(user):
token = get_reset_token(username=user['username'])
msg = Message('Password Reset Request',sender='----',recipients=[user['email']])
msg.body = f'''To reset your password, visit the following link:
{url_for('reset_token', token=token, _external=True)}
If you did not make this request then simply ignore this email and no changes will be made.
'''
mail.send(msg)
You can use mail.send_message() that takes in arguments title, sender, recipients and body. Here is a similar code that i used to send email activation token:
code_act = "127.0.0.1:5000/confirm-mail/"+token
mail.send_message("Account activation Link", sender="bot", recipients=email.split(), body="The activation link is " + code_act)
The below code successfully generates a token and sends a link to the user's inbox for confirmation. But when the user clicks on it, Flask is not recognizing the token it just created. Here is the error message:
"Got exception from ts.loads: 404 Not Found: The requested URL was not
found on the server. If you entered the URL manually please check
your spelling and try again."
The bottom line is that this is what should execute if I could make the confirmation procedure work properly:
return redirect(url_for('tutorials'))
But, as you can piece together by noting the error message that is coming out of #app.errorhandler(404), something is going wrong. I'm really stuck. These tests are being done way before the max_age of 86400 seconds is reached. Any help would be much appreciated!!!
from itsdangerous import URLSafeTimedSerializer
ts = URLSafeTimedSerializer(SECRET_KEY, salt='email-confirm-key')
#app.route('/signup', methods=['GET', 'POST'])
def signup():
#skipping unrelated lines of code
token = ts.dumps(form.email.data, salt='email-confirm-key')
subject = 'subject goes here'
msg = Message(subject=subject, sender='name#email.com', recipients=form.email.data.split())
link = url_for('confirm_email', token=token, _external=True)
msg.html = render_template("email_confirmationemail.html", link=link, name=request.form['first_name'])
with app.app_context():
mail.send(msg)
return redirect(url_for('checkyouremail'))
#app.route('/confirmemail/<token>')
def confirm_email(token):
try:
email = ts.loads(token, salt='email-confirm-key', max_age=86400)
#skipping unrelated lines of code
return redirect(url_for('tutorials'))
#app.errorhandler(404)
def not_found(e):
print('Got exception from ts.loads: {}'.format(e))
return render_template('404.html')
In models.py, my __init__ method for the User class has this line:
self.email = email.lower()
When users create a profile on a phone, their email address often starts with an uppercase letter.
So I just needed to change
token = ts.dumps(form.email.data, salt='email-confirm-key')
to
token = ts.dumps(form.email.data.lower(), salt='email-confirm-key')
so that the email held in the token matched with the email in the database when the user clicked on the confirmation link I sent them. (In short, adding .lower() as shown above in my call do dumps solved my problem).
I have my own signin/login functionality in my APP. At the time registering I am encrypting the password using passlib hash method and storing the encrypted value inside table. But my problem is while I am trying to logged in it could not match the value using Python.
Here is my code:
def signsave(request):
name = request.POST.get('uname')
password = request.POST.get('pass')
con_pass = request.POST.get('conpass')
new_pass = sha256_crypt.encrypt( password )
hash = new_pass
if password == con_pass:
passw = User(
uname=name,
password=new_pass,
raw_password=password,
)
passw.save()
Here I am saving all credentials in table.
def loginsave(request):
password = request.POST.get('pass')
uname = request.POST.get('uname')
new_pass = sha256_crypt.encrypt( password )
per = User.objects.all().filter(
Q(password__icontains=new_pass) &
Q(uname__icontains=uname)).count()
Here again I am trying to encrypt the registered password and matched with table but its not working and count is coming 0.
You're not supposed to hash the password again when trying to log in. You need to get the existing hash out of the database and use .verify() on it. There's an example on the homepage of passlib about how to use it: https://passlib.readthedocs.io/en/stable/